picolisp

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/picolisp.git/
Log | Files | Refs | README | LICENSE

httpGate.c (7647B)


      1 /* 04feb13abu
      2  * (c) Software Lab. Alexander Burger
      3  */
      4 
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 #include <fcntl.h>
      9 #include <errno.h>
     10 #include <ctype.h>
     11 #include <string.h>
     12 #include <signal.h>
     13 #include <time.h>
     14 #include <sys/time.h>
     15 #include <sys/stat.h>
     16 #include <netdb.h>
     17 #include <arpa/inet.h>
     18 #include <netinet/in.h>
     19 #include <sys/socket.h>
     20 
     21 #include <openssl/pem.h>
     22 #include <openssl/ssl.h>
     23 #include <openssl/err.h>
     24 
     25 typedef enum {NO,YES} bool;
     26 
     27 static int Http1;
     28 
     29 static char Head_410[] =
     30    "HTTP/1.0 410 Gone\r\n"
     31    "Server: PicoLisp\r\n"
     32    "Content-Type: text/html; charset=utf-8\r\n"
     33    "\r\n";
     34 
     35 static void giveup(char *msg) {
     36    fprintf(stderr, "httpGate: %s\n", msg);
     37    exit(2);
     38 }
     39 
     40 static inline bool pre(char *p, char *s) {
     41    while (*s)
     42       if (*p++ != *s++)
     43          return NO;
     44    return YES;
     45 }
     46 
     47 static int slow(SSL *ssl, int fd, char *p, int cnt) {
     48    int n;
     49 
     50    while ((n = ssl? SSL_read(ssl, p, cnt) : read(fd, p, cnt)) < 0)
     51       if (errno != EINTR)
     52          return 0;
     53    return n;
     54 }
     55 
     56 static int rdLine(SSL *ssl, int fd, char *p, int cnt) {
     57    int n, len;
     58 
     59    for (len = 0;;) {
     60       if ((n = ssl? SSL_read(ssl, p, cnt) : read(fd, p, cnt)) <= 0) {
     61          if (!n || errno != EINTR)
     62             return 0;
     63       }
     64       else {
     65          len += n;
     66          if (memchr(p, '\n', n))
     67             return len;
     68          p += n;
     69          if ((cnt -= n) == 0)
     70             return 0;
     71       }
     72    }
     73 }
     74 
     75 static void wrBytes(int fd, char *p, int cnt) {
     76    int n;
     77 
     78    do
     79       if ((n = write(fd, p, cnt)) >= 0)
     80          p += n, cnt -= n;
     81       else if (errno != EINTR)
     82          exit(1);
     83    while (cnt);
     84 }
     85 
     86 static void sslWrite(SSL *ssl, void *p, int cnt) {
     87    if (SSL_write(ssl, p, cnt) <= 0)
     88       exit(1);
     89 }
     90 
     91 static int gatePort(unsigned short port) {
     92    int sd, n;
     93    struct sockaddr_in6 addr;
     94 
     95    if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
     96       exit(1);
     97    n = 0;
     98    if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &n, sizeof(n)) < 0)
     99       exit(1);
    100    memset(&addr, 0, sizeof(addr));
    101    addr.sin6_family = AF_INET6;
    102    addr.sin6_addr = in6addr_any;
    103    n = 1;
    104    if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0)
    105       exit(1);
    106    addr.sin6_port = htons(port);
    107    if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    108       exit(1);
    109    if (listen(sd,5) < 0)
    110       exit(1);
    111    return sd;
    112 }
    113 
    114 static int gateConnect(unsigned short port) {
    115    int sd;
    116    struct sockaddr_in6 addr;
    117 
    118    if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
    119       exit(1);
    120    memset(&addr, 0, sizeof(addr));
    121    addr.sin6_family = AF_INET6;
    122    addr.sin6_addr = in6addr_loopback;
    123    addr.sin6_port = htons(port);
    124    return connect(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0? -1 : sd;
    125 }
    126 
    127 
    128 static pid_t Buddy;
    129 
    130 static void doSigAlarm(int n __attribute__((unused))) {
    131    kill(Buddy, SIGTERM);
    132    exit(0);
    133 }
    134 
    135 static void doSigUsr1(int n __attribute__((unused))) {
    136    alarm(420);
    137 }
    138 
    139 int main(int ac, char *av[]) {
    140    int cnt = ac>4? ac-3 : 1, ports[cnt], n, sd, cli, srv;
    141    struct sockaddr_in6 addr;
    142    char s[INET6_ADDRSTRLEN];
    143    char *gate;
    144    SSL_CTX *ctx;
    145    SSL *ssl;
    146 
    147    if (ac < 3)
    148       giveup("port dflt [pem [alt ..]]");
    149 
    150    sd = gatePort(atoi(av[1]));  // e.g. 80 or 443
    151    ports[0] = atoi(av[2]);  // e.g. 8080
    152    if (ac == 3 || *av[3] == '\0')
    153       ssl = NULL,  gate = "X-Pil: *Gate=http\r\nX-Pil: *Adr=%s\r\n";
    154    else {
    155       SSL_library_init();
    156       SSL_load_error_strings();
    157       if (!(ctx = SSL_CTX_new(SSLv23_server_method())) ||
    158             !SSL_CTX_use_certificate_file(ctx, av[3], SSL_FILETYPE_PEM) ||
    159                !SSL_CTX_use_PrivateKey_file(ctx, av[3], SSL_FILETYPE_PEM) ||
    160                                           !SSL_CTX_check_private_key(ctx) ) {
    161          ERR_print_errors_fp(stderr);
    162          giveup("SSL init");
    163       }
    164       ssl = SSL_new(ctx),  gate = "X-Pil: *Gate=https\r\nX-Pil: *Adr=%s\r\n";
    165    }
    166    for (n = 1; n < cnt; ++n)
    167       ports[n] = atoi(av[n+3]);
    168 
    169    signal(SIGCHLD,SIG_IGN);  /* Prevent zombies */
    170    if ((n = fork()) < 0)
    171       giveup("detach");
    172    if (n)
    173       return 0;
    174    setsid();
    175 
    176    for (;;) {
    177       socklen_t len = sizeof(addr);
    178       if ((cli = accept(sd, (struct sockaddr*)&addr, &len)) >= 0 && (n = fork()) >= 0) {
    179          if (!n) {
    180             int fd, port, i;
    181             char *p, *q, buf[4096], buf2[64];
    182 
    183             close(sd);
    184 
    185             alarm(420);
    186             if (ssl) {
    187                SSL_set_fd(ssl, cli);
    188                if (SSL_accept(ssl) < 0)
    189                   return 1;
    190             }
    191             n = rdLine(ssl, cli, buf, sizeof(buf));
    192             alarm(0);
    193             if (n < 6)
    194                return 1;
    195 
    196             /* "GET /url HTTP/1.x"
    197              * "GET /8080/url HTTP/1.x"
    198              * "POST /url HTTP/1.x"
    199              * "POST /8080/url HTTP/1.x"
    200              */
    201             if (pre(buf, "GET /"))
    202                p = buf + 5;
    203             else if (pre(buf, "POST /"))
    204                p = buf + 6;
    205             else
    206                return 1;
    207 
    208             port = (int)strtol(p, &q, 10);
    209             if (q == p  ||  *q != ' ' && *q != '/')
    210                port = ports[0],  q = p;
    211             else if (port < cnt) {
    212                if ((port = ports[port]) < 0)
    213                   return 1;
    214             }
    215             else if (port < 1024)
    216                return 1;
    217             else
    218                for (i = 1; i < cnt; ++i)
    219                   if (port == -ports[i])
    220                      return 1;
    221 
    222             if ((srv = gateConnect((unsigned short)port)) < 0) {
    223                if (!memchr(q,'~', buf + n - q))
    224                   return 1;
    225                if ((fd = open("void", O_RDONLY)) < 0)
    226                   return 1;
    227                alarm(420);
    228                if (ssl)
    229                   sslWrite(ssl, Head_410, strlen(Head_410));
    230                else
    231                   wrBytes(cli, Head_410, strlen(Head_410));
    232                alarm(0);
    233                while ((n = read(fd, buf, sizeof(buf))) > 0) {
    234                   alarm(420);
    235                   if (ssl)
    236                      sslWrite(ssl, buf, n);
    237                   else
    238                      wrBytes(cli, buf, n);
    239                   alarm(0);
    240                }
    241                return 0;
    242             }
    243 
    244             Http1 = 0;
    245             wrBytes(srv, buf, p - buf);
    246             if (*q == '/')
    247                ++q;
    248             p = q;
    249             while (*p++ != '\n')
    250                if (p >= buf + n)
    251                   return 1;
    252             wrBytes(srv, q, p - q);
    253             if (pre(p-10, "HTTP/1."))
    254                Http1 = *(p-3) - '0';
    255             inet_ntop(AF_INET6, &addr.sin6_addr, s, INET6_ADDRSTRLEN);
    256             wrBytes(srv, buf2, sprintf(buf2, gate, s));
    257             wrBytes(srv, p, buf + n - p);
    258 
    259             signal(SIGALRM, doSigAlarm);
    260             signal(SIGUSR1, doSigUsr1);
    261             if (Buddy = fork()) {
    262                for (;;) {
    263                   alarm(420);
    264                   n = slow(ssl, cli, buf, sizeof(buf));
    265                   alarm(0);
    266                   if (!n)
    267                      break;
    268                   wrBytes(srv, buf, n);
    269                }
    270                shutdown(cli, SHUT_RD);
    271                shutdown(srv, SHUT_WR);
    272             }
    273             else {
    274                Buddy = getppid();
    275                while ((n = read(srv, buf, sizeof(buf))) > 0) {
    276                   kill(Buddy, SIGUSR1);
    277                   alarm(420);
    278                   if (ssl)
    279                      sslWrite(ssl, buf, n);
    280                   else
    281                      wrBytes(cli, buf, n);
    282                   alarm(0);
    283                }
    284                shutdown(srv, SHUT_RD);
    285                shutdown(cli, SHUT_WR);
    286             }
    287             return 0;
    288          }
    289          close(cli);
    290       }
    291    }
    292 }