Index: auto.def ================================================================== --- auto.def +++ auto.def @@ -12,10 +12,11 @@ => {Enable Tcl integration via private stubs mechanism} internal-sqlite=1 => {Don't use the internal SQLite, use the system one} static=0 => {Link a static executable} lineedit=1 => {Disable line editing} fossil-debug=0 => {Build with fossil debugging enabled} + ipv6=1 => {Disable IPv6 support} json=0 => {Build with fossil JSON API enabled} } # sqlite wants these types if possible cc-with {-includes {stdint.h inttypes.h}} { @@ -88,10 +89,19 @@ if {[opt-bool static]} { # XXX: This will not work on all systems. define-append EXTRA_LDFLAGS -static } + +if {[opt-bool ipv6]} { + define-append EXTRA_CFLAGS -DWITH_IPV6 + msg-result "IPv6 support enabled" + if {[cc-check-functions getaddrinfo]} { + define-append EXTRA_CFLAGS -DHAVE_GETADDRINFO + msg-result "getaddrinfo() enabled" + } +} # Check for zlib, using the given location if specified set zlibpath [opt-val with-zlib] if {$zlibpath ne ""} { cc-with [list -cflags "-I$zlibpath -L$zlibpath"] Index: src/cgi.c ================================================================== --- src/cgi.c +++ src/cgi.c @@ -31,10 +31,11 @@ # include # include # include # include # include +# include /* for NI_NUMERICHOST */ #endif #ifdef __EMX__ typedef int socklen_t; #endif #include @@ -1267,12 +1268,12 @@ ** and subsequent code handles the actual generation of the webpage. */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; - struct sockaddr_in remoteName; - socklen_t size = sizeof(struct sockaddr_in); + struct sockaddr_storage remoteName; + socklen_t size = sizeof(remoteName); char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } @@ -1296,20 +1297,10 @@ cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); - if( zIpAddr==0 && - getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, - &size)>=0 - ){ - zIpAddr = inet_ntoa(remoteName.sin_addr); - } - if( zIpAddr ){ - cgi_setenv("REMOTE_ADDR", zIpAddr); - g.zIpAddr = mprintf("%s", zIpAddr); - } /* Get all the optional fields that follow the first line. */ while( fgets(zLine,sizeof(zLine),g.httpIn) ){ char *zFieldName; @@ -1340,10 +1331,19 @@ cgi_setenv("HTTP_HOST", zVal); }else if( fossil_strcmp(zFieldName,"if-none-match:")==0 ){ cgi_setenv("HTTP_IF_NONE_MATCH", zVal); }else if( fossil_strcmp(zFieldName,"if-modified-since:")==0 ){ cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); + }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ + char* p = zVal; + /* + ** x-forwarded-for header is a list of comma-separated addresses, + ** with leftmost address corresponding to the client + */ + while(*p && *p != ',') p++; + *p = '\0'; + zIpAddr = mprintf( "%s", zVal ); #if 0 }else if( fossil_strcmp(zFieldName,"referer:")==0 ){ cgi_setenv("HTTP_REFERER", zVal); #endif }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ @@ -1354,10 +1354,37 @@ g.zIpAddr = mprintf("%s", zIpAddr); cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); } } } + + if( zIpAddr==0 && + getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, + &size)>=0 + ){ + sa_family_t family; + int v4mapped=0; + if( remoteName.ss_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&remoteName)->sin6_addr)) ){ + v4mapped = 1; + } + if(!getnameinfo((struct sockaddr*)&remoteName, size, zLine, sizeof(zLine), + NULL, 0, NI_NUMERICHOST)){ + zIpAddr = zLine; + } else { + zIpAddr = NULL; + } + if(zIpAddr && v4mapped) { + /* ::ffff:172.16.0.2 */ + zIpAddr += 7; + } + } + if( zIpAddr ){ + cgi_setenv("REMOTE_ADDR", zIpAddr); + g.zIpAddr = mprintf("%s", zIpAddr); + } + cgi_init(); cgi_trace(0); } /* @@ -1664,42 +1691,136 @@ fd_set readfds; /* Set of file descriptors for select() */ socklen_t lenaddr; /* Length of the inaddr structure */ int child; /* PID of the child process */ int nchildren = 0; /* Number of child processes */ struct timeval delay; /* How long to wait inside select() */ +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; + struct addrinfo* res; + struct addrinfo* i; + struct sockaddr_storage inaddr; /* The socket address */ + char* sPort; + int iRet; +#else // HAVE_GETADDRINFO +#ifdef WITH_IPV6 + struct sockaddr_storage inaddr; /* The socket address */ +#else // WITH_IPV6 struct sockaddr_in inaddr; /* The socket address */ +#endif // WITH_IPV6 +#endif // HAVE_GETADDRINFO int opt = 1; /* setsockopt flag */ int iPort = mnPort; while( iPort<=mxPort ){ - memset(&inaddr, 0, sizeof(inaddr)); - inaddr.sin_family = AF_INET; - if( zIpAddr ){ - inaddr.sin_addr.s_addr = inet_addr(zIpAddr); - if( inaddr.sin_addr.s_addr == (-1) ){ - fossil_fatal("not a valid IP address: %s", zIpAddr); - } - }else if( flags & HTTP_SERVER_LOCALHOST ){ - inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - }else{ - inaddr.sin_addr.s_addr = htonl(INADDR_ANY); - } - inaddr.sin_port = htons(iPort); - listener = socket(AF_INET, SOCK_STREAM, 0); - if( listener<0 ){ +#ifdef HAVE_GETADDRINFO + memset(&hints, 0, sizeof(struct addrinfo)); +#ifdef WITH_IPV6 + hints.ai_family = PF_UNSPEC; +#else // WITH_IPV6 + hints.ai_family = PF_INET; +#endif // WITH_IPV6 + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if(!(flags & HTTP_SERVER_LOCALHOST)) hints.ai_flags |= AI_PASSIVE; + + sPort = mprintf("%d", iPort); + + if(iRet = getaddrinfo(NULL, sPort, &hints, &res)) { + fossil_fatal("Unable to obtain address: %s", gai_strerror(iRet)); + } + + for(i = res; i; i = i->ai_next) { + listener = socket(i->ai_family, i->ai_socktype, i->ai_protocol); + if(listener < 0) { + fossil_fatal("Unable to create socket"); + } + opt=1; + setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); + if(i->ai_family == AF_INET6) { + opt=0; + setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); + } + if( bind(listener, i->ai_addr, i->ai_addrlen)<0 ){ + close(listener); + listener = -1; + } + break; + } + + free(sPort); + freeaddrinfo(res); + + if(listener == -1) { iPort++; continue; } +#else // HAVE_GETADDRINFO + memset(&inaddr, 0, sizeof(inaddr)); + + if( zIpAddr ){ +#ifdef WITH_IPV6 + ((struct sockaddr_in6*)&inaddr)->sin6_family = AF_INET6; + if( inet_pton(AF_INET6, argv[1], &((struct sockaddr_in6*)&inaddr)->sin6_addr) < 1 ){ + ((struct sockaddr_in*)&inaddr)->sin_family = AF_INET; + ((struct sockaddr_in*)&inaddr)->sin_addr.s_addr = inet_addr(zIpAddr); + if( ((struct sockaddr_in*)&inaddr)->sin_addr.s_addr == (-1) ) +#else // WITH_IPV6 + inaddr.sin_family = AF_INET; + inaddr.sin_addr.s_addr = inet_addr(zIpAddr); + if( inaddr.sin_addr.s_addr == (-1) ) +#endif // WITH_IPV6 + { + fossil_fatal("not a valid IP address: %s", zIpAddr); + } +#ifdef WITH_IPV6 + } +#endif // WITH_IPV6 + }else if( flags & HTTP_SERVER_LOCALHOST ){ +#ifdef WITH_IPV6 + memcpy(&((struct sockaddr_in6*)&inaddr)->sin6_addr, &in6addr_loopback, sizeof(inaddr.sin6_addr)); +#else // WITH_IPV6 + inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +#endif // WITH_IPV6 + }else{ +#ifdef WITH_IPV6 + memcpy(&((struct sockaddr_in6*)&inaddr)->sin6_addr, &in6addr_any, sizeof(inaddr.sin6_addr)); +#else // WITH_IPV6 + inaddr.sin_addr.s_addr = htonl(INADDR_ANY); +#endif // WITH_IPV6 + } +#ifdef WITH_IPV6 + if( inaddr.ss_family == AF_INET6 ){ + ((struct sockaddr_in6*)&inaddr)->sin6_port = htons(iPort); + }else{ + ((struct sockaddr_in*)&inaddr)->sin_port = htons(iPort); + } + listener = socket(inaddr.ss_family, SOCK_STREAM, 0); +#else // WITH_IPV6 + inaddr.sin_port = htons(iPort); + listener = socket(AF_INET, SOCK_STREAM, 0); +#endif // WITH_IPV6 + if( listener<0 ){ + fossil_fatal("Unable to create socket"); + } /* if we can't terminate nicely, at least allow the socket to be reused */ setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); - if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){ +#ifdef WITH_IPV6 + opt=0; + setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); + + if( bind(listener, (struct sockaddr*)&inaddr, inaddr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) < 0 ) +#else // WITH_IPV6 + if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr)) < 0 ) +#endif // WITH_IPV6 + { close(listener); iPort++; continue; } +#endif // HAVE_GETADDRINFO break; } if( iPort>mxPort ){ if( mnPort==mxPort ){ fossil_fatal("unable to open listening socket on ports %d", mnPort); @@ -1773,11 +1894,11 @@ nchildren--; } } /* NOT REACHED */ fossil_exit(1); -#endif +#endif // WIN32 /* NOT REACHED */ return 0; } Index: src/http_socket.c ================================================================== --- src/http_socket.c +++ src/http_socket.c @@ -132,10 +132,58 @@ ** g.urlPort TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int socket_open(UrlData *pUrlData){ + int error = 0; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; + struct addrinfo* res; + struct addrinfo* i; + char ip[INET6_ADDRSTRLEN]; + void* addr; + char* sPort; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_ADDRCONFIG; +#ifdef WITH_IPV6 + hints.ai_family = PF_UNSPEC; +#else + hints.ai_family = PF_INET; +#endif + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + sPort = mprintf("%d", g.urlPort); + + if(getaddrinfo(g.urlName, sPort, &hints, &res)) { + socket_set_errmsg("can't resolve host name: %s", g.urlName); + free(sPort); + return 1; + } + for(i = res; i; i = i->ai_next) { + iSocket = socket(i->ai_family, i->ai_socktype, i->ai_protocol); + if(iSocket < 0) { + continue; + } + if(connect(iSocket, i->ai_addr, i->ai_addrlen) < 0) { + close(iSocket); + iSocket = -1; + continue; + } + if(!getnameinfo(i->ai_addr, i->ai_addrlen, ip, sizeof(ip), + NULL, 0, NI_NUMERICHOST)) + g.zIpAddr = mprintf("%s", ip); + break; + } + if(iSocket == -1) { + socket_set_errmsg("cannot connect to host %s:%s", g.urlName, sPort); + error = 1; + } + free(sPort); + freeaddrinfo(res); +#else static struct sockaddr_in addr; /* The server address */ static int addrIsInit = 0; /* True once addr is initialized */ socket_global_init(); if( !addrIsInit ){ @@ -170,16 +218,18 @@ } if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, pUrlData->port); socket_close(); - return 1; + error = 1; } +#endif #if !defined(_WIN32) - signal(SIGPIPE, SIG_IGN); + if(!error) + signal(SIGPIPE, SIG_IGN); #endif - return 0; + return error; } /* ** Send content out over the open socket connection. */ Index: src/login.c ================================================================== --- src/login.c +++ src/login.c @@ -800,10 +800,11 @@ ** This feature allows the "fossil ui" command to give the user ** full access rights without having to log in. */ zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil")); if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 || + fossil_strcmp(zIpAddr, "::1")==0 || g.fSshClient & CGI_SSH_CLIENT ) && g.useLocalauth && db_get_int("localauth",0)==0 && P("HTTPS")==0 ){ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -147,10 +147,11 @@ char *zSshCmd; /* SSH command string */ int fNoSync; /* Do not do an autosync ever. --nosync */ char *zPath; /* Name of webpage being served */ char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ + char *zRedirectBaseURL; /* Full text of the URL being served to be used in redirect */ char *zTop; /* Parent directory of zPath */ const char *zContentType; /* The content type of the input HTTP request */ int iErrPriority; /* Priority of current error message */ char *zErrMsg; /* Text of an error message */ int sslNotAvailable; /* SSL is not available. Do not redirect to https: */ @@ -1944,11 +1945,10 @@ ** "localauth" setting. Automatic login for the "server" command is available ** if the --localauth option is present and the "localauth" setting is off ** and the connection is from localhost. The optional REPOSITORY argument ** to "ui" may be a directory and will function as "server" if and only if ** the --notfound option is used. -** ** Options: ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) @@ -2026,10 +2026,11 @@ } } #else zBrowser = db_get("web-browser", "open"); #endif + zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); if( zIpAddr ){ zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); }else{ zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); }