Fossil SCM

Improved support for both IPv4 and IPv6 on "fossil server" on Windows. Patches from Olivier Mascia.

drh 2018-01-05 14:34 trunk
Commit e506ebb764e93715e9514d6c0078d3eb82580bee78f18180fa18a21b3d7edd03
1 file changed +294 -101
+294 -101
--- src/winhttp.c
+++ src/winhttp.c
@@ -28,10 +28,207 @@
2828
#include "winhttp.h"
2929
3030
#ifndef IPV6_V6ONLY
3131
# define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */
3232
#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
+}
33230
34231
/*
35232
** The HttpServer structure holds information about an instance of
36233
** the HTTP server itself.
37234
*/
@@ -39,11 +236,11 @@
39236
struct HttpServer {
40237
HANDLE hStoppedEvent; /* Event to signal when server is stopped,
41238
** must be closed by callee. */
42239
char *zStopper; /* The stopper file name, must be freed by
43240
** callee. */
44
- SOCKET listener; /* Socket on which the server is listening,
241
+ DualSocket listener; /* Sockets on which the server is listening,
45242
** may be closed by callee. */
46243
};
47244
48245
/*
49246
** The HttpRequest structure holds information about each incoming
@@ -51,11 +248,11 @@
51248
*/
52249
typedef struct HttpRequest HttpRequest;
53250
struct HttpRequest {
54251
int id; /* ID counter */
55252
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 */
57254
int flags; /* Flags passed to win32_http_server() */
58255
const char *zOptions; /* --baseurl, --notfound, --localauth, --th-trace */
59256
};
60257
61258
/*
@@ -99,12 +296,11 @@
99296
static void win32_server_stopper(void *pAppData){
100297
HttpServer *p = (HttpServer*)pAppData;
101298
if( p!=0 ){
102299
HANDLE hStoppedEvent = p->hStoppedEvent;
103300
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 ){
106302
while( 1 ){
107303
DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
108304
1000, TRUE);
109305
if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
110306
/* The event is either invalid, signaled, or abandoned. Bail
@@ -113,12 +309,11 @@
113309
break;
114310
}
115311
if( file_size(zStopper, ExtFILE)>=0 ){
116312
/* The stopper file has been found. Attempt to close the server
117313
** listener socket now and then exit. */
118
- closesocket(listener);
119
- p->listener = INVALID_SOCKET;
314
+ DualSocket_close(&p->listener);
120315
break;
121316
}
122317
}
123318
}
124319
if( hStoppedEvent!=NULL ){
@@ -140,12 +335,11 @@
140335
HttpRequest *p = (HttpRequest*)pAppData;
141336
FILE *in = 0, *out = 0, *aux = 0;
142337
int amt, got, i;
143338
int wanted = 0;
144339
char *z;
145
- char zIp[50];
146
- DWORD nIp = sizeof(zIp);
340
+ char *zIp;
147341
char zCmdFName[MAX_PATH];
148342
char zRequestFName[MAX_PATH];
149343
char zReplyFName[MAX_PATH];
150344
char zCmd[2000]; /* Command-line to process the request */
151345
char zHdr[4000]; /* The HTTP request header */
@@ -190,15 +384,11 @@
190384
/*
191385
** The repository name is only needed if there was no open checkout. This
192386
** is designed to allow the open checkout for the interactive user to work
193387
** with the local Fossil server started via the "ui" command.
194388
*/
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);
200390
if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
201391
assert( g.zRepositoryName && g.zRepositoryName[0] );
202392
sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s\n%s",
203393
get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName
204394
);
@@ -205,10 +395,11 @@
205395
}else{
206396
sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s",
207397
get_utf8_bom(0), zRequestFName, zReplyFName, zIp
208398
);
209399
}
400
+ fossil_free(zIp);
210401
aux = fossil_fopen(zCmdFName, "wb");
211402
if( aux==0 ) goto end_request;
212403
fwrite(zCmd, 1, strlen(zCmd), aux);
213404
214405
sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s",
@@ -246,12 +437,11 @@
246437
static void win32_scgi_request(void *pAppData){
247438
HttpRequest *p = (HttpRequest*)pAppData;
248439
FILE *in = 0, *out = 0;
249440
int amt, got, nHdr, i;
250441
int wanted = 0;
251
- char zIp[50];
252
- DWORD nIp = sizeof(zIp);
442
+ char *zIp;
253443
char zRequestFName[MAX_PATH];
254444
char zReplyFName[MAX_PATH];
255445
char zCmd[2000]; /* Command-line to process the request */
256446
char zHdr[4000]; /* The SCGI request header */
257447
@@ -278,20 +468,17 @@
278468
if( got<=0 ) break;
279469
fwrite(zHdr, 1, got, out);
280470
wanted += got;
281471
}
282472
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);
288474
sqlite3_snprintf(sizeof(zCmd), zCmd,
289475
"\"%s\" http \"%s\" \"%s\" %s \"%s\" --scgi --nossl%s",
290476
g.nameOfExe, zRequestFName, zReplyFName, zIp,
291477
g.zRepositoryName, p->zOptions
292478
);
479
+ fossil_free(zIp);
293480
in = fossil_fopen(zReplyFName, "w+b");
294481
fflush(out);
295482
fossil_system(zCmd);
296483
if( in ){
297484
while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
@@ -311,10 +498,12 @@
311498
for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
312499
for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
313500
fossil_free(p);
314501
}
315502
503
+/* forward reference */
504
+static void win32_http_service_running(DualSocket* pS);
316505
317506
/*
318507
** Start a listening socket and process incoming HTTP requests on
319508
** that socket.
320509
*/
@@ -328,13 +517,11 @@
328517
const char *zIpAddr, /* Bind to this IP address, if not NULL */
329518
int flags /* One or more HTTP_SERVER_ flags */
330519
){
331520
HANDLE hStoppedEvent;
332521
WSADATA wd;
333
- SOCKET s = INVALID_SOCKET;
334
- SOCKADDR_IN6 addr;
335
- int addrlen;
522
+ DualSocket ds;
336523
int idCnt = 0;
337524
int iPort = mnPort;
338525
Blob options;
339526
wchar_t zTmpPath[MAX_PATH];
340527
const char *zSkin;
@@ -375,57 +562,31 @@
375562
}
376563
#endif
377564
if( WSAStartup(MAKEWORD(2,0), &wd) ){
378565
fossil_fatal("unable to initialize winsock");
379566
}
380
- if( flags & HTTP_SERVER_LOCALHOST ){
381
- zIpAddr = "::1";
382
- }
567
+ DualSocket_init(&ds);
383568
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);
393569
if( zIpAddr ){
394
- char* zIp;
395
- if( strstr(zIpAddr, ".") ){
396
- zIp = mprintf("::ffff:%s", zIpAddr);
397
- }else{
398
- zIp = mprintf("%s", zIpAddr);
399
- }
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
- }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;
570
+ if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){
571
+ iPort++;
572
+ continue;
573
+ }
574
+ }else{
575
+ if( DualSocket_listen(&ds,
576
+ (flags & HTTP_SERVER_LOCALHOST) ? "L" : "W",
577
+ iPort
578
+ )==0 ){
579
+ iPort++;
580
+ continue;
581
+ }
421582
}
422583
break;
423584
}
424585
if( iPort>mxPort ){
425586
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);
427588
}else{
428589
fossil_fatal("unable to open listening socket on any"
429590
" port in the range %d..%d", mnPort, mxPort);
430591
}
431592
}
@@ -455,51 +616,65 @@
455616
DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
456617
GetCurrentProcess(), &pServer->hStoppedEvent,
457618
0, FALSE, DUPLICATE_SAME_ACCESS);
458619
assert( pServer->hStoppedEvent!=NULL );
459620
pServer->zStopper = fossil_strdup(zStopper);
460
- pServer->listener = s;
621
+ pServer->listener = ds;
461622
file_delete(zStopper);
462623
_beginthread(win32_server_stopper, 0, (void*)pServer);
463624
}
464625
/* Set the service status to running and pass the listener socket to the
465626
** service handling procedures. */
466
- win32_http_service_running(s);
627
+ win32_http_service_running(&ds);
467628
for(;;){
468
- SOCKET client;
469
- SOCKADDR_IN6 client_addr;
629
+ DualSocket client;
630
+ DualAddr client_addr;
470631
HttpRequest *pRequest;
471
- int len = sizeof(client_addr);
472632
int wsaError;
473633
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 ){
476636
/* If the service control handler has closed the listener socket,
477637
** cleanup and return, otherwise report a fatal error. */
478638
wsaError = WSAGetLastError();
639
+ DualSocket_close(&ds);
479640
if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
480641
WSACleanup();
481642
return;
482643
}else{
483
- closesocket(s);
484644
WSACleanup();
485645
fossil_fatal("error from accept()");
486646
}
487647
}
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
+ }
498673
}
499674
}
500
- closesocket(s);
675
+ DualSocket_close(&ds);
501676
WSACleanup();
502677
SetEvent(hStoppedEvent);
503678
CloseHandle(hStoppedEvent);
504679
}
505680
@@ -514,17 +689,18 @@
514689
const char *zNotFound; /* The --notfound option, or NULL */
515690
const char *zFileGlob; /* The --files option, or NULL */
516691
int flags; /* One or more HTTP_SERVER_ flags */
517692
int isRunningAsService; /* Are we running as a service ? */
518693
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 */
520695
};
521696
522697
/*
523698
** Variables used for running as windows service.
524699
*/
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}};
526702
static SERVICE_STATUS ssStatus;
527703
static SERVICE_STATUS_HANDLE sshStatusHandle;
528704
529705
/*
530706
** Get message string of the last system error. Return a pointer to the
@@ -611,15 +787,12 @@
611787
static void WINAPI win32_http_service_ctrl(
612788
DWORD dwCtrlCode
613789
){
614790
switch( dwCtrlCode ){
615791
case SERVICE_CONTROL_STOP: {
792
+ DualSocket_close(&hsData.s);
616793
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);
621794
break;
622795
}
623796
default: {
624797
break;
625798
}
@@ -674,13 +847,13 @@
674847
/*
675848
** When running as service, update the HttpService structure with the
676849
** listener socket and update the service status. This procedure must be
677850
** called from the http server when he is ready to accept connections.
678851
*/
679
-LOCAL void win32_http_service_running(SOCKET s){
852
+static void win32_http_service_running(DualSocket *pS){
680853
if( hsData.isRunningAsService ){
681
- hsData.s = s;
854
+ hsData.s = *pS;
682855
win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
683856
}
684857
}
685858
686859
/*
@@ -704,10 +877,12 @@
704877
hsData.port = nPort;
705878
hsData.zBaseUrl = zBaseUrl;
706879
hsData.zNotFound = zNotFound;
707880
hsData.zFileGlob = zFileGlob;
708881
hsData.flags = flags;
882
+
883
+ if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; }
709884
710885
/* Try to start the control dispatcher thread for the service. */
711886
if( !StartServiceCtrlDispatcherW(ServiceTable) ){
712887
if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
713888
return 1;
@@ -963,17 +1138,23 @@
9631138
fossil_print("Stopping service '%s'", zSvcName);
9641139
if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
9651140
if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
9661141
winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
9671142
}
1143
+ QueryServiceStatus(hSvc, &sstat);
9681144
}
969
- while( sstat.dwCurrentState!=SERVICE_STOPPED ){
1145
+ while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1146
+ sstat.dwCurrentState==SERVICE_RUNNING ){
9701147
Sleep(100);
9711148
fossil_print(".");
9721149
QueryServiceStatus(hSvc, &sstat);
9731150
}
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
+ }
9751156
}
9761157
if( !DeleteService(hSvc) ){
9771158
if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
9781159
fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
9791160
}else{
@@ -1108,21 +1289,27 @@
11081289
SERVICE_ALL_ACCESS);
11091290
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
11101291
QueryServiceStatus(hSvc, &sstat);
11111292
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
11121293
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 ){
11191302
Sleep(100);
11201303
fossil_print(".");
11211304
QueryServiceStatus(hSvc, &sstat);
11221305
}
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
+ }
11241311
}else{
11251312
fossil_print("Service '%s' is already started.\n", zSvcName);
11261313
}
11271314
CloseServiceHandle(hSvc);
11281315
CloseServiceHandle(hScm);
@@ -1148,17 +1335,23 @@
11481335
fossil_print("Stopping service '%s'", zSvcName);
11491336
if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
11501337
if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
11511338
winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
11521339
}
1340
+ QueryServiceStatus(hSvc, &sstat);
11531341
}
1154
- while( sstat.dwCurrentState!=SERVICE_STOPPED ){
1342
+ while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1343
+ sstat.dwCurrentState==SERVICE_RUNNING ){
11551344
Sleep(100);
11561345
fossil_print(".");
11571346
QueryServiceStatus(hSvc, &sstat);
11581347
}
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
+ }
11601353
}else{
11611354
fossil_print("Service '%s' is already stopped.\n", zSvcName);
11621355
}
11631356
CloseServiceHandle(hSvc);
11641357
CloseServiceHandle(hScm);
11651358
--- src/winhttp.c
+++ src/winhttp.c
@@ -28,10 +28,207 @@
28 #include "winhttp.h"
29
30 #ifndef IPV6_V6ONLY
31 # define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */
32 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
34 /*
35 ** The HttpServer structure holds information about an instance of
36 ** the HTTP server itself.
37 */
@@ -39,11 +236,11 @@
39 struct HttpServer {
40 HANDLE hStoppedEvent; /* Event to signal when server is stopped,
41 ** must be closed by callee. */
42 char *zStopper; /* The stopper file name, must be freed by
43 ** callee. */
44 SOCKET listener; /* Socket on which the server is listening,
45 ** may be closed by callee. */
46 };
47
48 /*
49 ** The HttpRequest structure holds information about each incoming
@@ -51,11 +248,11 @@
51 */
52 typedef struct HttpRequest HttpRequest;
53 struct HttpRequest {
54 int id; /* ID counter */
55 SOCKET s; /* Socket on which to receive data */
56 SOCKADDR_IN6 addr; /* Address from which data is coming */
57 int flags; /* Flags passed to win32_http_server() */
58 const char *zOptions; /* --baseurl, --notfound, --localauth, --th-trace */
59 };
60
61 /*
@@ -99,12 +296,11 @@
99 static void win32_server_stopper(void *pAppData){
100 HttpServer *p = (HttpServer*)pAppData;
101 if( p!=0 ){
102 HANDLE hStoppedEvent = p->hStoppedEvent;
103 const char *zStopper = p->zStopper;
104 SOCKET listener = p->listener;
105 if( hStoppedEvent!=NULL && zStopper!=0 && listener!=INVALID_SOCKET ){
106 while( 1 ){
107 DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
108 1000, TRUE);
109 if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
110 /* The event is either invalid, signaled, or abandoned. Bail
@@ -113,12 +309,11 @@
113 break;
114 }
115 if( file_size(zStopper, ExtFILE)>=0 ){
116 /* The stopper file has been found. Attempt to close the server
117 ** listener socket now and then exit. */
118 closesocket(listener);
119 p->listener = INVALID_SOCKET;
120 break;
121 }
122 }
123 }
124 if( hStoppedEvent!=NULL ){
@@ -140,12 +335,11 @@
140 HttpRequest *p = (HttpRequest*)pAppData;
141 FILE *in = 0, *out = 0, *aux = 0;
142 int amt, got, i;
143 int wanted = 0;
144 char *z;
145 char zIp[50];
146 DWORD nIp = sizeof(zIp);
147 char zCmdFName[MAX_PATH];
148 char zRequestFName[MAX_PATH];
149 char zReplyFName[MAX_PATH];
150 char zCmd[2000]; /* Command-line to process the request */
151 char zHdr[4000]; /* The HTTP request header */
@@ -190,15 +384,11 @@
190 /*
191 ** The repository name is only needed if there was no open checkout. This
192 ** is designed to allow the open checkout for the interactive user to work
193 ** with the local Fossil server started via the "ui" command.
194 */
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 }
200 if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
201 assert( g.zRepositoryName && g.zRepositoryName[0] );
202 sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s\n%s",
203 get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName
204 );
@@ -205,10 +395,11 @@
205 }else{
206 sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s",
207 get_utf8_bom(0), zRequestFName, zReplyFName, zIp
208 );
209 }
 
210 aux = fossil_fopen(zCmdFName, "wb");
211 if( aux==0 ) goto end_request;
212 fwrite(zCmd, 1, strlen(zCmd), aux);
213
214 sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s",
@@ -246,12 +437,11 @@
246 static void win32_scgi_request(void *pAppData){
247 HttpRequest *p = (HttpRequest*)pAppData;
248 FILE *in = 0, *out = 0;
249 int amt, got, nHdr, i;
250 int wanted = 0;
251 char zIp[50];
252 DWORD nIp = sizeof(zIp);
253 char zRequestFName[MAX_PATH];
254 char zReplyFName[MAX_PATH];
255 char zCmd[2000]; /* Command-line to process the request */
256 char zHdr[4000]; /* The SCGI request header */
257
@@ -278,20 +468,17 @@
278 if( got<=0 ) break;
279 fwrite(zHdr, 1, got, out);
280 wanted += got;
281 }
282 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 }
288 sqlite3_snprintf(sizeof(zCmd), zCmd,
289 "\"%s\" http \"%s\" \"%s\" %s \"%s\" --scgi --nossl%s",
290 g.nameOfExe, zRequestFName, zReplyFName, zIp,
291 g.zRepositoryName, p->zOptions
292 );
 
293 in = fossil_fopen(zReplyFName, "w+b");
294 fflush(out);
295 fossil_system(zCmd);
296 if( in ){
297 while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
@@ -311,10 +498,12 @@
311 for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
312 for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
313 fossil_free(p);
314 }
315
 
 
316
317 /*
318 ** Start a listening socket and process incoming HTTP requests on
319 ** that socket.
320 */
@@ -328,13 +517,11 @@
328 const char *zIpAddr, /* Bind to this IP address, if not NULL */
329 int flags /* One or more HTTP_SERVER_ flags */
330 ){
331 HANDLE hStoppedEvent;
332 WSADATA wd;
333 SOCKET s = INVALID_SOCKET;
334 SOCKADDR_IN6 addr;
335 int addrlen;
336 int idCnt = 0;
337 int iPort = mnPort;
338 Blob options;
339 wchar_t zTmpPath[MAX_PATH];
340 const char *zSkin;
@@ -375,57 +562,31 @@
375 }
376 #endif
377 if( WSAStartup(MAKEWORD(2,0), &wd) ){
378 fossil_fatal("unable to initialize winsock");
379 }
380 if( flags & HTTP_SERVER_LOCALHOST ){
381 zIpAddr = "::1";
382 }
383 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 if( zIpAddr ){
394 char* zIp;
395 if( strstr(zIpAddr, ".") ){
396 zIp = mprintf("::ffff:%s", zIpAddr);
397 }else{
398 zIp = mprintf("%s", zIpAddr);
399 }
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 }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;
421 }
422 break;
423 }
424 if( iPort>mxPort ){
425 if( mnPort==mxPort ){
426 fossil_fatal("unable to open listening socket on ports %d", mnPort);
427 }else{
428 fossil_fatal("unable to open listening socket on any"
429 " port in the range %d..%d", mnPort, mxPort);
430 }
431 }
@@ -455,51 +616,65 @@
455 DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
456 GetCurrentProcess(), &pServer->hStoppedEvent,
457 0, FALSE, DUPLICATE_SAME_ACCESS);
458 assert( pServer->hStoppedEvent!=NULL );
459 pServer->zStopper = fossil_strdup(zStopper);
460 pServer->listener = s;
461 file_delete(zStopper);
462 _beginthread(win32_server_stopper, 0, (void*)pServer);
463 }
464 /* Set the service status to running and pass the listener socket to the
465 ** service handling procedures. */
466 win32_http_service_running(s);
467 for(;;){
468 SOCKET client;
469 SOCKADDR_IN6 client_addr;
470 HttpRequest *pRequest;
471 int len = sizeof(client_addr);
472 int wsaError;
473
474 client = accept(s, (struct sockaddr*)&client_addr, &len);
475 if( client==INVALID_SOCKET ){
476 /* If the service control handler has closed the listener socket,
477 ** cleanup and return, otherwise report a fatal error. */
478 wsaError = WSAGetLastError();
 
479 if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
480 WSACleanup();
481 return;
482 }else{
483 closesocket(s);
484 WSACleanup();
485 fossil_fatal("error from accept()");
486 }
487 }
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);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498 }
499 }
500 closesocket(s);
501 WSACleanup();
502 SetEvent(hStoppedEvent);
503 CloseHandle(hStoppedEvent);
504 }
505
@@ -514,17 +689,18 @@
514 const char *zNotFound; /* The --notfound option, or NULL */
515 const char *zFileGlob; /* The --files option, or NULL */
516 int flags; /* One or more HTTP_SERVER_ flags */
517 int isRunningAsService; /* Are we running as a service ? */
518 const wchar_t *zServiceName;/* Name of the service */
519 SOCKET s; /* Socket on which the http server listens */
520 };
521
522 /*
523 ** Variables used for running as windows service.
524 */
525 static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL, INVALID_SOCKET};
 
