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
26static void
27convert_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
48static void
49convert_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
70static void
71convert_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
107static void
108convert_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
137extern "C" int
138socket(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
147extern "C" int
148bind(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
162extern "C" int
163shutdown(int socket, int how)
164{
165	RETURN_AND_SET_ERRNO(_kern_shutdown_socket(socket, how));
166}
167
168
169extern "C" int
170connect(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
185extern "C" int
186listen(int socket, int backlog)
187{
188	RETURN_AND_SET_ERRNO(_kern_listen(socket, backlog));
189}
190
191
192extern "C" int
193accept(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
229extern "C" ssize_t
230recv(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
236extern "C" ssize_t
237recvfrom(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
275extern "C" ssize_t
276recvmsg(int socket, struct msghdr *message, int flags)
277{
278	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_recvmsg(socket, message, flags));
279}
280
281
282extern "C" ssize_t
283send(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
289extern "C" ssize_t
290sendto(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
306extern "C" ssize_t
307sendmsg(int socket, const struct msghdr *message, int flags)
308{
309	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sendmsg(socket, message, flags));
310}
311
312
313extern "C" int
314getsockopt(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
332extern "C" int
333setsockopt(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
344extern "C" int
345getpeername(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
378extern "C" int
379getsockname(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
412extern "C" int
413sockatmark(int socket)
414{
415	RETURN_AND_SET_ERRNO(_kern_sockatmark(socket));
416}
417
418
419extern "C" int
420socketpair(int family, int type, int protocol, int socketVector[2])
421{
422	RETURN_AND_SET_ERRNO(_kern_socketpair(family, type, protocol,
423		socketVector));
424}
425