Created
August 19, 2016 01:23
-
-
Save zeph1e/41c1a3f54a69e0e31434ded8f3d15118 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/time.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <syslog.h> | |
#include <string.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
#include <signal.h> | |
#include <sys/wait.h> | |
#include <semaphore.h> | |
#include <time.h> | |
#include <vector> | |
#include <string> | |
#include <iostream> | |
#define PORT 80 | |
#define HEADER_GET "GET " | |
#define HEADER_HOST "Host: " | |
#define HEADER_CONTENT_TYPE "Content-Type: " | |
#define HEADER_CONTENT_LENGTH "Content-Length: " | |
#define HEADER_CACHE_CONTROL "Cache-Control: " | |
#define HEADER_EXPIRES "Expires: " | |
#define HEADER_DATE "Date: " | |
#define NOTFOUND "HTTP/1.1 404 Not Found\r\n\r\n404 Not Found" | |
#define TIMEOUT "HTTP/1.1 408 Request Timeout\r\n\r\n408 Request Timeout" | |
#define BOUNDARYLEN 100 | |
static sem_t sem; | |
void handle_sigchld(int sig) { | |
int saved_errno = errno; | |
while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {} | |
errno = saved_errno; | |
} | |
void init_sockaddr(struct sockaddr_in *name, const char *hostname, uint16_t port) | |
{ | |
struct hostent *hostinfo; | |
name->sin_family = AF_INET; | |
name->sin_port = htons(port); | |
hostinfo = gethostbyname(hostname); | |
if (!hostinfo) { | |
fprintf(stderr, "Unknown host %s\n", hostname); | |
exit(EXIT_FAILURE); | |
} | |
name->sin_addr = *(struct in_addr *)hostinfo->h_addr; | |
} | |
void set_boundary(char* boundary, size_t size, const char c, | |
const char* str, size_t len, size_t max) | |
{ | |
int maxlen = std::min(size - 1, max); | |
memset(boundary, c, maxlen); | |
boundary[maxlen] = 0; | |
if (str && maxlen > 10) { | |
size_t remained = maxlen - 2; | |
int i = 2; | |
boundary[i++] = '['; | |
boundary[i++] = ' '; | |
int strmax = std::min(remained - 4, len); | |
memcpy(&boundary[i], str, strmax); | |
i += strmax; | |
boundary[i++] = ' '; | |
boundary[i++] = ']'; | |
} | |
} | |
void print_transction(const std::string host, | |
const std::string path, | |
const std::vector<std::string>& request, | |
const std::vector<std::string>& response, | |
const unsigned char* data) | |
{ | |
static char boundary[BUFSIZ]; | |
std::string url = std::string("http://") + host + path; | |
time_t now = time(NULL); | |
sem_wait(&sem); | |
set_boundary(boundary, sizeof(boundary), '=', url.c_str(), url.size(), BOUNDARYLEN); | |
std::cout << boundary << std::endl; | |
std::cout << ctime(&now); | |
set_boundary(boundary, sizeof(boundary), '-', NULL, 0, BOUNDARYLEN); | |
std::cout << boundary << std::endl; | |
for (int i = 0; i < request.size(); i++) | |
std::cout << request[i]; | |
std::cout << boundary << std::endl; | |
for (int i = 0; i < response.size(); i++) | |
std::cout << response[i]; | |
std::cout << (const char*)data << std::endl; | |
set_boundary(boundary, sizeof(boundary), '=', NULL, 0, BOUNDARYLEN); | |
std::cout << boundary << std::endl << std::endl << std::endl; | |
sem_post(&sem); | |
} | |
void forward(int clsock) | |
{ | |
struct sockaddr_in svname; | |
socklen_t size = sizeof(svname); | |
int svsock = socket(PF_INET, SOCK_STREAM, 0); | |
FILE *fp_read, *fp_write; | |
std::vector<std::string> requests; | |
std::vector<std::string> responses; | |
std::string path; | |
std::string host; | |
std::string type; | |
std::string length; | |
char buf[BUFSIZ]; | |
fp_read = fdopen(dup(clsock), "r"); | |
fp_write = fdopen(dup(clsock), "w"); | |
while (fgets(buf, sizeof(buf), fp_read)) { | |
std::string header(buf); | |
std::size_t pos; | |
requests.push_back(header); | |
if (!header.compare(0, 4, HEADER_GET)) { | |
path = header.substr(4, header.find(' ', 4) - 4); | |
} | |
if (!header.compare(0, 6, HEADER_HOST)) { | |
host = header.substr(6, std::min(header.find('\r'), header.find('\n')) - 6); | |
if (!host.compare("localhost")) { | |
fputs(NOTFOUND, fp_write); | |
fclose(fp_read); | |
fclose(fp_write); | |
return; | |
} | |
} | |
if (std::min(header.find('\r'), header.find('\n')) == 0) | |
break; | |
} | |
fclose(fp_read); | |
if (requests.empty() || requests.front().compare(0, 4, HEADER_GET)) | |
return; | |
init_sockaddr(&svname, host.c_str(), PORT); | |
if (connect(svsock, (const struct sockaddr*)&svname, sizeof(svname)) < 0) { | |
int e = errno; | |
fprintf(stderr, "connect : %s : %s\n", strerror(e), host.c_str()); | |
fputs(NOTFOUND, fp_write); | |
fclose(fp_write); | |
return; | |
} | |
fp_read = fdopen(dup(svsock), "r"); | |
fp_write = fdopen(dup(svsock), "w"); | |
// forwarding requests | |
for (int i = 0; i < requests.size(); i++) { | |
fputs(requests[i].c_str(), fp_write); | |
} | |
fflush(fp_write); | |
// receiving response headers | |
while (fgets(buf, sizeof(buf), fp_read)) { | |
std::string header(buf); | |
responses.push_back(header); | |
size_t headerlen; | |
headerlen = sizeof(HEADER_CONTENT_LENGTH) - 1; | |
if (!header.compare(0, headerlen, HEADER_CONTENT_LENGTH)) | |
length = header.substr(headerlen, std::min(header.find('\r'), header.find('\n')) - headerlen); | |
headerlen = sizeof(HEADER_CONTENT_TYPE) - 1; | |
if (!header.compare(0, headerlen, HEADER_CONTENT_TYPE)) | |
type = header.substr(headerlen, std::min(header.find('\r'), header.find('\n')) - headerlen); | |
// debug | |
// manipulate Cache-Control | |
// headerlen = sizeof(HEADER_CACHE_CONTROL) - 1; | |
// if (!header.compare(0, headerlen, HEADER_CACHE_CONTROL)) { | |
// responses.pop_back(); | |
// header = std::string(HEADER_CACHE_CONTROL) + "no-store\r\n"; | |
// responses.push_back(header); | |
// } | |
// manipulate Expires header | |
// headerlen = sizeof(HEADER_EXPIRES) - 1; | |
// if (!header.compare(0, headerlen, HEADER_EXPIRES)) { | |
// responses.pop_back(); // remove expires | |
// // responses.push_back(std::string("Expires: 0\r\n")); | |
// } | |
// headerlen = sizeof(HEADER_DATE) - 1; | |
// if (!header.compare(0, headerlen, HEADER_DATE)) { | |
// std::string date = header.substr(headerlen, | |
// std::min(header.find('\r'), header.find('\n')) - headerlen); | |
// header = std::string(HEADER_EXPIRES) + date + "\r\n"; // make expires be same date in Date: | |
// responses.push_back(header); | |
// } | |
if (std::min(header.find('\r'), header.find('\n')) == 0) | |
break; | |
} | |
int len = atoi(length.c_str()); | |
unsigned char* data = new unsigned char[len + 1]; | |
fread(data, len, 1, fp_read); | |
data[len] = 0; | |
fclose(fp_read); | |
fclose(fp_write); | |
fp_write = fdopen(dup(clsock), "w"); | |
for (int i = 0; i < responses.size(); i++) { | |
fputs(responses[i].c_str(), fp_write); | |
} | |
fwrite(data, len, 1, fp_write); | |
print_transction(host, path, requests, responses, data); | |
delete[] data; | |
fflush(fp_write); | |
fclose(fp_write); | |
close(svsock); | |
} | |
int main(void) | |
{ | |
pid_t pid; | |
int sock; | |
struct sockaddr_in name; | |
struct sigaction sa; | |
sa.sa_handler = &handle_sigchld; | |
sigemptyset(&sa.sa_mask); | |
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; | |
if (sigaction(SIGCHLD, &sa, 0) == -1) { | |
perror(0); | |
exit(EXIT_FAILURE); | |
} | |
if (sem_init(&sem, 1, 1) < 0) { | |
perror("sem_init"); | |
exit(EXIT_FAILURE); | |
} | |
umask(0); | |
if ((chdir(getenv("HOME"))) < 0) { | |
perror("chdir"); | |
exit(EXIT_FAILURE); | |
} | |
// create socket | |
sock = socket(PF_INET, SOCK_STREAM, 0); | |
if (sock < 0) { | |
perror("socket"); | |
exit(EXIT_FAILURE); | |
} | |
int val = 1; | |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { | |
perror("setsockopt"); | |
exit(EXIT_FAILURE); | |
} | |
// bind & listen | |
bzero((char*)&name, sizeof(name)); | |
name.sin_family = AF_INET; | |
name.sin_port = htons(PORT); | |
name.sin_addr.s_addr = htonl(INADDR_ANY); | |
if (bind(sock, (struct sockaddr*)&name, sizeof(name)) < 0) { | |
perror("bind"); | |
exit(EXIT_FAILURE); | |
} | |
// fprintf(stderr, "bind: pid=%d ppid=%d\n", getpid(), getppid()); | |
if (listen(sock, 5) < 0) { | |
perror("listen"); | |
exit(EXIT_FAILURE); | |
} | |
// fprintf(stderr, "listen: pid=%d ppid=%d\n", getpid(), getppid()); | |
/* The Big Loop */ | |
while (1) { | |
struct sockaddr_in clname; | |
socklen_t size = sizeof(clname); | |
int clsock = accept(sock, (struct sockaddr*)&clname, &size); | |
// fprintf(stderr, "accept: pid=%d ppid=%d\n", getpid(), getppid()); | |
if (clsock < 0) { | |
perror("accept"); | |
exit(EXIT_FAILURE); | |
} | |
pid = fork(); | |
// fprintf(stderr, "fork: pid=%d ppid=%d\n", getpid(), getppid()); | |
if (pid < 0) { | |
perror("fork"); | |
close(clsock); | |
continue; | |
} | |
if (pid == 0) { | |
fd_set fdset; | |
struct timeval tmval; | |
FD_ZERO(&fdset); | |
FD_SET(clsock, &fdset); | |
tmval.tv_sec = 3L; | |
tmval.tv_usec = 0L; | |
if (select(FD_SETSIZE, &fdset, NULL, NULL, &tmval) < 0) { | |
perror("select"); | |
write(clsock, TIMEOUT, strlen(TIMEOUT)); | |
close(clsock); | |
break; | |
} | |
forward(clsock); | |
close(clsock); | |
break; | |
} | |
close(clsock); | |
} | |
exit(EXIT_SUCCESS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment