Use sock in fastcgi

This commit is contained in:
2023-01-13 02:26:24 +01:00
parent add135b3ad
commit e8655102b9
9 changed files with 128 additions and 163 deletions

View File

@ -15,7 +15,6 @@
#include <sys/un.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
// TODO use pipes for stdin, stdout, stderr in FastCGI
@ -28,20 +27,14 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) {
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);
*((int *) ptr) = htonl(0x80000000 | key_len);
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);
*((int *) ptr) = htonl(0x80000000 | val_len);
ptr += 4;
}
@ -53,6 +46,33 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) {
return ptr;
}
int fastcgi_send_data(fastcgi_cnx_t *cnx, unsigned char type, unsigned short len, void *data) {
// build header
FCGI_Header header = {
.version = FCGI_VERSION_1,
.type = type,
.requestId = htons(cnx->req_id),
.contentLength = htons(len),
.paddingLength = 0,
.reserved = 0,
};
// send FastCGI header with MSG_MORE flag
if (sock_send_x(&cnx->socket, &header, sizeof(header), (len != 0) ? MSG_MORE : 0) == -1) {
error("Unable to send to FastCGI socket");
return -1;
}
// send data (if available)
if (sock_send_x(&cnx->socket, data, len, 0) == -1) {
error("Unable to send to FastCGI socket");
return -1;
}
// return bytes sent totally
return len + (int) sizeof(header);
}
int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) {
conn->mode = mode;
conn->req_id = (req_num + 1) & 0xFFFF;
@ -60,45 +80,32 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock
conn->out_off = 0;
conn->webroot = uri->webroot;
int fcgi_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (fcgi_sock < 0) {
conn->socket.enc = 0;
if ((conn->socket.socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
error("Unable to create unix socket");
return -1;
}
conn->socket = fcgi_sock;
struct sockaddr_un sock_addr = {AF_UNIX};
struct sockaddr_un sock_addr = { AF_UNIX };
if (conn->mode == FASTCGI_BACKEND_PHP) {
snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", PHP_FPM_SOCKET);
strcpy(sock_addr.sun_path, PHP_FPM_SOCKET);
}
if (connect(conn->socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) {
if (connect(conn->socket.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) != 0) {
error("Unable to connect to unix socket of FastCGI socket");
return -1;
}
FCGI_Header header = {
.version = FCGI_VERSION_1,
.requestId = htons(conn->req_id),
.paddingLength = 0,
.reserved = 0
FCGI_BeginRequestBody begin = {
.role = htons(FCGI_RESPONDER),
.flags = 0,
.reserved = {0},
};
header.type = FCGI_BEGIN_REQUEST;
header.contentLength = htons(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)) {
error("Unable to send to FastCGI socket");
return -2;
}
char param_buf[4096];
char buf0[256];
char *param_ptr = param_buf + sizeof(header);
if (fastcgi_send_data(conn, FCGI_BEGIN_REQUEST, sizeof(begin), &begin) == -1)
return -1;
char param_buf[4096], buf0[256], *param_ptr = param_buf;
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");
@ -170,39 +177,18 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock
param_ptr = fastcgi_add_param(param_ptr, buf0, http_field_get_value(f));
}
unsigned short param_len = param_ptr - param_buf - sizeof(header);
header.type = FCGI_PARAMS;
header.contentLength = htons(param_len);
memcpy(param_buf, &header, sizeof(header));
if (send(conn->socket, param_buf, param_len + sizeof(header), 0) != param_len + sizeof(header)) {
error("Unable to send to FastCGI socket");
return -2;
}
if (fastcgi_send_data(conn, FCGI_PARAMS, param_ptr - param_buf, param_buf) == -1)
return -1;
header.type = FCGI_PARAMS;
header.contentLength = htons(0);
if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) {
error("Unable to send to FastCGI socket");
return -2;
}
if (fastcgi_send_data(conn, FCGI_PARAMS, 0, NULL) == -1)
return -1;
return 0;
}
int fastcgi_close_stdin(fastcgi_cnx_t *conn) {
FCGI_Header header = {
.version = FCGI_VERSION_1,
.type = FCGI_STDIN,
.requestId = htons(conn->req_id),
.contentLength = htons(0),
.paddingLength = 0,
.reserved = 0
};
if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) {
error("Unable to send to FastCGI socket");
return -2;
}
if (fastcgi_send_data(conn, FCGI_STDIN, 0, NULL) == -1)
return -1;
return 0;
}
@ -282,8 +268,7 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
int err = 0;
while (1) {
ret = recv(conn->socket, &header, sizeof(header), 0);
if (ret != sizeof(header)) {
if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) {
res->status = http_get_status(500);
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
error("Unable to receive from FastCGI socket");
@ -292,8 +277,7 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
req_id = ntohs(header.requestId);
content_len = ntohs(header.contentLength);
content = malloc(content_len + header.paddingLength);
ret = recv(conn->socket, content, content_len + header.paddingLength, 0);
if (ret != (content_len + header.paddingLength)) {
if (sock_recv_x(&conn->socket, content, content_len + header.paddingLength, 0) == -1) {
res->status = http_get_status(500);
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
error("Unable to receive from FastCGI socket");
@ -307,16 +291,14 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
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;
int app_status = ntohl(body->appStatus);
if (body->protocolStatus != FCGI_REQUEST_COMPLETE) {
error("FastCGI protocol error: %i", body->protocolStatus);
}
if (app_status != 0) {
error("FastCGI app terminated with exit code %i", app_status);
}
close(conn->socket);
conn->socket = 0;
sock_close(&conn->socket);
free(content);
return 1;
} else if (header.type == FCGI_STDERR) {
@ -379,7 +361,7 @@ int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) {
long ret;
char buf0[256], *content, *ptr;
int len;
unsigned short req_id, content_len;
unsigned short content_len;
if (conn->out_buf != NULL && conn->out_len > conn->out_off) {
content = conn->out_buf;
@ -389,23 +371,18 @@ int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) {
}
while (1) {
ret = recv(conn->socket, &header, sizeof(header), 0);
if (ret < 0) {
if ((sock_recv_x(&conn->socket, &header, sizeof(header), 0)) == -1) {
error("Unable to receive from FastCGI socket");
return -1;
} else if (ret != sizeof(header)) {
error("Unable to receive from FastCGI socket: received len (%li) != header len (%li)", ret, sizeof(header));
return -1;
}
req_id = ntohs(header.requestId);
content_len = ntohs(header.contentLength);
content = malloc(content_len + header.paddingLength);
ptr = content;
long rcv_len = 0;
while (rcv_len < content_len + header.paddingLength) {
ret = recv(conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0);
ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0);
if (ret < 0) {
error("Unable to receive from FastCGI socket");
free(content);
@ -416,16 +393,14 @@ int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) {
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;
int app_status = ntohl(body->appStatus);
if (body->protocolStatus != FCGI_REQUEST_COMPLETE) {
error("FastCGI protocol error: %i", body->protocolStatus);
}
if (app_status != 0) {
error("FastCGI app terminated with exit code %i", app_status);
}
close(conn->socket);
conn->socket = 0;
sock_close(&conn->socket);
free(content);
if (flags & FASTCGI_CHUNKED) {
@ -457,29 +432,24 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) {
long ret;
char buf0[256];
char *content, *ptr = buf;
unsigned short req_id, content_len;
unsigned short content_len;
if (conn->out_buf != NULL && conn->out_len > conn->out_off) {
ptr += snprintf(ptr, len, "%.*s", conn->out_len - conn->out_off, conn->out_buf + conn->out_off);
}
while (1) {
ret = recv(conn->socket, &header, sizeof(header), 0);
if (ret < 0) {
if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) {
error("Unable to receive from FastCGI socket");
return -1;
} else if (ret != sizeof(header)) {
error("Unable to receive from FastCGI socket: received len (%li) != header len (%li)", ret, sizeof(header));
return -1;
}
req_id = ntohs(header.requestId);
content_len = ntohs(header.contentLength);
content = malloc(content_len + header.paddingLength);
long rcv_len = 0;
while (rcv_len < content_len + header.paddingLength) {
ret = recv(conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0);
ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0);
if (ret < 0) {
error("Unable to receive from FastCGI socket");
free(content);
@ -490,16 +460,14 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) {
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;
int app_status = ntohl(body->appStatus);
if (body->protocolStatus != FCGI_REQUEST_COMPLETE) {
error("FastCGI protocol error: %i", body->protocolStatus);
}
if (app_status != 0) {
error("FastCGI app terminated with exit code %i", app_status);
}
close(conn->socket);
conn->socket = 0;
sock_close(&conn->socket);
free(content);
return 0;
@ -517,50 +485,31 @@ int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) {
}
int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) {
unsigned long rcv_len = 0;
char *buf[16384];
long ret;
FCGI_Header header = {
.version = FCGI_VERSION_1,
.type = FCGI_STDIN,
.requestId = htons(conn->req_id),
.contentLength = htons(0),
.paddingLength = 0,
.reserved = 0
};
while (rcv_len < len) {
ret = sock_recv(client, buf, sizeof(buf), 0);
if (ret <= 0) {
for (long to_send = (long) len, ret; to_send > 0; to_send -= ret) {
if ((ret = sock_recv(client, buf, (to_send > sizeof(buf)) ? sizeof(buf) : to_send, 0)) <= 0) {
error("Unable to receive");
return -1;
}
rcv_len += ret;
header.contentLength = htons(ret);
if (send(conn->socket, &header, sizeof(header), 0) != sizeof(header)) goto err;
if (send(conn->socket, buf, ret, 0) != ret) {
err:
error("Unable to send to FastCGI socket");
return -2;
}
if (fastcgi_send_data(conn, FCGI_STDIN, ret, buf) == -1)
return -1;
}
return 0;
}
int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) {
long ret;
unsigned long next_len;
for (long ret;;) {
if ((ret = sock_get_chunk_header(client)) < 0) {
return (int) ret;
} else if (ret == 0) {
break;
}
while (1) {
ret = sock_get_chunk_header(client);
if (ret < 0) return (int) ret;
next_len = ret;
if (next_len <= 0) break;
ret = fastcgi_receive(conn, client, next_len);
if (ret < 0) return (int) ret;
if ((ret = fastcgi_receive(conn, client, ret)) < 0)
return (int) ret;
}
return 0;

View File

@ -23,7 +23,8 @@
typedef struct {
int mode;
int socket;
sock socket;
int in, out, err;
unsigned short req_id;
const char *webroot;
char *out_buf;

View File

@ -251,8 +251,7 @@ int http_receive_request(sock *client, http_req *req) {
if (header_len < 0)
return (int) -header_len;
rcv_len = sock_recv(client, buf, header_len, 0);
if (rcv_len != header_len)
if (sock_recv_x(client, buf, header_len, 0) == -1)
return -1;
return 0;

View File

@ -54,8 +54,7 @@ typedef struct {
#define FCGI_NULL_REQUEST_ID 0
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned short role;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
@ -78,10 +77,7 @@ typedef struct {
#define FCGI_FILTER 3
typedef struct {
unsigned char appStatusB3;
unsigned char appStatusB2;
unsigned char appStatusB1;
unsigned char appStatusB0;
unsigned int appStatus;
unsigned char protocolStatus;
unsigned char reserved[3];
} FCGI_EndRequestBody;

View File

@ -549,7 +549,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
}
ptr = pos0 + 2;
}
sock_recv(&proxy->proxy, buffer, header_len, 0);
sock_recv_x(&proxy->proxy, buffer, header_len, 0);
ret = proxy_response_header(req, res, conf);
if (ret != 0) {
@ -654,7 +654,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
if (finish_comp) goto finish;
}
if (ret <= 0) break;
if (flags & PROXY_CHUNKED) sock_recv(&proxy->proxy, buffer, 2, 0);
if (flags & PROXY_CHUNKED) sock_recv_x(&proxy->proxy, buffer, 2, 0);
} while ((flags & PROXY_CHUNKED) && len_to_send > 0);
if (ret <= 0) return -1;

View File

@ -107,13 +107,17 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) {
}
long sock_send_x(sock *s, void *buf, unsigned long len, int flags) {
long sent = 0;
for (long ret; sent < len; sent += ret) {
ret = sock_send(s, (unsigned char *) buf + sent, len - sent, flags);
if (ret <= 0)
return ret;
for (long ret, sent = 0; sent < len; sent += ret) {
if ((ret = sock_send(s, (unsigned char *) buf + sent, len - sent, flags)) <= 0) {
if (errno == EINTR) {
errno = 0, ret = 0;
continue;
} else {
return -1;
}
}
}
return sent;
return (long) len;
}
long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
@ -134,6 +138,20 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
}
}
long sock_recv_x(sock *s, void *buf, unsigned long len, int flags) {
for (long ret, rcv = 0; rcv < len; rcv += ret) {
if ((ret = sock_recv(s, (unsigned char *) buf + rcv, len - rcv, flags)) <= 0) {
if (errno == EINTR) {
errno = 0, ret = 0;
continue;
} else {
return -1;
}
}
}
return (long) len;
}
long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) {
long ret;
unsigned long send_len = 0;
@ -213,16 +231,24 @@ long sock_get_chunk_header(sock *s) {
char buf[16];
do {
ret = sock_recv(s, buf, sizeof(buf), MSG_PEEK);
if (ret <= 0) return -2;
else if (ret < 2) continue;
if ((ret = sock_recv(s, buf, sizeof(buf) - 1, MSG_PEEK)) <= 0) {
if (errno == EINTR) {
errno = 0;
continue;
} else {
return -1;
}
} else if (ret < 2) {
continue;
}
buf[ret] = 0;
ret = sock_parse_chunk_header(buf, ret, &len);
if (ret == -2) return -1;
if ((ret = sock_parse_chunk_header(buf, ret, &len)) == -2)
return -1;
} while (ret < 0);
if (sock_recv(s, buf, len, 0) != len)
return -2;
if (sock_recv_x(s, buf, len, 0) == -1)
return -1;
return ret;
}

View File

@ -44,6 +44,8 @@ long sock_send_x(sock *s, void *buf, unsigned long len, int flags);
long sock_recv(sock *s, void *buf, unsigned long len, int flags);
long sock_recv_x(sock *s, void *buf, unsigned long len, int flags);
long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len);
long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len);

View File

@ -36,13 +36,9 @@ int ws_calc_accept_key(const char *key, char *accept_key) {
int ws_recv_frame_header(sock *s, ws_frame *frame) {
unsigned char buf[12];
long ret = sock_recv(s, buf, 2, 0);
if (ret < 0) {
if (sock_recv_x(s, buf, 2, 0) == -1) {
error("Unable to receive from socket");
return -1;
} else if (ret != 2) {
error("Unable to receive 2 bytes from socket");
return -2;
}
unsigned short bits = (buf[0] << 8) | buf[1];
@ -61,13 +57,9 @@ int ws_recv_frame_header(sock *s, ws_frame *frame) {
remaining += 8;
}
ret = sock_recv(s, buf, remaining, 0);
if (ret < 0) {
if (sock_recv_x(s, buf, remaining, 0) == -1) {
error("Unable to receive from socket");
return -1;
} else if (ret != remaining) {
error("Unable to receive correct number of bytes from socket");
return -2;
}
if (len == 126) {

View File

@ -38,7 +38,8 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) {
sock *client = &ctx->socket;
char *err_msg = ctx->err_msg;
fcgi_cnx->socket = 0;
fcgi_cnx->socket.socket = 0;
fcgi_cnx->socket.enc = 0;
fcgi_cnx->req_id = 0;
fcgi_cnx->r_addr = ctx->socket.addr;
fcgi_cnx->r_host = (ctx->host[0] != 0) ? ctx->host : NULL;
@ -144,9 +145,8 @@ static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) {
errno = 0;
}
if (fcgi_cnx->socket != 0) {
close(fcgi_cnx->socket);
fcgi_cnx->socket = 0;
if (fcgi_cnx->socket.socket != 0) {
sock_close(&fcgi_cnx->socket);
}
return ret;