Fossil

Check-in [e506ebb7]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improved support for both IPv4 and IPv6 on "fossil server" on Windows. Patches from Olivier Mascia.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:e506ebb764e93715e9514d6c0078d3eb82580bee78f18180fa18a21b3d7edd03
User & Date: drh 2018-01-05 14:34:59
Context
2018-01-05
15:25
Always try to extract the IP address and PORT number from the --port option to "fossil server" if the option contains a ':' character. check-in: 4d3cb0da user: drh tags: trunk
14:34
Improved support for both IPv4 and IPv6 on "fossil server" on Windows. Patches from Olivier Mascia. check-in: e506ebb7 user: drh tags: trunk
13:37
Improved parsing of the --port option on the "fossil server" command. check-in: f8f2c8d2 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/winhttp.c.

    26     26   #include <windows.h>
    27     27   #include <process.h>
    28     28   #include "winhttp.h"
    29     29   
    30     30   #ifndef IPV6_V6ONLY
    31     31   # define IPV6_V6ONLY 27  /* Because this definition is missing in MinGW */
    32     32   #endif
           33  +
           34  +/*
           35  +** The SocketAddr structure holds a SOCKADDR_STORAGE and its content size.
           36  +*/
           37  +typedef struct SocketAddr SocketAddr;
           38  +struct SocketAddr {
           39  +  SOCKADDR_STORAGE addr;
           40  +  int len;
           41  +};
           42  +
           43  +static char* SocketAddr_toString(const SocketAddr* pAddr){
           44  +  SocketAddr addr;
           45  +  char* zIp;
           46  +  DWORD nIp = 50;
           47  +  assert( pAddr!=NULL );
           48  +  memcpy(&addr, pAddr, sizeof(SocketAddr));
           49  +  if( addr.len==sizeof(SOCKADDR_IN6) ){
           50  +    ((SOCKADDR_IN6*)&addr)->sin6_port = 0;
           51  +  }else{
           52  +    ((SOCKADDR_IN*)&addr)->sin_port = 0;
           53  +  }
           54  +  zIp = fossil_malloc(nIp);
           55  +  if( WSAAddressToStringA((SOCKADDR*)&addr, addr.len, NULL, zIp, &nIp)!=0 ){
           56  +    zIp[0] = 0;
           57  +  }
           58  +  return zIp;
           59  +}
           60  +
           61  +/*
           62  +** The DualAddr structure holds two SocketAddr (one IPv4 and on IPv6).
           63  +*/
           64  +typedef struct DualAddr DualAddr;
           65  +struct DualAddr {
           66  +  SocketAddr a4;  /* IPv4 SOCKADDR_IN */
           67  +  SocketAddr a6;  /* IPv6 SOCKADDR_IN6 */
           68  +};
           69  +
           70  +static void DualAddr_init(DualAddr* pDA){
           71  +  assert( pDA!=NULL );
           72  +  memset(pDA, 0, sizeof(DualAddr));
           73  +  pDA->a4.len = sizeof(SOCKADDR_IN);
           74  +  pDA->a6.len = sizeof(SOCKADDR_IN6);
           75  +}
           76  +
           77  +/*
           78  +** The DualSocket structure holds two SOCKETs. One or both could be
           79  +** used or INVALID_SOCKET.  One is dedicated to IPv4, the other to IPv6.
           80  +*/
           81  +typedef struct DualSocket DualSocket;
           82  +struct DualSocket {
           83  +  SOCKET s4;    /* IPv4 socket or INVALID_SOCKET */
           84  +  SOCKET s6;    /* IPv6 socket or INVALID_SOCKET */
           85  +};
           86  +
           87  +/*
           88  +** Initializes a DualSocket.
           89  +*/
           90  +static void DualSocket_init(DualSocket* ds){
           91  +  assert( ds!=NULL );
           92  +  ds->s4 = INVALID_SOCKET;
           93  +  ds->s6 = INVALID_SOCKET;
           94  +};
           95  +
           96  +/*
           97  +** Close and reset a DualSocket.
           98  +*/
           99  +static void DualSocket_close(DualSocket* ds){
          100  +  assert( ds!=NULL );
          101  +  if( ds->s4!=INVALID_SOCKET ){
          102  +    closesocket(ds->s4);
          103  +    ds->s4 = INVALID_SOCKET;
          104  +  }
          105  +  if( ds->s6!=INVALID_SOCKET ){
          106  +    closesocket(ds->s6);
          107  +    ds->s6 = INVALID_SOCKET;
          108  +  }
          109  +};
          110  +
          111  +/*
          112  +** When ip is "W", listen to wildcard address (IPv4/IPv6 as available).
          113  +** When ip is "L", listen to loopback address (IPv4/IPv6 as available).
          114  +** Else listen only the specified ip, which is either IPv4 or IPv6 or invalid.
          115  +** Returns 1 on success, 0 on failure.
          116  +*/
          117  +static int DualSocket_listen(DualSocket* ds, const char* zIp, int iPort){
          118  +  SOCKADDR_IN addr4;
          119  +  SOCKADDR_IN6 addr6;
          120  +  assert( ds!=NULL && zIp!=NULL && iPort!=0 );
          121  +  DualSocket_close(ds);
          122  +  memset(&addr4, 0, sizeof(addr4));
          123  +  memset(&addr6, 0, sizeof(addr6));
          124  +  if (strcmp(zIp, "W")==0 || strcmp(zIp, "L")==0 ){
          125  +    ds->s4 = socket(AF_INET, SOCK_STREAM, 0);
          126  +    ds->s6 = socket(AF_INET6, SOCK_STREAM, 0);
          127  +    if( ds->s4==INVALID_SOCKET && ds->s6==INVALID_SOCKET ){
          128  +      return 0;
          129  +    }
          130  +    if (ds->s4!=INVALID_SOCKET ) {
          131  +      addr4.sin_family = AF_INET;
          132  +      addr4.sin_port = htons(iPort);
          133  +      if( strcmp(zIp, "L")==0 ){
          134  +        addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
          135  +      }else{
          136  +        addr4.sin_addr.s_addr = INADDR_ANY;
          137  +      }
          138  +    }
          139  +    if( ds->s6!=INVALID_SOCKET ) {
          140  +      DWORD ipv6only = 1; /* don't want a dual-stack socket */
          141  +      setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only,
          142  +                 sizeof(ipv6only));
          143  +      addr6.sin6_family = AF_INET6;
          144  +      addr6.sin6_port = htons(iPort);
          145  +      if( strcmp(zIp, "L")==0 ){
          146  +        memcpy(&addr6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback));
          147  +      }else{
          148  +        memcpy(&addr6.sin6_addr, &in6addr_any, sizeof(in6addr_any));
          149  +      }
          150  +    }
          151  +  }else{
          152  +    if( strstr(zIp, ".") ){
          153  +      int addrlen = sizeof(addr4);
          154  +      ds->s4 = socket(AF_INET, SOCK_STREAM, 0);
          155  +      if( ds->s4==INVALID_SOCKET ){
          156  +        return 0;
          157  +      }
          158  +      addr4.sin_family = AF_INET;
          159  +      if (WSAStringToAddress((char*)zIp, AF_INET, NULL,
          160  +                             (struct sockaddr *)&addr4, &addrlen) != 0){
          161  +        return 0;
          162  +      }
          163  +      addr4.sin_port = htons(iPort);
          164  +    }else{
          165  +      DWORD ipv6only = 1; /* don't want a dual-stack socket */
          166  +      int addrlen = sizeof(addr6);
          167  +      ds->s6 = socket(AF_INET6, SOCK_STREAM, 0);
          168  +      if( ds->s6==INVALID_SOCKET ){
          169  +        return 0;
          170  +      }
          171  +      setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only,
          172  +                 sizeof(ipv6only));
          173  +      addr6.sin6_family = AF_INET6;
          174  +      if (WSAStringToAddress((char*)zIp, AF_INET6, NULL,
          175  +                             (struct sockaddr *)&addr6, &addrlen) != 0){
          176  +        return 0;
          177  +      }
          178  +      addr6.sin6_port = htons(iPort);
          179  +    }
          180  +  }
          181  +  assert( ds->s4!=INVALID_SOCKET || ds->s6!=INVALID_SOCKET );
          182  +  if( ds->s4!=INVALID_SOCKET && bind(ds->s4, (struct sockaddr*)&addr4,
          183  +                                 sizeof(addr4))==SOCKET_ERROR ){
          184  +    return 0;
          185  +  }
          186  +  if( ds->s6!=INVALID_SOCKET && bind(ds->s6, (struct sockaddr*)&addr6,
          187  +                                 sizeof(addr6))==SOCKET_ERROR ){
          188  +    return 0;
          189  +  }
          190  +  if( ds->s4!=INVALID_SOCKET && listen(ds->s4, SOMAXCONN)==SOCKET_ERROR ){
          191  +    return 0;
          192  +  }
          193  +  if( ds->s6!=INVALID_SOCKET && listen(ds->s6, SOMAXCONN)==SOCKET_ERROR ){
          194  +    return 0;
          195  +  }
          196  +  return 1;
          197  +};
          198  +
          199  +/*
          200  +** Accepts connections on DualSocket.
          201  +*/
          202  +static void DualSocket_accept(DualSocket* pListen, DualSocket* pClient,
          203  +                              DualAddr* pClientAddr){
          204  +	fd_set rs;
          205  +  int rs_count = 0;
          206  +  assert( pListen!=NULL && pClient!=NULL && pClientAddr!= NULL );
          207  +  DualSocket_init(pClient);
          208  +  DualAddr_init(pClientAddr);
          209  +  FD_ZERO(&rs);
          210  +	if( pListen->s4!=INVALID_SOCKET ){
          211  +    FD_SET(pListen->s4, &rs);
          212  +    ++rs_count;
          213  +  }
          214  +	if( pListen->s6!=INVALID_SOCKET ){
          215  +    FD_SET(pListen->s6, &rs);
          216  +    ++rs_count;
          217  +  }
          218  +	if( select(rs_count, &rs, 0, 0, 0 /*blocking*/)==SOCKET_ERROR ){
          219  +		return;
          220  +  }
          221  +  if( FD_ISSET(pListen->s4, &rs) ){
          222  +    pClient->s4 = accept(pListen->s4, (struct sockaddr*)&pClientAddr->a4.addr,
          223  +                         &pClientAddr->a4.len);
          224  +  }
          225  +  if( FD_ISSET(pListen->s6, &rs) ){
          226  +    pClient->s6 = accept(pListen->s6, (struct sockaddr*)&pClientAddr->a6.addr,
          227  +                         &pClientAddr->a6.len);
          228  +  }
          229  +}
    33    230   
    34    231   /*
    35    232   ** The HttpServer structure holds information about an instance of
    36    233   ** the HTTP server itself.
    37    234   */
    38    235   typedef struct HttpServer HttpServer;
    39    236   struct HttpServer {
    40    237     HANDLE hStoppedEvent; /* Event to signal when server is stopped,
    41    238                           ** must be closed by callee. */
    42    239     char *zStopper;       /* The stopper file name, must be freed by
    43    240                           ** callee. */
    44         -  SOCKET listener;      /* Socket on which the server is listening,
          241  +  DualSocket listener;  /* Sockets on which the server is listening,
    45    242                           ** may be closed by callee. */
    46    243   };
    47    244   
    48    245   /*
    49    246   ** The HttpRequest structure holds information about each incoming
    50    247   ** HTTP request.
    51    248   */
    52    249   typedef struct HttpRequest HttpRequest;
    53    250   struct HttpRequest {
    54    251     int id;                /* ID counter */
    55    252     SOCKET s;              /* Socket on which to receive data */
    56         -  SOCKADDR_IN6 addr;     /* Address from which data is coming */
          253  +  SocketAddr addr;       /* Address from which data is coming */
    57    254     int flags;             /* Flags passed to win32_http_server() */
    58    255     const char *zOptions;  /* --baseurl, --notfound, --localauth, --th-trace */
    59    256   };
    60    257   
    61    258   /*
    62    259   ** Prefix for a temporary file.
    63    260   */
