Hi everyone,

I've written attached diff sometime ago to add:

1. Improved reverse proxy support. I'm not sure if there is any explicit
   support for reverse proxy, but the way in which I was trying to use it,
   i.e. exposing fossil repository at a _location_ in nginx, it wasn't
   working. So I decided to fix issues I encountered and added explicit
   support for it in the code which now works as I expected:

--8<---------------cut here---------------start------------->8---
chateau.d.if!abbe fossils % fossil server -R /code -P 8080 .
--8<---------------cut here---------------end--------------->8---

   And following is the corresponding nginx configuration to proxy traffic at
   /code/ to fossil server listening at port 8080:

--8<---------------cut here---------------start------------->8---
location /code/ {
    proxy_pass http://localhost:8080/ ;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect http://localhost:8080/code/ http://$http_host/code/;
}
--8<---------------cut here---------------end--------------->8---

   If this is achievable using current version (hosted at: fossil-scm.org),
   please let me know.

2. IPv6 support. Fossil uses IPv4 sockets by default. I was not sure if there
   was any technical reason to not add support for IPv6, so I modified it all
   relevant places (I was able to find) to use IPv6 sockets, perform hostname
   lookups using getaddrinfo(3), accept push/pulls to/from IPv6 address, and
   persist this information in the repository database.

I'm using it with the Fossil snapshot of September, 2011 with FreeBSD 8.2-R
(amd64) without any issues. Please let me know if you find any problems with
code.

I'll be glad if these changes makes to the trunk.

Thanks
-- 
Ashish SHUKLA

“The wonderful thing about science is that it doesn't ask for your
faith, it just ask for your eyes.” (xkcd #154)
Index: auto.def
===================================================================
--- auto.def
+++ auto.def
@@ -8,10 +8,11 @@
     with-zlib:path       => {Look for zlib in the given path}
     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}
 }
 
 # sqlite wants these types if possible
 cc-with {-includes {stdint.h inttypes.h}} {
     cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -65,10 +66,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]

Index: src/cgi.c
===================================================================
--- src/cgi.c
+++ src/cgi.c
@@ -992,12 +992,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);
+  char remoteName[256];
+  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();
@@ -1019,20 +1019,10 @@
   cgi_setenv("REQUEST_URI", zToken);
   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;
@@ -1059,18 +1049,57 @@
       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);
     }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
       cgi_setenv("HTTP_USER_AGENT", zVal);
     }
 #endif
+  }
+
+  if( zIpAddr==0 &&
+        getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName, 
+                                &size)>=0
+  ){
+    sa_family_t family;
+    void* addr = NULL;
+    int v4mapped=0;
+    switch(family = ((struct sockaddr_in*)&remoteName)->sin_family)
+    {
+      case AF_INET:
+        addr = &((struct sockaddr_in*)&remoteName)->sin_addr;
+        break;
+      case AF_INET6:
+        addr = &((struct sockaddr_in6*)&remoteName)->sin6_addr;
+       if(IN6_IS_ADDR_V4MAPPED(((struct in6_addr*)addr))) v4mapped=1;
+        break;
+    }
+    if(addr) {
+      zIpAddr = inet_ntop(family, addr, zLine, sizeof(zLine));
+      if(zIpAddr && v4mapped) {
+        /* ::ffff:172.16.0.2 */
+        zIpAddr += 7; 
+      }
+    }
+  }
+  if( zIpAddr ){
+    cgi_setenv("REMOTE_ADDR", zIpAddr);
+    g.zIpAddr = mprintf("%s", zIpAddr);
   }
 
   cgi_init();
 }
 
@@ -1108,31 +1137,57 @@
   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 WITH_IPV6
+  struct sockaddr_in6 inaddr;   /* The socket address */
+#else
   struct sockaddr_in inaddr;   /* The socket address */
