mplisp

miniPicoLisp with FFI and modules for Buddy BDD library, OpenGL, Gtk and GMP
git clone https://logand.com/git/mplisp.git/
Log | Files | Refs

httpGate.c (9022B)


      1 /* 14feb08abu
      2  * (c) Software Lab. Alexander Burger
      3  */
      4 
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <stdarg.h>
      8 #include <unistd.h>
      9 #include <fcntl.h>
     10 #include <errno.h>
     11 #include <ctype.h>
     12 #include <string.h>
     13 #include <signal.h>
     14 #include <netdb.h>
     15 #include <time.h>
     16 #include <sys/time.h>
     17 #include <sys/stat.h>
     18 #include <sys/socket.h>
     19 #include <arpa/inet.h>
     20 #include <netinet/tcp.h>
     21 #include <netinet/in.h>
     22 #include <syslog.h>
     23 
     24 #include <openssl/pem.h>
     25 #include <openssl/ssl.h>
     26 #include <openssl/err.h>
     27 
     28 typedef enum {NO,YES} bool;
     29 
     30 static bool Bin;
     31 static int Http1, Timeout;
     32 
     33 static char Head_200[] =
     34    "HTTP/1.0 200 OK\r\n"
     35    "Server: PicoLisp\r\n"
     36    "Content-Type: text/html; charset=utf-8\r\n"
     37    "\r\n";
     38 
     39 static void logger(char *fmt, ...) {
     40    va_list ap;
     41 
     42    va_start(ap,fmt);
     43    vsyslog(LOG_ERR, fmt, ap);
     44    va_end(ap);
     45 }
     46 
     47 static void giveup(char *msg) {
     48    fprintf(stderr, "httpGate: %s\n", msg);
     49    exit(2);
     50 }
     51 
     52 static inline bool pre(char *p, char *s) {
     53    while (*s)
     54       if (*p++ != *s++)
     55          return NO;
     56    return YES;
     57 }
     58 
     59 static char *ses(char *buf, int port, int *len) {
     60    int np;
     61    char *p, *q;
     62 
     63    if (Bin || Http1 == 0)
     64       return buf;
     65    if (pre(buf, "GET /")) {
     66       np = (int)strtol(buf+5, &q, 10);
     67       if (q == buf+5 || *q != '/' || np < 1024 || np > 65535)
     68          return buf;
     69       p = q++ - 4;
     70       do
     71          if (*q < '0' || *q > '9')
     72             return buf;
     73       while (*++q != '~');
     74       if (np == port) {
     75          p[0] = 'G',  p[1] = 'E',  p[2] = 'T',  p[3] = ' ';
     76          *len -= p - buf;
     77          return p;
     78       }
     79       return NULL;
     80    }
     81    if (pre(buf, "POST /")) {
     82       np = (int)strtol(buf+6, &q, 10);
     83       if (q == buf+6 || *q != '/' || np < 1024 || np > 65535)
     84          return buf;
     85       p = q++ - 5;
     86       do
     87          if (*q < '0' || *q > '9')
     88             return buf;
     89       while (*++q != '~');
     90       if (np == port) {
     91          p[0] = 'P',  p[1] = 'O',  p[2] = 'S',  p[3] = 'T',  p[4] = ' ';
     92          *len -= p - buf;
     93          return p;
     94       }
     95       return NULL;
     96    }
     97    return buf;
     98 }
     99 
    100 static int slow(SSL *ssl, int fd, char *p, int cnt) {
    101    int n;
    102 
    103    while ((n = ssl? SSL_read(ssl, p, cnt) : read(fd, p, cnt)) < 0)
    104       if (errno != EINTR)
    105          return 0;
    106    return n;
    107 }
    108 
    109 static void wrBytes(int fd, char *p, int cnt) {
    110    int n;
    111 
    112    do
    113       if ((n = write(fd, p, cnt)) >= 0)
    114          p += n, cnt -= n;
    115       else if (errno != EINTR) {
    116          logger("%d wrBytes error", fd);
    117          exit(1);
    118       }
    119    while (cnt);
    120 }
    121 
    122 static void sslWrite(SSL *ssl, void *p, int cnt) {
    123    if (SSL_write(ssl, p, cnt) <= 0) {
    124       logger("SSL_write error");
    125       exit(1);
    126    }
    127 }
    128 
    129 static int gateSocket(void) {
    130    int sd;
    131 
    132    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    133       logger("socket error");
    134       exit(1);
    135    }
    136    return sd;
    137 }
    138 
    139 static int gatePort(int port) {
    140    int n, sd;
    141    struct sockaddr_in addr;
    142 
    143    memset(&addr, 0, sizeof(addr));
    144    addr.sin_family = AF_INET;
    145    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    146    addr.sin_port = htons((unsigned short)port);
    147    n = 1,  setsockopt(sd = gateSocket(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
    148    if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    149       logger("%d bind error", sd);
    150       exit(1);
    151    }
    152    if (listen(sd,5) < 0) {
    153       logger("%d listen error", sd);
    154       exit(1);
    155    }
    156    return sd;
    157 }
    158 
    159 static int gateConnect(unsigned short port) {
    160    int sd;
    161    struct sockaddr_in addr;
    162 
    163    memset(&addr, 0, sizeof(addr));
    164    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    165    sd = gateSocket();
    166    addr.sin_family = AF_INET;
    167    addr.sin_port = htons(port);
    168    return connect(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0? -1 : sd;
    169 }
    170 
    171 
    172 static pid_t Buddy;
    173 
    174 static void doSigAlarm(int n __attribute__((unused))) {
    175    logger("Timeout %d", Timeout);
    176    kill(Buddy, SIGTERM);
    177    exit(0);
    178 }
    179 
    180 static void doSigUsr1(int n __attribute__((unused))) {
    181    alarm(Timeout);
    182 }
    183 
    184 int main(int ac, char *av[]) {
    185    int cnt = ac>4? ac-3 : 1, ports[cnt], n, sd, cli, srv;
    186    struct sockaddr_in addr;
    187    char *gate;
    188    SSL_CTX *ctx;
    189    SSL *ssl;
    190 
    191    if (ac < 3)
    192       giveup("port dflt [pem [alt ..]]");
    193 
    194    sd = gatePort(atoi(av[1]));  // e.g. 80 or 443
    195    ports[0] = atoi(av[2]);  // e.g. 8080
    196    if (ac == 3 || *av[3] == '\0')
    197       ssl = NULL,  gate = "Gate: http %s\r\n";
    198    else {
    199       SSL_library_init();
    200       SSL_load_error_strings();
    201       if (!(ctx = SSL_CTX_new(SSLv23_server_method())) ||
    202             !SSL_CTX_use_certificate_file(ctx, av[3], SSL_FILETYPE_PEM) ||
    203                !SSL_CTX_use_PrivateKey_file(ctx, av[3], SSL_FILETYPE_PEM) ||
    204                                           !SSL_CTX_check_private_key(ctx) ) {
    205          ERR_print_errors_fp(stderr);
    206          giveup("SSL init");
    207       }
    208       ssl = SSL_new(ctx),  gate = "Gate: https %s\r\n";
    209    }
    210    for (n = 1; n < cnt; ++n)
    211       ports[n] = atoi(av[n+3]);
    212 
    213    signal(SIGCHLD,SIG_IGN);  /* Prevent zombies */
    214    if ((n = fork()) < 0)
    215       giveup("detach");
    216    if (n)
    217       return 0;
    218    setsid();
    219 
    220    openlog("httpGate", LOG_CONS|LOG_PID, 0);
    221    for (;;) {
    222       socklen_t len = sizeof(addr);
    223       if ((cli = accept(sd, (struct sockaddr*)&addr, &len)) >= 0 && (n = fork()) >= 0) {
    224          if (!n) {
    225             int fd, port;
    226             char *p, *q, buf[4096], buf2[64];
    227 
    228             close(sd);
    229 
    230             alarm(Timeout = 420);
    231             if (ssl) {
    232                SSL_set_fd(ssl, cli);
    233                if (SSL_accept(ssl) < 0)
    234                   return 1;
    235                n = SSL_read(ssl, buf, sizeof(buf));
    236             }
    237             else
    238                n = read(cli, buf, sizeof(buf));
    239             alarm(0);
    240             if (n < 6)
    241                return 1;
    242 
    243             /* "@8080 "
    244              * "GET /url HTTP/1.x"
    245              * "GET /8080/url HTTP/1.x"
    246              * "POST /url HTTP/1.x"
    247              * "POST /8080/url HTTP/1.x"
    248              */
    249             Bin = NO;
    250             if (buf[0] == '@')
    251                p = buf + 1,  Bin = YES,  Timeout = 3600;
    252             else if (pre(buf, "GET /"))
    253                p = buf + 5;
    254             else if (pre(buf, "POST /"))
    255                p = buf + 6;
    256             else
    257                return 1;
    258 
    259             port = (int)strtol(p, &q, 10);
    260             if (q == p  ||  *q != ' ' && *q != '/')
    261                port = ports[0],  q = p;
    262             else if (port < cnt)
    263                port = ports[port];
    264             else if (port < 1024)
    265                return 1;
    266 
    267             if ((srv = gateConnect((unsigned short)port)) < 0) {
    268                logger("Can't connect to %d", port);
    269                if (!memchr(q,'~', buf + n - q)) {
    270                   buf[n] = '\0';
    271                   logger("Bad request: %s", buf);
    272                   return 1;
    273                }
    274                if ((fd = open("void", O_RDONLY)) < 0)
    275                   return 1;
    276                alarm(Timeout);
    277                if (ssl)
    278                   sslWrite(ssl, Head_200, strlen(Head_200));
    279                else
    280                   wrBytes(cli, Head_200, strlen(Head_200));
    281                alarm(0);
    282                while ((n = read(fd, buf, sizeof(buf))) > 0) {
    283                   alarm(Timeout);
    284                   if (ssl)
    285                      sslWrite(ssl, buf, n);
    286                   else
    287                      wrBytes(cli, buf, n);
    288                   alarm(0);
    289                }
    290                return 0;
    291             }
    292 
    293             Http1 = 0;
    294             if (buf[0] == '@')
    295                p = q + 1;
    296             else {
    297                wrBytes(srv, buf, p - buf);
    298                if (*q == '/')
    299                   ++q;
    300                p = q;
    301                while (*p++ != '\n')
    302                   if (p >= buf + n) {
    303                      buf[n] = '\0';
    304                      logger("Bad header: %s", buf);
    305                      return 1;
    306                   }
    307                wrBytes(srv, q, p - q);
    308                if (pre(p-10, "HTTP/1."))
    309                   Http1 = *(p-3) - '0';
    310                wrBytes(srv, buf2, sprintf(buf2, gate, inet_ntoa(addr.sin_addr)));
    311             }
    312             wrBytes(srv, p, buf + n - p);
    313 
    314             signal(SIGALRM, doSigAlarm);
    315             signal(SIGUSR1, doSigUsr1);
    316             if (Buddy = fork()) {
    317                for (;;) {
    318                   alarm(Timeout);
    319                   n = slow(ssl, cli, buf, sizeof(buf));
    320                   alarm(0);
    321                   if (!n || !(p = ses(buf, port, &n)))
    322                      break;
    323                   wrBytes(srv, p, n);
    324                }
    325                shutdown(cli, SHUT_RD);
    326                shutdown(srv, SHUT_WR);
    327             }
    328             else {
    329                Buddy = getppid();
    330                while ((n = read(srv, buf, sizeof(buf))) > 0) {
    331                   kill(Buddy, SIGUSR1);
    332                   alarm(Timeout);
    333                   if (ssl)
    334                      sslWrite(ssl, buf, n);
    335                   else
    336                      wrBytes(cli, buf, n);
    337                   alarm(0);
    338                }
    339                shutdown(srv, SHUT_RD);
    340                shutdown(cli, SHUT_WR);
    341             }
    342             return 0;
    343          }
    344          close(cli);
    345       }
    346    }
    347 }