Back to terttu 1: Hello world server

Kaikki oikeudet pidätetään ©. Artikkelissa syntyvä ohjelma on kokeiltavana täällä https://moijari.com:5008/.

Jos pidät sudokujen ratkaisusta niitä löytyy posteista: https://moijari.com/?p=2930 ja https://moijari.com/?p=1874.

Jos taas satunnaisbitit kiinnostavat postissa https://moijari.com/?p=1874 esitellään satunnaisbittigeneraattori ressu. Ensimmäinen maininta ressu satunnaisbittigeneraattorista: https://moijari.com/?p=62.

Tämä artikkeli nojaa aika paljon artikkeleissa https://moijari.com/?p=2930 ja https://moijari.com/?p=1874 kehitettyyn db8 ohjelmaan.

Seuraavaksi kokeilen taas tehdä palvelimen, joka lukee seuraavaa rakennetta. Edellinen yritys oli tälläinen: https://moijari.com/?p=1799 ja tälläinen https://moijari.com/?p=1256. Tämä on taas pidempi artikkeli tai artikkelisarja.

'memberid' = "customerid", 'memberapp' = "customerid"
'memberid' = "countryid", 'memberapp' = "countryid"
'memberid' = "productid", 'memberapp' = "productid"
'memberid' = "billid", 'memberapp' = "billid"
'appid' = "country", 'chapter' = "header", 'memberid' = "countryid"
'appid' = "country", 'chapter' = "header", 'memberid' = "countryname"
'countryid' = "fi", 'countryname' = "Finland"
'appid' = "company", 'chapter' = "header", 'memberid' = "companyid"
'appid' = "company", 'chapter' = "header", 'memberid' = "companyname"
'appid' = "company", 'chapter' = "header", 'memberid' = "companyaddress"
'appid' = "company", 'chapter' = "header", 'memberid' = "companyzip"
'appid' = "company", 'chapter' = "header", 'memberid' = "companycountry"
'appid' = "company", 'chapter' = "header", 'memberid' = "fiscalyear"
'appid' = "company", 'chapter' = "header", 'memberid' = "fiscalperiod"
'companyid' = "1", 'companyname' = "Vene Oy", 'companyaddress' = "venekatu 1", 'companyzip' = "00500", 'companycountry' = "fi", 'fiscalyear' = "2025", 'fiscalperiod' = "1"
'appid' = "customer", 'chapter' = "header", 'memberid' = "customerid"
'appid' = "customer", 'chapter' = "header", 'memberid' = "customername"
'appid' = "customer", 'chapter' = "header", 'memberid' = "customeraddress"
'appid' = "customer", 'chapter' = "header", 'memberid' = "customerzip"
'appid' = "customer", 'chapter' = "header", 'memberid' = "customercountry"
'customerid' = "1", 'customername' = "Asiakas 1", 'customeraddress' = "asiakaskatu 1", 'customerzip' = "00500", 'customercountry' = "fi"
'customerid' = "2", 'customername' = "Asiakas 2", 'customeraddress' = "asiakaskatu 2", 'customerzip' = "00500", 'customercountry' = "fi"
'customerid' = "3", 'customername' = "Asiakas 3", 'customeraddress' = "asiakaskatu 3", 'customerzip' = "00500", 'customercountry' = "fi"
'appid' = "product", 'chapter' = "header", 'memberid' = "productid"
'appid' = "product", 'chapter' = "header", 'memberid' = "productname"
'appid' = "product", 'chapter' = "header", 'memberid' = "productprice"
'productid' = "1", 'productname' = "Vene", 'productprice' = "100"
'productid' = "2", 'productname' = "Airot", 'productprice' = "10"
'productid' = "3", 'productname' = "Tappi", 'productprice' = "5"
'appid' = "bill", 'chapter' = "header", 'memberid' = "billid"
'appid' = "bill", 'chapter' = "header", 'memberid' = "companyid"
'appid' = "bill", 'chapter' = "header", 'memberid' = "customerid"
'appid' = "bill", 'chapter' = "header", 'memberid' = "customername"
'appid' = "bill", 'chapter' = "header", 'memberid' = "total"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "billid"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "billlineid"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "productid"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "productname"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "productprice"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "quantity"
'appid' = "bill", 'chapter' = "lines", 'memberid' = "amount"
'billid' = "1", 'companyid' = "1", 'customerid' = "1", 'customername' = "Asiakas 1", 'total' = "115"
'billid' = "1", 'billlineid' = "1", 'productid' = "1", 'productname' = "Vene", 'productprice' = "100", 'quantity' = "1", 'amount' = "100"
'billid' = "1", 'billlineid' = "2", 'productid' = "1", 'productname' = "Airot", 'productprice' = "10", 'quantity' = "1", 'amount' = "10"
'billid' = "1", 'billlineid' = "3", 'productid' = "1", 'productname' = "Tappi", 'productprice' = "5", 'quantity' = "1", 'amount' = "5"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "transactionid"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "companyid"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "customerid"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "customername"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "billid"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "fiscalyear"
'appid' = "transaction", 'chapter' = "header", 'memberid' = "fiscalperiod"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "transactionid"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "transactionlineid"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "productid"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "productprice"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "quantity"
'appid' = "transactionline", 'chapter' = "lines", 'memberid' = "amount"
'transactionid' = "1", 'companyid' = "1", 'customerid' = "1", 'customername' = "Asiakas 1", 'billid' = "1", 'fiscalyear' = "2025", 'fiscalperiod' = "1"
'transactionid' = "1", 'transactionlineid' = "1", 'productid' = "1", 'productprice' = "100", 'quantity' = "1", 'amount' = "100"
'transactionid' = "1", 'transactionlineid' = "2", 'productid' = "2", 'productprice' = "10", 'quantity' = "1", 'amount' = "10"
'transactionid' = "1", 'transactionlineid' = "3", 'productid' = "3", 'productprice' = "5", 'quantity' = "1", 'amount' = "5"

Edellisessä scriptissä on maat (country), yritys (company), asiakas (customer), tuote (product), lasku(bill) ja liiketapahtuma (transaction). Tämä on tälläinen yksinkertaistettu malli, jossa syötetään lasku, keräillään sen perusteella (ei tilausta, toimitusta, keräilyä jne.). Samoin liiketapahtumat perustuu yksinkertaiseen kirjanpitoon. Ajatus on että näiden perusteella voin miettiä mitä toimintoja terttuun tarvitaan.

Lukijalle jää mahdollinen tilauksen lisääminen, osto- ja myyntireskontra, laskujen maksaminen, liiketapahtumien muutos kaksinkertaiseksi, kirjanpidon tilien lisääminen, tilien saldot, tulos ja taselaskelma, ja mitä muuta?

Itse jäin pohtimaan pitäisikö liiketapahtumien olla kuvattu kuten laskutapahtumat (bill) ilman erillistä lines appia.

Jos pidät artikkeleistani, tarjoa vissy.

Ote lokista

Tässä ote https-lokista: ajo alkaa starting dbs3:lla rivillä, lapsiprosessin suoritus loppuu unlink rivillä.

20250829124115UTC starting dbs3 "DBS3 version 0.05 ©", port:5008, https
20250829124115UTC cert       xx xx xx xx ...
20250829124115UTC private    xx xx xx xx ...
20250829124115UTC server_https_init()
20250829124115UTC SSL_library_init()
20250829124115UTC OpenSSL_add_ssl_algorithms()
20250829124115UTC OpenSSL_add_ciphers()
20250829124115UTC OpenSSL_load_error_strings()
20250829124115UTC SSLv23_server_method()
20250829124115UTC SSL_CTX_new()
20250829124115UTC SSL_CTX_use_certificate_file()
20250829124115UTC SSL_CTX_use_PrivateKey_file()
20250829124115UTC cert       xx xx xx xx ...
20250829124115UTC private    xx xx xx xx ...
20250829124115UTC SSL_CTX_load_verify_locations()
20250829124115UTC server_basic_socket()
20250829124115UTC server_getaddrinfo()
20250829124115UTC getaddrinfo()
20250829124115UTC server_socket()
20250829124115UTC socket()
20250829124115UTC server_bind()
20250829124115UTC bind()
20250829124115UTC freeaddrinfo()
20250829124115UTC server_listen()
20250829124115UTC listen()
20250829124115UTC accept()
20250829124132UTC dbs_time_vars()
20250829124132UTC Fork start (parent) getpid:22877 getppid:1
20250829124132UTC fork()
20250829124132UTC Fork end (parent) pid:22884 getpid:22877 getppid:1
20250829124132UTC server_close()
20250829124132UTC init_child()
20250829124132UTC close()
20250829124132UTC close(s)
20250829124132UTC cert       xx xx xx xx ...
20250829124132UTC close(s)
20250829124132UTC private    xx xx xx xx ...
20250829124132UTC https_client()
20250829124132UTC accept()
20250829124132UTC SSL_new()
20250829124132UTC SSL_set_fd()
20250829124132UTC SSL_accept()
20250829124132UTC SSL_get_peer_certificate(), No peer certificate
20250829124132UTC SSL_read()
20250829124132UTC SSL_read(), retval 497, errno: 0, SSL_get_error(): 0, ERR_get_error(): 0
20250829124132UTC (497 bytes)
20250829124132UTC GET / HTTP/1.1
Host: moijari.com:5008
Sec-Fetch-Dest: document
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Mobile/15E148 Safari/604.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Language: en-GB,en;q=0.9
Priority: u=0, i
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=xxxxxxxxxx
Connection: keep-alive

neTable
20250829124132UTC 1 reads, received 497 chars, read 497 total bytes, input buffer size 2048 chars, data="
20250829124132UTC "
20250829124132UTC enter dba_loop()
20250829124132UTC entering dba_loop()
20250829124132UTC fetching cookies
20250829124132UTC htmlsessionid: "", htmlmode: ""
20250829124132UTC , htmllanguage: ""
20250829124132UTC end fetching cookies
20250829124132UTC html prettyprinter
20250829124132UTC buffers1: 0:96 1:1423 2:0 3:0 4:116 5:0 6:0 7:0 8:0
20250829124132UTC buffers2: 0:96 1:0 2:0 3:0 4:0 5:0 6:0 7:1539 8:0
20250829124132UTC buffers3: 0:96 1:0 2:0 3:0 4:0 5:0 6:0 7:0 8:1624
20250829124132UTC html prettyprinter done.
20250829124132UTC exit dba_loop2()
20250829124132UTC entering dba_loop2()
20250829124132UTC exiting dba_loop2()
20250829124132UTC buffers(ssl): 0:194SSL_write()
20250829124132UTC  1:0 2:0 3:0 4:0 5:0 6:0 7:0 8:1624SSL_write()
20250829124132UTC HTTP/1.0 200 OK
Location: 
Server: DBS3 version 0.05 ©
Date: Fri, 29 Aug 2025 12:41:32 GMT
Content-Length: 1624
SHA256: 4225cf245da9717dbbb1e66b202805d0ea31ee98d1fe9b3b8e8893495217b079



20250829124132UTC <!doctype html>
<html lang="fi">
	<head>
		<meta charset="utf-8">
		<title>dbs3</title>
		<meta name="author" content="Jari Kuivaniemi">
		<meta name="copyright" content="Jari Kuivaniemi">
		<meta name="format-detection" content="telephone=no">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
	</head>
	<body>
		<h1>Hello world!</h1>
		<p>Tämä on artikkelin
			<a href="https://moijari.com?p=2990">https://moijari.com?p=2990</a>
			testiohjelma. Ohjelma löytyy tästä linkistä
			<a href="https://moijari.com:5008">https://moijari.com:5008</a>.
		</p>
		<p>Seuraava kappale sisältää ressun tekemiä satunnaisbittejä. Ne ovat tässä vain tilan
		täytteeksi.</p>
		<code>00000 23496204191267746385650998012493370329155612974412020696891006400<br>
			00001 81315112026847649628221047771393379330971371284815590598140828693<br>
			00002 09250495208359184684622339401095353123798026687190612299578835807<br>
			00003 39895651168692313578558900101514362742417486681916530680174028012<br>
			00004 43879787855486291367847076047068243400013972222145074097633335417<br>
			00005 26303631154312243590394980530961208470172763830445300529165868566<br>
			00006 28148095820594368521769534523925829599397263973785456067376022954<br>
			00007 95421233364147892184067037589123418391962531136278187653055531215<br>
			00008 68641279150178930211392158508201074572103126263767098469272190235<br>
			00009 48173903581263922479628299914545018098718999337811637544143446315<br>
		</code>
		<p>DBS3 version 0.05 ©,
		sha256(03e1c9da9ee20148512545f61c7c8dae54db63aa88f57cc86b613cb058651cb3)</p>
	</body>
