httpRedirect

Daemon that redirects http GET requests on port 80 to a specified url (https)
git clone https://noulin.net/git/httpRedirect.git
Log | Files | Refs | README

httpRedirect.c (5929B)


      1 #! /usr/bin/env sheepy
      2 #include "libsheepyObject.h"
      3 
      4 #include <sys/types.h>
      5 #include <sys/socket.h>
      6 #include <netdb.h>
      7 #include <netinet/in.h>
      8 #include <sys/epoll.h>
      9 
     10 #include <string.h>
     11 #include <unistd.h> // for close
     12 #include <fcntl.h>  // non blocking file descriptor
     13 
     14 #include "unistd.h"
     15 #define KEEP_CWD 1
     16 #define STDIO_DEV_NULL 0
     17 
     18 /* enable/disable logging */
     19 /* #undef pLog */
     20 /* #define pLog(...) */
     21 
     22 const char *redir = "HTTP/1.1 301 Moved Permanently\r\n"
     23                     "Location: %s\r\n"
     24                     "\r\n";
     25 //"Server: http-redirect\r\n"
     26 
     27 char response[1024] = init0Var;
     28 size_t responseSize = 0;
     29 
     30 #define MAXEVENTS 64
     31 
     32 int sock;
     33 struct addrinfo server;
     34 
     35 int                epfd;
     36 struct epoll_event events[MAXEVENTS] = init0Var;
     37 
     38 // bind and open socket
     39 local void bindSocket(int *sock, struct addrinfo *server, const char *hostnode, const char *port) {
     40 
     41   logI("Host:%s:%s", hostnode, port);
     42   zeroBuf(server, sizeof *server); /* make sure the struct is empty */
     43   server->ai_family   = AF_UNSPEC;   /* allow IPv4 or IPv6 */
     44   server->ai_socktype = SOCK_STREAM; /* TCP */
     45 
     46   if (!hostnode) {
     47     server->ai_flags = AI_PASSIVE;   /* for wildcard IP address */
     48   }
     49 
     50   struct addrinfo *result;
     51   int s = getaddrinfo(hostnode, port, server, &result);
     52 
     53   if (s) {
     54     logE("getaddrinfo: Host:%s:%s %s", hostnode, port, gai_strerror(s));
     55     XFAILURE;
     56   }
     57 
     58   /* getaddrinfo() returns a list of address structures.
     59      Try each address until we successfully bind(2).
     60      If socket(2) (or bind(2)) fails, we (close the socket
     61      and) try the next address. */
     62 
     63   struct addrinfo *rp;
     64   for(rp = result; rp != NULL; rp = rp->ai_next) {
     65     *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
     66     if (*sock == -1)
     67       continue;
     68 
     69     if (bind(*sock, rp->ai_addr, rp->ai_addrlen) == 0)
     70       break;                  /* Success */
     71 
     72     close(*sock);
     73   }
     74 
     75   if (!rp) {                  /* No address succeeded */
     76     logE("bind failed: Host:%s:%s", hostnode, port);
     77     freeaddrinfo(result);
     78     XFAILURE;
     79   }
     80 
     81   freeaddrinfo(result);           /* No longer needed */
     82 
     83   /* setsockopt: Handy debugging trick that lets
     84    * us rerun the server immediately after we kill it;
     85    * otherwise we have to wait about 20 secs.
     86    * Eliminates "ERROR on binding: Address already in use" error.
     87    */
     88   int optval = 1;
     89   setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
     90 
     91   listen(*sock, SOMAXCONN);
     92 }
     93 
     94 // open socket and setup epoll
     95 local void init(const char *hostnode, const char *port) {
     96 
     97   bindSocket(&sock, &server, hostnode, port);
     98 
     99   // epoll
    100   epfd = epoll_create(1);
    101   struct epoll_event event = init0Var;
    102   zeroBuf(&event, sizeof event);
    103   event.events  = EPOLLIN;
    104   event.data.fd = sock;
    105   epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &event);
    106   // end epoll
    107 }
    108 
    109 // serve redirects
    110 local void serve(void) {
    111   int mysock;
    112   char buf[1024];
    113   int rval;
    114 
    115   //struct sockaddr_in remote_sin;
    116   //socklen_t remote_sinlen = sizeof(remote_sin);
    117 
    118   // wait event on server and control ports using epoll
    119   // process events
    120 
    121   forever {
    122     int nevents = epoll_wait(epfd, events, MAXEVENTS, -1 /* no timeout*/);
    123     if (nevents == -1) continue;
    124     range(i, nevents) {
    125       if ((events[i].events & EPOLLERR) ||
    126           (events[i].events & EPOLLHUP) ||
    127           (!(events[i].events & EPOLLIN))
    128          ) {
    129         /* An error has occured on this fd, or the socket is not
    130            ready for reading */
    131         logE("epoll error");
    132         close (events[i].data.fd);
    133         continue;
    134       }
    135       else {
    136         #define evsoc events[i].data.fd
    137         if (evsoc == sock) {
    138 	  // add client socket to event loop
    139           mysock = accept(evsoc, (struct sockaddr *)0, 0);
    140           if (mysock == -1) {
    141             logE("accept failed");
    142               continue;
    143           }
    144           // setnonblocking(mysock);
    145           int flags = fcntl(mysock, F_GETFL, 0);
    146           fcntl(mysock, F_SETFL, flags | O_NONBLOCK);
    147           struct epoll_event ev;
    148           ev.events  = EPOLLIN | EPOLLET;
    149           ev.data.fd = mysock;
    150           if (epoll_ctl(epfd, EPOLL_CTL_ADD, mysock, &ev) == -1) {
    151             logE("epoll_ctl: adding mysock");
    152           }
    153           continue;
    154         }
    155         // get data from event
    156         else {
    157           zeroBuf(buf, sizeof(buf));
    158           if ((rval = recv(evsoc, buf, sizeof(buf), 0)) < 0) {
    159             logE("reading message");
    160             close(evsoc);
    161             continue;
    162           }
    163           else if (rval == 0) {
    164             /* no packet received, process next event */
    165             logE("Ending connection\n");
    166             close(evsoc);
    167             continue;
    168           }
    169 
    170           if (!icStartsWithG(buf, "get ")) goto closeClientSocket;
    171 
    172           logVarG(rval);
    173           //logI(buf);
    174 
    175           //logI("get");
    176 
    177           buf[sizeof(buf)-1] = 0;
    178 
    179 	  var h = findG(&buf[4], " ");
    180 
    181 	  if (!h) goto closeClientSocket;
    182 
    183           //logI("path");
    184 
    185 	  if (!icStartsWithG(h+1, "http/1.1") and !icStartsWithG(h+1, "http/1.0")) goto closeClientSocket;
    186 
    187           //logI("http version");
    188 
    189           write(evsoc, response, responseSize);
    190 
    191 	  closeClientSocket:
    192           close(evsoc);
    193         }
    194       } // if event not error
    195     } // range nevents
    196   }
    197 
    198   killServer:
    199   close(epfd);
    200   close(sock);
    201 }
    202 
    203 int main(int ARGC, char** ARGV) {
    204 
    205   initLibsheepy(ARGV[0]);
    206   setLogMode(LOG_PROGNDATE);
    207   //setLogSymbols(LOG_UTF8);
    208   //disableLibsheepyErrorLogs;
    209 
    210   if (ARGC < 4) {
    211     logC(BLD MGT"Usage"RST": httpRedirect "BLD"interfaceIp portNumber redirectToUrl"RST);
    212     XFAILURE;
    213   }
    214 
    215   if (lenG(ARGV[3])>512) {
    216     logC("Redirect url is too long (>512). Stop.");
    217     XFAILURE;
    218   }
    219 
    220   init(ARGV[1], ARGV[2]);
    221 
    222   bLFormatS(response,sizeof(response), redir, ARGV[3]);
    223   responseSize = lenG(response)+1;
    224   //logI(response);
    225 
    226   logI("Server pid: "BLD BLU"%"PRIu64 RST,getpid());
    227 
    228   daemon(KEEP_CWD, STDIO_DEV_NULL);
    229 
    230   serve();
    231 }
    232 // vim: set expandtab ts=2 sw=2: