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