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
BAbstractSocket()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
BAbstractSocket(const BAbstractSocket & other)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
~BAbstractSocket()51 BAbstractSocket::~BAbstractSocket()
52 {
53 Disconnect();
54 }
55
56
57 status_t
InitCheck() const58 BAbstractSocket::InitCheck() const
59 {
60 return fInitStatus;
61 }
62
63
64 bool
IsBound() const65 BAbstractSocket::IsBound() const
66 {
67 return fIsBound;
68 }
69
70
71 bool
IsListening() const72 BAbstractSocket::IsListening() const
73 {
74 return fIsListening;
75 }
76
77
78 bool
IsConnected() const79 BAbstractSocket::IsConnected() const
80 {
81 return fIsConnected;
82 }
83
84
85 status_t
Listen(int backlog)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
Disconnect()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
SetTimeout(bigtime_t timeout)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
Timeout() const135 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&
Local() const147 BAbstractSocket::Local() const
148 {
149 return fLocal;
150 }
151
152
153 const BNetworkAddress&
Peer() const154 BAbstractSocket::Peer() const
155 {
156 return fPeer;
157 }
158
159
160 size_t
MaxTransmissionSize() const161 BAbstractSocket::MaxTransmissionSize() const
162 {
163 return SSIZE_MAX;
164 }
165
166
167 status_t
WaitForReadable(bigtime_t timeout) const168 BAbstractSocket::WaitForReadable(bigtime_t timeout) const
169 {
170 return _WaitFor(POLLIN, timeout);
171 }
172
173
174 status_t
WaitForWritable(bigtime_t timeout) const175 BAbstractSocket::WaitForWritable(bigtime_t timeout) const
176 {
177 return _WaitFor(POLLOUT, timeout);
178 }
179
180
181 int
Socket() const182 BAbstractSocket::Socket() const
183 {
184 return fSocket;
185 }
186
187
188 // #pragma mark - protected
189
190
191 status_t
Bind(const BNetworkAddress & local,bool reuseAddr,int type)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
Connect(const BNetworkAddress & peer,int type,bigtime_t timeout)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 // Bind to ADDR_ANY, if the address family supports it
227 BNetworkAddress local;
228 if (local.SetToWildcard(peer.Family()) == B_OK)
229 fInitStatus = Bind(local, true);
230 }
231 if (fInitStatus != B_OK)
232 return fInitStatus;
233
234 BNetworkAddress normalized = peer;
235 if (connect(fSocket, normalized, normalized.Length()) != 0) {
236 TRACE("%p: connecting to %s: %s\n", this,
237 normalized.ToString().c_str(), strerror(errno));
238 return fInitStatus = errno;
239 }
240
241 fIsConnected = true;
242 fPeer = normalized;
243 _UpdateLocalAddress();
244
245 TRACE("%p: connected to %s (local %s)\n", this, peer.ToString().c_str(),
246 fLocal.ToString().c_str());
247
248 return fInitStatus = B_OK;
249 }
250
251
252 status_t
AcceptNext(int & _acceptedSocket,BNetworkAddress & _peer)253 BAbstractSocket::AcceptNext(int& _acceptedSocket, BNetworkAddress& _peer)
254 {
255 sockaddr_storage source;
256 socklen_t sourceLength = sizeof(sockaddr_storage);
257
258 int fd = accept(fSocket, (sockaddr*)&source, &sourceLength);
259 if (fd < 0)
260 return fd;
261
262 _peer.SetTo(source);
263 _acceptedSocket = fd;
264 return B_OK;
265 }
266
267
268 // #pragma mark - private
269
270
271 status_t
_OpenIfNeeded(int family,int type)272 BAbstractSocket::_OpenIfNeeded(int family, int type)
273 {
274 if (fSocket >= 0)
275 return B_OK;
276
277 fSocket = socket(family, type, 0);
278 if (fSocket < 0)
279 return errno;
280
281 TRACE("%p: socket opened FD %d\n", this, fSocket);
282 return B_OK;
283 }
284
285
286 status_t
_UpdateLocalAddress()287 BAbstractSocket::_UpdateLocalAddress()
288 {
289 socklen_t localLength = sizeof(sockaddr_storage);
290 if (getsockname(fSocket, fLocal, &localLength) != 0)
291 return errno;
292
293 return B_OK;
294 }
295
296
297 status_t
_WaitFor(int flags,bigtime_t timeout) const298 BAbstractSocket::_WaitFor(int flags, bigtime_t timeout) const
299 {
300 if (fInitStatus != B_OK)
301 return fInitStatus;
302
303 int millis = 0;
304 if (timeout == B_INFINITE_TIMEOUT)
305 millis = -1;
306 if (timeout > 0)
307 millis = timeout / 1000;
308
309 struct pollfd entry;
310 entry.fd = Socket();
311 entry.events = flags;
312
313 int result;
314 do {
315 result = poll(&entry, 1, millis);
316 } while (result == -1 && errno == EINTR);
317 if (result < 0)
318 return errno;
319 if (result == 0)
320 return millis > 0 ? B_TIMED_OUT : B_WOULD_BLOCK;
321
322 return B_OK;
323 }
324