aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2021-02-01 11:11:43 +0000
committerOmar Polo <op@omarpolo.com>2021-02-01 11:11:43 +0000
commit2fafa2d23e5607def335902b7a9d10a9de5247a9 (patch)
treebc8f8e67a3a372511a30cc58a6fc2f515c366fbb
parente17642a7bb0f182c3c6a26c27681d49ca9dce8dc (diff)
bring the CGI implementation in par with GLV-1.12556
-rw-r--r--ex.c153
-rw-r--r--gmid.h9
-rw-r--r--iri.c28
-rw-r--r--server.c20
4 files changed, 162 insertions, 48 deletions
diff --git a/ex.c b/ex.c
index 04caedc..2e8e765 100644
--- a/ex.c
+++ b/ex.c
@@ -18,7 +18,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
#include <signal.h>
+#include <stdarg.h>
#include <string.h>
#include "gmid.h"
@@ -65,6 +68,41 @@ recv_string(int fd, char **ret)
}
int
+send_iri(int fd, struct iri *i)
+{
+ return send_string(fd, i->schema)
+ && send_string(fd, i->host)
+ && send_string(fd, i->port)
+ && send_string(fd, i->path)
+ && send_string(fd, i->query);
+}
+
+int
+recv_iri(int fd, struct iri *i)
+{
+ memset(i, 0, sizeof(*i));
+
+ if (!recv_string(fd, &i->schema)
+ || !recv_string(fd, &i->host)
+ || !recv_string(fd, &i->port)
+ || !recv_string(fd, &i->path)
+ || !recv_string(fd, &i->query))
+ return 0;
+
+ return 1;
+}
+
+void
+free_recvd_iri(struct iri *i)
+{
+ free(i->schema);
+ free(i->host);
+ free(i->port);
+ free(i->path);
+ free(i->query);
+}
+
+int
send_vhost(int fd, struct vhost *vhost)
{
ssize_t n;
@@ -178,11 +216,25 @@ safe_setenv(const char *name, const char *val)
setenv(name, val, 1);
}
+static char *
+xasprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+
+ va_start(ap, fmt);
+ if (vasprintf(&s, fmt, ap) == -1)
+ s = NULL;
+ va_end(ap);
+
+ return s;
+}
+
/* fd or -1 on error */
static int
-launch_cgi(const char *spath, const char *relpath, const char *query,
- const char *addr, const char *ruser, const char *cissuer, const char *chash,
- struct vhost *vhost)
+launch_cgi(struct iri *iri, const char *spath, char *relpath,
+ const char *addr, const char *ruser, const char *cissuer,
+ const char *chash, struct vhost *vhost)
{
int p[2]; /* read end, write end */
@@ -194,42 +246,58 @@ launch_cgi(const char *spath, const char *relpath, const char *query,
return -1;
case 0: { /* child */
- char *portno, *ex, *requri;
- char *argv[] = { NULL, NULL, NULL };
+ char *argv[] = {NULL, NULL, NULL};
+ char *ex, *pwd;
+ char iribuf[GEMINI_URL_LEN];
+ char path[PATH_MAX];
close(p[0]);
if (dup2(p[1], 1) == -1)
goto childerr;
- if (asprintf(&portno, "%d", conf.port) == -1)
- goto childerr;
+ ex = xasprintf("%s/%s", vhost->dir, spath);
+ argv[0] = ex;
+ argv[1] = iri->query;
- if (asprintf(&ex, "%s/%s", vhost->dir, spath) == -1)
- goto childerr;
+ serialize_iri(iri, iribuf, sizeof(iribuf));
- if (asprintf(&requri, "%s%s%s", spath,
- (relpath != NULL && *relpath == '\0') ? "" : "/",
- (relpath != NULL ? relpath : "")) == -1)
- goto childerr;
+ safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
+ safe_setenv("GEMINI_DOCUMENT_ROOT", vhost->dir);
+ safe_setenv("GEMINI_SCRIPT_FILENAME",
+ xasprintf("%s/%s", vhost->dir, spath));
+ safe_setenv("GEMINI_URL", iribuf);
+
+ strlcpy(path, "/", sizeof(path));
+ strlcat(path, spath, sizeof(path));
+ safe_setenv("GEMINI_URL_PATH", path);
+
+ if (relpath != NULL) {
+ strlcpy(path, "/", sizeof(path));
+ strlcat(path, relpath, sizeof(path));
+ safe_setenv("PATH_INFO", path);
+
+ strlcpy(path, vhost->dir, sizeof(path));
+ strlcat(path, "/", sizeof(path));
+ strlcat(path, relpath, sizeof(path));
+ safe_setenv("PATH_TRANSLATED", path);
+ }
- argv[0] = argv[1] = ex;
+ safe_setenv("QUERY_STRING", iri->query);
+ safe_setenv("REMOTE_ADDR", addr);
+ safe_setenv("REMOTE_HOST", addr);
+ safe_setenv("REQUEST_METHOD", "");
- safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
- safe_setenv("SERVER_PROTOCOL", "GEMINI");
- safe_setenv("SERVER_SOFTWARE", "gmid");
- safe_setenv("SERVER_PORT", portno);
+ strlcpy(path, "/", sizeof(path));
+ strlcat(path, spath, sizeof(path));
+ safe_setenv("SCRIPT_NAME", path);
- if (!strcmp(vhost->domain, "*"))
- safe_setenv("SERVER_NAME", vhost->domain);
+ safe_setenv("SERVER_NAME", iri->host);
- safe_setenv("SCRIPT_NAME", spath);
- safe_setenv("SCRIPT_EXECUTABLE", ex);
- safe_setenv("REQUEST_URI", requri);
- safe_setenv("REQUEST_RELATIVE", relpath);
- safe_setenv("QUERY_STRING", query);
- safe_setenv("REMOTE_HOST", addr);
- safe_setenv("REMOTE_ADDR", addr);
- safe_setenv("DOCUMENT_ROOT", vhost->dir);
+ snprintf(path, sizeof(path), "%d", conf.port);
+ safe_setenv("SERVER_PORT", path);
+
+ safe_setenv("SERVER_PROTOCOL", "GEMINI");
+ safe_setenv("SERVER_SOFTWARE", "gmid/1.5");
if (ruser != NULL) {
safe_setenv("AUTH_TYPE", "Certificate");
@@ -238,9 +306,15 @@ launch_cgi(const char *spath, const char *relpath, const char *query,
safe_setenv("TLS_CLIENT_HASH", chash);
}
- fchdir(vhost->dirfd);
+ strlcpy(path, argv[0], sizeof(path));
+ pwd = dirname(path);
+ if (chdir(pwd)) {
+ warn("chdir");
+ goto childerr;
+ }
- execvp(ex, argv);
+ execvp(argv[0], argv);
+ warn("execvp: %s", argv[0]);
goto childerr;
}
@@ -257,25 +331,28 @@ childerr:
int
executor_main(int fd)
{
- char *spath, *relpath, *query, *addr, *ruser, *cissuer, *chash;
+ char *spath, *relpath, *addr, *ruser, *cissuer, *chash;
struct vhost *vhost;
+ struct iri iri;
int d;
#ifdef __OpenBSD__
for (vhost = hosts; vhost->domain != NULL; ++vhost) {
- if (unveil(vhost->dir, "x") == -1)
+ /* r so we can chdir into the correct directory */
+ if (unveil(vhost->dir, "rx") == -1)
err(1, "unveil %s for domain %s",
vhost->dir, vhost->domain);
}
- if (pledge("stdio sendfd proc exec", NULL))
+ /* rpath to chdir into the correct directory */
+ if (pledge("stdio rpath sendfd proc exec", NULL))
err(1, "pledge");
#endif
for (;;) {
- if (!recv_string(fd, &spath)
+ if (!recv_iri(fd, &iri)
+ || !recv_string(fd, &spath)
|| !recv_string(fd, &relpath)
- || !recv_string(fd, &query)
|| !recv_string(fd, &addr)
|| !recv_string(fd, &ruser)
|| !recv_string(fd, &cissuer)
@@ -283,15 +360,15 @@ executor_main(int fd)
|| !recv_vhost(fd, &vhost))
break;
- d = launch_cgi(spath, relpath, query,
- addr, ruser, cissuer, chash, vhost);
+ d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash,
+ vhost);
if (!send_fd(fd, d))
break;
close(d);
+ free_recvd_iri(&iri);
free(spath);
free(relpath);
- free(query);
free(addr);
free(ruser);
free(cissuer);
diff --git a/gmid.h b/gmid.h
index fc9ec53..5d4becf 100644
--- a/gmid.h
+++ b/gmid.h
@@ -204,12 +204,12 @@ int vhost_auto_index(struct vhost*, const char*);
int check_path(struct client*, const char*, int*);
void open_file(struct pollfd*, struct client*);
void load_file(struct pollfd*, struct client*);
-void check_for_cgi(char *, char*, struct pollfd*, struct client*);
+void check_for_cgi(struct pollfd*, struct client*);
void mark_nonblock(int);
void handle_handshake(struct pollfd*, struct client*);
void handle_open_conn(struct pollfd*, struct client*);
void start_reply(struct pollfd*, struct client*, int, const char*);
-void start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*);
+void start_cgi(const char*, const char*, struct pollfd*, struct client*);
void send_file(struct pollfd*, struct client*);
void open_dir(struct pollfd*, struct client*);
void redirect_canonical_dir(struct pollfd*, struct client*);
@@ -226,6 +226,9 @@ void loop(struct tls*, int, int);
/* ex.c */
int send_string(int, const char*);
int recv_string(int, char**);
+int send_iri(int, struct iri*);
+int recv_iri(int, struct iri*);
+void free_recvd_iri(struct iri*);
int send_vhost(int, struct vhost*);
int recv_vhost(int, struct vhost**);
int send_fd(int, int);
@@ -242,6 +245,7 @@ char *utf8_nth(char*, size_t);
/* iri.c */
int parse_iri(char*, struct iri*, const char**);
int trim_req_iri(char*, const char **);
+int serialize_iri(struct iri*, char*, size_t);
/* puny.c */
int puny_decode(const char*, char*, size_t, const char**);
@@ -250,5 +254,6 @@ int puny_decode(const char*, char*, size_t, const char**);
int starts_with(const char*, const char*);
int ends_with(const char*, const char*);
ssize_t filesize(int);
+char *absolutify_path(const char*);
#endif
diff --git a/iri.c b/iri.c
index 3336b6e..efdeb11 100644
--- a/iri.c
+++ b/iri.c
@@ -377,3 +377,31 @@ trim_req_iri(char *iri, const char **err)
*i = '\0';
return 1;
}
+
+
+int
+serialize_iri(struct iri *i, char *buf, size_t len)
+{
+ size_t l;
+
+ /* in ex.c we receive empty "" strings as NULL */
+ if (i->schema == NULL || i->host == NULL) {
+ memset(buf, 0, len);
+ return 0;
+ }
+
+ strlcpy(buf, i->schema, len);
+ strlcat(buf, "://", len);
+ strlcat(buf, i->host, len);
+ strlcat(buf, "/", len);
+
+ if (i->path != NULL)
+ l = strlcat(buf, i->path, len);
+
+ if (i->query != NULL && *i->query != '\0') {
+ strlcat(buf, "?", len);
+ l = strlcat(buf, i->query, len);
+ }
+
+ return l < len;
+}
diff --git a/server.c b/server.c
index 0df7e6d..30f413c 100644
--- a/server.c
+++ b/server.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <limits.h>
#include <string.h>
#include "gmid.h"
@@ -150,7 +151,7 @@ open_file(struct pollfd *fds, struct client *c)
switch (check_path(c, c->iri.path, &c->fd)) {
case FILE_EXECUTABLE:
if (starts_with(c->iri.path, c->host->cgi)) {
- start_cgi(c->iri.path, "", c->iri.query, fds, c);
+ start_cgi(c->iri.path, "", fds, c);
return;
}
@@ -166,7 +167,7 @@ open_file(struct pollfd *fds, struct client *c)
case FILE_MISSING:
if (c->host->cgi != NULL && starts_with(c->iri.path, c->host->cgi)) {
- check_for_cgi(c->iri.path, c->iri.query, fds, c);
+ check_for_cgi(fds, c);
return;
}
start_reply(fds, c, NOT_FOUND, "not found");
@@ -206,9 +207,12 @@ load_file(struct pollfd *fds, struct client *c)
* executable is found or we emptied the path.
*/
void
-check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c)
+check_for_cgi(struct pollfd *fds, struct client *c)
{
+ char path[PATH_MAX];
char *end;
+
+ strlcpy(path, c->iri.path, sizeof(path));
end = strchr(path, '\0');
/* NB: assume CGI is enabled and path matches cgi */
@@ -223,7 +227,7 @@ check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c)
switch (check_path(c, path, &c->fd)) {
case FILE_EXECUTABLE:
- start_cgi(path, end+1, query, fds,c);
+ start_cgi(path, end+1, fds, c);
return;
case FILE_MISSING:
break;
@@ -396,7 +400,7 @@ start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
}
void
-start_cgi(const char *spath, const char *relpath, const char *query,
+start_cgi(const char *spath, const char *relpath,
struct pollfd *fds, struct client *c)
{
char addr[NI_MAXHOST];
@@ -420,9 +424,9 @@ start_cgi(const char *spath, const char *relpath, const char *query,
chash = NULL;
}
- if (!send_string(exfd, spath)
+ if (!send_iri(exfd, &c->iri)
+ || !send_string(exfd, spath)
|| !send_string(exfd, relpath)
- || !send_string(exfd, query)
|| !send_string(exfd, addr)
|| !send_string(exfd, ruser)
|| !send_string(exfd, cissuer)
@@ -515,7 +519,7 @@ open_dir(struct pollfd *fds, struct client *c)
switch (check_path(c, c->iri.path, &c->fd)) {
case FILE_EXECUTABLE:
if (starts_with(c->iri.path, c->host->cgi)) {
- start_cgi(c->iri.path, "", c->iri.query, fds, c);
+ start_cgi(c->iri.path, "", fds, c);
break;
}