mirror of https://github.com/omar-polo/gmid.git
improve logs
now we log the full IRI requested (before was only the path) and the response line (even for CGI).
This commit is contained in:
parent
61f8d630c8
commit
0be51733ef
50
gmid.c
50
gmid.c
|
@ -91,6 +91,56 @@ logs(int priority, struct client *c,
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* strchr, but with a bound */
|
||||||
|
static char *
|
||||||
|
gmid_strnchr(char *s, int c, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
if (s[i] == c)
|
||||||
|
return &s[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log_request(struct client *c, char *meta, size_t l)
|
||||||
|
{
|
||||||
|
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV], b[GEMINI_URL_LEN];
|
||||||
|
char *t;
|
||||||
|
size_t len;
|
||||||
|
int ec;
|
||||||
|
|
||||||
|
len = sizeof(c->addr);
|
||||||
|
ec = getnameinfo((struct sockaddr*)&c->addr, len,
|
||||||
|
hbuf, sizeof(hbuf),
|
||||||
|
sbuf, sizeof(sbuf),
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if (ec != 0)
|
||||||
|
fatal("getnameinfo: %s", gai_strerror(ec));
|
||||||
|
|
||||||
|
/* serialize the IRI */
|
||||||
|
strlcpy(b, c->iri.schema, sizeof(b));
|
||||||
|
strlcat(b, "://", sizeof(b));
|
||||||
|
strlcat(b, c->iri.host, sizeof(b));
|
||||||
|
strlcat(b, "/", sizeof(b));
|
||||||
|
strlcat(b, c->iri.path, sizeof(b)); /* TODO: sanitize UTF8 */
|
||||||
|
if (*c->iri.query != '\0') { /* TODO: sanitize UTF8 */
|
||||||
|
strlcat(b, "?", sizeof(b));
|
||||||
|
strlcat(b, c->iri.query, sizeof(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((t = gmid_strnchr(meta, '\r', l)) == NULL)
|
||||||
|
t = meta + len;
|
||||||
|
|
||||||
|
if (conf.foreground)
|
||||||
|
fprintf(stderr, "%s:%s GET %s %.*s\n", hbuf, sbuf, b,
|
||||||
|
(int)(t - meta), meta);
|
||||||
|
else
|
||||||
|
syslog(LOG_INFO | LOG_DAEMON, "%s:%s GET %s %.*s",
|
||||||
|
hbuf, sbuf, b, (int)(t - meta), meta);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sig_handler(int sig)
|
sig_handler(int sig)
|
||||||
{
|
{
|
||||||
|
|
54
gmid.h
54
gmid.h
|
@ -38,6 +38,7 @@
|
||||||
#define PATHBUF 2048
|
#define PATHBUF 2048
|
||||||
|
|
||||||
#define SUCCESS 20
|
#define SUCCESS 20
|
||||||
|
#define TEMP_REDIRECT 30
|
||||||
#define TEMP_FAILURE 40
|
#define TEMP_FAILURE 40
|
||||||
#define NOT_FOUND 51
|
#define NOT_FOUND 51
|
||||||
#define PROXY_REFUSED 53
|
#define PROXY_REFUSED 53
|
||||||
|
@ -88,28 +89,6 @@ struct conf {
|
||||||
extern struct conf conf;
|
extern struct conf conf;
|
||||||
extern int exfd;
|
extern int exfd;
|
||||||
|
|
||||||
enum {
|
|
||||||
S_HANDSHAKE,
|
|
||||||
S_OPEN,
|
|
||||||
S_INITIALIZING,
|
|
||||||
S_SENDING,
|
|
||||||
S_CLOSING,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct client {
|
|
||||||
struct tls *ctx;
|
|
||||||
int state;
|
|
||||||
int code;
|
|
||||||
const char *meta;
|
|
||||||
int fd, waiting_on_child;
|
|
||||||
int child;
|
|
||||||
char sbuf[1024]; /* static buffer */
|
|
||||||
void *buf, *i; /* mmap buffer */
|
|
||||||
ssize_t len, off; /* mmap/static buffer */
|
|
||||||
struct sockaddr_storage addr;
|
|
||||||
struct vhost *host; /* host she's talking to */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct iri {
|
struct iri {
|
||||||
char *schema;
|
char *schema;
|
||||||
char *host;
|
char *host;
|
||||||
|
@ -126,6 +105,30 @@ struct parser {
|
||||||
const char *err;
|
const char *err;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
S_HANDSHAKE,
|
||||||
|
S_OPEN,
|
||||||
|
S_INITIALIZING,
|
||||||
|
S_SENDING,
|
||||||
|
S_CLOSING,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct client {
|
||||||
|
struct tls *ctx;
|
||||||
|
char req[GEMINI_URL_LEN];
|
||||||
|
struct iri iri;
|
||||||
|
int state;
|
||||||
|
int code;
|
||||||
|
const char *meta;
|
||||||
|
int fd, waiting_on_child;
|
||||||
|
int child;
|
||||||
|
char sbuf[1024]; /* static buffer */
|
||||||
|
void *buf, *i; /* mmap buffer */
|
||||||
|
ssize_t len, off; /* mmap/static buffer */
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
struct vhost *host; /* host she's talking to */
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
FILE_EXISTS,
|
FILE_EXISTS,
|
||||||
FILE_EXECUTABLE,
|
FILE_EXECUTABLE,
|
||||||
|
@ -141,6 +144,7 @@ void fatal(const char*, ...);
|
||||||
|
|
||||||
__attribute__((format (printf, 3, 4)))
|
__attribute__((format (printf, 3, 4)))
|
||||||
void logs(int, struct client*, const char*, ...);
|
void logs(int, struct client*, const char*, ...);
|
||||||
|
void log_request(struct client*, char*, size_t);
|
||||||
|
|
||||||
void sig_handler(int);
|
void sig_handler(int);
|
||||||
int starts_with(const char*, const char*);
|
int starts_with(const char*, const char*);
|
||||||
|
@ -169,15 +173,15 @@ const char *mime(struct vhost*, const char*);
|
||||||
|
|
||||||
/* server.c */
|
/* server.c */
|
||||||
int check_path(struct client*, const char*, int*);
|
int check_path(struct client*, const char*, int*);
|
||||||
int open_file(char*, char*, struct pollfd*, struct client*);
|
int open_file(struct pollfd*, struct client*);
|
||||||
int check_for_cgi(char *, char*, struct pollfd*, struct client*);
|
int check_for_cgi(char *, char*, struct pollfd*, struct client*);
|
||||||
void mark_nonblock(int);
|
void mark_nonblock(int);
|
||||||
void handle_handshake(struct pollfd*, struct client*);
|
void handle_handshake(struct pollfd*, struct client*);
|
||||||
void handle_open_conn(struct pollfd*, struct client*);
|
void handle_open_conn(struct pollfd*, struct client*);
|
||||||
int start_reply(struct pollfd*, struct client*, int, const char*);
|
int start_reply(struct pollfd*, struct client*, int, const char*);
|
||||||
int start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*);
|
int start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*);
|
||||||
void send_file(char*, char*, struct pollfd*, struct client*);
|
void send_file(struct pollfd*, struct client*);
|
||||||
void send_dir(char*, struct pollfd*, struct client*);
|
void send_dir(struct pollfd*, struct client*);
|
||||||
void cgi_poll_on_child(struct pollfd*, struct client*);
|
void cgi_poll_on_child(struct pollfd*, struct client*);
|
||||||
void cgi_poll_on_client(struct pollfd*, struct client*);
|
void cgi_poll_on_client(struct pollfd*, struct client*);
|
||||||
void handle_cgi(struct pollfd*, struct client*);
|
void handle_cgi(struct pollfd*, struct client*);
|
||||||
|
|
126
server.c
126
server.c
|
@ -54,41 +54,43 @@ check_path(struct client *c, const char *path, int *fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
open_file(char *fpath, char *query, struct pollfd *fds, struct client *c)
|
open_file(struct pollfd *fds, struct client *c)
|
||||||
{
|
{
|
||||||
switch (check_path(c, fpath, &c->fd)) {
|
switch (check_path(c, c->iri.path, &c->fd)) {
|
||||||
case FILE_EXECUTABLE:
|
case FILE_EXECUTABLE:
|
||||||
if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi))
|
if (c->host->cgi != NULL && starts_with(c->iri.path, c->host->cgi))
|
||||||
return start_cgi(fpath, "", query, fds, c);
|
return start_cgi(c->iri.path, "", c->iri.query, fds, c);
|
||||||
|
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case FILE_EXISTS:
|
case FILE_EXISTS:
|
||||||
if ((c->len = filesize(c->fd)) == -1) {
|
if ((c->len = filesize(c->fd)) == -1) {
|
||||||
LOGE(c, "failed to get file size for %s", fpath);
|
LOGE(c, "failed to get file size for %s", c->iri.path);
|
||||||
goodbye(fds, c);
|
goodbye(fds, c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE,
|
if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE,
|
||||||
c->fd, 0)) == MAP_FAILED) {
|
c->fd, 0)) == MAP_FAILED) {
|
||||||
LOGW(c, "mmap: %s: %s", fpath, strerror(errno));
|
LOGW(c, "mmap: %s: %s", c->iri.path, strerror(errno));
|
||||||
goodbye(fds, c);
|
goodbye(fds, c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
c->i = c->buf;
|
c->i = c->buf;
|
||||||
return start_reply(fds, c, SUCCESS, mime(c->host, fpath));
|
if (!start_reply(fds, c, SUCCESS, mime(c->host, c->iri.path)))
|
||||||
|
return 0;
|
||||||
|
send_file(fds, c);
|
||||||
|
return 0;
|
||||||
|
|
||||||
case FILE_DIRECTORY:
|
case FILE_DIRECTORY:
|
||||||
LOGD(c, "%s is a directory, trying %s/index.gmi", fpath, fpath);
|
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
c->fd = -1;
|
c->fd = -1;
|
||||||
send_dir(fpath, fds, c);
|
send_dir(fds, c);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case FILE_MISSING:
|
case FILE_MISSING:
|
||||||
if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi))
|
if (c->host->cgi != NULL && starts_with(c->iri.path, c->host->cgi))
|
||||||
return check_for_cgi(fpath, query, fds, c);
|
return check_for_cgi(c->iri.path, c->iri.query, fds, c);
|
||||||
|
|
||||||
if (!start_reply(fds, c, NOT_FOUND, "not found"))
|
if (!start_reply(fds, c, NOT_FOUND, "not found"))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -203,13 +205,12 @@ hostnotfound:
|
||||||
void
|
void
|
||||||
handle_open_conn(struct pollfd *fds, struct client *c)
|
handle_open_conn(struct pollfd *fds, struct client *c)
|
||||||
{
|
{
|
||||||
char buf[GEMINI_URL_LEN];
|
|
||||||
const char *parse_err = "invalid request";
|
const char *parse_err = "invalid request";
|
||||||
struct iri iri;
|
|
||||||
|
|
||||||
bzero(buf, sizeof(buf));
|
bzero(c->req, sizeof(c->req));
|
||||||
|
bzero(&c->iri, sizeof(c->iri));
|
||||||
|
|
||||||
switch (tls_read(c->ctx, buf, sizeof(buf)-1)) {
|
switch (tls_read(c->ctx, c->req, sizeof(c->req)-1)) {
|
||||||
case -1:
|
case -1:
|
||||||
LOGE(c, "tls_read: %s", tls_error(c->ctx));
|
LOGE(c, "tls_read: %s", tls_error(c->ctx));
|
||||||
goodbye(fds, c);
|
goodbye(fds, c);
|
||||||
|
@ -224,33 +225,29 @@ handle_open_conn(struct pollfd *fds, struct client *c)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trim_req_iri(buf) || !parse_iri(buf, &iri, &parse_err)) {
|
if (!trim_req_iri(c->req) || !parse_iri(c->req, &c->iri, &parse_err)) {
|
||||||
if (!start_reply(fds, c, BAD_REQUEST, parse_err))
|
if (!start_reply(fds, c, BAD_REQUEST, parse_err))
|
||||||
return;
|
return;
|
||||||
goodbye(fds, c);
|
goodbye(fds, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(iri.schema, "gemini") || iri.port_no != conf.port) {
|
/* XXX: we should check that the SNI matches the requested host */
|
||||||
|
if (strcmp(c->iri.schema, "gemini") || c->iri.port_no != conf.port) {
|
||||||
if (!start_reply(fds, c, PROXY_REFUSED, "won't proxy request"))
|
if (!start_reply(fds, c, PROXY_REFUSED, "won't proxy request"))
|
||||||
return;
|
return;
|
||||||
goodbye(fds, c);
|
goodbye(fds, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI(c, "GET %s%s%s",
|
open_file(fds, c);
|
||||||
*iri.path ? iri.path : "/",
|
|
||||||
*iri.query ? "?" : "",
|
|
||||||
*iri.query ? iri.query : "");
|
|
||||||
|
|
||||||
send_file(iri.path, iri.query, fds, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
|
start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
|
||||||
{
|
{
|
||||||
char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */
|
char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */
|
||||||
int len;
|
size_t len;
|
||||||
|
|
||||||
c->code = code;
|
c->code = code;
|
||||||
c->meta = meta;
|
c->meta = meta;
|
||||||
|
@ -264,7 +261,7 @@ start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlcat(buf, "\r\n", sizeof(buf));
|
len = strlcat(buf, "\r\n", sizeof(buf));
|
||||||
assert(len < (int)sizeof(buf));
|
assert(len < sizeof(buf));
|
||||||
|
|
||||||
switch (tls_write(c->ctx, buf, len)) {
|
switch (tls_write(c->ctx, buf, len)) {
|
||||||
case TLS_WANT_POLLIN:
|
case TLS_WANT_POLLIN:
|
||||||
|
@ -274,6 +271,7 @@ start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
|
||||||
pfd->events = POLLOUT;
|
pfd->events = POLLOUT;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
log_request(c, buf, sizeof(buf));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +321,7 @@ start_cgi(const char *spath, const char *relpath, const char *query,
|
||||||
c->child = 1;
|
c->child = 1;
|
||||||
c->state = S_SENDING;
|
c->state = S_SENDING;
|
||||||
cgi_poll_on_child(fds, c);
|
cgi_poll_on_child(fds, c);
|
||||||
|
c->code = -1;
|
||||||
/* handle_cgi(fds, c); */
|
/* handle_cgi(fds, c); */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -332,16 +331,10 @@ err:
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
send_file(char *path, char *query, struct pollfd *fds, struct client *c)
|
send_file(struct pollfd *fds, struct client *c)
|
||||||
{
|
{
|
||||||
ssize_t ret, len;
|
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;
|
len = (c->buf + c->len) - c->i;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
|
@ -370,28 +363,56 @@ send_file(char *path, char *query, struct pollfd *fds, struct client *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
send_dir(char *path, struct pollfd *fds, struct client *client)
|
send_dir(struct pollfd *fds, struct client *c)
|
||||||
{
|
{
|
||||||
char fpath[PATHBUF];
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
bzero(fpath, PATHBUF);
|
/* guard against a re-entrant call:
|
||||||
|
*
|
||||||
if (path[0] != '.')
|
* open_file -> send_dir -> open_file -> send_dir
|
||||||
fpath[0] = '.';
|
*
|
||||||
|
* this can happen only if:
|
||||||
/* this cannot fail since sizeof(fpath) > maxlen of path */
|
*
|
||||||
strlcat(fpath, path, PATHBUF);
|
* - user requested a dir, say foo/
|
||||||
len = strlen(fpath);
|
* - we try to serve foo/index.gmi
|
||||||
|
* - foo/index.gmi is a directory.
|
||||||
/* add a trailing / in case. */
|
*
|
||||||
if (fpath[len-1] != '/') {
|
* It's an unlikely case, but can happen. We then redirect
|
||||||
fpath[len] = '/';
|
* to foo/index.gmi
|
||||||
|
*/
|
||||||
|
if (c->iri.path == c->sbuf) {
|
||||||
|
if (!start_reply(fds, c, TEMP_REDIRECT, c->sbuf))
|
||||||
|
return;
|
||||||
|
goodbye(fds, c);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(fpath, "index.gmi", sizeof(fpath));
|
len = strlen(c->iri.path);
|
||||||
|
if (len > 0 && c->iri.path[len-1] != '/') {
|
||||||
|
/* redirect to url with the trailing / */
|
||||||
|
strlcpy(c->sbuf, c->iri.path, sizeof(c->sbuf));
|
||||||
|
strlcat(c->sbuf, "/", sizeof(c->sbuf));
|
||||||
|
if (!start_reply(fds, c, TEMP_REDIRECT, c->sbuf))
|
||||||
|
return;
|
||||||
|
goodbye(fds, c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
send_file(fpath, NULL, fds, client);
|
strlcpy(c->sbuf, c->iri.path, sizeof(c->sbuf));
|
||||||
|
if (len != 0)
|
||||||
|
strlcat(c->sbuf, "/", sizeof(c->sbuf));
|
||||||
|
len = strlcat(c->sbuf, "index.gmi", sizeof(c->sbuf));
|
||||||
|
|
||||||
|
if (len >= sizeof(c->sbuf)) {
|
||||||
|
if (!start_reply(fds, c, TEMP_FAILURE, "internal server error"))
|
||||||
|
return;
|
||||||
|
goodbye(fds, c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(c->fd);
|
||||||
|
c->iri.path = c->sbuf;
|
||||||
|
open_file(fds, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -445,6 +466,13 @@ handle_cgi(struct pollfd *fds, struct client *c)
|
||||||
}
|
}
|
||||||
c->len = r;
|
c->len = r;
|
||||||
c->off = 0;
|
c->off = 0;
|
||||||
|
|
||||||
|
/* XXX: if we haven't still read a whole
|
||||||
|
* reply line, we should go back to poll! */
|
||||||
|
if (c->code == -1) {
|
||||||
|
c->code = 0;
|
||||||
|
log_request(c, c->sbuf, sizeof(c->sbuf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (c->len > 0) {
|
while (c->len > 0) {
|
||||||
|
@ -571,7 +599,7 @@ handle(struct pollfd *fds, struct client *client)
|
||||||
if (client->child)
|
if (client->child)
|
||||||
handle_cgi(fds, client);
|
handle_cgi(fds, client);
|
||||||
else
|
else
|
||||||
send_file(NULL, NULL, fds, client);
|
send_file(fds, client);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case S_CLOSING:
|
case S_CLOSING:
|
||||||
|
|
Loading…
Reference in New Issue