1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 #include "server_setup.h" 23 24 /* Purpose 25 * 26 * 1. Accept a TCP connection on a custom port (IPv4 or IPv6), or connect 27 * to a given (localhost) port. 28 * 29 * 2. Get commands on STDIN. Pass data on to the TCP stream. 30 * Get data from TCP stream and pass on to STDOUT. 31 * 32 * This program is made to perform all the socket/stream/connection stuff for 33 * the test suite's (perl) FTP server. Previously the perl code did all of 34 * this by its own, but I decided to let this program do the socket layer 35 * because of several things: 36 * 37 * o We want the perl code to work with rather old perl installations, thus 38 * we cannot use recent perl modules or features. 39 * 40 * o We want IPv6 support for systems that provide it, and doing optional IPv6 41 * support in perl seems if not impossible so at least awkward. 42 * 43 * o We want FTP-SSL support, which means that a connection that starts with 44 * plain sockets needs to be able to "go SSL" in the midst. This would also 45 * require some nasty perl stuff I'd rather avoid. 46 * 47 * (Source originally based on sws.c) 48 */ 49 50 /* 51 * Signal handling notes for sockfilt 52 * ---------------------------------- 53 * 54 * This program is a single-threaded process. 55 * 56 * This program is intended to be highly portable and as such it must be kept 57 * as simple as possible, due to this the only signal handling mechanisms used 58 * will be those of ANSI C, and used only in the most basic form which is good 59 * enough for the purpose of this program. 60 * 61 * For the above reason and the specific needs of this program signals SIGHUP, 62 * SIGPIPE and SIGALRM will be simply ignored on systems where this can be 63 * done. If possible, signals SIGINT and SIGTERM will be handled by this 64 * program as an indication to cleanup and finish execution as soon as 65 * possible. This will be achieved with a single signal handler 66 * 'exit_signal_handler' for both signals. 67 * 68 * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal 69 * will just set to one the global var 'got_exit_signal' storing in global var 70 * 'exit_signal' the signal that triggered this change. 71 * 72 * Nothing fancy that could introduce problems is used, the program at certain 73 * points in its normal flow checks if var 'got_exit_signal' is set and in 74 * case this is true it just makes its way out of loops and functions in 75 * structured and well behaved manner to achieve proper program cleanup and 76 * termination. 77 * 78 * Even with the above mechanism implemented it is worthwile to note that 79 * other signals might still be received, or that there might be systems on 80 * which it is not possible to trap and ignore some of the above signals. 81 * This implies that for increased portability and reliability the program 82 * must be coded as if no signal was being ignored or handled at all. Enjoy 83 * it! 84 */ 85 86 #ifdef HAVE_SIGNAL_H 87 #include <signal.h> 88 #endif 89 #ifdef HAVE_NETINET_IN_H 90 #include <netinet/in.h> 91 #endif 92 #ifdef HAVE_NETINET_IN6_H 93 #include <netinet/in6.h> 94 #endif 95 #ifdef HAVE_ARPA_INET_H 96 #include <arpa/inet.h> 97 #endif 98 #ifdef HAVE_NETDB_H 99 #include <netdb.h> 100 #endif 101 102 #define ENABLE_CURLX_PRINTF 103 /* make the curlx header define all printf() functions to use the curlx_* 104 versions instead */ 105 #include "curlx.h" /* from the private lib dir */ 106 #include "getpart.h" 107 #include "inet_pton.h" 108 #include "util.h" 109 #include "server_sockaddr.h" 110 #include "warnless.h" 111 112 /* include memdebug.h last */ 113 #include "memdebug.h" 114 115 #ifdef USE_WINSOCK 116 #undef EINTR 117 #define EINTR 4 /* errno.h value */ 118 #undef EAGAIN 119 #define EAGAIN 11 /* errno.h value */ 120 #undef ENOMEM 121 #define ENOMEM 12 /* errno.h value */ 122 #undef EINVAL 123 #define EINVAL 22 /* errno.h value */ 124 #endif 125 126 #define DEFAULT_PORT 8999 127 128 #ifndef DEFAULT_LOGFILE 129 #define DEFAULT_LOGFILE "log/sockfilt.log" 130 #endif 131 132 const char *serverlogfile = DEFAULT_LOGFILE; 133 134 static bool verbose = FALSE; 135 static bool bind_only = FALSE; 136 #ifdef ENABLE_IPV6 137 static bool use_ipv6 = FALSE; 138 #endif 139 static const char *ipv_inuse = "IPv4"; 140 static unsigned short port = DEFAULT_PORT; 141 static unsigned short connectport = 0; /* if non-zero, we activate this mode */ 142 143 enum sockmode { 144 PASSIVE_LISTEN, /* as a server waiting for connections */ 145 PASSIVE_CONNECT, /* as a server, connected to a client */ 146 ACTIVE, /* as a client, connected to a server */ 147 ACTIVE_DISCONNECT /* as a client, disconnected from server */ 148 }; 149 150 /* do-nothing macro replacement for systems which lack siginterrupt() */ 151 152 #ifndef HAVE_SIGINTERRUPT 153 #define siginterrupt(x,y) do {} while(0) 154 #endif 155 156 /* vars used to keep around previous signal handlers */ 157 158 typedef RETSIGTYPE (*SIGHANDLER_T)(int); 159 160 #ifdef SIGHUP 161 static SIGHANDLER_T old_sighup_handler = SIG_ERR; 162 #endif 163 164 #ifdef SIGPIPE 165 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; 166 #endif 167 168 #ifdef SIGALRM 169 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; 170 #endif 171 172 #ifdef SIGINT 173 static SIGHANDLER_T old_sigint_handler = SIG_ERR; 174 #endif 175 176 #ifdef SIGTERM 177 static SIGHANDLER_T old_sigterm_handler = SIG_ERR; 178 #endif 179 180 #if defined(SIGBREAK) && defined(WIN32) 181 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; 182 #endif 183 184 /* var which if set indicates that the program should finish execution */ 185 186 SIG_ATOMIC_T got_exit_signal = 0; 187 188 /* if next is set indicates the first signal handled in exit_signal_handler */ 189 190 static volatile int exit_signal = 0; 191 192 /* signal handler that will be triggered to indicate that the program 193 should finish its execution in a controlled manner as soon as possible. 194 The first time this is called it will set got_exit_signal to one and 195 store in exit_signal the signal that triggered its execution. */ 196 197 static RETSIGTYPE exit_signal_handler(int signum) 198 { 199 int old_errno = errno; 200 if(got_exit_signal == 0) { 201 got_exit_signal = 1; 202 exit_signal = signum; 203 } 204 (void)signal(signum, exit_signal_handler); 205 errno = old_errno; 206 } 207 208 static void install_signal_handlers(void) 209 { 210 #ifdef SIGHUP 211 /* ignore SIGHUP signal */ 212 old_sighup_handler = signal(SIGHUP, SIG_IGN); 213 if(old_sighup_handler == SIG_ERR) 214 logmsg("cannot install SIGHUP handler: %s", strerror(errno)); 215 #endif 216 #ifdef SIGPIPE 217 /* ignore SIGPIPE signal */ 218 old_sigpipe_handler = signal(SIGPIPE, SIG_IGN); 219 if(old_sigpipe_handler == SIG_ERR) 220 logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); 221 #endif 222 #ifdef SIGALRM 223 /* ignore SIGALRM signal */ 224 old_sigalrm_handler = signal(SIGALRM, SIG_IGN); 225 if(old_sigalrm_handler == SIG_ERR) 226 logmsg("cannot install SIGALRM handler: %s", strerror(errno)); 227 #endif 228 #ifdef SIGINT 229 /* handle SIGINT signal with our exit_signal_handler */ 230 old_sigint_handler = signal(SIGINT, exit_signal_handler); 231 if(old_sigint_handler == SIG_ERR) 232 logmsg("cannot install SIGINT handler: %s", strerror(errno)); 233 else 234 siginterrupt(SIGINT, 1); 235 #endif 236 #ifdef SIGTERM 237 /* handle SIGTERM signal with our exit_signal_handler */ 238 old_sigterm_handler = signal(SIGTERM, exit_signal_handler); 239 if(old_sigterm_handler == SIG_ERR) 240 logmsg("cannot install SIGTERM handler: %s", strerror(errno)); 241 else 242 siginterrupt(SIGTERM, 1); 243 #endif 244 #if defined(SIGBREAK) && defined(WIN32) 245 /* handle SIGBREAK signal with our exit_signal_handler */ 246 old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler); 247 if(old_sigbreak_handler == SIG_ERR) 248 logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); 249 else 250 siginterrupt(SIGBREAK, 1); 251 #endif 252 } 253 254 static void restore_signal_handlers(void) 255 { 256 #ifdef SIGHUP 257 if(SIG_ERR != old_sighup_handler) 258 (void)signal(SIGHUP, old_sighup_handler); 259 #endif 260 #ifdef SIGPIPE 261 if(SIG_ERR != old_sigpipe_handler) 262 (void)signal(SIGPIPE, old_sigpipe_handler); 263 #endif 264 #ifdef SIGALRM 265 if(SIG_ERR != old_sigalrm_handler) 266 (void)signal(SIGALRM, old_sigalrm_handler); 267 #endif 268 #ifdef SIGINT 269 if(SIG_ERR != old_sigint_handler) 270 (void)signal(SIGINT, old_sigint_handler); 271 #endif 272 #ifdef SIGTERM 273 if(SIG_ERR != old_sigterm_handler) 274 (void)signal(SIGTERM, old_sigterm_handler); 275 #endif 276 #if defined(SIGBREAK) && defined(WIN32) 277 if(SIG_ERR != old_sigbreak_handler) 278 (void)signal(SIGBREAK, old_sigbreak_handler); 279 #endif 280 } 281 282 #ifdef WIN32 283 /* 284 * read-wrapper to support reading from stdin on Windows. 285 */ 286 static ssize_t read_wincon(int fd, void *buf, size_t count) 287 { 288 HANDLE handle = NULL; 289 DWORD mode, rcount = 0; 290 BOOL success; 291 292 if(fd == fileno(stdin)) { 293 handle = GetStdHandle(STD_INPUT_HANDLE); 294 } 295 else { 296 return read(fd, buf, count); 297 } 298 299 if(GetConsoleMode(handle, &mode)) { 300 success = ReadConsole(handle, buf, curlx_uztoul(count), &rcount, NULL); 301 } 302 else { 303 success = ReadFile(handle, buf, curlx_uztoul(count), &rcount, NULL); 304 } 305 if(success) { 306 return rcount; 307 } 308 309 errno = GetLastError(); 310 return -1; 311 } 312 #undef read 313 #define read(a,b,c) read_wincon(a,b,c) 314 315 /* 316 * write-wrapper to support writing to stdout and stderr on Windows. 317 */ 318 static ssize_t write_wincon(int fd, const void *buf, size_t count) 319 { 320 HANDLE handle = NULL; 321 DWORD mode, wcount = 0; 322 BOOL success; 323 324 if(fd == fileno(stdout)) { 325 handle = GetStdHandle(STD_OUTPUT_HANDLE); 326 } 327 else if(fd == fileno(stderr)) { 328 handle = GetStdHandle(STD_ERROR_HANDLE); 329 } 330 else { 331 return write(fd, buf, count); 332 } 333 334 if(GetConsoleMode(handle, &mode)) { 335 success = WriteConsole(handle, buf, curlx_uztoul(count), &wcount, NULL); 336 } 337 else { 338 success = WriteFile(handle, buf, curlx_uztoul(count), &wcount, NULL); 339 } 340 if(success) { 341 return wcount; 342 } 343 344 errno = GetLastError(); 345 return -1; 346 } 347 #undef write 348 #define write(a,b,c) write_wincon(a,b,c) 349 #endif 350 351 /* 352 * fullread is a wrapper around the read() function. This will repeat the call 353 * to read() until it actually has read the complete number of bytes indicated 354 * in nbytes or it fails with a condition that cannot be handled with a simple 355 * retry of the read call. 356 */ 357 358 static ssize_t fullread(int filedes, void *buffer, size_t nbytes) 359 { 360 int error; 361 ssize_t nread = 0; 362 363 do { 364 ssize_t rc = read(filedes, 365 (unsigned char *)buffer + nread, nbytes - nread); 366 367 if(got_exit_signal) { 368 logmsg("signalled to die"); 369 return -1; 370 } 371 372 if(rc < 0) { 373 error = errno; 374 if((error == EINTR) || (error == EAGAIN)) 375 continue; 376 logmsg("reading from file descriptor: %d,", filedes); 377 logmsg("unrecoverable read() failure: (%d) %s", 378 error, strerror(error)); 379 return -1; 380 } 381 382 if(rc == 0) { 383 logmsg("got 0 reading from stdin"); 384 return 0; 385 } 386 387 nread += rc; 388 389 } while((size_t)nread < nbytes); 390 391 if(verbose) 392 logmsg("read %zd bytes", nread); 393 394 return nread; 395 } 396 397 /* 398 * fullwrite is a wrapper around the write() function. This will repeat the 399 * call to write() until it actually has written the complete number of bytes 400 * indicated in nbytes or it fails with a condition that cannot be handled 401 * with a simple retry of the write call. 402 */ 403 404 static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) 405 { 406 int error; 407 ssize_t nwrite = 0; 408 409 do { 410 ssize_t wc = write(filedes, (const unsigned char *)buffer + nwrite, 411 nbytes - nwrite); 412 413 if(got_exit_signal) { 414 logmsg("signalled to die"); 415 return -1; 416 } 417 418 if(wc < 0) { 419 error = errno; 420 if((error == EINTR) || (error == EAGAIN)) 421 continue; 422 logmsg("writing to file descriptor: %d,", filedes); 423 logmsg("unrecoverable write() failure: (%d) %s", 424 error, strerror(error)); 425 return -1; 426 } 427 428 if(wc == 0) { 429 logmsg("put 0 writing to stdout"); 430 return 0; 431 } 432 433 nwrite += wc; 434 435 } while((size_t)nwrite < nbytes); 436 437 if(verbose) 438 logmsg("wrote %zd bytes", nwrite); 439 440 return nwrite; 441 } 442 443 /* 444 * read_stdin tries to read from stdin nbytes into the given buffer. This is a 445 * blocking function that will only return TRUE when nbytes have actually been 446 * read or FALSE when an unrecoverable error has been detected. Failure of this 447 * function is an indication that the sockfilt process should terminate. 448 */ 449 450 static bool read_stdin(void *buffer, size_t nbytes) 451 { 452 ssize_t nread = fullread(fileno(stdin), buffer, nbytes); 453 if(nread != (ssize_t)nbytes) { 454 logmsg("exiting..."); 455 return FALSE; 456 } 457 return TRUE; 458 } 459 460 /* 461 * write_stdout tries to write to stdio nbytes from the given buffer. This is a 462 * blocking function that will only return TRUE when nbytes have actually been 463 * written or FALSE when an unrecoverable error has been detected. Failure of 464 * this function is an indication that the sockfilt process should terminate. 465 */ 466 467 static bool write_stdout(const void *buffer, size_t nbytes) 468 { 469 ssize_t nwrite = fullwrite(fileno(stdout), buffer, nbytes); 470 if(nwrite != (ssize_t)nbytes) { 471 logmsg("exiting..."); 472 return FALSE; 473 } 474 return TRUE; 475 } 476 477 static void lograw(unsigned char *buffer, ssize_t len) 478 { 479 char data[120]; 480 ssize_t i; 481 unsigned char *ptr = buffer; 482 char *optr = data; 483 ssize_t width = 0; 484 int left = sizeof(data); 485 486 for(i = 0; i<len; i++) { 487 switch(ptr[i]) { 488 case '\n': 489 msnprintf(optr, left, "\\n"); 490 width += 2; 491 optr += 2; 492 left -= 2; 493 break; 494 case '\r': 495 msnprintf(optr, left, "\\r"); 496 width += 2; 497 optr += 2; 498 left -= 2; 499 break; 500 default: 501 msnprintf(optr, left, "%c", (ISGRAPH(ptr[i]) || 502 ptr[i] == 0x20) ?ptr[i]:'.'); 503 width++; 504 optr++; 505 left--; 506 break; 507 } 508 509 if(width>60) { 510 logmsg("'%s'", data); 511 width = 0; 512 optr = data; 513 left = sizeof(data); 514 } 515 } 516 if(width) 517 logmsg("'%s'", data); 518 } 519 520 #ifdef USE_WINSOCK 521 /* 522 * WinSock select() does not support standard file descriptors, 523 * it can only check SOCKETs. The following function is an attempt 524 * to re-create a select() function with support for other handle types. 525 * 526 * select() function with support for WINSOCK2 sockets and all 527 * other handle types supported by WaitForMultipleObjectsEx() as 528 * well as disk files, anonymous and names pipes, and character input. 529 * 530 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx 531 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx 532 */ 533 struct select_ws_wait_data { 534 HANDLE handle; /* actual handle to wait for during select */ 535 HANDLE event; /* internal event to abort waiting thread */ 536 }; 537 static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter) 538 { 539 struct select_ws_wait_data *data; 540 HANDLE handle, handles[2]; 541 INPUT_RECORD inputrecord; 542 LARGE_INTEGER size, pos; 543 DWORD type, length; 544 545 /* retrieve handles from internal structure */ 546 data = (struct select_ws_wait_data *) lpParameter; 547 if(data) { 548 handle = data->handle; 549 handles[0] = data->event; 550 handles[1] = handle; 551 free(data); 552 } 553 else 554 return (DWORD)-1; 555 556 /* retrieve the type of file to wait on */ 557 type = GetFileType(handle); 558 switch(type) { 559 case FILE_TYPE_DISK: 560 /* The handle represents a file on disk, this means: 561 * - WaitForMultipleObjectsEx will always be signalled for it. 562 * - comparison of current position in file and total size of 563 * the file can be used to check if we reached the end yet. 564 * 565 * Approach: Loop till either the internal event is signalled 566 * or if the end of the file has already been reached. 567 */ 568 while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) 569 == WAIT_TIMEOUT) { 570 /* get total size of file */ 571 length = 0; 572 size.QuadPart = 0; 573 size.LowPart = GetFileSize(handle, &length); 574 if((size.LowPart != INVALID_FILE_SIZE) || 575 (GetLastError() == NO_ERROR)) { 576 size.HighPart = length; 577 /* get the current position within the file */ 578 pos.QuadPart = 0; 579 pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart, 580 FILE_CURRENT); 581 if((pos.LowPart != INVALID_SET_FILE_POINTER) || 582 (GetLastError() == NO_ERROR)) { 583 /* compare position with size, abort if not equal */ 584 if(size.QuadPart == pos.QuadPart) { 585 /* sleep and continue waiting */ 586 SleepEx(0, FALSE); 587 continue; 588 } 589 } 590 } 591 /* there is some data available, stop waiting */ 592 break; 593 } 594 break; 595 596 case FILE_TYPE_CHAR: 597 /* The handle represents a character input, this means: 598 * - WaitForMultipleObjectsEx will be signalled on any kind of input, 599 * including mouse and window size events we do not care about. 600 * 601 * Approach: Loop till either the internal event is signalled 602 * or we get signalled for an actual key-event. 603 */ 604 while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) 605 == WAIT_OBJECT_0 + 1) { 606 /* check if this is an actual console handle */ 607 length = 0; 608 if(GetConsoleMode(handle, &length)) { 609 /* retrieve an event from the console buffer */ 610 length = 0; 611 if(PeekConsoleInput(handle, &inputrecord, 1, &length)) { 612 /* check if the event is not an actual key-event */ 613 if(length == 1 && inputrecord.EventType != KEY_EVENT) { 614 /* purge the non-key-event and continue waiting */ 615 ReadConsoleInput(handle, &inputrecord, 1, &length); 616 continue; 617 } 618 } 619 } 620 /* there is some data available, stop waiting */ 621 break; 622 } 623 break; 624 625 case FILE_TYPE_PIPE: 626 /* The handle represents an anonymous or named pipe, this means: 627 * - WaitForMultipleObjectsEx will always be signalled for it. 628 * - peek into the pipe and retrieve the amount of data available. 629 * 630 * Approach: Loop till either the internal event is signalled 631 * or there is data in the pipe available for reading. 632 */ 633 while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) 634 == WAIT_TIMEOUT) { 635 /* peek into the pipe and retrieve the amount of data available */ 636 length = 0; 637 if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) { 638 /* if there is no data available, sleep and continue waiting */ 639 if(length == 0) { 640 SleepEx(0, FALSE); 641 continue; 642 } 643 } 644 else { 645 /* if the pipe has been closed, sleep and continue waiting */ 646 if(GetLastError() == ERROR_BROKEN_PIPE) { 647 SleepEx(0, FALSE); 648 continue; 649 } 650 } 651 /* there is some data available, stop waiting */ 652 break; 653 } 654 break; 655 656 default: 657 /* The handle has an unknown type, try to wait on it */ 658 WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE); 659 break; 660 } 661 662 return 0; 663 } 664 static HANDLE select_ws_wait(HANDLE handle, HANDLE event) 665 { 666 struct select_ws_wait_data *data; 667 HANDLE thread = NULL; 668 669 /* allocate internal waiting data structure */ 670 data = malloc(sizeof(struct select_ws_wait_data)); 671 if(data) { 672 data->handle = handle; 673 data->event = event; 674 675 /* launch waiting thread */ 676 thread = CreateThread(NULL, 0, 677 &select_ws_wait_thread, 678 data, 0, NULL); 679 680 /* free data if thread failed to launch */ 681 if(!thread) { 682 free(data); 683 } 684 } 685 686 return thread; 687 } 688 struct select_ws_data { 689 curl_socket_t fd; /* the original input handle (indexed by fds) */ 690 curl_socket_t wsasock; /* the internal socket handle (indexed by wsa) */ 691 WSAEVENT wsaevent; /* the internal WINSOCK2 event (indexed by wsa) */ 692 HANDLE thread; /* the internal threads handle (indexed by thd) */ 693 }; 694 static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, 695 fd_set *exceptfds, struct timeval *timeout) 696 { 697 DWORD milliseconds, wait, idx; 698 WSANETWORKEVENTS wsanetevents; 699 struct select_ws_data *data; 700 HANDLE handle, *handles; 701 WSAEVENT wsaevent; 702 int error, fds; 703 HANDLE waitevent = NULL; 704 DWORD nfd = 0, thd = 0, wsa = 0; 705 int ret = 0; 706 707 /* check if the input value is valid */ 708 if(nfds < 0) { 709 errno = EINVAL; 710 return -1; 711 } 712 713 /* check if we got descriptors, sleep in case we got none */ 714 if(!nfds) { 715 Sleep((timeout->tv_sec*1000)+(DWORD)(((double)timeout->tv_usec)/1000.0)); 716 return 0; 717 } 718 719 /* create internal event to signal waiting threads */ 720 waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); 721 if(!waitevent) { 722 errno = ENOMEM; 723 return -1; 724 } 725 726 /* allocate internal array for the internal data */ 727 data = calloc(nfds, sizeof(struct select_ws_data)); 728 if(data == NULL) { 729 CloseHandle(waitevent); 730 errno = ENOMEM; 731 return -1; 732 } 733 734 /* allocate internal array for the internal event handles */ 735 handles = calloc(nfds, sizeof(HANDLE)); 736 if(handles == NULL) { 737 CloseHandle(waitevent); 738 free(data); 739 errno = ENOMEM; 740 return -1; 741 } 742 743 /* loop over the handles in the input descriptor sets */ 744 for(fds = 0; fds < nfds; fds++) { 745 long networkevents = 0; 746 handles[nfd] = 0; 747 748 if(FD_ISSET(fds, readfds)) 749 networkevents |= FD_READ|FD_ACCEPT|FD_CLOSE; 750 751 if(FD_ISSET(fds, writefds)) 752 networkevents |= FD_WRITE|FD_CONNECT; 753 754 if(FD_ISSET(fds, exceptfds)) 755 networkevents |= FD_OOB|FD_CLOSE; 756 757 /* only wait for events for which we actually care */ 758 if(networkevents) { 759 data[nfd].fd = curlx_sitosk(fds); 760 if(fds == fileno(stdin)) { 761 handle = GetStdHandle(STD_INPUT_HANDLE); 762 handle = select_ws_wait(handle, waitevent); 763 handles[nfd] = handle; 764 data[thd].thread = handle; 765 thd++; 766 } 767 else if(fds == fileno(stdout)) { 768 handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE); 769 } 770 else if(fds == fileno(stderr)) { 771 handles[nfd] = GetStdHandle(STD_ERROR_HANDLE); 772 } 773 else { 774 wsaevent = WSACreateEvent(); 775 if(wsaevent != WSA_INVALID_EVENT) { 776 error = WSAEventSelect(fds, wsaevent, networkevents); 777 if(error != SOCKET_ERROR) { 778 handle = (HANDLE) wsaevent; 779 handles[nfd] = handle; 780 data[wsa].wsasock = curlx_sitosk(fds); 781 data[wsa].wsaevent = wsaevent; 782 wsa++; 783 } 784 else { 785 curl_socket_t socket = curlx_sitosk(fds); 786 WSACloseEvent(wsaevent); 787 handle = (HANDLE) socket; 788 handle = select_ws_wait(handle, waitevent); 789 handles[nfd] = handle; 790 data[thd].thread = handle; 791 thd++; 792 } 793 } 794 } 795 nfd++; 796 } 797 } 798 799 /* convert struct timeval to milliseconds */ 800 if(timeout) { 801 milliseconds = ((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); 802 } 803 else { 804 milliseconds = INFINITE; 805 } 806 807 /* wait for one of the internal handles to trigger */ 808 wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE); 809 810 /* signal the event handle for the waiting threads */ 811 SetEvent(waitevent); 812 813 /* loop over the internal handles returned in the descriptors */ 814 for(idx = 0; idx < nfd; idx++) { 815 curl_socket_t sock = data[idx].fd; 816 handle = handles[idx]; 817 fds = curlx_sktosi(sock); 818 819 /* check if the current internal handle was triggered */ 820 if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= idx && 821 WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) { 822 /* first handle stdin, stdout and stderr */ 823 if(fds == fileno(stdin)) { 824 /* stdin is never ready for write or exceptional */ 825 FD_CLR(sock, writefds); 826 FD_CLR(sock, exceptfds); 827 } 828 else if(fds == fileno(stdout) || fds == fileno(stderr)) { 829 /* stdout and stderr are never ready for read or exceptional */ 830 FD_CLR(sock, readfds); 831 FD_CLR(sock, exceptfds); 832 } 833 else { 834 /* try to handle the event with the WINSOCK2 functions */ 835 wsanetevents.lNetworkEvents = 0; 836 error = WSAEnumNetworkEvents(fds, handle, &wsanetevents); 837 if(error != SOCKET_ERROR) { 838 /* remove from descriptor set if not ready for read/accept/close */ 839 if(!(wsanetevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))) 840 FD_CLR(sock, readfds); 841 842 /* remove from descriptor set if not ready for write/connect */ 843 if(!(wsanetevents.lNetworkEvents & (FD_WRITE|FD_CONNECT))) 844 FD_CLR(sock, writefds); 845 846 /* HACK: 847 * use exceptfds together with readfds to signal 848 * that the connection was closed by the client. 849 * 850 * Reason: FD_CLOSE is only signaled once, sometimes 851 * at the same time as FD_READ with data being available. 852 * This means that recv/sread is not reliable to detect 853 * that the connection is closed. 854 */ 855 /* remove from descriptor set if not exceptional */ 856 if(!(wsanetevents.lNetworkEvents & (FD_OOB|FD_CLOSE))) 857 FD_CLR(sock, exceptfds); 858 } 859 } 860 861 /* check if the event has not been filtered using specific tests */ 862 if(FD_ISSET(sock, readfds) || FD_ISSET(sock, writefds) || 863 FD_ISSET(sock, exceptfds)) { 864 ret++; 865 } 866 } 867 else { 868 /* remove from all descriptor sets since this handle did not trigger */ 869 FD_CLR(sock, readfds); 870 FD_CLR(sock, writefds); 871 FD_CLR(sock, exceptfds); 872 } 873 } 874 875 for(fds = 0; fds < nfds; fds++) { 876 if(FD_ISSET(fds, readfds)) 877 logmsg("select_ws: %d is readable", fds); 878 879 if(FD_ISSET(fds, writefds)) 880 logmsg("select_ws: %d is writable", fds); 881 882 if(FD_ISSET(fds, exceptfds)) 883 logmsg("select_ws: %d is excepted", fds); 884 } 885 886 for(idx = 0; idx < wsa; idx++) { 887 WSAEventSelect(data[idx].wsasock, NULL, 0); 888 WSACloseEvent(data[idx].wsaevent); 889 } 890 891 for(idx = 0; idx < thd; idx++) { 892 WaitForSingleObject(data[idx].thread, INFINITE); 893 CloseHandle(data[idx].thread); 894 } 895 896 CloseHandle(waitevent); 897 898 free(handles); 899 free(data); 900 901 return ret; 902 } 903 #define select(a,b,c,d,e) select_ws(a,b,c,d,e) 904 #endif /* USE_WINSOCK */ 905 906 /* 907 sockfdp is a pointer to an established stream or CURL_SOCKET_BAD 908 909 if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must 910 accept() 911 */ 912 static bool juggle(curl_socket_t *sockfdp, 913 curl_socket_t listenfd, 914 enum sockmode *mode) 915 { 916 struct timeval timeout; 917 fd_set fds_read; 918 fd_set fds_write; 919 fd_set fds_err; 920 curl_socket_t sockfd = CURL_SOCKET_BAD; 921 int maxfd = -99; 922 ssize_t rc; 923 int error = 0; 924 925 /* 'buffer' is this excessively large only to be able to support things like 926 test 1003 which tests exceedingly large server response lines */ 927 unsigned char buffer[17010]; 928 char data[16]; 929 930 if(got_exit_signal) { 931 logmsg("signalled to die, exiting..."); 932 return FALSE; 933 } 934 935 #ifdef HAVE_GETPPID 936 /* As a last resort, quit if sockfilt process becomes orphan. Just in case 937 parent ftpserver process has died without killing its sockfilt children */ 938 if(getppid() <= 1) { 939 logmsg("process becomes orphan, exiting"); 940 return FALSE; 941 } 942 #endif 943 944 timeout.tv_sec = 120; 945 timeout.tv_usec = 0; 946 947 FD_ZERO(&fds_read); 948 FD_ZERO(&fds_write); 949 FD_ZERO(&fds_err); 950 951 FD_SET((curl_socket_t)fileno(stdin), &fds_read); 952 953 switch(*mode) { 954 955 case PASSIVE_LISTEN: 956 957 /* server mode */ 958 sockfd = listenfd; 959 /* there's always a socket to wait for */ 960 FD_SET(sockfd, &fds_read); 961 maxfd = (int)sockfd; 962 break; 963 964 case PASSIVE_CONNECT: 965 966 sockfd = *sockfdp; 967 if(CURL_SOCKET_BAD == sockfd) { 968 /* eeek, we are supposedly connected and then this cannot be -1 ! */ 969 logmsg("socket is -1! on %s:%d", __FILE__, __LINE__); 970 maxfd = 0; /* stdin */ 971 } 972 else { 973 /* there's always a socket to wait for */ 974 FD_SET(sockfd, &fds_read); 975 #ifdef USE_WINSOCK 976 FD_SET(sockfd, &fds_err); 977 #endif 978 maxfd = (int)sockfd; 979 } 980 break; 981 982 case ACTIVE: 983 984 sockfd = *sockfdp; 985 /* sockfd turns CURL_SOCKET_BAD when our connection has been closed */ 986 if(CURL_SOCKET_BAD != sockfd) { 987 FD_SET(sockfd, &fds_read); 988 #ifdef USE_WINSOCK 989 FD_SET(sockfd, &fds_err); 990 #endif 991 maxfd = (int)sockfd; 992 } 993 else { 994 logmsg("No socket to read on"); 995 maxfd = 0; 996 } 997 break; 998 999 case ACTIVE_DISCONNECT: 1000 1001 logmsg("disconnected, no socket to read on"); 1002 maxfd = 0; 1003 sockfd = CURL_SOCKET_BAD; 1004 break; 1005 1006 } /* switch(*mode) */ 1007 1008 1009 do { 1010 1011 /* select() blocking behavior call on blocking descriptors please */ 1012 1013 rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); 1014 1015 if(got_exit_signal) { 1016 logmsg("signalled to die, exiting..."); 1017 return FALSE; 1018 } 1019 1020 } while((rc == -1) && ((error = errno) == EINTR)); 1021 1022 if(rc < 0) { 1023 logmsg("select() failed with error: (%d) %s", 1024 error, strerror(error)); 1025 return FALSE; 1026 } 1027 1028 if(rc == 0) 1029 /* timeout */ 1030 return TRUE; 1031 1032 1033 if(FD_ISSET(fileno(stdin), &fds_read)) { 1034 ssize_t buffer_len; 1035 /* read from stdin, commands/data to be dealt with and possibly passed on 1036 to the socket 1037 1038 protocol: 1039 1040 4 letter command + LF [mandatory] 1041 1042 4-digit hexadecimal data length + LF [if the command takes data] 1043 data [the data being as long as set above] 1044 1045 Commands: 1046 1047 DATA - plain pass-through data 1048 */ 1049 1050 if(!read_stdin(buffer, 5)) 1051 return FALSE; 1052 1053 logmsg("Received %c%c%c%c (on stdin)", 1054 buffer[0], buffer[1], buffer[2], buffer[3]); 1055 1056 if(!memcmp("PING", buffer, 4)) { 1057 /* send reply on stdout, just proving we are alive */ 1058 if(!write_stdout("PONG\n", 5)) 1059 return FALSE; 1060 } 1061 1062 else if(!memcmp("PORT", buffer, 4)) { 1063 /* Question asking us what PORT number we are listening to. 1064 Replies to PORT with "IPv[num]/[port]" */ 1065 msnprintf((char *)buffer, sizeof(buffer), "%s/%hu\n", ipv_inuse, port); 1066 buffer_len = (ssize_t)strlen((char *)buffer); 1067 msnprintf(data, sizeof(data), "PORT\n%04zx\n", buffer_len); 1068 if(!write_stdout(data, 10)) 1069 return FALSE; 1070 if(!write_stdout(buffer, buffer_len)) 1071 return FALSE; 1072 } 1073 else if(!memcmp("QUIT", buffer, 4)) { 1074 /* just die */ 1075 logmsg("quits"); 1076 return FALSE; 1077 } 1078 else if(!memcmp("DATA", buffer, 4)) { 1079 /* data IN => data OUT */ 1080 1081 if(!read_stdin(buffer, 5)) 1082 return FALSE; 1083 1084 buffer[5] = '\0'; 1085 1086 buffer_len = (ssize_t)strtol((char *)buffer, NULL, 16); 1087 if(buffer_len > (ssize_t)sizeof(buffer)) { 1088 logmsg("ERROR: Buffer size (%zu bytes) too small for data size " 1089 "(%zd bytes)", sizeof(buffer), buffer_len); 1090 return FALSE; 1091 } 1092 logmsg("> %zd bytes data, server => client", buffer_len); 1093 1094 if(!read_stdin(buffer, buffer_len)) 1095 return FALSE; 1096 1097 lograw(buffer, buffer_len); 1098 1099 if(*mode == PASSIVE_LISTEN) { 1100 logmsg("*** We are disconnected!"); 1101 if(!write_stdout("DISC\n", 5)) 1102 return FALSE; 1103 } 1104 else { 1105 /* send away on the socket */ 1106 ssize_t bytes_written = swrite(sockfd, buffer, buffer_len); 1107 if(bytes_written != buffer_len) { 1108 logmsg("Not all data was sent. Bytes to send: %zd sent: %zd", 1109 buffer_len, bytes_written); 1110 } 1111 } 1112 } 1113 else if(!memcmp("DISC", buffer, 4)) { 1114 /* disconnect! */ 1115 if(!write_stdout("DISC\n", 5)) 1116 return FALSE; 1117 if(sockfd != CURL_SOCKET_BAD) { 1118 logmsg("====> Client forcibly disconnected"); 1119 sclose(sockfd); 1120 *sockfdp = CURL_SOCKET_BAD; 1121 if(*mode == PASSIVE_CONNECT) 1122 *mode = PASSIVE_LISTEN; 1123 else 1124 *mode = ACTIVE_DISCONNECT; 1125 } 1126 else 1127 logmsg("attempt to close already dead connection"); 1128 return TRUE; 1129 } 1130 } 1131 1132 1133 if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) { 1134 ssize_t nread_socket; 1135 if(*mode == PASSIVE_LISTEN) { 1136 /* there's no stream set up yet, this is an indication that there's a 1137 client connecting. */ 1138 curl_socket_t newfd = accept(sockfd, NULL, NULL); 1139 if(CURL_SOCKET_BAD == newfd) { 1140 error = SOCKERRNO; 1141 logmsg("accept(%d, NULL, NULL) failed with error: (%d) %s", 1142 sockfd, error, strerror(error)); 1143 } 1144 else { 1145 logmsg("====> Client connect"); 1146 if(!write_stdout("CNCT\n", 5)) 1147 return FALSE; 1148 *sockfdp = newfd; /* store the new socket */ 1149 *mode = PASSIVE_CONNECT; /* we have connected */ 1150 } 1151 return TRUE; 1152 } 1153 1154 /* read from socket, pass on data to stdout */ 1155 nread_socket = sread(sockfd, buffer, sizeof(buffer)); 1156 1157 if(nread_socket > 0) { 1158 msnprintf(data, sizeof(data), "DATA\n%04zx\n", nread_socket); 1159 if(!write_stdout(data, 10)) 1160 return FALSE; 1161 if(!write_stdout(buffer, nread_socket)) 1162 return FALSE; 1163 1164 logmsg("< %zd bytes data, client => server", nread_socket); 1165 lograw(buffer, nread_socket); 1166 } 1167 1168 if(nread_socket <= 0 1169 #ifdef USE_WINSOCK 1170 || FD_ISSET(sockfd, &fds_err) 1171 #endif 1172 ) { 1173 logmsg("====> Client disconnect"); 1174 if(!write_stdout("DISC\n", 5)) 1175 return FALSE; 1176 sclose(sockfd); 1177 *sockfdp = CURL_SOCKET_BAD; 1178 if(*mode == PASSIVE_CONNECT) 1179 *mode = PASSIVE_LISTEN; 1180 else 1181 *mode = ACTIVE_DISCONNECT; 1182 return TRUE; 1183 } 1184 } 1185 1186 return TRUE; 1187 } 1188 1189 static curl_socket_t sockdaemon(curl_socket_t sock, 1190 unsigned short *listenport) 1191 { 1192 /* passive daemon style */ 1193 srvr_sockaddr_union_t listener; 1194 int flag; 1195 int rc; 1196 int totdelay = 0; 1197 int maxretr = 10; 1198 int delay = 20; 1199 int attempt = 0; 1200 int error = 0; 1201 1202 do { 1203 attempt++; 1204 flag = 1; 1205 rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1206 (void *)&flag, sizeof(flag)); 1207 if(rc) { 1208 error = SOCKERRNO; 1209 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", 1210 error, strerror(error)); 1211 if(maxretr) { 1212 rc = wait_ms(delay); 1213 if(rc) { 1214 /* should not happen */ 1215 error = errno; 1216 logmsg("wait_ms() failed with error: (%d) %s", 1217 error, strerror(error)); 1218 sclose(sock); 1219 return CURL_SOCKET_BAD; 1220 } 1221 if(got_exit_signal) { 1222 logmsg("signalled to die, exiting..."); 1223 sclose(sock); 1224 return CURL_SOCKET_BAD; 1225 } 1226 totdelay += delay; 1227 delay *= 2; /* double the sleep for next attempt */ 1228 } 1229 } 1230 } while(rc && maxretr--); 1231 1232 if(rc) { 1233 logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s", 1234 attempt, totdelay, error, strerror(error)); 1235 logmsg("Continuing anyway..."); 1236 } 1237 1238 /* When the specified listener port is zero, it is actually a 1239 request to let the system choose a non-zero available port. */ 1240 1241 #ifdef ENABLE_IPV6 1242 if(!use_ipv6) { 1243 #endif 1244 memset(&listener.sa4, 0, sizeof(listener.sa4)); 1245 listener.sa4.sin_family = AF_INET; 1246 listener.sa4.sin_addr.s_addr = INADDR_ANY; 1247 listener.sa4.sin_port = htons(*listenport); 1248 rc = bind(sock, &listener.sa, sizeof(listener.sa4)); 1249 #ifdef ENABLE_IPV6 1250 } 1251 else { 1252 memset(&listener.sa6, 0, sizeof(listener.sa6)); 1253 listener.sa6.sin6_family = AF_INET6; 1254 listener.sa6.sin6_addr = in6addr_any; 1255 listener.sa6.sin6_port = htons(*listenport); 1256 rc = bind(sock, &listener.sa, sizeof(listener.sa6)); 1257 } 1258 #endif /* ENABLE_IPV6 */ 1259 if(rc) { 1260 error = SOCKERRNO; 1261 logmsg("Error binding socket on port %hu: (%d) %s", 1262 *listenport, error, strerror(error)); 1263 sclose(sock); 1264 return CURL_SOCKET_BAD; 1265 } 1266 1267 if(!*listenport) { 1268 /* The system was supposed to choose a port number, figure out which 1269 port we actually got and update the listener port value with it. */ 1270 curl_socklen_t la_size; 1271 srvr_sockaddr_union_t localaddr; 1272 #ifdef ENABLE_IPV6 1273 if(!use_ipv6) 1274 #endif 1275 la_size = sizeof(localaddr.sa4); 1276 #ifdef ENABLE_IPV6 1277 else 1278 la_size = sizeof(localaddr.sa6); 1279 #endif 1280 memset(&localaddr.sa, 0, (size_t)la_size); 1281 if(getsockname(sock, &localaddr.sa, &la_size) < 0) { 1282 error = SOCKERRNO; 1283 logmsg("getsockname() failed with error: (%d) %s", 1284 error, strerror(error)); 1285 sclose(sock); 1286 return CURL_SOCKET_BAD; 1287 } 1288 switch(localaddr.sa.sa_family) { 1289 case AF_INET: 1290 *listenport = ntohs(localaddr.sa4.sin_port); 1291 break; 1292 #ifdef ENABLE_IPV6 1293 case AF_INET6: 1294 *listenport = ntohs(localaddr.sa6.sin6_port); 1295 break; 1296 #endif 1297 default: 1298 break; 1299 } 1300 if(!*listenport) { 1301 /* Real failure, listener port shall not be zero beyond this point. */ 1302 logmsg("Apparently getsockname() succeeded, with listener port zero."); 1303 logmsg("A valid reason for this failure is a binary built without"); 1304 logmsg("proper network library linkage. This might not be the only"); 1305 logmsg("reason, but double check it before anything else."); 1306 sclose(sock); 1307 return CURL_SOCKET_BAD; 1308 } 1309 } 1310 1311 /* bindonly option forces no listening */ 1312 if(bind_only) { 1313 logmsg("instructed to bind port without listening"); 1314 return sock; 1315 } 1316 1317 /* start accepting connections */ 1318 rc = listen(sock, 5); 1319 if(0 != rc) { 1320 error = SOCKERRNO; 1321 logmsg("listen(%d, 5) failed with error: (%d) %s", 1322 sock, error, strerror(error)); 1323 sclose(sock); 1324 return CURL_SOCKET_BAD; 1325 } 1326 1327 return sock; 1328 } 1329 1330 1331 int main(int argc, char *argv[]) 1332 { 1333 srvr_sockaddr_union_t me; 1334 curl_socket_t sock = CURL_SOCKET_BAD; 1335 curl_socket_t msgsock = CURL_SOCKET_BAD; 1336 int wrotepidfile = 0; 1337 const char *pidname = ".sockfilt.pid"; 1338 bool juggle_again; 1339 int rc; 1340 int error; 1341 int arg = 1; 1342 enum sockmode mode = PASSIVE_LISTEN; /* default */ 1343 const char *addr = NULL; 1344 1345 while(argc>arg) { 1346 if(!strcmp("--version", argv[arg])) { 1347 printf("sockfilt IPv4%s\n", 1348 #ifdef ENABLE_IPV6 1349 "/IPv6" 1350 #else 1351 "" 1352 #endif 1353 ); 1354 return 0; 1355 } 1356 else if(!strcmp("--verbose", argv[arg])) { 1357 verbose = TRUE; 1358 arg++; 1359 } 1360 else if(!strcmp("--pidfile", argv[arg])) { 1361 arg++; 1362 if(argc>arg) 1363 pidname = argv[arg++]; 1364 } 1365 else if(!strcmp("--logfile", argv[arg])) { 1366 arg++; 1367 if(argc>arg) 1368 serverlogfile = argv[arg++]; 1369 } 1370 else if(!strcmp("--ipv6", argv[arg])) { 1371 #ifdef ENABLE_IPV6 1372 ipv_inuse = "IPv6"; 1373 use_ipv6 = TRUE; 1374 #endif 1375 arg++; 1376 } 1377 else if(!strcmp("--ipv4", argv[arg])) { 1378 /* for completeness, we support this option as well */ 1379 #ifdef ENABLE_IPV6 1380 ipv_inuse = "IPv4"; 1381 use_ipv6 = FALSE; 1382 #endif 1383 arg++; 1384 } 1385 else if(!strcmp("--bindonly", argv[arg])) { 1386 bind_only = TRUE; 1387 arg++; 1388 } 1389 else if(!strcmp("--port", argv[arg])) { 1390 arg++; 1391 if(argc>arg) { 1392 char *endptr; 1393 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1394 if((endptr != argv[arg] + strlen(argv[arg])) || 1395 ((ulnum != 0UL) && ((ulnum < 1025UL) || (ulnum > 65535UL)))) { 1396 fprintf(stderr, "sockfilt: invalid --port argument (%s)\n", 1397 argv[arg]); 1398 return 0; 1399 } 1400 port = curlx_ultous(ulnum); 1401 arg++; 1402 } 1403 } 1404 else if(!strcmp("--connect", argv[arg])) { 1405 /* Asked to actively connect to the specified local port instead of 1406 doing a passive server-style listening. */ 1407 arg++; 1408 if(argc>arg) { 1409 char *endptr; 1410 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1411 if((endptr != argv[arg] + strlen(argv[arg])) || 1412 (ulnum < 1025UL) || (ulnum > 65535UL)) { 1413 fprintf(stderr, "sockfilt: invalid --connect argument (%s)\n", 1414 argv[arg]); 1415 return 0; 1416 } 1417 connectport = curlx_ultous(ulnum); 1418 arg++; 1419 } 1420 } 1421 else if(!strcmp("--addr", argv[arg])) { 1422 /* Set an IP address to use with --connect; otherwise use localhost */ 1423 arg++; 1424 if(argc>arg) { 1425 addr = argv[arg]; 1426 arg++; 1427 } 1428 } 1429 else { 1430 puts("Usage: sockfilt [option]\n" 1431 " --version\n" 1432 " --verbose\n" 1433 " --logfile [file]\n" 1434 " --pidfile [file]\n" 1435 " --ipv4\n" 1436 " --ipv6\n" 1437 " --bindonly\n" 1438 " --port [port]\n" 1439 " --connect [port]\n" 1440 " --addr [address]"); 1441 return 0; 1442 } 1443 } 1444 1445 #ifdef WIN32 1446 win32_init(); 1447 atexit(win32_cleanup); 1448 1449 setmode(fileno(stdin), O_BINARY); 1450 setmode(fileno(stdout), O_BINARY); 1451 setmode(fileno(stderr), O_BINARY); 1452 #endif 1453 1454 install_signal_handlers(); 1455 1456 #ifdef ENABLE_IPV6 1457 if(!use_ipv6) 1458 #endif 1459 sock = socket(AF_INET, SOCK_STREAM, 0); 1460 #ifdef ENABLE_IPV6 1461 else 1462 sock = socket(AF_INET6, SOCK_STREAM, 0); 1463 #endif 1464 1465 if(CURL_SOCKET_BAD == sock) { 1466 error = SOCKERRNO; 1467 logmsg("Error creating socket: (%d) %s", 1468 error, strerror(error)); 1469 write_stdout("FAIL\n", 5); 1470 goto sockfilt_cleanup; 1471 } 1472 1473 if(connectport) { 1474 /* Active mode, we should connect to the given port number */ 1475 mode = ACTIVE; 1476 #ifdef ENABLE_IPV6 1477 if(!use_ipv6) { 1478 #endif 1479 memset(&me.sa4, 0, sizeof(me.sa4)); 1480 me.sa4.sin_family = AF_INET; 1481 me.sa4.sin_port = htons(connectport); 1482 me.sa4.sin_addr.s_addr = INADDR_ANY; 1483 if(!addr) 1484 addr = "127.0.0.1"; 1485 Curl_inet_pton(AF_INET, addr, &me.sa4.sin_addr); 1486 1487 rc = connect(sock, &me.sa, sizeof(me.sa4)); 1488 #ifdef ENABLE_IPV6 1489 } 1490 else { 1491 memset(&me.sa6, 0, sizeof(me.sa6)); 1492 me.sa6.sin6_family = AF_INET6; 1493 me.sa6.sin6_port = htons(connectport); 1494 if(!addr) 1495 addr = "::1"; 1496 Curl_inet_pton(AF_INET6, addr, &me.sa6.sin6_addr); 1497 1498 rc = connect(sock, &me.sa, sizeof(me.sa6)); 1499 } 1500 #endif /* ENABLE_IPV6 */ 1501 if(rc) { 1502 error = SOCKERRNO; 1503 logmsg("Error connecting to port %hu: (%d) %s", 1504 connectport, error, strerror(error)); 1505 write_stdout("FAIL\n", 5); 1506 goto sockfilt_cleanup; 1507 } 1508 logmsg("====> Client connect"); 1509 msgsock = sock; /* use this as stream */ 1510 } 1511 else { 1512 /* passive daemon style */ 1513 sock = sockdaemon(sock, &port); 1514 if(CURL_SOCKET_BAD == sock) { 1515 write_stdout("FAIL\n", 5); 1516 goto sockfilt_cleanup; 1517 } 1518 msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ 1519 } 1520 1521 logmsg("Running %s version", ipv_inuse); 1522 1523 if(connectport) 1524 logmsg("Connected to port %hu", connectport); 1525 else if(bind_only) 1526 logmsg("Bound without listening on port %hu", port); 1527 else 1528 logmsg("Listening on port %hu", port); 1529 1530 wrotepidfile = write_pidfile(pidname); 1531 if(!wrotepidfile) { 1532 write_stdout("FAIL\n", 5); 1533 goto sockfilt_cleanup; 1534 } 1535 1536 do { 1537 juggle_again = juggle(&msgsock, sock, &mode); 1538 } while(juggle_again); 1539 1540 sockfilt_cleanup: 1541 1542 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) 1543 sclose(msgsock); 1544 1545 if(sock != CURL_SOCKET_BAD) 1546 sclose(sock); 1547 1548 if(wrotepidfile) 1549 unlink(pidname); 1550 1551 restore_signal_handlers(); 1552 1553 if(got_exit_signal) { 1554 logmsg("============> sockfilt exits with signal (%d)", exit_signal); 1555 /* 1556 * To properly set the return status of the process we 1557 * must raise the same signal SIGINT or SIGTERM that we 1558 * caught and let the old handler take care of it. 1559 */ 1560 raise(exit_signal); 1561 } 1562 1563 logmsg("============> sockfilt quits"); 1564 return 0; 1565 } 1566