From cc1bd152e30e14827d2a002fef99384f418c22ab Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 25 Oct 2020 23:16:50 -0400 Subject: Initial support for CGI scripts --- src/config.c | 1 + src/serve.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) (limited to 'src') diff --git a/src/config.c b/src/config.c index 693766e..dc2a07e 100644 --- a/src/config.c +++ b/src/config.c @@ -210,6 +210,7 @@ conf_ini_handler(void *user, const char *section, bool *value; } route_bvars[] = { { "autoindex", &route->autoindex }, + { "cgi", &route->cgi }, }; for (size_t i = 0; i < sizeof(route_strvars) / sizeof(route_strvars[0]); ++i) { diff --git a/src/serve.c b/src/serve.c index 18c2993..de38c9f 100644 --- a/src/serve.c +++ b/src/serve.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,6 +7,7 @@ #include #include #include +#include #include #include "config.h" #include "gemini.h" @@ -117,6 +119,82 @@ internal_error: goto exit; } +static void +serve_cgi(struct gmnisrv_client *client, const char *path) +{ + int pfd[2]; + if (pipe(pfd) == -1) { + server_error("pipe: %s", strerror(errno)); + client_submit_response(client, GEMINI_STATUS_PERMANENT_FAILURE, + "Internal server error", NULL); + return; + } + + pid_t pid = fork(); + if (pid == -1) { + server_error("fork: %s", strerror(errno)); + client_submit_response(client, GEMINI_STATUS_PERMANENT_FAILURE, + "Internal server error", NULL); + close(pfd[0]); + close(pfd[1]); + return; + } else if (pid == 0) { + close(pfd[0]); + dup2(pfd[1], STDOUT_FILENO); + close(pfd[1]); + + // I don't feel like freeing this stuff and this process is + // going to die soon anyway so let's just be hip and call it an + // arena allocator :^) + struct Curl_URL *url = curl_url(); + assert(url); + CURLUcode uc = curl_url_set(url, CURLUPART_URL, client->buf, 0); + assert(uc == CURLUE_OK); + + char *query; + uc = curl_url_get(url, CURLUPART_QUERY, &query, CURLU_URLDECODE); + if (uc != CURLUE_OK) { + assert(uc == CURLUE_NO_QUERY); + } else { + setenv("QUERY_STRING", query, 1); + } + + char abuf[INET6_ADDRSTRLEN + 1]; + const char *addrs = inet_ntop(client->addr.sa_family, + client->addr.sa_data, abuf, sizeof(abuf)); + assert(addrs); + + // Compatible with Jetforce + setenv("GATEWAY_INTERFACE", "GCI/1.1", 1); + setenv("SERVER_PROTOCOL", "GEMINI", 1); + setenv("SERVER_SOFTWARE", "gmnisrv/0.0.0", 1); + setenv("GEMINI_URL", client->buf, 1); + setenv("SCRIPT_NAME", path, 1); + //setenv("PATH_INFO", "", 1); // TODO + setenv("SERVER_NAME", client->host->hostname, 1); + setenv("HOSTNAME", client->host->hostname, 1); + //setenv("SERVER_PORT", "", 1); // TODO + setenv("REMOTE_HOST", addrs, 1); + setenv("REMOTE_ADDR", addrs, 1); + + const SSL_CIPHER *cipher = SSL_get_current_cipher(client->ssl); + setenv("TLS_CIPHER", SSL_CIPHER_get_name(cipher), 1); + setenv("TLS_VERSION", SSL_CIPHER_get_version(cipher), 1); + + // TODO: Client certificate details + + execlp(path, path, NULL); + server_error("execlp: %s", strerror(errno)); + _exit(1); + } else { + close(pfd[1]); + FILE *f = fdopen(pfd[0], "r"); + client_submit_response(client, GEMINI_STATUS_SUCCESS, "(cgi)", f); + client->state = CLIENT_STATE_BODY; // The CGI script sends meta + client->bufix = client->bufln = 0; + } +} + static bool route_match(struct gmnisrv_route *route, const char *path, const char **revised) { @@ -219,6 +297,11 @@ serve_request(struct gmnisrv_client *client) } } + if (route->cgi) { + serve_cgi(client, path); + return; + } + FILE *body = fopen(path, "r"); if (!body) { if (errno == ENOENT) { -- cgit v1.2.3