1 /* 2 * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2016, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <AbstractSocket.h> 9 10 #include <arpa/inet.h> 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <netinet/in.h> 14 #include <sys/poll.h> 15 #include <sys/time.h> 16 17 18 //#define TRACE_SOCKET 19 #ifdef TRACE_SOCKET 20 # define TRACE(x...) printf(x) 21 #else 22 # define TRACE(x...) ; 23 #endif 24 25 26 BAbstractSocket::BAbstractSocket() 27 : 28 fInitStatus(B_NO_INIT), 29 fSocket(-1), 30 fIsBound(false), 31 fIsConnected(false), 32 fIsListening(false) 33 { 34 } 35 36 37 BAbstractSocket::BAbstractSocket(const BAbstractSocket& other) 38 : 39 fInitStatus(other.fInitStatus), 40 fLocal(other.fLocal), 41 fPeer(other.fPeer), 42 fIsConnected(other.fIsConnected), 43 fIsListening(other.fIsListening) 44 { 45 fSocket = dup(other.fSocket); 46 if (fSocket < 0) 47 fInitStatus = errno; 48 } 49 50 51 BAbstractSocket::~BAbstractSocket() 52 { 53 Disconnect(); 54 } 55 56 57 status_t 58 BAbstractSocket::InitCheck() const 59 { 60 return fInitStatus; 61 } 62 63 64 bool 65 BAbstractSocket::IsBound() const 66 { 67 return fIsBound; 68 } 69 70 71 bool 72 BAbstractSocket::IsListening() const 73 { 74 return fIsListening; 75 } 76 77 78 bool 79 BAbstractSocket::IsConnected() const 80 { 81 return fIsConnected; 82 } 83 84 85 status_t 86 BAbstractSocket::Listen(int backlog) 87 { 88 if (!fIsBound) 89 return B_NO_INIT; 90 91 if (listen(Socket(), backlog) != 0) 92 return fInitStatus = errno; 93 94 fIsListening = true; 95 return B_OK; 96 } 97 98 99 void 100 BAbstractSocket::Disconnect() 101 { 102 if (fSocket < 0) 103 return; 104 105 TRACE("%p: BAbstractSocket::Disconnect()\n", this); 106 107 close(fSocket); 108 fSocket = -1; 109 fIsConnected = false; 110 fIsBound = false; 111 } 112 113 114 status_t 115 BAbstractSocket::SetTimeout(bigtime_t timeout) 116 { 117 if (timeout < 0) 118 timeout = 0; 119 120 struct timeval tv; 121 tv.tv_sec = timeout / 1000000LL; 122 tv.tv_usec = timeout % 1000000LL; 123 124 if (setsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval)) != 0 125 || setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, 126 sizeof(timeval)) != 0) { 127 return errno; 128 } 129 130 return B_OK; 131 } 132 133 134 bigtime_t 135 BAbstractSocket::Timeout() const 136 { 137 struct timeval tv; 138 socklen_t size = sizeof(tv); 139 if (getsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, &size) != 0) 140 return B_INFINITE_TIMEOUT; 141 142 return tv.tv_sec * 1000000LL + tv.tv_usec; 143 } 144 145 146 const BNetworkAddress& 147 BAbstractSocket::Local() const 148 { 149 return fLocal; 150 } 151 152 153 const BNetworkAddress& 154 BAbstractSocket::Peer() const 155 { 156 return fPeer; 157 } 158 159 160 size_t 161 BAbstractSocket::MaxTransmissionSize() const 162 { 163 return SSIZE_MAX; 164 } 165 166 167 status_t 168 BAbstractSocket::WaitForReadable(bigtime_t timeout) const 169 { 170 return _WaitFor(POLLIN, timeout); 171 } 172 173 174 status_t 175 BAbstractSocket::WaitForWritable(bigtime_t timeout) const 176 { 177 return _WaitFor(POLLOUT, timeout); 178 } 179 180 181 int 182 BAbstractSocket::Socket() const 183 { 184 return fSocket; 185 } 186 187 188 // #pragma mark - protected 189 190 191 status_t 192 BAbstractSocket::Bind(const BNetworkAddress& local, bool reuseAddr, int type) 193 { 194 fInitStatus = _OpenIfNeeded(local.Family(), type); 195 if (fInitStatus != B_OK) 196 return fInitStatus; 197 198 if (reuseAddr) { 199 int value = 1; 200 if (setsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR, &value, 201 sizeof(value)) != 0) { 202 return fInitStatus = errno; 203 } 204 } 205 206 if (bind(fSocket, local, local.Length()) != 0) 207 return fInitStatus = errno; 208 209 fIsBound = true; 210 _UpdateLocalAddress(); 211 return B_OK; 212 } 213 214 215 status_t 216 BAbstractSocket::Connect(const BNetworkAddress& peer, int type, 217 bigtime_t timeout) 218 { 219 Disconnect(); 220 221 fInitStatus = _OpenIfNeeded(peer.Family(), type); 222 if (fInitStatus == B_OK) 223 fInitStatus = SetTimeout(timeout); 224 225 if (fInitStatus == B_OK && !IsBound()) { 226 BNetworkAddress local; 227 local.SetToWildcard(peer.Family()); 228 fInitStatus = Bind(local, true); 229 } 230 if (fInitStatus != B_OK) 231 return fInitStatus; 232 233 BNetworkAddress normalized = peer; 234 if (connect(fSocket, normalized, normalized.Length()) != 0) { 235 TRACE("%p: connecting to %s: %s\n", this, 236 normalized.ToString().c_str(), strerror(errno)); 237 return fInitStatus = errno; 238 } 239 240 fIsConnected = true; 241 fPeer = normalized; 242 _UpdateLocalAddress(); 243 244 TRACE("%p: connected to %s (local %s)\n", this, peer.ToString().c_str(), 245 fLocal.ToString().c_str()); 246 247 return fInitStatus = B_OK; 248 } 249 250 251 status_t 252 BAbstractSocket::AcceptNext(int& _acceptedSocket, BNetworkAddress& _peer) 253 { 254 sockaddr_storage source; 255 socklen_t sourceLength = sizeof(sockaddr_storage); 256 257 int fd = accept(fSocket, (sockaddr*)&source, &sourceLength); 258 if (fd < 0) 259 return fd; 260 261 _peer.SetTo(source); 262 _acceptedSocket = fd; 263 return B_OK; 264 } 265 266 267 // #pragma mark - private 268 269 270 status_t 271 BAbstractSocket::_OpenIfNeeded(int family, int type) 272 { 273 if (fSocket >= 0) 274 return B_OK; 275 276 fSocket = socket(family, type, 0); 277 if (fSocket < 0) 278 return errno; 279 280 TRACE("%p: socket opened FD %d\n", this, fSocket); 281 return B_OK; 282 } 283 284 285 status_t 286 BAbstractSocket::_UpdateLocalAddress() 287 { 288 socklen_t localLength = sizeof(sockaddr_storage); 289 if (getsockname(fSocket, fLocal, &localLength) != 0) 290 return errno; 291 292 return B_OK; 293 } 294 295 296 status_t 297 BAbstractSocket::_WaitFor(int flags, bigtime_t timeout) const 298 { 299 if (fInitStatus != B_OK) 300 return fInitStatus; 301 302 int millis = 0; 303 if (timeout == B_INFINITE_TIMEOUT) 304 millis = -1; 305 if (timeout > 0) 306 millis = timeout / 1000; 307 308 struct pollfd entry; 309 entry.fd = Socket(); 310 entry.events = flags; 311 312 int result; 313 do { 314 result = poll(&entry, 1, millis); 315 } while (result == -1 && errno == EINTR); 316 if (result < 0) 317 return errno; 318 if (result == 0) 319 return millis > 0 ? B_TIMED_OUT : B_WOULD_BLOCK; 320 321 return B_OK; 322 } 323