init: create repository with bootstrap code

This commit is contained in:
Kevin J. 2024-07-21 19:43:17 +02:00
commit 87d75b7f48
6 changed files with 828 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
proxy

15
Makefile Normal file
View File

@ -0,0 +1,15 @@
CC=gcc
CFLAGS= -g -Wall -Werror
all: proxy
proxy: proxy.c
$(CC) $(CFLAGS) -o proxy_parse.o -c proxy_parse.c
$(CC) $(CFLAGS) -o proxy.o -c proxy.c
$(CC) $(CFLAGS) -o proxy proxy_parse.o proxy.o
clean:
rm -f proxy *.o
tar:
tar -cvzf ass1.tgz proxy.c README Makefile proxy_parse.c proxy_parse.h

10
README Normal file
View File

@ -0,0 +1,10 @@
============
CUSTOM PROXY
============
Proxy for HTTP/1.0 standard protocol
RFC: datatracker.ietf.org/doc/html/rfc2616
RES: cs.princeton.edu/courses/archive/spr13/cos461/assignments-proxy.html
RES: en.wikipedia.org/wiki/Proxy_server
RES: TCP/IP sockets in C - Practical guide for programmers 2nd edition

59
proxy.c Normal file
View File

@ -0,0 +1,59 @@
#include "proxy_parse.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#define PROXY_PORT 80
#define PROXY_CONN 20
int main(int argc, char *argv[]) {
int server_socket;
int new_fd;
int ret;
ret = server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ret < 0) {
fprintf(stderr, "Failed to create a socket to listen on\n");
return EXIT_FAILURE;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PROXY_PORT);
ret = bind(server_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (ret < 0) {
fprintf(stderr, "Failed to bind to port %d\n", PROXY_PORT);
return EXIT_FAILURE;
}
ret = listen(server_socket, PROXY_CONN);
if (ret < 0) {
fprintf(stderr, "Failed to listen on port %d\n", PROXY_PORT);
return EXIT_FAILURE;
}
fprintf(stdout, "Listening on port %d\n", PROXY_PORT);
for (;;) {
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
int client_socket;
ret = client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &client_addrlen);
if (ret < 0) {
fprintf(stderr, "Failed to establish socket connection with client\n");
return EXIT_FAILURE;
}
fprintf(stdout, "[CLIENT %d] Successfully connected\n", client_socket);
}
printf("New connection with fd -> %d\n", new_fd);
return EXIT_SUCCESS;
}

604
proxy_parse.c Normal file
View File

