Althttpd

Changes On Branch signal-safety
Login

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

Changes In Branch signal-safety Excluding Merge-Ins

This is equivalent to a diff from 2f322f9b58 to 7b35e12f15

2024-01-12
13:30
Rework logging steps and cleanup to avoid calling into libc functions which are not signal-safe when handling a signal, as discussed in [forum thread 4dc31619341ce947|4dc31619341ce947]. ... (check-in: 9faea68111 user: stephan tags: trunk)
13:22
Improvements to robot defenses. ... (check-in: bb0fab2645 user: drh tags: trunk)
2024-01-04
12:07
Remove an unused var. ... (Closed-Leaf check-in: 7b35e12f15 user: stephan tags: signal-safety)
12:06
Use clock_gettime() to record the response wall-clock time for logging purposes, as it's signal-safe. ... (check-in: b26779f148 user: stephan tags: signal-safety)
2023-12-14
22:48
Initial reworking for signal safety, as per forum post febca13ffc. This is far from ready for use. ... (check-in: 070b08d958 user: stephan tags: signal-safety)
2023-10-14
19:13
Add a note that --root DIR should always been an absolute path. ... (check-in: 2f322f9b58 user: stephan tags: trunk)
2023-08-12
15:02
Fix redundancies in the MIME table. ... (check-in: c0bdc68e6c user: drh tags: trunk)

Changes to Makefile.
34
35
36
37
38
39
40
41
42
	@flags="`cat version`"; set -x; \
	$(CC) $(CFLAGS) "-D$$flags" -static -o althttpd althttpd.c

static-althttpsd:	althttpd.c version
	@flags="`cat version`"; set -x; \
	$(CC) $(CFLAGS) "-D$$flags" -static -fPIC -o althttpsd -DENABLE_TLS althttpd.c -lssl -lcrypto -lpthread -ldl

clean:	
	rm -f althttpd althttpsd version







|

34
35
36
37
38
39
40
41
42
	@flags="`cat version`"; set -x; \
	$(CC) $(CFLAGS) "-D$$flags" -static -o althttpd althttpd.c

static-althttpsd:	althttpd.c version
	@flags="`cat version`"; set -x; \
	$(CC) $(CFLAGS) "-D$$flags" -static -fPIC -o althttpsd -DENABLE_TLS althttpd.c -lssl -lcrypto -lpthread -ldl

clean:
	rm -f althttpd althttpsd version
Changes to althttpd.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
** 2001-09-15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This source code file implements a small, simple, stand-alone HTTP
** server.  
**
** Features:
**
**     * Launched from inetd/xinetd/systemd, or as a stand-alone server
**     * One process per request
**     * Deliver static content or run CGI or SCGI
**     * Virtual sites based on the "Host:" property of the HTTP header
**     * Runs in a chroot jail
**     * Unified log file in a CSV format
**     * Small code base (this 1 file) to facilitate security auditing
**     * Simple setup - no configuration files to misconfigure
** 
** This file implements a small and simple but secure and effective web
** server.  There are no frills.  Anything that could be reasonably
** omitted has been.
**
** Setup rules:
**
**    (1) Launch as root from inetd/systemd like this:













|











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
** 2001-09-15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This source code file implements a small, simple, stand-alone HTTP
** server.
**
** Features:
**
**     * Launched from inetd/xinetd/systemd, or as a stand-alone server
**     * One process per request
**     * Deliver static content or run CGI or SCGI
**     * Virtual sites based on the "Host:" property of the HTTP header
**     * Runs in a chroot jail
**     * Unified log file in a CSV format
**     * Small code base (this 1 file) to facilitate security auditing
**     * Simple setup - no configuration files to misconfigure
**
** This file implements a small and simple but secure and effective web
** server.  There are no frills.  Anything that could be reasonably
** omitted has been.
**
** Setup rules:
**
**    (1) Launch as root from inetd/systemd like this:
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
**                   originating from the same host).  This is the
**                   default if --root is omitted.
**
**  --family ipv4    Only accept input from IPV4 or IPV6, respectively.
**  --family ipv6    These options are only meaningful if althttpd is run
**                   as a stand-alone server.
**
**  --jail BOOLEAN   Indicates whether or not to form a chroot jail if 
**                   initially run as root.  The default is true, so the only
**                   useful variant of this option is "--jail 0" which prevents
**                   the formation of the chroot jail.
**
**  --max-age SEC    The value for "Cache-Control: max-age=%d".  Defaults to
**                   120 seconds.
**
**  --max-cpu SEC    Maximum number of seconds of CPU time allowed per
**                   HTTP connection.  Default 30 (build option:
**                   -DMAX_CPU=integer). 0 means no limit.
**
**  --debug BOOLEAN  Disables input timeouts.  This is useful for debugging
**                   when inputs are being typed in manually.
**
**  --enable-sab     Add new lines to the HTTP reply header that are
**                   prerequisites for SharedArrayBuffer.  These are the lines:
**                     Cross-Origin-Embedder-Policy: require-corp
**                     Cross-Origin-Opener-Policy: same-origin
**        
**
** Additional command-line options available when compiling with ENABLE_TLS:
**
**  --cert FILE      The TLS certificate, the "fullchain.pem" file
**
**  --pkey FILE      The TLS private key, the "privkey.pem" file.  May be
**                   omitted if the --cert file is the concatenation of







|


















|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
**                   originating from the same host).  This is the
**                   default if --root is omitted.
**
**  --family ipv4    Only accept input from IPV4 or IPV6, respectively.
**  --family ipv6    These options are only meaningful if althttpd is run
**                   as a stand-alone server.
**
**  --jail BOOLEAN   Indicates whether or not to form a chroot jail if
**                   initially run as root.  The default is true, so the only
**                   useful variant of this option is "--jail 0" which prevents
**                   the formation of the chroot jail.
**
**  --max-age SEC    The value for "Cache-Control: max-age=%d".  Defaults to
**                   120 seconds.
**
**  --max-cpu SEC    Maximum number of seconds of CPU time allowed per
**                   HTTP connection.  Default 30 (build option:
**                   -DMAX_CPU=integer). 0 means no limit.
**
**  --debug BOOLEAN  Disables input timeouts.  This is useful for debugging
**                   when inputs are being typed in manually.
**
**  --enable-sab     Add new lines to the HTTP reply header that are
**                   prerequisites for SharedArrayBuffer.  These are the lines:
**                     Cross-Origin-Embedder-Policy: require-corp
**                     Cross-Origin-Opener-Policy: same-origin
**
**
** Additional command-line options available when compiling with ENABLE_TLS:
**
**  --cert FILE      The TLS certificate, the "fullchain.pem" file
**
**  --pkey FILE      The TLS private key, the "privkey.pem" file.  May be
**                   omitted if the --cert file is the concatenation of
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
**      command-line option.  The root of the jail is the directory that
**      contains the various $HOST.website content subdirectories.
**
** (2)  No input is read while this process has root privileges.  Root
**      privileges are dropped prior to reading any input (but after entering
**      the chroot jail, of course).  If root privileges cannot be dropped
**      (for example because the --user command-line option was omitted or
**      because the user specified by the --user option does not exist), 
**      then the process aborts with an error prior to reading any input.
**
** (3)  The length of an HTTP request is limited to MAX_CONTENT_LENGTH bytes
**      (default: 250 million).  Any HTTP request longer than this fails
**      with an error. (Build option: -DMAX_CONTENT_LENGTH=integer)
**
** (4)  There are hard-coded time-outs on each HTTP request.  If this process







|







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
**      command-line option.  The root of the jail is the directory that
**      contains the various $HOST.website content subdirectories.
**
** (2)  No input is read while this process has root privileges.  Root
**      privileges are dropped prior to reading any input (but after entering
**      the chroot jail, of course).  If root privileges cannot be dropped
**      (for example because the --user command-line option was omitted or
**      because the user specified by the --user option does not exist),
**      then the process aborts with an error prior to reading any input.
**
** (3)  The length of an HTTP request is limited to MAX_CONTENT_LENGTH bytes
**      (default: 250 million).  Any HTTP request longer than this fails
**      with an error. (Build option: -DMAX_CONTENT_LENGTH=integer)
**
** (4)  There are hard-coded time-outs on each HTTP request.  If this process
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
**      are converted into "_".  This applies to the pathname only, not
**      to the query parameters or fragment.
**
** (7)  If the first character of any URI pathname component is "." or "-"
**      then a 404 Not Found reply is generated.  This prevents attacks
**      such as including ".." or "." directory elements in the pathname
**      and allows placing files and directories in the content subdirectory
**      that are invisible to all HTTP requests, by making the first 
**      character of the file or subdirectory name "-" or ".".
**
** (8)  The request URI must begin with "/" or else a 404 error is generated.
**
** (9)  This program never sets the value of an environment variable to a
**      string that begins with "() {".
**
** (10) If the --ipshun option is used, specific IP addresses can be
**      temporarily block for abusive behavior.  
**
** (11) If a CGI reports status code 418 ("I'm a teapot", rfc2324) and if
**      the --ipshun option is used, then the IP address is temporarily
**      blocked.
**
** Security Auditing:
**







|








|







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
**      are converted into "_".  This applies to the pathname only, not
**      to the query parameters or fragment.
**
** (7)  If the first character of any URI pathname component is "." or "-"
**      then a 404 Not Found reply is generated.  This prevents attacks
**      such as including ".." or "." directory elements in the pathname
**      and allows placing files and directories in the content subdirectory
**      that are invisible to all HTTP requests, by making the first
**      character of the file or subdirectory name "-" or ".".
**
** (8)  The request URI must begin with "/" or else a 404 error is generated.
**
** (9)  This program never sets the value of an environment variable to a
**      string that begins with "() {".
**
** (10) If the --ipshun option is used, specific IP addresses can be
**      temporarily block for abusive behavior.
**
** (11) If a CGI reports status code 418 ("I'm a teapot", rfc2324) and if
**      the --ipshun option is used, then the IP address is temporarily
**      blocked.
**
** Security Auditing:
**
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
** If the file "-auth" exists in the same directory as the content file
** (for both static content and CGI) then it contains the information used
** for basic authorization.  The file format is as follows:
**
**    *  Blank lines and lines that begin with '#' are ignored
**    *  "http-redirect" forces a redirect to HTTPS if not there already
**    *  "https-only" disallows operation in HTTP
**    *  "user NAME LOGIN:PASSWORD" checks to see if LOGIN:PASSWORD 
**       authorization credentials are provided, and if so sets the
**       REMOTE_USER to NAME.
**    *  "realm TEXT" sets the realm to TEXT.
**
** There can be multiple "user" lines.  If no "user" line matches, the
** request fails with a 401 error.
**







|







298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
** If the file "-auth" exists in the same directory as the content file
** (for both static content and CGI) then it contains the information used
** for basic authorization.  The file format is as follows:
**
**    *  Blank lines and lines that begin with '#' are ignored
**    *  "http-redirect" forces a redirect to HTTPS if not there already
**    *  "https-only" disallows operation in HTTP
**    *  "user NAME LOGIN:PASSWORD" checks to see if LOGIN:PASSWORD
**       authorization credentials are provided, and if so sets the
**       REMOTE_USER to NAME.
**    *  "realm TEXT" sets the realm to TEXT.
**
** There can be multiple "user" lines.  If no "user" line matches, the
** request fails with a 401 error.
**
370
371
372
373
374
375
376















377
378
379
380
381
382
383
#  ifdef ENABLE_TLS
#    define SERVER_SOFTWARE_TLS SERVER_SOFTWARE ", " OPENSSL_VERSION_TEXT
#  else
#    define SERVER_SOFTWARE_TLS SERVER_SOFTWARE
#  endif
#endif
















/*
** We record most of the state information as global variables.  This
** saves having to pass information to subroutines as parameters, and
** makes the executable smaller...
*/
static const char *zRoot = 0;    /* Root directory of the website */
static char *zPostData= 0;       /* POST data */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#  ifdef ENABLE_TLS
#    define SERVER_SOFTWARE_TLS SERVER_SOFTWARE ", " OPENSSL_VERSION_TEXT
#  else
#    define SERVER_SOFTWARE_TLS SERVER_SOFTWARE
#  endif
#endif

/*
** Clock ID to use for clock_gettime(), for use in collecting the
** wall-clock processing time using clock_gettime() (which is
** signal-safe). We use clock_gettime(), rather than gmtime(), for
** measuring request wall-clock time because it's signal-safe.
** See discussion at:
** https://sqlite.org/althttpd/forumpost/4dc31619341ce947
*/
#ifdef _POSIX_MONOTONIC_CLOCK
#  define ALTHTTPD_CLOCK_ID CLOCK_MONOTONIC
#else
#  define ALTHTTPD_CLOCK_ID CLOCK_REALTIME
   /* noting that this can jump if the system time changes */
#endif

/*
** We record most of the state information as global variables.  This
** saves having to pass information to subroutines as parameters, and
** makes the executable smaller...
*/
static const char *zRoot = 0;    /* Root directory of the website */
static char *zPostData= 0;       /* POST data */
416
417
418
419
420
421
422

423
424
425

426
427
428
429
430
431
432
433
434
435
436
437

438
439
440
441
442
443
444
445

446
447
448
449
450
451
452
static char *zHttpScheme = "http";/* HTTP_SCHEME CGI variable */
static char *zHttps = 0;         /* HTTPS CGI variable */
static int nIn = 0;              /* Number of bytes of input */
static int nOut = 0;             /* Number of bytes of output */
static char zReplyStatus[4];     /* Reply status code */
static int statusSent = 0;       /* True after status line is sent */
static const char *zLogFile = 0; /* Log to this file */

static const char *zIPShunDir=0; /* Directory containing hostile IP addresses */
static int debugFlag = 0;        /* True if being debugged */
static struct timeval beginTime; /* Time when this process starts */

static int closeConnection = 0;  /* True to send Connection: close in reply */
static int nRequest = 0;         /* Number of requests processed */
static int omitLog = 0;          /* Do not make logfile entries if true */
static int useHttps = 0;         /* 0=HTTP, 1=external HTTPS (stunnel),
                                 ** 2=builtin TLS support */