................................................................................
    97    294   ** is found.  If there is no stopper file name, do nothing.
    98    295   */
    99    296   static void win32_server_stopper(void *pAppData){
   100    297     HttpServer *p = (HttpServer*)pAppData;
   101    298     if( p!=0 ){
   102    299       HANDLE hStoppedEvent = p->hStoppedEvent;
   103    300       const char *zStopper = p->zStopper;
   104         -    SOCKET listener = p->listener;
   105         -    if( hStoppedEvent!=NULL && zStopper!=0 && listener!=INVALID_SOCKET ){
          301  +    if( hStoppedEvent!=NULL && zStopper!=0 ){
   106    302         while( 1 ){
   107    303           DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
   108    304                                                     1000, TRUE);
   109    305           if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
   110    306             /* The event is either invalid, signaled, or abandoned.  Bail
   111    307             ** out now because those conditions should indicate the parent
   112    308             ** thread is dead or dying. */
   113    309             break;
   114    310           }
   115    311           if( file_size(zStopper, ExtFILE)>=0 ){
   116    312             /* The stopper file has been found.  Attempt to close the server
   117    313             ** listener socket now and then exit. */
   118         -          closesocket(listener);
   119         -          p->listener = INVALID_SOCKET;
          314  +          DualSocket_close(&p->listener);
   120    315             break;
   121    316           }
   122    317         }
   123    318       }
   124    319       if( hStoppedEvent!=NULL ){
   125    320         CloseHandle(hStoppedEvent);
   126    321         p->hStoppedEvent = NULL;
................................................................................
   138    333   */
   139    334   static void win32_http_request(void *pAppData){
   140    335     HttpRequest *p = (HttpRequest*)pAppData;
   141    336     FILE *in = 0, *out = 0, *aux = 0;
   142    337     int amt, got, i;
   143    338     int wanted = 0;
   144    339     char *z;
   145         -  char zIp[50];
   146         -  DWORD nIp = sizeof(zIp);
          340  +  char *zIp;
   147    341     char zCmdFName[MAX_PATH];
   148    342     char zRequestFName[MAX_PATH];
   149    343     char zReplyFName[MAX_PATH];
   150    344     char zCmd[2000];          /* Command-line to process the request */
   151    345     char zHdr[4000];          /* The HTTP request header */
   152    346   
   153    347     sqlite3_snprintf(MAX_PATH, zCmdFName,
................................................................................
   188    382     }
   189    383   
   190    384     /*
   191    385     ** The repository name is only needed if there was no open checkout.  This
   192    386     ** is designed to allow the open checkout for the interactive user to work
   193    387     ** with the local Fossil server started via the "ui" command.
   194    388     */
   195         -  p->addr.sin6_port = 0;
   196         -  if( WSAAddressToStringA((SOCKADDR*)&p->addr, sizeof(p->addr),
   197         -                          NULL, zIp, &nIp)!=0 ){
   198         -    zIp[0] = 0;
   199         -  }
          389  +  zIp = SocketAddr_toString(&p->addr);
   200    390     if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
   201    391       assert( g.zRepositoryName && g.zRepositoryName[0] );
   202    392       sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s\n%s",
   203    393         get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName
   204    394       );
   205    395     }else{
   206    396       sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s",
   207    397         get_utf8_bom(0), zRequestFName, zReplyFName, zIp
   208    398       );
   209    399     }
          400  +  fossil_free(zIp);
   210    401     aux = fossil_fopen(zCmdFName, "wb");
   211    402     if( aux==0 ) goto end_request;
   212    403     fwrite(zCmd, 1, strlen(zCmd), aux);
   213    404   
   214    405     sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s",
   215    406       g.nameOfExe, zCmdFName, p->zOptions
   216    407     );
