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

commit 2101da6eb3470feb0187e3988a16513fb610edc1
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun, 12 May 2019 17:23:45 +0200

redirect http port 80 to https port 443

README.md      |  13 ++++
httpRedirect.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.yml    |  32 ++++++++
3 files changed, 277 insertions(+)

Diffstat:
AREADME.md | 13+++++++++++++
AhttpRedirect.c | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.yml | 32++++++++++++++++++++++++++++++++
3 files changed, 277 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -0,0 +1,13 @@ +# httpRedirect + +Server for redirecting HTTP GET requests on port 80 to https port 443 + +- epoll +- daemon + +Usage: +``` +httpRedirect interfaceIp portNumber redirectToUrl + +./httpRedirect.c 127.0.0.1 8080 https://google.com/ +``` diff --git a/httpRedirect.c b/httpRedirect.c @@ -0,0 +1,232 @@ +#! /usr/bin/env sheepy +#include "libsheepyObject.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/epoll.h> + +#include <string.h> +#include <unistd.h> // for close +#include <fcntl.h> // non blocking file descriptor + +#include "unistd.h" +#define KEEP_CWD 1 +#define STDIO_DEV_NULL 0 + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +const char *redir = "HTTP/1.1 301 Moved Permanently\r\n" + "Location: %s\r\n" + "\r\n"; +//"Server: http-redirect\r\n" + +char response[1024] = init0Var; +size_t responseSize = 0; + +#define MAXEVENTS 64 + +int sock; +struct addrinfo server; + +int epfd; +struct epoll_event events[MAXEVENTS] = init0Var; + +// bind and open socket +local void bindSocket(int *sock, struct addrinfo *server, const char *hostnode, const char *port) { + + logI("Host:%s:%s", hostnode, port); + zeroBuf(server, sizeof *server); /* make sure the struct is empty */ + server->ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ + server->ai_socktype = SOCK_STREAM; /* TCP */ + + if (!hostnode) { + server->ai_flags = AI_PASSIVE; /* for wildcard IP address */ + } + + struct addrinfo *result; + int s = getaddrinfo(hostnode, port, server, &result); + + if (s) { + logE("getaddrinfo: Host:%s:%s %s", hostnode, port, gai_strerror(s)); + XFAILURE; + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + struct addrinfo *rp; + for(rp = result; rp != NULL; rp = rp->ai_next) { + *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (*sock == -1) + continue; + + if (bind(*sock, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(*sock); + } + + if (!rp) { /* No address succeeded */ + logE("bind failed: Host:%s:%s", hostnode, port); + freeaddrinfo(result); + XFAILURE; + } + + freeaddrinfo(result); /* No longer needed */ + + /* setsockopt: Handy debugging trick that lets + * us rerun the server immediately after we kill it; + * otherwise we have to wait about 20 secs. + * Eliminates "ERROR on binding: Address already in use" error. + */ + int optval = 1; + setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)); + + listen(*sock, SOMAXCONN); +} + +// open socket and setup epoll +local void init(const char *hostnode, const char *port) { + + bindSocket(&sock, &server, hostnode, port); + + // epoll + epfd = epoll_create(1); + struct epoll_event event = init0Var; + zeroBuf(&event, sizeof event); + event.events = EPOLLIN; + event.data.fd = sock; + epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &event); + // end epoll +} + +// serve redirects +local void serve(void) { + int mysock; + char buf[1024]; + int rval; + + //struct sockaddr_in remote_sin; + //socklen_t remote_sinlen = sizeof(remote_sin); + + // wait event on server and control ports using epoll + // process events + + forever { + int nevents = epoll_wait(epfd, events, MAXEVENTS, -1 /* no timeout*/); + if (nevents == -1) continue; + range(i, nevents) { + if ((events[i].events & EPOLLERR) || + (events[i].events & EPOLLHUP) || + (!(events[i].events & EPOLLIN)) + ) { + /* An error has occured on this fd, or the socket is not + ready for reading */ + logE("epoll error"); + close (events[i].data.fd); + continue; + } + else { + #define evsoc events[i].data.fd + if (evsoc == sock) { + // add client socket to event loop + mysock = accept(evsoc, (struct sockaddr *)0, 0); + if (mysock == -1) { + logE("accept failed"); + continue; + } + // setnonblocking(mysock); + int flags = fcntl(mysock, F_GETFL, 0); + fcntl(mysock, F_SETFL, flags | O_NONBLOCK); + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLET; + ev.data.fd = mysock; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, mysock, &ev) == -1) { + logE("epoll_ctl: adding mysock"); + } + continue; + } + // get data from event + else { + zeroBuf(buf, sizeof(buf)); + if ((rval = recv(evsoc, buf, sizeof(buf), 0)) < 0) { + logE("reading message"); + close(evsoc); + continue; + } + else if (rval == 0) { + /* no packet received, process next event */ + logE("Ending connection\n"); + close(evsoc); + continue; + } + + logVarG(rval); + logI(buf); + + if (!icStartsWithG(buf, "get ")) goto closeClientSocket; + + //logI("get"); + + buf[sizeof(buf)-1] = 0; + + var h = findG(&buf[4], " "); + + if (!h) goto closeClientSocket; + + //logI("path"); + + if (!icStartsWithG(h+1, "http/1.1") and !icStartsWithG(h+1, "http/1.0")) goto closeClientSocket; + + //logI("http version"); + + write(evsoc, response, responseSize); + + closeClientSocket: + close(evsoc); + } + } // if event not error + } // range nevents + } + + killServer: + close(epfd); + close(sock); +} + +int main(int ARGC, char** ARGV) { + + initLibsheepy(ARGV[0]); + setLogMode(LOG_PROGNDATE); + //setLogSymbols(LOG_UTF8); + //disableLibsheepyErrorLogs; + + if (ARGC < 4) { + logC(BLD MGT"Usage"RST": httpRedirect "BLD"interfaceIp portNumber redirectToUrl"RST); + XFAILURE; + } + + if (lenG(ARGV[3])>512) { + logC("Redirect url is too long (>512). Stop."); + XFAILURE; + } + + init(ARGV[1], ARGV[2]); + + bLFormatS(response,sizeof(response), redir, ARGV[3]); + responseSize = lenG(response)+1; + //logI(response); + + logI("Server pid: "BLD BLU"%"PRIu64 RST,getpid()); + + daemon(KEEP_CWD, STDIO_DEV_NULL); + + serve(); +} +// vim: set expandtab ts=2 sw=2: diff --git a/package.yml b/package.yml @@ -0,0 +1,32 @@ +--- + name: httpRedirect + version: 0.0.1 + description: "explanation" + bin: ./httpRedirect.c + cflags: -O3 -std=gnu11 -fPIC -pipe + #lflags: -lpcre + repository: + type: git + url: git+https://github.com/USER/httpRedirect.git + keywords: + #- utility + - command + author: Anonymous + license: MIT + bugs: + url: https://github.com/USER/httpRedirect/issues + homepage: https://github.com/USER/httpRedirect#readme + #compileHelp: # text displayed when there is a compilation error + #dependencies: + # md4c: + # Test configuration: + #testBin: ./testHttpRedirect.c + #testCflags: -ggdb -std=gnu11 -fPIC -pipe -fprofile-arcs -ftest-coverage -Wall -Wextra + #testLflags: -lcheck_pic -lrt -lm -lsubunit -fprofile-arcs -ftest-coverage -rdynamic + # Memcheck configuration: + #memcheckBin: ./memcheckHttpRedirect.c + #memcheckCmd: valgrind --leak-check=full --show-leak-kinds=all + #memcheckCflags: -ggdb -std=gnu11 -fPIC -pipe + #memcheckLflags: -rdynamic + #documentationCmd: # command for generating the documentation with spm doc + private: false # true for private package