diff options
| -rw-r--r-- | opentracker.c | 1 | ||||
| -rw-r--r-- | ot_socket.c | 20 | ||||
| -rw-r--r-- | ot_socket.h | 1 | ||||
| -rw-r--r-- | tests/t_socket.c | 43 |
4 files changed, 63 insertions, 2 deletions
diff --git a/opentracker.c b/opentracker.c index 27b2570..ec92fe0 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -339,6 +339,7 @@ static void listen_bind_one(ot_listen *entry) { | |||
| 339 | if (entry->proto == FLAG_TCP) { | 339 | if (entry->proto == FLAG_TCP) { |
| 340 | if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1) | 340 | if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1) |
| 341 | panic("socket_tcp_listen"); | 341 | panic("socket_tcp_listen"); |
| 342 | socket_defer_accept((int)entry->sock); | ||
| 342 | } else { | 343 | } else { |
| 343 | if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1) | 344 | if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1) |
| 344 | panic("socket_udp_bind"); | 345 | panic("socket_udp_bind"); |
diff --git a/ot_socket.c b/ot_socket.c index 47ed26d..78a16d2 100644 --- a/ot_socket.c +++ b/ot_socket.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <errno.h> | 8 | #include <errno.h> |
| 9 | #include <fcntl.h> | 9 | #include <fcntl.h> |
| 10 | #include <netinet/in.h> | 10 | #include <netinet/in.h> |
| 11 | #include <netinet/tcp.h> | ||
| 11 | #include <string.h> | 12 | #include <string.h> |
| 12 | #include <sys/socket.h> | 13 | #include <sys/socket.h> |
| 13 | #include <unistd.h> | 14 | #include <unistd.h> |
| @@ -120,6 +121,25 @@ static int socket_bind_v6(int s, const ot_ip6 ip, uint16_t port, socket_sharing | |||
| 120 | return bind(s, (struct sockaddr *)&sa, sizeof(sa)); | 121 | return bind(s, (struct sockaddr *)&sa, sizeof(sa)); |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 124 | /* Defer the listening socket reporting it to be ready to accept until enough | ||
| 125 | sensible data is available for an accept+read pair. Where available. */ | ||
| 126 | int socket_defer_accept(int s) { | ||
| 127 | #if defined(TCP_DEFER_ACCEPT) | ||
| 128 | int patience = OT_CLIENT_TIMEOUT; | ||
| 129 | |||
| 130 | return setsockopt(s, IPPROTO_TCP, TCP_DEFER_ACCEPT, &patience, sizeof(patience)); | ||
| 131 | #elif defined(SO_ACCEPTFILTER) | ||
| 132 | struct accept_filter_arg filter; | ||
| 133 | |||
| 134 | memset(&filter, 0, sizeof(filter)); | ||
| 135 | strcpy(filter.af_name, "httpready"); | ||
| 136 | return setsockopt(s, SOL_SOCKET, SO_ACCEPTFILTER, &filter, sizeof(filter)); | ||
| 137 | #else | ||
| 138 | (void)s; | ||
| 139 | return -1; | ||
| 140 | #endif | ||
| 141 | } | ||
| 142 | |||
| 123 | int socket_tcp_listen(const ot_ip6 ip, uint16_t port, socket_sharing sharing) { | 143 | int socket_tcp_listen(const ot_ip6 ip, uint16_t port, socket_sharing sharing) { |
| 124 | int s = socket_v6(SOCK_STREAM); | 144 | int s = socket_v6(SOCK_STREAM); |
| 125 | 145 | ||
diff --git a/ot_socket.h b/ot_socket.h index f73a5c9..5bbffe1 100644 --- a/ot_socket.h +++ b/ot_socket.h | |||
| @@ -38,6 +38,7 @@ int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 ds | |||
| 38 | int socket_is_connected(int s); | 38 | int socket_is_connected(int s); |
| 39 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); | 39 | int socket_accept(int s, ot_ip6 ip, uint16_t *port); |
| 40 | int socket_set_blocking(int s, int blocking); | 40 | int socket_set_blocking(int s, int blocking); |
| 41 | int socket_defer_accept(int s); | ||
| 41 | 42 | ||
| 42 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); | 43 | ssize_t socket_recv(int s, void *buf, size_t len, ot_ip6 ip, uint16_t *port); |
| 43 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); | 44 | ssize_t socket_send(int s, const void *buf, size_t len, const ot_ip6 ip, uint16_t port); |
diff --git a/tests/t_socket.c b/tests/t_socket.c index ab4b785..20a0c0a 100644 --- a/tests/t_socket.c +++ b/tests/t_socket.c | |||
| @@ -78,15 +78,17 @@ static int sock_flag(int s, int level, int option) { | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | /* Wait for one event, so a test never blocks forever on a lost packet */ | 80 | /* Wait for one event, so a test never blocks forever on a lost packet */ |
| 81 | static int wait_for(int s, short events) { | 81 | static int wait_for_ms(int s, short events, int timeout_ms) { |
| 82 | struct pollfd pfd; | 82 | struct pollfd pfd; |
| 83 | 83 | ||
| 84 | pfd.fd = s; | 84 | pfd.fd = s; |
| 85 | pfd.events = events; | 85 | pfd.events = events; |
| 86 | pfd.revents = 0; | 86 | pfd.revents = 0; |
| 87 | return poll(&pfd, 1, 2000) == 1; | 87 | return poll(&pfd, 1, timeout_ms) == 1; |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | static int wait_for(int s, short events) { return wait_for_ms(s, events, 2000); } | ||
| 91 | |||
| 90 | /* ------------------------------------------------------------ invariants */ | 92 | /* ------------------------------------------------------------ invariants */ |
| 91 | 93 | ||
| 92 | /* Asked for the wildcard address, because a socket bound to one specific | 94 | /* Asked for the wildcard address, because a socket bound to one specific |
| @@ -200,6 +202,42 @@ static void case_a_signal_ends_a_blocking_read(void) { | |||
| 200 | close(reader_socket); | 202 | close(reader_socket); |
| 201 | } | 203 | } |
| 202 | 204 | ||
| 205 | /* What a deferred accept is for is a descriptor that already has the request | ||
| 206 | in it, so what is checked is the silence before: a client that has | ||
| 207 | connected but said nothing must not make the listener readable. Kernels | ||
| 208 | that cannot do this say so, and then there is nothing to check. */ | ||
| 209 | static void case_deferred_accept(void) { | ||
| 210 | struct sockaddr_in6 address; | ||
| 211 | char ip[16]; | ||
| 212 | uint16_t port = 0; | ||
| 213 | int listener, client; | ||
| 214 | |||
| 215 | test_case("a connection that has sent nothing is held back from accept"); | ||
| 216 | listener = socket_tcp_listen(ip_loopback, 0, SOCKET_EXCLUSIVE); | ||
| 217 | CHECK(listener != -1); | ||
| 218 | if (socket_defer_accept(listener) == -1) { | ||
| 219 | close(listener); | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | bound_address(listener, ip, &port); | ||
| 223 | |||
| 224 | client = socket(AF_INET6, SOCK_STREAM, 0); | ||
| 225 | CHECK(client != -1); | ||
| 226 | memset(&address, 0, sizeof(address)); | ||
| 227 | address.sin6_family = AF_INET6; | ||
| 228 | address.sin6_port = htons(port); | ||
| 229 | memcpy(&address.sin6_addr, ip_loopback, 16); | ||
| 230 | CHECK_INT(connect(client, (struct sockaddr *)&address, sizeof(address)), 0); | ||
| 231 | |||
| 232 | CHECK_INT(wait_for_ms(listener, POLLIN, 300), 0); | ||
| 233 | |||
| 234 | CHECK_INT((int)write(client, "GET / HTTP/1.0\r\n\r\n", 18), 18); | ||
| 235 | CHECK_INT(wait_for(listener, POLLIN), 1); | ||
| 236 | |||
| 237 | close(client); | ||
| 238 | close(listener); | ||
| 239 | } | ||
| 240 | |||
| 203 | /* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however | 241 | /* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however |
| 204 | the kernel chose to report it. Linux fills a v4 mapped sockaddr_in6 here | 242 | the kernel chose to report it. Linux fills a v4 mapped sockaddr_in6 here |
| 205 | and the BSDs fill a plain sockaddr_in, so this is the case that tells the | 243 | and the BSDs fill a plain sockaddr_in, so this is the case that tells the |
| @@ -352,6 +390,7 @@ void test_socket(void) { | |||
| 352 | case_a_signal_ends_a_blocking_read(); | 390 | case_a_signal_ends_a_blocking_read(); |
| 353 | case_udp_v4mapped(); | 391 | case_udp_v4mapped(); |
| 354 | case_tcp_roundtrip(); | 392 | case_tcp_roundtrip(); |
| 393 | case_deferred_accept(); | ||
| 355 | case_connect_refused(); | 394 | case_connect_refused(); |
| 356 | case_bind_failure(); | 395 | case_bind_failure(); |
| 357 | #ifdef WANT_SYNC_LIVE | 396 | #ifdef WANT_SYNC_LIVE |