................................................................................
   244    435   ** Process a single incoming SCGI request.
   245    436   */
   246    437   static void win32_scgi_request(void *pAppData){
   247    438     HttpRequest *p = (HttpRequest*)pAppData;
   248    439     FILE *in = 0, *out = 0;
   249    440     int amt, got, nHdr, i;
   250    441     int wanted = 0;
   251         -  char zIp[50];
   252         -  DWORD nIp = sizeof(zIp);
          442  +  char *zIp;
   253    443     char zRequestFName[MAX_PATH];
   254    444     char zReplyFName[MAX_PATH];
   255    445     char zCmd[2000];          /* Command-line to process the request */
   256    446     char zHdr[4000];          /* The SCGI request header */
   257    447   
   258    448     sqlite3_snprintf(MAX_PATH, zRequestFName,
   259    449                      "%s_%06d_in.txt", zTempPrefix, p->id);
................................................................................
   276    466     while( wanted>amt ){
   277    467       got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0);
   278    468       if( got<=0 ) break;
   279    469       fwrite(zHdr, 1, got, out);
   280    470       wanted += got;
   281    471     }
   282    472     assert( g.zRepositoryName && g.zRepositoryName[0] );
   283         -  p->addr.sin6_port = 0;
   284         -  if (WSAAddressToStringA((SOCKADDR*)&p->addr, sizeof(p->addr),
   285         -                          NULL, zIp, &nIp)!=0){
   286         -    zIp[0] = 0;
   287         -  }
          473  +  zIp = SocketAddr_toString(&p->addr);
   288    474     sqlite3_snprintf(sizeof(zCmd), zCmd,
   289    475       "\"%s\" http \"%s\" \"%s\" %s \"%s\" --scgi --nossl%s",
   290    476       g.nameOfExe, zRequestFName, zReplyFName, zIp,
   291    477       g.zRepositoryName, p->zOptions
   292    478     );
          479  +  fossil_free(zIp);
   293    480     in = fossil_fopen(zReplyFName, "w+b");
   294    481     fflush(out);
   295    482     fossil_system(zCmd);
   296    483     if( in ){
   297    484       while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
   298    485         send(p->s, zHdr, got, 0);
   299    486       }
