aboutsummaryrefslogtreecommitdiff
path: root/gmid.c
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2021-01-17 23:23:58 +0000
committerOmar Polo <op@omarpolo.com>2021-01-17 23:23:58 +0000
commitd3a08f4d172687d4e3e60e7faaa8830bb7f9f9db (patch)
tree1bcfcd4b2bec16247127914b983e22d5547026f8 /gmid.c
parent5f564d23e9ea96fd6565ee4099fba6c31b71567e (diff)
reorganize: move bunch of functions to server.c
cgi.c wasn't really needed; it better to group all the server related functions together, cgi or not. Now gmid.c contains only startup and utility code.
Diffstat (limited to 'gmid.c')
-rw-r--r--gmid.c522
1 files changed, 43 insertions, 479 deletions
diff --git a/gmid.c b/gmid.c
index bb1254b..dad0d29 100644
--- a/gmid.c
+++ b/gmid.c
@@ -14,12 +14,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include <assert.h>
#include <err.h>
#include <errno.h>
+
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
@@ -31,7 +28,6 @@
struct vhost hosts[HOSTSLEN];
-int connected_clients;
int goterror;
int exfd;
@@ -60,9 +56,7 @@ struct etm { /* file extension to mime */
{NULL, NULL}
};
-__attribute__ ((format (printf, 1, 2)))
-__attribute__ ((__noreturn__))
-static inline void
+void
fatal(const char *fmt, ...)
{
va_list ap;
@@ -138,31 +132,6 @@ starts_with(const char *str, const char *prefix)
return 1;
}
-int
-start_reply(struct pollfd *pfd, struct client *client, int code, const char *reason)
-{
- char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */
- int len;
-
- client->code = code;
- client->meta = reason;
- client->state = S_INITIALIZING;
-
- len = snprintf(buf, sizeof(buf), "%d %s\r\n", code, reason);
- assert(len < (int)sizeof(buf));
-
- switch (tls_write(client->ctx, buf, len)) {
- case TLS_WANT_POLLIN:
- pfd->events = POLLIN;
- return 0;
- case TLS_WANT_POLLOUT:
- pfd->events = POLLOUT;
- return 0;
- default:
- return 1;
- }
-}
-
ssize_t
filesize(int fd)
{
@@ -207,286 +176,74 @@ mime(const char *path)
return def;
}
-int
-check_path(struct client *c, const char *path, int *fd)
+char *
+absolutify_path(const char *path)
{
- struct stat sb;
-
- assert(path != NULL);
- if ((*fd = openat(c->host->dirfd, *path ? path : ".",
- O_RDONLY | O_NOFOLLOW | O_CLOEXEC)) == -1) {
- return FILE_MISSING;
- }
-
- if (fstat(*fd, &sb) == -1) {
- LOGN(c, "failed stat for %s: %s", path, strerror(errno));
- return FILE_MISSING;
- }
-
- if (S_ISDIR(sb.st_mode))
- return FILE_DIRECTORY;
+ char *wd, *r;
- if (sb.st_mode & S_IXUSR)
- return FILE_EXECUTABLE;
+ if (*path == '/')
+ return strdup(path);
- return FILE_EXISTS;
+ wd = getcwd(NULL, 0);
+ if (asprintf(&r, "%s/%s", wd, path) == -1)
+ err(1, "asprintf");
+ free(wd);
+ return r;
}
-int
-open_file(char *fpath, char *query, struct pollfd *fds, struct client *c)
+void
+yyerror(const char *msg)
{
- switch (check_path(c, fpath, &c->fd)) {
- case FILE_EXECUTABLE:
- if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi))
- return start_cgi(fpath, "", query, fds, c);
-
- /* fallthrough */
-
- case FILE_EXISTS:
- if ((c->len = filesize(c->fd)) == -1) {
- LOGE(c, "failed to get file size for %s", fpath);
- goodbye(fds, c);
- return 0;
- }
-
- if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE,
- c->fd, 0)) == MAP_FAILED) {
- warn("mmap: %s", fpath);
- goodbye(fds, c);
- return 0;
- }
- c->i = c->buf;
- return start_reply(fds, c, SUCCESS, mime(fpath));
-
- case FILE_DIRECTORY:
- LOGD(c, "%s is a directory, trying %s/index.gmi", fpath, fpath);
- close(c->fd);
- c->fd = -1;
- send_dir(fpath, fds, c);
- return 0;
-
- case FILE_MISSING:
- if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi))
- return check_for_cgi(fpath, query, fds, c);
-
- if (!start_reply(fds, c, NOT_FOUND, "not found"))
- return 0;
- goodbye(fds, c);
- return 0;
-
- default:
- /* unreachable */
- abort();
- }
+ goterror = 1;
+ fprintf(stderr, "%d: %s\n", yylineno, msg);
}
-void
-send_file(char *path, char *query, struct pollfd *fds, struct client *c)
+int
+parse_portno(const char *p)
{
- ssize_t ret, len;
-
- if (c->fd == -1) {
- if (!open_file(path, query, fds, c))
- return;
- c->state = S_SENDING;
- }
-
- len = (c->buf + c->len) - c->i;
-
- while (len > 0) {
- switch (ret = tls_write(c->ctx, c->i, len)) {
- case -1:
- LOGE(c, "tls_write: %s", tls_error(c->ctx));
- goodbye(fds, c);
- return;
-
- case TLS_WANT_POLLIN:
- fds->events = POLLIN;
- return;
-
- case TLS_WANT_POLLOUT:
- fds->events = POLLOUT;
- return;
-
- default:
- c->i += ret;
- len -= ret;
- break;
- }
- }
+ char *ep;
+ long lval;
- goodbye(fds, c);
+ errno = 0;
+ lval = strtol(p, &ep, 10);
+ if (p[0] == '\0' || *ep != '\0')
+ errx(1, "not a number: %s", p);
+ if (lval < 0 || lval > UINT16_MAX)
+ errx(1, "port number out of range for domain %s: %ld", p, lval);
+ return lval;
}
void
-send_dir(char *path, struct pollfd *fds, struct client *client)
+parse_conf(const char *path)
{
- char fpath[PATHBUF];
- size_t len;
-
- bzero(fpath, PATHBUF);
-
- if (path[0] != '.')
- fpath[0] = '.';
-
- /* this cannot fail since sizeof(fpath) > maxlen of path */
- strlcat(fpath, path, PATHBUF);
- len = strlen(fpath);
-
- /* add a trailing / in case. */
- if (fpath[len-1] != '/') {
- fpath[len] = '/';
- }
-
- strlcat(fpath, "index.gmi", sizeof(fpath));
+ if ((yyin = fopen(path, "r")) == NULL)
+ err(1, "cannot open config %s", path);
+ yyparse();
+ fclose(yyin);
- send_file(fpath, NULL, fds, client);
+ if (goterror)
+ exit(1);
}
void
-handle_handshake(struct pollfd *fds, struct client *c)
+load_vhosts(struct tls_config *tlsconf)
{
struct vhost *h;
- const char *servname;
- switch (tls_handshake(c->ctx)) {
- case 0: /* success */
- case -1: /* already handshaked */
- break;
- case TLS_WANT_POLLIN:
- fds->events = POLLIN;
- return;
- case TLS_WANT_POLLOUT:
- fds->events = POLLOUT;
- return;
- default:
- /* unreachable */
- abort();
- }
-
- servname = tls_conn_servername(c->ctx);
- if (servname == NULL)
- goto hostnotfound;
+ /* we need to set something, then we can add how many key we want */
+ if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
+ errx(1, "tls_config_set_keypair_file failed");
for (h = hosts; h->domain != NULL; ++h) {
- if (!strcmp(h->domain, servname) || !strcmp(h->domain, "*"))
- break;
- }
-
- if (h->domain != NULL) {
- c->state = S_OPEN;
- c->host = h;
- handle_open_conn(fds, c);
- return;
- }
-
-hostnotfound:
- /* XXX: check the correct response */
- if (!start_reply(fds, c, BAD_REQUEST, "Wrong host or missing SNI"))
- return;
- goodbye(fds, c);
-}
-
-void
-handle_open_conn(struct pollfd *fds, struct client *c)
-{
- char buf[GEMINI_URL_LEN];
- const char *parse_err = "invalid request";
- struct iri iri;
-
- bzero(buf, sizeof(buf));
-
- switch (tls_read(c->ctx, buf, sizeof(buf)-1)) {
- case -1:
- LOGE(c, "tls_read: %s", tls_error(c->ctx));
- goodbye(fds, c);
- return;
-
- case TLS_WANT_POLLIN:
- fds->events = POLLIN;
- return;
-
- case TLS_WANT_POLLOUT:
- fds->events = POLLOUT;
- return;
- }
-
- if (!trim_req_iri(buf) || !parse_iri(buf, &iri, &parse_err)) {
- if (!start_reply(fds, c, BAD_REQUEST, parse_err))
- return;
- goodbye(fds, c);
- return;
- }
-
- if (strcmp(iri.schema, "gemini") || iri.port_no != conf.port) {
- if (!start_reply(fds, c, PROXY_REFUSED, "won't proxy request"))
- return;
- goodbye(fds, c);
- return;
- }
-
- LOGI(c, "GET %s%s%s",
- *iri.path ? iri.path : "/",
- *iri.query ? "?" : "",
- *iri.query ? iri.query : "");
-
- send_file(iri.path, iri.query, fds, c);
-}
-
-void
-handle(struct pollfd *fds, struct client *client)
-{
- switch (client->state) {
- case S_HANDSHAKE:
- handle_handshake(fds, client);
- break;
-
- case S_OPEN:
- handle_open_conn(fds, client);
- break;
-
- case S_INITIALIZING:
- if (!start_reply(fds, client, client->code, client->meta))
- return;
-
- if (client->code != SUCCESS) {
- /* we don't need a body */
- goodbye(fds, client);
- return;
- }
-
- client->state = S_SENDING;
-
- /* fallthrough */
-
- case S_SENDING:
- if (client->child)
- handle_cgi(fds, client);
- else
- send_file(NULL, NULL, fds, client);
- break;
-
- case S_CLOSING:
- goodbye(fds, client);
- break;
+ if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
+ errx(1, "failed to load the keypair (%s, %s)",
+ h->cert, h->key);
- default:
- /* unreachable */
- abort();
+ if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1)
+ err(1, "open %s for domain %s", h->dir, h->domain);
}
}
-void
-mark_nonblock(int fd)
-{
- int flags;
-
- if ((flags = fcntl(fd, F_GETFL)) == -1)
- fatal("fcntl(F_GETFL): %s", strerror(errno));
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
- fatal("fcntl(F_SETFL): %s", strerror(errno));
-}
-
int
make_socket(int port, int family)
{
@@ -542,197 +299,6 @@ make_socket(int port, int family)
return sock;
}
-void
-do_accept(int sock, struct tls *ctx, struct pollfd *fds, struct client *clients)
-{
- int i, fd;
- struct sockaddr_storage addr;
- socklen_t len;
-
- len = sizeof(addr);
- if ((fd = accept(sock, (struct sockaddr*)&addr, &len)) == -1) {
- if (errno == EWOULDBLOCK)
- return;
- fatal("accept: %s", strerror(errno));
- }
-
- mark_nonblock(fd);
-
- for (i = 0; i < MAX_USERS; ++i) {
- if (fds[i].fd == -1) {
- bzero(&clients[i], sizeof(struct client));
- if (tls_accept_socket(ctx, &clients[i].ctx, fd) == -1)
- break; /* goodbye fd! */
-
- fds[i].fd = fd;
- fds[i].events = POLLIN;
-
- clients[i].state = S_HANDSHAKE;
- clients[i].fd = -1;
- clients[i].child = 0;
- clients[i].waiting_on_child = 0;
- clients[i].buf = MAP_FAILED;
- clients[i].af = AF_INET;
- clients[i].addr = addr;
-
- connected_clients++;
- return;
- }
- }
-
- close(fd);
-}
-
-void
-goodbye(struct pollfd *pfd, struct client *c)
-{
- c->state = S_CLOSING;
-
- switch (tls_close(c->ctx)) {
- case TLS_WANT_POLLIN:
- pfd->events = POLLIN;
- return;
- case TLS_WANT_POLLOUT:
- pfd->events = POLLOUT;
- return;
- }
-
- connected_clients--;
-
- tls_free(c->ctx);
- c->ctx = NULL;
-
- if (c->buf != MAP_FAILED)
- munmap(c->buf, c->len);
-
- if (c->fd != -1)
- close(c->fd);
-
- close(pfd->fd);
- pfd->fd = -1;
-}
-
-void
-loop(struct tls *ctx, int sock4, int sock6)
-{
- int i;
- struct client clients[MAX_USERS];
- struct pollfd fds[MAX_USERS];
-
- for (i = 0; i < MAX_USERS; ++i) {
- fds[i].fd = -1;
- fds[i].events = POLLIN;
- bzero(&clients[i], sizeof(struct client));
- }
-
- fds[0].fd = sock4;
- fds[1].fd = sock6;
-
- for (;;) {
- if (poll(fds, MAX_USERS, INFTIM) == -1) {
- if (errno == EINTR) {
- warnx("connected clients: %d",
- connected_clients);
- continue;
- }
- fatal("poll: %s", strerror(errno));
- }
-
- for (i = 0; i < MAX_USERS; i++) {
- if (fds[i].revents == 0)
- continue;
-
- if (fds[i].revents & (POLLERR|POLLNVAL))
- fatal("bad fd %d: %s", fds[i].fd,
- strerror(errno));
-
- if (fds[i].revents & POLLHUP) {
- /* fds[i] may be the fd of the stdin
- * of a cgi script that has exited. */
- if (!clients[i].waiting_on_child) {
- goodbye(&fds[i], &clients[i]);
- continue;
- }
- }
-
- if (fds[i].fd == sock4)
- do_accept(sock4, ctx, fds, clients);
- else if (fds[i].fd == sock6)
- do_accept(sock6, ctx, fds, clients);
- else
- handle(&fds[i], &clients[i]);
- }
- }
-}
-
-char *
-absolutify_path(const char *path)
-{
- char *wd, *r;
-
- if (*path == '/')
- return strdup(path);
-
- wd = getcwd(NULL, 0);
- if (asprintf(&r, "%s/%s", wd, path) == -1)
- err(1, "asprintf");
- free(wd);
- return r;
-}
-
-void
-yyerror(const char *msg)
-{
- goterror = 1;
- fprintf(stderr, "%d: %s\n", yylineno, msg);
-}
-
-int
-parse_portno(const char *p)
-{
- char *ep;
- long lval;
-
- errno = 0;
- lval = strtol(p, &ep, 10);
- if (p[0] == '\0' || *ep != '\0')
- errx(1, "not a number: %s", p);
- if (lval < 0 || lval > UINT16_MAX)
- errx(1, "port number out of range for domain %s: %ld", p, lval);
- return lval;
-}
-
-void
-parse_conf(const char *path)
-{
- if ((yyin = fopen(path, "r")) == NULL)
- err(1, "cannot open config %s", path);
- yyparse();
- fclose(yyin);
-
- if (goterror)
- exit(1);
-}
-
-void
-load_vhosts(struct tls_config *tlsconf)
-{
- struct vhost *h;
-
- /* we need to set something, then we can add how many key we want */
- if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
- errx(1, "tls_config_set_keypair_file failed");
-
- for (h = hosts; h->domain != NULL; ++h) {
- if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
- errx(1, "failed to load the keypair (%s, %s)",
- h->cert, h->key);
-
- if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1)
- err(1, "open %s for domain %s", h->dir, h->domain);
- }
-}
-
int
listener_main()
{
@@ -798,8 +364,6 @@ main(int argc, char **argv)
conf.ipv6 = 0;
conf.protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
- connected_clients = 0;
-
while ((ch = getopt(argc, argv, "6C:c:d:fhK:np:x:")) != -1) {
switch (ch) {
case '6':