URI:
       phroxy.c - phroxy - Gopher to HTTP proxy
  HTML git clone git://git.z3bra.org/phroxy.git
   DIR Log
   DIR Files
   DIR Refs
   DIR LICENSE
       ---
       phroxy.c (9594B)
       ---
            1 #include <err.h>
            2 #include <errno.h>
            3 #include <limits.h>
            4 #include <netdb.h>
            5 #include <signal.h>
            6 #include <stdio.h>
            7 #include <stdlib.h>
            8 #include <string.h>
            9 #include <time.h>
           10 #include <unistd.h>
           11 
           12 #include <sys/types.h>
           13 #include <sys/socket.h>
           14 #include <sys/socket.h>
           15 
           16 /* supported items */
           17 enum {
           18         ITEM_0,
           19         ITEM_1,
           20         ITEM_2,
           21         ITEM_3,
           22         ITEM_4,
           23         ITEM_5,
           24         ITEM_6,
           25         ITEM_7,
           26         ITEM_8,
           27         ITEM_9,
           28         ITEM_I,
           29         ITEM_g,
           30         ITEM_h,
           31         ITEM_s,
           32         ITEM_P,
           33         ITEM_d,
           34 };
           35 
           36 #include "config.h"
           37 
           38 void *
           39 xreallocarray(void *m, const size_t n, const size_t s)
           40 {
           41         void *nm;
           42 
           43         if (n == 0 || s == 0) {
           44                 free(m);
           45                 return NULL;
           46         }
           47         if (s && n > (size_t)-1/s)
           48                 errx(1, "realloc: overflow");
           49         if (!(nm = realloc(m, n * s)))
           50                 errx(1, "realloc: %s", strerror(errno));
           51 
           52         return nm;
           53 }
           54 
           55 
           56 static int
           57 connectto(const char *host, const char *port)
           58 {
           59         static const struct addrinfo hints = {
           60             .ai_family = AF_UNSPEC,
           61             .ai_socktype = SOCK_STREAM,
           62             .ai_protocol = IPPROTO_TCP,
           63         };
           64         struct addrinfo *addrs, *addr;
           65         struct timeval tv;
           66         int r, sock = -1;
           67 
           68         if ((r = getaddrinfo(host, port, &hints, &addrs)))
           69                 return -1;
           70 
           71         /* connection timeout */
           72         tv.tv_sec  = 5;
           73         tv.tv_usec = 0;
           74 
           75         for (addr = addrs; addr; addr = addr->ai_next) {
           76                 if ((sock = socket(addr->ai_family, addr->ai_socktype,
           77                                    addr->ai_protocol)) < 0)
           78                         continue;
           79                 if ((r = connect(sock, addr->ai_addr, addr->ai_addrlen)) < 0) {
           80                         close(sock);
           81                         continue;
           82                 }
           83                 break;
           84         }
           85 
           86         freeaddrinfo(addrs);
           87 
           88         if (sock < 0 || r < 0)
           89                 return -1;
           90 
           91         setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv));
           92         setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
           93 
           94         return sock;
           95 }
           96 
           97 int
           98 sendselector(int sock, const char *selector, const char *search)
           99 {
          100         char *msg, *p;
          101         char *fmt = "%s\r\n";
          102         size_t ln;
          103         ssize_t n;
          104 
          105         ln = strlen(selector) + 3;
          106         if (search) {
          107                 fmt = "%s\t%s\r\n";
          108                 ln += strlen(search) + 1;
          109         }
          110 
          111         msg = p = malloc(ln);
          112         snprintf(msg, ln--, fmt, selector, search);
          113 
          114         while ((n = write(sock, p, ln)) > 0) {
          115                 ln -= n;
          116                 p += n;
          117         }
          118 
          119         free(msg);
          120         return n;
          121 }
          122 
          123 static char
          124 hex2bin(const unsigned char *in)
          125 {
          126   int out;
          127 
          128   if (*in == '%')
          129     in++;
          130 
          131   if ('A' <= in[0] && in[0] <= 'F') out  = 16 * (in[0] - 'A' + 10);
          132   if ('0' <= in[0] && in[0] <= '9') out  = 16 * (in[0] - '0');
          133 
          134   if ('A' <= in[1] && in[1] <= 'F') out += (in[1] - 'A' + 10);
          135   if ('0' <= in[1] && in[1] <= '9') out += (in[1] - '0');
          136 
          137   return out;
          138 }
          139 
          140 char *
          141 urldec(char *search)
          142 {
          143         char *msg, *p;
          144 
          145         if (!search)
          146                 return NULL;
          147 
          148         msg = p = search;
          149         for (p = msg; *p != '\0'; msg++, p++) {
          150                 switch(*p) {
          151                 case '+':
          152                         *msg = ' ';
          153                         break;
          154                 case '%':
          155                         *msg = hex2bin((unsigned char *)p);
          156                         p += 2;
          157                         break;
          158                 default:
          159                         *msg = *p;
          160                 }
          161         }
          162         *msg = '\0';
          163 
          164         return search;
          165 }
          166 
          167 
          168 char *
          169 getrawitem(int sock, size_t *sz)
          170 {
          171         char *raw, *buf;
          172         size_t bn, bs;
          173         ssize_t n;
          174 
          175         raw = buf = NULL;
          176         bn = bs = n = 0;
          177 
          178         do {
          179                 bs -= n;
          180                 buf += n;
          181                 if (bs < 1) {
          182                         raw = xreallocarray(raw, ++bn, BUFSIZ);
          183                         buf = raw + (bn-1) * BUFSIZ;
          184                         bs = BUFSIZ;
          185                 }
          186         } while ((n = read(sock, buf, bs)) > 0);
          187 
          188         *buf = '\0';
          189 
          190         if (sz)
          191                 *sz = buf - raw;
          192 
          193         if (n < 0) {
          194                 free(raw);
          195                 return NULL;
          196         }
          197 
          198         return raw;
          199 }
          200 
          201 void
          202 printhttp(int code)
          203 {
          204         switch (code) {
          205         case 400: printf("HTTP/1.1 400 That's Illegal\r\n"); break;
          206         case 404: printf("HTTP/1.1 404 Google Broke The Web\r\n"); break;
          207         case 405: printf("HTTP/1.1 405 Don't Do That\r\n"); break;
          208         case 415: printf("HTTP/1.1 415 Gopher Type Not Handled\r\n"); break;
          209         case 444: printf("HTTP/1.1 444 You're Alone\r\n"); break;
          210         case 500: printf("HTTP/1.1 500 You Broke The Web\r\n"); break;
          211         }
          212         printf("\r\n");
          213 }
          214 
          215 char *
          216 contenttype(char i, char *path)
          217 {
          218         static char *ext, type[32];
          219 
          220         /* isolate file extention, if any */
          221         ext = strrchr(path, '.');
          222         ext = ext ? ext + 1 : "*";
          223 
          224         switch(i) {
          225         case '0':
          226         case '1':
          227         case '7':
          228         case 'h':
          229                 snprintf(type, sizeof(type)-1, "text/html; charset=utf-8");
          230                 break;
          231         case '6':
          232                 snprintf(type, sizeof(type)-1, "text/x-uuencode");
          233                 break;
          234         case '4':
          235         case '5':
          236         case '9':
          237                 snprintf(type, sizeof(type)-1, "application/octet-stream");
          238                 break;
          239         case 'I':
          240                 /* assume 4 chars max for extension */
          241                 snprintf(type, sizeof(type)-1, "image/%s", ext);
          242                 break;
          243         case 's':
          244                 snprintf(type, sizeof(type)-1, "audio/%s", ext);
          245                 break;
          246         case 'g':
          247                 snprintf(type, sizeof(type)-1, "image/gif");
          248                 break;
          249         case 'P':
          250         case 'd':
          251                 snprintf(type, sizeof(type)-1, "application/%s", ext);
          252                 break;
          253         default:
          254                 return NULL;
          255                 break; /* NOTREACHED */
          256         }
          257 
          258         return type;
          259 }
          260 
          261 const char *
          262 itemname(char i)
          263 {
          264         switch(i) {
          265         case '0': return items[ITEM_0];
          266         case '1': return items[ITEM_1];
          267         case '2': return items[ITEM_2];
          268         case '3': return items[ITEM_3];
          269         case '4': return items[ITEM_4];
          270         case '5': return items[ITEM_5];
          271         case '6': return items[ITEM_6];
          272         case '7': return items[ITEM_7];
          273         case '8': return items[ITEM_8];
          274         case '9': return items[ITEM_9];
          275         case 'I': return items[ITEM_I];
          276         case 'g': return items[ITEM_g];
          277         case 'h': return items[ITEM_h];
          278         case 's': return items[ITEM_s];
          279         case 'd': return items[ITEM_d];
          280         case 'P': return items[ITEM_P];
          281         }
          282 
          283         return NULL;
          284 }
          285 
          286 
          287 void
          288 printheaders(char *ctype)
          289 {
          290         time_t t;
          291 
          292         t = time(NULL);
          293         if (t > 0)
          294                 printf("Date: %s", asctime(gmtime(&t)));
          295         if (ctype)
          296                 printf("Content-Type: %s\r\n", ctype);
          297         printf("Server: phroxy\r\n");
          298         printf("Host: %s\r\n", http_host);
          299         printf("Connection: close\r\n");
          300 }
          301 
          302 int
          303 printmenu(int fd, char *data)
          304 {
          305         char i, *p, a[LINE_MAX], *f[4];
          306         char *ifmt = "<div class='item'><span> </span><code>%s</code></div>\n";
          307         char *afmt = "<div class='item'><span>%s</span><a href='/%s:%s/%c%s'>%s</a></div>\n";
          308         char *sfmt = "<div class='item'><span>%s</span><details><summary>%s</summary><form method='get' action='/%s:%s/%c%s'><input type='text' name='q'></form></details></div>\n";
          309 
          310         p = data;
          311 
          312         while((p = strsep(&data, "\n"))) {
          313                 i = *p++;
          314                 if (i == '.')
          315                         break;
          316 
          317                 f[0] = strsep(&p, "\t");
          318                 f[1] = strsep(&p, "\t");
          319                 f[2] = strsep(&p, "\t");
          320                 f[3] = strsep(&p, "\r");
          321                 if (!f[1])
          322                         continue;
          323 
          324                 switch(i) {
          325                 case 'i':
          326                         snprintf(a, sizeof(a), ifmt, f[0]);
          327                         break;
          328                 case '7':
          329                         snprintf(a, sizeof(a), sfmt, itemname(i), f[0], f[2], f[3], i, f[1]);
          330                         break;
          331                 default:
          332                         snprintf(a, sizeof(a), afmt, itemname(i), f[2], f[3], i, f[1], f[0]);
          333                 }
          334 
          335                 write(fd, a, strlen(a));
          336         }
          337 
          338         return 0;
          339 }
          340 
          341 int
          342 printhtml(int fd, const char *data, size_t len)
          343 {
          344         size_t r, n;
          345         const char *s, *e, *x;
          346 
          347         write(fd, "<pre>", 5);
          348 
          349         for (n = 0; n < len; n++) {
          350 
          351                 s = data + n;
          352 
          353                 /* escape XML characters */
          354                 x = NULL;
          355                 switch (*s) {
          356                 case '&': x = x ? x : "&amp;"; /* FALLTHROUGH */
          357                 case '<': x = x ? x : "&lt;";  /* FALLTHROUGH */
          358                 case '>': x = x ? x : "&gt;";  /* FALLTHROUGH */
          359                         write(fd, x, strlen(x));
          360                         break;
          361                 default:
          362                         e = strpbrk(s, "&<>");
          363                         r = e ? (size_t)(e - s) : len - n;
          364                         if (r) {
          365                                 write(fd, s, r);
          366                                 n += r - 1;
          367                         }
          368                 }
          369         }
          370         write(fd, "</pre>\n", 7);
          371         return 0;
          372 }
          373 
          374 int
          375 servebots()
          376 {
          377         printf("HTTP/1.1 200 OK\r\n");
          378         printheaders("text/plain");
          379         printf("Content-Length: %ld\r\n", strlen(robotstxt));
          380         printf("\r\n");
          381         fflush(stdout);
          382         write(1, robotstxt, strlen(robotstxt));
          383         fflush(stdout);
          384 
          385         return 0;
          386 }
          387 
          388 int
          389 serveitem(char item, char *path, char *data, size_t len)
          390 {
          391         char *send;
          392         int sent;
          393 
          394 
          395         if (!contenttype(item, path)) {
          396                 printhttp(415);
          397                 return 1;
          398         }
          399 
          400         printf("HTTP/1.1 200 OK\r\n");
          401         printheaders(contenttype(item, path));
          402 
          403         switch(item) {
          404         case '7': // search
          405         case '1': // menu
          406         case '0': // text
          407                 printf("\r\n");
          408                 fflush(stdout);
          409                 write(1, head, strlen(head));
          410                 if (item == '1' || item == '7') printmenu(1, data);
          411                 if (item == '0') printhtml(1, data, len);
          412                 write(1, foot, strlen(foot));
          413                 break;
          414 
          415         case '4': // BinHexed Macintosh file
          416         case '5': // DOS binary archive of some sort
          417         case '6': // uuencoded
          418         case '9': // binary
          419         case 'g': // gif
          420         case 'I': // image
          421         case 's': // sound
          422         case 'd': // document
          423         case 'P': // pdf (~document)
          424         case 'h': // http redirect
          425                 printf("Content-Length: %ld\r\n", len);
          426                 printf("\r\n");
          427                 fflush(stdout);
          428                 send = data;
          429                 while (len > 0) {
          430                         if ((sent = write(1, send, len)) < 0)
          431                                 return 1;
          432                         len -= sent;
          433                         send += sent;
          434                 }
          435                 break;
          436 
          437         case '2': // CSO phone-book server
          438         case '3': // Error
          439         case '8': // telnet session.
          440         case 'T': // tn3270 session.
          441         case '+': // mirror link
          442         default:
          443                 /* IGNORE */
          444                 break;
          445         }
          446 
          447         free(data);
          448         fflush(stdout);
          449 
          450         return 0;
          451 }
          452 
          453 int
          454 phroxy(char *url)
          455 {
          456         int sock;
          457         size_t len;
          458         char item = 0;
          459         char *hole, *path, *host, *port;
          460         char *data = NULL, *srch = NULL;
          461 
          462         if (!strncmp(url, "/robots.txt", 11))
          463                 return servebots();
          464 
          465         url++;
          466         hole = strsep(&url, "/");
          467         if (!hole || !strnlen(hole, 1))
          468                 hole = default_hole;
          469 
          470         host = strsep(&hole, ":");
          471         port = strsep(&hole, "\0");
          472         if (!port)
          473                 port = "70";
          474 
          475         if (url)
          476                 item = *url++;
          477 
          478         if (!item)
          479                 item = '1';
          480 
          481         path = strsep(&url, "\0");
          482         if (!path || *path == '\0')
          483                 path = "/";
          484 
          485         if((srch = strchr(path, '?'))) {
          486                 *srch = '\0';
          487                 srch += 3; /* go past "?q=" in URL, to fetch actual query */
          488         }
          489 
          490         if ((sock = connectto(host, port)) < 0) {
          491                 printhttp(500);
          492                 return 1;
          493         }
          494 
          495         if (!sendselector(sock, path, urldec(srch)))
          496                 data = getrawitem(sock, &len);
          497 
          498         close(sock);
          499 
          500         if (!data) {
          501                 printhttp(444);
          502                 return 1;
          503         }
          504 
          505         serveitem(item, path, data, len);
          506 
          507         return 0;
          508 }
          509 
          510 int
          511 main(void)
          512 {
          513         ssize_t rlen;
          514         char request[512], *url;
          515 
          516         rlen = read(0, request, sizeof(request) - 1);
          517         if (rlen < 0)
          518                 return 1;
          519 
          520         request[rlen] = '\0';
          521 
          522         if (strncmp(request, "GET ", 4)) {
          523                 printhttp(405);
          524                 return 1;
          525         }
          526 
          527         url = strtok(request + 4, " ");
          528 
          529         return phroxy(url);
          530 }