xref: /haiku/src/system/libnetwork/socket.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
1 /*
2  * Copyright 2002-2008, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 /*!
7 	The socket API directly forwards all requests into the kernel stack
8 	via the networking stack driver.
9 */
10 
11 #include <r5_compatibility.h>
12 
13 #include <errno.h>
14 #include <netinet/in.h>
15 #include <pthread.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <unistd.h>
20 
21 #include <syscall_utils.h>
22 
23 #include <syscalls.h>
24 
25 
26 static void
27 convert_from_r5_sockaddr(struct sockaddr *_to, const struct sockaddr *_from)
28 {
29 	const r5_sockaddr_in *from = (r5_sockaddr_in *)_from;
30 	sockaddr_in *to = (sockaddr_in *)_to;
31 
32 	memset(to, 0, sizeof(sockaddr));
33 	to->sin_len = sizeof(sockaddr);
34 
35 	if (from == NULL)
36 		return;
37 
38 	if (from->sin_family == R5_AF_INET)
39 		to->sin_family = AF_INET;
40 	else
41 		to->sin_family = from->sin_family;
42 
43 	to->sin_port = from->sin_port;
44 	to->sin_addr.s_addr = from->sin_addr;
45 }
46 
47 
48 static void
49 convert_to_r5_sockaddr(struct sockaddr *_to,
50 	const struct sockaddr *_from)
51 {
52 	const sockaddr_in *from = (sockaddr_in *)_from;
53 	r5_sockaddr_in *to = (r5_sockaddr_in *)_to;
54 
55 	if (to == NULL)
56 		return;
57 
58 	memset(to, 0, sizeof(r5_sockaddr_in));
59 
60 	if (from->sin_family == AF_INET)
61 		to->sin_family = R5_AF_INET;
62 	else
63 		to->sin_family = from->sin_family;
64 
65 	to->sin_port = from->sin_port;
66 	to->sin_addr = from->sin_addr.s_addr;
67 }
68 
69 
70 static void
71 convert_from_r5_socket(int& family, int& type, int& protocol)
72 {
73 	switch (family) {
74 		case R5_AF_INET:
75 			family = AF_INET;
76 			break;
77 	}
78 
79 	switch (type) {
80 		case R5_SOCK_DGRAM:
81 			type = SOCK_DGRAM;
82 			break;
83 		case R5_SOCK_STREAM:
84 			type = SOCK_STREAM;
85 			break;
86 #if 0
87 		case R5_SOCK_RAW:
88 			type = SOCK_RAW;
89 			break;
90 #endif
91 	}
92 
93 	switch (protocol) {
94 		case R5_IPPROTO_UDP:
95 			protocol = IPPROTO_UDP;
96 			break;
97 		case R5_IPPROTO_TCP:
98 			protocol = IPPROTO_TCP;
99 			break;
100 		case R5_IPPROTO_ICMP:
101 			protocol = IPPROTO_ICMP;
102 			break;
103 	}
104 }
105 
106 
107 static void
108 convert_from_r5_sockopt(int& level, int& option)
109 {
110 	if (level == R5_SOL_SOCKET)
111 		level = SOL_SOCKET;
112 
113 	switch (option) {
114 		case R5_SO_DEBUG:
115 			option = SO_DEBUG;
116 			break;
117 		case R5_SO_REUSEADDR:
118 			option = SO_REUSEADDR;
119 			break;
120 		case R5_SO_NONBLOCK:
121 			option = SO_NONBLOCK;
122 			break;
123 		case R5_SO_REUSEPORT:
124 			option = SO_REUSEPORT;
125 			break;
126 		case R5_SO_FIONREAD:
127 			// there is no SO_FIONREAD
128 			option = -1;
129 			break;
130 	}
131 }
132 
133 
134 // #pragma mark -
135 
136 
137 extern "C" int
138 socket(int family, int type, int protocol)
139 {
140 	if (check_r5_compatibility())
141 		convert_from_r5_socket(family, type, protocol);
142 
143 	RETURN_AND_SET_ERRNO(_kern_socket(family, type, protocol));
144 }
145 
146 
147 extern "C" int
148 bind(int socket, const struct sockaddr *address, socklen_t addressLength)
149 {
150 	struct sockaddr haikuAddr;
151 
152 	if (check_r5_compatibility()) {
153 		convert_from_r5_sockaddr(&haikuAddr, address);
154 		address = &haikuAddr;
155 		addressLength = sizeof(struct sockaddr_in);
156 	}
157 
158 	RETURN_AND_SET_ERRNO(_kern_bind(socket, address, addressLength));
159 }
160 
161 
162 extern "C" int
163 shutdown(int socket, int how)
164 {
165 	RETURN_AND_SET_ERRNO(_kern_shutdown_socket(socket, how));
166 }
167 
168 
169 extern "C" int
170 connect(int socket, const struct sockaddr *address, socklen_t addressLength)
171 {
172 	struct sockaddr haikuAddr;
173 
174 	if (check_r5_compatibility()) {
175 		convert_from_r5_sockaddr(&haikuAddr, address);
176 		address = &haikuAddr;
177 		addressLength = sizeof(struct sockaddr_in);
178 	}
179 
180 	RETURN_AND_SET_ERRNO_TEST_CANCEL(
181 		_kern_connect(socket, address, addressLength));
182 }
183 
184 
185 extern "C" int
186 listen(int socket, int backlog)
187 {
188 	RETURN_AND_SET_ERRNO(_kern_listen(socket, backlog));
189 }
190 
191 
192 extern "C" int
193 accept(int socket, struct sockaddr *_address, socklen_t *_addressLength)
194 {
195 	return accept4(socket, _address, _addressLength, 0);
196 }
197 
198 
199 extern "C" int
200 accept4(int socket, struct sockaddr *_address, socklen_t *_addressLength, int flags)
201 {
202 	bool r5compatible = check_r5_compatibility();
203 	struct sockaddr haikuAddr;
204 
205 	sockaddr* address;
206 	socklen_t addressLength;
207 
208 	if (r5compatible && _address != NULL) {
209 		address = &haikuAddr;
210 		addressLength = sizeof(haikuAddr);
211 	} else {
212 		address = _address;
213 		addressLength = _addressLength ? *_addressLength : 0;
214 	}
215 
216 	int acceptSocket = _kern_accept(socket, address, &addressLength, flags);
217 
218 	pthread_testcancel();
219 
220 	if (acceptSocket < 0) {
221 		errno = acceptSocket;
222 		return -1;
223 	}
224 
225 	if (r5compatible && _address != NULL) {
226 		convert_to_r5_sockaddr(_address, &haikuAddr);
227 		if (_addressLength != NULL)
228 			*_addressLength = sizeof(struct r5_sockaddr_in);
229 	} else if (_addressLength != NULL)
230 		*_addressLength = addressLength;
231 
232 	return acceptSocket;
233 }
234 
235 
236 extern "C" ssize_t
237 recv(int socket, void *data, size_t length, int flags)
238 {
239 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recv(socket, data, length, flags));
240 }
241 
242 
243 extern "C" ssize_t
244 recvfrom(int socket, void *data, size_t length, int flags,
245 	struct sockaddr *_address, socklen_t *_addressLength)
246 {
247 	bool r5compatible = check_r5_compatibility();
248 	struct sockaddr haikuAddr;
249 
250 	sockaddr* address;
251 	socklen_t addressLength;
252 
253 	if (r5compatible && _address != NULL) {
254 		address = &haikuAddr;
255 		addressLength = sizeof(haikuAddr);
256 	} else {
257 		address = _address;
258 		addressLength = _addressLength ? *_addressLength : 0;
259 	}
260 
261 	ssize_t bytesReceived = _kern_recvfrom(socket, data, length, flags,
262 		address, &addressLength);
263 
264 	pthread_testcancel();
265 
266 	if (bytesReceived < 0) {
267 		errno = bytesReceived;
268 		return -1;
269 	}
270 
271 	if (r5compatible) {
272 		convert_to_r5_sockaddr(_address, &haikuAddr);
273 		if (_addressLength != NULL)
274 			*_addressLength = sizeof(struct r5_sockaddr_in);
275 	} else if (_addressLength != NULL)
276 		*_addressLength = addressLength;
277 
278 	return bytesReceived;
279 }
280 
281 
282 extern "C" ssize_t
283 recvmsg(int socket, struct msghdr *message, int flags)
284 {
285 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recvmsg(socket, message, flags));
286 }
287 
288 
289 extern "C" ssize_t
290 send(int socket, const void *data, size_t length, int flags)
291 {
292 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_send(socket, data, length, flags));
293 }
294 
295 
296 extern "C" ssize_t
297 sendto(int socket, const void *data, size_t length, int flags,
298 	const struct sockaddr *address, socklen_t addressLength)
299 {
300 	struct sockaddr haikuAddr;
301 
302 	if (check_r5_compatibility()) {
303 		convert_from_r5_sockaddr(&haikuAddr, address);
304 		address = &haikuAddr;
305 		addressLength = sizeof(struct sockaddr_in);
306 	}
307 
308 	RETURN_AND_SET_ERRNO_TEST_CANCEL(
309 		_kern_sendto(socket, data, length, flags, address, addressLength));
310 }
311 
312 
313 extern "C" ssize_t
314 sendmsg(int socket, const struct msghdr *message, int flags)
315 {
316 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sendmsg(socket, message, flags));
317 }
318 
319 
320 extern "C" int
321 getsockopt(int socket, int level, int option, void *value, socklen_t *_length)
322 {
323 	if (check_r5_compatibility()) {
324 		if (option == R5_SO_FIONREAD) {
325 			// there is no SO_FIONREAD in our stack; we're using FIONREAD
326 			// instead
327 			*_length = sizeof(int);
328 			return ioctl(socket, FIONREAD, value);
329 		}
330 
331 		convert_from_r5_sockopt(level, option);
332 	}
333 
334 	RETURN_AND_SET_ERRNO(_kern_getsockopt(socket, level, option, value,
335 		_length));
336 }
337 
338 
339 extern "C" int
340 setsockopt(int socket, int level, int option, const void *value,
341 	socklen_t length)
342 {
343 	if (check_r5_compatibility())
344 		convert_from_r5_sockopt(level, option);
345 
346 	RETURN_AND_SET_ERRNO(_kern_setsockopt(socket, level, option, value,
347 		length));
348 }
349 
350 
351 extern "C" int
352 getpeername(int socket, struct sockaddr *_address, socklen_t *_addressLength)
353 {
354 	bool r5compatible = check_r5_compatibility();
355 	struct sockaddr haikuAddr;
356 
357 	sockaddr* address;
358 	socklen_t addressLength;
359 
360 	if (r5compatible && _address != NULL) {
361 		address = &haikuAddr;
362 		addressLength = sizeof(haikuAddr);
363 	} else {
364 		address = _address;
365 		addressLength = _addressLength ? *_addressLength : 0;
366 	}
367 
368 	status_t error = _kern_getpeername(socket, address, &addressLength);
369 	if (error != B_OK) {
370 		errno = error;
371 		return -1;
372 	}
373 
374 	if (r5compatible) {
375 		convert_to_r5_sockaddr(_address, &haikuAddr);
376 		if (_addressLength != NULL)
377 			*_addressLength = sizeof(struct r5_sockaddr_in);
378 	} else if (_addressLength != NULL)
379 		*_addressLength = addressLength;
380 
381 	return 0;
382 }
383 
384 
385 extern "C" int
386 getsockname(int socket, struct sockaddr *_address, socklen_t *_addressLength)
387 {
388 	bool r5compatible = check_r5_compatibility();
389 	struct sockaddr haikuAddr;
390 
391 	sockaddr* address;
392 	socklen_t addressLength;
393 
394 	if (r5compatible && _address != NULL) {
395 		address = &haikuAddr;
396 		addressLength = sizeof(haikuAddr);
397 	} else {
398 		address = _address;
399 		addressLength = _addressLength ? *_addressLength : 0;
400 	}
401 
402 	status_t error = _kern_getsockname(socket, address, &addressLength);
403 	if (error != B_OK) {
404 		errno = error;
405 		return -1;
406 	}
407 
408 	if (r5compatible) {
409 		convert_to_r5_sockaddr(_address, &haikuAddr);
410 		if (_addressLength != NULL)
411 			*_addressLength = sizeof(struct r5_sockaddr_in);
412 	} else if (_addressLength != NULL)
413 		*_addressLength = addressLength;
414 
415 	return 0;
416 }
417 
418 
419 extern "C" int
420 sockatmark(int socket)
421 {
422 	RETURN_AND_SET_ERRNO(_kern_sockatmark(socket));
423 }
424 
425 
426 extern "C" int
427 socketpair(int family, int type, int protocol, int socketVector[2])
428 {
429 	RETURN_AND_SET_ERRNO(_kern_socketpair(family, type, protocol,
430 		socketVector));
431 }
432