Skip to content

Instantly share code, notes, and snippets.

@zeph1e
Created August 19, 2016 01:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeph1e/41c1a3f54a69e0e31434ded8f3d15118 to your computer and use it in GitHub Desktop.
Save zeph1e/41c1a3f54a69e0e31434ded8f3d15118 to your computer and use it in GitHub Desktop.
#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