@ -0,0 +1,604 @@
/*
proxy_parse.c -- a HTTP Request Parsing Library.
COS 461
*/
#include "proxy_parse.h"
#define DEFAULT_NHDRS 8
#define MAX_REQ_LEN 65535
#define MIN_REQ_LEN 4
static const char *root_abs_path = "/";
/* private function declartions */
int ParsedRequest_printRequestLine(struct ParsedRequest *pr,
char * buf, size_t buflen,
size_t *tmp);
size_t ParsedRequest_requestLineLen(struct ParsedRequest *pr);
/*
* debug() prints out debugging info if DEBUG is set to 1
*
* parameter format: same as printf
*
*/
void debug(const char * format, ...) {
va_list args;
if (DEBUG) {
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
}
/*
* ParsedHeader Public Methods
*/
/* Set a header with key and value */
int ParsedHeader_set(struct ParsedRequest *pr,
const char * key, const char * value)
{
struct ParsedHeader *ph;
ParsedHeader_remove (pr, key);
if (pr->headerslen <= pr->headersused+1) {
pr->headerslen = pr->headerslen * 2;
pr->headers =
(struct ParsedHeader *)realloc(pr->headers,
pr->headerslen * sizeof(struct ParsedHeader));
if (!pr->headers)
return -1;
}
ph = pr->headers + pr->headersused;
pr->headersused += 1;
ph->key = (char *)malloc(strlen(key)+1);
memcpy(ph->key, key, strlen(key));
ph->key[strlen(key)] = '\0';
ph->value = (char *)malloc(strlen(value)+1);
memcpy(ph->value, value, strlen(value));
ph->value[strlen(value)] = '\0';
ph->keylen = strlen(key)+1;
ph->valuelen = strlen(value)+1;
return 0;
}
/* get the parsedHeader with the specified key or NULL */
struct ParsedHeader* ParsedHeader_get(struct ParsedRequest *pr,
const char * key)
{
size_t i = 0;
struct ParsedHeader * tmp;
while(pr->headersused > i)
{
tmp = pr->headers + i;
if(tmp->key && key && strcmp(tmp->key, key) == 0)
{
return tmp;
}
i++;
}
return NULL;
}
/* remove the specified key from parsedHeader */
int ParsedHeader_remove(struct ParsedRequest *pr, const char *key)
{
struct ParsedHeader *tmp;
tmp = ParsedHeader_get(pr, key);
if(tmp == NULL)
return -1;
free(tmp->key);
free(tmp->value);
tmp->key = NULL;
return 0;
}
/* modify the header with given key, giving it a new value
* return 1 on success and 0 if no such header found
*
int ParsedHeader_modify(struct ParsedRequest *pr, const char * key,
const char *newValue)
{
struct ParsedHeader *tmp;
tmp = ParsedHeader_get(pr, key);
if(tmp != NULL)
{
if(tmp->valuelen < strlen(newValue)+1)
{
tmp->valuelen = strlen(newValue)+1;
tmp->value = (char *) realloc(tmp->value,
tmp->valuelen * sizeof(char));
}
strcpy(tmp->value, newValue);
return 1;
}
return 0;
}
*/
/*
ParsedHeader Private Methods
*/
void ParsedHeader_create(struct ParsedRequest *pr)
{
pr->headers =
(struct ParsedHeader *)malloc(sizeof(struct ParsedHeader)*DEFAULT_NHDRS);
pr->headerslen = DEFAULT_NHDRS;
pr->headersused = 0;
}
size_t ParsedHeader_lineLen(struct ParsedHeader * ph)
{
if(ph->key != NULL)
{
return strlen(ph->key)+strlen(ph->value)+4;
}
return 0;
}
size_t ParsedHeader_headersLen(struct ParsedRequest *pr)
{
if (!pr || !pr->buf)
return 0;
size_t i = 0;
int len = 0;
while(pr->headersused > i)
{
len += ParsedHeader_lineLen(pr->headers + i);
i++;
}
len += 2;
return len;
}
int ParsedHeader_printHeaders(struct ParsedRequest * pr, char * buf,
size_t len)
{
char * current = buf;
struct ParsedHeader * ph;
size_t i = 0;
if(len < ParsedHeader_headersLen(pr))
{
debug("buffer for printing headers too small\n");
return -1;
}
while(pr->headersused > i)
{
ph = pr->headers+i;
if (ph->key) {
memcpy(current, ph->key, strlen(ph->key));
memcpy(current+strlen(ph->key), ": ", 2);
memcpy(current+strlen(ph->key) +2 , ph->value,
strlen(ph->value));
memcpy(current+strlen(ph->key) +2+strlen(ph->value) ,
"\r\n", 2);
current += strlen(ph->key)+strlen(ph->value)+4;
}
i++;
}
memcpy(current, "\r\n",2);
return 0;
}
void ParsedHeader_destroyOne(struct ParsedHeader * ph)
{
if(ph->key != NULL)
{
free(ph->key);
ph->key = NULL;
free(ph->value);
ph->value = NULL;
ph->keylen = 0;
ph->valuelen = 0;
}
}
void ParsedHeader_destroy(struct ParsedRequest * pr)
{
size_t i = 0;
while(pr->headersused > i)
{
ParsedHeader_destroyOne(pr->headers + i);
i++;
}
pr->headersused = 0;
free(pr->headers);
pr->headerslen = 0;
}
int ParsedHeader_parse(struct ParsedRequest * pr, char * line)
{
char * key;
char * value;
char * index1;
char * index2;
index1 = index(line, ':');
if(index1 == NULL)
{
debug("No colon found\n");
return -1;
}
key = (char *)malloc((index1-line+1)*sizeof(char));
memcpy(key, line, index1-line);
key[index1-line]='\0';
index1 += 2;
index2 = strstr(index1, "\r\n");
value = (char *) malloc((index2-index1+1)*sizeof(char));
memcpy(value, index1, (index2-index1));
value[index2-index1] = '\0';
ParsedHeader_set(pr, key, value);
free(key);
free(value);
return 0;
}
/*
ParsedRequest Public Methods
*/
void ParsedRequest_destroy(struct ParsedRequest *pr)
{
if(pr->buf != NULL)
{
free(pr->buf);
}
if (pr->path != NULL) {
free(pr->path);
}
if(pr->headerslen > 0)
{
ParsedHeader_destroy(pr);
}
free(pr);
}
struct ParsedRequest* ParsedRequest_create()
{
struct ParsedRequest *pr;
pr = (struct ParsedRequest *)malloc(sizeof(struct ParsedRequest));
if (pr != NULL)
{
ParsedHeader_create(pr);
pr->buf = NULL;
pr->method = NULL;
pr->protocol = NULL;
pr->host = NULL;
pr->path = NULL;
pr->version = NULL;
pr->buf = NULL;
pr->buflen = 0;
}
return pr;
}
/*
Recreate the entire buffer from a parsed request object.
buf must be allocated
*/
int ParsedRequest_unparse(struct ParsedRequest *pr, char *buf,
size_t buflen)
{
if (!pr || !pr->buf)
return -1;
size_t tmp;
if (ParsedRequest_printRequestLine(pr, buf, buflen, &tmp) < 0)
return -1;
if (ParsedHeader_printHeaders(pr, buf+tmp, buflen-tmp) < 0)
return -1;
return 0;
}
/*
Recreate the headers from a parsed request object.
buf must be allocated
*/
int ParsedRequest_unparse_headers(struct ParsedRequest *pr, char *buf,
size_t buflen)
{
if (!pr || !pr->buf)
return -1;
if (ParsedHeader_printHeaders(pr, buf, buflen) < 0)
return -1;
return 0;
}
/* Size of the headers if unparsed into a string */
size_t ParsedRequest_totalLen(struct ParsedRequest *pr)
{
if (!pr || !pr->buf)
return 0;
return ParsedRequest_requestLineLen(pr)+ParsedHeader_headersLen(pr);
}
/*
Parse request buffer
Parameters:
parse: ptr to a newly created ParsedRequest object
buf: ptr to the buffer containing the request (need not be NUL terminated)
and the trailing \r\n\r\n
buflen: length of the buffer including the trailing \r\n\r\n
Return values:
-1: failure
0: success
*/
int
ParsedRequest_parse(struct ParsedRequest * parse, const char *buf,
int buflen)
{
char *full_addr;
char *saveptr;
char *index;
char *currentHeader;
if (parse->buf != NULL) {
debug("parse object already assigned to a request\n");
return -1;
}
if (buflen < MIN_REQ_LEN || buflen > MAX_REQ_LEN) {
debug("invalid buflen %d", buflen);
return -1;
}
/* Create NUL terminated tmp buffer */
char *tmp_buf = (char *)malloc(buflen + 1); /* including NUL */
memcpy(tmp_buf, buf, buflen);
tmp_buf[buflen] = '\0';
index = strstr(tmp_buf, "\r\n\r\n");
if (index == NULL) {
debug("invalid request line, no end of header\n");
free(tmp_buf);
return -1;
}
/* Copy request line into parse->buf */
index = strstr(tmp_buf, "\r\n");
if (parse->buf == NULL) {
parse->buf = (char *) malloc((index-tmp_buf)+1);
parse->buflen = (index-tmp_buf)+1;
}
memcpy(parse->buf, tmp_buf, index-tmp_buf);
parse->buf[index-tmp_buf] = '\0';
/* Parse request line */
parse->method = strtok_r(parse->buf, " ", &saveptr);
if (parse->method == NULL) {
debug( "invalid request line, no whitespace\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
if (strcmp (parse->method, "GET")) {
debug( "invalid request line, method not 'GET': %s\n",
parse->method);
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
full_addr = strtok_r(NULL, " ", &saveptr);
if (full_addr == NULL) {
debug( "invalid request line, no full address\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
parse->version = full_addr + strlen(full_addr) + 1;
if (parse->version == NULL) {
debug( "invalid request line, missing version\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
if (strncmp (parse->version, "HTTP/", 5)) {
debug( "invalid request line, unsupported version %s\n",
parse->version);
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
parse->protocol = strtok_r(full_addr, "://", &saveptr);
if (parse->protocol == NULL) {
debug( "invalid request line, missing host\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
const char *rem = full_addr + strlen(parse->protocol) + strlen("://");
size_t abs_uri_len = strlen(rem);
parse->host = strtok_r(NULL, "/", &saveptr);
if (parse->host == NULL) {
debug( "invalid request line, missing host\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
if (strlen(parse->host) == abs_uri_len) {
debug("invalid request line, missing absolute path\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
return -1;
}
parse->path = strtok_r(NULL, " ", &saveptr);
if (parse->path == NULL) { // replace empty abs_path with "/"
int rlen = strlen(root_abs_path);
parse->path = (char *)malloc(rlen + 1);
strncpy(parse->path, root_abs_path, rlen + 1);
} else if (strncmp(parse->path, root_abs_path, strlen(root_abs_path)) == 0) {
debug("invalid request line, path cannot begin "
"with two slash characters\n");
free(tmp_buf);
free(parse->buf);
parse->buf = NULL;
parse->path = NULL;
return -1;
} else {
// copy parse->path, prefix with a slash
char *tmp_path = parse->path;
int rlen = strlen(root_abs_path);
int plen = strlen(parse->path);
parse->path = (char *)malloc(rlen + plen + 1);
strncpy(parse->path, root_abs_path, rlen);
strncpy(parse->path + rlen, tmp_path, plen + 1);
}
parse->host = strtok_r(parse->host, ":", &saveptr);
parse->port = strtok_r(NULL, "/", &saveptr);
if (parse->host == NULL) {
debug( "invalid request line, missing host\n");
free(tmp_buf);
free(parse->buf);
free(parse->path);
parse->buf = NULL;
parse->path = NULL;
return -1;
}
if (parse->port != NULL) {
int port = strtol (parse->port, (char **)NULL, 10);
if (port == 0 && errno == EINVAL) {
debug("invalid request line, bad port: %s\n", parse->port);
free(tmp_buf);
free(parse->buf);
free(parse->path);
parse->buf = NULL;
parse->path = NULL;
return -1;
}
}
/* Parse headers */
int ret = 0;
currentHeader = strstr(tmp_buf, "\r\n")+2;
while (currentHeader[0] != '\0' &&
!(currentHeader[0] == '\r' && currentHeader[1] == '\n')) {
//debug("line %s %s", parse->version, currentHeader);
if (ParsedHeader_parse(parse, currentHeader)) {
ret = -1;
break;
}
currentHeader = strstr(currentHeader, "\r\n");
if (currentHeader == NULL || strlen (currentHeader) < 2)
break;
currentHeader += 2;
}
free(tmp_buf);
return ret;
}
/*
ParsedRequest Private Methods
*/
size_t ParsedRequest_requestLineLen(struct ParsedRequest *pr)
{
if (!pr || !pr->buf)
return 0;
size_t len =
strlen(pr->method) + 1 + strlen(pr->protocol) + 3 +
strlen(pr->host) + 1 + strlen(pr->version) + 2;
if(pr->port != NULL)
{
len += strlen(pr->port)+1;
}
/* path is at least a slash */
len += strlen(pr->path);
return len;
}
int ParsedRequest_printRequestLine(struct ParsedRequest *pr,
char * buf, size_t buflen,
size_t *tmp)
{
char * current = buf;
if(buflen < ParsedRequest_requestLineLen(pr))
{
debug("not enough memory for first line\n");
return -1;
}
memcpy(current, pr->method, strlen(pr->method));
current += strlen(pr->method);
current[0] = ' ';
current += 1;
memcpy(current, pr->protocol, strlen(pr->protocol));
current += strlen(pr->protocol);
memcpy(current, "://", 3);
current += 3;
memcpy(current, pr->host, strlen(pr->host));
current += strlen(pr->host);
if(pr->port != NULL)
{
current[0] = ':';
current += 1;
memcpy(current, pr->port, strlen(pr->port));
current += strlen(pr->port);
}
/* path is at least a slash */
memcpy(current, pr->path, strlen(pr->path));
current += strlen(pr->path);
current[0] = ' ';
current += 1;
memcpy(current, pr->version, strlen(pr->version));
current += strlen(pr->version);
memcpy(current, "\r\n", 2);
current +=2;
*tmp = current-buf;
return 0;
}

138
proxy_parse.h Normal file
View File

@ -0,0 +1,138 @@
/*
* proxy_parse.h -- a HTTP Request Parsing Library.
*
* Written by: Matvey Arye
* For: COS 518
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#ifndef PROXY_PARSE
#define PROXY_PARSE
#define DEBUG 1
/*
ParsedRequest objects are created from parsing a buffer containing a HTTP
request. The request buffer consists of a request line followed by a number
of headers. Request line fields such as method, protocol etc. are stored
explicitly. Headers such as 'Content-Length' and their values are maintained
in a linked list. Each node in this list is a ParsedHeader and contains a
key-value pair.
The buf and buflen fields are used internally to maintain the parsed request
line.
*/
struct ParsedRequest {
char *method;
char *protocol;
char *host;
char *port;
char *path;
char *version;
char *buf;
size_t buflen;
struct ParsedHeader *headers;
size_t headersused;
size_t headerslen;
};
/*
ParsedHeader: any header after the request line is a key-value pair with the
format "key:value\r\n" and is maintained in the ParsedHeader linked list
within ParsedRequest
*/
struct ParsedHeader {
char * key;
size_t keylen;
char * value;
size_t valuelen;
};
/* Create an empty parsing object to be used exactly once for parsing a single
* request buffer */
struct ParsedRequest* ParsedRequest_create();
/* Parse the request buffer in buf given that buf is of length buflen */
int ParsedRequest_parse(struct ParsedRequest * parse, const char *buf,
int buflen);
/* Destroy the parsing object. */
void ParsedRequest_destroy(struct ParsedRequest *pr);
/*
Retrieve the entire buffer from a parsed request object. buf must be an
allocated buffer of size buflen, with enough space to write the request
line, headers and the trailing \r\n. buf will not be NUL terminated by
unparse().
*/
int ParsedRequest_unparse(struct ParsedRequest *pr, char *buf,
size_t buflen);
/*
Retrieve the entire buffer with the exception of request line from a parsed
request object. buf must be an allocated buffer of size buflen, with enough
space to write the headers and the trailing \r\n. buf will not be NUL
terminated by unparse(). If there are no headers, the trailing \r\n is
unparsed.
*/
int ParsedRequest_unparse_headers(struct ParsedRequest *pr, char *buf,
size_t buflen);
/* Total length including request line, headers and the trailing \r\n*/
size_t ParsedRequest_totalLen(struct ParsedRequest *pr);
/* Length including headers, if any, and the trailing \r\n but excluding the
* request line.
*/
size_t ParsedHeader_headersLen(struct ParsedRequest *pr);
/* Set, get, and remove null-terminated header keys and values */
int ParsedHeader_set(struct ParsedRequest *pr, const char * key,
const char * value);
struct ParsedHeader* ParsedHeader_get(struct ParsedRequest *pr,
const char * key);
int ParsedHeader_remove (struct ParsedRequest *pr, const char * key);
/* debug() prints out debugging info if DEBUG is set to 1 */
void debug(const char * format, ...);
/* Example usage:
const char *c =
"GET http://www.google.com:80/index.html/ HTTP/1.0\r\nContent-Length:"
" 80\r\nIf-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT\r\n\r\n";
int len = strlen(c);
if (ParsedRequest_parse(req, c, len) < 0) {
printf("parse failed\n");
return -1;
}
printf("Method:%s\n", req->method);
printf("Host:%s\n", req->host);
int rlen = ParsedRequest_totalLen(req);
char *b = (char *)malloc(rlen+1);
if (ParsedRequest_unparse(req, b, rlen) < 0) {
printf("unparse failed\n");
return -1;
}
b[rlen]='\0';
struct ParsedHeader *r = ParsedHeader_get(req, "If-Modified-Since");
printf("Modified value: %s\n", r->value);
ParsedRequest_destroy(req);
*/
#endif