526 static SERVICE_STATUS ssStatus;
527 static SERVICE_STATUS_HANDLE sshStatusHandle;
528
529 /*
530 ** Get message string of the last system error. Return a pointer to the
@@ -611,15 +787,12 @@
611 static void WINAPI win32_http_service_ctrl(
612 DWORD dwCtrlCode
613 ){
614 switch( dwCtrlCode ){
615 case SERVICE_CONTROL_STOP: {
 
616 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 break;
622 }
623 default: {
624 break;
625 }
@@ -674,13 +847,13 @@
674 /*
675 ** When running as service, update the HttpService structure with the
676 ** listener socket and update the service status. This procedure must be
677 ** called from the http server when he is ready to accept connections.
678 */
679 LOCAL void win32_http_service_running(SOCKET s){
680 if( hsData.isRunningAsService ){
681 hsData.s = s;
682 win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
683 }
684 }
685
686 /*
@@ -704,10 +877,12 @@
704 hsData.port = nPort;
705 hsData.zBaseUrl = zBaseUrl;
706 hsData.zNotFound = zNotFound;
707 hsData.zFileGlob = zFileGlob;
708 hsData.flags = flags;
 
 
709
710 /* Try to start the control dispatcher thread for the service. */
711 if( !StartServiceCtrlDispatcherW(ServiceTable) ){
712 if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
713 return 1;
@@ -963,17 +1138,23 @@
963 fossil_print("Stopping service '%s'", zSvcName);
964 if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
965 if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
966 winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
967 }
 
