/
githubmirror
/
curl
Обзор
Документация
Войти
/
githubmirror
/
curl
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/server/util.c
1 227 строк
35 KB
Stefan Eissing
dnsd: add support for DoH
07 авг 2026, 10:20
Не верифицирован
07 авг 2026, 10:20
7f6a756
Код
Авторство
О чём код?
/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "first.h" #include <toolx/tool_time.h> #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif #ifdef _WIN32 #include <share.h> #endif void loghex(const unsigned char *buffer, ssize_t len) { char data[12000]; ssize_t i; const unsigned char *ptr = buffer; char *optr = data; ssize_t width = 0; int left = sizeof(data); for(i = 0; i < len && (left > 2); i++) { snprintf(optr, left, "%02x", ptr[i]); width += 2; optr += 2; left -= 2; } if(width) logmsg("'%s'", data); } void logmsg(const char *msg, ...) { va_list ap; char buffer[2048 + 1]; FILE *logfp; struct curltime tv; CURLcode result; time_t sec; struct tm now; char timebuf[50]; static time_t epoch_offset; static int known_offset; if(!serverlogfile) { fprintf(stderr, "Error: Server log file not set\n"); return; } tv = curlx_now(); if(!known_offset) { epoch_offset = time(NULL) - tv.tv_sec; known_offset = 1; } sec = epoch_offset + tv.tv_sec; result = toolx_localtime(sec, &now); if(result) memset(&now, 0, sizeof(now)); snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld", now.tm_hour, now.tm_min, now.tm_sec, (long)tv.tv_usec); va_start(ap, msg); /* Suppress for builds where CURL_PRINTF() is not set */ #ifdef CURL_HAVE_DIAG #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif vsnprintf(buffer, sizeof(buffer), msg, ap); #ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif va_end(ap); do { logfp = curlx_fopen(serverlogfile, "ab"); /* !checksrc! disable ERRNOVAR 1 */ } while(!logfp && (errno == EINTR)); if(logfp) { fprintf(logfp, "%s %s\n", timebuf, buffer); curlx_fclose(logfp); } else { char errbuf[STRERROR_LEN]; int error = errno; fprintf(stderr, "fopen() failed with error (%d) %s\n", error, curlx_strerror(error, errbuf, sizeof(errbuf))); fprintf(stderr, "Error opening file '%s'\n", serverlogfile); fprintf(stderr, "Msg not logged: %s %s\n", timebuf, buffer); } } #ifdef _WIN32 static void win32_cleanup(void) { #ifdef USE_WINSOCK WSACleanup(); #endif /* flush buffers of all streams regardless of their mode */ _flushall(); } int win32_init(void) { curlx_now_init(); #ifdef USE_WINSOCK { WORD wVersionRequested; WSADATA wsaData; int err; char buffer[STRERROR_LEN]; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if(err) { curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)); fprintf(stderr, "Winsock init failed: %s\n", buffer); logmsg("Error initializing Winsock -- aborting"); return 1; } if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { WSACleanup(); curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)); fprintf(stderr, "Winsock init failed: %s\n", buffer); logmsg("No suitable winsock.dll found -- aborting"); return 1; } } #endif /* USE_WINSOCK */ atexit(win32_cleanup); return 0; } #endif /* _WIN32 */ /* fopens the test case file */ FILE *test2fopen(long testno, const char *logdir2) { FILE *stream; char filename[256]; /* first try the alternative, preprocessed, file */ snprintf(filename, sizeof(filename), "%s/test%ld", logdir2, testno); stream = curlx_fopen(filename, "rb"); return stream; } #ifdef _WIN32 #define t_getpid() GetCurrentProcessId() #else #define t_getpid() getpid() #endif curl_off_t our_getpid(void) { curl_off_t pid = (curl_off_t)t_getpid(); #ifdef _WIN32 /* store pid + MAX_PID to avoid conflict with Cygwin/msys PIDs, see also: * - 2019-01-31: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=b5e1003722cb14235c4f166be72c09acdffc62ea * - 2019-02-02: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=448cf5aa4b429d5a9cebf92a0da4ab4b5b6d23fe * - 2024-12-19: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=363357c023ce01e936bdaedf0f479292a8fa4e0f */ pid += 4194304; #endif return pid; } int write_pidfile(const char *filename) { FILE *pidfile; curl_off_t pid; pid = our_getpid(); pidfile = curlx_fopen(filename, "wb"); if(!pidfile) { char errbuf[STRERROR_LEN]; logmsg("Could not write pid file: %s (%d) %s", filename, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); return 0; /* fail */ } fprintf(pidfile, "%ld\n", (long)pid); curlx_fclose(pidfile); logmsg("Wrote pid %ld to %s", (long)pid, filename); return 1; /* success */ } /* store the used port number in a file */ int write_portfile(const char *filename, int port) { FILE *portfile = curlx_fopen(filename, "wb"); if(!portfile) { char errbuf[STRERROR_LEN]; logmsg("Could not write port file: %s (%d) %s", filename, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); return 0; /* fail */ } fprintf(portfile, "%d\n", port); curlx_fclose(portfile); logmsg("Wrote port %d to %s", port, filename); return 1; /* success */ } void set_advisor_read_lock(const char *filename) { FILE *lockfile; int error = 0; char errbuf[STRERROR_LEN]; do { lockfile = curlx_fopen(filename, "wb"); /* !checksrc! disable ERRNOVAR 1 */ } while(!lockfile && ((error = errno) == EINTR)); if(!lockfile) { logmsg("Error creating lock file %s error (%d) %s", filename, error, curlx_strerror(error, errbuf, sizeof(errbuf))); return; } if(curlx_fclose(lockfile)) logmsg("Error closing lock file %s error (%d) %s", filename, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } void clear_advisor_read_lock(const char *filename) { int error = 0; int rc; /* * Log all removal failures. Even those due to file not existing. * This allows to detect if unexpectedly the file has already been * removed by a process different than the one that should do this. */ do { rc = unlink(filename); /* !checksrc! disable ERRNOVAR 1 */ } while(rc && ((error = errno) == EINTR)); if(rc) { char errbuf[STRERROR_LEN]; logmsg("Error removing lock file %s error (%d) %s", filename, error, curlx_strerror(error, errbuf, sizeof(errbuf))); } } /* store the entire request in a file */ void storerequest(const char *reqbuf, size_t totalsize, const char *filename) { int error = 0; char errbuf[STRERROR_LEN]; size_t written; size_t writeleft; FILE *dump; char dumpfile[256]; snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, filename); if(!reqbuf) return; if(totalsize == 0) return; do { dump = curlx_fopen(dumpfile, "ab"); /* !checksrc! disable ERRNOVAR 1 */ } while(!dump && ((error = errno) == EINTR)); if(!dump) { logmsg("storerequest: Error opening file %s error (%d) %s", dumpfile, error, curlx_strerror(error, errbuf, sizeof(errbuf))); return; } writeleft = totalsize; do { written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); if(got_exit_signal) goto storerequest_cleanup; if(written > 0) writeleft -= written; error = errno; /* !checksrc! disable ERRNOVAR 1 */ } while((writeleft > 0) && (error == EINTR)); if(writeleft == 0) logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); else if(writeleft > 0) { logmsg("Error writing file %s error (%d) %s", dumpfile, error, curlx_strerror(error, errbuf, sizeof(errbuf))); logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", totalsize - writeleft, totalsize, dumpfile); } storerequest_cleanup: if(curlx_fclose(dump)) logmsg("Error closing file %s error (%d) %s", dumpfile, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } static bool initiate_exit(int signum) /* stay signal-safe */ { if(got_exit_signal == 0) { got_exit_signal = 1; exit_signal = signum; } #ifdef _WIN32 if(!exit_event) return FALSE; (void)SetEvent(exit_event); #endif return TRUE; } #ifndef _WIN32 /* vars used to keep around previous signal handlers */ typedef void (*SIGHANDLER_T)(int); #ifdef SIGHUP static SIGHANDLER_T old_sighup_handler = SIG_ERR; #endif #ifdef SIGPIPE static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; #endif #ifdef SIGALRM static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; #endif #ifdef SIGINT static SIGHANDLER_T old_sigint_handler = SIG_ERR; #endif #ifdef SIGTERM static SIGHANDLER_T old_sigterm_handler = SIG_ERR; #endif /* signal handler that is triggered to indicate that the program * should finish its execution in a controlled manner as soon as possible. * The first time this is called it sets got_exit_signal to 1 and * stores in exit_signal the signal that triggered its execution. * * Only call signal-safe functions from the signal handler, as required by * the POSIX specification: * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 */ static void exit_signal_handler(int signum) /* stay signal-safe */ { int old_errno = errno; exit_msg = "exit_signal_handler(): triggered"; (void)initiate_exit(signum); #if !(defined(HAVE_SIGACTION) && defined(SA_RESTART)) (void)signal(signum, exit_signal_handler); #endif errno = old_errno; } static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler, int norestart) { #if defined(HAVE_SIGACTION) && defined(SA_RESTART) struct sigaction sa, oldsa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, signum); sa.sa_flags = norestart ? 0 : SA_RESTART; if(sigaction(signum, &sa, &oldsa)) return SIG_ERR; return oldsa.sa_handler; #else SIGHANDLER_T oldhdlr = signal(signum, handler); #ifdef HAVE_SIGINTERRUPT if(oldhdlr != SIG_ERR) siginterrupt(signum, norestart); #else (void)norestart; #endif return oldhdlr; #endif } #else /* _WIN32 */ /* CTRL event handler for Windows Console applications to handle exit events. * * Background information from MSDN: * When a CTRL+C interrupt occurs, Win32 operating systems generate a new * thread to specifically handle that interrupt. This can cause a single-thread * application, such as one in UNIX, to become multi-threaded and cause * unexpected behavior. */ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) /* stay signal-safe */ { static const char msgU[] = "ctrl_event_handler(): unhandled\n"; static const char msgH[] = "ctrl_event_handler(): handled\n"; static const char msgF[] = "ctrl_event_handler(): failed to handle\n"; HANDLE out = GetStdHandle(STD_ERROR_HANDLE); DWORD dwWritten; int signum = 0; switch(dwCtrlType) { case CTRL_C_EVENT: signum = SIGINT; break; case CTRL_CLOSE_EVENT: signum = SIGTERM; break; case CTRL_BREAK_EVENT: signum = SIGBREAK; break; default: WriteFile(out, msgU, CURL_CSTRLEN(msgU), &dwWritten, NULL); exit_msg = msgU; return FALSE; } if(!initiate_exit(signum)) { WriteFile(out, msgF, CURL_CSTRLEN(msgF), &dwWritten, NULL); exit_msg = msgF; return FALSE; } WriteFile(out, msgH, CURL_CSTRLEN(msgH), &dwWritten, NULL); exit_msg = msgH; return TRUE; } #ifndef CURL_WINDOWS_UWP static DWORD thread_main_id = 0; static HANDLE thread_main_window = NULL; static HWND hidden_main_window = NULL; /* Window message handler for Windows applications to add support * for graceful process termination via taskkill (without /f) which * sends WM_CLOSE to all Windows of a process (even hidden ones). * * Therefore we create and run a hidden Window in a separate thread * to receive and handle the WM_CLOSE message as SIGTERM signal. */ static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(hwnd == hidden_main_window) { switch(uMsg) { case WM_CLOSE: { static const char msg[] = "main_window_proc(): WM_CLOSE -> SIGTERM\n"; DWORD dwWritten; WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, CURL_CSTRLEN(msg), &dwWritten, NULL); exit_msg = msg; initiate_exit(SIGTERM); break; } case WM_DESTROY: PostQuitMessage(0); break; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); } /* Window message queue loop for hidden main window, details see above. */ static DWORD WINAPI main_window_loop(void *lpParameter) { WNDCLASS wc; BOOL ret; MSG msg; DWORD dwWritten; ZeroMemory(&wc, sizeof(wc)); wc.lpfnWndProc = (WNDPROC)main_window_proc; wc.hInstance = (HINSTANCE)lpParameter; wc.lpszClassName = TEXT("MainWClass"); if(!RegisterClass(&wc)) { static const char str[] = "RegisterClass() failed\n"; WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), &dwWritten, NULL); return (DWORD)-1; } hidden_main_window = CreateWindowEx(0, TEXT("MainWClass"), TEXT("Recv WM_CLOSE msg"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)NULL, (HMENU)NULL, wc.hInstance, NULL); if(!hidden_main_window) { static const char str[] = "CreateWindowEx() failed\n"; WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), &dwWritten, NULL); return (DWORD)-1; } do { ret = GetMessage(&msg, NULL, 0, 0); if(ret == -1) { static const char str[] = "GetMessage() failed\n"; WriteFile(GetStdHandle(STD_ERROR_HANDLE), str, CURL_CSTRLEN(str), &dwWritten, NULL); return (DWORD)-1; } else if(ret) { if(msg.message == WM_APP) { DestroyWindow(hidden_main_window); } else if(msg.hwnd && !TranslateMessage(&msg)) { DispatchMessage(&msg); } } } while(ret); hidden_main_window = NULL; return (DWORD)msg.wParam; } #endif /* CURL_WINDOWS_UWP */ #endif /* !_WIN32 */ void install_signal_handlers(bool keep_sigalrm) { char errbuf[STRERROR_LEN]; (void)errbuf; (void)keep_sigalrm; #ifndef _WIN32 #ifdef SIGHUP /* ignore SIGHUP signal */ old_sighup_handler = set_signal(SIGHUP, SIG_IGN, 0); if(old_sighup_handler == SIG_ERR) logmsg("cannot install SIGHUP handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #ifdef SIGPIPE /* ignore SIGPIPE signal */ old_sigpipe_handler = set_signal(SIGPIPE, SIG_IGN, 0); if(old_sigpipe_handler == SIG_ERR) logmsg("cannot install SIGPIPE handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #ifdef SIGALRM if(!keep_sigalrm) { /* ignore SIGALRM signal */ old_sigalrm_handler = set_signal(SIGALRM, SIG_IGN, 0); if(old_sigalrm_handler == SIG_ERR) logmsg("cannot install SIGALRM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } #endif #ifdef SIGINT /* handle SIGINT signal with our exit_signal_handler */ old_sigint_handler = set_signal(SIGINT, exit_signal_handler, 1); if(old_sigint_handler == SIG_ERR) logmsg("cannot install SIGINT handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #ifdef SIGTERM /* handle SIGTERM signal with our exit_signal_handler */ old_sigterm_handler = set_signal(SIGTERM, exit_signal_handler, 1); if(old_sigterm_handler == SIG_ERR) logmsg("cannot install SIGTERM handler: (%d) %s", errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif #else /* _WIN32 */ /* setup Windows exit event before any signal can trigger */ exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); if(!exit_event) logmsg("cannot create exit event"); if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE)) logmsg("cannot install CTRL event handler"); #ifndef CURL_WINDOWS_UWP thread_main_window = CreateThread(NULL, 0, &main_window_loop, GetModuleHandle(NULL), 0, &thread_main_id); if(!thread_main_window || !thread_main_id) logmsg("cannot start main window loop"); #endif #endif /* !_WIN32 */ } void restore_signal_handlers(bool keep_sigalrm) { (void)keep_sigalrm; #ifndef _WIN32 #ifdef SIGHUP if(old_sighup_handler != SIG_ERR) (void)set_signal(SIGHUP, old_sighup_handler, 0); #endif #ifdef SIGPIPE if(old_sigpipe_handler != SIG_ERR) (void)set_signal(SIGPIPE, old_sigpipe_handler, 0); #endif #ifdef SIGALRM if(!keep_sigalrm) { if(old_sigalrm_handler != SIG_ERR) (void)set_signal(SIGALRM, old_sigalrm_handler, 0); } #endif #ifdef SIGINT if(old_sigint_handler != SIG_ERR) (void)set_signal(SIGINT, old_sigint_handler, 0); #endif #ifdef SIGTERM if(old_sigterm_handler != SIG_ERR) (void)set_signal(SIGTERM, old_sigterm_handler, 0); #endif #else /* _WIN32 */ (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); #ifndef CURL_WINDOWS_UWP if(thread_main_window && thread_main_id) { if(PostThreadMessage(thread_main_id, WM_APP, 0, 0)) { if(WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE)) { if(CloseHandle(thread_main_window)) { thread_main_window = NULL; thread_main_id = 0; } } } } #endif if(exit_event && CloseHandle(exit_event)) exit_event = NULL; #endif /* !_WIN32 */ } #ifdef USE_UNIX_SOCKETS int bind_unix_socket(curl_socket_t sock, const char *unix_socket, struct sockaddr_un *sau) { char errbuf[STRERROR_LEN]; int rc; size_t len; if(!unix_socket) { logmsg("Unix socket domain path not specified."); return -1; } len = strlen(unix_socket); memset(sau, 0, sizeof(struct sockaddr_un)); sau->sun_family = AF_UNIX; if(len >= sizeof(sau->sun_path) - 1) { logmsg("Too long unix socket domain path (%zu)", len); return -1; } curlx_strcopy(sau->sun_path, sizeof(sau->sun_path), unix_socket, len); rc = bind(sock, (struct sockaddr *)sau, sizeof(struct sockaddr_un)); if(rc && SOCKERRNO == SOCKEADDRINUSE) { int sockerr; /* socket already exists. Perhaps it is stale? */ curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0); if(unixfd == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; logmsg("Failed to create socket at %s (%d) %s", unix_socket, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return -1; } /* check whether the server is alive */ rc = connect(unixfd, (struct sockaddr*)sau, sizeof(struct sockaddr_un)); sockerr = SOCKERRNO; sclose(unixfd); if(rc && sockerr != SOCKECONNREFUSED) { logmsg("Failed to connect to %s (%d) %s", unix_socket, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return rc; } #if !defined(_WIN32) && defined(S_IFSOCK) /* No lstat(), S_IFSOCK on Windows */ /* socket server is not alive, now check if it was actually a socket. */ { curlx_struct_stat statbuf; rc = lstat(unix_socket, &statbuf); if(rc && errno != ENOENT) { logmsg("Error binding socket, failed to stat %s (%d) %s", unix_socket, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); return -1; } if(!rc && (statbuf.st_mode & S_IFMT) != S_IFSOCK) { logmsg("Error binding socket, %s is not a socket", unix_socket); return -1; } } #endif /* dead socket, cleanup and retry bind */ if(unlink(unix_socket) && errno != ENOENT) { logmsg("Error binding socket, failed to unlink %s: %d (%s)", unix_socket, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); return -1; } /* stale socket is gone, retry bind */ rc = bind(sock, (struct sockaddr *)sau, sizeof(struct sockaddr_un)); } return rc; } #endif curl_socket_t sockdaemon(curl_socket_t sock, uint16_t *listenport, const char *unix_socket, bool bind_only) { /* passive daemon style */ srvr_sockaddr_union_t listener; int flag; int rc; int totdelay = 0; int maxretr = 10; int delay = 20; int attempt = 0; int sockerr = 0; char errbuf[STRERROR_LEN]; #ifndef USE_UNIX_SOCKETS (void)unix_socket; #endif #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) if(socket_domain != AF_UNIX) { #endif do { attempt++; flag = 1; rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)); if(rc) { sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); if(maxretr) { if(curlx_wait_ms(delay)) { /* should not happen */ sockerr = SOCKERRNO; logmsg("curlx_wait_ms() failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } if(got_exit_signal) { logmsg("signalled to die, exiting..."); sclose(sock); return CURL_SOCKET_BAD; } totdelay += delay; delay *= 2; /* double the sleep for next attempt */ } } } while(rc && maxretr--); if(rc) { logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. " "Error (%d) %s", attempt, totdelay, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); logmsg("Continuing anyway..."); } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) } #endif /* When the specified listener port is zero, it is actually a request to let the system choose a non-zero available port. */ switch(socket_domain) { case AF_INET: memset(&listener.sa4, 0, sizeof(listener.sa4)); listener.sa4.sin_family = AF_INET; listener.sa4.sin_addr.s_addr = INADDR_ANY; listener.sa4.sin_port = htons(*listenport); rc = bind(sock, &listener.sa, sizeof(listener.sa4)); break; #ifdef USE_IPV6 case AF_INET6: memset(&listener.sa6, 0, sizeof(listener.sa6)); listener.sa6.sin6_family = AF_INET6; listener.sa6.sin6_addr = in6addr_any; listener.sa6.sin6_port = htons(*listenport); rc = bind(sock, &listener.sa, sizeof(listener.sa6)); break; #endif /* USE_IPV6 */ #ifdef USE_UNIX_SOCKETS case AF_UNIX: rc = bind_unix_socket(sock, unix_socket, &listener.sau); break; #endif default: rc = 1; } if(rc) { sockerr = SOCKERRNO; #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) logmsg("Error binding socket on path %s (%d) %s", unix_socket, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif logmsg("Error binding socket on port %hu (%d) %s", *listenport, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } if(!*listenport #ifdef USE_UNIX_SOCKETS && !unix_socket #endif ) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); else #endif la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } switch(localaddr.sa.sa_family) { case AF_INET: *listenport = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: *listenport = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } if(!*listenport) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); logmsg("proper network library linkage. This might not be the only"); logmsg("reason, but double check it before anything else."); sclose(sock); return CURL_SOCKET_BAD; } } /* bindonly option forces no listening */ if(bind_only) { logmsg("instructed to bind port without listening"); return sock; } /* start accepting connections */ if(listen(sock, 5)) { sockerr = SOCKERRNO; logmsg("listen(%ld, 5) failed with error (%d) %s", (long)sock, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } return sock; } int open_udp_sock(curl_socket_t *psock, uint16_t *pport) { srvr_sockaddr_union_t me; curl_socket_t sock = CURL_SOCKET_BAD; uint16_t port = *pport; int sockerr, flag, rc; char errbuf[STRERROR_LEN]; int result = 0; sock = socket(socket_domain, SOCK_DGRAM, 0); if(sock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } #ifdef USE_IPV6 if(socket_domain == AF_INET6) { memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; me.sa6.sin6_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa6)); } else #endif { memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; me.sa4.sin_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa4)); } if(rc) { sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } if(!port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 if(socket_domain == AF_INET6) la_size = sizeof(localaddr.sa6); else #endif la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } switch(localaddr.sa.sa_family) { case AF_INET: port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } if(!port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); logmsg("proper network library linkage. This might not be the only"); logmsg("reason, but double check it before anything else."); result = 2; goto out; } } out: if(result) { if(sock != CURL_SOCKET_BAD) sclose(sock); sock = CURL_SOCKET_BAD; port = 0; } *psock = sock; *pport = port; return result; } bool socket_domain_is_ip(void) { switch(socket_domain) { case AF_INET: #ifdef USE_IPV6 case AF_INET6: #endif return TRUE; default: /* case AF_UNIX: */ return FALSE; } } int open_stream_sock(curl_socket_t *psock, uint16_t *pport) { srvr_sockaddr_union_t me; char errbuf[STRERROR_LEN]; int flag, sockerr, rc = 0; curl_socket_t sock = CURL_SOCKET_BAD; uint16_t port = *pport; int result = 0; sock = socket(socket_domain, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) if(socket_domain != AF_UNIX) { #endif flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) } #endif if(curlx_nonblock(sock, TRUE)) { sockerr = SOCKERRNO; logmsg("curlx_nonblock failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } switch(socket_domain) { case AF_INET: memset(&me.sa4, 0, sizeof(me.sa4)); me.sa4.sin_family = AF_INET; me.sa4.sin_addr.s_addr = INADDR_ANY; me.sa4.sin_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa4)); break; #ifdef USE_IPV6 case AF_INET6: memset(&me.sa6, 0, sizeof(me.sa6)); me.sa6.sin6_family = AF_INET6; me.sa6.sin6_addr = in6addr_any; me.sa6.sin6_port = htons(port); rc = bind(sock, &me.sa, sizeof(me.sa6)); break; #endif /* USE_IPV6 */ #ifdef USE_UNIX_SOCKETS case AF_UNIX: rc = bind_unix_socket(sock, server_unix_socket, &me.sau); #endif /* USE_UNIX_SOCKETS */ } if(rc) { sockerr = SOCKERRNO; #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) logmsg("Error binding socket on path %s (%d) %s", server_unix_socket, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif logmsg("Error binding socket on port %hu (%d) %s", port, sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } if(!port) { /* The system was supposed to choose a port number, figure out which port we actually got and update the listener port value with it. */ curl_socklen_t la_size; srvr_sockaddr_union_t localaddr; memset(&localaddr, 0, sizeof(localaddr)); #ifdef USE_IPV6 if(socket_domain != AF_INET6) #endif la_size = sizeof(localaddr.sa4); #ifdef USE_IPV6 else la_size = sizeof(localaddr.sa6); #endif if(getsockname(sock, &localaddr.sa, &la_size) < 0) { sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto out; } switch(localaddr.sa.sa_family) { case AF_INET: port = ntohs(localaddr.sa4.sin_port); break; #ifdef USE_IPV6 case AF_INET6: port = ntohs(localaddr.sa6.sin6_port); break; #endif default: break; } if(!port) { /* Real failure, listener port shall not be zero beyond this point. */ logmsg("Apparently getsockname() succeeded, with listener port zero."); logmsg("A valid reason for this failure is a binary built without"); logmsg("proper network library linkage. This might not be the only"); logmsg("reason, but double check it before anything else."); result = 1; goto out; } } out: if(result) { if(sock != CURL_SOCKET_BAD) sclose(sock); sock = CURL_SOCKET_BAD; port = 0; } *psock = sock; *pport = port; return result; } /* returns a socket handle, or 0 if there are no more waiting sockets, or < 0 if there was an error */ curl_socket_t accept_connection(curl_socket_t listen_sock) { curl_socket_t msgsock = CURL_SOCKET_BAD; int sockerr; char errbuf[STRERROR_LEN]; int flag = 1; msgsock = accept(listen_sock, NULL, NULL); if(got_exit_signal) { if(msgsock != CURL_SOCKET_BAD) sclose(msgsock); return CURL_SOCKET_BAD; } if(msgsock == CURL_SOCKET_BAD) { sockerr = SOCKERRNO; if(SOCK_EAGAIN(sockerr)) { /* nothing to accept */ return 0; } logmsg("MAJOR ERROR, accept() failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return CURL_SOCKET_BAD; } if(curlx_nonblock(msgsock, TRUE)) { sockerr = SOCKERRNO; logmsg("curlx_nonblock failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(msgsock); return CURL_SOCKET_BAD; } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) if(socket_domain != AF_UNIX) { #endif if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, (void *)&flag, sizeof(flag))) { sockerr = SOCKERRNO; logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s", sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(msgsock); return CURL_SOCKET_BAD; } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) } #endif /* * As soon as this server accepts a connection from the test harness it * must set the server logs advisor read lock to indicate that server * logs should not be read until this lock is removed by this server. */ if(!serverlogslocked) set_advisor_read_lock(loglockfile); serverlogslocked += 1; logmsg("====> Client connect"); #if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) if(socket_domain_is_ip()) { /* * Disable the Nagle algorithm to make it easier to send out a large * response in many small segments to torture the clients more. */ if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(flag))) logmsg("====> TCP_NODELAY failed"); } #endif return msgsock; } bool curlx_str_case_equal(const struct Curl_str *s1, const struct Curl_str *s2) { return ((s1->len == s2->len) && !CURL_STRNICMP(s1->str, s2->str, s1->len)); }