................................................................................
   309    496     ** software keeps the files open for a few seconds, preventing the file
   310    497     ** from being deleted on the first try. */
   311    498     for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
   312    499     for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
   313    500     fossil_free(p);
   314    501   }
   315    502   
          503  +/* forward reference */
          504  +static void win32_http_service_running(DualSocket* pS);
   316    505   
   317    506   /*
   318    507   ** Start a listening socket and process incoming HTTP requests on
   319    508   ** that socket.
   320    509   */
   321    510   void win32_http_server(
   322    511     int mnPort, int mxPort,   /* Range of allowed TCP port numbers */
................................................................................
   326    515     const char *zNotFound,    /* The --notfound option, or NULL */
   327    516     const char *zFileGlob,    /* The --fileglob option, or NULL */
   328    517     const char *zIpAddr,      /* Bind to this IP address, if not NULL */
   329    518     int flags                 /* One or more HTTP_SERVER_ flags */
   330    519   ){
   331    520     HANDLE hStoppedEvent;
   332    521     WSADATA wd;
   333         -  SOCKET s = INVALID_SOCKET;
   334         -  SOCKADDR_IN6 addr;
   335         -  int addrlen;
          522  +  DualSocket ds;
   336    523     int idCnt = 0;
   337    524     int iPort = mnPort;
   338    525     Blob options;
   339    526     wchar_t zTmpPath[MAX_PATH];
   340    527     const char *zSkin;
   341    528   #if USE_SEE
   342    529     const char *zSavedKey = 0;
................................................................................
   373    560       blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
   374    561                    zSavedKey, savedKeySize);
   375    562     }
   376    563   #endif
   377    564     if( WSAStartup(MAKEWORD(2,0), &wd) ){
   378    565       fossil_fatal("unable to initialize winsock");
   379    566     }
   380         -  if( flags & HTTP_SERVER_LOCALHOST ){
   381         -    zIpAddr = "::1";
   382         -  }
          567  +  DualSocket_init(&ds);
   383    568     while( iPort<=mxPort ){
   384         -    DWORD ipv6only = 0;
   385         -    s = socket(AF_INET6, SOCK_STREAM, 0);
   386         -    if( s==INVALID_SOCKET ){
   387         -      fossil_fatal("unable to create a socket");
   388         -    }
   389         -    setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only,
   390         -               sizeof(ipv6only));
   391         -    memset(&addr, 0, sizeof(addr));
   392         -    addrlen = sizeof(addr);
   393    569       if( zIpAddr ){
   394         -      char* zIp;
   395         -      if( strstr(zIpAddr, ".") ){
   396         -        zIp = mprintf("::ffff:%s", zIpAddr);
   397         -      }else{
   398         -        zIp = mprintf("%s", zIpAddr);
          570  +      if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){
          571  +        iPort++;
          572  +        continue;
   399    573         }
   400         -      addr.sin6_family = AF_INET6;
   401         -      if (WSAStringToAddress(zIp, AF_INET6, NULL,
   402         -                             (struct sockaddr *)&addr, &addrlen) != 0){
   403         -        fossil_fatal("not a valid IP address: %s", zIpAddr);
   404         -      }
   405         -      ((SOCKADDR_IN6*)&addr)->sin6_port = htons(iPort);
   406         -      fossil_free(zIp);
   407    574       }else{
   408         -      addr.sin6_family = AF_INET6;
   409         -      addr.sin6_port = htons(iPort);
   410         -      memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
   411         -    }
   412         -    if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
   413         -      closesocket(s);
   414         -      iPort++;
   415         -      continue;
   416         -    }
   417         -    if( listen(s, SOMAXCONN)==SOCKET_ERROR ){
   418         -      closesocket(s);
   419         -      iPort++;
   420         -      continue;
          575  +      if( DualSocket_listen(&ds,
          576  +                            (flags & HTTP_SERVER_LOCALHOST) ? "L" : "W",
          577  +                            iPort
          578  +                           )==0 ){
          579  +        iPort++;
          580  +        continue;
          581  +      }
   421    582       }
   422    583       break;
   423    584     }
   424    585     if( iPort>mxPort ){
   425    586       if( mnPort==mxPort ){
   426         -      fossil_fatal("unable to open listening socket on ports %d", mnPort);
          587  +      fossil_fatal("unable to open listening socket on port %d", mnPort);
   427    588       }else{
   428    589         fossil_fatal("unable to open listening socket on any"
   429    590                      " port in the range %d..%d", mnPort, mxPort);
   430    591       }
   431    592     }
   432    593     if( !GetTempPathW(MAX_PATH, zTmpPath) ){
   433    594       fossil_fatal("unable to get path to the temporary directory.");
................................................................................
   453    614       HttpServer *pServer = fossil_malloc(sizeof(HttpServer));
   454    615       memset(pServer, 0, sizeof(HttpServer));
   455    616       DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
   456    617                       GetCurrentProcess(), &pServer->hStoppedEvent,
   457    618                       0, FALSE, DUPLICATE_SAME_ACCESS);
   458    619       assert( pServer->hStoppedEvent!=NULL );
   459    620       pServer->zStopper = fossil_strdup(zStopper);
   460         -    pServer->listener = s;
          621  +    pServer->listener = ds;
   461    622       file_delete(zStopper);
   462    623       _beginthread(win32_server_stopper, 0, (void*)pServer);
   463    624     }
   464    625     /* Set the service status to running and pass the listener socket to the
   465    626     ** service handling procedures. */
   466         -  win32_http_service_running(s);
          627  +  win32_http_service_running(&ds);
   467    628     for(;;){
   468         -    SOCKET client;
   469         -    SOCKADDR_IN6 client_addr;
          629  +    DualSocket client;
          630  +    DualAddr client_addr;
   470    631       HttpRequest *pRequest;
   471         -    int len = sizeof(client_addr);
   472    632       int wsaError;
   473    633   
   474         -    client = accept(s, (struct sockaddr*)&client_addr, &len);
   475         -    if( client==INVALID_SOCKET ){
          634  +    DualSocket_accept(&ds, &client, &client_addr);
          635  +    if( client.s4==INVALID_SOCKET && client.s6==INVALID_SOCKET ){
   476    636         /* If the service control handler has closed the listener socket,
   477    637         ** cleanup and return, otherwise report a fatal error. */
   478    638         wsaError =  WSAGetLastError();
          639  +      DualSocket_close(&ds);
   479    640         if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
   480    641           WSACleanup();
   481    642           return;
   482    643         }else{
   483         -        closesocket(s);
   484    644           WSACleanup();
   485    645           fossil_fatal("error from accept()");
   486    646         }
   487    647       }
   488         -    pRequest = fossil_malloc(sizeof(HttpRequest));
   489         -    pRequest->id = ++idCnt;
   490         -    pRequest->s = client;
   491         -    pRequest->addr = client_addr;
   492         -    pRequest->flags = flags;
   493         -    pRequest->zOptions = blob_str(&options);
   494         -    if( flags & HTTP_SERVER_SCGI ){
   495         -      _beginthread(win32_scgi_request, 0, (void*)pRequest);
   496         -    }else{
   497         -      _beginthread(win32_http_request, 0, (void*)pRequest);
          648  +    if( client.s4!=INVALID_SOCKET ){
          649  +      pRequest = fossil_malloc(sizeof(HttpRequest));
          650  +      pRequest->id = ++idCnt;
          651  +      pRequest->s = client.s4;
          652  +      memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4));
          653  +      pRequest->flags = flags;
          654  +      pRequest->zOptions = blob_str(&options);
          655  +      if( flags & HTTP_SERVER_SCGI ){
          656  +        _beginthread(win32_scgi_request, 0, (void*)pRequest);
          657  +      }else{
          658  +        _beginthread(win32_http_request, 0, (void*)pRequest);
          659  +      }
          660  +    }
          661  +    if( client.s6!=INVALID_SOCKET ){
          662  +      pRequest = fossil_malloc(sizeof(HttpRequest));
          663  +      pRequest->id = ++idCnt;
          664  +      pRequest->s = client.s6;
          665  +      memcpy(&pRequest->addr, &client_addr.a6, sizeof(client_addr.a6));
          666  +      pRequest->flags = flags;
          667  +      pRequest->zOptions = blob_str(&options);
          668  +      if( flags & HTTP_SERVER_SCGI ){
          669  +        _beginthread(win32_scgi_request, 0, (void*)pRequest);
          670  +      }else{
          671  +        _beginthread(win32_http_request, 0, (void*)pRequest);
          672  +      }
   498    673       }
   499    674     }
   500         -  closesocket(s);
          675  +  DualSocket_close(&ds);
   501    676     WSACleanup();
   502    677     SetEvent(hStoppedEvent);
   503    678     CloseHandle(hStoppedEvent);
   504    679   }
   505    680   
   506    681   /*
   507    682   ** The HttpService structure is used to pass information to the service main
................................................................................
   512    687     int port;                 /* Port on which the http server should run */
   513    688     const char *zBaseUrl;     /* The --baseurl option, or NULL */
   514    689     const char *zNotFound;    /* The --notfound option, or NULL */
   515    690     const char *zFileGlob;    /* The --files option, or NULL */
   516    691     int flags;                /* One or more HTTP_SERVER_ flags */
   517    692     int isRunningAsService;   /* Are we running as a service ? */
   518    693     const wchar_t *zServiceName;/* Name of the service */
   519         -  SOCKET s;                 /* Socket on which the http server listens */
          694  +  DualSocket s;             /* Sockets on which the http server listens */
   520    695   };
   521    696   
   522    697   /*
   523    698   ** Variables used for running as windows service.
   524    699   */
   525         -static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL, INVALID_SOCKET};
          700  +static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL,
          701  +                             {INVALID_SOCKET, INVALID_SOCKET}};
   526    702   static SERVICE_STATUS ssStatus;
   527    703   static SERVICE_STATUS_HANDLE sshStatusHandle;
   528    704   
   529    705   /*
   530    706   ** Get message string of the last system error. Return a pointer to the
   531    707   ** message string. Call fossil_unicode_free() to deallocate any memory used
   532    708   ** to store the message string when done.
................................................................................
   609    785   ** control manager.
   610    786   */
   611    787   static void WINAPI win32_http_service_ctrl(
   612    788     DWORD dwCtrlCode
   613    789   ){
   614    790     switch( dwCtrlCode ){
   615    791       case SERVICE_CONTROL_STOP: {
          792  +      DualSocket_close(&hsData.s);
   616    793         win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
   617         -      if( hsData.s != INVALID_SOCKET ){
   618         -        closesocket(hsData.s);
   619         -      }
   620         -      win32_report_service_status(ssStatus.dwCurrentState, NO_ERROR, 0);
   621    794         break;
   622    795       }
   623    796       default: {
   624    797         break;
   625    798       }
   626    799     }
   627    800     return;
................................................................................
   672    845   }
   673    846   
   674    847   /*
   675    848   ** When running as service, update the HttpService structure with the
   676    849   ** listener socket and update the service status. This procedure must be
   677    850   ** called from the http server when he is ready to accept connections.
   678    851   */
   679         -LOCAL void win32_http_service_running(SOCKET s){
          852  +static void win32_http_service_running(DualSocket *pS){
   680    853     if( hsData.isRunningAsService ){
   681         -    hsData.s = s;
          854  +    hsData.s = *pS;
   682    855       win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
   683    856     }
   684    857   }
   685    858   
   686    859   /*
   687    860   ** Try to start the http server as a windows service. If we are running in
   688    861   ** a interactive console session, this routine fails and returns a non zero
................................................................................
   702    875   
   703    876     /* Initialize the HttpService structure. */
   704    877     hsData.port = nPort;
   705    878     hsData.zBaseUrl = zBaseUrl;
   706    879     hsData.zNotFound = zNotFound;
   707    880     hsData.zFileGlob = zFileGlob;
   708    881     hsData.flags = flags;
          882  +
          883  +  if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; }
   709    884   
   710    885     /* Try to start the control dispatcher thread for the service. */
   711    886     if( !StartServiceCtrlDispatcherW(ServiceTable) ){
   712    887       if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
   713    888         return 1;
   714    889       }else{
   715    890         fossil_fatal("error from StartServiceCtrlDispatcher()");
................................................................................
   961   1136       QueryServiceStatus(hSvc, &sstat);
   962   1137       if( sstat.dwCurrentState!=SERVICE_STOPPED ){
   963   1138         fossil_print("Stopping service '%s'", zSvcName);
   964   1139         if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
   965   1140           if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
   966   1141             winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
   967   1142           }
         1143  +        QueryServiceStatus(hSvc, &sstat);
   968   1144         }
   969         -      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
         1145  +      while( sstat.dwCurrentState==SERVICE_STOP_PENDING  ||
         1146  +             sstat.dwCurrentState==SERVICE_RUNNING ){
   970   1147           Sleep(100);
   971   1148           fossil_print(".");
   972   1149           QueryServiceStatus(hSvc, &sstat);
   973   1150         }
   974         -      fossil_print("\nService '%s' stopped.\n", zSvcName);
         1151  +      if( sstat.dwCurrentState==SERVICE_STOPPED ){
         1152  +        fossil_print("\nService '%s' stopped.\n", zSvcName);
         1153  +      }else{
         1154  +        winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
         1155  +      }
   975   1156       }
   976   1157       if( !DeleteService(hSvc) ){
   977   1158         if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
   978   1159           fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
   979   1160         }else{
   980   1161           winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
   981   1162         }
