summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2020-10-25 23:16:50 -0400
committerDrew DeVault <sir@cmpwn.com>2020-10-25 23:16:50 -0400
commitcc1bd152e30e14827d2a002fef99384f418c22ab (patch)
tree9362291ad4d52508bf605dc51e698e5aa79e1f99 /src
parent8baeb5a51c4dfa03956887ade2ef77295f17c95e (diff)
downloadgmnisrv-cc1bd152e30e14827d2a002fef99384f418c22ab.tar.gz
gmnisrv-cc1bd152e30e14827d2a002fef99384f418c22ab.tar.xz
gmnisrv-cc1bd152e30e14827d2a002fef99384f418c22ab.zip
Initial support for CGI scripts
Diffstat (limited to 'src')
-rw-r--r--src/config.c1
-rw-r--r--src/serve.c83
2 files changed, 84 insertions, 0 deletions
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 <arpa/inet.h>
#include <assert.h>
#include <dirent.h>
#include <errno.h>
@@ -6,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#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) {