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