</html>
20250829124132UTC SSL connection using TLS_AES_128_GCM_SHA256
20250829124132UTC dbs_query_vars()
20250829124132UTC SSL_free()
20250829124132UTC critical_wait()
20250829124132UTC critical_post()
20250829124132UTC server_close(news)
20250829124132UTC close()
20250829124132UTC Fork end (child) pid:0 getpid:22884 getppid:22877
20250829124132UTC exit_child()
20250829124132UTC unlink dbs3pid22884.log

Pääohjelma main()

Pääohjelma lukee komentoriviparametrit –http, –https ja –port ja käynnistää niiden perusteella http- tai https-palvelimen oikealla porttinumerolla. Oletuksena luodaan http palvelin porttinumerolla 5000.

Lisäksi ohjelma muodostaa tämän suoritettavan tiedoston tiivisteen, joka tulostetaan aina sivun loppuun (htmldigest).

int http = 1;
int https = 0;

void main(int argc, char *argv[])
{
  int c;

  procname = argv[0];

  unsigned char filedigest[HashLen];

  dbs_file_digest("/proc/self/exe", filedigest);
  htmldigest[0] = '\0';
  for(int c = 0; c < HashLen; c++) {
    char twodigits[3];
    sprintf(twodigits, "%02x", filedigest[c]);
    strcat(htmldigest, twodigits);
  }

  //
  // look thru command line parameters
  //

  for(c = 1; c < argc; c++) {

    if(!strncmp("-", argv[c], 1)) {
      if(!strncmp("--port", argv[c], 6)) {
	if(*(argv[c] + 6) != '\0')
	  myport = argv[c] + 6;
	else if(c + 1 < argc) {
	  myport = argv[c + 1];
	  c++;
	}

      } else if(!strcmp("--https", argv[c])) {
	https = 1;
	http = 0;

      } else if(!strcmp("--http", argv[c])) {
	http = 1;
	https = 0;

      } else {
	log_errprintf("%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }      
    }
  }

  log_errprintf("starting dbs3 \"%s\"", programname);
  log_errprintf(", port:%s", myport);
  if(http)
    log_errprintf(", http");
  if(https)
    log_errprintf(", https");
  log_errprintf("\n");


  if(http)
    http_server();
  if(https)
    https_server();
}

Http palvelin

Http palvelin ajaa tarvittavia palvelimen käynnistykseen liittyviä rutiineja server_basic_socket() rutiinilla ja luo uuden prosessin fork():illa lapsiprosessia varten.

static void http_server()
{
  int quit, reset, addr_size;
  struct sockaddr_in sa_cli;

  signal(SIGPIPE, SIG_IGN);
  signal(SIGCHLD, SIG_IGN);

  // plus space for '\0'
  htmlin = malloc(htmlinlen + 1);
  
  reset = 1;
  quit = 0;
  
  for(;;) {

    log_mode = 1;
    
    if(quit) {
      break;
    }

    if(reset) {
      if(s != -1)
	close(s);
      log_printf("server_basic_socket()\n");
      s = server_basic_socket();
      reset = 0;
    }
  
    fprintf(stdout,"\n");
    fflush(stdout);
    
    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      log_errprintf("%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

    fprintf(stdout,"========================================\n");
    log_printf("dbs_time_vars()\n"); 
    dbs_time_vars();
    
    if(beepaccept) {
      fprintf(stderr,"\a\n");
      fflush(stderr);
    }
    
    pid_t pid;
    log_printf("Fork start (parent)");
    log_printf(" getpid:%d getppid:%d", getpid(), getppid()); 
    log_printf("\n"); 

    log_printf("fork()\n"); 
    if((pid = fork()) < 0) {

      log_errprintf("%s: cannot fork()", procname);
      log_errprintf(", errno %d", errno);
      log_errprintf( "(%s)\n", strerror(errno));
      fflush(stderr);
      
      perror("fork");
      fflush(stderr);

      exit(1);
    } else if(pid == 0) { // child

      log_printf("init_child()\n"); 
      init_child();

      log_mode = 2; // child
            
      log_printf("close(s)\n"); 
      close(s);
      
      log_printf("Fork start (child)");
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
      log_printf("\n");

      log_printf("close(s)\n");
      close(s);

      log_printf("http_client()\n"); 
      http_client(news, sa_cli, addr_size, "http");

      dbs_run_critical_sections(); 
     
      log_printf("server_close(news)\n"); 
      server_close(news);

      log_printf("Fork end (child)"); 
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid()); 
      log_printf("\n"); 

      log_printf("exit_child()\n"); 
      exit_child();

      log_mode = 1;
      
      exit(0);
    }
    log_printf("Fork end (parent)"); 
    log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
    log_printf("\n");

    log_printf("server_close()\n"); 
    server_close(news);
  } // for(;;)
}

server_basic_socket()

Seuraavaa rutiinia käytetään sekä http että https palvelimessa.

static int server_basic_socket()
{
  int s;
  struct addrinfo *res;

  log_printf("server_getaddrinfo()\n"); 
  server_getaddrinfo(myport, &res);
  log_printf("server_socket()\n"); 
  s = server_socket(res);
  log_printf("server_bind()\n"); 
  server_bind(s, res);
  log_printf("freeaddrinfo()\n"); 
  freeaddrinfo(res);
  log_printf("server_listen()\n"); 
  server_listen(s);

  return(s);
}

Http client

Http client lukee asiakkaan pyynnön read():lla, suorittaa asiakasohjelman dbs_run_loop():lla, ja kirjoittaa tulokset write():lla.

static void http_client(int news, struct sockaddr_in sa_cli, int addr_size, char *name)
{
  int clen = 0;
  int reads = 0;
  int first = 1;
  int bytes, totalbytes = 0;
  unsigned char buffer10[10];

  htmlparams = NULL;
  htmlin[0] = '\0';

  dbs_server_vars(sa_cli, addr_size, name);

  while(htmlparams == NULL ||
	clen - strlen(htmlparams) > 0) {
    if(!first) {
      log_printf(", ");
    }

    struct pollfd fds;
    fds.fd = news;
    fds.events = POLLIN;
    bytes = 0;
    int retval = poll(&fds, 1, 1000 * 10);
    log_printf("******poll: %d\n", retval);
    if(retval > 0) { // -1 err, 0 expire
      log_printf("read()\n");
      if((bytes = read(news, htmlin + totalbytes, htmlinlen - totalbytes)) < 0) {
	log_errprintf("%s: cannot read()\n", procname);
	perror("read");
	//fflush(stderr);
      }
      if(retval > 0)
	reads++;
    } 

    log_printf("(%d bytes)", bytes);
    log_printf("%s", htmlin);
    if(bytes == 0)
      break;
    if(bytes > 3 &&
       !isprint(htmlin[0]) &&
       !isprint(htmlin[1]) &&
       !isprint(htmlin[2])) {
      log_printf("https packet?\n");
      htmlin[0] = '\0';
      totalbytes = 0;
      bytes = 0;
      break;
    }
    *(htmlin + totalbytes + bytes) = '\0';

    totalbytes += bytes;
    if(totalbytes >= htmlinlen) {
      // plus space for '\0'
      htmlin = realloc(htmlin, htmlinlen * 2 + 1);
      htmlinlen *= 2;
    }
    
    htmlparams = dbs_html_get_params();
    unsigned char *p = htmlin;
    dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
    if(!strcmp(htmlmethod, "GET"))
      break;
    dbs_html_get_request_line_num("Content-Length", sizeof(buffer10), buffer10);
    clen = atoi(buffer10);
    first = 0;
  }

  if(totalbytes != 0) {
    
    log_printf("%d reads", reads);
    log_printf(", received %d chars", totalbytes);
    log_printf(", read %d total bytes", totalbytes);
    log_printf(", input buffer size %d chars", htmlinlen);
    log_printf(", data=\"");
    log_dump_string(htmlin);
    log_printf("\"\n");
    
    dbs_run_loop();
    
    log_printf("buffers(html):");
    for(int c = 0; c < HTML_BUFFERS; c++) { 
      log_printf(" %d:%ld", c, strlen(html_get_string(c)));
      if(strlen(html_get_string(c)) > 0) {
	if((bytes = write(news, html_get_string(c), strlen(html_get_string(c)))) == -1) {
	  log_errprintf("%s: cannot write() dbs_html[%d]\n", procname, c);
	  perror("write");
	  //fflush(stderr);
	}
      }
    }
    for(int c = 0; c < HTML_BUFFERS; c++) {
      if(strlen(html_get_string(c)))
	log_printf("%s\n",html_get_string(c));
    }
    log_printf("\n");
  }
  dbs_query_vars();
}

Https palvelin

static void https_server()
{
  int quit, reset, addr_size;
  struct sockaddr_in sa_cli;
  SSL_CTX *ctx = NULL;

  signal(SIGPIPE, SIG_IGN);
  signal(SIGCHLD, SIG_IGN);

  // plus space for '\0'
  htmlin = malloc(htmlinlen+1);
  
  reset = 1;
  quit = 0;
  
  for(;;) {

    log_mode = 1;
    
    if(quit) {
      break;
    }

    if(https_keyschange_needed())
      reset = 1;
    
    if(reset) {
      if(ctx != NULL) {
	log_printf("SSL_CTX_free()\n");
	SSL_CTX_free(ctx);  
      }
      if(s != -1)
	close(s);
      log_printf("server_https_init()\n");
      ctx = server_https_init();
      log_printf("server_basic_socket()\n");
      s = server_basic_socket();
      reset = 0;
    }
  
    fprintf(stdout,"\n");
    fflush(stdout);
    
    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      log_errprintf("%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

    fprintf(stdout,"========================================\n");
    log_printf("dbs_time_vars()\n"); 
    dbs_time_vars();
    
    pid_t pid;
    log_printf("Fork start (parent)");
    log_printf(" getpid:%d getppid:%d", getpid(), getppid()); 
    log_printf("\n"); 

    log_printf("fork()\n"); 
    if((pid = fork()) < 0) {

      log_errprintf("%s: cannot fork()", procname);
      log_errprintf(", errno %d", errno);
      log_errprintf( "(%s)\n", strerror(errno));
      
      perror("fork");
      fflush(stderr);

      exit(1);
    } else if(pid == 0) { // child

      log_printf("init_child()\n"); 
      init_child();

      log_mode = 2; // child
      	
      log_printf("close(s)\n"); 
      close(s);

      log_printf("close(s)\n");
      close(s);

      log_printf("https_client()\n");
      https_client(news, ctx, sa_cli, addr_size, "https");

      dbs_run_critical_sections(); 
     
      log_printf("server_close(news)\n"); 
      server_close(news);

      log_printf("Fork end (child)"); 
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid()); 
      log_printf("\n"); 

      log_printf("exit_child()\n"); 
      exit_child();

      exit(0);
    }
    log_printf("Fork end (parent)"); 
    log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
    log_printf("\n");

    log_printf("server_close()\n"); 
    server_close(news);
  } // for(;;)
}

server_https_init()

server_https_init() kutsuu SSL palvelimen tarvitsemia kutsuja. Uusi koodi tässä tekee SSL avaintenmuutoksen: siitä lisää myöhemmin.

static SSL_CTX *server_https_init()
{
  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  unsigned char cert_digest2[HashLen]; // before change
  unsigned char privatekey_digest2[HashLen];
  unsigned char cert_digest3[HashLen]; // after change
  unsigned char privatekey_digest3[HashLen];

  log_printf("SSL_library_init()\n");
  SSL_library_init();
  
  log_printf("OpenSSL_add_ssl_algorithms()\n");
  OpenSSL_add_ssl_algorithms();
  
  log_printf("OpenSSL_add_ciphers()\n");
  OpenSSL_add_all_ciphers();
  
  log_printf("OpenSSL_load_error_strings()\n");
  SSL_load_error_strings();
  
  log_printf("SSLv23_server_method()\n");
  if((method = (SSL_METHOD *)    
      SSLv23_server_method()) == NULL) {
    log_errprintf("%s: cannot SSLv3_server_method()\n", procname);
    //fflush(stderr);
  }
  
  log_printf("SSL_CTX_new()\n");
  if((ctx = SSL_CTX_new(method)) == NULL) {
    log_errprintf("%s: cannot SSL_CTX_new()\n", procname);
  }

  for(;;) { // 2025 JariK
    dbs_file_digest(cert_file, cert_digest2); // save before change digests
    dbs_file_digest(privatekey_file, privatekey_digest2);
  
    log_printf("SSL_CTX_use_certificate_file()\n");
    if(SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_certificate_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }
    
    log_printf("SSL_CTX_use_PrivateKey_file()\n");
    if(SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_PrivateKey_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }

    dbs_file_digest(cert_file, cert_digest3); // save after change digests
    dbs_file_digest(privatekey_file, privatekey_digest3);

    // redo if before stamps are different from after stamps
    
    if(memcmp(cert_digest2, cert_digest3, sizeof(cert_digest3)) ||
       memcmp(privatekey_digest2,privatekey_digest3, sizeof(privatekey_digest3)))
      continue;

    memcpy(cert_digest, cert_digest2, sizeof(cert_digest2));
    memcpy(privatekey_digest, privatekey_digest2, sizeof(privatekey_digest2));
    
    log_dump("cert", 32, cert_digest, 32);  // first 32 bytes
    log_dump("private", 32, privatekey_digest, 32);  // first 32 bytes
  
    break;
  }

  log_printf("SSL_CTX_load_verify_locations()\n");
  if(SSL_CTX_load_verify_locations(ctx, cert_file, NULL) != 1) {
    int err2;
    err2 = ERR_get_error();
    log_errprintf("\n%s: cannot SSL_CTX_load_verify_locations()", procname);
    log_errprintf(", ERR_get_error(): %d", err2);
    while((err2 = ERR_get_error()) != 0)
      log_errprintf(", %d", err2);
    log_errprintf("\n");
  }
  return(ctx);
}

Https client

Https client alustaa SSL yhteyttä SSL toiminnoilla, lukee asiakkaan kyselyn SSL_read():lla ja suorittaa asiakasohjelman dbs_run_loop():lla ja kirjoittaa vastauksen takaisin asiakkaalle SSL_write() rutiinilla:

static void https_client(int news, SSL_CTX *ctx, struct sockaddr_in sa_cli, int addr_size, char *name)
{
  int status, ok;
  unsigned char buffer10[10];

  SSL *ssl;
  X509 *peer_cert;

  unsigned long long useconds;

  useconds = dbs_get_useconds();
  
  ok = 1;

  dbs_server_vars(sa_cli, addr_size, name);
  
  log_printf("SSL_new()\n"); 
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stdout,"\n%s: cannot SSL_new()", procname);
  }
  
  log_printf("SSL_set_fd()\n"); 
  if(SSL_set_fd(ssl, news) != 1) {
    fprintf(stdout,"\n%s: cannot SSL_set_fd()\n", procname);
  }
  
  log_printf("SSL_accept()\n"); 
  if((status = SSL_accept(ssl)) < 0) {
    log_printf("%s: cannot SSL_accept(), retval: %d\n", procname, status);
  }
  
  log_printf("SSL_get_peer_certificate()");
  peer_cert = SSL_get_peer_certificate(ssl);
  if(peer_cert == NULL) {
    log_printf(", No peer certificate");
  }
  log_printf("\n");
  
  int clen = 0;
  int reads = 0;
  int first = 1;
  int bytes, totalbytes = 0;

  htmlparams = NULL;

  htmlin[0] = '\0';
  while(htmlparams == NULL || clen - strlen(htmlparams) > 0) {
    if(!first) {
      log_printf(", ");
    } 
    int tries = 0;
    for(;;) {
      log_printf("SSL_read()\n"); 
      bytes = SSL_read(ssl, htmlin + totalbytes, htmlinlen - totalbytes);

      int errerrno, err, err2;

      errerrno = errno;
      err = SSL_get_error(ssl, bytes);
      err2 = ERR_get_error();

      log_printf("SSL_read()");
      log_printf(", retval %d", bytes);
      log_printf(", errno: %d", errerrno);
      log_printf(", SSL_get_error(): %d", err);
      log_printf(", ERR_get_error(): %d", err2);

      
      while((err2 = ERR_get_error()) != 0)
	log_printf(", %d", err2);
      log_printf("\n");
      
      if(bytes >= 0) {
	reads++;
	break;
      }
      if(bytes < 0) {
	if(++tries < 5 && (err == 1 || err == 5)) {
	  usleep(1024 * 512 / 5);
	  log_printf(" try:%d", tries);
	  continue;
	}
	log_printf("cannot SSL_read()\n");
	ok = 0;
	break;
      } // if(bytes
      break;
    } // for(;;)
    log_printf("(%d bytes)\n", bytes);
    log_printf("%s\n", htmlin);
    if(!ok)
      break;

    *(htmlin + totalbytes + bytes) = '\0';
    if(bytes == 0)
      break;

    totalbytes += bytes;
    if(totalbytes >= htmlinlen) {
      // plus space for '\0'
      htmlin = realloc(htmlin, htmlinlen * 2 + 1);
      htmlinlen *= 2;
    }
    
    htmlparams = dbs_html_get_params();
    unsigned char *p = htmlin;
    dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
    if(!strcmp(htmlmethod, "GET"))
      break;
    dbs_html_get_request_line_num("Content-Length",
				  sizeof(buffer10), buffer10);
    clen = atoi(buffer10);
    first = 0;
  }

  log_printf("%d reads", reads);
  log_printf(", received %d chars", totalbytes);
  log_printf(", read %d total bytes", totalbytes);
  log_printf(", input buffer size %d chars", htmlinlen);
  log_printf(", data=\"\n");
  log_dump_string(htmlin);
  log_printf("\"\n");
  
  strncpy(htmlsslcipher, SSL_get_cipher(ssl), sizeof(htmlsslcipher));
  
  if(ok) 
    dbs_run_loop();
  
  if(ok) {
    log_printf("buffers(ssl):");
    for(int c = 0; c < HTML_BUFFERS; c++) {
      log_printf(" %d:%ld", c, strlen(html_get_string(c)));
      if(strlen(html_get_string(c)) > 0) {
	log_printf("SSL_write()\n");
	if((status = SSL_write(ssl, html_get_string(c), strlen(html_get_string(c)))) < 1) {
	  log_errprintf("%s: cannot SSL_write(), buffer %d, status: %d, SSL error: %d\n",
		  procname, c, status, SSL_get_error(ssl, status));
	}
      }
    }
    for(int c = 0; c < HTML_BUFFERS; c++) {
      if(strlen(html_get_string(c)))
	log_printf("%s\n", html_get_string(c));
    }
  }
  log_printf("SSL connection using %s\n", htmlsslcipher);

  log_printf("dbs_query_vars()\n");
  dbs_query_vars();
  
  log_printf("SSL_free()\n");
  SSL_free(ssl);
}

