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