1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <assert.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <netdb.h>
9 #include <poll.h>
10 #include <stdarg.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <threads.h>
15 #include <unistd.h>
16
17 #include <fuchsia/net/c/fidl.h>
18 #include <zircon/device/vfs.h>
19 #include <zircon/syscalls.h>
20
21 #include <lib/fdio/debug.h>
22 #include <lib/fdio/io.h>
23 #include <lib/fdio/util.h>
24 #include <lib/zxs/zxs.h>
25
26 #include "private.h"
27 #include "private-socket.h"
28 #include "unistd.h"
29
get_service_handle(const char * path,zx_handle_t * saved,mtx_t * lock,zx_handle_t * out)30 static zx_status_t get_service_handle(const char* path, zx_handle_t* saved,
31 mtx_t* lock, zx_handle_t* out) {
32 zx_status_t r;
33 zx_handle_t h0, h1;
34 mtx_lock(lock);
35 if (*saved == ZX_HANDLE_INVALID) {
36 if ((r = zx_channel_create(0, &h0, &h1)) != ZX_OK) {
37 mtx_unlock(lock);
38 return r;
39 }
40 if ((r = fdio_service_connect(path, h1)) != ZX_OK) {
41 mtx_unlock(lock);
42 zx_handle_close(h0);
43 return r;
44 }
45 *saved = h0;
46 }
47 *out = *saved;
48 mtx_unlock(lock);
49 return ZX_OK;
50 }
51
52 // This wrapper waits for the service to publish the service handle.
53 // TODO(ZX-1890): move to a better mechanism when available.
get_service_with_retries(const char * path,zx_handle_t * saved,mtx_t * lock,zx_handle_t * out)54 static zx_status_t get_service_with_retries(const char* path, zx_handle_t* saved,
55 mtx_t* lock, zx_handle_t* out) {
56 zx_status_t r;
57 unsigned retry = 0;
58 while ((r = get_service_handle(path, saved, lock, out)) == ZX_ERR_NOT_FOUND) {
59 if (retry >= 24) {
60 // 10-second timeout
61 return ZX_ERR_NOT_FOUND;
62 }
63 retry++;
64 zx_nanosleep(zx_deadline_after((retry < 8) ? ZX_MSEC(250) : ZX_MSEC(500)));
65 }
66 return r;
67 }
68
get_socket_provider(zx_handle_t * out)69 static zx_status_t get_socket_provider(zx_handle_t* out) {
70 static zx_handle_t saved = ZX_HANDLE_INVALID;
71 static mtx_t lock = MTX_INIT;
72 return get_service_with_retries("/svc/fuchsia.net.LegacySocketProvider", &saved, &lock, out);
73 }
74
75 __EXPORT
socket(int domain,int type,int protocol)76 int socket(int domain, int type, int protocol) {
77 fdio_t* io = NULL;
78
79 zx_handle_t socket_provider;
80 zx_status_t status = get_socket_provider(&socket_provider);
81 if (status != ZX_OK) {
82 return ERRNO(EIO);
83 }
84
85 zxs_socket_t socket = {};
86 status = zxs_socket(socket_provider, domain,
87 type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC), protocol, NULL,
88 0u, &socket);
89 if (status != ZX_OK) {
90 return STATUS(status);
91 }
92
93 if (socket.flags & ZXS_FLAG_DATAGRAM) {
94 io = fdio_socket_create_datagram(socket.socket, 0);
95 } else {
96 io = fdio_socket_create_stream(socket.socket, 0);
97 }
98
99 if (io == NULL) {
100 return ERRNO(EIO);
101 }
102
103 if (type & SOCK_NONBLOCK) {
104 io->ioflag |= IOFLAG_NONBLOCK;
105 }
106
107 // TODO(ZX-973): Implement CLOEXEC.
108 // if (type & SOCK_CLOEXEC) {
109 // }
110
111 int fd;
112 if ((fd = fdio_bind_to_fd(io, -1, 0)) < 0) {
113 io->ops->close(io);
114 fdio_release(io);
115 return ERRNO(EMFILE);
116 }
117 return fd;
118 }
119
120 __EXPORT
connect(int fd,const struct sockaddr * addr,socklen_t len)121 int connect(int fd, const struct sockaddr* addr, socklen_t len) {
122 const zxs_socket_t* socket = NULL;
123 fdio_t* io = fd_to_socket(fd, &socket);
124 if (io == NULL) {
125 return ERRNO(EBADF);
126 }
127
128 zx_status_t status = zxs_connect(socket, addr, len);
129 if (status == ZX_ERR_SHOULD_WAIT) {
130 io->ioflag |= IOFLAG_SOCKET_CONNECTING;
131 fdio_release(io);
132 return ERRNO(EINPROGRESS);
133 } else if (status == ZX_OK) {
134 io->ioflag |= IOFLAG_SOCKET_CONNECTED;
135 }
136 fdio_release(io);
137 return STATUS(status);
138 }
139
140 __EXPORT
bind(int fd,const struct sockaddr * addr,socklen_t len)141 int bind(int fd, const struct sockaddr* addr, socklen_t len) {
142 const zxs_socket_t* socket;
143 fdio_t* io = fd_to_socket(fd, &socket);
144 if (io == NULL) {
145 return ERRNO(EBADF);
146 }
147
148 zx_status_t status = zxs_bind(socket, addr, len);
149 fdio_release(io);
150 return STATUS(status);
151 }
152
153 __EXPORT
listen(int fd,int backlog)154 int listen(int fd, int backlog) {
155 const zxs_socket_t* socket;
156 fdio_t* io = fd_to_socket(fd, &socket);
157 if (io == NULL) {
158 return ERRNO(EBADF);
159 }
160
161 zx_status_t status = zxs_listen(socket, backlog);
162
163 if (status == ZX_OK) {
164 zxsio_t* sio = (zxsio_t*)io;
165 sio->flags |= ZXSIO_DID_LISTEN;
166 }
167
168 fdio_release(io);
169 return STATUS(status);
170 }
171
172 __EXPORT
accept4(int fd,struct sockaddr * restrict addr,socklen_t * restrict len,int flags)173 int accept4(int fd, struct sockaddr* restrict addr, socklen_t* restrict len,
174 int flags) {
175 if (flags & ~SOCK_NONBLOCK) {
176 return ERRNO(EINVAL);
177 }
178
179 const zxs_socket_t* socket = NULL;
180 fdio_t* io = fd_to_socket(fd, &socket);
181 if (io == NULL) {
182 return ERRNO(EBADF);
183 }
184
185 zxsio_t* sio = (zxsio_t*)io;
186 if (!(sio->flags & ZXSIO_DID_LISTEN)) {
187 fdio_release(io);
188 return ERROR(ZX_ERR_BAD_STATE);
189 }
190
191 int nfd = fdio_reserve_fd(0);
192 if (nfd < 0) {
193 return nfd;
194 }
195
196 size_t actual = 0u;
197 zxs_socket_t accepted;
198 memset(&accepted, 0, sizeof(accepted));
199 zx_status_t status = zxs_accept(socket, addr, len ? *len : 0u, &actual, &accepted);
200 fdio_release(io);
201 if (status == ZX_ERR_SHOULD_WAIT) {
202 fdio_release_reserved(nfd);
203 return ERRNO(EWOULDBLOCK);
204 } else if (status != ZX_OK) {
205 fdio_release_reserved(nfd);
206 return ERROR(status);
207 }
208
209 fdio_t* io2 = NULL;
210 if ((io2 = fdio_socket_create_stream(accepted.socket, IOFLAG_SOCKET_CONNECTED)) == NULL) {
211 fdio_release_reserved(nfd);
212 return ERROR(ZX_ERR_NO_RESOURCES);
213 }
214
215 if (flags & SOCK_NONBLOCK) {
216 io2->ioflag |= IOFLAG_NONBLOCK;
217 }
218
219 if (len != NULL) {
220 *len = actual;
221 }
222
223 if ((nfd = fdio_assign_reserved(nfd, io2)) < 0) {
224 io2->ops->close(io2);
225 fdio_release(io2);
226 return -1;
227 }
228 return nfd;
229 }
230
addrinfo_status_to_eai(int32_t status)231 static int addrinfo_status_to_eai(int32_t status) {
232 switch (status) {
233 case fuchsia_net_AddrInfoStatus_ok:
234 return 0;
235 case fuchsia_net_AddrInfoStatus_bad_flags:
236 return EAI_BADFLAGS;
237 case fuchsia_net_AddrInfoStatus_no_name:
238 return EAI_NONAME;
239 case fuchsia_net_AddrInfoStatus_again:
240 return EAI_AGAIN;
241 case fuchsia_net_AddrInfoStatus_fail:
242 return EAI_FAIL;
243 case fuchsia_net_AddrInfoStatus_no_data:
244 return EAI_NONAME;
245 case fuchsia_net_AddrInfoStatus_buffer_overflow:
246 return EAI_OVERFLOW;
247 case fuchsia_net_AddrInfoStatus_system_error:
248 return EAI_SYSTEM;
249 default:
250 // unknown status
251 return EAI_SYSTEM;
252 }
253 }
254
255 __EXPORT
getaddrinfo(const char * __restrict node,const char * __restrict service,const struct addrinfo * __restrict hints,struct addrinfo ** __restrict res)256 int getaddrinfo(const char* __restrict node,
257 const char* __restrict service,
258 const struct addrinfo* __restrict hints,
259 struct addrinfo** __restrict res) {
260 if ((node == NULL && service == NULL) || res == NULL) {
261 errno = EINVAL;
262 return EAI_SYSTEM;
263 }
264
265 zx_status_t r;
266 zx_handle_t sp;
267 r = get_socket_provider(&sp);
268 if (r != ZX_OK) {
269 errno = EIO;
270 return EAI_SYSTEM;
271 }
272
273 size_t node_size = 0;
274 if (node) {
275 node_size = strlen(node);
276 }
277 size_t service_size = 0;
278 if (service) {
279 service_size = strlen(service);
280 }
281
282
283 fuchsia_net_AddrInfoHints ht_storage, *ht;
284 memset(&ht_storage, 0, sizeof(ht_storage));
285 ht = &ht_storage;
286 if (hints == NULL) {
287 ht = NULL;
288 } else {
289 ht->flags = hints->ai_flags;
290 ht->family = hints->ai_family;
291 ht->sock_type = hints->ai_socktype;
292 ht->protocol = hints->ai_protocol;
293 }
294
295 fuchsia_net_AddrInfoStatus status = 0;
296 uint32_t nres = 0;
297 fuchsia_net_AddrInfo ai[4];
298 r = fuchsia_net_LegacySocketProviderGetAddrInfo(
299 sp, node, node_size, service, service_size, ht, &status, &nres, ai);
300
301 if (r != ZX_OK) {
302 errno = fdio_status_to_errno(r);
303 return EAI_SYSTEM;
304 }
305 if (status != fuchsia_net_AddrInfoStatus_ok) {
306 int eai = addrinfo_status_to_eai(status);
307 if (eai == EAI_SYSTEM) {
308 errno = EIO;
309 return EAI_SYSTEM;
310 }
311 return eai;
312 }
313 if (nres > 4) {
314 errno = EIO;
315 return EAI_SYSTEM;
316 }
317
318 struct res_entry {
319 struct addrinfo ai;
320 struct sockaddr_storage addr_storage;
321 };
322 struct res_entry* entry = calloc(nres, sizeof(struct res_entry));
323
324 for (uint32_t i = 0; i < nres; i++) {
325 entry[i].ai.ai_flags = ai[i].flags;
326 entry[i].ai.ai_family = ai[i].family;
327 entry[i].ai.ai_socktype = ai[i].sock_type;
328 entry[i].ai.ai_protocol = ai[i].protocol;
329 entry[i].ai.ai_addr = (struct sockaddr*) &entry[i].addr_storage;
330 entry[i].ai.ai_canonname = NULL; // TODO: support canonname
331 switch (entry[i].ai.ai_family) {
332 case AF_INET: {
333 struct sockaddr_in
334 * addr = (struct sockaddr_in*) entry[i].ai.ai_addr;
335 addr->sin_family = AF_INET;
336 addr->sin_port = htons(ai[i].port);
337 if (ai[i].addr.len > sizeof(ai[i].addr.val)) {
338 free(entry);
339 errno = EIO;
340 return EAI_SYSTEM;
341 }
342 memcpy(&addr->sin_addr, ai[i].addr.val, ai[i].addr.len);
343 entry[i].ai.ai_addrlen = sizeof(struct sockaddr_in);
344
345 break;
346 }
347
348 case AF_INET6: {
349 struct sockaddr_in6
350 * addr = (struct sockaddr_in6*) entry[i].ai.ai_addr;
351 addr->sin6_family = AF_INET6;
352 addr->sin6_port = htons(ai[i].port);
353 if (ai[i].addr.len > sizeof(ai[i].addr.val)) {
354 free(entry);
355 errno = EIO;
356 return EAI_SYSTEM;
357 }
358 memcpy(&addr->sin6_addr, ai[i].addr.val, ai[i].addr.len);
359 entry[i].ai.ai_addrlen = sizeof(struct sockaddr_in6);
360
361 break;
362 }
363
364 default: {
365 free(entry);
366 errno = EIO;
367 return EAI_SYSTEM;
368 }
369 }
370 }
371 struct addrinfo* next = NULL;
372 for (int i = nres - 1; i >= 0; --i) {
373 entry[i].ai.ai_next = next;
374 next = &entry[i].ai;
375 }
376 *res = next;
377
378 return 0;
379 }
380
381 __EXPORT
freeaddrinfo(struct addrinfo * res)382 void freeaddrinfo(struct addrinfo* res) {
383 free(res);
384 }
385
386 __EXPORT
getsockname(int fd,struct sockaddr * restrict addr,socklen_t * restrict len)387 int getsockname(int fd, struct sockaddr* restrict addr, socklen_t* restrict len) {
388 if (len == NULL || addr == NULL) {
389 return ERRNO(EINVAL);
390 }
391
392 const zxs_socket_t* socket = NULL;
393 fdio_t* io = fd_to_socket(fd, &socket);
394 if (io == NULL) {
395 return ERRNO(EBADF);
396 }
397
398 size_t actual = 0u;
399 zx_status_t status = zxs_getsockname(socket, addr, *len, &actual);
400 if (status == ZX_OK) {
401 *len = actual;
402 }
403 fdio_release(io);
404 return STATUS(status);
405 }
406
407 __EXPORT
getpeername(int fd,struct sockaddr * restrict addr,socklen_t * restrict len)408 int getpeername(int fd, struct sockaddr* restrict addr, socklen_t* restrict len) {
409 if (len == NULL || addr == NULL) {
410 return ERRNO(EINVAL);
411 }
412
413 const zxs_socket_t* socket = NULL;
414 fdio_t* io = fd_to_socket(fd, &socket);
415 if (io == NULL) {
416 return ERRNO(EBADF);
417 }
418
419 size_t actual = 0u;
420 zx_status_t status = zxs_getpeername(socket, addr, *len, &actual);
421 if (status == ZX_OK) {
422 *len = actual;
423 }
424 fdio_release(io);
425 return STATUS(status);
426 }
427
428 __EXPORT
getsockopt(int fd,int level,int optname,void * restrict optval,socklen_t * restrict optlen)429 int getsockopt(int fd, int level, int optname, void* restrict optval,
430 socklen_t* restrict optlen) {
431 const zxs_socket_t* socket = NULL;
432 fdio_t* io = fd_to_socket(fd, &socket);
433 if (io == NULL) {
434 return ERRNO(EBADF);
435 }
436
437 zx_status_t r;
438 if (level == SOL_SOCKET && optname == SO_ERROR) {
439 if (optval == NULL || optlen == NULL || *optlen < sizeof(int)) {
440 r = ZX_ERR_INVALID_ARGS;
441 } else {
442 zx_status_t status;
443 size_t actual = 0u;
444 r = zxs_getsockopt(socket, SOL_SOCKET, SO_ERROR, &status,
445 sizeof(status), &actual);
446 if (r == ZX_OK) {
447 int errno_ = 0;
448 if (status != ZX_OK) {
449 errno_ = fdio_status_to_errno(status);
450 }
451 *(int*)optval = errno_;
452 *optlen = sizeof(int);
453 }
454 }
455 } else {
456 if (optval == NULL || optlen == NULL) {
457 r = ZX_ERR_INVALID_ARGS;
458 } else {
459 size_t actual = 0u;
460 r = zxs_getsockopt(socket, level, optname, optval, *optlen, &actual);
461 if (r == ZX_OK) {
462 *optlen = actual;
463 }
464 }
465 }
466 fdio_release(io);
467
468 return STATUS(r);
469 }
470
471 __EXPORT
setsockopt(int fd,int level,int optname,const void * optval,socklen_t optlen)472 int setsockopt(int fd, int level, int optname, const void* optval,
473 socklen_t optlen) {
474 const zxs_socket_t* socket = NULL;
475 fdio_t* io = fd_to_socket(fd, &socket);
476 if (io == NULL) {
477 return ERRNO(EBADF);
478 }
479
480 zxs_option_t option = {
481 .level = level,
482 .name = optname,
483 .value = optval,
484 .length = optlen,
485 };
486
487 zx_status_t status = zxs_setsockopts(socket, &option, 1u);
488 fdio_release(io);
489 return STATUS(status);
490 }
491