|
| 1 | +#include <sys/socket.h> |
| 2 | + |
| 3 | +#include <errno.h> |
| 4 | +#include <netinet/in.h> |
| 5 | +#include <string.h> |
| 6 | + |
| 7 | +#include <wasi/api.h> |
| 8 | +#include <wasi/descriptor_table.h> |
| 9 | +#include <wasi/sockets_utils.h> |
| 10 | + |
| 11 | +int tcp_accept(tcp_socket_t *socket, bool client_blocking, |
| 12 | + struct sockaddr *addr, socklen_t *addrlen) |
| 13 | +{ |
| 14 | + output_sockaddr_t output_addr; |
| 15 | + if (!__wasi_sockets_utils__output_addr_validate( |
| 16 | + socket->family, addr, addrlen, &output_addr)) { |
| 17 | + errno = EINVAL; |
| 18 | + return -1; |
| 19 | + } |
| 20 | + |
| 21 | + tcp_socket_state_listening_t listener; |
| 22 | + if (socket->state.tag == TCP_SOCKET_STATE_LISTENING) { |
| 23 | + listener = socket->state.listening; |
| 24 | + } else { |
| 25 | + errno = EINVAL; |
| 26 | + return -1; |
| 27 | + } |
| 28 | + |
| 29 | + tcp_borrow_tcp_socket_t socket_borrow = |
| 30 | + tcp_borrow_tcp_socket(socket->socket); |
| 31 | + |
| 32 | + tcp_tuple3_own_tcp_socket_own_input_stream_own_output_stream_t |
| 33 | + client_and_io; |
| 34 | + network_error_code_t error; |
| 35 | + while (!tcp_method_tcp_socket_accept(socket_borrow, &client_and_io, |
| 36 | + &error)) { |
| 37 | + if (error == NETWORK_ERROR_CODE_WOULD_BLOCK) { |
| 38 | + if (socket->blocking) { |
| 39 | + poll_borrow_pollable_t pollable_borrow = |
| 40 | + poll_borrow_pollable( |
| 41 | + socket->socket_pollable); |
| 42 | + poll_method_pollable_block(pollable_borrow); |
| 43 | + } else { |
| 44 | + errno = EWOULDBLOCK; |
| 45 | + return -1; |
| 46 | + } |
| 47 | + } else { |
| 48 | + errno = __wasi_sockets_utils__map_error(error); |
| 49 | + return -1; |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + tcp_own_tcp_socket_t client = client_and_io.f0; |
| 54 | + tcp_borrow_tcp_socket_t client_borrow = tcp_borrow_tcp_socket(client); |
| 55 | + |
| 56 | + poll_own_pollable_t client_pollable = |
| 57 | + tcp_method_tcp_socket_subscribe(client_borrow); |
| 58 | + |
| 59 | + streams_own_input_stream_t input = client_and_io.f1; |
| 60 | + streams_borrow_input_stream_t input_borrow = |
| 61 | + streams_borrow_input_stream(input); |
| 62 | + poll_own_pollable_t input_pollable = |
| 63 | + streams_method_input_stream_subscribe(input_borrow); |
| 64 | + |
| 65 | + streams_own_output_stream_t output = client_and_io.f2; |
| 66 | + streams_borrow_output_stream_t output_borrow = |
| 67 | + streams_borrow_output_stream(output); |
| 68 | + poll_own_pollable_t output_pollable = |
| 69 | + streams_method_output_stream_subscribe(output_borrow); |
| 70 | + |
| 71 | + if (output_addr.tag != OUTPUT_SOCKADDR_NULL) { |
| 72 | + network_ip_socket_address_t remote_address; |
| 73 | + if (!tcp_method_tcp_socket_remote_address( |
| 74 | + client_borrow, &remote_address, &error)) { |
| 75 | + // TODO wasi-sockets: How to recover from this in a POSIX compatible way? |
| 76 | + abort(); |
| 77 | + } |
| 78 | + |
| 79 | + __wasi_sockets_utils__output_addr_write(remote_address, |
| 80 | + &output_addr); |
| 81 | + } |
| 82 | + |
| 83 | + descriptor_table_entry_t client_entry = { .tag = DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET, .tcp_socket = { |
| 84 | + .socket = client, |
| 85 | + .socket_pollable = client_pollable, |
| 86 | + .blocking = client_blocking, |
| 87 | + .fake_nodelay = socket->fake_nodelay, |
| 88 | + .family = socket->family, |
| 89 | + .state = { .tag = TCP_SOCKET_STATE_CONNECTED, .connected = { |
| 90 | + .input = input, |
| 91 | + .input_pollable = input_pollable, |
| 92 | + .output = output, |
| 93 | + .output_pollable = output_pollable, |
| 94 | + } }, |
| 95 | + } }; |
| 96 | + |
| 97 | + int client_fd; |
| 98 | + if (!descriptor_table_insert(client_entry, &client_fd)) { |
| 99 | + errno = EMFILE; |
| 100 | + return -1; |
| 101 | + } |
| 102 | + |
| 103 | + return client_fd; |
| 104 | +} |
| 105 | + |
| 106 | +int udp_accept(udp_socket_t *socket, bool client_blocking, |
| 107 | + struct sockaddr *addr, socklen_t *addrlen) |
| 108 | +{ |
| 109 | + // UDP doesn't support accept |
| 110 | + errno = EOPNOTSUPP; |
| 111 | + return -1; |
| 112 | +} |
| 113 | + |
| 114 | +int accept(int socket, struct sockaddr *restrict addr, |
| 115 | + socklen_t *restrict addrlen) |
| 116 | +{ |
| 117 | + return accept4(socket, addr, addrlen, 0); |
| 118 | +} |
| 119 | + |
| 120 | +int accept4(int socket, struct sockaddr *restrict addr, |
| 121 | + socklen_t *restrict addrlen, int flags) |
| 122 | +{ |
| 123 | + descriptor_table_entry_t *entry; |
| 124 | + if (!descriptor_table_get_ref(socket, &entry)) { |
| 125 | + errno = EBADF; |
| 126 | + return -1; |
| 127 | + } |
| 128 | + |
| 129 | + bool client_blocking = (flags & SOCK_NONBLOCK) == 0; |
| 130 | + // Ignore SOCK_CLOEXEC flag. That concept does not exist in WASI. |
| 131 | + |
| 132 | + switch (entry->tag) { |
| 133 | + case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: |
| 134 | + return tcp_accept(&entry->tcp_socket, client_blocking, addr, |
| 135 | + addrlen); |
| 136 | + case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: |
| 137 | + return udp_accept(&entry->udp_socket, client_blocking, addr, |
| 138 | + addrlen); |
| 139 | + default: |
| 140 | + errno = EOPNOTSUPP; |
| 141 | + return -1; |
| 142 | + } |
| 143 | +} |
0 commit comments