Althttpd

Changes On Branch signal-after-exit
Login

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

Changes In Branch signal-after-exit Excluding Merge-Ins

This is equivalent to a diff from c482a5e04d to 092234da85

2024-02-08
08:43
Improve the handling of a signal after exit() is called, as that leads to undefined results in some libc implementations. We now keep track of whether exit() has been called and skip signal handling if a signal is triggered after that. ... (check-in: b60cc28c07 user: stephan tags: trunk)
2024-01-26
03:17
Rework exit() calls so that Timeout() cannot be run after exit(). This is an attempt to work around the problem reported in /forumpost/9947114e61. ... (Closed-Leaf check-in: 092234da85 user: stephan tags: signal-after-exit)
2024-01-17
14:40
Audit recent log changes, making improvements to comments. Add mention of the automatic content compression feature to the feature list in the main header comment. Identify ".md" files has text/plain. ... (check-in: c482a5e04d user: drh tags: trunk)
12:27
Rename LOG_PID to ALTHTTPD_LOG_PID because LOG_PID is defined indirectly by syslog.h, causing it to always be defined on some builds. ... (check-in: a0a5f93c36 user: stephan tags: trunk)

Changes to althttpd.c.
462
463
464
465
466
467
468

469
470
471
472
473
474
475
476
477
478
479
462
463
464
465
466
467
468
469
470
471
472

473
474
475
476
477
478
479







+



-







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 */
static int isExiting = 0;        /* True when althttpd_exit() has been called */

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



#ifdef ENABLE_TLS
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
629
630
631
632
633
634
635
636



637
638



639
640
641

642
643
644
645
646
647
648
629
630
631
632
633
634
635

636
637
638
639

640
641
642
643
644

645
646
647
648
649
650
651
652







-
+
+
+

-
+
+
+


-
+








/* forward references */
static int tls_init_conn(int iSocket);
static void tls_close_conn(void);
static void althttpd_fflush(FILE *f);

/*
** Flush the buffer then exit.
** Flush stdout then exit() with the given result code.  This must
** always be used instead of exit() so that a corner case involving
** post-exit() signal handling via Timeout() can be accounted for.
*/
static void althttpd_exit(void){
static void althttpd_exit(int iErrCode){
  assert( isExiting == 0 );
  isExiting = iErrCode ? iErrCode : 1;
  althttpd_fflush(stdout);
  tls_close_conn();
  exit(0);
  exit(iErrCode);
}

/*
** Mapping between CGI variable names and values stored in
** global variables.
*/
static struct {
1465
1466
1467
1468
1469
1470
1471
1472

1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
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
1469
1470
1471
1472
1473
1474
1475

1476
1477
1478
1479
1480
1481
1482
1483
1484
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
1518
1519







-
+




















-
+














-
+







    "Content-type: text/plain; charset=utf-8\r\n"
    "\r\n"
    "Service to IP address %s temporarily blocked due to abuse\n",
    zRemoteAddr
  );
  closeConnection = 1;
  MakeLogEntry(0, lineno);
  althttpd_exit();
  althttpd_exit(0);
}

/*
** Tell the client that there is no such document
*/
static void NotFound(int lineno){
  if( LikelyHackAttempt() ){
    BlockIPAddress();
    ServiceUnavailable(lineno);
  }
  UnlinkExpiredIPBlockers();
  StartResponse("404 Not Found");
  nOut += althttpd_printf(
    "Content-type: text/html; charset=utf-8\r\n"
    "\r\n"
    "<head><title lineno=\"%d\">Not Found</title></head>\n"
    "<body><h1>Document Not Found</h1>\n"
    "The document %s is not available on this server\n"
    "</body>\n", lineno, zScript);
  MakeLogEntry(0, lineno);
  althttpd_exit();
  althttpd_exit(0);
}

