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