968 }
969 while( sstat.dwCurrentState!=SERVICE_STOPPED ){
 
970 Sleep(100);
971 fossil_print(".");
972 QueryServiceStatus(hSvc, &sstat);
973 }
974 fossil_print("\nService '%s' stopped.\n", zSvcName);
 
 
 
 
975 }
976 if( !DeleteService(hSvc) ){
977 if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
978 fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
979 }else{
@@ -1108,21 +1289,27 @@
1108 SERVICE_ALL_ACCESS);
1109 if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1110 QueryServiceStatus(hSvc, &sstat);
1111 if( sstat.dwCurrentState!=SERVICE_RUNNING ){
1112 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 ){
 
 
1119 Sleep(100);
1120 fossil_print(".");
1121 QueryServiceStatus(hSvc, &sstat);
1122 }
1123 fossil_print("\nService '%s' started.\n", zSvcName);
 
 
 
 
1124 }else{
1125 fossil_print("Service '%s' is already started.\n", zSvcName);
1126 }
1127 CloseServiceHandle(hSvc);
1128 CloseServiceHandle(hScm);
@@ -1148,17 +1335,23 @@
1148 fossil_print("Stopping service '%s'", zSvcName);
1149 if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
1150 if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
1151 winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1152 }
 
1153 }
1154 while( sstat.dwCurrentState!=SERVICE_STOPPED ){
 
1155 Sleep(100);
1156 fossil_print(".");
1157 QueryServiceStatus(hSvc, &sstat);
1158 }
1159 fossil_print("\nService '%s' stopped.\n", zSvcName);
 
 
 
 
1160 }else{
1161 fossil_print("Service '%s' is already stopped.\n", zSvcName);
1162 }
1163 CloseServiceHandle(hSvc);
1164 CloseServiceHandle(hScm);
1165
--- src/winhttp.c
+++ src/winhttp.c
@@ -28,10 +28,207 @@
28 #include "winhttp.h"
29
30 #ifndef IPV6_V6ONLY
31 # define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */
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 }
230
231 /*
232 ** The HttpServer structure holds information about an instance of
233 ** the HTTP server itself.
234 */
@@ -39,11 +236,11 @@
236 struct HttpServer {
237 HANDLE hStoppedEvent; /* Event to signal when server is stopped,
238 ** must be closed by callee. */
239 char *zStopper; /* The stopper file name, must be freed by
240 ** callee. */
241 DualSocket listener; /* Sockets on which the server is listening,
242 ** may be closed by callee. */
243 };
244
245 /*
246 ** The HttpRequest structure holds information about each incoming
@@ -51,11 +248,11 @@
248 */
249 typedef struct HttpRequest HttpRequest;
250 struct HttpRequest {
251 int id; /* ID counter */
252 SOCKET s; /* Socket on which to receive data */
253 SocketAddr addr; /* Address from which data is coming */
254 int flags; /* Flags passed to win32_http_server() */
255 const char *zOptions; /* --baseurl, --notfound, --localauth, --th-trace */
256 };
257
258 /*
@@ -99,12 +296,11 @@
296 static void win32_server_stopper(void *pAppData){
297 HttpServer *p = (HttpServer*)pAppData;
298 if( p!=0 ){
299 HANDLE hStoppedEvent = p->hStoppedEvent;
300 const char *zStopper = p->zStopper;
301 if( hStoppedEvent!=NULL && zStopper!=0 ){
 
302 while( 1 ){
303 DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
304 1000, TRUE);
305 if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
306 /* The event is either invalid, signaled, or abandoned. Bail
@@ -113,12 +309,11 @@
309 break;
310 }
311 if( file_size(zStopper, ExtFILE)>=0 ){
312 /* The stopper file has been found. Attempt to close the server
313 ** listener socket now and then exit. */
314 DualSocket_close(&p->listener);
 
315 break;
316 }
317 }
318 }
319 if( hStoppedEvent!=NULL ){
@@ -140,12 +335,11 @@
335 HttpRequest *p = (HttpRequest*)pAppData;
336 FILE *in = 0, *out = 0, *aux = 0;
337 int amt, got, i;
338 int wanted = 0;
339 char *z;
340 char *zIp;
 
341 char zCmdFName[MAX_PATH];
342 char zRequestFName[MAX_PATH];
343 char zReplyFName[MAX_PATH];
344 char zCmd[2000]; /* Command-line to process the request */
345 char zHdr[4000]; /* The HTTP request header */
@@ -190,15 +384,11 @@
384 /*
385 ** The repository name is only needed if there was no open checkout. This
386 ** is designed to allow the open checkout for the interactive user to work
387 ** with the local Fossil server started via the "ui" command.
388 */
389 zIp = SocketAddr_toString(&p->addr);
 
 
 
 
390 if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
391 assert( g.zRepositoryName && g.zRepositoryName[0] );
392 sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s\n%s",
393 get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName
394 );
@@ -205,10 +395,11 @@
395 }else{
396 sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s",
397 get_utf8_bom(0), zRequestFName, zReplyFName, zIp
398 );
399 }
400 fossil_free(zIp);
401 aux = fossil_fopen(zCmdFName, "wb");
402 if( aux==0 ) goto end_request;
403 fwrite(zCmd, 1, strlen(zCmd), aux);
404
405 sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s",
@@ -246,12 +437,11 @@
437 static void win32_scgi_request(void *pAppData){
438 HttpRequest *p = (HttpRequest*)pAppData;
439 FILE *in = 0, *out = 0;
440 int amt, got, nHdr, i;
441 int wanted = 0;
442 char *zIp;
 
443 char zRequestFName[MAX_PATH];
444 char zReplyFName[MAX_PATH];
445 char zCmd[2000]; /* Command-line to process the request */
446 char zHdr[4000]; /* The SCGI request header */
447
@@ -278,20 +468,17 @@
468 if( got<=0 ) break;
469 fwrite(zHdr, 1, got, out);
470 wanted += got;
471 }
472 assert( g.zRepositoryName && g.zRepositoryName[0] );
473 zIp = SocketAddr_toString(&p->addr);
 
 
 
 
474 sqlite3_snprintf(sizeof(zCmd), zCmd,
475 "\"%s\" http \"%s\" \"%s\" %s \"%s\" --scgi --nossl%s",
476 g.nameOfExe, zRequestFName, zReplyFName, zIp,
477 g.zRepositoryName, p->zOptions
478 );
479 fossil_free(zIp);
480 in = fossil_fopen(zReplyFName, "w+b");
481 fflush(out);
482 fossil_system(zCmd);
483 if( in ){
484 while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
@@ -311,10 +498,12 @@
498 for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
499 for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
500 fossil_free(p);
501 }
502
503 /* forward reference */
504 static void win32_http_service_running(DualSocket* pS);
505
506 /*
507 ** Start a listening socket and process incoming HTTP requests on
508 ** that socket.
509 */
@@ -328,13 +517,11 @@
517 const char *zIpAddr, /* Bind to this IP address, if not NULL */
518 int flags /* One or more HTTP_SERVER_ flags */
519 ){
520 HANDLE hStoppedEvent;
521 WSADATA wd;
522 DualSocket ds;
 
 
523 int idCnt = 0;
524 int iPort = mnPort;
525 Blob options;
526 wchar_t zTmpPath[MAX_PATH];
527 const char *zSkin;
@@ -375,57 +562,31 @@
562 }
563 #endif
564 if( WSAStartup(MAKEWORD(2,0), &wd) ){
565 fossil_fatal("unable to initialize winsock");
566 }
567 DualSocket_init(&ds);
 
 
568 while( iPort<=mxPort ){
 
 
 
 
 
 
 
 
 
569 if( zIpAddr ){
570 if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){
571 iPort++;
572 continue;
573 }
574 }else{
575 if( DualSocket_listen(&ds,
576 (flags & HTTP_SERVER_LOCALHOST) ? "L" : "W",
577 iPort
578 )==0 ){
579 iPort++;
580 continue;
581 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582 }
583 break;
584 }
585 if( iPort>mxPort ){
586 if( mnPort==mxPort ){
587 fossil_fatal("unable to open listening socket on port %d", mnPort);
588 }else{
589 fossil_fatal("unable to open listening socket on any"
590 " port in the range %d..%d", mnPort, mxPort);
591 }
592 }
@@ -455,51 +616,65 @@
616 DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
617 GetCurrentProcess(), &pServer->hStoppedEvent,
618 0, FALSE, DUPLICATE_SAME_ACCESS);
619 assert( pServer->hStoppedEvent!=NULL );
620 pServer->zStopper = fossil_strdup(zStopper);
621 pServer->listener = ds;
622 file_delete(zStopper);
623 _beginthread(win32_server_stopper, 0, (void*)pServer);
624 }
625 /* Set the service status to running and pass the listener socket to the
626 ** service handling procedures. */
627 win32_http_service_running(&ds);
628 for(;;){
629 DualSocket client;
630 DualAddr client_addr;
631 HttpRequest *pRequest;
 
632 int wsaError;
633
634 DualSocket_accept(&ds, &client, &client_addr);
635 if( client.s4==INVALID_SOCKET && client.s6==INVALID_SOCKET ){
636 /* If the service control handler has closed the listener socket,
637 ** cleanup and return, otherwise report a fatal error. */
638 wsaError = WSAGetLastError();
639 DualSocket_close(&ds);
640 if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
641 WSACleanup();
642 return;
643 }else{
 
644 WSACleanup();
645 fossil_fatal("error from accept()");
646 }
647 }
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 }
673 }
674 }
675 DualSocket_close(&ds);
676 WSACleanup();
677 SetEvent(hStoppedEvent);
678 CloseHandle(hStoppedEvent);
679 }
680
@@ -514,17 +689,18 @@
689 const char *zNotFound; /* The --notfound option, or NULL */
690 const char *zFileGlob; /* The --files option, or NULL */
691 int flags; /* One or more HTTP_SERVER_ flags */
692 int isRunningAsService; /* Are we running as a service ? */
693 const wchar_t *zServiceName;/* Name of the service */
694 DualSocket s; /* Sockets on which the http server listens */
695 };
696
697 /*
698 ** Variables used for running as windows service.
699 */
700 static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL,
701 {INVALID_SOCKET, INVALID_SOCKET}};
702 static SERVICE_STATUS ssStatus;
703 static SERVICE_STATUS_HANDLE sshStatusHandle;
704
705 /*
706 ** Get message string of the last system error. Return a pointer to the
@@ -611,15 +787,12 @@
787 static void WINAPI win32_http_service_ctrl(
788 DWORD dwCtrlCode
789 ){
790 switch( dwCtrlCode ){
791 case SERVICE_CONTROL_STOP: {
792 DualSocket_close(&hsData.s);
793 win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
 
 
 
 
794 break;
795 }
796 default: {
797 break;
798 }
@@ -674,13 +847,13 @@
847 /*
848 ** When running as service, update the HttpService structure with the
849 ** listener socket and update the service status. This procedure must be
850 ** called from the http server when he is ready to accept connections.
851 */
852 static void win32_http_service_running(DualSocket *pS){
853 if( hsData.isRunningAsService ){
854 hsData.s = *pS;
855 win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
856 }
857 }
858
859 /*
@@ -704,10 +877,12 @@
877 hsData.port = nPort;
878 hsData.zBaseUrl = zBaseUrl;
879 hsData.zNotFound = zNotFound;
880 hsData.zFileGlob = zFileGlob;
881 hsData.flags = flags;
882
883 if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; }
884
885 /* Try to start the control dispatcher thread for the service. */
886 if( !StartServiceCtrlDispatcherW(ServiceTable) ){
887 if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
888 return 1;
@@ -963,17 +1138,23 @@
1138 fossil_print("Stopping service '%s'", zSvcName);
1139 if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
1140 if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
1141 winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1142 }
1143 QueryServiceStatus(hSvc, &sstat);
1144 }
1145 while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1146 sstat.dwCurrentState==SERVICE_RUNNING ){
1147 Sleep(100);
1148 fossil_print(".");
1149 QueryServiceStatus(hSvc, &sstat);
1150 }
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 }
1156 }
1157 if( !DeleteService(hSvc) ){
1158 if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
1159 fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
1160 }else{
@@ -1108,21 +1289,27 @@
1289 SERVICE_ALL_ACCESS);
1290 if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1291 QueryServiceStatus(hSvc, &sstat);
1292 if( sstat.dwCurrentState!=SERVICE_RUNNING ){
1293 fossil_print("Starting service '%s'", zSvcName);
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 ){
1302 Sleep(100);
1303 fossil_print(".");
1304 QueryServiceStatus(hSvc, &sstat);
1305 }
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 }
1311 }else{
1312 fossil_print("Service '%s' is already started.\n", zSvcName);
1313 }
1314 CloseServiceHandle(hSvc);
1315 CloseServiceHandle(hScm);
@@ -1148,17 +1335,23 @@
1335 fossil_print("Stopping service '%s'", zSvcName);
1336 if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
1337 if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
1338 winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1339 }
1340 QueryServiceStatus(hSvc, &sstat);
1341 }
1342 while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1343 sstat.dwCurrentState==SERVICE_RUNNING ){
1344 Sleep(100);
1345 fossil_print(".");
1346 QueryServiceStatus(hSvc, &sstat);
1347 }
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 }
1353 }else{
1354 fossil_print("Service '%s' is already stopped.\n", zSvcName);
1355 }
1356 CloseServiceHandle(hSvc);
1357 CloseServiceHandle(hScm);
1358

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button