Fossil SCM
Rework the cgi_http_server() routine so that it uses two separate sockets, one each for IPv4 and IPv6.
Commit
945e0ae4eb57dac7b0bf72ef6fb8da5c8c9cb90934049b715add1e40b18d85e7
Parent
a9b075af835a995…
1 file changed
+252
-187
+252
-187
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -2497,29 +2497,10 @@ | ||
| 2497 | 2497 | } |
| 2498 | 2498 | fossil_free(zToFree); |
| 2499 | 2499 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 2500 | 2500 | cgi_init(); |
| 2501 | 2501 | } |
| 2502 | - | |
| 2503 | -#if !defined(_WIN32) | |
| 2504 | -/* | |
| 2505 | -** Change the listening socket, if necessary, so that it will accept both IPv4 | |
| 2506 | -** and IPv6 | |
| 2507 | -*/ | |
| 2508 | -static void allowBothIpV4andV6(int listener){ | |
| 2509 | -#if defined(IPV6_V6ONLY) | |
| 2510 | - int ipv6only = -1; | |
| 2511 | - socklen_t ipv6only_size = sizeof(ipv6only); | |
| 2512 | - getsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &ipv6only_size); | |
| 2513 | - if( ipv6only ){ | |
| 2514 | - ipv6only = 0; | |
| 2515 | - setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, ipv6only_size); | |
| 2516 | - } | |
| 2517 | -#endif /* defined(IPV6_ONLY) */ | |
| 2518 | -} | |
| 2519 | -#endif /* !defined(_WIN32) */ | |
| 2520 | - | |
| 2521 | 2502 | |
| 2522 | 2503 | #if INTERFACE |
| 2523 | 2504 | /* |
| 2524 | 2505 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 2525 | 2506 | */ |
| @@ -2559,150 +2540,222 @@ | ||
| 2559 | 2540 | ){ |
| 2560 | 2541 | #if defined(_WIN32) |
| 2561 | 2542 | /* Use win32_http_server() instead */ |
| 2562 | 2543 | fossil_exit(1); |
| 2563 | 2544 | #else |
| 2564 | - int listener = -1; /* The server socket */ | |
| 2565 | - int connection; /* A socket for each individual connection */ | |
| 2545 | + int listen4 = -1; /* Main socket; IPv4 or unix-domain */ | |
| 2546 | + int listen6 = -1; /* Aux socket for corresponding IPv6 */ | |
| 2547 | + int mxListen = -1; /* Maximum of listen4 and listen6 */ | |
| 2548 | + int connection; /* An incoming connection */ | |
| 2566 | 2549 | int nRequest = 0; /* Number of requests handled so far */ |
| 2567 | 2550 | fd_set readfds; /* Set of file descriptors for select() */ |
| 2568 | 2551 | socklen_t lenaddr; /* Length of the inaddr structure */ |
| 2569 | 2552 | int child; /* PID of the child process */ |
| 2570 | 2553 | int nchildren = 0; /* Number of child processes */ |
| 2571 | 2554 | struct timeval delay; /* How long to wait inside select() */ |
| 2572 | - struct sockaddr_in6 inaddr; /* The socket address */ | |
| 2573 | - struct sockaddr_in inaddr4; /* IPv4 address; needed by OpenBSD */ | |
| 2555 | + struct sockaddr_in6 inaddr6; /* Address for IPv6 */ | |
| 2556 | + struct sockaddr_in inaddr4; /* Address for IPv4 */ | |
| 2574 | 2557 | struct sockaddr_un uxaddr; /* The address for unix-domain sockets */ |
| 2575 | 2558 | int opt = 1; /* setsockopt flag */ |
| 2576 | 2559 | int rc; /* Result code from system calls */ |
| 2577 | 2560 | int iPort = mnPort; /* Port to try to use */ |
| 2578 | - int bIPv4 = 0; /* Use IPv4 only; use inaddr4, not inaddr */ | |
| 2579 | - | |
| 2580 | - while( iPort<=mxPort ){ | |
| 2581 | - if( flags & HTTP_SERVER_UNIXSOCKET ){ | |
| 2582 | - /* Initialize a Unix socket named g.zSockName */ | |
| 2583 | - assert( g.zSockName!=0 ); | |
| 2584 | - memset(&uxaddr, 0, sizeof(uxaddr)); | |
| 2585 | - if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){ | |
| 2586 | - fossil_fatal("name of unix socket too big: %s\nmax size: %d\n", | |
| 2587 | - g.zSockName, (int)sizeof(uxaddr.sun_path)); | |
| 2588 | - } | |
| 2589 | - if( file_isdir(g.zSockName, ExtFILE)!=0 ){ | |
| 2590 | - if( !file_issocket(g.zSockName) ){ | |
| 2591 | - fossil_fatal("cannot name socket \"%s\" because another object" | |
| 2592 | - " with that name already exists", g.zSockName); | |
| 2593 | - }else{ | |
| 2594 | - unlink(g.zSockName); | |
| 2595 | - } | |
| 2596 | - } | |
| 2597 | - uxaddr.sun_family = AF_UNIX; | |
| 2598 | - strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1); | |
| 2599 | - listener = socket(AF_UNIX, SOCK_STREAM, 0); | |
| 2600 | - if( listener<0 ){ | |
| 2601 | - fossil_fatal("unable to create a unix socket named %s", | |
| 2602 | - g.zSockName); | |
| 2603 | - } | |
| 2604 | - /* Set the access permission for the new socket. Default to 0660. | |
| 2605 | - ** But use an alternative specified by --socket-mode if available. | |
| 2606 | - ** Do this before bind() to avoid a race condition. */ | |
| 2607 | - if( g.zSockMode ){ | |
| 2608 | - file_set_mode(g.zSockName, listener, g.zSockMode, 0); | |
| 2609 | - }else{ | |
| 2610 | - file_set_mode(g.zSockName, listener, "0660", 1); | |
| 2611 | - } | |
| 2612 | - }else{ | |
| 2613 | - /* Initialize a TCP/IP socket on port iPort */ | |
| 2614 | - if( (flags & HTTP_SERVER_LOCALHOST)!=0 && zIpAddr==0 ){ | |
| 2615 | - /* Map all loopback to 127.0.0.1, since this is the easiest way | |
| 2616 | - ** to support OpenBSD and its limitations without burdening | |
| 2617 | - ** Linux and MacOS with lots of extra code and complication. */ | |
| 2618 | - zIpAddr = "127.0.0.1"; | |
| 2619 | - } | |
| 2620 | - if( zIpAddr ){ | |
| 2621 | - if( strchr(zIpAddr,':') ){ | |
| 2622 | - memset(&inaddr, 0, sizeof(inaddr)); | |
| 2623 | - inaddr.sin6_family = AF_INET6; | |
| 2624 | - bIPv4 = 0; | |
| 2625 | - if( inet_pton(AF_INET6, zIpAddr, &inaddr.sin6_addr)==0 ){ | |
| 2626 | - fossil_fatal("not a valid IPv6 address: %s", zIpAddr); | |
| 2627 | - } | |
| 2628 | - }else{ | |
| 2629 | - memset(&inaddr4, 0, sizeof(inaddr4)); | |
| 2630 | - inaddr4.sin_family = AF_INET; | |
| 2631 | - bIPv4 = 1; | |
| 2632 | - inaddr4.sin_addr.s_addr = inet_addr(zIpAddr); | |
| 2633 | - if( inaddr4.sin_addr.s_addr == INADDR_NONE ){ | |
| 2634 | - fossil_fatal("not a valid IPv4 address: %s", zIpAddr); | |
| 2635 | - } | |
| 2636 | - } | |
| 2637 | - }else{ | |
| 2638 | - /* Bind to any and all available IP addresses */ | |
| 2639 | - memset(&inaddr, 0, sizeof(inaddr)); | |
| 2640 | - inaddr.sin6_family = AF_INET6; | |
| 2641 | - inaddr.sin6_addr = in6addr_any; | |
| 2642 | - bIPv4 = 0; | |
| 2643 | - } | |
| 2644 | - if( bIPv4 ){ | |
| 2645 | - inaddr4.sin_port = htons(iPort); | |
| 2646 | - listener = socket(AF_INET, SOCK_STREAM, 0); | |
| 2647 | - }else{ | |
| 2648 | - inaddr.sin6_port = htons(iPort); | |
| 2649 | - listener = socket(AF_INET6, SOCK_STREAM, 0); | |
| 2650 | - allowBothIpV4andV6(listener); | |
| 2651 | - } | |
| 2652 | - if( listener<0 ){ | |
| 2653 | - iPort++; | |
| 2654 | - continue; | |
| 2655 | - } | |
| 2656 | - } | |
| 2657 | - | |
| 2658 | - /* if we can't terminate nicely, at least allow the socket to be reused */ | |
| 2659 | - setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); | |
| 2660 | - | |
| 2661 | - if( flags & HTTP_SERVER_UNIXSOCKET ){ | |
| 2662 | - rc = bind(listener, (struct sockaddr*)&uxaddr, sizeof(uxaddr)); | |
| 2663 | - /* Set the owner of the socket if requested by --socket-owner. This | |
| 2664 | - ** must wait until after bind(), after the filesystem object has been | |
| 2665 | - ** created. See https://lkml.org/lkml/2004/11/1/84 and | |
| 2666 | - ** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */ | |
| 2667 | - if( g.zSockOwner ){ | |
| 2668 | - file_set_owner(g.zSockName, listener, g.zSockOwner); | |
| 2669 | - } | |
| 2670 | - }else if( bIPv4 ){ | |
| 2671 | - rc = bind(listener, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); | |
| 2672 | - }else{ | |
| 2673 | - rc = bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr)); | |
| 2674 | - } | |
| 2675 | - if( rc<0 ){ | |
| 2676 | - close(listener); | |
| 2677 | - iPort++; | |
| 2678 | - continue; | |
| 2679 | - } | |
| 2680 | - break; | |
| 2681 | - } | |
| 2682 | - if( iPort>mxPort ){ | |
| 2683 | - if( flags & HTTP_SERVER_UNIXSOCKET ){ | |
| 2684 | - fossil_fatal("unable to listen on unix socket %s", zIpAddr); | |
| 2685 | - }else if( mnPort==mxPort ){ | |
| 2686 | - fossil_fatal("unable to open listening socket on port %d", mnPort); | |
| 2687 | - }else{ | |
| 2688 | - fossil_fatal("unable to open listening socket on any" | |
| 2689 | - " port in the range %d..%d", mnPort, mxPort); | |
| 2690 | - } | |
| 2691 | - } | |
| 2692 | - if( iPort>mxPort ) return 1; | |
| 2693 | - listen(listener,10); | |
| 2694 | - if( flags & HTTP_SERVER_UNIXSOCKET ){ | |
| 2695 | - fossil_print("Listening for %s requests on unix socket %s\n", | |
| 2696 | - (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" : | |
| 2697 | - g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", g.zSockName); | |
| 2698 | - }else{ | |
| 2699 | - fossil_print("Listening for %s requests on TCP port %d\n", | |
| 2700 | - (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" : | |
| 2701 | - g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", iPort); | |
| 2702 | - } | |
| 2703 | - fflush(stdout); | |
| 2561 | + const char *zRequestType; /* Type of requests to listen for */ | |
| 2562 | + | |
| 2563 | + | |
| 2564 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 2565 | + zRequestType = "SCGI"; | |
| 2566 | + }else if( g.httpUseSSL ){ | |
| 2567 | + zRequestType = "TLS-encrypted HTTPS"; | |
| 2568 | + }else{ | |
| 2569 | + zRequestType = "HTTP"; | |
| 2570 | + } | |
| 2571 | + | |
| 2572 | + if( flags & HTTP_SERVER_UNIXSOCKET ){ | |
| 2573 | + /* CASE 1: A unix socket named g.zSockName. After creation, set the | |
| 2574 | + ** permissions on the new socket to g.zSockMode and make the | |
| 2575 | + ** owner of the socket be g.zSockOwner. | |
| 2576 | + */ | |
| 2577 | + assert( g.zSockName!=0 ); | |
| 2578 | + memset(&uxaddr, 0, sizeof(uxaddr)); | |
| 2579 | + if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){ | |
| 2580 | + fossil_fatal("name of unix socket too big: %s\nmax size: %d\n", | |
| 2581 | + g.zSockName, (int)sizeof(uxaddr.sun_path)); | |
| 2582 | + } | |
| 2583 | + if( file_isdir(g.zSockName, ExtFILE)!=0 ){ | |
| 2584 | + if( !file_issocket(g.zSockName) ){ | |
| 2585 | + fossil_fatal("cannot name socket \"%s\" because another object" | |
| 2586 | + " with that name already exists", g.zSockName); | |
| 2587 | + }else{ | |
| 2588 | + unlink(g.zSockName); | |
| 2589 | + } | |
| 2590 | + } | |
| 2591 | + uxaddr.sun_family = AF_UNIX; | |
| 2592 | + strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1); | |
| 2593 | + listen4 = socket(AF_UNIX, SOCK_STREAM, 0); | |
| 2594 | + if( listen4<0 ){ | |
| 2595 | + fossil_fatal("unable to create a unix socket named %s", | |
| 2596 | + g.zSockName); | |
| 2597 | + } | |
| 2598 | + mxListen = listen4; | |
| 2599 | + listen6 = -1; | |
| 2600 | + | |
| 2601 | + /* Set the access permission for the new socket. Default to 0660. | |
| 2602 | + ** But use an alternative specified by --socket-mode if available. | |
| 2603 | + ** Do this before bind() to avoid a race condition. */ | |
| 2604 | + if( g.zSockMode ){ | |
| 2605 | + file_set_mode(g.zSockName, listen4, g.zSockMode, 0); | |
| 2606 | + }else{ | |
| 2607 | + file_set_mode(g.zSockName, listen4, "0660", 1); | |
| 2608 | + } | |
| 2609 | + rc = bind(listen4, (struct sockaddr*)&uxaddr, sizeof(uxaddr)); | |
| 2610 | + /* Set the owner of the socket if requested by --socket-owner. This | |
| 2611 | + ** must wait until after bind(), after the filesystem object has been | |
| 2612 | + ** created. See https://lkml.org/lkml/2004/11/1/84 and | |
| 2613 | + ** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */ | |
| 2614 | + if( g.zSockOwner ){ | |
| 2615 | + file_set_owner(g.zSockName, listen4, g.zSockOwner); | |
| 2616 | + } | |
| 2617 | + fossil_print("Listening for %s requests on unix socket %s\n", | |
| 2618 | + zRequestType, g.zSockName); | |
| 2619 | + fflush(stdout); | |
| 2620 | + }else if( zIpAddr && strchr(zIpAddr,':')!=0 ){ | |
| 2621 | + /* CASE 2: TCP on IPv6 IP address specified by zIpAddr and on port iPort. | |
| 2622 | + */ | |
| 2623 | + assert( mnPort==mxPort ); | |
| 2624 | + memset(&inaddr6, 0, sizeof(inaddr6)); | |
| 2625 | + inaddr6.sin6_family = AF_INET6; | |
| 2626 | + inaddr6.sin6_port = htons(iPort); | |
| 2627 | + if( inet_pton(AF_INET6, zIpAddr, &inaddr6.sin6_addr)==0 ){ | |
| 2628 | + fossil_fatal("not a valid IPv6 address: %s", zIpAddr); | |
| 2629 | + } | |
| 2630 | + listen6 = socket(AF_INET6, SOCK_STREAM, 0); | |
| 2631 | + if( listen6>0 ){ | |
| 2632 | + opt = 1; | |
| 2633 | + setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); | |
| 2634 | + rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6)); | |
| 2635 | + if( rc<0 ){ | |
| 2636 | + close(listen6); | |
| 2637 | + listen6 = -1; | |
| 2638 | + } | |
| 2639 | + } | |
| 2640 | + if( listen6<0 ){ | |
| 2641 | + fossil_fatal("cannot open a listening socket on [%s]:%d", | |
| 2642 | + zIpAddr, mnPort); | |
| 2643 | + } | |
| 2644 | + mxListen = listen6; | |
| 2645 | + listen4 = -1; | |
| 2646 | + fossil_print("Listening for %s requests on [%s]:%d\n", | |
| 2647 | + zRequestType, zIpAddr, iPort); | |
| 2648 | + fflush(stdout); | |
| 2649 | + }else if( zIpAddr && zIpAddr[0] ){ | |
| 2650 | + /* CASE 3: TCP on IPv4 IP address specified by zIpAddr and on port iPort. | |
| 2651 | + */ | |
| 2652 | + assert( mnPort==mxPort ); | |
| 2653 | + memset(&inaddr4, 0, sizeof(inaddr4)); | |
| 2654 | + inaddr4.sin_family = AF_INET; | |
| 2655 | + inaddr4.sin_port = htons(iPort); | |
| 2656 | + if( strcmp(zIpAddr, "localhost")==0 ) zIpAddr = "127.0.0.1"; | |
| 2657 | + inaddr4.sin_addr.s_addr = inet_addr(zIpAddr); | |
| 2658 | + if( inaddr4.sin_addr.s_addr == INADDR_NONE ){ | |
| 2659 | + fossil_fatal("not a valid IPv4 address: %s", zIpAddr); | |
| 2660 | + } | |
| 2661 | + listen4 = socket(AF_INET, SOCK_STREAM, 0); | |
| 2662 | + if( listen4>0 ){ | |
| 2663 | + setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); | |
| 2664 | + rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); | |
| 2665 | + if( rc<0 ){ | |
| 2666 | + close(listen6); | |
| 2667 | + listen4 = -1; | |
| 2668 | + } | |
| 2669 | + } | |
| 2670 | + if( listen4<0 ){ | |
| 2671 | + fossil_fatal("cannot open a listening socket on %s:%d", | |
| 2672 | + zIpAddr, mnPort); | |
| 2673 | + } | |
| 2674 | + mxListen = listen4; | |
| 2675 | + listen6 = -1; | |
| 2676 | + fossil_print("Listening for %s requests on TCP port %s:%d\n", | |
| 2677 | + zRequestType, zIpAddr, iPort); | |
| 2678 | + fflush(stdout); | |
| 2679 | + }else{ | |
| 2680 | + /* CASE 4: Listen on all available IP addresses, or on only loopback | |
| 2681 | + ** addresses (if HTTP_SERVER_LOCALHOST). The TCP port is the | |
| 2682 | + ** first available in the range of mnPort..mxPort. Listen | |
| 2683 | + ** on both IPv4 and IPv6, if possible. The TCP port scan is done | |
| 2684 | + ** on IPv4. | |
| 2685 | + */ | |
| 2686 | + while( iPort<=mxPort ){ | |
| 2687 | + const char *zProto; | |
| 2688 | + memset(&inaddr4, 0, sizeof(inaddr4)); | |
| 2689 | + inaddr4.sin_family = AF_INET; | |
| 2690 | + inaddr4.sin_port = htons(iPort); | |
| 2691 | + if( flags & HTTP_SERVER_LOCALHOST ){ | |
| 2692 | + inaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
| 2693 | + }else{ | |
| 2694 | + inaddr4.sin_addr.s_addr = htonl(INADDR_ANY); | |
| 2695 | + } | |
| 2696 | + listen4 = socket(AF_INET, SOCK_STREAM, 0); | |
| 2697 | + if( listen4>0 ){ | |
| 2698 | + setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); | |
| 2699 | + rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); | |
| 2700 | + if( rc<0 ){ | |
| 2701 | + close(listen4); | |
| 2702 | + listen4 = -1; | |
| 2703 | + } | |
| 2704 | + } | |
| 2705 | + if( listen4<0 ){ | |
| 2706 | + iPort++; | |
| 2707 | + continue; | |
| 2708 | + } | |
| 2709 | + mxListen = listen4; | |
| 2710 | + | |
| 2711 | + /* If we get here, that means we found an open TCP port at iPort for | |
| 2712 | + ** IPv4. Try to set up a corresponding IPv6 socket on the same port. | |
| 2713 | + */ | |
| 2714 | + memset(&inaddr6, 0, sizeof(inaddr6)); | |
| 2715 | + inaddr6.sin6_family = AF_INET6; | |
| 2716 | + inaddr6.sin6_port = htons(iPort); | |
| 2717 | + if( flags & HTTP_SERVER_LOCALHOST ){ | |
| 2718 | + inaddr6.sin6_addr = in6addr_loopback; | |
| 2719 | + }else{ | |
| 2720 | + inaddr6.sin6_addr = in6addr_any; | |
| 2721 | + } | |
| 2722 | + listen6 = socket(AF_INET6, SOCK_STREAM, 0); | |
| 2723 | + if( listen6>0 ){ | |
| 2724 | + setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); | |
| 2725 | + setsockopt(listen6, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); | |
| 2726 | + rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6)); | |
| 2727 | + if( rc<0 ){ | |
| 2728 | + close(listen6); | |
| 2729 | + listen6 = -1; | |
| 2730 | + } | |
| 2731 | + } | |
| 2732 | + if( listen6<0 ){ | |
| 2733 | + zProto = "IPv4 only"; | |
| 2734 | + }else{ | |
| 2735 | + zProto = "IPv4 and IPv6"; | |
| 2736 | + if( listen6>listen4 ) mxListen = listen6; | |
| 2737 | + } | |
| 2738 | + | |
| 2739 | + fossil_print("Listening for %s requests on TCP port %s%d, %s\n", | |
| 2740 | + zRequestType, | |
| 2741 | + (flags & HTTP_SERVER_LOCALHOST)!=0 ? "localhost:" : "", | |
| 2742 | + iPort, zProto); | |
| 2743 | + fflush(stdout); | |
| 2744 | + break; | |
| 2745 | + } | |
| 2746 | + if( iPort>mxPort ){ | |
| 2747 | + fossil_fatal("no available TCP ports in the range %d..%d", | |
| 2748 | + mnPort, mxPort); | |
| 2749 | + } | |
| 2750 | + } | |
| 2751 | + | |
| 2752 | + /* If we get to this point, that means there is at least one listening | |
| 2753 | + ** socket on either listen4 or listen6 and perhaps on both. */ | |
| 2754 | + assert( listen4>0 || listen6>0 ); | |
| 2755 | + if( listen4>0 ) listen(listen4,10); | |
| 2756 | + if( listen6>0 ) listen(listen6,10); | |
| 2704 | 2757 | if( zBrowser && (flags & HTTP_SERVER_UNIXSOCKET)==0 ){ |
| 2705 | 2758 | assert( strstr(zBrowser,"%d")!=0 ); |
| 2706 | 2759 | zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort); |
| 2707 | 2760 | #if defined(__CYGWIN__) |
| 2708 | 2761 | /* On Cygwin, we can do better than "echo" */ |
| @@ -2716,57 +2769,69 @@ | ||
| 2716 | 2769 | #endif |
| 2717 | 2770 | if( fossil_system(zBrowser)<0 ){ |
| 2718 | 2771 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2719 | 2772 | } |
| 2720 | 2773 | } |
| 2774 | + | |
| 2775 | + /* What for incomming requests. For each request, fork() a child process | |
| 2776 | + ** to deal with that request. The child process returns. The parent | |
| 2777 | + ** keeps on listening and never returns. | |
| 2778 | + */ | |
| 2721 | 2779 | while( 1 ){ |
| 2722 | 2780 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2723 | 2781 | while( nchildren>=FOSSIL_MAX_CONNECTIONS ){ |
| 2724 | 2782 | if( wait(0)>=0 ) nchildren--; |
| 2725 | 2783 | } |
| 2726 | 2784 | #endif |
| 2727 | 2785 | delay.tv_sec = 0; |
| 2728 | 2786 | delay.tv_usec = 100000; |
| 2729 | 2787 | FD_ZERO(&readfds); |
| 2730 | - assert( listener>=0 ); | |
| 2731 | - FD_SET( listener, &readfds); | |
| 2732 | - select( listener+1, &readfds, 0, 0, &delay); | |
| 2733 | - if( FD_ISSET(listener, &readfds) ){ | |
| 2734 | - lenaddr = sizeof(inaddr); | |
| 2735 | - connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); | |
| 2736 | - if( connection>=0 ){ | |
| 2737 | - if( flags & HTTP_SERVER_NOFORK ){ | |
| 2738 | - child = 0; | |
| 2739 | - }else{ | |
| 2740 | - child = fork(); | |
| 2741 | - } | |
| 2742 | - if( child!=0 ){ | |
| 2743 | - if( child>0 ){ | |
| 2744 | - nchildren++; | |
| 2745 | - nRequest++; | |
| 2746 | - } | |
| 2747 | - close(connection); | |
| 2748 | - }else{ | |
| 2749 | - int nErr = 0, fd; | |
| 2750 | - g.zSockName = 0 /* avoid deleting the socket via atexit() */; | |
| 2751 | - close(0); | |
| 2752 | - fd = dup(connection); | |
| 2753 | - if( fd!=0 ) nErr++; | |
| 2754 | - close(1); | |
| 2755 | - fd = dup(connection); | |
| 2756 | - if( fd!=1 ) nErr++; | |
| 2757 | - if( 0 && !g.fAnyTrace ){ | |
| 2758 | - close(2); | |
| 2759 | - fd = dup(connection); | |
| 2760 | - if( fd!=2 ) nErr++; | |
| 2761 | - } | |
| 2762 | - close(connection); | |
| 2763 | - close(listener); | |
| 2764 | - g.nPendingRequest = nchildren+1; | |
| 2765 | - g.nRequest = nRequest+1; | |
| 2766 | - return nErr; | |
| 2767 | - } | |
| 2788 | + assert( listen4>0 || listen6>0 ); | |
| 2789 | + if( listen4>0 ) FD_SET( listen4, &readfds); | |
| 2790 | + if( listen6>0 ) FD_SET( listen6, &readfds); | |
| 2791 | + select( mxListen+1, &readfds, 0, 0, &delay); | |
| 2792 | + if( listen4>0 && FD_ISSET(listen4, &readfds) ){ | |
| 2793 | + lenaddr = sizeof(inaddr4); | |
| 2794 | + connection = accept(listen4, (struct sockaddr*)&inaddr4, &lenaddr); | |
| 2795 | + }else if( listen6>0 && FD_ISSET(listen6, &readfds) ){ | |
| 2796 | + lenaddr = sizeof(inaddr6); | |
| 2797 | + connection = accept(listen6, (struct sockaddr*)&inaddr6, &lenaddr); | |
| 2798 | + }else{ | |
| 2799 | + connection = -1; | |
| 2800 | + } | |
| 2801 | + if( connection>=0 ){ | |
| 2802 | + if( flags & HTTP_SERVER_NOFORK ){ | |
| 2803 | + child = 0; | |
| 2804 | + }else{ | |
| 2805 | + child = fork(); | |
| 2806 | + } | |
| 2807 | + if( child!=0 ){ | |
| 2808 | + if( child>0 ){ | |
| 2809 | + nchildren++; | |
| 2810 | + nRequest++; | |
| 2811 | + } | |
| 2812 | + close(connection); | |
| 2813 | + }else{ | |
| 2814 | + int nErr = 0, fd; | |
| 2815 | + g.zSockName = 0 /* avoid deleting the socket via atexit() */; | |
| 2816 | + close(0); | |
| 2817 | + fd = dup(connection); | |
| 2818 | + if( fd!=0 ) nErr++; | |
| 2819 | + close(1); | |
| 2820 | + fd = dup(connection); | |
| 2821 | + if( fd!=1 ) nErr++; | |
| 2822 | + if( 0 && !g.fAnyTrace ){ | |
| 2823 | + close(2); | |
| 2824 | + fd = dup(connection); | |
| 2825 | + if( fd!=2 ) nErr++; | |
| 2826 | + } | |
| 2827 | + close(connection); | |
| 2828 | + if( listen4>0 ) close(listen4); | |
| 2829 | + if( listen6>0 ) close(listen6); | |
| 2830 | + g.nPendingRequest = nchildren+1; | |
| 2831 | + g.nRequest = nRequest+1; | |
| 2832 | + return nErr; | |
| 2768 | 2833 | } |
| 2769 | 2834 | } |
| 2770 | 2835 | /* Bury dead children */ |
| 2771 | 2836 | if( nchildren ){ |
| 2772 | 2837 | while(1){ |
| 2773 | 2838 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -2497,29 +2497,10 @@ | |
| 2497 | } |
| 2498 | fossil_free(zToFree); |
| 2499 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 2500 | cgi_init(); |
| 2501 | } |
| 2502 | |
| 2503 | #if !defined(_WIN32) |
| 2504 | /* |
| 2505 | ** Change the listening socket, if necessary, so that it will accept both IPv4 |
| 2506 | ** and IPv6 |
| 2507 | */ |
| 2508 | static void allowBothIpV4andV6(int listener){ |
| 2509 | #if defined(IPV6_V6ONLY) |
| 2510 | int ipv6only = -1; |
| 2511 | socklen_t ipv6only_size = sizeof(ipv6only); |
| 2512 | getsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &ipv6only_size); |
| 2513 | if( ipv6only ){ |
| 2514 | ipv6only = 0; |
| 2515 | setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, ipv6only_size); |
| 2516 | } |
| 2517 | #endif /* defined(IPV6_ONLY) */ |
| 2518 | } |
| 2519 | #endif /* !defined(_WIN32) */ |
| 2520 | |
| 2521 | |
| 2522 | #if INTERFACE |
| 2523 | /* |
| 2524 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 2525 | */ |
| @@ -2559,150 +2540,222 @@ | |
| 2559 | ){ |
| 2560 | #if defined(_WIN32) |
| 2561 | /* Use win32_http_server() instead */ |
| 2562 | fossil_exit(1); |
| 2563 | #else |
| 2564 | int listener = -1; /* The server socket */ |
| 2565 | int connection; /* A socket for each individual connection */ |
| 2566 | int nRequest = 0; /* Number of requests handled so far */ |
| 2567 | fd_set readfds; /* Set of file descriptors for select() */ |
| 2568 | socklen_t lenaddr; /* Length of the inaddr structure */ |
| 2569 | int child; /* PID of the child process */ |
| 2570 | int nchildren = 0; /* Number of child processes */ |
| 2571 | struct timeval delay; /* How long to wait inside select() */ |
| 2572 | struct sockaddr_in6 inaddr; /* The socket address */ |
| 2573 | struct sockaddr_in inaddr4; /* IPv4 address; needed by OpenBSD */ |
| 2574 | struct sockaddr_un uxaddr; /* The address for unix-domain sockets */ |
| 2575 | int opt = 1; /* setsockopt flag */ |
| 2576 | int rc; /* Result code from system calls */ |
| 2577 | int iPort = mnPort; /* Port to try to use */ |
| 2578 | int bIPv4 = 0; /* Use IPv4 only; use inaddr4, not inaddr */ |
| 2579 | |
| 2580 | while( iPort<=mxPort ){ |
| 2581 | if( flags & HTTP_SERVER_UNIXSOCKET ){ |
| 2582 | /* Initialize a Unix socket named g.zSockName */ |
| 2583 | assert( g.zSockName!=0 ); |
| 2584 | memset(&uxaddr, 0, sizeof(uxaddr)); |
| 2585 | if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){ |
| 2586 | fossil_fatal("name of unix socket too big: %s\nmax size: %d\n", |
| 2587 | g.zSockName, (int)sizeof(uxaddr.sun_path)); |
| 2588 | } |
| 2589 | if( file_isdir(g.zSockName, ExtFILE)!=0 ){ |
| 2590 | if( !file_issocket(g.zSockName) ){ |
| 2591 | fossil_fatal("cannot name socket \"%s\" because another object" |
| 2592 | " with that name already exists", g.zSockName); |
| 2593 | }else{ |
| 2594 | unlink(g.zSockName); |
| 2595 | } |
| 2596 | } |
| 2597 | uxaddr.sun_family = AF_UNIX; |
| 2598 | strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1); |
| 2599 | listener = socket(AF_UNIX, SOCK_STREAM, 0); |
| 2600 | if( listener<0 ){ |
| 2601 | fossil_fatal("unable to create a unix socket named %s", |
| 2602 | g.zSockName); |
| 2603 | } |
| 2604 | /* Set the access permission for the new socket. Default to 0660. |
| 2605 | ** But use an alternative specified by --socket-mode if available. |
| 2606 | ** Do this before bind() to avoid a race condition. */ |
| 2607 | if( g.zSockMode ){ |
| 2608 | file_set_mode(g.zSockName, listener, g.zSockMode, 0); |
| 2609 | }else{ |
| 2610 | file_set_mode(g.zSockName, listener, "0660", 1); |
| 2611 | } |
| 2612 | }else{ |
| 2613 | /* Initialize a TCP/IP socket on port iPort */ |
| 2614 | if( (flags & HTTP_SERVER_LOCALHOST)!=0 && zIpAddr==0 ){ |
| 2615 | /* Map all loopback to 127.0.0.1, since this is the easiest way |
| 2616 | ** to support OpenBSD and its limitations without burdening |
| 2617 | ** Linux and MacOS with lots of extra code and complication. */ |
| 2618 | zIpAddr = "127.0.0.1"; |
| 2619 | } |
| 2620 | if( zIpAddr ){ |
| 2621 | if( strchr(zIpAddr,':') ){ |
| 2622 | memset(&inaddr, 0, sizeof(inaddr)); |
| 2623 | inaddr.sin6_family = AF_INET6; |
| 2624 | bIPv4 = 0; |
| 2625 | if( inet_pton(AF_INET6, zIpAddr, &inaddr.sin6_addr)==0 ){ |
| 2626 | fossil_fatal("not a valid IPv6 address: %s", zIpAddr); |
| 2627 | } |
| 2628 | }else{ |
| 2629 | memset(&inaddr4, 0, sizeof(inaddr4)); |
| 2630 | inaddr4.sin_family = AF_INET; |
| 2631 | bIPv4 = 1; |
| 2632 | inaddr4.sin_addr.s_addr = inet_addr(zIpAddr); |
| 2633 | if( inaddr4.sin_addr.s_addr == INADDR_NONE ){ |
| 2634 | fossil_fatal("not a valid IPv4 address: %s", zIpAddr); |
| 2635 | } |
| 2636 | } |
| 2637 | }else{ |
| 2638 | /* Bind to any and all available IP addresses */ |
| 2639 | memset(&inaddr, 0, sizeof(inaddr)); |
| 2640 | inaddr.sin6_family = AF_INET6; |
| 2641 | inaddr.sin6_addr = in6addr_any; |
| 2642 | bIPv4 = 0; |
| 2643 | } |
| 2644 | if( bIPv4 ){ |
| 2645 | inaddr4.sin_port = htons(iPort); |
| 2646 | listener = socket(AF_INET, SOCK_STREAM, 0); |
| 2647 | }else{ |
| 2648 | inaddr.sin6_port = htons(iPort); |
| 2649 | listener = socket(AF_INET6, SOCK_STREAM, 0); |
| 2650 | allowBothIpV4andV6(listener); |
| 2651 | } |
| 2652 | if( listener<0 ){ |
| 2653 | iPort++; |
| 2654 | continue; |
| 2655 | } |
| 2656 | } |
| 2657 | |
| 2658 | /* if we can't terminate nicely, at least allow the socket to be reused */ |
| 2659 | setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); |
| 2660 | |
| 2661 | if( flags & HTTP_SERVER_UNIXSOCKET ){ |
| 2662 | rc = bind(listener, (struct sockaddr*)&uxaddr, sizeof(uxaddr)); |
| 2663 | /* Set the owner of the socket if requested by --socket-owner. This |
| 2664 | ** must wait until after bind(), after the filesystem object has been |
| 2665 | ** created. See https://lkml.org/lkml/2004/11/1/84 and |
| 2666 | ** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */ |
| 2667 | if( g.zSockOwner ){ |
| 2668 | file_set_owner(g.zSockName, listener, g.zSockOwner); |
| 2669 | } |
| 2670 | }else if( bIPv4 ){ |
| 2671 | rc = bind(listener, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); |
| 2672 | }else{ |
| 2673 | rc = bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr)); |
| 2674 | } |
| 2675 | if( rc<0 ){ |
| 2676 | close(listener); |
| 2677 | iPort++; |
| 2678 | continue; |
| 2679 | } |
| 2680 | break; |
| 2681 | } |
| 2682 | if( iPort>mxPort ){ |
| 2683 | if( flags & HTTP_SERVER_UNIXSOCKET ){ |
| 2684 | fossil_fatal("unable to listen on unix socket %s", zIpAddr); |
| 2685 | }else if( mnPort==mxPort ){ |
| 2686 | fossil_fatal("unable to open listening socket on port %d", mnPort); |
| 2687 | }else{ |
| 2688 | fossil_fatal("unable to open listening socket on any" |
| 2689 | " port in the range %d..%d", mnPort, mxPort); |
| 2690 | } |
| 2691 | } |
| 2692 | if( iPort>mxPort ) return 1; |
| 2693 | listen(listener,10); |
| 2694 | if( flags & HTTP_SERVER_UNIXSOCKET ){ |
| 2695 | fossil_print("Listening for %s requests on unix socket %s\n", |
| 2696 | (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" : |
| 2697 | g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", g.zSockName); |
| 2698 | }else{ |
| 2699 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 2700 | (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" : |
| 2701 | g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", iPort); |
| 2702 | } |
| 2703 | fflush(stdout); |
| 2704 | if( zBrowser && (flags & HTTP_SERVER_UNIXSOCKET)==0 ){ |
| 2705 | assert( strstr(zBrowser,"%d")!=0 ); |
| 2706 | zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort); |
| 2707 | #if defined(__CYGWIN__) |
| 2708 | /* On Cygwin, we can do better than "echo" */ |
| @@ -2716,57 +2769,69 @@ | |
| 2716 | #endif |
| 2717 | if( fossil_system(zBrowser)<0 ){ |
| 2718 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2719 | } |
| 2720 | } |
| 2721 | while( 1 ){ |
| 2722 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2723 | while( nchildren>=FOSSIL_MAX_CONNECTIONS ){ |
| 2724 | if( wait(0)>=0 ) nchildren--; |
| 2725 | } |
| 2726 | #endif |
| 2727 | delay.tv_sec = 0; |
| 2728 | delay.tv_usec = 100000; |
| 2729 | FD_ZERO(&readfds); |
| 2730 | assert( listener>=0 ); |
| 2731 | FD_SET( listener, &readfds); |
| 2732 | select( listener+1, &readfds, 0, 0, &delay); |
| 2733 | if( FD_ISSET(listener, &readfds) ){ |
| 2734 | lenaddr = sizeof(inaddr); |
| 2735 | connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); |
| 2736 | if( connection>=0 ){ |
| 2737 | if( flags & HTTP_SERVER_NOFORK ){ |
| 2738 | child = 0; |
| 2739 | }else{ |
| 2740 | child = fork(); |
| 2741 | } |
| 2742 | if( child!=0 ){ |
| 2743 | if( child>0 ){ |
| 2744 | nchildren++; |
| 2745 | nRequest++; |
| 2746 | } |
| 2747 | close(connection); |
| 2748 | }else{ |
| 2749 | int nErr = 0, fd; |
| 2750 | g.zSockName = 0 /* avoid deleting the socket via atexit() */; |
| 2751 | close(0); |
| 2752 | fd = dup(connection); |
| 2753 | if( fd!=0 ) nErr++; |
| 2754 | close(1); |
| 2755 | fd = dup(connection); |
| 2756 | if( fd!=1 ) nErr++; |
| 2757 | if( 0 && !g.fAnyTrace ){ |
| 2758 | close(2); |
| 2759 | fd = dup(connection); |
| 2760 | if( fd!=2 ) nErr++; |
| 2761 | } |
| 2762 | close(connection); |
| 2763 | close(listener); |
| 2764 | g.nPendingRequest = nchildren+1; |
| 2765 | g.nRequest = nRequest+1; |
| 2766 | return nErr; |
| 2767 | } |
| 2768 | } |
| 2769 | } |
| 2770 | /* Bury dead children */ |
| 2771 | if( nchildren ){ |
| 2772 | while(1){ |
| 2773 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -2497,29 +2497,10 @@ | |
| 2497 | } |
| 2498 | fossil_free(zToFree); |
| 2499 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 2500 | cgi_init(); |
| 2501 | } |
| 2502 | |
| 2503 | #if INTERFACE |
| 2504 | /* |
| 2505 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 2506 | */ |
| @@ -2559,150 +2540,222 @@ | |
| 2540 | ){ |
| 2541 | #if defined(_WIN32) |
| 2542 | /* Use win32_http_server() instead */ |
| 2543 | fossil_exit(1); |
| 2544 | #else |
| 2545 | int listen4 = -1; /* Main socket; IPv4 or unix-domain */ |
| 2546 | int listen6 = -1; /* Aux socket for corresponding IPv6 */ |
| 2547 | int mxListen = -1; /* Maximum of listen4 and listen6 */ |
| 2548 | int connection; /* An incoming connection */ |
| 2549 | int nRequest = 0; /* Number of requests handled so far */ |
| 2550 | fd_set readfds; /* Set of file descriptors for select() */ |
| 2551 | socklen_t lenaddr; /* Length of the inaddr structure */ |
| 2552 | int child; /* PID of the child process */ |
| 2553 | int nchildren = 0; /* Number of child processes */ |
| 2554 | struct timeval delay; /* How long to wait inside select() */ |
| 2555 | struct sockaddr_in6 inaddr6; /* Address for IPv6 */ |
| 2556 | struct sockaddr_in inaddr4; /* Address for IPv4 */ |
| 2557 | struct sockaddr_un uxaddr; /* The address for unix-domain sockets */ |
| 2558 | int opt = 1; /* setsockopt flag */ |
| 2559 | int rc; /* Result code from system calls */ |
| 2560 | int iPort = mnPort; /* Port to try to use */ |
| 2561 | const char *zRequestType; /* Type of requests to listen for */ |
| 2562 | |
| 2563 | |
| 2564 | if( flags & HTTP_SERVER_SCGI ){ |
| 2565 | zRequestType = "SCGI"; |
| 2566 | }else if( g.httpUseSSL ){ |
| 2567 | zRequestType = "TLS-encrypted HTTPS"; |
| 2568 | }else{ |
| 2569 | zRequestType = "HTTP"; |
| 2570 | } |
| 2571 | |
| 2572 | if( flags & HTTP_SERVER_UNIXSOCKET ){ |
| 2573 | /* CASE 1: A unix socket named g.zSockName. After creation, set the |
| 2574 | ** permissions on the new socket to g.zSockMode and make the |
| 2575 | ** owner of the socket be g.zSockOwner. |
| 2576 | */ |
| 2577 | assert( g.zSockName!=0 ); |
| 2578 | memset(&uxaddr, 0, sizeof(uxaddr)); |
| 2579 | if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){ |
| 2580 | fossil_fatal("name of unix socket too big: %s\nmax size: %d\n", |
| 2581 | g.zSockName, (int)sizeof(uxaddr.sun_path)); |
| 2582 | } |
| 2583 | if( file_isdir(g.zSockName, ExtFILE)!=0 ){ |
| 2584 | if( !file_issocket(g.zSockName) ){ |
| 2585 | fossil_fatal("cannot name socket \"%s\" because another object" |
| 2586 | " with that name already exists", g.zSockName); |
| 2587 | }else{ |
| 2588 | unlink(g.zSockName); |
| 2589 | } |
| 2590 | } |
| 2591 | uxaddr.sun_family = AF_UNIX; |
| 2592 | strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1); |
| 2593 | listen4 = socket(AF_UNIX, SOCK_STREAM, 0); |
| 2594 | if( listen4<0 ){ |
| 2595 | fossil_fatal("unable to create a unix socket named %s", |
| 2596 | g.zSockName); |
| 2597 | } |
| 2598 | mxListen = listen4; |
| 2599 | listen6 = -1; |
| 2600 | |
| 2601 | /* Set the access permission for the new socket. Default to 0660. |
| 2602 | ** But use an alternative specified by --socket-mode if available. |
| 2603 | ** Do this before bind() to avoid a race condition. */ |
| 2604 | if( g.zSockMode ){ |
| 2605 | file_set_mode(g.zSockName, listen4, g.zSockMode, 0); |
| 2606 | }else{ |
| 2607 | file_set_mode(g.zSockName, listen4, "0660", 1); |
| 2608 | } |
| 2609 | rc = bind(listen4, (struct sockaddr*)&uxaddr, sizeof(uxaddr)); |
| 2610 | /* Set the owner of the socket if requested by --socket-owner. This |
| 2611 | ** must wait until after bind(), after the filesystem object has been |
| 2612 | ** created. See https://lkml.org/lkml/2004/11/1/84 and |
| 2613 | ** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */ |
| 2614 | if( g.zSockOwner ){ |
| 2615 | file_set_owner(g.zSockName, listen4, g.zSockOwner); |
| 2616 | } |
| 2617 | fossil_print("Listening for %s requests on unix socket %s\n", |
| 2618 | zRequestType, g.zSockName); |
| 2619 | fflush(stdout); |
| 2620 | }else if( zIpAddr && strchr(zIpAddr,':')!=0 ){ |
| 2621 | /* CASE 2: TCP on IPv6 IP address specified by zIpAddr and on port iPort. |
| 2622 | */ |
| 2623 | assert( mnPort==mxPort ); |
| 2624 | memset(&inaddr6, 0, sizeof(inaddr6)); |
| 2625 | inaddr6.sin6_family = AF_INET6; |
| 2626 | inaddr6.sin6_port = htons(iPort); |
| 2627 | if( inet_pton(AF_INET6, zIpAddr, &inaddr6.sin6_addr)==0 ){ |
| 2628 | fossil_fatal("not a valid IPv6 address: %s", zIpAddr); |
| 2629 | } |
| 2630 | listen6 = socket(AF_INET6, SOCK_STREAM, 0); |
| 2631 | if( listen6>0 ){ |
| 2632 | opt = 1; |
| 2633 | setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2634 | rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6)); |
| 2635 | if( rc<0 ){ |
| 2636 | close(listen6); |
| 2637 | listen6 = -1; |
| 2638 | } |
| 2639 | } |
| 2640 | if( listen6<0 ){ |
| 2641 | fossil_fatal("cannot open a listening socket on [%s]:%d", |
| 2642 | zIpAddr, mnPort); |
| 2643 | } |
| 2644 | mxListen = listen6; |
| 2645 | listen4 = -1; |
| 2646 | fossil_print("Listening for %s requests on [%s]:%d\n", |
| 2647 | zRequestType, zIpAddr, iPort); |
| 2648 | fflush(stdout); |
| 2649 | }else if( zIpAddr && zIpAddr[0] ){ |
| 2650 | /* CASE 3: TCP on IPv4 IP address specified by zIpAddr and on port iPort. |
| 2651 | */ |
| 2652 | assert( mnPort==mxPort ); |
| 2653 | memset(&inaddr4, 0, sizeof(inaddr4)); |
| 2654 | inaddr4.sin_family = AF_INET; |
| 2655 | inaddr4.sin_port = htons(iPort); |
| 2656 | if( strcmp(zIpAddr, "localhost")==0 ) zIpAddr = "127.0.0.1"; |
| 2657 | inaddr4.sin_addr.s_addr = inet_addr(zIpAddr); |
| 2658 | if( inaddr4.sin_addr.s_addr == INADDR_NONE ){ |
| 2659 | fossil_fatal("not a valid IPv4 address: %s", zIpAddr); |
| 2660 | } |
| 2661 | listen4 = socket(AF_INET, SOCK_STREAM, 0); |
| 2662 | if( listen4>0 ){ |
| 2663 | setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2664 | rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); |
| 2665 | if( rc<0 ){ |
| 2666 | close(listen6); |
| 2667 | listen4 = -1; |
| 2668 | } |
| 2669 | } |
| 2670 | if( listen4<0 ){ |
| 2671 | fossil_fatal("cannot open a listening socket on %s:%d", |
| 2672 | zIpAddr, mnPort); |
| 2673 | } |
| 2674 | mxListen = listen4; |
| 2675 | listen6 = -1; |
| 2676 | fossil_print("Listening for %s requests on TCP port %s:%d\n", |
| 2677 | zRequestType, zIpAddr, iPort); |
| 2678 | fflush(stdout); |
| 2679 | }else{ |
| 2680 | /* CASE 4: Listen on all available IP addresses, or on only loopback |
| 2681 | ** addresses (if HTTP_SERVER_LOCALHOST). The TCP port is the |
| 2682 | ** first available in the range of mnPort..mxPort. Listen |
| 2683 | ** on both IPv4 and IPv6, if possible. The TCP port scan is done |
| 2684 | ** on IPv4. |
| 2685 | */ |
| 2686 | while( iPort<=mxPort ){ |
| 2687 | const char *zProto; |
| 2688 | memset(&inaddr4, 0, sizeof(inaddr4)); |
| 2689 | inaddr4.sin_family = AF_INET; |
| 2690 | inaddr4.sin_port = htons(iPort); |
| 2691 | if( flags & HTTP_SERVER_LOCALHOST ){ |
| 2692 | inaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| 2693 | }else{ |
| 2694 | inaddr4.sin_addr.s_addr = htonl(INADDR_ANY); |
| 2695 | } |
| 2696 | listen4 = socket(AF_INET, SOCK_STREAM, 0); |
| 2697 | if( listen4>0 ){ |
| 2698 | setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2699 | rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); |
| 2700 | if( rc<0 ){ |
| 2701 | close(listen4); |
| 2702 | listen4 = -1; |
| 2703 | } |
| 2704 | } |
| 2705 | if( listen4<0 ){ |
| 2706 | iPort++; |
| 2707 | continue; |
| 2708 | } |
| 2709 | mxListen = listen4; |
| 2710 | |
| 2711 | /* If we get here, that means we found an open TCP port at iPort for |
| 2712 | ** IPv4. Try to set up a corresponding IPv6 socket on the same port. |
| 2713 | */ |
| 2714 | memset(&inaddr6, 0, sizeof(inaddr6)); |
| 2715 | inaddr6.sin6_family = AF_INET6; |
| 2716 | inaddr6.sin6_port = htons(iPort); |
| 2717 | if( flags & HTTP_SERVER_LOCALHOST ){ |
| 2718 | inaddr6.sin6_addr = in6addr_loopback; |
| 2719 | }else{ |
| 2720 | inaddr6.sin6_addr = in6addr_any; |
| 2721 | } |
| 2722 | listen6 = socket(AF_INET6, SOCK_STREAM, 0); |
| 2723 | if( listen6>0 ){ |
| 2724 | setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2725 | setsockopt(listen6, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); |
| 2726 | rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6)); |
| 2727 | if( rc<0 ){ |
| 2728 | close(listen6); |
| 2729 | listen6 = -1; |
| 2730 | } |
| 2731 | } |
| 2732 | if( listen6<0 ){ |
| 2733 | zProto = "IPv4 only"; |
| 2734 | }else{ |
| 2735 | zProto = "IPv4 and IPv6"; |
| 2736 | if( listen6>listen4 ) mxListen = listen6; |
| 2737 | } |
| 2738 | |
| 2739 | fossil_print("Listening for %s requests on TCP port %s%d, %s\n", |
| 2740 | zRequestType, |
| 2741 | (flags & HTTP_SERVER_LOCALHOST)!=0 ? "localhost:" : "", |
| 2742 | iPort, zProto); |
| 2743 | fflush(stdout); |
| 2744 | break; |
| 2745 | } |
| 2746 | if( iPort>mxPort ){ |
| 2747 | fossil_fatal("no available TCP ports in the range %d..%d", |
| 2748 | mnPort, mxPort); |
| 2749 | } |
| 2750 | } |
| 2751 | |
| 2752 | /* If we get to this point, that means there is at least one listening |
| 2753 | ** socket on either listen4 or listen6 and perhaps on both. */ |
| 2754 | assert( listen4>0 || listen6>0 ); |
| 2755 | if( listen4>0 ) listen(listen4,10); |
| 2756 | if( listen6>0 ) listen(listen6,10); |
| 2757 | if( zBrowser && (flags & HTTP_SERVER_UNIXSOCKET)==0 ){ |
| 2758 | assert( strstr(zBrowser,"%d")!=0 ); |
| 2759 | zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort); |
| 2760 | #if defined(__CYGWIN__) |
| 2761 | /* On Cygwin, we can do better than "echo" */ |
| @@ -2716,57 +2769,69 @@ | |
| 2769 | #endif |
| 2770 | if( fossil_system(zBrowser)<0 ){ |
| 2771 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2772 | } |
| 2773 | } |
| 2774 | |
| 2775 | /* What for incomming requests. For each request, fork() a child process |
| 2776 | ** to deal with that request. The child process returns. The parent |
| 2777 | ** keeps on listening and never returns. |
| 2778 | */ |
| 2779 | while( 1 ){ |
| 2780 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2781 | while( nchildren>=FOSSIL_MAX_CONNECTIONS ){ |
| 2782 | if( wait(0)>=0 ) nchildren--; |
| 2783 | } |
| 2784 | #endif |
| 2785 | delay.tv_sec = 0; |
| 2786 | delay.tv_usec = 100000; |
| 2787 | FD_ZERO(&readfds); |
| 2788 | assert( listen4>0 || listen6>0 ); |
| 2789 | if( listen4>0 ) FD_SET( listen4, &readfds); |
| 2790 | if( listen6>0 ) FD_SET( listen6, &readfds); |
| 2791 | select( mxListen+1, &readfds, 0, 0, &delay); |
| 2792 | if( listen4>0 && FD_ISSET(listen4, &readfds) ){ |
| 2793 | lenaddr = sizeof(inaddr4); |
| 2794 | connection = accept(listen4, (struct sockaddr*)&inaddr4, &lenaddr); |
| 2795 | }else if( listen6>0 && FD_ISSET(listen6, &readfds) ){ |
| 2796 | lenaddr = sizeof(inaddr6); |
| 2797 | connection = accept(listen6, (struct sockaddr*)&inaddr6, &lenaddr); |
| 2798 | }else{ |
| 2799 | connection = -1; |
| 2800 | } |
| 2801 | if( connection>=0 ){ |
| 2802 | if( flags & HTTP_SERVER_NOFORK ){ |
| 2803 | child = 0; |
| 2804 | }else{ |
| 2805 | child = fork(); |
| 2806 | } |
| 2807 | if( child!=0 ){ |
| 2808 | if( child>0 ){ |
| 2809 | nchildren++; |
| 2810 | nRequest++; |
| 2811 | } |
| 2812 | close(connection); |
| 2813 | }else{ |
| 2814 | int nErr = 0, fd; |
| 2815 | g.zSockName = 0 /* avoid deleting the socket via atexit() */; |
| 2816 | close(0); |
| 2817 | fd = dup(connection); |
| 2818 | if( fd!=0 ) nErr++; |
| 2819 | close(1); |
| 2820 | fd = dup(connection); |
| 2821 | if( fd!=1 ) nErr++; |
| 2822 | if( 0 && !g.fAnyTrace ){ |
| 2823 | close(2); |
| 2824 | fd = dup(connection); |
| 2825 | if( fd!=2 ) nErr++; |
| 2826 | } |
| 2827 | close(connection); |
| 2828 | if( listen4>0 ) close(listen4); |
| 2829 | if( listen6>0 ) close(listen6); |
| 2830 | g.nPendingRequest = nchildren+1; |
| 2831 | g.nRequest = nRequest+1; |
| 2832 | return nErr; |
| 2833 | } |
| 2834 | } |
| 2835 | /* Bury dead children */ |
| 2836 | if( nchildren ){ |
| 2837 | while(1){ |
| 2838 |