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:
| A | README.md | | | 13 | +++++++++++++ |
| A | httpRedirect.c | | | 232 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | package.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