static int useTimeout = 1;       /* True to use times */
static int nTimeoutLine = 0;     /* Line number where timeout was set */
static int standalone = 0;       /* Run as a standalone server (no inetd) */
static int ipv6Only = 0;         /* Use IPv6 only */
static int ipv4Only = 0;         /* Use IPv4 only */
static struct rusage priorSelf;  /* Previously report SELF time */
static struct rusage priorChild; /* Previously report CHILD time */

static int mxAge = 120;          /* Cache-control max-age */
static char *default_path = "/bin:/usr/bin";  /* Default PATH variable */
static char *zScgi = 0;          /* Value of the SCGI env variable */
static int rangeStart = 0;       /* Start of a Range: request */
static int rangeEnd = 0;         /* End of a Range: request */
static int maxCpu = MAX_CPU;     /* Maximum CPU time per process */
static int enableSAB = 0;        /* Add reply header to enable
                                 ** SharedArrayBuffer */


/* Forward reference */
static void Malfunction(int errNo, const char *zFormat, ...);



#ifdef ENABLE_TLS







>



>












>








>







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
static char *zHttpScheme = "http";/* HTTP_SCHEME CGI variable */
static char *zHttps = 0;         /* HTTPS CGI variable */
static int nIn = 0;              /* Number of bytes of input */
static int nOut = 0;             /* Number of bytes of output */
static char zReplyStatus[4];     /* Reply status code */
static int statusSent = 0;       /* True after status line is sent */
static const char *zLogFile = 0; /* Log to this file */
static char zExpLogFile[500] = {0}; /* %-expanded log file name */
static const char *zIPShunDir=0; /* Directory containing hostile IP addresses */
static int debugFlag = 0;        /* True if being debugged */
static struct timeval beginTime; /* Time when this process starts */
static struct timespec tsBeginTime; /* clock_gettime() when request processing starts */
static int closeConnection = 0;  /* True to send Connection: close in reply */
static int nRequest = 0;         /* Number of requests processed */
static int omitLog = 0;          /* Do not make logfile entries if true */
static int useHttps = 0;         /* 0=HTTP, 1=external HTTPS (stunnel),
                                 ** 2=builtin TLS support */
static int useTimeout = 1;       /* True to use times */
static int nTimeoutLine = 0;     /* Line number where timeout was set */
static int standalone = 0;       /* Run as a standalone server (no inetd) */
static int ipv6Only = 0;         /* Use IPv6 only */
static int ipv4Only = 0;         /* Use IPv4 only */
static struct rusage priorSelf;  /* Previously report SELF time */
static struct rusage priorChild; /* Previously report CHILD time */
/*static struct timespec tsSelf;*/
static int mxAge = 120;          /* Cache-control max-age */
static char *default_path = "/bin:/usr/bin";  /* Default PATH variable */
static char *zScgi = 0;          /* Value of the SCGI env variable */
static int rangeStart = 0;       /* Start of a Range: request */
static int rangeEnd = 0;         /* End of a Range: request */
static int maxCpu = MAX_CPU;     /* Maximum CPU time per process */
static int enableSAB = 0;        /* Add reply header to enable
                                 ** SharedArrayBuffer */
static int inSignalHandler = 0;  /* True if running a signal handler */

/* Forward reference */
static void Malfunction(int errNo, const char *zFormat, ...);



#ifdef ENABLE_TLS
654
655
656
657
658
659
660










661
662
663
664

665
666
667
668




669







670
671
672
673
674

675
676

677
678


679
680
681











682




683

684
685
686
687
688
689
690





691

692





















































































































693
694
695
696
697
698

699

700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729





730
731








732








733
734







735
736
737
738

739




















740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758

759
760
761


762



763


764


765
766
767
768
769
770


771
772








773
774
775
776






777
778




779
780
781
782
783
784
785
786
787
788
  { "SCRIPT_NAME",              &zRealScript },
  { "SERVER_NAME",              &zServerName },
  { "SERVER_PORT",              &zServerPort },
  { "SERVER_PROTOCOL",          &zProtocol },
  { "SERVER_SOFTWARE",          &zServerSoftware },
};












/*
** Double any double-quote characters in a string.  This is used to
** quote strings for output into the CSV log file.

*/
static char *Escape(const char *z){
  size_t i, j;
  size_t n;




  char c;







  char *zOut;
  for(i=0; (c=z[i])!=0 && c!='"'; i++){}
  if( c==0 ) return (char *)z;
  n = 1;
  for(i++; (c=z[i])!=0; i++){ if( c=='"' ) n++; }

  zOut = malloc( i+n+1 );
  if( zOut==0 ) return "";

  for(i=j=0; (c=z[i])!=0; i++){
    zOut[j++] = c;


    if( c=='"' ) zOut[j++] = c;
  }
  zOut[j] = 0;











  return zOut;




}


/*
** Convert a struct timeval into an integer number of microseconds
*/
static long long int tvms(struct timeval *p){
  return ((long long int)p->tv_sec)*1000000 + (long long int)p->tv_usec;
}







/*





















































































































** Make an entry in the log file.  If the HTTP connection should be
** closed, then terminate this process.  Otherwise return.
*/
static void MakeLogEntry(int exitCode, int lineNum){
  FILE *log;
  if( zPostData ){

    free(zPostData);

    zPostData = 0;
  }
  if( zLogFile && !omitLog ){
    struct timeval now;
    struct tm *pTm;
    struct rusage self, children;
    int waitStatus;
    const char *zRM = zRemoteUser ? zRemoteUser : "";
    const char *zFilename;
    size_t sz;
    char zDate[200];
    char zExpLogFile[500];

    if( zScript==0 ) zScript = "";
    if( zRealScript==0 ) zRealScript = "";
    if( zRemoteAddr==0 ) zRemoteAddr = "";
    if( zHttpHost==0 ) zHttpHost = "";
    if( zReferer==0 ) zReferer = "";
    if( zAgent==0 ) zAgent = "";
    if( zQuerySuffix==0 ) zQuerySuffix = "";
    gettimeofday(&now, 0);
    pTm = localtime(&now.tv_sec);
    strftime(zDate, sizeof(zDate), "%Y-%m-%d %H:%M:%S", pTm);
    sz = strftime(zExpLogFile, sizeof(zExpLogFile), zLogFile, pTm);
    if( sz>0 && sz<sizeof(zExpLogFile)-2 ){
      zFilename = zExpLogFile;
    }else{
      zFilename = zLogFile;
    }
    waitpid(-1, &waitStatus, WNOHANG);





    getrusage(RUSAGE_SELF, &self);
    getrusage(RUSAGE_CHILDREN, &children);








    if( (log = fopen(zFilename,"a"))!=0 ){








#ifdef COMBINED_LOG_FORMAT
      strftime(zDate, sizeof(zDate), "%d/%b/%Y:%H:%M:%S %Z", pTm);







      fprintf(log, "%s - - [%s] \"%s %s %s\" %s %d \"%s\" \"%s\"\n",
              zRemoteAddr, zDate, zMethod, zScript, zProtocol,
              zReplyStatus, nOut, zReferer, zAgent);
#else

      strftime(zDate, sizeof(zDate), "%Y-%m-%d %H:%M:%S", pTm);




















      /* Log record files:
      **  (1) Date and time
      **  (2) IP address
      **  (3) URL being accessed
      **  (4) Referer
      **  (5) Reply status
      **  (6) Bytes received
      **  (7) Bytes sent
      **  (8) Self user time
      **  (9) Self system time
      ** (10) Children user time
      ** (11) Children system time
      ** (12) Total wall-clock time
      ** (13) Request number for same TCP/IP connection
      ** (14) User agent
      ** (15) Remote user
      ** (16) Bytes of URL that correspond to the SCRIPT_NAME
      ** (17) Line number in source file
      */

      fprintf(log,
        "%s,%s,\"%s://%s%s%s\",\"%s\","
           "%s,%d,%d,%lld,%lld,%lld,%lld,%lld,%d,\"%s\",\"%s\",%d,%d\n",


        zDate, zRemoteAddr, zHttpScheme, Escape(zHttpHost), Escape(zScript),



        Escape(zQuerySuffix),


        Escape(zReferer), zReplyStatus, nIn, nOut,


        tvms(&self.ru_utime) - tvms(&priorSelf.ru_utime),
        tvms(&self.ru_stime) - tvms(&priorSelf.ru_stime),
        tvms(&children.ru_utime) - tvms(&priorChild.ru_utime),
        tvms(&children.ru_stime) - tvms(&priorChild.ru_stime),
        tvms(&now) - tvms(&beginTime),
        nRequest, Escape(zAgent), Escape(zRM),


        (int)(strlen(zHttpScheme)+strlen(zHttpHost)+strlen(zRealScript)+3),
        lineNum








      );
      priorSelf = self;
      priorChild = children;
#endif






      fclose(log);
      nIn = nOut = 0;




    }
  }
  if( closeConnection ){
    exit(exitCode);
  }
  statusSent = 0;
}

/*
** Allocate memory safely







>
>
>
>
>
>
>
>
>
>


<
<
>

|
|
|
>
>
>
>
|
>
>
>
>
>
>
>
|
<
<
|
|
>
|
|
>
|
<
>
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>

>







>
>
>
>
>
|
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|

>
|
>



<
<




<
<
<








<
<
<
<
|





>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>

|
>
>
>
>
>
>
>
|
|
|
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



















>
|
|
<
>
>
|
>
>
>
|
>
>
|
>
>
|
|
|
|
|
|
>
>
|
|
>
>
>
>
>
>
>
>
|



>
>
>
>
>
>
|

>
>
>
>


|







673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691


692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709


710
711
712
713
714
715
716

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883


884
885
886
887



888
889
890
891
892
893
894
895




896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982

983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
  { "SCRIPT_NAME",              &zRealScript },
  { "SERVER_NAME",              &zServerName },
  { "SERVER_PORT",              &zServerPort },
  { "SERVER_PROTOCOL",          &zProtocol },
  { "SERVER_SOFTWARE",          &zServerSoftware },
};

/*
** A structure for holding a single date and time.
*/
typedef struct DateTime DateTime;
struct DateTime {
  long long iJD;      /* The julian day number times 86400000 */
  int Y, M, D;        /* Year, month, and day */
  int h, m;           /* Hour and minutes */
  double s;           /* Seconds */
};

/*


** Populates p based on the time stored in t.
*/
static void unixToDateTime(time_t t, DateTime *p){
  int Z, A, B, C, D, E, X1;
  int day_s;
  p->iJD = (long long)((t/86400.0 + 2440587.5) * 86400000);
  Z = (int)((p->iJD + 43200000)/86400000);
  A = (int)((Z - 1867216.25)/36524.25);
  A = Z + 1 + A - (A/4);
  B = A + 1524;
  C = (int)((B - 122.1)/365.25);
  D = (36525*(C&32767))/100;
  E = (int)((B-D)/30.6001);
  X1 = (int)(30.6001*E);
  p->D = B - D - X1;
  p->M = E<14 ? E-1 : E-13;
  p->Y = p->M>2 ? C - 4716 : C - 4715;



  day_s = t % 86400;
  p->h = day_s / 3600;
  p->m = day_s % 3600 / 60;
  p->s = day_s % 60;
#if 0
  fprintf(stderr,"day_s = %d, DateTime Y=%d M=%d D=%d h=%d m=%d s=%d\n",
          (int)day_s,

          p->Y, p->M, p->D,
          p->h, p->m, (int)p->s);
#endif
}

#ifdef COMBINED_LOG_FORMAT
/*
** Copies and converts _relevant_ fields of pD into pTm. Only those
** fields relevant for althttpd's own logging are handled and the rest
** are zeroed out, thus pTm is not necessarily suitable for all
** strftime() formats or libc time functions after calling this.
*/
static void DateTime_toTm(const DateTime *pD, struct tm *pTm){
  memset(pTm, 0, sizeof(*pTm));
  pTm->tm_isdst = -1;
  pTm->tm_sec = (int)pD->s;
  pTm->tm_min = pD->m;
  pTm->tm_hour = pD->h;
  pTm->tm_mday = pD->D;
  pTm->tm_mon = pD->M - 1;
  pTm->tm_year = pD->Y - 1900;
}
#endif

/*
** Convert a struct timeval into an integer number of microseconds
*/
static long long int tvms(struct timeval *p){
  return ((long long int)p->tv_sec)*1000000 + (long long int)p->tv_usec;
}
/*
** Convert a struct timespec into an integer number of microseconds
*/
static long long int tsms(struct timespec *p){
  return ((long long int)p->tv_sec)*1000000 + (long long int)(p->tv_nsec/1000);
}

/*
** If (z+strlen(zIn)) < zEnd then this appends zIn to z, sets *zTail
** to the end of that string, and returns z. If (z+strlen(zIn)) >= zEnd
** then *zTail is set to 0 and 0 is returned. If z is NULL, *zTail
** is set to 0 but this is otherwise a no-op.
*/
static char * log_str(char *z, const char *zEnd, const char *zIn,
                      char **zTail){
  const size_t n = z ? strlen(zIn) : 0;
  if( !z || zEnd<=z+n ){
    *zTail = 0;
    return 0;
  }
  memcpy( z, zIn, n );
  *zTail = z+n;
  return z;
}

/*
** Works like log_str() but doubles all double-quote characters in the
** intput. If z is NULL or zIn is too long for the input buffer,
** *zTail is set to 0 and 0 is returned.
**
** This is used to quote strings for output into the CSV log file.
*/
static char * log_escstr(char *z, const char *zEnd, const char *zIn,
                         char **zTail){
  char * zOrig = z;
  char c = *zIn;
  if( !z || z>=zEnd ){
    goto too_short;
  }
  for( ; c && z<zEnd; c=*++zIn ){
    if( '"'==c ){
      if( z>=zEnd-1 ){
        goto too_short;
      }
      *z++ = c;
    }
    *z++ = c;
  }
  *zTail = z;
  return zOrig;
too_short:
  *zTail = 0;
  return 0;
}

/*
** Appends d to z, sets *zTail to the byte after the new end of z, and
** returns z. If appending d would extend past zEnd then *zTail is set
** to 0 and 0 is returned, but z is not modified.  If z is NULL, *zTail
** is set to 0 but this is otherwise a no-op.
**
** minDigits must be at least 1 and d will be left-padded with 0's to
** a minimum of that length.
*/
static char * log_int(char *z, const char *zEnd, long long d,
                      int minDigits, char **zTail){
  char buf[128];
  int i = (int)sizeof(buf), n = 0;
  int neg = 0;

  assert( minDigits>0 );
  if( !z ){
    *zTail = 0;
    return 0;
  }else if( d<0 ){
    neg = 1;
    d = -d;
  }
  for( ; (minDigits-- > 0) || d>0; d/=10 ){
    buf[--i] = '0' + (d%10);
    assert(i>0);
    ++n;
  }
  if( neg ){
    buf[--i] = '-';
    ++n;
  }
  if( zEnd <= z+n ){
    return 0;
  }
  memcpy(z, &buf[i], n);
  *zTail = z+n;
  return z;
}

#ifndef COMBINED_LOG_FORMAT
/*
** A proxy for log_str() and log_int() which appends 19 bytes of
** pD's timestamp to z in the form "YYYY-MM-DD hh:mm:ss", assigns
** *zTail to the address one after the end of that string, and returns
** z. If (z+19 >= zEnd) then *zTail is set to 0 and 0 is returned, but
** this is otherwise a no-op.
*/
static char * log_DateTime(char * z, const char *zEnd,
                           const struct DateTime *pD, char **zTail){
  char * zOut = z;
  if( z+19 >= zEnd ){
    *zTail = 0;
    return 0;
  }
#define aint(N,DIGITS,SUFFIX) log_int( zOut, zEnd, (int)(N), (DIGITS), &zOut ); \
  if( SUFFIX ) log_str( zOut, zEnd, SUFFIX, &zOut )
  aint(pD->Y, 4, "-");
  aint(pD->M, 2, "-");
  aint(pD->D, 2, " ");
  aint(pD->h, 2, ":");
  aint(pD->m, 2, ":");
  aint(pD->s, 2, 0);
#undef aint
  *zTail = zOut;
  return z;
}
#endif

/*
** Make an entry in the log file.  If the HTTP connection should be
** closed, then terminate this process.  Otherwise return.
*/
static void MakeLogEntry(int exitCode, int lineNum){
  int logfd;
  if( zPostData ){
    if( inSignalHandler==0 ){
      free(zPostData);
    }
    zPostData = 0;
  }
  if( zLogFile && !omitLog ){


    struct rusage self, children;
    int waitStatus;
    const char *zRM = zRemoteUser ? zRemoteUser : "";
    const char *zFilename;




    if( zScript==0 ) zScript = "";
    if( zRealScript==0 ) zRealScript = "";
    if( zRemoteAddr==0 ) zRemoteAddr = "";
    if( zHttpHost==0 ) zHttpHost = "";
    if( zReferer==0 ) zReferer = "";
    if( zAgent==0 ) zAgent = "";
    if( zQuerySuffix==0 ) zQuerySuffix = "";




    if( zExpLogFile[0]!=0 ){
      zFilename = zExpLogFile;
    }else{
      zFilename = zLogFile;
    }
    waitpid(-1, &waitStatus, WNOHANG);
    if( inSignalHandler!=0 ){
      /* getrusage() is not signal-safe, so fall back to 0. */
      memset(&self, 0, sizeof(self));
      memset(&children, 0, sizeof(children));
    }else{
      getrusage(RUSAGE_SELF, &self);
      getrusage(RUSAGE_CHILDREN, &children);
    }
    if( (logfd = open(zFilename,O_WRONLY | O_CREAT | O_APPEND, 0640)) > 0 ){
      time_t tNow;              /* current time */
      struct timespec tsNow;    /* current time (again!) */
      struct DateTime dt;       /* high-level tNow */
      char msgbuf[5000];        /* message buffer */
      const char * const zEnd = &msgbuf[0] + sizeof(msgbuf);
      char * zPos = &msgbuf[0]; /* current write pos */

#define astr( STR ) log_str( zPos, zEnd, (STR), &zPos )
#define acomma astr(",")
#define astr2( STR ) astr( STR ); acomma
#define aint( N ) log_int( zPos, zEnd, (N), 1, &zPos ); acomma

      clock_gettime(ALTHTTPD_CLOCK_ID, &tsNow);
      time(&tNow);
      unixToDateTime(tNow, &dt);
#ifdef COMBINED_LOG_FORMAT
      /* Potential TODO: eliminate use of strftime(). Default builds
      ** of althttpd do not use COMBINED_LOG_FORMAT, so this is a
      ** low-priority issue. */
      {
        struct tm vTm/* to remove once we have a strftime() substitute */;
        char zDate[200];
#if 0
        /* Older impl, for reference: */
        fprintf(log, "%s - - [%s] \"%s %s %s\" %s %d \"%s\" \"%s\"\n",
                zRemoteAddr, zDate, zMethod, zScript, zProtocol,
                zReplyStatus, nOut, zReferer, zAgent);
#endif
        DateTime_toTm(&dt, &vTm);
        strftime(zDate, sizeof(zDate), "%d/%b/%Y:%H:%M:%S %Z", &vTm);
        astr( zRemoteAddr );
        astr( " - - [" );
        astr( zDate );
        astr( "] \"" );
        astr( zMethod );
        astr( " " );
        astr( zScript );
        astr( " " );
        astr( zProtocol );
        astr( "\" " );
        astr( zReplyStatus );
        astr( " " );
        aint( nOut );
        astr( " \"" );
        astr( zReferer );
        astr( "\" \"" );
        astr( zAgent );
        astr( "\"\n" );
      }
#else
      /* Log record files:
      **  (1) Date and time
      **  (2) IP address
      **  (3) URL being accessed
      **  (4) Referer
      **  (5) Reply status
      **  (6) Bytes received
      **  (7) Bytes sent
      **  (8) Self user time
      **  (9) Self system time
      ** (10) Children user time
      ** (11) Children system time
      ** (12) Total wall-clock time
      ** (13) Request number for same TCP/IP connection
      ** (14) User agent
      ** (15) Remote user
      ** (16) Bytes of URL that correspond to the SCRIPT_NAME
      ** (17) Line number in source file
      */
#define escstr(X) log_escstr( zPos, zEnd, X, &zPos )
      /* (1) */ log_DateTime( zPos, zEnd, &dt, &zPos );
      astr(",");

      /* (2) */ astr2( zRemoteAddr );
      /* (3) */ astr( "\"" );
      astr( zHttpScheme );
      astr( "://" );
      escstr(zHttpHost);
      escstr(zScript);
      escstr(zQuerySuffix);
      astr2( "\"" );
      /* (4) */ astr( "\"" ); escstr(zReferer); astr2("\"");
      /* (5) */ astr2( zReplyStatus );
      /* (6) */ aint( nIn );
      /* (7) */ aint( nOut );
      /* (8) */ aint( tvms(&self.ru_utime) - tvms(&priorSelf.ru_utime) );
      /* (9) */ aint( tvms(&self.ru_stime) - tvms(&priorSelf.ru_stime) );
      /* (10) */ aint( tvms(&children.ru_utime) - tvms(&priorChild.ru_utime) );
      /* (11) */ aint( tvms(&children.ru_stime) - tvms(&priorChild.ru_stime) );
      /* (12) */ aint( tsms(&tsNow) - tsms(&tsBeginTime) );
      /* (13) */ aint( nRequest );
      /* (14) */ astr( "\"" ); escstr(zAgent); astr2("\"");
      /* (15) */ astr( "\"" ); escstr(zRM); astr2("\"");
      /* (16) */ aint( strlen(zHttpScheme)+strlen(zHttpHost)+strlen(zRealScript)+3 );
      /* (17) */ log_int( zPos, zEnd, lineNum, 1, &zPos );
#undef escstr
#ifdef LOG_PID
      /* Appending of PID to the log is used only to assist in
      ** debugging of hanging althttpd processes:
      ** https://sqlite.org/althttpd/forumpost/4dc31619341ce947 */
      acomma;
      /* (18) */ log_int( zPos, zEnd, getpid(), 1, &zPos );
#endif
      astr( "\n" );
      priorSelf = self;
      priorChild = children;
#endif
      if( zPos!=0 ){
        assert( zPos<zEnd-1 );
        assert( zPos>&msgbuf[0] );
        *zPos = 0;
        write( logfd, msgbuf, (size_t)(zPos - &msgbuf[0]) );
      }
      close(logfd);
      nIn = nOut = 0;
#undef astr
#undef astr2
#undef aint
#undef acomma
    }
  }
  if( closeConnection || inSignalHandler ){
    exit(exitCode);
  }
  statusSent = 0;
}

/*
** Allocate memory safely
1235
1236
1237
1238
1239
1240
1241

1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258

1259
1260
1261
1262
1263
1264
1265
    if( zScript && zScript[0] ){
      char zBuf[10];
      zBuf[0] = '9';
      zBuf[1] = '0' + (iSig/10)%10;
      zBuf[2] = '0' + iSig%10;
      zBuf[3] = 0;
      strcpy(zReplyStatus, zBuf);

      switch( iSig ){
        case SIGALRM:
          MakeLogEntry(0, nTimeoutLine);
          break;
        case SIGSEGV:
          MakeLogEntry(0, 131);  /* LOG: SIGSEGV */
          break;
        case SIGPIPE:
          MakeLogEntry(0, 132);  /* LOG: SIGPIPE */
          break;
        case SIGXCPU:
          MakeLogEntry(0, 133);  /* LOG: SIGXCPU */
          break;
        default:
          MakeLogEntry(0, 139);  /* LOG: Unknown signal */
          break;
      }

    }
    exit(0);
  }
}

/*
** Tell the client that there is an error in the script.







>

















>







1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
    if( zScript && zScript[0] ){
      char zBuf[10];
      zBuf[0] = '9';
      zBuf[1] = '0' + (iSig/10)%10;
      zBuf[2] = '0' + iSig%10;
      zBuf[3] = 0;
      strcpy(zReplyStatus, zBuf);
      ++inSignalHandler;
      switch( iSig ){
        case SIGALRM:
          MakeLogEntry(0, nTimeoutLine);
          break;
        case SIGSEGV:
          MakeLogEntry(0, 131);  /* LOG: SIGSEGV */
          break;
        case SIGPIPE:
          MakeLogEntry(0, 132);  /* LOG: SIGPIPE */
          break;
        case SIGXCPU:
          MakeLogEntry(0, 133);  /* LOG: SIGXCPU */
          break;
        default:
          MakeLogEntry(0, 139);  /* LOG: Unknown signal */
          break;
      }
      --inSignalHandler;
    }
    exit(0);
  }
}

/*
** Tell the client that there is an error in the script.
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
static void Decode64(char *z64){
  char *zData;
  int n64;
  int i, j;
  int a, b, c, d;
  static int isInit = 0;
  static int trans[128];
  static unsigned char zBase[] = 
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  if( !isInit ){
    for(i=0; i<128; i++){ trans[i] = 0; }
    for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; }
    isInit = 1;
  }







|







1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
static void Decode64(char *z64){
  char *zData;
  int n64;
  int i, j;
  int a, b, c, d;
  static int isInit = 0;
  static int trans[128];
  static unsigned char zBase[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  if( !isInit ){
    for(i=0; i<128; i++){ trans[i] = 0; }
    for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; }
    isInit = 1;
  }
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
#ifdef ENABLE_TLS
/* This is a self-signed cert in the PEM format that can be used when
** no other certs are available.
**
** NB: Use of this self-signed cert is wildly insecure.  Use for testing
** purposes only.
*/
static const char sslSelfCert[] = 
"-----BEGIN CERTIFICATE-----\n"
"MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n"
"CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n"
"EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n"
"MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n"
"QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n"
"VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n"







|







1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
#ifdef ENABLE_TLS
/* This is a self-signed cert in the PEM format that can be used when
** no other certs are available.
**
** NB: Use of this self-signed cert is wildly insecure.  Use for testing
** purposes only.
*/
static const char sslSelfCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n"
"CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n"
"EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n"
"MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n"
"QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n"
"VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n"
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
"G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n"
"pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n"
"s/TsrXk=\n"
"-----END CERTIFICATE-----\n";

/* This is the private-key corresponding to the cert above
*/
static const char sslSelfPKey[] = 
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n"
"q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n"
"glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n"
"GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n"
"Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n"
"U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n"







|







1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
"G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n"
"pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n"
"s/TsrXk=\n"
"-----END CERTIFICATE-----\n";

/* This is the private-key corresponding to the cert above
*/
static const char sslSelfPKey[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n"
"q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n"
"glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n"
"GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n"
"Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n"
"U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n"
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
const MimeTypeDef *GetMimeType(const char *zName, int nName){
  const char *z;
  int i;
  int first, last;
  int len;
  char zSuffix[20];

  /* A table of mimetypes based on file suffixes. 
  ** Suffixes must be in sorted order so that we can do a binary
  ** search to find the mime-type
  */
  static const MimeTypeDef aMime[] = {
  { "ai",         2, 0x00, "application/postscript"           },
  { "aif",        3, 0x00, "audio/x-aiff"                     },
  { "aifc",       4, 0x00, "audio/x-aiff"                     },







|







1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
const MimeTypeDef *GetMimeType(const char *zName, int nName){
  const char *z;
  int i;
  int first, last;
  int len;
  char zSuffix[20];

  /* A table of mimetypes based on file suffixes.
  ** Suffixes must be in sorted order so that we can do a binary
  ** search to find the mime-type
  */
  static const MimeTypeDef aMime[] = {
  { "ai",         2, 0x00, "application/postscript"           },
  { "aif",        3, 0x00, "audio/x-aiff"                     },
  { "aifc",       4, 0x00, "audio/x-aiff"                     },
1980
1981
1982
1983
1984
1985
1986






1987
1988
1989
1990

1991
1992
1993
1994
1995
1996
1997
  TlsServerConn *pServer = (TlsServerConn*)pServerArg;
  SSL_free(pServer->ssl);
  memset(pServer, 0, sizeof(TlsServerConn));
  free(pServer);
}

static void tls_atexit(void){






  if( tlsState.sslCon ){
    tls_close_server(tlsState.sslCon);
    tlsState.sslCon = NULL;
  }

}
#endif /* ENABLE_TLS */


/*
** Works like fgets():
**







>
>
>
>
>
>
|



>







2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
  TlsServerConn *pServer = (TlsServerConn*)pServerArg;
  SSL_free(pServer->ssl);
  memset(pServer, 0, sizeof(TlsServerConn));
  free(pServer);
}

static void tls_atexit(void){
#if 0
  /*
  ** Shutting down TLS can lead to spurious hung processes on some
  ** platforms/builds.  See the long discussion on this at:
  ** https://sqlite.org/althttpd/forumpost/4dc31619341ce947
  */
  if( inSignalHandler==0 && tlsState.sslCon!=0 ){
    tls_close_server(tlsState.sslCon);
    tlsState.sslCon = NULL;
  }
#endif
}
#endif /* ENABLE_TLS */


/*
** Works like fgets():
**
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
      memcpy(zGzFilename + szFilename, ".gz", 4);
      if( access(zGzFilename, R_OK)==0 ){
        memset(&statbuf, 0, sizeof(statbuf));
        if( stat(zGzFilename, &statbuf)==0 ){
          zEncoding = "gzip";
          zFile = zGzFilename;
          pStat = &statbuf;
        }      
      }
    }
  }
  in = fopen(zFile,"rb");
  if( in==0 ) NotFound(480); /* LOG: fopen() failed for static content */
  if( rangeEnd>0 && rangeStart<pStat->st_size ){
    StartResponse("206 Partial Content");







|







2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
      memcpy(zGzFilename + szFilename, ".gz", 4);
      if( access(zGzFilename, R_OK)==0 ){
        memset(&statbuf, 0, sizeof(statbuf));
        if( stat(zGzFilename, &statbuf)==0 ){
          zEncoding = "gzip";
          zFile = zGzFilename;
          pStat = &statbuf;
        }
      }
    }
  }
  in = fopen(zFile,"rb");
  if( in==0 ) NotFound(480); /* LOG: fopen() failed for static content */
  if( rangeEnd>0 && rangeStart<pStat->st_size ){
    StartResponse("206 Partial Content");
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
      if( zFallback ){
        struct stat statbuf;
        int rc;
        memset(&statbuf, 0, sizeof(statbuf));
        if( chdir(zDir) ){
          char zBuf[1000];
          Malfunction(720, /* LOG: chdir() failed */
               "cannot chdir to [%s] from [%s]", 
               zDir, getcwd(zBuf,999));
        }
        rc = stat(zFallback, &statbuf);
        if( rc==0 && S_ISREG(statbuf.st_mode) && access(zFallback,R_OK)==0 ){
          closeConnection = 1;
          rc = SendFile(zFallback, (int)strlen(zFallback), &statbuf);
          free(zFallback);







|







2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
      if( zFallback ){
        struct stat statbuf;
        int rc;
        memset(&statbuf, 0, sizeof(statbuf));
        if( chdir(zDir) ){
          char zBuf[1000];
          Malfunction(720, /* LOG: chdir() failed */
               "cannot chdir to [%s] from [%s]",
               zDir, getcwd(zBuf,999));
        }
        rc = stat(zFallback, &statbuf);
        if( rc==0 && S_ISREG(statbuf.st_mode) && access(zFallback,R_OK)==0 ){
          closeConnection = 1;
          rc = SendFile(zFallback, (int)strlen(zFallback), &statbuf);
          free(zFallback);
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
**
**    *  There is a file in zIPShunDir whose name is exactly zRemoteAddr
**       and that is N bytes in size.
**
**    *  N==0 or the mtime of the file is less than N*BANISH_TIME seconds
**       ago.
**
** If N>0 and the mtime is greater than N*5*BANISH_TIME seconds 
** (25 minutes per byte, by default) old, then the file is deleted.
**
** The size of the file determines how long the embargo is suppose to
** last.  A zero-byte file embargos forever.  Otherwise, the embargo
** is for BANISH_TIME bytes for each byte in the file.
*/
static int DisallowedRemoteAddr(void){







|







2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
**
**    *  There is a file in zIPShunDir whose name is exactly zRemoteAddr
**       and that is N bytes in size.
**
**    *  N==0 or the mtime of the file is less than N*BANISH_TIME seconds
**       ago.
**
** If N>0 and the mtime is greater than N*5*BANISH_TIME seconds
** (25 minutes per byte, by default) old, then the file is deleted.
**
** The size of the file determines how long the embargo is suppose to
** last.  A zero-byte file embargos forever.  Otherwise, the embargo
** is for BANISH_TIME bytes for each byte in the file.
*/
static int DisallowedRemoteAddr(void){
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641


2642




2643




2644
2645
2646
2647
2648
2649
2650
** This routine processes a single HTTP request on standard input and
** sends the reply to standard output.  If the argument is 1 it means
** that we are should close the socket without processing additional
** HTTP requests after the current request finishes.  0 means we are
** allowed to keep the connection open and to process additional requests.
** This routine may choose to close the connection even if the argument
** is 0.
** 
** If the connection should be closed, this routine calls exit() and
** thus never returns.  If this routine does return it means that another
** HTTP request may appear on the wire.
**
** socketId must be 0 (if running via xinetd/etc) or the socket ID
** accept()ed by http_server(). It is only used for built-in TLS
** mode.
*/
void ProcessOneRequest(int forceClose, int socketId){
  int i, j, j0;
  char *z;                  /* Used to parse up a string */
  struct stat statbuf;      /* Information about the file to be retrieved */
  FILE *in;                 /* For reading from CGI scripts */
#ifdef LOG_HEADER
  FILE *hdrLog = 0;         /* Log file for complete header content */
#endif
  char zLine[10000];        /* A buffer for input lines or forming names */
  const MimeTypeDef *pMimeType = 0; /* URI's mimetype */












  /* Must see a header within 10 seconds for the first request.
  ** Allow up to 5 more minutes for the follow-on requests
  */
  if( useTimeout ){
    if( nRequest>0 ){
      SetTimeout(60*5, 801);  /* LOG: Timeout request header (1+) */
    }else{







|


















>
>

>
>
>
>
|
>
>
>
>







2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
** This routine processes a single HTTP request on standard input and
** sends the reply to standard output.  If the argument is 1 it means
** that we are should close the socket without processing additional
** HTTP requests after the current request finishes.  0 means we are
** allowed to keep the connection open and to process additional requests.
** This routine may choose to close the connection even if the argument
** is 0.
**
** If the connection should be closed, this routine calls exit() and
** thus never returns.  If this routine does return it means that another
** HTTP request may appear on the wire.
**
** socketId must be 0 (if running via xinetd/etc) or the socket ID
** accept()ed by http_server(). It is only used for built-in TLS
** mode.
*/
void ProcessOneRequest(int forceClose, int socketId){
  int i, j, j0;
  char *z;                  /* Used to parse up a string */
  struct stat statbuf;      /* Information about the file to be retrieved */
  FILE *in;                 /* For reading from CGI scripts */
#ifdef LOG_HEADER
  FILE *hdrLog = 0;         /* Log file for complete header content */
#endif
  char zLine[10000];        /* A buffer for input lines or forming names */
  const MimeTypeDef *pMimeType = 0; /* URI's mimetype */
  size_t sz = 0;
  struct tm vTm;            /* Timestamp for zExpLogFile */

  if( zLogFile ){
    assert(beginTime.tv_sec > 0);
    gmtime_r(&beginTime.tv_sec, &vTm);
    sz = strftime(zExpLogFile, sizeof(zExpLogFile), zLogFile, &vTm);
  }
  if( sz==0 || sz>=sizeof(zExpLogFile)-2 ){
    /* Invalid zExpLogFile name */
    zExpLogFile[0] = 0;
  }
  /* Must see a header within 10 seconds for the first request.
  ** Allow up to 5 more minutes for the follow-on requests
  */
  if( useTimeout ){
    if( nRequest>0 ){
      SetTimeout(60*5, 801);  /* LOG: Timeout request header (1+) */
    }else{
2666
2667
2668
2669
2670
2671
2672

2673
2674
2675
2676
2677
2678
2679
  /* Get the first line of the request and parse out the
  ** method, the script and the protocol.
  */
  omitLog = 1;
  if( althttpd_fgets(zLine,sizeof(zLine),stdin)==0 ){
    exit(0);
  }

  gettimeofday(&beginTime, 0);
  omitLog = 0;
  nIn += (i = (int)strlen(zLine));

  /* Parse the first line of the HTTP request */
  zMethod = StrDup(GetFirstElement(zLine,&z));
  zRealScript = zScript = StrDup(GetFirstElement(z,&z));







>







2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
  /* Get the first line of the request and parse out the
  ** method, the script and the protocol.
  */
  omitLog = 1;
  if( althttpd_fgets(zLine,sizeof(zLine),stdin)==0 ){
    exit(0);
  }
  clock_gettime(ALTHTTPD_CLOCK_ID, &tsBeginTime);
  gettimeofday(&beginTime, 0);
  omitLog = 0;
  nIn += (i = (int)strlen(zLine));

  /* Parse the first line of the HTTP request */
  zMethod = StrDup(GetFirstElement(zLine,&z));
  zRealScript = zScript = StrDup(GetFirstElement(z,&z));
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
    if( pipe(px) ){
      Malfunction(440, /* LOG: pipe() failed */
                  "Unable to create a pipe for the CGI program");
    }
    if( pipe(py) ){
      Malfunction(441, /* LOG: pipe() failed */
                  "Unable to create a pipe for the CGI program");
    }        

    /* Create the child process that will run the CGI. */
    if( fork()==0 ){
      /* This code is run by the child CGI process only
      ** Begin by setting up the CGI-to-althttpd pipe */
      close(1);
      if( dup(px[1])<0 ){







|







3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
    if( pipe(px) ){
      Malfunction(440, /* LOG: pipe() failed */
                  "Unable to create a pipe for the CGI program");
    }
    if( pipe(py) ){
      Malfunction(441, /* LOG: pipe() failed */
                  "Unable to create a pipe for the CGI program");
    }

    /* Create the child process that will run the CGI. */
    if( fork()==0 ){
      /* This code is run by the child CGI process only
      ** Begin by setting up the CGI-to-althttpd pipe */
      close(1);
      if( dup(px[1])<0 ){
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
      /* Close all surplus file descriptors */
      for(i=3; close(i)==0; i++){}

      /* Move into the directory holding the CGI program */
      if( chdir(zDir) ){
        char zBuf[1000];
        Malfunction(445, /* LOG: chdir() failed */
             "CGI cannot chdir to [%s] from [%s]", 
             zDir, getcwd(zBuf,999));
      }

      /* Setup the CGI environment appropriately. */
      ComputeRequestUri();
      putenv("GATEWAY_INTERFACE=CGI/1.0");
      for(i=0; i<(int)(sizeof(cgienv)/sizeof(cgienv[0])); i++){







|







3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
      /* Close all surplus file descriptors */
      for(i=3; close(i)==0; i++){}

      /* Move into the directory holding the CGI program */
      if( chdir(zDir) ){
        char zBuf[1000];
        Malfunction(445, /* LOG: chdir() failed */
             "CGI cannot chdir to [%s] from [%s]",
             zDir, getcwd(zBuf,999));
      }

      /* Setup the CGI environment appropriately. */
      ComputeRequestUri();
      putenv("GATEWAY_INTERFACE=CGI/1.0");
      for(i=0; i<(int)(sizeof(cgienv)/sizeof(cgienv[0])); i++){
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
    }
    if( zPostData ){
       free(zPostData);
       zPostData = 0;
       nPostData = 0;
    }
    close(py[1]);
        
    /* Wait for the CGI program to reply and process that reply */
    if( in==0 ){
      CgiError();
    }else{
      CgiHandleReply(in, strncmp(zBaseFilename,"nph-",4)==0);
    }
  }else if( lenFile>5 && strcmp(&zFile[lenFile-5],".scgi")==0 ){







|







3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
    }
    if( zPostData ){
       free(zPostData);
       zPostData = 0;
       nPostData = 0;
    }
    close(py[1]);

    /* Wait for the CGI program to reply and process that reply */
    if( in==0 ){
      CgiError();
    }else{
      CgiHandleReply(in, strncmp(zBaseFilename,"nph-",4)==0);
    }
  }else if( lenFile>5 && strcmp(&zFile[lenFile-5],".scgi")==0 ){
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
  /* Get information about the user if available */
  if( zPermUser ) pwd = getpwnam(zPermUser);
  else if( getuid()==0 ){
    Malfunction(518, "Cannot run as root. Use the -user USER flag.");
    return 1;
  }

  /* Enter the chroot jail if requested */  
  if( zPermUser && useChrootJail && getuid()==0 ){
    if( chroot(".")<0 ){
      Malfunction(519, /* LOG: chroot() failed */
                  "unable to create chroot jail");
    }else{
      zRoot = "";
    }







|







3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
  /* Get information about the user if available */
  if( zPermUser ) pwd = getpwnam(zPermUser);
  else if( getuid()==0 ){
    Malfunction(518, "Cannot run as root. Use the -user USER flag.");
    return 1;
  }

  /* Enter the chroot jail if requested */
  if( zPermUser && useChrootJail && getuid()==0 ){
    if( chroot(".")<0 ){
      Malfunction(519, /* LOG: chroot() failed */
                  "unable to create chroot jail");
    }else{
      zRoot = "";
    }