xref: /haiku/src/system/libnetwork/socket.cpp (revision e705c841d784f0035a0ef3e9e96f6e017df16681)
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 	bool r5compatible = check_r5_compatibility();
196 	struct sockaddr haikuAddr;
197 
198 	sockaddr* address;
199 	socklen_t addressLength;
200 
201 	if (r5compatible && _address != NULL) {
202 		address = &haikuAddr;
203 		addressLength = sizeof(haikuAddr);
204 	} else {
205 		address = _address;
206 		addressLength = _addressLength ? *_addressLength : 0;
207 	}
208 
209 	int acceptSocket = _kern_accept(socket, address, &addressLength);
210 
211 	pthread_testcancel();
212 
213 	if (acceptSocket < 0) {
214 		errno = acceptSocket;
215 		return -1;
216 	}
217 
218 	if (r5compatible && _address != NULL) {
219 		convert_to_r5_sockaddr(_address, &haikuAddr);
220 		if (_addressLength != NULL)
221 			*_addressLength = sizeof(struct r5_sockaddr_in);
222 	} else if (_addressLength != NULL)
223 		*_addressLength = addressLength;
224 
225 	return acceptSocket;
226 }
227 
228 
229 extern "C" ssize_t
230 recv(int socket, void *data, size_t length, int flags)
231 {
232 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recv(socket, data, length, flags));
233 }
234 
235 
236 extern "C" ssize_t
237 recvfrom(int socket, void *data, size_t length, int flags,
238 	struct sockaddr *_address, socklen_t *_addressLength)
239 {
240 	bool r5compatible = check_r5_compatibility();
241 	struct sockaddr haikuAddr;
242 
243 	sockaddr* address;
244 	socklen_t addressLength;
245 
246 	if (r5compatible && _address != NULL) {
247 		address = &haikuAddr;
248 		addressLength = sizeof(haikuAddr);
249 	} else {
250 		address = _address;
251 		addressLength = _addressLength ? *_addressLength : 0;
252 	}
253 
254 	ssize_t bytesReceived = _kern_recvfrom(socket, data, length, flags,
255 		address, &addressLength);
256 
257 	pthread_testcancel();
258 
259 	if (bytesReceived < 0) {
260 		errno = bytesReceived;
261 		return -1;
262 	}
263 
264 	if (r5compatible) {
265 		convert_to_r5_sockaddr(_address, &haikuAddr);
266 		if (_addressLength != NULL)
267 			*_addressLength = sizeof(struct r5_sockaddr_in);
268 	} else if (_addressLength != NULL)
269 		*_addressLength = addressLength;
270 
271 	return bytesReceived;
272 }
273 
274 
275 extern "C" ssize_t
276 recvmsg(int socket, struct msghdr *message, int flags)
277 {
278 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recvmsg(socket, message, flags));
279 }
280 
281 
282 extern "C" ssize_t
283 send(int socket, const void *data, size_t length, int flags)
284 {
285 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_send(socket, data, length, flags));
286 }
287 
288 
289 extern "C" ssize_t
290 sendto(int socket, const void *data, size_t length, int flags,
291 	const struct sockaddr *address, socklen_t addressLength)
292 {
293 	struct sockaddr haikuAddr;
294 
295 	if (check_r5_compatibility()) {
296 		convert_from_r5_sockaddr(&haikuAddr, address);
297 		address = &haikuAddr;
298 		addressLength = sizeof(struct sockaddr_in);
299 	}
300 
301 	RETURN_AND_SET_ERRNO_TEST_CANCEL(
302 		_kern_sendto(socket, data, length, flags, address, addressLength));
303 }
304 
305 
306 extern "C" ssize_t
307 sendmsg(int socket, const struct msghdr *message, int flags)
308 {
309 	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sendmsg(socket, message, flags));
310 }
311 
312 
313 extern "C" int
314 getsockopt(int socket, int level, int option, void *value, socklen_t *_length)
315 {
316 	if (check_r5_compatibility()) {
317 		if (option == R5_SO_FIONREAD) {
318 			// there is no SO_FIONREAD in our stack; we're using FIONREAD
319 			// instead
320 			*_length = sizeof(int);
321 			return ioctl(socket, FIONREAD, value);
322 		}
323 
324 		convert_from_r5_sockopt(level, option);
325 	}
326 
327 	RETURN_AND_SET_ERRNO(_kern_getsockopt(socket, level, option, value,
328 		_length));
329 }
330 
331 
332 extern "C" int
333 setsockopt(int socket, int level, int option, const void *value,
334 	socklen_t length)
335 {
336 	if (check_r5_compatibility())
337 		convert_from_r5_sockopt(level, option);
338 
339 	RETURN_AND_SET_ERRNO(_kern_setsockopt(socket, level, option, value,
340 		length));
341 }
342 
343 
344 extern "C" int
345 getpeername(int socket, struct sockaddr *_address, socklen_t *_addressLength)
346 {
347 	bool r5compatible = check_r5_compatibility();
348 	struct sockaddr haikuAddr;
349 
350 	sockaddr* address;
351 	socklen_t addressLength;
352 
353 	if (r5compatible && _address != NULL) {
354 		address = &haikuAddr;
355 		addressLength = sizeof(haikuAddr);
356 	} else {
357 		address = _address;
358 		addressLength = _addressLength ? *_addressLength : 0;
359 	}
360 
361 	status_t error = _kern_getpeername(socket, address, &addressLength);
362 	if (error != B_OK) {
363 		errno = error;
364 		return -1;
365 	}
366 
367 	if (r5compatible) {
368 		convert_to_r5_sockaddr(_address, &haikuAddr);
369 		if (_addressLength != NULL)
370 			*_addressLength = sizeof(struct r5_sockaddr_in);
371 	} else if (_addressLength != NULL)
372 		*_addressLength = addressLength;
373 
374 	return 0;
375 }
376 
377 
378 extern "C" int
379 getsockname(int socket, struct sockaddr *_address, socklen_t *_addressLength)
380 {
381 	bool r5compatible = check_r5_compatibility();
382 	struct sockaddr haikuAddr;
383 
384 	sockaddr* address;
385 	socklen_t addressLength;
386 
387 	if (r5compatible && _address != NULL) {
388 		address = &haikuAddr;
389 		addressLength = sizeof(haikuAddr);
390 	} else {
391 		address = _address;
392 		addressLength = _addressLength ? *_addressLength : 0;
393 	}
394 
395 	status_t error = _kern_getsockname(socket, address, &addressLength);
396 	if (error != B_OK) {
397 		errno = error;
398 		return -1;
399 	}
400 
401 	if (r5compatible) {
402 		convert_to_r5_sockaddr(_address, &haikuAddr);
403 		if (_addressLength != NULL)
404 			*_addressLength = sizeof(struct r5_sockaddr_in);
405 	} else if (_addressLength != NULL)
406 		*_addressLength = addressLength;
407 
408 	return 0;
409 }
410 
411 
412 extern "C" int
413 sockatmark(int socket)
414 {
415 	RETURN_AND_SET_ERRNO(_kern_sockatmark(socket));
416 }
417 
418 
419 extern "C" int
420 socketpair(int family, int type, int protocol, int socketVector[2])
421 {
422 	RETURN_AND_SET_ERRNO(_kern_socketpair(family, type, protocol,
423 		socketVector));
424 }
425