Compare commits
No commits in common. "master" and "reload-dementor-httpd-2028-1" have entirely different histories.
master
...
reload-dem
32 changed files with 22 additions and 167 deletions
0
.gitignore → httpd/.gitignore
vendored
0
.gitignore → httpd/.gitignore
vendored
|
|
@ -17,7 +17,7 @@ void daemon_init(struct config *cfg)
|
||||||
config = cfg;
|
config = cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_pid(void)
|
int get_pid()
|
||||||
{
|
{
|
||||||
FILE *stream = fopen(config->pid_file, "r");
|
FILE *stream = fopen(config->pid_file, "r");
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int get_pid(void);
|
int get_pid();
|
||||||
|
|
||||||
/* @brief
|
/* @brief
|
||||||
*/
|
*/
|
||||||
|
|
@ -42,12 +42,6 @@ ssize_t read_value(struct string *str, size_t offset, struct string **res)
|
||||||
if (str->size <= offset + nread || str->data[offset + nread] != '\n')
|
if (str->size <= offset + nread || str->data[offset + nread] != '\n')
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
|
|
||||||
// Trim trailing \r
|
|
||||||
if ((*res)->size > 0 && (*res)->data[(*res)->size - 1] == '\r')
|
|
||||||
{
|
|
||||||
(*res)->size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nread;
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,8 +53,7 @@ ssize_t parse_headers(struct http_request *res, struct string *req,
|
||||||
|
|
||||||
// Yes I know I do one useless allocation but I really don't care at this
|
// Yes I know I do one useless allocation but I really don't care at this
|
||||||
// point
|
// point
|
||||||
while (i < req->size && req->data[i] != '\n'
|
while (req->data[i] != '\n') // ! Blank line
|
||||||
&& req->data[i] != '\r') // ! Blank line
|
|
||||||
{
|
{
|
||||||
if (header == NULL)
|
if (header == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -80,26 +73,21 @@ ssize_t parse_headers(struct http_request *res, struct string *req,
|
||||||
return ERR_HTTP_OUT_OF_MEMORY;
|
return ERR_HTTP_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// Read field
|
// Read field
|
||||||
ssize_t nread = read_field(req, i, &header->field);
|
ssize_t nread = read_field(req, offset, &header->field);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return nread; // Contains error code when negative
|
return nread; // Contains error code when negative
|
||||||
|
|
||||||
i += nread + 1;
|
i += nread;
|
||||||
|
|
||||||
// Read value
|
// Read value
|
||||||
nread = read_value(req, i, &header->value);
|
nread = read_value(req, offset, &header->value);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return nread; // Contains error code when negative
|
return nread; // Contains error code when negative
|
||||||
|
|
||||||
i += nread + 1;
|
i += nread + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < req->size && req->data[i] == '\r')
|
return i + 1;
|
||||||
i++;
|
|
||||||
if (i < req->size && req->data[i] == '\n')
|
|
||||||
i++;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct http_header *get_header(struct http_header *headers, const char *field)
|
struct http_header *get_header(struct http_header *headers, const char *field)
|
||||||
|
|
@ -68,15 +68,9 @@ static ssize_t parse_reqline(struct http_request *res, struct string *req)
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
i += skipped;
|
i += skipped;
|
||||||
|
|
||||||
// CRLF (EOL) oh qu'il est casse couilles celui-là
|
// CRLF (EOL)
|
||||||
ssize_t req_size = req->size; // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
|
if (req->data[i++] != '\r' && req->data[i++] != '\n')
|
||||||
if (i < req_size && req->data[i] == '\r')
|
|
||||||
i++;
|
|
||||||
if (i >= req_size || req->data[i] != '\n')
|
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
i++;
|
|
||||||
// Donc 2h de debug pour ça là ? Plutot envie de me tirer une balle si vous
|
|
||||||
// voulez mon avis
|
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
@ -192,32 +186,9 @@ static void check_req(struct http_request *req, struct http_response *resp)
|
||||||
else if (string_compare_strictly_n_str(req->protocol, "HTTP/1.1",
|
else if (string_compare_strictly_n_str(req->protocol, "HTTP/1.1",
|
||||||
strlen("HTTP/1.1"))
|
strlen("HTTP/1.1"))
|
||||||
!= 0)
|
!= 0)
|
||||||
|
|
||||||
resp->status_code = 505;
|
resp->status_code = 505;
|
||||||
|
|
||||||
// Host
|
|
||||||
if (resp->status_code != 400 && resp->status_code != 505)
|
|
||||||
{
|
|
||||||
int host_count = 0;
|
|
||||||
struct http_header *cur = req->headers;
|
|
||||||
while (cur != NULL)
|
|
||||||
{
|
|
||||||
if (cur->field->size == 4
|
|
||||||
&& string_compare_n_str(cur->field, "host", 4) == 0)
|
|
||||||
{
|
|
||||||
host_count++;
|
|
||||||
if (cur->value == NULL || cur->value->size == 0)
|
|
||||||
{
|
|
||||||
resp->status_code = 400;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host_count != 1)
|
|
||||||
resp->status_code = 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf("%s %d\n", req->protocol->data, resp->status_code);
|
// printf("%s %d\n", req->protocol->data, resp->status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,8 +217,7 @@ void handle_request(int client_fd, char *client_ip)
|
||||||
{
|
{
|
||||||
string_concat_str(str, buffer, nread);
|
string_concat_str(str, buffer, nread);
|
||||||
}
|
}
|
||||||
if (nread > 0)
|
string_concat_str(str, buffer, nread);
|
||||||
string_concat_str(str, buffer, nread);
|
|
||||||
|
|
||||||
// Parse request
|
// Parse request
|
||||||
struct http_request *req = parse_request(str);
|
struct http_request *req = parse_request(str);
|
||||||
|
|
@ -332,17 +302,8 @@ struct http_request *parse_request(struct string *req)
|
||||||
ssize_t nread = parse_reqline(res, req);
|
ssize_t nread = parse_reqline(res, req);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
{
|
{
|
||||||
if (nread == ERR_HTTP_NOT_IMPLEMENTED)
|
free(res);
|
||||||
res->status_code = 501;
|
return NULL;
|
||||||
else
|
|
||||||
res->status_code = 400;
|
|
||||||
|
|
||||||
if (res->target == NULL)
|
|
||||||
res->target = string_create("", 0);
|
|
||||||
if (res->protocol == NULL)
|
|
||||||
res->protocol = string_create("HTTP/1.1", 8);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split path and query
|
// Split path and query
|
||||||
|
|
@ -354,8 +315,8 @@ struct http_request *parse_request(struct string *req)
|
||||||
nread = parse_headers(res, req, i);
|
nread = parse_headers(res, req, i);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
{
|
{
|
||||||
res->status_code = 400;
|
free(res);
|
||||||
return res;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -375,11 +336,8 @@ struct http_response *generate_response(struct http_request *req)
|
||||||
res->protocol = string_create(protocol, strlen(protocol));
|
res->protocol = string_create(protocol, strlen(protocol));
|
||||||
|
|
||||||
// Target
|
// Target
|
||||||
if (req->status_code == 0)
|
str_concat_string(config->servers->root_dir,
|
||||||
{
|
strlen(config->servers->root_dir), req->target);
|
||||||
str_concat_string(config->servers->root_dir,
|
|
||||||
strlen(config->servers->root_dir), req->target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status code
|
// Status code
|
||||||
if (req->status_code == 0)
|
if (req->status_code == 0)
|
||||||
|
|
@ -401,8 +359,7 @@ struct http_response *generate_response(struct http_request *req)
|
||||||
res->status_code = req->status_code;
|
res->status_code = req->status_code;
|
||||||
|
|
||||||
// Check protocol and method
|
// Check protocol and method
|
||||||
if (req->status_code == 0)
|
check_req(req, res);
|
||||||
check_req(req, res);
|
|
||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
char *time = get_time();
|
char *time = get_time();
|
||||||
|
|
@ -424,10 +381,6 @@ struct http_response *generate_response(struct http_request *req)
|
||||||
res->status_code = 403;
|
res->status_code = 403;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
append_header(&res->headers, create_header("Content-Length", "0"));
|
|
||||||
}
|
|
||||||
append_header(&res->headers, create_header("Connection", "close"));
|
append_header(&res->headers, create_header("Connection", "close"));
|
||||||
|
|
||||||
// Status msg
|
// Status msg
|
||||||
|
|
@ -26,7 +26,7 @@ void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg)
|
||||||
config.server_cfg = serv_cfg;
|
config.server_cfg = serv_cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_err(void)
|
void print_err()
|
||||||
{
|
{
|
||||||
print_log_err("%s", get_err());
|
print_log_err("%s", get_err());
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ void print_log_err(char *format, ...)
|
||||||
fprintf(stderr, "Error: %s", get_err());
|
fprintf(stderr, "Error: %s", get_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_err(void)
|
char *get_err()
|
||||||
{
|
{
|
||||||
return strerror(errno);
|
return strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg);
|
||||||
/* @brief Retrieves the last error with errno and prints the corresponding
|
/* @brief Retrieves the last error with errno and prints the corresponding
|
||||||
* error message in the logs and stderr
|
* error message in the logs and stderr
|
||||||
*/
|
*/
|
||||||
void print_err(void);
|
void print_err();
|
||||||
|
|
||||||
/* @brief Prints error logs, just like print_log() but for errors
|
/* @brief Prints error logs, just like print_log() but for errors
|
||||||
*/
|
*/
|
||||||
|
|
@ -22,6 +22,6 @@ void print_log_err(char *format, ...);
|
||||||
|
|
||||||
/* @brief Returns the string corresponding to the last error that happened
|
/* @brief Returns the string corresponding to the last error that happened
|
||||||
*/
|
*/
|
||||||
char *get_err(void);
|
char *get_err();
|
||||||
|
|
||||||
#endif // ! ERRORS_H
|
#endif // ! ERRORS_H
|
||||||
|
|
@ -130,49 +130,3 @@ def test_bad_request():
|
||||||
assert response.status == 400
|
assert response.status == 400
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
|
||||||
def test_head_index():
|
|
||||||
proc = spawn_httpd("out.log")
|
|
||||||
try:
|
|
||||||
req = requests.head(f"http://{host}:{port}/index.html")
|
|
||||||
assert req.status_code == 200
|
|
||||||
assert req.text == ""
|
|
||||||
finally:
|
|
||||||
kill_httpd(proc)
|
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
|
||||||
def test_missing_host():
|
|
||||||
proc = spawn_httpd("out.log")
|
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
|
||||||
sock.connect((host,int(port)))
|
|
||||||
|
|
||||||
request = f"GET /index.html HTTP/1.1\r\nConnection: close\r\n\r\n"
|
|
||||||
|
|
||||||
sock.sendall(request.encode())
|
|
||||||
|
|
||||||
response = http.client.HTTPResponse(sock)
|
|
||||||
response.begin()
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert response.status == 400
|
|
||||||
finally:
|
|
||||||
kill_httpd(proc)
|
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
|
||||||
def test_directory_traversal():
|
|
||||||
proc = spawn_httpd("out.log")
|
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
|
||||||
sock.connect((host,int(port)))
|
|
||||||
|
|
||||||
request = f"GET /../test_suite.py HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
|
||||||
|
|
||||||
sock.sendall(request.encode())
|
|
||||||
|
|
||||||
response = http.client.HTTPResponse(sock)
|
|
||||||
response.begin()
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert response.status in [400, 403, 404]
|
|
||||||
finally:
|
|
||||||
kill_httpd(proc)
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Simple test script for HTTP/1.1 Host header compliance
|
|
||||||
# Usage: ./test_host_compliance.sh [IP] [PORT]
|
|
||||||
|
|
||||||
IP=${1:-"127.0.0.1"}
|
|
||||||
PORT=${2:-"6996"}
|
|
||||||
|
|
||||||
echo "Targeting server at $IP:$PORT"
|
|
||||||
|
|
||||||
test_req() {
|
|
||||||
NAME="$1"
|
|
||||||
PAYLOAD="$2"
|
|
||||||
EXPECTED="$3"
|
|
||||||
|
|
||||||
echo -n "Test: $NAME ... "
|
|
||||||
# Send payload, wait max 1s for response
|
|
||||||
RESP=$(printf "$PAYLOAD" | nc -w 1 $IP $PORT 2>/dev/null | head -n 1)
|
|
||||||
|
|
||||||
if echo "$RESP" | grep -q "$EXPECTED"; then
|
|
||||||
echo "PASS"
|
|
||||||
else
|
|
||||||
echo "FAIL (Expected '$EXPECTED', got '$RESP')"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# 1. Valid Request
|
|
||||||
test_req "Valid Request" "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" "200 OK"
|
|
||||||
|
|
||||||
# 2. Missing Host Header
|
|
||||||
test_req "Missing Host" "GET / HTTP/1.1\r\n\r\n" "400 Bad Request"
|
|
||||||
|
|
||||||
# 3. Empty Host Header
|
|
||||||
test_req "Empty Host" "GET / HTTP/1.1\r\nHost:\r\n\r\n" "400 Bad Request"
|
|
||||||
|
|
||||||
# 4. Multiple Host Headers
|
|
||||||
test_req "Multiple Hosts" "GET / HTTP/1.1\r\nHost: a\r\nHost: b\r\n\r\n" "400 Bad Request"
|
|
||||||
|
|
||||||
# 5. Bad Protocol Version (Should be 505 now with the fix)
|
|
||||||
test_req "Bad Protocol (HTTP/1.0)" "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n" "505"
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue