From fdcf92be04d4d447c471e895e3f3e370cbb9d027 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 22 Oct 2026 22:59:44 +0200 Subject: [PATCH] Prevent SIGPIPE from terminating embedded STCP transports --- lib/socket_compat.c | 22 ++++++++++++++++++++++ lib/socket_compat.h | 5 +++++ src/transport_layer/stcp.c | 4 ++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/socket_compat.c b/lib/socket_compat.c index 027d53e1..3ce7fad2 100644 --- a/lib/socket_compat.c +++ b/lib/socket_compat.c @@ -4,6 +4,7 @@ #include "socket_compat.h" #include "debug_config.h" +#include #ifdef _WIN32 // ==================== Windows Implementation ==================== @@ -211,6 +212,27 @@ int socket_set_mark(socket_t sock, int mark) { #endif } +/* Ошибки send остаются у вызывающего; закрытый пир не завершает процесс сигналом. */ +ssize_t socket_send(socket_t sock, const void* buf, size_t len) { +#ifdef _WIN32 + if (len > INT_MAX) { WSASetLastError(WSAEMSGSIZE); return SOCKET_ERROR_CODE; } + return send(sock, (const char*)buf, (int)len, 0); +#elif defined(MSG_NOSIGNAL) + return send(sock, buf, len, MSG_NOSIGNAL); +#elif defined(SO_NOSIGPIPE) + int enabled = 1; + if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &enabled, sizeof(enabled)) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "cannot disable SIGPIPE for socket=%d: %s", (int)sock, strerror(errno)); + return SOCKET_ERROR_CODE; + } + return send(sock, buf, len, 0); +#else + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "safe TCP send is unsupported on this platform"); + errno = ENOTSUP; + return SOCKET_ERROR_CODE; +#endif +} + ssize_t socket_sendto(socket_t sock, const void* buf, size_t len, const struct sockaddr* dest, socklen_t dest_len) { #ifdef _WIN32 diff --git a/lib/socket_compat.h b/lib/socket_compat.h index a0b14ff7..72062540 100644 --- a/lib/socket_compat.h +++ b/lib/socket_compat.h @@ -75,6 +75,11 @@ int socket_bind_to_device(socket_t sock, const char* ifname); // SO_MARK в значение mark; 0 / -1. Без поддержки опции, в том числе Windows, возвращает -1. int socket_set_mark(socket_t sock, int mark); +/* Один TCP send без SIGPIPE и изменения обработчиков процесса. Буфер заимствован; + * возвращает число байт либо SOCKET_ERROR_CODE, включая EAGAIN. Ошибку логирует владелец. + * MSG_NOSIGNAL либо SO_NOSIGPIPE; без поддержки возвращает ENOTSUP. Windows: len <= INT_MAX. */ +ssize_t socket_send(socket_t sock, const void* buf, size_t len); + // Один sendto/recvfrom с flags=0: число байт или SOCKET_ERROR_CODE; errno/WSA-код читать через socket_get_error. // буферами владеет вызывающий; для Windows len должен помещаться в int. dest_len — размер адреса назначения; // src_len при приёме — входная ёмкость/выходная длина адреса (может быть NULL по правилам recvfrom ОС). diff --git a/src/transport_layer/stcp.c b/src/transport_layer/stcp.c index 5550a4d3..38182505 100644 --- a/src/transport_layer/stcp.c +++ b/src/transport_layer/stcp.c @@ -186,7 +186,7 @@ int stcp_send_raw(struct stcp_conn *c, uint8_t *data, size_t len) { c->wire_bytes += len; return 1; } - ssize_t sent = send(c->sock, data, len, 0); + ssize_t sent = socket_send(c->sock, data, len); if (sent < 0) { int err = socket_get_error(); if (err != ERR_AGAIN && err != ERR_WOULDBLOCK) { @@ -234,7 +234,7 @@ void stcp_write_cb(socket_t sock, void *arg) { struct stcp_conn *c = (struct stcp_conn *)arg; if (c->close_err) { if (!c->close_call_soon) stcp_conn_do_close(c, c->close_err); return; } if (!c->send_buf) { uasync_set_socket_write(c->ua, c->socket_id, 0); return; } - ssize_t sent = send(sock, c->send_buf + c->send_offset, c->send_len - c->send_offset, 0); + ssize_t sent = socket_send(sock, c->send_buf + c->send_offset, c->send_len - c->send_offset); if (sent < 0) { int err = socket_get_error(); if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) return;