diff options
Diffstat (limited to 'tests/server/sockfilt.c')
-rw-r--r-- | tests/server/sockfilt.c | 698 |
1 files changed, 594 insertions, 104 deletions
diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 714d6fcd..a4496e0f 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, 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 @@ -19,10 +19,11 @@ * KIND, either express or implied. * ***************************************************************************/ +#include "server_setup.h" /* Purpose * - * 1. Accept a TCP connection on a custom port (ipv4 or ipv6), or connect + * 1. Accept a TCP connection on a custom port (IPv4 or IPv6), or connect * to a given (localhost) port. * * 2. Get commands on STDIN. Pass data on to the TCP stream. @@ -79,19 +80,9 @@ * if no signal was being ignored or handled at all. Enjoy it! */ -#define CURL_NO_OLDIES - -#include "setup.h" /* portability help from the lib directory */ - #ifdef HAVE_SIGNAL_H #include <signal.h> #endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -109,10 +100,23 @@ #include "getpart.h" #include "inet_pton.h" #include "util.h" +#include "server_sockaddr.h" +#include "warnless.h" /* include memdebug.h last */ #include "memdebug.h" +#ifdef USE_WINSOCK +#undef EINTR +#define EINTR 4 /* errno.h value */ +#undef EAGAIN +#define EAGAIN 11 /* errno.h value */ +#undef ENOMEM +#define ENOMEM 12 /* errno.h value */ +#undef EINVAL +#define EINVAL 22 /* errno.h value */ +#endif + #define DEFAULT_PORT 8999 #ifndef DEFAULT_LOGFILE @@ -122,6 +126,7 @@ const char *serverlogfile = DEFAULT_LOGFILE; static bool verbose = FALSE; +static bool bind_only = FALSE; #ifdef ENABLE_IPV6 static bool use_ipv6 = FALSE; #endif @@ -166,6 +171,10 @@ static SIGHANDLER_T old_sigint_handler = SIG_ERR; static SIGHANDLER_T old_sigterm_handler = SIG_ERR; #endif +#if defined(SIGBREAK) && defined(WIN32) +static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; +#endif + /* var which if set indicates that the program should finish execution */ SIG_ATOMIC_T got_exit_signal = 0; @@ -181,13 +190,13 @@ static volatile int exit_signal = 0; static RETSIGTYPE exit_signal_handler(int signum) { - int old_errno = ERRNO; + int old_errno = errno; if(got_exit_signal == 0) { got_exit_signal = 1; exit_signal = signum; } (void)signal(signum, exit_signal_handler); - SET_ERRNO(old_errno); + errno = old_errno; } static void install_signal_handlers(void) @@ -195,32 +204,39 @@ static void install_signal_handlers(void) #ifdef SIGHUP /* ignore SIGHUP signal */ if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) - logmsg("cannot install SIGHUP handler: %s", strerror(ERRNO)); + logmsg("cannot install SIGHUP handler: %s", strerror(errno)); #endif #ifdef SIGPIPE /* ignore SIGPIPE signal */ if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) - logmsg("cannot install SIGPIPE handler: %s", strerror(ERRNO)); + logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); #endif #ifdef SIGALRM /* ignore SIGALRM signal */ if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) - logmsg("cannot install SIGALRM handler: %s", strerror(ERRNO)); + logmsg("cannot install SIGALRM handler: %s", strerror(errno)); #endif #ifdef SIGINT /* handle SIGINT signal with our exit_signal_handler */ if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) - logmsg("cannot install SIGINT handler: %s", strerror(ERRNO)); + logmsg("cannot install SIGINT handler: %s", strerror(errno)); else siginterrupt(SIGINT, 1); #endif #ifdef SIGTERM /* handle SIGTERM signal with our exit_signal_handler */ if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) - logmsg("cannot install SIGTERM handler: %s", strerror(ERRNO)); + logmsg("cannot install SIGTERM handler: %s", strerror(errno)); else siginterrupt(SIGTERM, 1); #endif +#if defined(SIGBREAK) && defined(WIN32) + /* handle SIGBREAK signal with our exit_signal_handler */ + if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); + else + siginterrupt(SIGBREAK, 1); +#endif } static void restore_signal_handlers(void) @@ -245,7 +261,80 @@ static void restore_signal_handlers(void) if(SIG_ERR != old_sigterm_handler) (void)signal(SIGTERM, old_sigterm_handler); #endif +#if defined(SIGBREAK) && defined(WIN32) + if(SIG_ERR != old_sigbreak_handler) + (void)signal(SIGBREAK, old_sigbreak_handler); +#endif +} + +#ifdef WIN32 +/* + * read-wrapper to support reading from stdin on Windows. + */ +static ssize_t read_wincon(int fd, void *buf, size_t count) +{ + HANDLE handle = NULL; + DWORD mode, rcount = 0; + BOOL success; + + if(fd == fileno(stdin)) { + handle = GetStdHandle(STD_INPUT_HANDLE); + } + else { + return read(fd, buf, count); + } + + if(GetConsoleMode(handle, &mode)) { + success = ReadConsole(handle, buf, curlx_uztoul(count), &rcount, NULL); + } + else { + success = ReadFile(handle, buf, curlx_uztoul(count), &rcount, NULL); + } + if(success) { + return rcount; + } + + errno = GetLastError(); + return -1; +} +#undef read +#define read(a,b,c) read_wincon(a,b,c) + +/* + * write-wrapper to support writing to stdout and stderr on Windows. + */ +static ssize_t write_wincon(int fd, const void *buf, size_t count) +{ + HANDLE handle = NULL; + DWORD mode, wcount = 0; + BOOL success; + + if(fd == fileno(stdout)) { + handle = GetStdHandle(STD_OUTPUT_HANDLE); + } + else if(fd == fileno(stderr)) { + handle = GetStdHandle(STD_ERROR_HANDLE); + } + else { + return write(fd, buf, count); + } + + if(GetConsoleMode(handle, &mode)) { + success = WriteConsole(handle, buf, curlx_uztoul(count), &wcount, NULL); + } + else { + success = WriteFile(handle, buf, curlx_uztoul(count), &wcount, NULL); + } + if(success) { + return wcount; + } + + errno = GetLastError(); + return -1; } +#undef write +#define write(a,b,c) write_wincon(a,b,c) +#endif /* * fullread is a wrapper around the read() function. This will repeat the call @@ -269,10 +358,12 @@ static ssize_t fullread(int filedes, void *buffer, size_t nbytes) } if(rc < 0) { - error = ERRNO; + error = errno; if((error == EINTR) || (error == EAGAIN)) continue; - logmsg("unrecoverable read() failure: %s", strerror(error)); + logmsg("reading from file descriptor: %d,", filedes); + logmsg("unrecoverable read() failure: (%d) %s", + error, strerror(error)); return -1; } @@ -313,10 +404,12 @@ static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) } if(wc < 0) { - error = ERRNO; + error = errno; if((error == EINTR) || (error == EAGAIN)) continue; - logmsg("unrecoverable write() failure: %s", strerror(error)); + logmsg("writing to file descriptor: %d,", filedes); + logmsg("unrecoverable write() failure: (%d) %s", + error, strerror(error)); return -1; } @@ -406,6 +499,383 @@ static void lograw(unsigned char *buffer, ssize_t len) logmsg("'%s'", data); } +#ifdef USE_WINSOCK +/* + * WinSock select() does not support standard file descriptors, + * it can only check SOCKETs. The following function is an attempt + * to re-create a select() function with support for other handle types. + * + * select() function with support for WINSOCK2 sockets and all + * other handle types supported by WaitForMultipleObjectsEx() as + * well as disk files, anonymous and names pipes, and character input. + * + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx + */ +struct select_ws_wait_data { + HANDLE handle; /* actual handle to wait for during select */ + HANDLE event; /* internal event to abort waiting thread */ +}; +static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter) +{ + struct select_ws_wait_data *data; + HANDLE handle, handles[2]; + INPUT_RECORD inputrecord; + LARGE_INTEGER size, pos; + DWORD type, length; + + /* retrieve handles from internal structure */ + data = (struct select_ws_wait_data *) lpParameter; + if(data) { + handle = data->handle; + handles[0] = data->event; + handles[1] = handle; + free(data); + } + else + return -1; + + /* retrieve the type of file to wait on */ + type = GetFileType(handle); + switch(type) { + case FILE_TYPE_DISK: + /* The handle represents a file on disk, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - comparison of current position in file and total size of + * the file can be used to check if we reached the end yet. + * + * Approach: Loop till either the internal event is signalled + * or if the end of the file has already been reached. + */ + while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) + == WAIT_OBJECT_0 + 1) { + /* get total size of file */ + length = 0; + size.QuadPart = 0; + size.LowPart = GetFileSize(handle, &length); + if((size.LowPart != INVALID_FILE_SIZE) || + (GetLastError() == NO_ERROR)) { + size.HighPart = length; + /* get the current position within the file */ + pos.QuadPart = 0; + pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart, FILE_CURRENT); + if((pos.LowPart != INVALID_SET_FILE_POINTER) || + (GetLastError() == NO_ERROR)) { + /* compare position with size, abort if not equal */ + if(size.QuadPart == pos.QuadPart) { + /* sleep and continue waiting */ + SleepEx(0, FALSE); + continue; + } + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + case FILE_TYPE_CHAR: + /* The handle represents a character input, this means: + * - WaitForMultipleObjectsEx will be signalled on any kind of input, + * including mouse and window size events we do not care about. + * + * Approach: Loop till either the internal event is signalled + * or we get signalled for an actual key-event. + */ + while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) + == WAIT_OBJECT_0 + 1) { + /* check if this is an actual console handle */ + length = 0; + if(GetConsoleMode(handle, &length)) { + /* retrieve an event from the console buffer */ + length = 0; + if(PeekConsoleInput(handle, &inputrecord, 1, &length)) { + /* check if the event is not an actual key-event */ + if(length == 1 && inputrecord.EventType != KEY_EVENT) { + /* purge the non-key-event and continue waiting */ + ReadConsoleInput(handle, &inputrecord, 1, &length); + continue; + } + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + case FILE_TYPE_PIPE: + /* The handle represents an anonymous or named pipe, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - peek into the pipe and retrieve the amount of data available. + * + * Approach: Loop till either the internal event is signalled + * or there is data in the pipe available for reading. + */ + while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) + == WAIT_OBJECT_0 + 1) { + /* peek into the pipe and retrieve the amount of data available */ + length = 0; + if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) { + /* if there is no data available, sleep and continue waiting */ + if(length == 0) { + SleepEx(0, FALSE); + continue; + } + } + else { + /* if the pipe has been closed, sleep and continue waiting */ + if(GetLastError() == ERROR_BROKEN_PIPE) { + SleepEx(0, FALSE); + continue; + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + default: + /* The handle has an unknown type, try to wait on it */ + WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE); + break; + } + + return 0; +} +static HANDLE select_ws_wait(HANDLE handle, HANDLE event) +{ + struct select_ws_wait_data *data; + HANDLE thread = NULL; + + /* allocate internal waiting data structure */ + data = malloc(sizeof(struct select_ws_wait_data)); + if(data) { + data->handle = handle; + data->event = event; + + /* launch waiting thread */ + thread = CreateThread(NULL, 0, + &select_ws_wait_thread, + data, 0, NULL); + + /* free data if thread failed to launch */ + if(!thread) { + free(data); + } + } + + return thread; +} +struct select_ws_data { + curl_socket_t fd; /* the original input handle (indexed by fds) */ + curl_socket_t wsasock; /* the internal socket handle (indexed by wsa) */ + WSAEVENT wsaevent; /* the internal WINSOCK2 event (indexed by wsa) */ + HANDLE thread; /* the internal threads handle (indexed by thd) */ +}; +static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + DWORD milliseconds, wait, idx; + WSANETWORKEVENTS wsanetevents; + struct select_ws_data *data; + HANDLE handle, *handles; + curl_socket_t sock; + long networkevents; + WSAEVENT wsaevent; + int error, fds; + HANDLE waitevent = NULL; + DWORD nfd = 0, thd = 0, wsa = 0; + int ret = 0; + + /* check if the input value is valid */ + if(nfds < 0) { + errno = EINVAL; + return -1; + } + + /* check if we got descriptors, sleep in case we got none */ + if(!nfds) { + Sleep((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); + return 0; + } + + /* create internal event to signal waiting threads */ + waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!waitevent) { + errno = ENOMEM; + return -1; + } + + /* allocate internal array for the internal data */ + data = malloc(nfds * sizeof(struct select_ws_data)); + if(data == NULL) { + errno = ENOMEM; + return -1; + } + + /* allocate internal array for the internal event handles */ + handles = malloc(nfds * sizeof(HANDLE)); + if(handles == NULL) { + free(data); + errno = ENOMEM; + return -1; + } + + /* clear internal arrays */ + memset(data, 0, nfds * sizeof(struct select_ws_data)); + memset(handles, 0, nfds * sizeof(HANDLE)); + + /* loop over the handles in the input descriptor sets */ + for(fds = 0; fds < nfds; fds++) { + networkevents = 0; + handles[nfd] = 0; + + if(FD_ISSET(fds, readfds)) + networkevents |= FD_READ|FD_ACCEPT|FD_CLOSE; + + if(FD_ISSET(fds, writefds)) + networkevents |= FD_WRITE|FD_CONNECT; + + if(FD_ISSET(fds, exceptfds)) + networkevents |= FD_OOB|FD_CLOSE; + + /* only wait for events for which we actually care */ + if(networkevents) { + data[nfd].fd = curlx_sitosk(fds); + if(fds == fileno(stdin)) { + handle = GetStdHandle(STD_INPUT_HANDLE); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + data[thd].thread = handle; + thd++; + } + else if(fds == fileno(stdout)) { + handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE); + } + else if(fds == fileno(stderr)) { + handles[nfd] = GetStdHandle(STD_ERROR_HANDLE); + } + else { + wsaevent = WSACreateEvent(); + if(wsaevent != WSA_INVALID_EVENT) { + error = WSAEventSelect(fds, wsaevent, networkevents); + if(error != SOCKET_ERROR) { + handle = (HANDLE) wsaevent; + handles[nfd] = handle; + data[wsa].wsasock = curlx_sitosk(fds); + data[wsa].wsaevent = wsaevent; + wsa++; + } + else { + WSACloseEvent(wsaevent); + handle = (HANDLE) curlx_sitosk(fds); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + data[thd].thread = handle; + thd++; + } + } + } + nfd++; + } + } + + /* convert struct timeval to milliseconds */ + if(timeout) { + milliseconds = ((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); + } + else { + milliseconds = INFINITE; + } + + /* wait for one of the internal handles to trigger */ + wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE); + + /* signal the event handle for the waiting threads */ + SetEvent(waitevent); + + /* loop over the internal handles returned in the descriptors */ + for(idx = 0; idx < nfd; idx++) { + handle = handles[idx]; + sock = data[idx].fd; + fds = curlx_sktosi(sock); + + /* check if the current internal handle was triggered */ + if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= idx && + WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) { + /* first handle stdin, stdout and stderr */ + if(fds == fileno(stdin)) { + /* stdin is never ready for write or exceptional */ + FD_CLR(sock, writefds); + FD_CLR(sock, exceptfds); + } + else if(fds == fileno(stdout) || fds == fileno(stderr)) { + /* stdout and stderr are never ready for read or exceptional */ + FD_CLR(sock, readfds); + FD_CLR(sock, exceptfds); + } + else { + /* try to handle the event with the WINSOCK2 functions */ + wsanetevents.lNetworkEvents = 0; + error = WSAEnumNetworkEvents(fds, handle, &wsanetevents); + if(error != SOCKET_ERROR) { + /* remove from descriptor set if not ready for read/accept/close */ + if(!(wsanetevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))) + FD_CLR(sock, readfds); + + /* remove from descriptor set if not ready for write/connect */ + if(!(wsanetevents.lNetworkEvents & (FD_WRITE|FD_CONNECT))) + FD_CLR(sock, writefds); + + /* HACK: + * use exceptfds together with readfds to signal + * that the connection was closed by the client. + * + * Reason: FD_CLOSE is only signaled once, sometimes + * at the same time as FD_READ with data being available. + * This means that recv/sread is not reliable to detect + * that the connection is closed. + */ + /* remove from descriptor set if not exceptional */ + if(!(wsanetevents.lNetworkEvents & (FD_OOB|FD_CLOSE))) + FD_CLR(sock, exceptfds); + } + } + + /* check if the event has not been filtered using specific tests */ + if(FD_ISSET(sock, readfds) || FD_ISSET(sock, writefds) || + FD_ISSET(sock, exceptfds)) { + ret++; + } + } + else { + /* remove from all descriptor sets since this handle did not trigger */ + FD_CLR(sock, readfds); + FD_CLR(sock, writefds); + FD_CLR(sock, exceptfds); + } + } + + for(idx = 0; idx < wsa; idx++) { + WSAEventSelect(data[idx].wsasock, NULL, 0); + WSACloseEvent(data[idx].wsaevent); + } + + for(idx = 0; idx < thd; idx++) { + WaitForSingleObject(data[idx].thread, INFINITE); + CloseHandle(data[idx].thread); + } + + CloseHandle(waitevent); + + free(handles); + free(data); + + return ret; +} +#define select(a,b,c,d,e) select_ws(a,b,c,d,e) +#endif /* USE_WINSOCK */ + /* sockfdp is a pointer to an established stream or CURL_SOCKET_BAD @@ -421,7 +891,7 @@ static bool juggle(curl_socket_t *sockfdp, fd_set fds_write; fd_set fds_err; curl_socket_t sockfd = CURL_SOCKET_BAD; - curl_socket_t maxfd = CURL_SOCKET_BAD; + int maxfd = -99; ssize_t rc; ssize_t nread_socket; ssize_t bytes_written; @@ -454,17 +924,7 @@ static bool juggle(curl_socket_t *sockfdp, FD_ZERO(&fds_write); FD_ZERO(&fds_err); -#ifdef USE_WINSOCK - /* - ** WinSock select() does not support standard file descriptors, - ** it can only check SOCKETs. Since this program in its current - ** state will not work on WinSock based systems, next line is - ** commented out to allow warning-free compilation awaiting the - ** day it will be fixed to also run on WinSock systems. - */ -#else - FD_SET(fileno(stdin), &fds_read); -#endif + FD_SET((curl_socket_t)fileno(stdin), &fds_read); switch(*mode) { @@ -474,7 +934,7 @@ static bool juggle(curl_socket_t *sockfdp, sockfd = listenfd; /* there's always a socket to wait for */ FD_SET(sockfd, &fds_read); - maxfd = sockfd; + maxfd = (int)sockfd; break; case PASSIVE_CONNECT: @@ -488,7 +948,10 @@ static bool juggle(curl_socket_t *sockfdp, else { /* there's always a socket to wait for */ FD_SET(sockfd, &fds_read); - maxfd = sockfd; +#ifdef USE_WINSOCK + FD_SET(sockfd, &fds_err); +#endif + maxfd = (int)sockfd; } break; @@ -498,7 +961,10 @@ static bool juggle(curl_socket_t *sockfdp, /* sockfd turns CURL_SOCKET_BAD when our connection has been closed */ if(CURL_SOCKET_BAD != sockfd) { FD_SET(sockfd, &fds_read); - maxfd = sockfd; +#ifdef USE_WINSOCK + FD_SET(sockfd, &fds_err); +#endif + maxfd = (int)sockfd; } else { logmsg("No socket to read on"); @@ -518,14 +984,16 @@ static bool juggle(curl_socket_t *sockfdp, do { - rc = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); + /* select() blocking behavior call on blocking descriptors please */ + + rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); if(got_exit_signal) { logmsg("signalled to die, exiting..."); return FALSE; } - } while((rc == -1) && ((error = SOCKERRNO) == EINTR)); + } while((rc == -1) && ((error = errno) == EINTR)); if(rc < 0) { logmsg("select() failed with error: (%d) %s", @@ -639,17 +1107,22 @@ static bool juggle(curl_socket_t *sockfdp, if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) { + curl_socket_t newfd = CURL_SOCKET_BAD; /* newly accepted socket */ + if(*mode == PASSIVE_LISTEN) { /* there's no stream set up yet, this is an indication that there's a client connecting. */ - sockfd = accept(sockfd, NULL, NULL); - if(CURL_SOCKET_BAD == sockfd) - logmsg("accept() failed"); + newfd = accept(sockfd, NULL, NULL); + if(CURL_SOCKET_BAD == newfd) { + error = SOCKERRNO; + logmsg("accept(%d, NULL, NULL) failed with error: (%d) %s", + sockfd, error, strerror(error)); + } else { logmsg("====> Client connect"); if(!write_stdout("CNCT\n", 5)) return FALSE; - *sockfdp = sockfd; /* store the new socket */ + *sockfdp = newfd; /* store the new socket */ *mode = PASSIVE_CONNECT; /* we have connected */ } return TRUE; @@ -658,7 +1131,22 @@ static bool juggle(curl_socket_t *sockfdp, /* read from socket, pass on data to stdout */ nread_socket = sread(sockfd, buffer, sizeof(buffer)); - if(nread_socket <= 0) { + if(nread_socket > 0) { + snprintf(data, sizeof(data), "DATA\n%04zx\n", nread_socket); + if(!write_stdout(data, 10)) + return FALSE; + if(!write_stdout(buffer, nread_socket)) + return FALSE; + + logmsg("< %zd bytes data, client => server", nread_socket); + lograw(buffer, nread_socket); + } + + if(nread_socket <= 0 +#ifdef USE_WINSOCK + || FD_ISSET(sockfd, &fds_err) +#endif + ) { logmsg("====> Client disconnect"); if(!write_stdout("DISC\n", 5)) return FALSE; @@ -670,15 +1158,6 @@ static bool juggle(curl_socket_t *sockfdp, *mode = ACTIVE_DISCONNECT; return TRUE; } - - snprintf(data, sizeof(data), "DATA\n%04zx\n", nread_socket); - if(!write_stdout(data, 10)) - return FALSE; - if(!write_stdout(buffer, nread_socket)) - return FALSE; - - logmsg("< %zd bytes data, client => server", nread_socket); - lograw(buffer, nread_socket); } return TRUE; @@ -688,10 +1167,7 @@ static curl_socket_t sockdaemon(curl_socket_t sock, unsigned short *listenport) { /* passive daemon style */ - struct sockaddr_in me; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 me6; -#endif /* ENABLE_IPV6 */ + srvr_sockaddr_union_t listener; int flag; int rc; int totdelay = 0; @@ -713,7 +1189,7 @@ static curl_socket_t sockdaemon(curl_socket_t sock, rc = wait_ms(delay); if(rc) { /* should not happen */ - error = SOCKERRNO; + error = errno; logmsg("wait_ms() failed with error: (%d) %s", error, strerror(error)); sclose(sock); @@ -742,19 +1218,19 @@ static curl_socket_t sockdaemon(curl_socket_t sock, #ifdef ENABLE_IPV6 if(!use_ipv6) { #endif - memset(&me, 0, sizeof(me)); - me.sin_family = AF_INET; - me.sin_addr.s_addr = INADDR_ANY; - me.sin_port = htons(*listenport); - rc = bind(sock, (struct sockaddr *) &me, sizeof(me)); + 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)); #ifdef ENABLE_IPV6 } else { - memset(&me6, 0, sizeof(me6)); - me6.sin6_family = AF_INET6; - me6.sin6_addr = in6addr_any; - me6.sin6_port = htons(*listenport); - rc = bind(sock, (struct sockaddr *) &me6, sizeof(me6)); + 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)); } #endif /* ENABLE_IPV6 */ if(rc) { @@ -769,36 +1245,30 @@ static curl_socket_t sockdaemon(curl_socket_t sock, /* 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; - struct sockaddr *localaddr; - struct sockaddr_in localaddr4; + srvr_sockaddr_union_t localaddr; #ifdef ENABLE_IPV6 - struct sockaddr_in6 localaddr6; - if(!use_ipv6) { + if(!use_ipv6) #endif - la_size = sizeof(localaddr4); - localaddr = (struct sockaddr *)&localaddr4; + la_size = sizeof(localaddr.sa4); #ifdef ENABLE_IPV6 - } - else { - la_size = sizeof(localaddr6); - localaddr = (struct sockaddr *)&localaddr6; - } + else + la_size = sizeof(localaddr.sa6); #endif - memset(localaddr, 0, (size_t)la_size); - if(getsockname(sock, localaddr, &la_size) < 0) { + memset(&localaddr.sa, 0, (size_t)la_size); + if(getsockname(sock, &localaddr.sa, &la_size) < 0) { error = SOCKERRNO; logmsg("getsockname() failed with error: (%d) %s", error, strerror(error)); sclose(sock); return CURL_SOCKET_BAD; } - switch (localaddr->sa_family) { + switch (localaddr.sa.sa_family) { case AF_INET: - *listenport = ntohs(localaddr4.sin_port); + *listenport = ntohs(localaddr.sa4.sin_port); break; #ifdef ENABLE_IPV6 case AF_INET6: - *listenport = ntohs(localaddr6.sin6_port); + *listenport = ntohs(localaddr.sa6.sin6_port); break; #endif default: @@ -815,12 +1285,18 @@ static curl_socket_t sockdaemon(curl_socket_t sock, } } + /* bindonly option forces no listening */ + if(bind_only) { + logmsg("instructed to bind port without listening"); + return sock; + } + /* start accepting connections */ rc = listen(sock, 5); if(0 != rc) { error = SOCKERRNO; - logmsg("listen() failed with error: (%d) %s", - error, strerror(error)); + logmsg("listen(%d, 5) failed with error: (%d) %s", + sock, error, strerror(error)); sclose(sock); return CURL_SOCKET_BAD; } @@ -831,10 +1307,7 @@ static curl_socket_t sockdaemon(curl_socket_t sock, int main(int argc, char *argv[]) { - struct sockaddr_in me; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 me6; -#endif /* ENABLE_IPV6 */ + srvr_sockaddr_union_t me; curl_socket_t sock = CURL_SOCKET_BAD; curl_socket_t msgsock = CURL_SOCKET_BAD; int wrotepidfile = 0; @@ -886,6 +1359,10 @@ int main(int argc, char *argv[]) #endif arg++; } + else if(!strcmp("--bindonly", argv[arg])) { + bind_only = TRUE; + arg++; + } else if(!strcmp("--port", argv[arg])) { arg++; if(argc>arg) { @@ -934,6 +1411,7 @@ int main(int argc, char *argv[]) " --pidfile [file]\n" " --ipv4\n" " --ipv6\n" + " --bindonly\n" " --port [port]\n" " --connect [port]\n" " --addr [address]"); @@ -944,6 +1422,10 @@ int main(int argc, char *argv[]) #ifdef WIN32 win32_init(); atexit(win32_cleanup); + + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); + setmode(fileno(stderr), O_BINARY); #endif install_signal_handlers(); @@ -961,6 +1443,7 @@ int main(int argc, char *argv[]) error = SOCKERRNO; logmsg("Error creating socket: (%d) %s", error, strerror(error)); + write_stdout("FAIL\n", 5); goto sockfilt_cleanup; } @@ -970,32 +1453,33 @@ int main(int argc, char *argv[]) #ifdef ENABLE_IPV6 if(!use_ipv6) { #endif - memset(&me, 0, sizeof(me)); - me.sin_family = AF_INET; - me.sin_port = htons(connectport); - me.sin_addr.s_addr = INADDR_ANY; + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_port = htons(connectport); + me.sa4.sin_addr.s_addr = INADDR_ANY; if (!addr) addr = "127.0.0.1"; - Curl_inet_pton(AF_INET, addr, &me.sin_addr); + Curl_inet_pton(AF_INET, addr, &me.sa4.sin_addr); - rc = connect(sock, (struct sockaddr *) &me, sizeof(me)); + rc = connect(sock, &me.sa, sizeof(me.sa4)); #ifdef ENABLE_IPV6 } else { - memset(&me6, 0, sizeof(me6)); - me6.sin6_family = AF_INET6; - me6.sin6_port = htons(connectport); + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_port = htons(connectport); if (!addr) addr = "::1"; - Curl_inet_pton(AF_INET6, addr, &me6.sin6_addr); + Curl_inet_pton(AF_INET6, addr, &me.sa6.sin6_addr); - rc = connect(sock, (struct sockaddr *) &me6, sizeof(me6)); + rc = connect(sock, &me.sa, sizeof(me.sa6)); } #endif /* ENABLE_IPV6 */ if(rc) { error = SOCKERRNO; logmsg("Error connecting to port %hu: (%d) %s", connectport, error, strerror(error)); + write_stdout("FAIL\n", 5); goto sockfilt_cleanup; } logmsg("====> Client connect"); @@ -1004,8 +1488,10 @@ int main(int argc, char *argv[]) else { /* passive daemon style */ sock = sockdaemon(sock, &port); - if(CURL_SOCKET_BAD == sock) + if(CURL_SOCKET_BAD == sock) { + write_stdout("FAIL\n", 5); goto sockfilt_cleanup; + } msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ } @@ -1013,12 +1499,16 @@ int main(int argc, char *argv[]) if(connectport) logmsg("Connected to port %hu", connectport); + else if(bind_only) + logmsg("Bound without listening on port %hu", port); else logmsg("Listening on port %hu", port); wrotepidfile = write_pidfile(pidname); - if(!wrotepidfile) + if(!wrotepidfile) { + write_stdout("FAIL\n", 5); goto sockfilt_cleanup; + } do { juggle_again = juggle(&msgsock, sock, &mode); |