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: