xref: /haiku/src/kits/network/libnetapi/AbstractSocket.cpp (revision 82bfaa954dcfd90582fb2c1a0e918971eea57091)
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 		// 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
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
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
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
298 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