-
Notifications
You must be signed in to change notification settings - Fork 3
net,server: suppress SIGPIPE on writes to a departed peer #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -393,6 +393,55 @@ static int keepalive_after_split_trailer(uint16_t port) | |
| return 0; | ||
| } | ||
|
|
||
| /* Set by note_sigpipe(); a server write must leave it clear. */ | ||
| static volatile sig_atomic_t g_sigpipe_raised; | ||
|
|
||
| static void note_sigpipe(int sig) | ||
| { | ||
| (void)sig; | ||
| g_sigpipe_raised = 1; | ||
| } | ||
|
|
||
| /* Queue a full request, then close the peer: the queued bytes still reach the | ||
| * handler's response write. Own server, so no constraint on the accept loop. */ | ||
| static int no_sigpipe_on_response(void) | ||
| { | ||
| static const char http_req[] = | ||
| "GET /nope HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n"; | ||
| WolfCertServerCfgSrv cfg = { | ||
| .protocol = WOLFCERT_PROTO_EST, | ||
| .bind_host = "127.0.0.1", .bind_port = 0, | ||
| }; | ||
| WolfCertServer* srv = NULL; | ||
| struct sigaction sa, old; | ||
| int sv[2]; | ||
|
|
||
| REQUIRE(wolfcert_server_start(&cfg, &srv) == WOLFCERT_OK); | ||
| REQUIRE(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); | ||
| REQUIRE(write(sv[1], http_req, sizeof(http_req) - 1) | ||
| == (ssize_t)(sizeof(http_req) - 1)); | ||
| close(sv[1]); | ||
|
|
||
| /* Catch, not ignore, so "not raised" differs from "raised and | ||
| * swallowed"; main() ignores it for the other cases. */ | ||
| memset(&sa, 0, sizeof(sa)); | ||
| sa.sa_handler = note_sigpipe; | ||
| sigemptyset(&sa.sa_mask); | ||
| REQUIRE(sigaction(SIGPIPE, &sa, &old) == 0); | ||
| g_sigpipe_raised = 0; | ||
|
|
||
| /* An unwritable peer errors either way; the signal is the assertion. */ | ||
| (void)wolfcert_server_serve_fd(srv, sv[0]); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, and the inverse of the It is weaker than the unit assertion, since -11 comes back whether or not the write succeeded, and the five sibling cases in this file already assert that requests get responses - a refactor that stopped the server responding would fail loudly there anyway. Take it or leave it. |
||
|
|
||
| REQUIRE(sigaction(SIGPIPE, &old, NULL) == 0); | ||
| close(sv[0]); | ||
| wolfcert_server_free(srv); | ||
|
|
||
| REQUIRE(g_sigpipe_raised == 0); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| int main(void) | ||
| { | ||
| /* A truncated request makes the server respond and close while the | ||
|
|
@@ -422,6 +471,8 @@ int main(void) | |
| rc = accept_multisegment_chunked_body(port); | ||
| if (rc == 0) | ||
| rc = keepalive_after_split_trailer(port); | ||
| if (rc == 0) | ||
| rc = no_sigpipe_on_response(); | ||
|
|
||
| wolfcert_server_stop(srv); | ||
| pthread_join(tid, NULL); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,9 +31,12 @@ | |
|
|
||
| #include <wolfcert/wolfcert.h> | ||
| #include <wolfcert/http.h> | ||
| #include "internal.h" | ||
|
|
||
| #include <arpa/inet.h> | ||
| #include <netinet/in.h> | ||
| #include <signal.h> | ||
| #include <stdint.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <sys/socket.h> | ||
|
|
@@ -55,6 +58,73 @@ static long mono_ms(void) | |
| return (long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | ||
| } | ||
|
|
||
| /* Set by note_sigpipe(); a library write must leave it clear. */ | ||
| static volatile sig_atomic_t g_sigpipe_raised; | ||
|
|
||
| static void note_sigpipe(int sig) | ||
| { | ||
| (void)sig; | ||
| g_sigpipe_raised = 1; | ||
| } | ||
|
|
||
| /* Write to a socketpair whose peer is closed, handler armed. `nosigpipe` | ||
| * applies the socket option wolfcert_posix_connect() sets. */ | ||
| static int write_to_dead_peer(int nosigpipe, int* out_rc) | ||
| { | ||
| static const uint8_t body[256] = { 0 }; | ||
| struct sigaction sa, old; | ||
| int sv[2]; | ||
|
|
||
| /* Before closing the peer: setsockopt(SO_NOSIGPIPE) fails with EINVAL | ||
| * once the peer is gone. */ | ||
| REQUIRE(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); | ||
|
Frauschi marked this conversation as resolved.
|
||
| if (nosigpipe) | ||
| wolfcert_sock_nosigpipe(sv[0]); | ||
| close(sv[1]); | ||
|
|
||
| /* Catch, not ignore, so "not raised" differs from "raised and | ||
| * swallowed"; CI runs every test with SIGPIPE ignored. */ | ||
| memset(&sa, 0, sizeof(sa)); | ||
| sa.sa_handler = note_sigpipe; | ||
| sigemptyset(&sa.sa_mask); | ||
| REQUIRE(sigaction(SIGPIPE, &sa, &old) == 0); | ||
| g_sigpipe_raised = 0; | ||
|
|
||
| *out_rc = wolfcert_posix_transport.write(NULL, (void*)(intptr_t)sv[0], | ||
| body, sizeof(body), 0); | ||
|
|
||
| REQUIRE(sigaction(SIGPIPE, &old, NULL) == 0); | ||
| close(sv[0]); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /* Both arms in force. WOLFCERT_ERR_IO pins that the write reached send(). */ | ||
| static int test_no_sigpipe_on_dead_peer(void) | ||
| { | ||
| int rc = 0; | ||
|
|
||
| REQUIRE(write_to_dead_peer(1, &rc) == 0); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The split fixes the You are right that it cannot be isolated through the transport write, since the transport always passes the flag. A direct Not blocking, but it is the half of my earlier comment that is still open, so I would rather it landed here than in a follow-up. |
||
| REQUIRE(g_sigpipe_raised == 0); | ||
| REQUIRE(rc == WOLFCERT_ERR_IO); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| #ifdef MSG_NOSIGNAL | ||
| /* No socket option: the send flag alone must suppress the signal. */ | ||
| static int test_send_flag_alone_suppresses(void) | ||
| { | ||
| int rc = 0; | ||
|
|
||
| REQUIRE(write_to_dead_peer(0, &rc) == 0); | ||
| REQUIRE(g_sigpipe_raised == 0); | ||
| REQUIRE(rc == WOLFCERT_ERR_IO); | ||
|
|
||
| return 0; | ||
| } | ||
| #endif | ||
|
|
||
| int main(void) | ||
| { | ||
| /* Success path with a positive timeout: stand up a loopback listener and | ||
|
|
@@ -88,6 +158,14 @@ int main(void) | |
| REQUIRE(fd2 < 0); | ||
| REQUIRE(elapsed < 3000); | ||
|
|
||
| if (test_no_sigpipe_on_dead_peer()) | ||
| return 1; | ||
|
|
||
| #ifdef MSG_NOSIGNAL | ||
| if (test_send_flag_alone_suppresses()) | ||
| return 1; | ||
| #endif | ||
|
|
||
| printf("OK (unreachable connect returned in %ldms)\n", elapsed); | ||
| return 0; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.