From 885ec2226fd5165418e0b0a590c564d636f5cf96 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sun, 27 Dec 2020 18:33:50 +0100 Subject: [PATCH] FastCGI working buggy --- src/client.c | 77 ++++++-- src/fastcgi.c | 436 +++++++++++++++++++++++++++++++++++++++++- src/fastcgi.h | 136 ++++++++++++- src/http.c | 2 +- src/http.h | 2 +- src/necronda-server.c | 16 +- src/necronda-server.h | 2 + 7 files changed, 641 insertions(+), 30 deletions(-) diff --git a/src/client.c b/src/client.c index 1a8ac1f..94b3458 100644 --- a/src/client.c +++ b/src/client.c @@ -9,6 +9,7 @@ #include "utils.h" #include "uri.h" #include "http.h" +#include "fastcgi.h" int server_keep_alive = 1; @@ -22,7 +23,7 @@ char *get_webroot(const char *http_host) { unsigned long len = strlen(webroot_base); while (webroot_base[len - 1] == '/') len--; long pos = strchr(http_host, ':') - http_host; - sprintf(webroot, "%.*s/%.*s", (int) len, webroot_base, (int) (pos == -1 ? strlen(http_host) : pos), http_host); + sprintf(webroot, "%.*s/%.*s", (int) len, webroot_base, (int) (pos < 0 ? strlen(http_host) : pos), http_host); return webroot; } @@ -35,7 +36,7 @@ int client_websocket_handler() { return 0; } -int client_request_handler(sock *client, int req_num) { +int client_request_handler(sock *client, unsigned long client_num, unsigned int req_num) { struct timespec begin, end; int ret, client_keep_alive, dir_mode; char buf0[1024], buf1[1024]; @@ -43,10 +44,12 @@ int client_request_handler(sock *client, int req_num) { char buffer[CHUNK_SIZE]; err_msg[0] = 0; char *host, *hdr_connection, *webroot; - unsigned long content_length = 0; + long content_length = 0; FILE *file = NULL; msg_buf[0] = 0; int accept_if_modified_since = 0; + int use_fastcgi = 0; + fastcgi_conn php_fpm = {.socket = 0, .req_id = 0}; http_res res; sprintf(res.version, "1.1"); @@ -221,21 +224,42 @@ int client_request_handler(sock *client, int req_num) { fseek(file, 0, SEEK_END); content_length = ftell(file); fseek(file, 0, SEEK_SET); + } else { + res.status = http_get_status(200); + if (fastcgi_init(&php_fpm, client_num, req_num, client, &req, &uri) != 0) { + res.status = http_get_status(502); + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + goto respond; + } + + if (strncmp(req.method, "POST", 4) == 0 || strncmp(req.method, "PUT", 3) == 0) { + // TODO send content to fastcgi + } else { + fastcgi_close_stdin(&php_fpm); + } + + char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding", HTTP_LOWER); + if (accept_encoding != NULL && strstr(accept_encoding, "deflate") != NULL) { + //http_add_header_field(&res.hdr, "Content-Encoding", "deflate"); + } + + if (fastcgi_header(&php_fpm, &res, err_msg) != 0) { + goto respond; + } + + content_length = -1; + use_fastcgi = 1; + if (http_get_header_field(&res.hdr, "Content-Length", HTTP_PRESERVE_UPPER) == NULL) { + http_add_header_field(&res.hdr, "Transfer-Encoding", "chunked"); + } + } respond: - if (server_keep_alive && client_keep_alive) { - http_add_header_field(&res.hdr, "Connection", "keep-alive"); - sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION); - http_add_header_field(&res.hdr, "Keep-Alive", buf0); - } else { - http_add_header_field(&res.hdr, "Connection", "close"); - } if (http_get_header_field(&res.hdr, "Accept-Ranges", HTTP_PRESERVE_UPPER) == NULL) { http_add_header_field(&res.hdr, "Accept-Ranges", "none"); } - - if (res.status->code >= 400 && res.status->code < 600) { + if (!use_fastcgi && file == NULL && res.status->code >= 400 && res.status->code < 600) { http_error_msg *http_msg = http_get_error_msg(res.status->code); sprintf(msg_pre_buf, http_error_document, res.status->code, res.status->msg, http_msg != NULL ? http_msg->err_msg : "", err_msg[0] != 0 ? err_msg : ""); @@ -244,8 +268,19 @@ int client_request_handler(sock *client, int req_num) { http_error_icon, "#C00000"); http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8"); } - sprintf(buf0, "%li", content_length); - http_add_header_field(&res.hdr, "Content-Length", buf0); + if (content_length >= 0) { + sprintf(buf0, "%li", content_length); + http_add_header_field(&res.hdr, "Content-Length", buf0); + } else if (http_get_header_field(&res.hdr, "Transfer-Encoding", HTTP_PRESERVE_UPPER) == NULL) { + server_keep_alive = 0; + } + if (server_keep_alive && client_keep_alive) { + http_add_header_field(&res.hdr, "Connection", "keep-alive"); + sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION); + http_add_header_field(&res.hdr, "Keep-Alive", buf0); + } else { + http_add_header_field(&res.hdr, "Connection", "close"); + } http_send_response(client, &res); clock_gettime(CLOCK_MONOTONIC, &end); @@ -294,6 +329,12 @@ int client_request_handler(sock *client, int req_num) { } snd_len += ret; } + } else if (use_fastcgi) { + char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding", HTTP_PRESERVE_UPPER); + int chunked = transfer_encoding != NULL && strncmp(transfer_encoding, "chunked", 7) == 0; + char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding", HTTP_PRESERVE_UPPER); + int comp = content_encoding != NULL && strncmp(content_encoding, "deflate", 7) == 0; + fastcgi_send(&php_fpm, client, (chunked ? FASTCGI_CHUNKED : 0) | (comp ? FASTCGI_COMPRESS : 0)); } } @@ -308,7 +349,7 @@ int client_request_handler(sock *client, int req_num) { return !client_keep_alive; } -int client_connection_handler(sock *client) { +int client_connection_handler(sock *client, unsigned long client_num) { struct timespec begin, end; int ret, req_num; char buf[16]; @@ -344,7 +385,7 @@ int client_connection_handler(sock *client) { req_num = 0; ret = 0; while (ret == 0 && server_keep_alive && req_num < REQ_PER_CONNECTION) { - ret = client_request_handler(client, req_num++); + ret = client_request_handler(client, client_num, req_num++); log_prefix = log_conn_prefix; } @@ -363,7 +404,7 @@ int client_connection_handler(sock *client) { return 0; } -int client_handler(sock *client, long client_num, struct sockaddr_in6 *client_addr) { +int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *client_addr) { int ret; struct sockaddr_in6 *server_addr; struct sockaddr_storage server_addr_storage; @@ -404,7 +445,7 @@ int client_handler(sock *client, long client_num, struct sockaddr_in6 *client_ad print("Started child process with PID %i", getpid()); - ret = client_connection_handler(client); + ret = client_connection_handler(client, client_num); free(client_addr_str_ptr); free(server_addr_str_ptr); free(log_conn_prefix); diff --git a/src/fastcgi.c b/src/fastcgi.c index b6e0cfa..f16bb05 100644 --- a/src/fastcgi.c +++ b/src/fastcgi.c @@ -1,8 +1,442 @@ /** * Necronda Web Server - * FastCGI implementation + * FastCGI interface implementation * src/fastcgi.c * Lorenz Stechauner, 2020-12-26 */ #include "fastcgi.h" +#include "necronda-server.h" + +#include + + +char *fastcgi_add_param(char *buf, const char *key, const char *value) { + char *ptr = buf; + unsigned long key_len = strlen(key); + unsigned long val_len = strlen(value); + + + if (key_len <= 127) { + ptr[0] = (char) (key_len & 0x7F); + ptr++; + } else { + ptr[0] = (char) (0x80 | (key_len >> 24)); + ptr[1] = (char) ((key_len >> 16) & 0xFF); + ptr[2] = (char) ((key_len >> 8) & 0xFF); + ptr[3] = (char) (key_len & 0xFF); + ptr += 4; + } + if (val_len <= 127) { + ptr[0] = (char) (val_len & 0x7F); + ptr++; + } else { + ptr[0] = (char) (0x80 | (val_len >> 24)); + ptr[1] = (char) ((val_len >> 16) & 0xFF); + ptr[2] = (char) ((val_len >> 8) & 0xFF); + ptr[3] = (char) (val_len & 0xFF); + ptr += 4; + } + + memcpy(ptr, key, key_len); + ptr += key_len; + memcpy(ptr, value, val_len); + ptr += val_len; + + return ptr; +} + +int fastcgi_init(fastcgi_conn *conn, unsigned int client_num, unsigned int req_num, const sock *client, + const http_req *req, const http_uri *uri) { + unsigned short req_id = (client_num & 0xFFF) << 4; + if (client_num == 0) { + req_id |= (req_num + 1) & 0xF; + } else { + req_id |= req_num & 0xF; + } + conn->req_id = req_id; + conn->out_buf = NULL; + conn->out_off = 0; + + int php_fpm = socket(AF_UNIX, SOCK_STREAM, 0); + if (php_fpm < 0) { + fprintf(stderr, ERR_STR "Unable to create unix socket: %s" CLR_STR "\n", strerror(errno)); + return -1; + } + conn->socket = php_fpm; + + struct sockaddr_un php_fpm_addr = {AF_UNIX, PHP_FPM_SOCKET}; + if (connect(conn->socket, (struct sockaddr *) &php_fpm_addr, sizeof(php_fpm_addr)) < 0) { + fprintf(stderr, ERR_STR "Unable to connect to unix socket of PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -1; + } + + FCGI_Header header = { + .version = FCGI_VERSION_1, + .requestIdB1 = req_id >> 8, + .requestIdB0 = req_id & 0xFF, + .paddingLength = 0, + .reserved = 0 + }; + + header.type = FCGI_BEGIN_REQUEST; + header.contentLengthB1 = 0; + header.contentLengthB0 = sizeof(FCGI_BeginRequestBody); + FCGI_BeginRequestRecord begin = { + header, + {.roleB1 = (FCGI_RESPONDER >> 8) & 0xFF, .roleB0 = FCGI_RESPONDER & 0xFF, .flags = 0} + }; + if (send(conn->socket, &begin, sizeof(begin), 0) != sizeof(begin)) { + fprintf(stderr, ERR_STR "Unable to send to PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -2; + } + + char param_buf[4096]; + char buf0[256]; + char *param_ptr = param_buf + sizeof(header); + + param_ptr = fastcgi_add_param(param_ptr, "REDIRECT_STATUS", "CGI"); + param_ptr = fastcgi_add_param(param_ptr, "DOCUMENT_ROOT", uri->webroot); + param_ptr = fastcgi_add_param(param_ptr, "GATEWAY_INTERFACE", "CGI/1.1"); + param_ptr = fastcgi_add_param(param_ptr, "SERVER_SOFTWARE", SERVER_STR); + param_ptr = fastcgi_add_param(param_ptr, "SERVER_PROTOCOL", "HTTP/1.1"); + param_ptr = fastcgi_add_param(param_ptr, "SERVER_NAME", http_get_header_field(&req->hdr, "Host", HTTP_LOWER)); + if (client->enc) { + param_ptr = fastcgi_add_param(param_ptr, "HTTPS", "on"); + } + + struct sockaddr_storage addr_storage; + struct sockaddr_in6 *addr; + socklen_t len = sizeof(addr_storage); + getsockname(client->socket, (struct sockaddr *) &addr_storage, &len); + addr = (struct sockaddr_in6 *) &addr_storage; + sprintf(buf0, "%i", addr->sin6_port); + param_ptr = fastcgi_add_param(param_ptr, "SERVER_PORT", buf0); + + len = sizeof(addr_storage); + getpeername(client->socket, (struct sockaddr *) &addr_storage, &len); + addr = (struct sockaddr_in6 *) &addr_storage; + sprintf(buf0, "%i", addr->sin6_port); + param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0); + + char addr_str[INET6_ADDRSTRLEN]; + char *addr_ptr; + inet_ntop(addr->sin6_family, (void *) &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN); + if (strncmp(addr_str, "::ffff:", 7) == 0) { + addr_ptr = addr_str + 7; + } else { + addr_ptr = addr_str; + } + param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", addr_ptr); + param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", addr_ptr); + //param_ptr = fastcgi_add_param(param_ptr, "REMOTE_IDENT", ""); + //param_ptr = fastcgi_add_param(param_ptr, "REMOTE_USER", ""); + + param_ptr = fastcgi_add_param(param_ptr, "REQUEST_METHOD", req->method); + param_ptr = fastcgi_add_param(param_ptr, "REQUEST_URI", req->uri); + param_ptr = fastcgi_add_param(param_ptr, "SCRIPT_NAME", uri->filename + strlen(uri->webroot)); + param_ptr = fastcgi_add_param(param_ptr, "SCRIPT_FILENAME", uri->filename); + //param_ptr = fastcgi_add_param(param_ptr, "PATH_TRANSLATED", uri->filename); + + param_ptr = fastcgi_add_param(param_ptr, "QUERY_STRING", uri->query != NULL ? uri->query : ""); + if (uri->pathinfo != NULL && strlen(uri->pathinfo) > 0) { + sprintf(buf0, "/%s", uri->pathinfo); + } else { + sprintf(buf0, ""); + } + param_ptr = fastcgi_add_param(param_ptr, "PATH_INFO", buf0); + + //param_ptr = fastcgi_add_param(param_ptr, "AUTH_TYPE", ""); + char *content_length = http_get_header_field(&req->hdr, "Content-Length", HTTP_LOWER); + param_ptr = fastcgi_add_param(param_ptr, "CONTENT_LENGTH", content_length != NULL ? content_length : ""); + char *content_type = http_get_header_field(&req->hdr, "Content-Type", HTTP_LOWER); + param_ptr = fastcgi_add_param(param_ptr, "CONTENT_TYPE", content_type != NULL ? content_type : ""); + + for (int i = 0; i < req->hdr.field_num; i++) { + char *ptr = buf0; + ptr += sprintf(ptr, "HTTP_"); + for (int j = 0; j < strlen(req->hdr.fields[i][0]); j++, ptr++) { + char ch = req->hdr.fields[i][0][j]; + if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { + ch = ch; + } else if (ch >= 'a' && ch <= 'z') { + ch &= 0x5F; + } else { + ch = '_'; + } + ptr[0] = ch; + ptr[1] = 0; + } + param_ptr = fastcgi_add_param(param_ptr, buf0, req->hdr.fields[i][1]); + } + + unsigned short param_len = param_ptr - param_buf - sizeof(header); + header.type = FCGI_PARAMS; + header.contentLengthB1 = param_len >> 8; + header.contentLengthB0 = param_len & 0xFF; + memcpy(param_buf, &header, sizeof(header)); + if (send(conn->socket, param_buf, param_len + sizeof(header), 0) != param_len + sizeof(header)) { + fprintf(stderr, ERR_STR "Unable to send to PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -2; + } + + FILE *f = fopen("/home/lorenz/Desktop/test.dmp", "wb"); + fwrite(param_buf, 1, param_len, f); + fclose(f); + + header.type = FCGI_PARAMS; + header.contentLengthB1 = 0; + header.contentLengthB0 = 0; + if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) { + fprintf(stderr, ERR_STR "Unable to send to PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -2; + } + + return 0; +} + +int fastcgi_close_stdin(fastcgi_conn *conn) { + FCGI_Header header = { + .version = FCGI_VERSION_1, + .type = FCGI_STDIN, + .requestIdB1 = conn->req_id >> 8, + .requestIdB0 = conn->req_id & 0xFF, + .contentLengthB1 = 0, + .contentLengthB0 = 0, + .paddingLength = 0, + .reserved = 0 + }; + + if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) { + fprintf(stderr, ERR_STR "Unable to send to PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -2; + } + + return 0; +} + +int fastcgi_php_error(char *msg, int msg_len, char *err_msg) { + char *msg_str = malloc(msg_len + 1); + char *ptr0 = msg_str; + strncpy(msg_str, msg, msg_len); + char *ptr1 = NULL; + int len; + int err = 0; + while (1) { + ptr1 = strstr(ptr0, "PHP message: "); + if (ptr1 == NULL) { + len = (int) (msg_len - (ptr0 - msg_str)); + } else { + len = (int) (ptr1 - ptr0); + } + if (len == 0) { + goto next; + } + + int msg_type = 0; + int msg_pre_len = 0; + if (len >= 14 && strncmp(ptr0, "PHP Warning: ", 14) == 0) { + msg_type = 1; + msg_pre_len = 14; + } else if (len >= 18 && strncmp(ptr0, "PHP Fatal error: ", 18) == 0) { + msg_type = 2; + msg_pre_len = 18; + } else if (len >= 18 && strncmp(ptr0, "PHP Parse error: ", 18) == 0) { + msg_type = 2; + msg_pre_len = 18; + } else if (len >= 18 && strncmp(ptr0, "PHP Notice: ", 13) == 0) { + msg_type = 1; + msg_pre_len = 13; + } + + char *ptr2 = ptr0; + char *ptr3; + int len2; + while (ptr2 - ptr0 < len) { + ptr3 = strchr(ptr2, '\n'); + len2 = (int) (len - (ptr2 - ptr0)); + if (ptr3 != NULL && (ptr3 - ptr2) < len2) { + len2 = (int) (ptr3 - ptr2); + } + print("%s%.*s%s", msg_type == 1 ? WRN_STR : msg_type == 2 ? ERR_STR: "", len2, ptr2, msg_type != 0 ? CLR_STR : ""); + if (msg_type == 2 && ptr2 == ptr0) { + sprintf(err_msg, "%.*s", len2, ptr2); + err = 1; + } + if (ptr3 == NULL) { + break; + } + ptr2 = ptr3 + 1; + } + + next: + if (ptr1 == NULL) { + break; + } + ptr0 = ptr1 + 13; + } + return err; +} + +int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) { + FCGI_Header header; + char *content; + unsigned short content_len, req_id; + int ret; + int err = 0; + + while (1) { + ret = recv(conn->socket, &header, sizeof(header), 0); + if (ret < 0) { + res->status = http_get_status(502); + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -1; + } else if (ret != sizeof(header)) { + res->status = http_get_status(502); + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM" CLR_STR "\n"); + return -1; + } + req_id = (header.requestIdB1 << 8) | header.requestIdB0; + content_len = (header.contentLengthB1 << 8) | header.contentLengthB0; + content = malloc(content_len + header.paddingLength); + ret = recv(conn->socket, content, content_len + header.paddingLength, 0); + if (ret < 0) { + res->status = http_get_status(502); + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + free(content); + return -1; + } else if (ret != (content_len + header.paddingLength)) { + res->status = http_get_status(502); + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM" CLR_STR "\n"); + free(content); + return -1; + } + + if (req_id != conn->req_id) { + continue; + } + + if (header.type == FCGI_END_REQUEST) { + FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; + int app_status = (body->appStatusB3 << 24) | (body->appStatusB2 << 16) | (body->appStatusB1 << 8) | + body->appStatusB0; + if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { + print(ERR_STR "FastCGI protocol error: %i" CLR_STR, body->protocolStatus); + } + if (app_status != 0) { + print(ERR_STR "Script terminated with exit code %i" CLR_STR, app_status); + } + close(conn->socket); + conn->socket = 0; + free(content); + return -2; + } else if (header.type == FCGI_STDERR) { + err = err || fastcgi_php_error(content, content_len, err_msg); + } else if (header.type == FCGI_STDOUT) { + break; + } else { + fprintf(stderr, ERR_STR "Unknown FastCGI type: %i" CLR_STR "\n", header.type); + } + + free(content); + } + if (err) { + res->status = http_get_status(500); + return -3; + } + + conn->out_buf = content; + conn->out_len = content_len; + conn->out_off = (unsigned short) (strstr(content, "\r\n\r\n") - content + 4); + + // TODO process headers and add fields to res + + return 0; +} + +int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) { + FCGI_Header header; + int ret; + char buf0[256]; + int len; + char *content, *ptr; + unsigned short req_id, content_len; + + if (conn->out_buf != NULL && conn->out_len > conn->out_off) { + content = conn->out_buf; + ptr = content + conn->out_off; + content_len = conn->out_len - conn->out_off; + goto out; + } + + while (1) { + ret = recv(conn->socket, &header, sizeof(header), 0); + if (ret < 0) { + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + return -1; + } else if (ret != sizeof(header)) { + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM" CLR_STR "\n"); + return -1; + } + req_id = (header.requestIdB1 << 8) | header.requestIdB0; + content_len = (header.contentLengthB1 << 8) | header.contentLengthB0; + content = malloc(content_len + header.paddingLength); + ptr = content; + ret = recv(conn->socket, content, content_len + header.paddingLength, 0); + if (ret < 0) { + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR "\n", strerror(errno)); + free(content); + return -1; + } else if (ret != (content_len + header.paddingLength)) { + fprintf(stderr, ERR_STR "Unable to receive from PHP-FPM" CLR_STR "\n"); + free(content); + return -1; + } + + if (header.type == FCGI_END_REQUEST) { + FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; + int app_status = (body->appStatusB3 << 24) | (body->appStatusB2 << 16) | (body->appStatusB1 << 8) | + body->appStatusB0; + if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { + print(ERR_STR "FastCGI protocol error: %i" CLR_STR, body->protocolStatus); + } + if (app_status != 0) { + print(ERR_STR "Script terminated with exit code %i" CLR_STR, app_status); + } + close(conn->socket); + conn->socket = 0; + free(content); + + if (flags & FASTCGI_CHUNKED) { + if (client->enc) { + SSL_write(client->ssl, "0\r\n\r\n", 5); + } else { + send(client->socket, "0\r\n\r\n", 5, 0); + } + } + + return 0; + } else if (header.type == FCGI_STDERR) { + print(ERR_STR "%.*s" CLR_STR, content_len, content); + } else if (header.type == FCGI_STDOUT) { + out: + len = sprintf(buf0, "%X\r\n", content_len); + if (client->enc) { + if (flags & FASTCGI_CHUNKED) SSL_write(client->ssl, buf0, len); + SSL_write(client->ssl, ptr, content_len); + if (flags & FASTCGI_CHUNKED) SSL_write(client->ssl, "\r\n", 2); + } else { + if (flags & FASTCGI_CHUNKED) send(client->socket, buf0, len, 0); + send(client->socket, ptr, content_len, 0); + if (flags & FASTCGI_CHUNKED) send(client->socket, "\r\n", 2, 0); + } + } else { + fprintf(stderr, ERR_STR "Unknown FastCGI type: %i" CLR_STR "\n", header.type); + } + free(content); + } +} diff --git a/src/fastcgi.h b/src/fastcgi.h index 6f81f1f..bfb4664 100644 --- a/src/fastcgi.h +++ b/src/fastcgi.h @@ -1,6 +1,6 @@ /** * Necronda Web Server - * FastCGI implementation (header file) + * FastCGI interface implementation (header file) * src/fastcgi.h * Lorenz Stechauner, 2020-12-26 */ @@ -8,4 +8,138 @@ #ifndef NECRONDA_SERVER_FASTCGI_H #define NECRONDA_SERVER_FASTCGI_H +#define FASTCGI_CHUNKED 1 +#define FASTCGI_COMPRESS 2 + +typedef struct { + int socket; + unsigned short req_id; + char *out_buf; + unsigned short out_len; + unsigned short out_off; +} fastcgi_conn; + +char *fastcgi_add_param(char *buf, const char *key, const char *value); + +int fastcgi_init(fastcgi_conn *conn, unsigned int client_num, unsigned int req_num, const sock *client, + const http_req *req, const http_uri *uri); + +int fastcgi_close_stdin(fastcgi_conn *conn); + +int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg); + +int fastcgi_send(fastcgi_conn *conn, sock *client, int flags); + + +/* + * Listening socket file number + */ +#define FCGI_LISTENSOCK_FILENO 0 + +typedef struct { + unsigned char version; + unsigned char type; + unsigned char requestIdB1; + unsigned char requestIdB0; + unsigned char contentLengthB1; + unsigned char contentLengthB0; + unsigned char paddingLength; + unsigned char reserved; +} FCGI_Header; + +/* + * Number of bytes in a FCGI_Header. Future versions of the protocol + * will not reduce this number. + */ +#define FCGI_HEADER_LEN 8 + +/* + * Value for version component of FCGI_Header + */ +#define FCGI_VERSION_1 1 + +/* + * Values for type component of FCGI_Header + */ +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + +/* + * Value for requestId component of FCGI_Header + */ +#define FCGI_NULL_REQUEST_ID 0 + +typedef struct { + unsigned char roleB1; + unsigned char roleB0; + unsigned char flags; + unsigned char reserved[5]; +} FCGI_BeginRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_BeginRequestBody body; +} FCGI_BeginRequestRecord; + +/* + * Mask for flags component of FCGI_BeginRequestBody + */ +#define FCGI_KEEP_CONN 1 + +/* + * Values for role component of FCGI_BeginRequestBody + */ +#define FCGI_RESPONDER 1 +#define FCGI_AUTHORIZER 2 +#define FCGI_FILTER 3 + +typedef struct { + unsigned char appStatusB3; + unsigned char appStatusB2; + unsigned char appStatusB1; + unsigned char appStatusB0; + unsigned char protocolStatus; + unsigned char reserved[3]; +} FCGI_EndRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_EndRequestBody body; +} FCGI_EndRequestRecord; + +/* + * Values for protocolStatus component of FCGI_EndRequestBody + */ +#define FCGI_REQUEST_COMPLETE 0 +#define FCGI_CANT_MPX_CONN 1 +#define FCGI_OVERLOADED 2 +#define FCGI_UNKNOWN_ROLE 3 + +/* + * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records + */ +#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" +#define FCGI_MAX_REQS "FCGI_MAX_REQS" +#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" + +typedef struct { + unsigned char type; + unsigned char reserved[7]; +} FCGI_UnknownTypeBody; + +typedef struct { + FCGI_Header header; + FCGI_UnknownTypeBody body; +} FCGI_UnknownTypeRecord; + #endif //NECRONDA_SERVER_FASTCGI_H diff --git a/src/http.c b/src/http.c index 527e290..8b4d77b 100644 --- a/src/http.c +++ b/src/http.c @@ -156,7 +156,7 @@ int http_receive_request(sock *client, http_req *req) { } } -char *http_get_header_field(http_hdr *hdr, const char *field_name, int strict) { +char *http_get_header_field(const http_hdr *hdr, const char *field_name, int strict) { size_t len = strlen(field_name); char *_field_name = malloc(len + 1); strcpy(_field_name, field_name); diff --git a/src/http.h b/src/http.h index 2dc5ccf..c4e1726 100644 --- a/src/http.h +++ b/src/http.h @@ -164,7 +164,7 @@ void http_free_res(http_res *res); int http_receive_request(sock *client, http_req *req); -char *http_get_header_field(http_hdr *hdr, const char *field_name, int strict); +char *http_get_header_field(const http_hdr *hdr, const char *field_name, int strict); void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); diff --git a/src/necronda-server.c b/src/necronda-server.c index 55ddcaf..868318e 100644 --- a/src/necronda-server.c +++ b/src/necronda-server.c @@ -238,23 +238,23 @@ int main(int argc, const char *argv[]) { } sockets[0] = socket(AF_INET6, SOCK_STREAM, 0); - if (sockets[0] == -1) goto socket_err; + if (sockets[0] < 0) goto socket_err; sockets[1] = socket(AF_INET6, SOCK_STREAM, 0); - if (sockets[1] == -1) { + if (sockets[1] < 0) { socket_err: fprintf(stderr, ERR_STR "Unable to create socket: %s" CLR_STR "\n", strerror(errno)); return 1; } for (int i = 0; i < NUM_SOCKETS; i++) { - if (setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &YES, sizeof(YES)) == -1) { + if (setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &YES, sizeof(YES)) < 0) { fprintf(stderr, ERR_STR "Unable to set options for socket %i: %s" CLR_STR "\n", i, strerror(errno)); return 1; } } - if (bind(sockets[0], (struct sockaddr *) &addresses[0], sizeof(addresses[0])) == -1) goto bind_err; - if (bind(sockets[1], (struct sockaddr *) &addresses[1], sizeof(addresses[1])) == -1) { + if (bind(sockets[0], (struct sockaddr *) &addresses[0], sizeof(addresses[0])) < 0) goto bind_err; + if (bind(sockets[1], (struct sockaddr *) &addresses[1], sizeof(addresses[1])) < 0) { bind_err: fprintf(stderr, ERR_STR "Unable to bind socket to address: %s" CLR_STR "\n", strerror(errno)); return 1; @@ -294,7 +294,7 @@ int main(int argc, const char *argv[]) { } for (int i = 0; i < NUM_SOCKETS; i++) { - if (listen(sockets[i], LISTEN_BACKLOG) == -1) { + if (listen(sockets[i], LISTEN_BACKLOG) < 0) { fprintf(stderr, ERR_STR "Unable to listen on socket %i: %s" CLR_STR "\n", i, strerror(errno)); return 1; } @@ -315,7 +315,7 @@ int main(int argc, const char *argv[]) { timeout.tv_usec = 0; read_socket_fds = socket_fds; ready_sockets_num = select(max_socket_fd + 1, &read_socket_fds, NULL, NULL, &timeout); - if (ready_sockets_num == -1) { + if (ready_sockets_num < 0) { fprintf(stderr, ERR_STR "Unable to select sockets: %s" CLR_STR "\n", strerror(errno)); return 1; } @@ -323,7 +323,7 @@ int main(int argc, const char *argv[]) { for (int i = 0; i < NUM_SOCKETS; i++) { if (FD_ISSET(sockets[i], &read_socket_fds)) { client_fd = accept(sockets[i], (struct sockaddr *) &client_addr, &client_addr_len); - if (client_fd == -1) { + if (client_fd < 0) { fprintf(stderr, ERR_STR "Unable to accept connection: %s" CLR_STR "\n", strerror(errno)); continue; } diff --git a/src/necronda-server.h b/src/necronda-server.h index 81b682e..34fead1 100644 --- a/src/necronda-server.h +++ b/src/necronda-server.h @@ -41,6 +41,7 @@ #define ERR_STR "\x1B[1;31m" #define CLR_STR "\x1B[0m" #define BLD_STR "\x1B[1m" +#define WRN_STR "\x1B[1;33m" #define HTTP_STR "\x1B[1;31m" #define HTTPS_STR "\x1B[1;32m" @@ -56,6 +57,7 @@ #define NECRONDA_ZLIB_LEVEL 9 #define MAGIC_FILE "/usr/share/file/misc/magic.mgc" +#define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock" int sockets[NUM_SOCKETS]; pid_t children[MAX_CHILDREN];