/*
** Tell the client that they are not welcomed here.
*/
static void Forbidden(int lineno){
  StartResponse("403 Forbidden");
  nOut += althttpd_printf(
    "Content-type: text/plain; charset=utf-8\r\n"
    "\r\n"
    "Access denied\n"
  );
  closeConnection = 1;
  MakeLogEntry(0, lineno);
  althttpd_exit();
  althttpd_exit(0);
}

/*
** Tell the client that authorization is required to access the
** document.
*/
static void NotAuthorized(const char *zRealm){
1534
1535
1536
1537
1538
1539
1540
1541

1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557


1558
1559
1560

1561
1562
1563
1564
1565
1566
1567
1538
1539
1540
1541
1542
1543
1544

1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565

1566
1567
1568
1569
1570
1571
1572
1573







-
+
















+
+


-
+







    "Content-type: text/html; charset=utf-8\r\n"
    "\r\n"
    "<head><title>CGI Program Error</title></head>\n"
    "<body><h1>CGI Program Error</h1>\n"
    "The CGI program %s generated an error\n"
    "</body>\n", zScript);
  MakeLogEntry(0, 120);  /* LOG: CGI Error */
  althttpd_exit();
  althttpd_exit(0);
  exit(0);
}

/*
** Set the timeout in seconds.  0 means no-timeout.
*/
static void SetTimeout(int nSec, int lineNum){
  if( useTimeout ){
    nTimeoutLine = lineNum;
    alarm(nSec);
  }
}

/*
** This is called if we timeout or catch some other kind of signal.
** Log an error code which is 900+iSig and then quit.
**
** If called after althttpd_exit(), this is a no-op.
*/
static void Timeout(int iSig){
  if( !debugFlag ){
  if( debugFlag==0 && isExiting==0 ){
    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);
1581
1582
1583
1584
1585
1586
1587
1588

1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624

1625
1626
1627
1628
1629
1630
1631
1587
1588
1589
1590
1591
1592
1593

1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608

1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629

1630
1631
1632
1633
1634
1635
1636
1637







-
+














-
+




















-
+







          break;
        default:
          MakeLogEntry(0, 139);  /* LOG: Unknown signal */
          break;
      }
      --inSignalHandler;
    }
    exit(0);
    althttpd_exit(0);
  }
}

/*
** Tell the client that there is an error in the script.
*/
static void CgiScriptWritable(void){
  StartResponse("500 CGI Configuration Error");
  nOut += althttpd_printf(
    "Content-type: text/plain; charset=utf-8\r\n"
    "\r\n"
    "The CGI program %s is writable by users other than its owner.\n",
    zRealScript);
  MakeLogEntry(0, 140);  /* LOG: CGI script is writable */
  althttpd_exit();
  althttpd_exit(0);
}

/*
** Tell the client that the server malfunctioned.
*/
void Malfunction(int linenum, const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
  StartResponse("500 Server Malfunction");
  nOut += althttpd_printf(
    "Content-type: text/plain; charset=utf-8\r\n"
    "\r\n"
    "Web server malfunctioned; error number %d\n\n", linenum);
  if( zFormat ){
    nOut += althttpd_vprintf(zFormat, ap);
    althttpd_printf("\n");
    nOut++;
  }
  va_end(ap);
  MakeLogEntry(0, linenum);
  althttpd_exit();
  althttpd_exit(0);
}

/*
** Do a server redirect to the document specified.  The document
** name not contain scheme or network location or the query string.
** It will be just the path.
*/
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2315
2316
2317
2318
2319
2320
2321

2322
2323
2324
2325
2326
2327
2328
2329
2330

2331
2332
2333
2334
2335
2336
2337







-









-







  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():
**
2787
2788
2789
2790
2791
2792
2793
2794

2795
2796
2797
2798
2799
2800
2801
2791
2792
2793
2794
2795
2796
2797

2798
2799
2800
2801
2802
2803
2804
2805







-
+







               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);
          althttpd_exit();
          althttpd_exit(0);
        }else{
          Malfunction(706, /* LOG: bad SCGI fallback */
             "bad fallback file: \"%s\"\n", zFallback);
        }
      }
      Malfunction(707, /* LOG: Cannot open socket to SCGI */
           "cannot open socket to SCGI server %s\n",
3010
3011
3012
3013
3014
3015
3016
3017

3018
3019
3020
3021
3022
3023
3024
3014
3015
3016
3017
3018
3019
3020

3021
3022
3023
3024
3025
3026
3027
3028







-
+







  tls_init_conn(socketId);

  /* 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);
    althttpd_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 */
3044
3045
3046
3047
3048
3049
3050
3051

3052
3053
3054
3055
3056
3057
3058
3048
3049
3050
3051
3052
3053
3054

3055
3056
3057
3058
3059
3060
3061
3062







-
+







      nOut += althttpd_printf(
        "Content-type: text/plain; charset=utf-8\r\n"
        "\r\n"
        "URI too long\n"
      );
      MakeLogEntry(0, 201); /* LOG: bad protocol in HTTP header */
    }
    althttpd_exit();
    althttpd_exit(0);
  }
  if( zScript[0]!='/' ) NotFound(210); /* LOG: Empty request URI */
  while( zScript[1]=='/' ){
    zScript++;
    zRealScript++;
  }
  if( forceClose ){
3069
3070
3071
3072
3073
3074
3075
3076

3077
3078
3079
3080
3081
3082
3083
3073
3074
3075
3076
3077
3078
3079

3080
3081
3082
3083
3084
3085
3086
3087







-
+







    StartResponse("501 Not Implemented");
    nOut += althttpd_printf(
      "Content-type: text/plain; charset=utf-8\r\n"
      "\r\n"
      "The %s method is not implemented on this server.\n",
      zMethod);
    MakeLogEntry(0, 220); /* LOG: Unknown request method */
    althttpd_exit();
    althttpd_exit(0);
  }

  /* If there is a log file (if zLogFile!=0) and if the pathname in
  ** the first line of the http request contains the magic string
  ** "FullHeaderLog" then write the complete header text into the
  ** file %s(zLogFile)-hdr.  Overwrite the file.  This is for protocol
  ** debugging only and is only enabled if althttpd is compiled with
3269
3270
3271
3272
3273
3274
3275
3276

3277
3278
3279
3280
3281
3282
3283
3273
3274
3275
3276
3277
3278
3279

3280
3281
3282
3283
3284
3285
3286
3287







-
+







      StartResponse("500 Request too large");
      nOut += althttpd_printf(
        "Content-type: text/plain; charset=utf-8\r\n"
        "\r\n"
        "Too much POST data\n"
      );
      MakeLogEntry(0, 270); /* LOG: Request too large */
      althttpd_exit();
      althttpd_exit(0);
    }
    rangeEnd = 0;
    zPostData = SafeMalloc( len+1 );
    SetTimeout(15 + len/2000, 803);  /* LOG: Timeout POST data */
    nPostData = althttpd_fread(zPostData,1,len,stdin);
    nIn += nPostData;
  }
3524
3525
3526
3527
3528
3529
3530
3531

3532
3533
3534
3535
3536
3537
3538
3528
3529
3530
3531
3532
3533
3534

3535
3536
3537
3538
3539
3540
3541
3542







-
+







        if( *cgienv[i].pzEnvValue ){
          SetEnv(cgienv[i].zEnvName,*cgienv[i].pzEnvValue);
        }
      }

      /* Run the CGI program */
      execl(zBaseFilename, zBaseFilename, (char*)0);
      exit(0);  /* Not reached */
      althttpd_exit(0);  /* Not reached */
    }

    /* This parent process.  The child has been started.
    ** Set up the CGI-to-althttp pipe on which to receive the reply
    */
    close(px[1]);
    in = fdopen(px[0], "rb");
3603
3604
3605
3606
3607
3608
3609
3610

3611
3612
3613
3614
3615
3616
3617
3607
3608
3609
3610
3611
3612
3613

3614
3615
3616
3617
3618
3619
3620
3621







-
+







  if( strlen(zPath)<=sizeof(zUrl)-1000 ){
    while( zPath[0]=='/' ) zPath++;
    sprintf(zUrl, "http://localhost:%d/%s", iPort, zPath);
    for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){
      execlp(azBrowserProg[i], azBrowserProg[i], zUrl, (char*)0);
    }
  }
  exit(1);
  althttpd_exit(1);
}

#define MAX_PARALLEL 50  /* Number of simultaneous children */

/*
** All possible forms of an IP address.  Needed to work around GCC strict
** aliasing rules.
3681
3682
3683
3684
3685
3686
3687
3688

3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702

3703
3704
3705
3706
3707
3708
3709
3685
3686
3687
3688
3689
3690
3691

3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705

3706
3707
3708
3709
3710
3711
3712
3713







-
+













-
+







  if( iPort>mxPort ){
    if( mnPort==mxPort ){
      fprintf(stderr,"unable to open listening socket on port %d\n", mnPort);
    }else{
      fprintf(stderr,"unable to open listening socket on any"
                     " port in the range %d..%d\n", mnPort, mxPort);
    }
    exit(1);
    althttpd_exit(1);
  }
  if( iPort>mxPort ) return 1;
  listen(listener,10);
  printf("Listening for %s requests on TCP port %d\n",
         useHttps?"TLS-encrypted HTTPS":"HTTP",  iPort);
  fflush(stdout);
  if( zPage ){
    child = fork();
    if( child!=0 ){
      if( child>0 ) nchildren++;
    }else{
      launch_web_browser(zPage, iPort);
      /* NOT REACHED */
      exit(1);
      althttpd_exit(1);
    }
  }
  while( 1 ){
    if( nchildren>MAX_PARALLEL ){
      /* Slow down if connections are arriving too fast */
      sleep( nchildren-MAX_PARALLEL );
    }
3739
3740
3741
3742
3743
3744
3745
3746

3747
3748
3749
3750
3751
3752
3753
3743
3744
3745
3746
3747
3748
3749

3750
3751
3752
3753
3754
3755
3756
3757







-
+







    /* Bury dead children */
    while( (child = waitpid(0, 0, WNOHANG))>0 ){
      /* printf("process %d ends\n", child); fflush(stdout); */
      nchildren--;
    }
  }
  /* NOT REACHED */
  exit(1);
  althttpd_exit(1);
}

int main(int argc, const char **argv){
  int i;                     /* Loop counter */
  const char *zPermUser = 0; /* Run daemon with this user's permissions */
  int mnPort = 0;            /* Range of TCP ports for server mode */
  int mxPort = 0;
3883
3884
3885
3886
3887
3888
3889
3890

3891
3892
3893
3894
3895
3896
3897
3887
3888
3889
3890
3891
3892
3893

3894
3895
3896
3897
3898
3899
3900
3901







-
+







    if( strcmp(z, "-version")==0 ){
      puts(SERVER_SOFTWARE_TLS);
      return 0;
    }else
    if( strcmp(z, "-datetest")==0 ){
      TestParseRfc822Date();
      printf("Ok\n");
      exit(0);
      althttpd_exit(0);
    }else
    if( strcmp(z,"-remote-addr")==0 ){
      /* Used for testing purposes only - to simulate a remote IP address when
      ** input is really coming from a disk file. */
      zRemoteAddr = StrDup(zArg);
    }else
    {
4019
4020
4021
4022
4023
4024
4025
4026

4027
4028
4029
4030
4031
4032
4033
4023
4024
4025
4026
4027
4028
4029

4030
4031
4032
4033
4034
4035
4036
4037







-
+








  /* Process the input stream */
  for(i=0; i<100; i++){
    ProcessOneRequest(0, httpConnection);
  }
  ProcessOneRequest(1, httpConnection);
  tls_close_conn();
  exit(0);
  althttpd_exit(0);
}

#if 0
/* Copy/paste the following text into SQLite to generate the xref
** table that describes all error codes.
*/
BEGIN;