DBS_run_loop()

Ohjelma käynnistää dba_loop():n, muotoilee uudestaan dba_loop():in tuloksen (html scripti) ja käynnistää dba_loop2():sen.

static void dbs_run_loop()
{
  unsigned char *p;

  p = htmlin;
  dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
  dbs_html_parse_string(sizeof(htmlpath), htmlpath, &p);
  dbs_html_parse_string(sizeof(htmlversion), htmlversion, &p);
  dbs_parse_filename(sizeof(htmlfilename), htmlfilename, htmlpath);
  dbs_parse_extension(sizeof(htmlfileextension), htmlfileextension, htmlfilename);
  
  htmlparams = dbs_html_get_params();
  if(htmlparams == NULL)
    htmlparams = "";
  
  html_clear_all();
 
  log_printf("enter dba_loop()\n");
  dba_loop();

  log_printf("html prettyprinter\n");
  
#ifdef DEBUG25
  log_printf("buffers1:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
  for(int c = 1; c < HTML_BUFFERS - 2; c++) {
    p = html_get_string(c);
    html_set(HTML_BUFFERS - 2);
    html_printf("%s", p);
    html_set(c);
    html_clear();
  }
  
#ifdef DEBUG25
  log_printf("buffers2:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
  p = html_get_string(HTML_BUFFERS - 2);
  html_set(HTML_BUFFERS - 1);
  html_indent(p);
  html_set(HTML_BUFFERS - 2);
  html_clear();
  
#ifdef DEBUG25
  log_printf("buffers3:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
#ifdef DEBUG25
  log_printf("html prettyprinter done.\n");
#endif
  
  log_printf("exit dba_loop2()\n");
  dba_loop2();
}

html_indent()

Ohjelma formatoi html-tekstin. Puskuri käydään läpi alusta loppuun ja jokaisen tagin kohdalla katsotaan löytyykö tagille pari (joko aloitus tai lopetus). Jos pari löytyy ja tagien välissä on toisia tagejä tehdään sisennys.

int indent;
int printedchars = 0;

#define aDEBUG28

void html_out_crlf()
{
  if(printedchars) {
#ifdef DEBUG28
    fprintf(stdout,"(%d)", indent * 8 + printedchars);
#endif
    html_printf("\n");
#ifdef DEBUG28
    fprintf(stdout,"\n");
#endif
    for(int c = 0; c < indent; c++) {
      html_printf("\t");
#ifdef DEBUG28
      fprintf(stdout,"\t");
#endif
    }
    printedchars = 0;
  }
}

#define aDEBUG31 2

void html_parse_name(int size, unsigned char *name, unsigned char **p2)
{
  int count;
  unsigned char *p, *n;

  p = *p2;
  count = 0;
  n = name;
  while(isalpha(*p) || isdigit(*p) || *p == '!') {
    if(++count < size)
      *n++ = *p;
    p++;
    count++;
  }
  *n = '\0';

#ifdef DEBUG31
  fprintf(stdout,"[name:%s]", name);
#endif
}

#define aDEBUG36 2
#define aDEBUG37 2

void html_indent(unsigned char *p2) // 2021 JariK
{
  int space, ch, tags, tagfound, intag, inchar;
  unsigned char *p, *q, *q2, name[32], tag[48];

  p = p2;
  ch = 0;
  indent = 0;
  inchar = 0;
  while(*p != '\0') {
    space = 0;
    while(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ') {
      p++;
      space = 1;
    }
    if(!strncmp(p,"<br>", 4)) {
      p += 4;
      html_printf("<br>");
#ifdef DEBUG36
      fprintf(stdout,"<br>");
#endif
      printedchars += 4;
      
      while(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ')
	p++;
#ifdef DEBUG36
      fprintf(stdout,"[nextchar:%c]", *p);
#endif
      if(strncmp(p,"</", 2))
	html_out_crlf();
    }

    if(!strncmp(p,"</", 2)) { // Check for end tag

      q2 = p;
      p += 2;
      q = p;
      html_parse_name(sizeof(name), name, &q);
      q = q2 - 1;
      tags = 0;
      tagfound = 0;

      // Search for start tag backward
      sprintf(tag,"<%s", name);
      while(q >= p2) {
        if(*q == '<') {
          if(!strncmp(tag, q, strlen(tag))) {
#ifdef DEBUG36    
            fprintf(stdout,"[tag:%s]", tag);
#endif
            tagfound = 1;
            break;
          } else
            tags++;
        }
        q--;
      }
      if(tags && indent > 0) {
        indent--;
#ifdef DEBUG36
        fprintf(stdout,"[indent:%d]", indent);
        fprintf(stdout,"[tagfound:%d]", tagfound);
#endif
        html_out_crlf();
      }
      html_printf("</");
#ifdef DEBUG36
      fprintf(stdout,"</");
#endif
      space = 0;
      intag = 1;
      inchar = 0;
#ifdef DEBUG36
      fprintf(stdout,"[inchar:%d]", inchar);
#endif
      printedchars += 2;

    } else if(*p == '<') { // Check for start tag

      p++;
      q = p;
      html_parse_name(sizeof(name), name, &q);
      tags = 0;
      tagfound = 0;

      // Search for end tag forward
      sprintf(tag,"</%s", name);
      while(*q != '\0') {
        if(*q == '<') {
          if(!strncmp(tag, q, strlen(tag))) {
#ifdef DEBUG36
            fprintf(stdout,"[tag:%s]", tag);
#endif
            tagfound = 1;
            break;
          } else
            tags++;
        }
        q++;
      }
      html_out_crlf();
      if(tags && tagfound) {
        indent++;
#ifdef DEBUG36
        fprintf(stdout,"[indent:%d]", indent);
        fprintf(stdout,"[tagfound:%d]", tagfound);
#endif
      }
      html_printf("<");
#ifdef DEBUG36
      fprintf(stdout,"<");
#endif
      space = 0;
      intag = 1;
      inchar = 0;
#ifdef DEBUG36
      fprintf(stdout,"[inchar:%d]", inchar);
#endif
      printedchars++;
    } else if((isprint(*p) || *p>=128) && *p!='\0') {
      if(!inchar && !intag) {
	inchar = 1;
#ifdef DEBUG36
	fprintf(stdout,"[inchar:%d]", inchar);
#endif
	//html_out_crlf();
      }
      int count = 0;
      for(q = p; (isprint(*q) || *q>=128) && *q!='\0'; q++) {
	if(*q == '\r' || *q == '\n' || *q == '\t' || *q == ' ')
	  break;
#ifdef DEBUG36
	fprintf(stdout,"%c", *q);
#endif
	count++;
      }
#ifdef DEBUG36
      fprintf(stdout,"(%d)", count);
#endif
    }

    int chars = 0;

    // calculate number of chars
    q = p;
    while((*q != '<' && (isprint(*q) || *q >= 128)) && *q != '\0') {
      if(*q == '\r' || *q == '\n' || *q == '\t' || *q == ' ')
        break;
      if(*q < 0x80 || *q > 0xbf) // utf8 too
        chars++;
      if(*q == '>')
	break;
      q++;
    }

    // print line break if needed
    if(indent * 8 + printedchars + chars > 100 && !intag) {
      html_out_crlf(); // print nl+indent
      space = 0;
    }
    
    // print space if needed
    if(ch != '>' && // no space after tag
       *p != '<' && // no space before tag
       printedchars > 0 &&
       space) {
      html_printf(" ");
#ifdef DEBUG36
      fprintf(stdout," ");
#endif
      space = 0;
    }

    //print the string
    while((*p != '<' && (isprint(*p) || *p >= 128)) && *p != '\0') {
      if(*p == '>') {
        intag = 0;
      }
      if(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ')
        break;
      ch = *p;
      html_printf("%c", *p);
#ifdef DEBUG36
      fprintf(stdout,"%c", *p);
#endif
      printedchars++;
      if(*p == '>') {
	p++;
	break;
      }
      p++;
    }
#ifdef DEBUG36
    fprintf(stdout,"(%d)", chars);
#endif
  } //while(*p!='\0')
#ifdef DEBUG37
  fprintf(stdout,"%s", html[html_now]);
#endif
}

dba_loop()

Ohjelma tulostaa palvelimen palauttaman sivun. Tässä Hello world palvelimessa rutiini ei tee muuta. Sivu ei tässä vaiheessa ole muotoiltu oikein, dba_run_loop() muotoilee sen.

html_set(HTML_HEADER_BUFFER) jälkeiset rivit menevät html otsakkeeseen ja html_set(HTML_PAYLOAD_BUFFER) rivit ovat varsinainen html-scripti joka sisältää veppisivulle tulevan html:n.

popen() kappaleella tulostetaan newressulla satunnaisbittejä.

html_set(HTML_LAST_BUFFER) on viimeinen puskuri, johon tulostetaan vielä footteri ja </body> ja </html>. Välissä oleviin puskureihin voi tulostaa muita html kappaleita. Tässä niitä ei käytetä.

Lopuksi kirjoitetaan päiväys (date) otsakkeen loppuun.

static void dba_loop()
{
  log_printf("entering dba_loop()\n");

  log_printf("fetching cookies\n");

  dbs_get_cookie("terttu_sessionid", sizeof(htmlsessionid), htmlsessionid);
  log_printf("htmlsessionid: \"%s\"", htmlsessionid);
  dbs_get_cookie("terttu_mode", sizeof(htmlmode), htmlmode);
  log_printf(", htmlmode: \"%s\"\n", htmlmode);
  dbs_get_cookie("terttu_language", sizeof(htmllanguage), htmllanguage);
  log_printf(", htmllanguage: \"%s\"\n", htmllanguage);

  log_printf("end fetching cookies\n");
  
  html_set(HTML_HEADER_BUFFER);
  
  html_printf("HTTP/1.0 200 OK\r\n");
  html_printf("Location: \r\n");
  html_printf("Server: %s\r\n", programname);

  html_set(HTML_PAYLOAD_BUFFER);

  html_printf("<!doctype html>\r\n");
  html_printf("<html lang=\"fi\">");
  
  html_printf("<head>");
  html_printf("<meta charset=\"utf-8\">");
  html_printf("<title>dbs3</title>");
  html_printf("<meta name=\"author\" content=\"Jari Kuivaniemi\">");
  html_printf("<meta name=\"copyright\" content=\"Jari Kuivaniemi\">");
  html_printf("<meta name=\"format-detection\" content=\"telephone=no\">"); // Safari
  html_printf("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
  html_printf("</head>");
  html_printf("<body>");
  html_printf("<h1>Hello world!</h1>");

  html_printf("<p>Tämä on artikkelin <a href=\"https://moijari.com?p=2990\">https://moijari.com?p=2990</a> testiohjelma.");
  html_printf(" Ohjelma löytyy tästä linkistä <a href=\"https://moijari.com:5008\">https://moijari.com:5008</a>.</p>");
  
  FILE *fp1;
  unsigned char buffer[1024];
  if((fp1 = popen("./newressu","r")) != NULL) {
    html_printf("<code>");
    while(fgets(buffer, sizeof(buffer), fp1) != NULL) {
      html_printf("%s<br>", buffer);
    }
    html_printf("</code>");
    pclose(fp1);
  }

  //html_printf("<p>Tämä on artikkelin https://moijari.com/?p=2990 testiohjelma.");
  //html_printf(" Ohjelma löytyy linkistä https://moijari.com:5008/.</p>");

  html_set(HTML_LAST_BUFFER);
  html_clear();
  dba_app_footer();
  html_printf("</body>");
  html_printf("</html>");
  
  time_t now;
  unsigned char timebuf[128];
  
  html_set(HTML_HEADER_BUFFER);
  now = time(NULL);
  strftime(timebuf, sizeof(timebuf), HTMLTIMEFORMAT, gmtime(&now));
  html_printf("Date: %s\r\n", timebuf);  
}

dba_loop2()

Rutiini on jatkoa edelliselle loop rutiinille siinä mielessä että tässä on käytössä formatoitu versio html scriptistä. Rutiini laskee html otsakkeeseen merkkien määrän ja laskee sha256 tiivisteen. Merkkien määrän ja sha256:n laskemisessa ei lasketa otsakkeen merkkejä, ja tulokset laskennasta sijoitetaan otsakkeeseen (HTML_HEADER_BUFFER).

Lopuksi kirjoitetaan otsakkeen ja payload:in välinen merkkisarja “\r\n\r\n”.

static void dba_loop2()
{
  int c, len;

  log_printf("entering dba_loop2()\n");

  html_set(HTML_HEADER_BUFFER);
  
  len = strlen(html_get_string(1)) +
    strlen(html_get_string(2)) +
    strlen(html_get_string(3)) +
    strlen(html_get_string(4)) +
    strlen(html_get_string(5)) +
    strlen(html_get_string(6)) +
    strlen(html_get_string(7)) +
    strlen(html_get_string(8));
  html_printf("Content-Length: %d\n", len);

  unsigned char hash[HashLen];
  HashCtx ctx;

#define DEBUG46 2
  
#ifdef DEBUG46
  FILE *fp1;
  char *filename = "dbs3sha.deb";
  
  if((fp1 = fopen(filename, "w")) == NULL) {
    log_errprintf("%s: cannot open file %s\n", procname, filename);
    exit(1);
  }
#endif
  
  HashInit(&ctx);
  for(c = 1; c < HTML_BUFFERS; c++) {
    if(strlen(html_get_string(c)) > 0) {
      HashUpdate(&ctx, html_get_string(c), strlen(html_get_string(c)));
      //fprintf(stdout,"%d \"%s\"\n", c, html_get_string(c));
#ifdef DEBUG46
      fwrite(html_get_string(c), strlen(html_get_string(c)), 1, fp1);
#endif
    }
  }
  HashFinal(hash, &ctx);  

#ifdef DEBUG46
  fclose(fp1);
#endif
  
  html_printf("SHA256: ");
  for(c = 0; c < sizeof(hash); c++) {
    html_printf("%02x", hash[c]);
  }
  html_printf("\n");
  
  html_printf("\r\n\r\n");
  
  log_printf("exiting dba_loop2()\n");
}

Virheilmoitukset lokiin

Aiemmin lokit tulostettiin vain stderr tiedostoon. Tein muutokset, joilla ne päätyvät myös dbs3.log:iin ja dbs3pid*.log:iin. Aiemmin virheilmoitukset toimivat näin:

    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      fprintf(stderr,"%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

Uudessa versiossa koodi muutetaan näin:

    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      log_errprintf("%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

Vielä koodimuutokset:

void log_wait(); // cross-use with log_printf() routines
void log_post();

void log_printf_valist(FILE *fp1, const char *format, va_list ap) // JariK 2025
{
  int count;
  static char *printbuf = NULL;
  static int printbuf_len = 0;
  static int printtime = 1;

  va_list ap2;
  va_copy(ap2, ap);

  count = vsnprintf(printbuf, printbuf_len, format, ap) + 1;
  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf = realloc(printbuf, printbuf_len);
    count = vsnprintf(printbuf, printbuf_len, format, ap2) + 1;
  }

  // print all characters from printbuf.
  // print all characters to log adding time and date.
  
  fputs(printbuf, fp1);
  
  FILE *fp2;
  if((fp2 = fopen(log_filename, "a")) != NULL) {
    if(printtime) {
      time_t now = time(NULL);
      unsigned char timestamp[32];
      log_wait(); // JariK 2025
      strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S%Z", localtime((time_t *)&now));
      fputs(timestamp, fp2);
      fputc(' ', fp2);
      printtime = 0;
    }
    fputs(printbuf, fp2);
    fclose(fp2);
    if(printbuf[strlen(printbuf) - 1] == '\n') {
      printtime = 1;
      log_post(); // JariK 2025
    }
  }
  if(log_mode == 2) {
    if((fp2 = fopen(log_childfile, "a")) != NULL) {
      fputs(printbuf, fp2);
      fclose(fp2);
    }
  }
}

void log_printf(const char *format, ...) // JariK 2025
{
  va_list args;

  va_start(args, format);
  log_printf_valist(stdout, format, args);
  //fprintf(stdout,format, args);
  va_end(args);
}

void log_errprintf(const char *format, ...) // JariK 2025
{
  va_list args;

  va_start(args, format);
  log_printf_valist(stderr, format, args);
  //fprintf(stdout,format, args);
  va_end(args);
}

SSL yhteyden avainten vaihto

Rutiini tarkistaa onko ssl avaimet vaihtuneet, ja jos on palauttaa tiedon palvelimelle:

static int https_keyschange_needed() // 2025 JariK
{
  unsigned char cert_digest2[HashLen];
  unsigned char privatekey_digest2[HashLen];

  dbs_file_digest(cert_file, cert_digest2);
  dbs_file_digest(privatekey_file, privatekey_digest2);

  log_dump("cert", 32, cert_digest2, 32);  // first 32 bytes
  log_dump("private", 32, privatekey_digest2, 32);  // first 32 bytes
  
  if(memcmp(cert_digest, cert_digest2, sizeof(cert_digest2)) ||
     memcmp(privatekey_digest, privatekey_digest2, sizeof(privatekey_digest2)))
    return(1);
  else
    return(0);
}

Palvelimen koodi:

  for(;;) {

    log_mode = 1;
    
    if(quit) {
      break;
    }

    if(https_keyschange_needed())
      reset = 1;
    
    if(reset) {
      if(ctx != NULL) {
	log_printf("SSL_CTX_free()\n");
	SSL_CTX_free(ctx);  
      }
      if(s != -1)
	close(s);
      log_printf("server_https_init()\n");
      ctx = server_https_init();
      log_printf("server_basic_socket()\n");
      s = server_basic_socket();
      reset = 0;
    }
...
  }
}

server_https_init() luuppaa kunnes avainten asetuksen alun ja lopun tiedostotiivisteet ovat koko muutoksen ajan samanlaiset, ja tallettaa viimeiset tiivisteet _digest loppuisiin kenttiin. Luuppaus on siltä varalta että avaimet eivät ole vielä täysin muuttuneet: näin saadaan molemmat avaimet varmasti samasta avainpäivityksestä.

static SSL_CTX *server_https_init()
{
...
  for(;;) { // 2025 JariK
    dbs_file_digest(cert_file, cert_digest2); // save before change digests
    dbs_file_digest(privatekey_file, privatekey_digest2);
  
    log_printf("SSL_CTX_use_certificate_file()\n");
    if(SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_certificate_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }
    
    log_printf("SSL_CTX_use_PrivateKey_file()\n");
    if(SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_PrivateKey_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }

    dbs_file_digest(cert_file, cert_digest3); // save after change digests
    dbs_file_digest(privatekey_file, privatekey_digest3);

    // redo if before stamps are different from after stamps
    
    if(memcmp(cert_digest2, cert_digest3, sizeof(cert_digest3)) ||
       memcmp(privatekey_digest2,privatekey_digest3, sizeof(privatekey_digest3)))
      continue;

    memcpy(cert_digest, cert_digest2, sizeof(cert_digest2));
    memcpy(privatekey_digest, privatekey_digest2, sizeof(privatekey_digest2));
    
    log_dump("cert", 32, cert_digest, 32);  // first 32 bytes
    log_dump("private", 32, privatekey_digest, 32);  // first 32 bytes
  
    break;
  }
...
}

dbs3.c lähdekoodi kokonaisuudessaan

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>

#include "sha256.h"

char *programname = "DBS3 version 0.06 ©";
unsigned char *procname;
unsigned char *myport = "5000";

unsigned char *htmlin;
int htmlinlen = 2048;
unsigned char *htmlparams = NULL;

int dbs_output = 0;

void dbs_dump(unsigned char *header, int len, unsigned char *buf, int linelen)
{
  int c;

  if(dbs_output) {
    fprintf(stdout,"\n");
    dbs_output = 0;
  }
  for(c = 0; c < len; c++) {
    if(c % linelen == 0) {
      if(c > 0)
	fprintf(stdout,"\n");
      fprintf(stdout,"%-10s", header);
    }
    fprintf(stdout," %02x", buf[c]);
  }
  fprintf(stdout,"\n");
  fflush(stdout);
}

#include <stdarg.h>

unsigned char log_filename[128] = "dbs3.log";
unsigned char log_childfile[128] = "dbs3child.log"; // for zombihunt

int log_mode = 1; // 1 = main, 2 = child

#include <time.h>

void log_wait(); // cross-use with log_printf() routines
void log_post();

void log_printf_valist(FILE *fp1, const char *format, va_list ap) // JariK 2025
{
  int count;
  static char *printbuf = NULL;
  static int printbuf_len = 0;
  static int printtime = 1;

  va_list ap2;
  va_copy(ap2, ap);

  count = vsnprintf(printbuf, printbuf_len, format, ap) + 1;
  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf = realloc(printbuf, printbuf_len);
    count = vsnprintf(printbuf, printbuf_len, format, ap2) + 1;
  }

  // print all characters from printbuf.
  // print all characters to log adding time and date.
  
  fputs(printbuf, fp1);
  
  FILE *fp2;
  if((fp2 = fopen(log_filename, "a")) != NULL) {
    if(printtime) {
      time_t now = time(NULL);
      unsigned char timestamp[32];
      log_wait(); // JariK 2025
      strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S%Z", localtime((time_t *)&now));
      fputs(timestamp, fp2);
      fputc(' ', fp2);
      printtime = 0;
    }
    fputs(printbuf, fp2);
    fclose(fp2);
    if(printbuf[strlen(printbuf) - 1] == '\n') {
      printtime = 1;
      log_post(); // JariK 2025
    }
  }
  if(log_mode == 2) {
    if((fp2 = fopen(log_childfile, "a")) != NULL) {
      fputs(printbuf, fp2);
      fclose(fp2);
    }
  }
}

#ifdef OLD1

void log_printf(const char *format, ...) // JariK 2025
{
  int count;
  va_list args;
  static char *printbuf = NULL;
  static int printbuf_len = 0;
  static int printtime = 1;

  va_start(args, format);
  count = vsnprintf(printbuf, printbuf_len, format, args) + 1;
  va_end(args);

  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf = realloc(printbuf, printbuf_len);
    va_start(args, format);
    count = vsnprintf(printbuf, printbuf_len, format, args) + 1;
    va_end(args);
  }

  // print all characters from printbuf.
  // print all characters to log adding time and date.
  
  fputs(printbuf, stdout);

  FILE *fp1;
  if((fp1 = fopen(log_filename, "a")) != NULL) {
    if(printtime) {
      time_t now = time(NULL);
      unsigned char timestamp[32];
      log_wait(); // JariK 2025
      strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S%Z", localtime((time_t *)&now));
      fputs(timestamp, fp1);
      fputc(' ', fp1);
      printtime = 0;
    }
    fputs(printbuf, fp1);
    fclose(fp1);
    if(printbuf[strlen(printbuf) - 1] == '\n') {
      printtime = 1;
      log_post(); // JariK 2025
    }
  }
  if(log_mode == 2) {
    if((fp1 = fopen(log_childfile, "a")) != NULL) {
      fputs(printbuf, fp1);
      fclose(fp1);
    }
  }
}

#endif // end of #ifdef OLD1

void log_printf(const char *format, ...) // JariK 2025
{
  va_list args;

  va_start(args, format);
  log_printf_valist(stdout, format, args);
  //fprintf(stdout,format, args);
  va_end(args);
}

void log_errprintf(const char *format, ...) // JariK 2025
{
  va_list args;

  va_start(args, format);
  log_printf_valist(stderr, format, args);
  //fprintf(stdout,format, args);
  va_end(args);
}

#include <semaphore.h>
#include <signal.h>

static sem_t *logmutex = NULL;
static char logsemname[32] = "dbs3log.sem";

void log_wait() // JariK 2025
{
  signal(SIGINT, SIG_IGN);
  if(logmutex == NULL) {
    if((logmutex = sem_open(logsemname, 0, 0644, 1)) == SEM_FAILED) {
      if((logmutex = sem_open(logsemname, O_CREAT, 0644, 1)) == SEM_FAILED) {
	//if((mutex = sem_open(semname, O_CREAT | O_EXCL, 0644, 1)) == SEM_FAILED) {
	log_errprintf("%s: log_wait: cannot sem_open()", procname);
	log_errprintf(" errno %d", errno);
	log_errprintf("(%s)\n", strerror(errno));
	perror("sem_open");
      }
    }
  }
  if(sem_wait(logmutex) == -1) {
    log_errprintf("%s: log_wait: cannot sem_wait()", procname);
    log_errprintf(", errno: %d", errno);
    log_errprintf("(%s)\n", strerror(errno));
    perror("sem_wait");
  }
}

void log_post() // JariK 2025
{
  if(sem_post(logmutex) == -1) {
    log_errprintf( "%s: log_post: cannot sem_post()", procname);
    log_errprintf( ", errno: %d", errno);
    log_errprintf( "(%s)\n", strerror(errno));
    perror("sem_post");
  }
  sem_close(logmutex);
  logmutex = NULL;
  
  signal(SIGINT, SIG_DFL);
}

void log_dump(unsigned char *header, int len, unsigned char *buf, int linelen)
{
  int c;

  for(c = 0; c < len; c++) {
    if(c % linelen == 0) {
      if(c > 0)
	log_printf("\n");
      log_printf("%-10s", header);
    }
    log_printf(" %02x", buf[c]);
  }
  log_printf("\n");
}


static void log_clear()
{
}

static void log_dump_string(unsigned char *line)
{
}

#define HTML_BUFFERS 9

int html_now = 0;
unsigned char *html[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
int html_size[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void html_set(int bufno)
{
  html_now = bufno;
  if(html[bufno] == NULL) {
    html_size[bufno] = 10;
    html[bufno] = malloc(html_size[bufno]);
  }
}

int html_get()
{
  return(html_now);
}

unsigned char *html_get_string(int bufno)
{
  return(html[bufno]);
}

void html_clear()
{
  html_set(html_now);
  html[html_now][0] = '\0';
}

void html_clear_all()
{
  int c;
  for(c = 0; c < HTML_BUFFERS; c++) {
    html_set(c);
    html_clear();
  }
}

static void html_printf_valist(const char *format, va_list ap)
{
  int count;
  static char *printbuf = NULL;
  static int printbuf_len = 0;

  va_list ap2;
  va_copy(ap2, ap);

  count = vsnprintf(printbuf, printbuf_len, format, ap) + 1;
  if(printbuf_len < count) {
    printbuf_len = count;
    printbuf = realloc(printbuf, printbuf_len);
    count = vsnprintf(printbuf, printbuf_len, format, ap2) + 1;
  }

  if(html_size[html_now] <
     strlen(html[html_now]) +
     strlen(printbuf) + 1) {
    html_size[html_now] =
     strlen(html[html_now]) +
     strlen(printbuf) + 1;
    html[html_now] = realloc(html[html_now], html_size[html_now]);
  }
  
  strcpy(html[html_now] + strlen(html[html_now]), printbuf);
}

void html_printf(const char *format, ...)
{
  va_list args;

  va_start(args, format);
  html_printf_valist(format, args);
  //fprintf(stdout,format, args);
  va_end(args);
}

void html_buf_printf(int bufno, const char *format, ...)
{
  int save_html;
  va_list args;

  save_html = html_get();
  html_set(bufno);
  va_start(args, format);
  html_printf_valist(format, args);
  va_end(args);
  html_set(save_html);
}

int indent;
int printedchars = 0;

#define aDEBUG28

void html_out_crlf()
{
  if(printedchars) {
#ifdef DEBUG28
    fprintf(stdout,"(%d)", indent * 8 + printedchars);
#endif
    html_printf("\n");
#ifdef DEBUG28
    fprintf(stdout,"\n");
#endif
    for(int c = 0; c < indent; c++) {
      html_printf("\t");
#ifdef DEBUG28
      fprintf(stdout,"\t");
#endif
    }
    printedchars = 0;
  }
}

#define aDEBUG31 2

void html_parse_name(int size, unsigned char *name, unsigned char **p2)
{
  int count;
  unsigned char *p, *n;

  p = *p2;
  count = 0;
  n = name;
  while(isalpha(*p) || isdigit(*p) || *p == '!') {
    if(++count < size)
      *n++ = *p;
    p++;
    count++;
  }
  *n = '\0';

#ifdef DEBUG31
  fprintf(stdout,"[name:%s]", name);
#endif
}

#define aDEBUG36 2
#define aDEBUG37 2

void html_indent(unsigned char *p2) // 2021 JariK
{
  int space, ch, tags, tagfound, intag, inchar;
  unsigned char *p, *q, *q2, name[32], tag[48];

  p = p2;
  ch = 0;
  indent = 0;
  inchar = 0;
  while(*p != '\0') {
    space = 0;
    while(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ') {
      p++;
      space = 1;
    }
    if(!strncmp(p,"<br>", 4)) {
      p += 4;
      html_printf("<br>");
#ifdef DEBUG36
      fprintf(stdout,"<br>");
#endif
      printedchars += 4;
      
      while(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ')
	p++;
#ifdef DEBUG36
      fprintf(stdout,"[nextchar:%c]", *p);
#endif
      if(strncmp(p,"</", 2))
	html_out_crlf();
    }

    if(!strncmp(p,"</", 2)) { // Check for end tag

      q2 = p;
      p += 2;
      q = p;
      html_parse_name(sizeof(name), name, &q);
      q = q2 - 1;
      tags = 0;
      tagfound = 0;

      // Search for start tag backward
      sprintf(tag,"<%s", name);
      while(q >= p2) {
        if(*q == '<') {
          if(!strncmp(tag, q, strlen(tag))) {
#ifdef DEBUG36    
            fprintf(stdout,"[tag:%s]", tag);
#endif
            tagfound = 1;
            break;
          } else
            tags++;
        }
        q--;
      }
      if(tags && indent > 0) {
        indent--;
#ifdef DEBUG36
        fprintf(stdout,"[indent:%d]", indent);
        fprintf(stdout,"[tagfound:%d]", tagfound);
#endif
        html_out_crlf();
      }
      html_printf("</");
#ifdef DEBUG36
      fprintf(stdout,"</");
#endif
      space = 0;
      intag = 1;
      inchar = 0;
#ifdef DEBUG36
      fprintf(stdout,"[inchar:%d]", inchar);
#endif
      printedchars += 2;

    } else if(*p == '<') { // Check for start tag

      p++;
      q = p;
      html_parse_name(sizeof(name), name, &q);
      tags = 0;
      tagfound = 0;

      // Search for end tag forward
      sprintf(tag,"</%s", name);
      while(*q != '\0') {
        if(*q == '<') {
          if(!strncmp(tag, q, strlen(tag))) {
#ifdef DEBUG36
            fprintf(stdout,"[tag:%s]", tag);
#endif
            tagfound = 1;
            break;
          } else
            tags++;
        }
        q++;
      }
      html_out_crlf();
      if(tags && tagfound) {
        indent++;
#ifdef DEBUG36
        fprintf(stdout,"[indent:%d]", indent);
        fprintf(stdout,"[tagfound:%d]", tagfound);
#endif
      }
      html_printf("<");
#ifdef DEBUG36
      fprintf(stdout,"<");
#endif
      space = 0;
      intag = 1;
      inchar = 0;
#ifdef DEBUG36
      fprintf(stdout,"[inchar:%d]", inchar);
#endif
      printedchars++;
    } else if((isprint(*p) || *p>=128) && *p!='\0') {
      if(!inchar && !intag) {
	inchar = 1;
#ifdef DEBUG36
	fprintf(stdout,"[inchar:%d]", inchar);
#endif
	//html_out_crlf();
      }
      int count = 0;
      for(q = p; (isprint(*q) || *q>=128) && *q!='\0'; q++) {
	if(*q == '\r' || *q == '\n' || *q == '\t' || *q == ' ')
	  break;
#ifdef DEBUG36
	fprintf(stdout,"%c", *q);
#endif
	count++;
      }
#ifdef DEBUG36
      fprintf(stdout,"(%d)", count);
#endif
    }

    int chars = 0;

    // calculate number of chars
    q = p;
    while((*q != '<' && (isprint(*q) || *q >= 128)) && *q != '\0') {
      if(*q == '\r' || *q == '\n' || *q == '\t' || *q == ' ')
        break;
      if(*q < 0x80 || *q > 0xbf) // utf8 too
        chars++;
      if(*q == '>')
	break;
      q++;
    }

    // print line break if needed
    if(indent * 8 + printedchars + chars > 100 && !intag) {
      html_out_crlf(); // print nl+indent
      space = 0;
    }
    
    // print space if needed
    if(ch != '>' && // no space after tag
       *p != '<' && // no space before tag
       printedchars > 0 &&
       space) {
      html_printf(" ");
#ifdef DEBUG36
      fprintf(stdout," ");
#endif
      space = 0;
    }

    //print the string
    while((*p != '<' && (isprint(*p) || *p >= 128)) && *p != '\0') {
      if(*p == '>') {
        intag = 0;
      }
      if(*p == '\r' || *p == '\n' || *p == '\t' || *p == ' ')
        break;
      ch = *p;
      html_printf("%c", *p);
#ifdef DEBUG36
      fprintf(stdout,"%c", *p);
#endif
      printedchars++;
      if(*p == '>') {
	p++;
	break;
      }
      p++;
    }
#ifdef DEBUG36
    fprintf(stdout,"(%d)", chars);
#endif
  } //while(*p!='\0')
#ifdef DEBUG37
  fprintf(stdout,"%s", html[html_now]);
#endif
}

#define backlog 64

int s = -1, news;
//char cert_file[128] = "/etc/letsencrypt/live/moijari.com/fullchain.pem";
//char privatekey_file[128] = "/etc/letsencrypt/live/moijari.com/privkey.pem";
char cert_file[128] = "fullchain.pem";
char privatekey_file[128] = "privkey.pem";

#define HTMLTIMEFORMAT "%a, %d %b %Y %H:%M:%S GMT"

unsigned char htmlmethod[10];
unsigned char htmlpath[128];
unsigned char htmlversion[32];
unsigned char htmlfilename[64];
unsigned char htmlfileextension[10];
unsigned char htmlcookie[1024];
unsigned char htmlsessionid[33];
unsigned char htmldigest[HashLen * 2 + 1];
unsigned char htmlmode[10];
unsigned char htmllanguage[10];
unsigned char htmltimeshort[32];
unsigned char htmltime[32];
unsigned char htmlsslcipher[64];

static unsigned char *dbs_html_get_request_line(unsigned char *name, int lenbuffer, unsigned char *buffer)
{
  int len;
  unsigned char *p, *q, tag[32];

  p = htmlin;

#ifdef DEBUG11
  fprintf(stdout,"Data:");
  dump_string(stdout, p);
  fprintf(stdout,"\n");
#endif
  
  sprintf(tag, "\r\n%s:", name);
  while(*p != '\0') {
    if(!strncmp(p, tag, strlen(tag))) {
      p += strlen(tag);
      while(isblank(*p))
	p++;
      q = buffer;
      len = 0;
      while(*p != '\r' && *p != '\n' && *p != '\0') {
	if(++len < lenbuffer)
	  *q++ = *p;
	p++;
      }
      *q = '\0';
      return(buffer);
    }
    if(*p != '\0')
      p++;
  }
  return(NULL);
}

static void dbs_html_get_request_line_num(unsigned char *name, int lenbuffer, unsigned char *buffer)
{
  unsigned char *p;

  if((p = dbs_html_get_request_line(name, lenbuffer, buffer)) == NULL)
    sprintf(buffer,"0");
#ifdef OLD1
  else {
    int len = 0;
    unsigned char *n = buffer;
    while(isdigit(*p)) {
      if(++len < lenbuffer)
        *n++ = *p;
      p++;
    }
    *n = '\0';
  }
#endif
}

void dbs_get_cookie(unsigned char *name, int valuelen, unsigned char *value)
{
  int len;
  unsigned char *p, *n;

  value[0] = '\0';
  
  p = htmlcookie;
  
  while(*p != '\0') {
    while(isblank(*p))
      p++;
    if(!strncmp(p, name, strlen(name))) {
      p += strlen(name);
      while(isblank(*p))
        p++;
      if(*p == '=')
        p++;
      while(isblank(*p))
        p++;
      n = value;
      len = 0;
      while(*p != ';' && isprint(*p)) {
        if(++len < valuelen)
          *n++ = *p;
        p++;
      }
      *n = '\0';
      return;
    }
    while(*p != ';' && *p != '\n' && *p != '\0')
      p++;
    if(*p == ';')
      p++;
    while(isblank(*p))
      p++;
  }
}

static void dbs_html_parse_string(int buflen, char *buf, unsigned char **p2)
{
  int len;
  unsigned char *p;

  p = *p2;
  len = 0;
  while(*p != '\0' && !isblank(*p) && *p != '\n' && *p != '\r') {
    if(++len < buflen) {
      *buf++ = *p;
    }
    p++;
  }
  *buf = '\0';

  while(isblank(*p))
    p++;

  *p2 = p;
}

static void dbs_parse_filename(int buflen, char *buf, unsigned char *p)
{
  int len;
  unsigned char *n;

  len = 0;
  n = buf;

  while(*p != '\0') {
    if(*p == '/') {
      n = buf;
      len = 0;
    } else {
      if(++len < buflen)
        *n++ = *p;
    }
    p++;
  }
  *n = '\0';
}

void dbs_parse_extension(int buflen, char *buf, unsigned char *p)
{
  int len;
  unsigned char *n;

  len = 0;
  n = buf;

  while(*p != '\0') {
    if(*p == '.') {
      n = buf;
      len = 0;
    } else {
      if(++len < buflen)
        *n++ = *p;
    }
    p++;
  }
  *n = '\0';
}

static unsigned char *dbs_html_get_params()
{
  int ok = 0;
  unsigned char *p = htmlin;

  while(*p != '\0') {
    if(!strncmp(p,"\r\n\r\n", 4)) {
      p += 4;
      ok = 1;
      break;
    }
    p++;
  }

  if(ok)
    return(p);
  else
    return(NULL);
}

static void dbs_time_vars()
{
}

#define HTML_HEADER_BUFFER 0
#define HTML_PAYLOAD_BUFFER 1
#define HTML_FOREIGN_BUFFER 2
#define HTML_QUERY_BUFFER 3
#define HTML_LAST_BUFFER 4

void dba_app_footer()
{
  html_printf("<p>%s", programname);
  html_printf(",  sha256(%s)", htmldigest);
  html_printf("</p>");
  
#ifdef OLD1
  html_printf("<br><br>%s,  sha256(%s)<br><br>",
		  programname, htmldigest);
#endif
}

static void dba_loop()
{
  log_printf("entering dba_loop()\n");

  log_printf("fetching cookies\n");

  dbs_get_cookie("terttu_sessionid", sizeof(htmlsessionid), htmlsessionid);
  log_printf("htmlsessionid: \"%s\"", htmlsessionid);
  dbs_get_cookie("terttu_mode", sizeof(htmlmode), htmlmode);
  log_printf(", htmlmode: \"%s\"\n", htmlmode);
  dbs_get_cookie("terttu_language", sizeof(htmllanguage), htmllanguage);
  log_printf(", htmllanguage: \"%s\"\n", htmllanguage);

  log_printf("end fetching cookies\n");
  
  html_set(HTML_HEADER_BUFFER);
  
  html_printf("HTTP/1.0 200 OK\r\n");
  html_printf("Location: \r\n");
  html_printf("Server: %s\r\n", programname);

  html_set(HTML_PAYLOAD_BUFFER);

  html_printf("<!doctype html>\r\n");
  html_printf("<html lang=\"fi\">");
  
  html_printf("<head>");
  html_printf("<meta charset=\"utf-8\">");
  html_printf("<title>dbs3</title>");
  html_printf("<meta name=\"author\" content=\"Jari Kuivaniemi\">");
  html_printf("<meta name=\"copyright\" content=\"Jari Kuivaniemi\">");
  html_printf("<meta name=\"format-detection\" content=\"telephone=no\">"); // Safari
  html_printf("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
  html_printf("</head>");
  html_printf("<body>");
  html_printf("<h1>Hello world!</h1>");

  html_printf("<p>Tämä on artikkelin <a href=\"https://moijari.com?p=2990\">https://moijari.com?p=2990</a> testiohjelma.");
  html_printf(" Ohjelma löytyy tästä linkistä <a href=\"https://moijari.com:5008\">https://moijari.com:5008</a>.</p>");
  
  FILE *fp1;
  unsigned char buffer[1024];
  
  if((fp1 = popen("./newressu","r")) != NULL) {

    html_printf("<p>Seuraava kappale sisältää ressun tekemiä satunnaisbittejä.");
    html_printf(" Ne ovat tässä vain tilan täytteeksi.</p>");

    html_printf("<code>");
    while(fgets(buffer, sizeof(buffer), fp1) != NULL) {
      html_printf("%s<br>", buffer);
    }
    html_printf("</code>");
    pclose(fp1);
  }

  // html_printf("<p>Tässä on <b>Boldia tekstiä</b>. Tämä taas <u>alleviivattua tekstiä</u>.</p>");

  
  //html_printf("<p>Tämä on artikkelin https://moijari.com/?p=2990 testiohjelma.");
  //html_printf(" Ohjelma löytyy linkistä https://moijari.com:5008/.</p>");

  html_set(HTML_LAST_BUFFER);
  html_clear();
  dba_app_footer();
  html_printf("</body>");
  html_printf("</html>");
  
  time_t now;
  unsigned char timebuf[128];
  
  html_set(HTML_HEADER_BUFFER);
  now = time(NULL);
  strftime(timebuf, sizeof(timebuf), HTMLTIMEFORMAT, gmtime(&now));
  html_printf("Date: %s\r\n", timebuf);  
}

static void dba_loop2()
{
  int c, len;

  log_printf("entering dba_loop2()\n");

  html_set(HTML_HEADER_BUFFER);
  
  len = strlen(html_get_string(1)) +
    strlen(html_get_string(2)) +
    strlen(html_get_string(3)) +
    strlen(html_get_string(4)) +
    strlen(html_get_string(5)) +
    strlen(html_get_string(6)) +
    strlen(html_get_string(7)) +
    strlen(html_get_string(8));
  html_printf("Content-Length: %d\n", len);

  unsigned char hash[HashLen];
  HashCtx ctx;

#define DEBUG46 2
  
#ifdef DEBUG46
  FILE *fp1;
  char *filename = "dbs3sha.deb";
  
  if((fp1 = fopen(filename, "w")) == NULL) {
    log_errprintf("%s: cannot open file %s\n", procname, filename);
    exit(1);
  }
#endif
  
  HashInit(&ctx);
  for(c = 1; c < HTML_BUFFERS; c++) {
    if(strlen(html_get_string(c)) > 0) {
      HashUpdate(&ctx, html_get_string(c), strlen(html_get_string(c)));
      //fprintf(stdout,"%d \"%s\"\n", c, html_get_string(c));
#ifdef DEBUG46
      fwrite(html_get_string(c), strlen(html_get_string(c)), 1, fp1);
#endif
    }
  }
  HashFinal(hash, &ctx);  

#ifdef DEBUG46
  fclose(fp1);
#endif
  
  html_printf("SHA256: ");
  for(c = 0; c < sizeof(hash); c++) {
    html_printf("%02x", hash[c]);
  }
  html_printf("\n");
  
  html_printf("\r\n\r\n");
  
  log_printf("exiting dba_loop2()\n");
}

#define DEBUG25 2

static void dbs_run_loop()
{
  unsigned char *p;

  p = htmlin;
  dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
  dbs_html_parse_string(sizeof(htmlpath), htmlpath, &p);
  dbs_html_parse_string(sizeof(htmlversion), htmlversion, &p);
  dbs_parse_filename(sizeof(htmlfilename), htmlfilename, htmlpath);
  dbs_parse_extension(sizeof(htmlfileextension), htmlfileextension, htmlfilename);
  
  htmlparams = dbs_html_get_params();
  if(htmlparams == NULL)
    htmlparams = "";
  
  html_clear_all();
 
  log_printf("enter dba_loop()\n");
  dba_loop();

  log_printf("html prettyprinter\n");
  
#ifdef DEBUG25
  log_printf("buffers1:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
  for(int c = 1; c < HTML_BUFFERS - 2; c++) {
    p = html_get_string(c);
    html_set(HTML_BUFFERS - 2);
    html_printf("%s", p);
    html_set(c);
    html_clear();
  }
  
#ifdef DEBUG25
  log_printf("buffers2:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
  p = html_get_string(HTML_BUFFERS - 2);
  html_set(HTML_BUFFERS - 1);
  html_indent(p);
  html_set(HTML_BUFFERS - 2);
  html_clear();
  
#ifdef DEBUG25
  log_printf("buffers3:");
  for(int c = 0; c < HTML_BUFFERS; c++) { 
    log_printf(" %d:%ld", c, strlen(html_get_string(c)));
  }
  log_printf("\n");  
#endif
  
#ifdef DEBUG25
  log_printf("html prettyprinter done.\n");
#endif
  
  log_printf("exit dba_loop2()\n");
  dba_loop2();
}

#define DEBUG42 2

void init_child()
{
  // open file for hunting zombies

  sprintf(log_childfile, "dbs3pid%d.log", getpid());
}

void exit_child()
{
  // if process ends normally
  // delete child's logfile
  log_printf("unlink %s\n", log_childfile);
  unlink(log_childfile);
}

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

static void dbs_server_vars(struct sockaddr_in sa_cli, int addr_size, char *name)
{
}

#include <sys/time.h>

unsigned long long dbs_get_useconds()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return(tv.tv_usec + 1000000 * tv.tv_sec);
}

static void dbs_query_vars()
{
}

static sem_t *criticalmutex = NULL;
static char criticalsemname[32] = "dbs3critical.sem";

void critical_wait() // JariK 2025
{
  signal(SIGINT, SIG_IGN);
  if(criticalmutex == NULL) {
    if((criticalmutex = sem_open(criticalsemname, 0, 0644, 1)) == SEM_FAILED) {
      if((criticalmutex = sem_open(criticalsemname, O_CREAT, 0644, 1)) == SEM_FAILED) {
	//if((mutex = sem_open(semname, O_CREAT | O_EXCL, 0644, 1)) == SEM_FAILED) {
	log_errprintf( "%s: critical_wait(): cannot sem_open()", procname);
	log_errprintf(" errno %d", errno);
	log_errprintf( "(%s)\n", strerror(errno));
	perror("sem_open");
      }
    }
  }
  if(sem_wait(criticalmutex) == -1) {
    log_errprintf( "%s: critical_wait(): cannot sem_wait()", procname);
    log_errprintf( ", errno: %d", errno);
    log_errprintf( "(%s)\n", strerror(errno));
    perror("sem_wait");
  }
}

void critical_post() // JariK 2025
{
  if(sem_post(criticalmutex) == -1) {
    log_errprintf( "%s: critical_post(): cannot sem_post()", procname);
    log_errprintf( ", errno: %d", errno);
    log_errprintf( "(%s)\n", strerror(errno));
    perror("sem_post");
  }
  sem_close(criticalmutex);
  criticalmutex = NULL;
  
  signal(SIGINT, SIG_DFL);
}

void dbs_run_critical_sections()
{
  log_printf("critical_wait()\n");
  critical_wait();

  log_printf("critical_post()\n");
  critical_post();
}

static int server_getaddrinfo(unsigned char *myport, struct addrinfo **res)
{
  int status;
  struct addrinfo hints;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;                   
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  log_printf("getaddrinfo()\n"); 
  if ((status = getaddrinfo(NULL, myport, &hints, res)) != 0) {
#ifdef OLD1
    fprintf(stderr, "%s: getaddrinfo error: %s",
            procname, gai_strerror(status));
    fprintf(stderr,", error code %d\n", status);
#endif
    log_errprintf( "%s: getaddrinfo error: %s",
	    procname, gai_strerror(status));
    log_errprintf( ", error code %d\n", status);
    //fflush(stderr);
  }
  return(status);
}

static int server_socket(struct addrinfo *res)
{
  int s;

  log_printf("socket()\n"); 
  if((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
    log_errprintf("%s: socket()", procname);
    log_errprintf(", errno: %d", errno);
    log_errprintf("(%s)\n", strerror(errno));
    perror("socket");
    //fflush(stderr);
  }
  return(s);
}

static void server_bind(int s, struct addrinfo *res)
{
  log_printf("bind()\n"); 
  if(bind(s, res->ai_addr, res->ai_addrlen) == -1) {
    int errerrno = errno;
    if(errerrno == 98) {
      fprintf(stdout,"%s cannot bind, waiting to bind", procname);
      while(errerrno == 98) {
	sleep(10);
	fprintf(stdout,".");
	fflush(stdout);
	bind(s, res->ai_addr, res->ai_addrlen);
	errerrno = errno;
      }
      fprintf(stdout,"bind done!\n");
      //fflush(stdout);
    } else {
      int errerrno = errno;
      log_errprintf("%s: cannot bind()", procname);
      log_errprintf(", errno: %d", errerrno);
      log_errprintf( "(%s)\n", strerror(errerrno));
      perror("bind");
      //fflush(stderr);
    }
  }
}

static int server_listen(int s)
{
  int retval;
  
  log_printf("listen()\n"); 
  if((retval = listen(s, backlog)) == -1) { // ==!
    int errerrno = errno;
    log_errprintf("%s: cannot listen()", procname);
    log_errprintf(", errno %d", errerrno);
    log_errprintf( "(%s)\n", strerror(errerrno));
    perror("listen");
    //fflush(stderr);
  }
  
  return(retval);
}

static void server_close(int s)
{
  log_printf("close()\n"); 
  if(close(s) == -1) {
    int errerrno = errno;
    log_errprintf("%s: cannot close()", procname);
    log_errprintf(", errno %d", errerrno);
    log_errprintf( "(%s)\n", strerror(errerrno));
    perror("close");
    //fflush(stderr);
  }
}

static int server_basic_socket()
{
  int s;
  struct addrinfo *res;

  log_printf("server_getaddrinfo()\n"); 
  server_getaddrinfo(myport, &res);
  log_printf("server_socket()\n"); 
  s = server_socket(res);
  log_printf("server_bind()\n"); 
  server_bind(s, res);
  log_printf("freeaddrinfo()\n"); 
  freeaddrinfo(res);
  log_printf("server_listen()\n"); 
  server_listen(s);

  return(s);
}

#define DEBUG38 2

static void http_client(int news, struct sockaddr_in sa_cli, int addr_size, char *name)
{
  int clen = 0;
  int reads = 0;
  int first = 1;
  int bytes, totalbytes = 0;
  unsigned char buffer10[10];

  htmlparams = NULL;
  htmlin[0] = '\0';

  dbs_server_vars(sa_cli, addr_size, name);

  while(htmlparams == NULL ||
	clen - strlen(htmlparams) > 0) {
    if(!first) {
      log_printf(", ");
    }

    struct pollfd fds;
    fds.fd = news;
    fds.events = POLLIN;
    bytes = 0;
    int retval = poll(&fds, 1, 1000 * 10);
    log_printf("******poll: %d\n", retval);
    if(retval > 0) { // -1 err, 0 expire
      log_printf("read()\n");
      if((bytes = read(news, htmlin + totalbytes, htmlinlen - totalbytes)) < 0) {
	log_errprintf("%s: cannot read()\n", procname);
	perror("read");
	//fflush(stderr);
      }
      if(retval > 0)
	reads++;
    } 

    log_printf("(%d bytes)", bytes);
    log_printf("%s", htmlin);
    if(bytes == 0)
      break;
    if(bytes > 3 &&
       !isprint(htmlin[0]) &&
       !isprint(htmlin[1]) &&
       !isprint(htmlin[2])) {
      log_printf("https packet?\n");
      htmlin[0] = '\0';
      totalbytes = 0;
      bytes = 0;
      break;
    }
    *(htmlin + totalbytes + bytes) = '\0';

    totalbytes += bytes;
    if(totalbytes >= htmlinlen) {
      // plus space for '\0'
      htmlin = realloc(htmlin, htmlinlen * 2 + 1);
      htmlinlen *= 2;
    }
    
    htmlparams = dbs_html_get_params();
    unsigned char *p = htmlin;
    dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
    if(!strcmp(htmlmethod, "GET"))
      break;
    dbs_html_get_request_line_num("Content-Length", sizeof(buffer10), buffer10);
    clen = atoi(buffer10);
    first = 0;
  }

  if(totalbytes != 0) {
    
    log_printf("%d reads", reads);
    log_printf(", received %d chars", totalbytes);
    log_printf(", read %d total bytes", totalbytes);
    log_printf(", input buffer size %d chars", htmlinlen);
    log_printf(", data=\"");
    log_dump_string(htmlin);
    log_printf("\"\n");
    
    dbs_run_loop();
    
    log_printf("buffers(html):");
    for(int c = 0; c < HTML_BUFFERS; c++) { 
      log_printf(" %d:%ld", c, strlen(html_get_string(c)));
      if(strlen(html_get_string(c)) > 0) {
	if((bytes = write(news, html_get_string(c), strlen(html_get_string(c)))) == -1) {
	  log_errprintf("%s: cannot write() dbs_html[%d]\n", procname, c);
	  perror("write");
	  //fflush(stderr);
	}
      }
    }
    for(int c = 0; c < HTML_BUFFERS; c++) {
      if(strlen(html_get_string(c)))
	log_printf("%s\n",html_get_string(c));
    }
    log_printf("\n");
  }
  dbs_query_vars();
}

int beepaccept = 1;

#define DEBUG45 2
#define aDEBUG57 2

static void http_server()
{
  int quit, reset, addr_size;
  struct sockaddr_in sa_cli;

  signal(SIGPIPE, SIG_IGN);
  signal(SIGCHLD, SIG_IGN);

  // plus space for '\0'
  htmlin = malloc(htmlinlen + 1);
  
  reset = 1;
  quit = 0;
  
  for(;;) {

    log_mode = 1;
    
    if(quit) {
      break;
    }

    if(reset) {
      if(s != -1)
	close(s);
      log_printf("server_basic_socket()\n");
      s = server_basic_socket();
      reset = 0;
    }
  
    fprintf(stdout,"\n");
    fflush(stdout);
    
    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      log_errprintf("%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

    fprintf(stdout,"========================================\n");
    log_printf("dbs_time_vars()\n"); 
    dbs_time_vars();
    
    if(beepaccept) {
      fprintf(stderr,"\a\n");
      fflush(stderr);
    }
    
    pid_t pid;
    log_printf("Fork start (parent)");
    log_printf(" getpid:%d getppid:%d", getpid(), getppid()); 
    log_printf("\n"); 

    log_printf("fork()\n"); 
    if((pid = fork()) < 0) {

      log_errprintf("%s: cannot fork()", procname);
      log_errprintf(", errno %d", errno);
      log_errprintf( "(%s)\n", strerror(errno));
      fflush(stderr);
      
      perror("fork");
      fflush(stderr);

      exit(1);
    } else if(pid == 0) { // child

      log_printf("init_child()\n"); 
      init_child();

      log_mode = 2; // child
            
      log_printf("close(s)\n"); 
      close(s);
      
      log_printf("Fork start (child)");
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
      log_printf("\n");

      log_printf("close(s)\n");
      close(s);

      log_printf("http_client()\n"); 
      http_client(news, sa_cli, addr_size, "http");

      dbs_run_critical_sections(); 
     
      log_printf("server_close(news)\n"); 
      server_close(news);

      log_printf("Fork end (child)"); 
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid()); 
      log_printf("\n"); 

      log_printf("exit_child()\n"); 
      exit_child();

      log_mode = 1;
      
      exit(0);
    }
    log_printf("Fork end (parent)"); 
    log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
    log_printf("\n");

    log_printf("server_close()\n"); 
    server_close(news);
  } // for(;;)
}

static void https_client(int news, SSL_CTX *ctx, struct sockaddr_in sa_cli, int addr_size, char *name)
{
  int status, ok;
  unsigned char buffer10[10];

  SSL *ssl;
  X509 *peer_cert;

  unsigned long long useconds;

  useconds = dbs_get_useconds();
  
  ok = 1;

  dbs_server_vars(sa_cli, addr_size, name);
  
  log_printf("SSL_new()\n"); 
  if((ssl = SSL_new(ctx)) == NULL) {
    fprintf(stdout,"\n%s: cannot SSL_new()", procname);
  }
  
  log_printf("SSL_set_fd()\n"); 
  if(SSL_set_fd(ssl, news) != 1) {
    fprintf(stdout,"\n%s: cannot SSL_set_fd()\n", procname);
  }
  
  log_printf("SSL_accept()\n"); 
  if((status = SSL_accept(ssl)) < 0) {
    log_printf("%s: cannot SSL_accept(), retval: %d\n", procname, status);
  }
  
  log_printf("SSL_get_peer_certificate()");
  peer_cert = SSL_get_peer_certificate(ssl);
  if(peer_cert == NULL) {
    log_printf(", No peer certificate");
  }
  log_printf("\n");
  
  int clen = 0;
  int reads = 0;
  int first = 1;
  int bytes, totalbytes = 0;

  htmlparams = NULL;

  htmlin[0] = '\0';
  while(htmlparams == NULL || clen - strlen(htmlparams) > 0) {
    if(!first) {
      log_printf(", ");
    } 
    int tries = 0;
    for(;;) {
      log_printf("SSL_read()\n"); 
      bytes = SSL_read(ssl, htmlin + totalbytes, htmlinlen - totalbytes);

      int errerrno, err, err2;

      errerrno = errno;
      err = SSL_get_error(ssl, bytes);
      err2 = ERR_get_error();

      log_printf("SSL_read()");
      log_printf(", retval %d", bytes);
      log_printf(", errno: %d", errerrno);
      log_printf(", SSL_get_error(): %d", err);
      log_printf(", ERR_get_error(): %d", err2);

      
      while((err2 = ERR_get_error()) != 0)
	log_printf(", %d", err2);
      log_printf("\n");
      
      if(bytes >= 0) {
	reads++;
	break;
      }
      if(bytes < 0) {
	if(++tries < 5 && (err == 1 || err == 5)) {
	  usleep(1024 * 512 / 5);
	  log_printf(" try:%d", tries);
	  continue;
	}
	log_printf("cannot SSL_read()\n");
	ok = 0;
	break;
      } // if(bytes
      break;
    } // for(;;)
    log_printf("(%d bytes)\n", bytes);
    log_printf("%s\n", htmlin);
    if(!ok)
      break;

    *(htmlin + totalbytes + bytes) = '\0';
    if(bytes == 0)
      break;

    totalbytes += bytes;
    if(totalbytes >= htmlinlen) {
      // plus space for '\0'
      htmlin = realloc(htmlin, htmlinlen * 2 + 1);
      htmlinlen *= 2;
    }
    
    htmlparams = dbs_html_get_params();
    unsigned char *p = htmlin;
    dbs_html_parse_string(sizeof(htmlmethod), htmlmethod, &p);
    if(!strcmp(htmlmethod, "GET"))
      break;
    dbs_html_get_request_line_num("Content-Length",
				  sizeof(buffer10), buffer10);
    clen = atoi(buffer10);
    first = 0;
  }

  log_printf("%d reads", reads);
  log_printf(", received %d chars", totalbytes);
  log_printf(", read %d total bytes", totalbytes);
  log_printf(", input buffer size %d chars", htmlinlen);
  log_printf(", data=\"\n");
  log_dump_string(htmlin);
  log_printf("\"\n");
  
  strncpy(htmlsslcipher, SSL_get_cipher(ssl), sizeof(htmlsslcipher));
  
  if(ok) 
    dbs_run_loop();
  
  if(ok) {
    log_printf("buffers(ssl):");
    for(int c = 0; c < HTML_BUFFERS; c++) {
      log_printf(" %d:%ld", c, strlen(html_get_string(c)));
      if(strlen(html_get_string(c)) > 0) {
	log_printf("SSL_write()\n");
	if((status = SSL_write(ssl, html_get_string(c), strlen(html_get_string(c)))) < 1) {
	  log_errprintf("%s: cannot SSL_write(), buffer %d, status: %d, SSL error: %d\n",
		  procname, c, status, SSL_get_error(ssl, status));
	}
      }
    }
    for(int c = 0; c < HTML_BUFFERS; c++) {
      if(strlen(html_get_string(c)))
	log_printf("%s\n", html_get_string(c));
    }
  }
  log_printf("SSL connection using %s\n", htmlsslcipher);

  log_printf("dbs_query_vars()\n");
  dbs_query_vars();
  
  log_printf("SSL_free()\n");
  SSL_free(ssl);
}

static void dbs_file_digest(char *filename, unsigned char *hash)
{
  int c;
  unsigned char buffer[1024];
  FILE *fp1;
  HashCtx ctx;
  
  HashInit(&ctx);
  if((fp1 = fopen(filename, "rb")) != NULL) {
    while((c = fread(buffer, 1, sizeof(buffer), fp1)) > 0)
      HashUpdate(&ctx, buffer, c);
    fclose(fp1);
  }
  HashFinal(hash, &ctx);  
}

unsigned char cert_digest[HashLen]; // current certificate digests
unsigned char privatekey_digest[HashLen];

static SSL_CTX *server_https_init()
{
  SSL_METHOD *method = NULL;
  SSL_CTX *ctx = NULL;
  unsigned char cert_digest2[HashLen]; // before change
  unsigned char privatekey_digest2[HashLen];
  unsigned char cert_digest3[HashLen]; // after change
  unsigned char privatekey_digest3[HashLen];

  log_printf("SSL_library_init()\n");
  SSL_library_init();
  
  log_printf("OpenSSL_add_ssl_algorithms()\n");
  OpenSSL_add_ssl_algorithms();
  
  log_printf("OpenSSL_add_ciphers()\n");
  OpenSSL_add_all_ciphers();
  
  log_printf("OpenSSL_load_error_strings()\n");
  SSL_load_error_strings();
  
  log_printf("SSLv23_server_method()\n");
  if((method = (SSL_METHOD *)    
      SSLv23_server_method()) == NULL) {
    log_errprintf("%s: cannot SSLv3_server_method()\n", procname);
    //fflush(stderr);
  }
  
  log_printf("SSL_CTX_new()\n");
  if((ctx = SSL_CTX_new(method)) == NULL) {
    log_errprintf("%s: cannot SSL_CTX_new()\n", procname);
  }

  for(;;) { // 2025 JariK
    dbs_file_digest(cert_file, cert_digest2); // save before change digests
    dbs_file_digest(privatekey_file, privatekey_digest2);
  
    log_printf("SSL_CTX_use_certificate_file()\n");
    if(SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_certificate_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }
    
    log_printf("SSL_CTX_use_PrivateKey_file()\n");
    if(SSL_CTX_use_PrivateKey_file(ctx, privatekey_file, SSL_FILETYPE_PEM) != 1) {
      int err2;
      err2 = ERR_get_error();
      log_errprintf("%s: cannot SSL_CTX_use_PrivateKey_file()", procname);
      log_errprintf(", ERR_get_error(): %d", err2);
      while((err2 = ERR_get_error()) != 0)
	log_errprintf(", %d", err2);
      log_errprintf("\n");
    }

    dbs_file_digest(cert_file, cert_digest3); // save after change digests
    dbs_file_digest(privatekey_file, privatekey_digest3);

    // redo if before stamps are different from after stamps
    
    if(memcmp(cert_digest2, cert_digest3, sizeof(cert_digest3)) ||
       memcmp(privatekey_digest2,privatekey_digest3, sizeof(privatekey_digest3)))
      continue;

    memcpy(cert_digest, cert_digest2, sizeof(cert_digest2));
    memcpy(privatekey_digest, privatekey_digest2, sizeof(privatekey_digest2));
    
    log_dump("cert", 32, cert_digest, 32);  // first 32 bytes
    log_dump("private", 32, privatekey_digest, 32);  // first 32 bytes
  
    break;
  }

  log_printf("SSL_CTX_load_verify_locations()\n");
  if(SSL_CTX_load_verify_locations(ctx, cert_file, NULL) != 1) {
    int err2;
    err2 = ERR_get_error();
    log_errprintf("\n%s: cannot SSL_CTX_load_verify_locations()", procname);
    log_errprintf(", ERR_get_error(): %d", err2);
    while((err2 = ERR_get_error()) != 0)
      log_errprintf(", %d", err2);
    log_errprintf("\n");
  }
  return(ctx);
}

static int https_keyschange_needed() // 2025 JariK
{
  unsigned char cert_digest2[HashLen];
  unsigned char privatekey_digest2[HashLen];

  dbs_file_digest(cert_file, cert_digest2);
  dbs_file_digest(privatekey_file, privatekey_digest2);

  log_dump("cert", 32, cert_digest2, 32);  // first 32 bytes
  log_dump("private", 32, privatekey_digest2, 32);  // first 32 bytes
  
  if(memcmp(cert_digest, cert_digest2, sizeof(cert_digest2)) ||
     memcmp(privatekey_digest, privatekey_digest2, sizeof(privatekey_digest2)))
    return(1);
  else
    return(0);
}

static void https_server()
{
  int quit, reset, addr_size;
  struct sockaddr_in sa_cli;
  SSL_CTX *ctx = NULL;

  signal(SIGPIPE, SIG_IGN);
  signal(SIGCHLD, SIG_IGN);

  // plus space for '\0'
  htmlin = malloc(htmlinlen+1);
  
  reset = 1;
  quit = 0;
  
  for(;;) {

    log_mode = 1;
    
    if(quit) {
      break;
    }

    if(https_keyschange_needed())
      reset = 1;
    
    if(reset) {
      if(ctx != NULL) {
	log_printf("SSL_CTX_free()\n");
	SSL_CTX_free(ctx);  
      }
      if(s != -1)
	close(s);
      log_printf("server_https_init()\n");
      ctx = server_https_init();
      log_printf("server_basic_socket()\n");
      s = server_basic_socket();
      reset = 0;
    }
  
    fprintf(stdout,"\n");
    fflush(stdout);
    
    addr_size = sizeof(sa_cli);
    log_printf("accept()\n"); 
    if((news = accept(s, (struct sockaddr *)&sa_cli, &addr_size)) == -1) {
      log_errprintf("%s: cannot accept()\n", procname);
      perror("accept");
      //fflush(stderr);
    }

    fprintf(stdout,"========================================\n");
    log_printf("dbs_time_vars()\n"); 
    dbs_time_vars();
    
    pid_t pid;
    log_printf("Fork start (parent)");
    log_printf(" getpid:%d getppid:%d", getpid(), getppid()); 
    log_printf("\n"); 

    log_printf("fork()\n"); 
    if((pid = fork()) < 0) {

      log_errprintf("%s: cannot fork()", procname);
      log_errprintf(", errno %d", errno);
      log_errprintf( "(%s)\n", strerror(errno));
      
      perror("fork");
      fflush(stderr);

      exit(1);
    } else if(pid == 0) { // child

      log_printf("init_child()\n"); 
      init_child();

      log_mode = 2; // child
      	
      log_printf("close(s)\n"); 
      close(s);

      log_printf("close(s)\n");
      close(s);

      log_printf("https_client()\n");
      https_client(news, ctx, sa_cli, addr_size, "https");

      dbs_run_critical_sections(); 
     
      log_printf("server_close(news)\n"); 
      server_close(news);

      log_printf("Fork end (child)"); 
      log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid()); 
      log_printf("\n"); 

      log_printf("exit_child()\n"); 
      exit_child();

      exit(0);
    }
    log_printf("Fork end (parent)"); 
    log_printf(" pid:%d getpid:%d getppid:%d", pid, getpid(), getppid());
    log_printf("\n");

    log_printf("server_close()\n"); 
    server_close(news);
  } // for(;;)
}

int http = 1;
int https = 0;

void main(int argc, char *argv[])
{
  int c;

  procname = argv[0];

  unsigned char filedigest[HashLen];

  dbs_file_digest("/proc/self/exe", filedigest);
  htmldigest[0] = '\0';
  for(int c = 0; c < HashLen; c++) {
    char twodigits[3];
    sprintf(twodigits, "%02x", filedigest[c]);
    strcat(htmldigest, twodigits);
  }

  //
  // look thru command line parameters
  //

  for(c = 1; c < argc; c++) {

    if(!strncmp("-", argv[c], 1)) {
      if(!strncmp("--port", argv[c], 6)) {
	if(*(argv[c] + 6) != '\0')
	  myport = argv[c] + 6;
	else if(c + 1 < argc) {
	  myport = argv[c + 1];
	  c++;
	}

      } else if(!strcmp("--https", argv[c])) {
	https = 1;
	http = 0;

      } else if(!strcmp("--http", argv[c])) {
	http = 1;
	https = 0;

      } else {
	log_errprintf("%s: invalid option %s\n", procname, argv[c]);
	exit(1);
      }      
    }
  }

  log_errprintf("starting dbs3 \"%s\"", programname);
  log_errprintf(", port:%s", myport);
  if(http)
    log_errprintf(", http");
  if(https)
    log_errprintf(", https");
  log_errprintf("\n");


  if(http)
    http_server();
  if(https)
    https_server();
}

Published
Categorized as muuta

Leave a comment