+#endif
   int opt = 1;                 /* setsockopt flag */
   int iPort = mnPort;
 
   while( iPort<=mxPort ){
     memset(&inaddr, 0, sizeof(inaddr));
+#ifdef WITH_IPV6
+    inaddr.sin6_family = AF_INET6;
+#else
     inaddr.sin_family = AF_INET;
+#endif
     if( flags & HTTP_SERVER_LOCALHOST ){
+#ifdef WITH_IPV6
+      memcpy(&inaddr.sin6_addr, &in6addr_loopback, sizeof(inaddr.sin6_addr));
+#else
       inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+#endif
     }else{
+#ifdef WITH_IPV6
+      memcpy(&inaddr.sin6_addr, &in6addr_any, sizeof(inaddr.sin6_addr));
+#else
       inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
     }
+#ifdef WITH_IPV6
+    inaddr.sin6_port = htons(iPort);
+    listener = socket(AF_INET6, SOCK_STREAM, 0);
+#else
     inaddr.sin_port = htons(iPort);
     listener = socket(AF_INET, SOCK_STREAM, 0);
+#endif
     if( listener<0 ){
       iPort++;
       continue;
     }
 
     /* if we can't terminate nicely, at least allow the socket to be reused */
     setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+
+#ifdef WITH_IPV6
+    opt=0;
+    setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+#endif
 
     if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){
       close(listener);
       iPort++;
       continue;

Index: src/http_socket.c
===================================================================
--- src/http_socket.c
+++ src/http_socket.c
@@ -135,10 +135,61 @@
 **    g.urlPort       TCP/IP port to use.  Ex: 80
 **
 ** Return the number of errors.
 */
 int socket_open(void){
+  int error = 0;
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+  struct addrinfo* res;
+  struct addrinfo* i;
+  char ip[INET6_ADDRSTRLEN];
+  void* addr;
+
+  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;
+  if(getaddrinfo(g.urlName, NULL, &hints, &res)) {
+    socket_set_errmsg("can't resolve host name: %s", g.urlName);
+    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(i->ai_family == AF_INET) {
+      ((struct sockaddr_in*)i->ai_addr)->sin_port = htons(g.urlPort);
+      addr = &((struct sockaddr_in*)i->ai_addr)->sin_addr;
+    }
+#ifdef WITH_IPV6
+    else if(i->ai_family == AF_INET6) {
+      ((struct sockaddr_in6*)i->ai_addr)->sin6_port = htons(g.urlPort);
+      addr = &((struct sockaddr_in6*)i->ai_addr)->sin6_addr;
+    }
+#endif
+    if(connect(iSocket, i->ai_addr, i->ai_addrlen) < 0) {
+      close(iSocket);
+      iSocket = -1;
+      continue;
+    }
+    inet_ntop(i->ai_family, addr, &ip[0], INET6_ADDRSTRLEN);
+    g.zIpAddr = mprintf("%s", ip);
+    break;
+  }
+  if(iSocket == -1) {
+    socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
+    error = 1;
+  }
+  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 ){
@@ -172,16 +223,18 @@
     return 1;
   }
   if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
     socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
     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
@@ -563,11 +563,12 @@
   **
   ** 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
+  if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
+        fossil_strcmp(zIpAddr, "::1")==0 )
    && g.useLocalauth
    && db_get_int("localauth",0)==0
    && P("HTTPS")==0
   ){
     uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
@@ -877,11 +878,11 @@
       db_exists("SELECT 1 FROM user"
                 " WHERE login='anonymous'"
                 "   AND cap LIKE '%%h%%'") ){
     const char *zUrl = PD("REQUEST_URI", "index");
     @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
-    @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
+    @ Use <a 
href="%s(g.zTop)/login?anon=1&amp;g=%T(g.zRoot)%T(zUrl)">anonymous login</a>
     @ to enable hyperlinks.</p>
   }
 }
 
 /*

Index: src/main.c
===================================================================
--- src/main.c
+++ src/main.c
@@ -98,10 +98,12 @@
   int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
   int fNoSync;            /* Do not do an autosync even.  --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 *zRoot;            /* Repository web root */
   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: 
*/
@@ -823,20 +825,21 @@
   const char *zHost;
   const char *zMode;
   const char *zCur;
 
   if( g.zBaseURL!=0 ) return;
+  if( g.zRoot==0 ) g.zRoot="";
   zHost = PD("HTTP_HOST","");
   zMode = PD("HTTPS","off");
   zCur = PD("SCRIPT_NAME","/");
   i = strlen(zCur);
   while( i>0 && zCur[i-1]=='/' ) i--;
   if( fossil_stricmp(zMode,"on")==0 ){
-    g.zBaseURL = mprintf("https://%s%.*s";, zHost, i, zCur);
+    g.zBaseURL = mprintf("https://%s%s%.*s";, zHost, g.zRoot, i, zCur);
     g.zTop = &g.zBaseURL[8+strlen(zHost)];
   }else{
-    g.zBaseURL = mprintf("http://%s%.*s";, zHost, i, zCur);
+    g.zBaseURL = mprintf("http://%s%s%.*s";, zHost, g.zRoot, i, zCur);
     g.zTop = &g.zBaseURL[7+strlen(zHost)];
   }
 }
 
 /*
@@ -1364,18 +1367,22 @@
 
 /*
 ** COMMAND: server
 ** COMMAND: ui
 **
-** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY?
-**    Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY?
+** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
+**    Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
 **
 ** Open a socket and begin listening and responding to HTTP requests on
 ** TCP port 8080, or on any other TCP port defined by the -P or
 ** --port option.  The optional argument is the name of the repository.
 ** The repository argument may be omitted if the working directory is
 ** within an open checkout.
+**
+** If HTTP requests are being reverse proxied to the fossil server,
+** and in proxy server fossil is mapped at a virtual directory, then
+** virtual directory can be specified using optional -R or --root option.
 **
 ** The "ui" command automatically starts a web browser after initializing
 ** the web server.  The "ui" command also binds to 127.0.0.1 and so will
 ** only process HTTP traffic from the local machine.
 **
@@ -1404,10 +1411,11 @@
   zStopperFile = find_option("stopper", 0, 1);
 #endif
 
   g.thTrace = find_option("th-trace", 0, 0)!=0;
   g.useLocalauth = find_option("localauth", 0, 0)!=0;
+  g.zRoot = find_option("root", "R", 1);
   if( g.thTrace ){
     blob_zero(&g.thLog);
   }
   zPort = find_option("port", "P", 1);
   zNotFound = find_option("notfound", 0, 1);
@@ -1441,11 +1449,11 @@
       }
     }
 #else
     zBrowser = db_get("web-browser", "open");
 #endif
-    zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
+    zBrowserCmd = mprintf("%s http://localhost:%%d/%s &", zBrowser, 
(g.zRoot==0?"":g.zRoot));
   }
   db_close(1);
   if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
     fossil_fatal("unable to listen on TCP socket %d", iPort);
   }

Attachment: pgpwlwFE7gR6x.pgp
Description: PGP signature

_______________________________________________
fossil-users mailing list
fossil-users@lists.fossil-scm.org
http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users

Reply via email to