................................................................................
  1106   1287       if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
  1107   1288       hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
  1108   1289                           SERVICE_ALL_ACCESS);
  1109   1290       if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
  1110   1291       QueryServiceStatus(hSvc, &sstat);
  1111   1292       if( sstat.dwCurrentState!=SERVICE_RUNNING ){
  1112   1293         fossil_print("Starting service '%s'", zSvcName);
  1113         -      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
  1114         -        if( !StartServiceW(hSvc, 0, NULL) ){
  1115         -          winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
  1116         -        }
  1117         -      }
  1118         -      while( sstat.dwCurrentState!=SERVICE_RUNNING ){
         1294  +      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){ 
         1295  +        if( !StartServiceW(hSvc, 0, NULL) ){ 
         1296  +          winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); 
         1297  +        } 
         1298  +        QueryServiceStatus(hSvc, &sstat); 
         1299  +      } 
         1300  +      while( sstat.dwCurrentState==SERVICE_START_PENDING ||
         1301  +             sstat.dwCurrentState==SERVICE_STOPPED ){
  1119   1302           Sleep(100);
  1120   1303           fossil_print(".");
  1121   1304           QueryServiceStatus(hSvc, &sstat);
  1122   1305         }
  1123         -      fossil_print("\nService '%s' started.\n", zSvcName);
         1306  +      if( sstat.dwCurrentState==SERVICE_RUNNING ){
         1307  +        fossil_print("\nService '%s' started.\n", zSvcName);
         1308  +      }else{
         1309  +        winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
         1310  +      }
  1124   1311       }else{
  1125   1312         fossil_print("Service '%s' is already started.\n", zSvcName);
  1126   1313       }
  1127   1314       CloseServiceHandle(hSvc);
  1128   1315       CloseServiceHandle(hScm);
  1129   1316     }else
  1130   1317     if( strncmp(zMethod, "stop", n)==0 ){
................................................................................
  1146   1333       QueryServiceStatus(hSvc, &sstat);
  1147   1334       if( sstat.dwCurrentState!=SERVICE_STOPPED ){
  1148   1335         fossil_print("Stopping service '%s'", zSvcName);
  1149   1336         if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
  1150   1337           if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
  1151   1338             winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
  1152   1339           }
         1340  +        QueryServiceStatus(hSvc, &sstat);
  1153   1341         }
  1154         -      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
         1342  +      while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
         1343  +             sstat.dwCurrentState==SERVICE_RUNNING ){
  1155   1344           Sleep(100);
  1156   1345           fossil_print(".");
  1157   1346           QueryServiceStatus(hSvc, &sstat);
  1158   1347         }
  1159         -      fossil_print("\nService '%s' stopped.\n", zSvcName);
         1348  +      if( sstat.dwCurrentState==SERVICE_STOPPED ){
         1349  +        fossil_print("\nService '%s' stopped.\n", zSvcName);
         1350  +      }else{
         1351  +        winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
         1352  +      }
  1160   1353       }else{
  1161   1354         fossil_print("Service '%s' is already stopped.\n", zSvcName);
  1162   1355       }
  1163   1356       CloseServiceHandle(hSvc);
  1164   1357       CloseServiceHandle(hScm);
  1165   1358     }else
  1166   1359     {