diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-11-20 15:54:56 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-11-20 15:54:56 +0000 |
commit | e6c7ec852fff7782025440ba3dc423ef88e77468 (patch) | |
tree | ded00acf8d5fff247ba0242a56098a6f229155c4 /usr.sbin/relayd | |
parent | 06cda98f2ac65d76367d70562e5e25990af9200b (diff) |
it may be desirable to send a HTTP error page with error code and a
meaningful message if a HTTP/HTTPS relay closes the connection for
some reason. for example, a "403 Forbidden" if the request was
rejected by a filter. this will be enabled with the "return error"
option and is disabled by default, the standard behaviour is to
silently drop the connection; the browser may display an empty page in
this case. the look+feel of the HTTP error page can be customized with
a CSS style sheet, but we do not intend to allow customization of the
error page contents (hoststated is not a webserver!).
ok pyr@
Diffstat (limited to 'usr.sbin/relayd')
-rw-r--r-- | usr.sbin/relayd/log.c | 58 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 25 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 127 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 4 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 17 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 6 |
6 files changed, 217 insertions, 20 deletions
diff --git a/usr.sbin/relayd/log.c b/usr.sbin/relayd/log.c index 44724696450..2f2e37ce781 100644 --- a/usr.sbin/relayd/log.c +++ b/usr.sbin/relayd/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.8 2007/11/04 22:09:02 reyk Exp $ */ +/* $OpenBSD: log.c,v 1.9 2007/11/20 15:54:55 reyk Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -246,3 +246,59 @@ print_time(struct timeval *a, struct timeval *b, char *buf, size_t len) snprintf(buf, len, "%.2lu:%.2lu:%.2lu", h, min, sec); return (buf); } + +const char * +print_httperror(u_int code) +{ + u_int i; + struct { + u_int ht_code; + const char *ht_err; + } httperr[] = { + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authorative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Moved Temporarily" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Timeout" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URL Too Long" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested Range Not Satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Timeout" }, + { 505, "HTTP Version Not Supported" }, + { 0 } + }; + + for (i = 0; httperr[i].ht_code != 0; i++) + if (httperr[i].ht_code == code) + return (httperr[i].ht_err); + return ("Unknown Error"); +} diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index f8db0d6c26e..4be6e44b47d 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.85 2007/11/20 15:44:21 pyr Exp $ */ +/* $OpenBSD: parse.y,v 1.86 2007/11/20 15:54:55 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -116,7 +116,7 @@ typedef struct { %token SERVICE TABLE BACKUP HOST REAL INCLUDE %token CHECK TCP ICMP EXTERNAL REQUEST RESPONSE -%token TIMEOUT CODE DIGEST PORT TAG INTERFACE +%token TIMEOUT CODE DIGEST PORT TAG INTERFACE STYLE RETURN %token VIRTUAL INTERVAL DISABLE STICKYADDR BACKLOG PATH SCRIPT %token SEND EXPECT NOTHING SSL LOADBALANCE ROUNDROBIN CIPHERS COOKIE %token RELAY LISTEN ON FORWARD TO NAT LOOKUP PREFORK NO MARK MARKED @@ -203,6 +203,22 @@ proto_type : TCP { $$ = RELAY_PROTO_TCP; } } ; +eflags_l : eflags comma eflags_l + | eflags + ; + +opteflags : /* nothing */ + | eflags + ; + +eflags : STYLE STRING + { + if ((proto->style = strdup($2)) == NULL) + fatal("out of memory"); + free($2); + } + ; + port : PORT STRING { struct servent *servent; @@ -649,6 +665,8 @@ protoptsl : SSL sslflags | TCP tcpflags | TCP '{' tcpflags_l '}' | PROTO proto_type { proto->type = $2; } + | RETURN ERROR opteflags { proto->flags |= F_RETURN; } + | RETURN ERROR '{' eflags_l '}' { proto->flags |= F_RETURN; } | direction protonode log { struct protonode *pn, *proot, pk; struct proto_tree *tree; @@ -1236,6 +1254,7 @@ lookup(char *s) { "demote", DEMOTE }, { "digest", DIGEST }, { "disable", DISABLE }, + { "error", ERROR }, { "expect", EXPECT }, { "external", EXTERNAL }, { "filter", FILTER }, @@ -1270,6 +1289,7 @@ lookup(char *s) { "request", REQUEST }, { "response", RESPONSE }, { "retry", RETRY }, + { "return", RETURN }, { "roundrobin", ROUNDROBIN }, { "sack", SACK }, { "script", SCRIPT }, @@ -1279,6 +1299,7 @@ lookup(char *s) { "socket", SOCKET }, { "ssl", SSL }, { "sticky-address", STICKYADDR }, + { "style", STYLE }, { "table", TABLE }, { "tag", TAG }, { "tcp", TCP }, diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 80b1a03a7e5..28b90f48edb 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.58 2007/11/20 15:10:46 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.59 2007/11/20 15:54:55 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> @@ -83,6 +83,7 @@ int relay_from_table(struct session *); void relay_write(struct bufferevent *, void *); void relay_read(struct bufferevent *, void *); void relay_error(struct bufferevent *, short, void *); +void relay_dump(struct ctl_relay_event *, const void *, size_t); int relay_resolve(struct ctl_relay_event *, struct protonode *, struct protonode *); @@ -94,6 +95,7 @@ void relay_read_httpcontent(struct bufferevent *, void *); void relay_read_httpchunks(struct bufferevent *, void *); char *relay_expand_http(struct ctl_relay_event *, char *, char *, size_t); +void relay_close_http(struct session *, u_int, const char *); SSL_CTX *relay_ssl_ctx_create(struct relay *); void relay_ssl_transaction(struct session *); @@ -692,7 +694,7 @@ relay_connected(int fd, short sig, void *arg) struct bufferevent *bev; if (sig == EV_TIMEOUT) { - relay_close(con, "connect timeout"); + relay_close_http(con, 504, "connect timeout"); return; } @@ -706,7 +708,8 @@ relay_connected(int fd, short sig, void *arg) outrd = relay_read_http; if ((con->out.nodes = calloc(proto->response_nodes, sizeof(u_int8_t))) == NULL) { - relay_close(con, "failed to allocate nodes"); + relay_close_http(con, 500, + "failed to allocate nodes"); return; } } @@ -723,7 +726,8 @@ relay_connected(int fd, short sig, void *arg) */ bev = bufferevent_new(fd, outrd, outwr, relay_error, &con->out); if (bev == NULL) { - relay_close(con, "failed to allocate output buffer event"); + relay_close_http(con, 500, + "failed to allocate output buffer event"); return; } evbuffer_free(bev->output); @@ -796,6 +800,21 @@ relay_write(struct bufferevent *bev, void *arg) } void +relay_dump(struct ctl_relay_event *cre, const void *buf, size_t len) +{ + /* + * This function will dump the specified message directly + * to the underlying session, without waiting for success + * of non-blocking events etc. This is useful to print an + * error message before gracefully closing the session. + */ + if (cre->ssl != NULL) + (void)SSL_write(cre->ssl, buf, len); + else + (void)write(cre->s, buf, len); +} + +void relay_read(struct bufferevent *bev, void *arg) { struct ctl_relay_event *cre = (struct ctl_relay_event *)arg; @@ -862,7 +881,7 @@ relay_resolve(struct ctl_relay_event *cre, relay_bufferevent_print(cre->dst, ": ") == -1 || relay_bufferevent_print(cre->dst, ptr) == -1 || relay_bufferevent_print(cre->dst, "\r\n") == -1) { - relay_close(con, "failed to modify header"); + relay_close_http(con, 500, "failed to modify header"); return (-1); } DPRINTF("relay_resolve: add '%s: %s'", @@ -873,14 +892,14 @@ relay_resolve(struct ctl_relay_event *cre, break; DPRINTF("relay_resolve: missing '%s: %s'", pn->key, pn->value); - relay_close(con, "incomplete header"); + relay_close_http(con, 403, "incomplete request"); return (-1); case NODE_ACTION_FILTER: if (pn->flags & PNFLAG_MARK) break; DPRINTF("relay_resolve: filtered '%s: %s'", pn->key, pn->value); - relay_close(con, "rejecting header"); + relay_close_http(con, 403, "rejecting request"); return (-1); default: break; @@ -979,7 +998,7 @@ relay_handle_http(struct ctl_relay_event *cre, struct protonode *proot, * trying to circumvent the filter. */ if (cre->nodes[proot->id] > 1) { - relay_close(con, "repeated header line"); + relay_close_http(con, 400, "repeated header line"); return (PN_FAIL); } ret = PN_PASS; @@ -1024,7 +1043,7 @@ relay_handle_http(struct ctl_relay_event *cre, struct protonode *proot, return (ret); fail: - relay_close(con, strerror(errno)); + relay_close_http(con, 500, strerror(errno)); return (PN_FAIL); } @@ -1206,7 +1225,8 @@ relay_read_http(struct bufferevent *bev, void *arg) if (pk.value == NULL || strlen(pk.value) < 3) { if (cre->line == 1) { free(line); - goto fail; + relay_close_http(con, 400, "malformed"); + return; } DPRINTF("relay_read_http: request '%s'", line); @@ -1315,9 +1335,8 @@ relay_read_http(struct bufferevent *bev, void *arg) * include the line length in the content-length. */ cre->toread = strtonum(pk.value, 1, INT_MAX, &errstr); - if (errstr) { - relay_close(con, errstr); + relay_close_http(con, 500, errstr); free(line); return; } @@ -1483,7 +1502,7 @@ relay_read_http(struct bufferevent *bev, void *arg) if (cre->dir == RELAY_DIR_REQUEST && proto->lateconnect && cre->dst->bev == NULL && relay_connect(con) == -1) { - relay_close(con, "session failed"); + relay_close_http(con, 502, "session failed"); return; } } @@ -1497,7 +1516,87 @@ relay_read_http(struct bufferevent *bev, void *arg) relay_close(con, "last http read (done)"); return; fail: - relay_close(con, strerror(errno)); + relay_close_http(con, 500, strerror(errno)); +} + +void +relay_close_http(struct session *con, u_int code, const char *msg) +{ + struct relay *rlay = (struct relay *)con->relay; + struct bufferevent *bev = con->in.bev; + const char *httperr = print_httperror(code), *text = ""; + char *httpmsg; + time_t t; + struct tm *lt; + char tmbuf[32], hbuf[128]; + const char *style; + + /* In some cases this function may be called from generic places */ + if (rlay->proto->type != RELAY_PROTO_HTTP || + (rlay->proto->flags & F_RETURN) == 0) { + relay_close(con, msg); + return; + } + + if (bev == NULL) + goto done; + + /* Some system information */ + if (print_host(&rlay->conf.ss, hbuf, sizeof(hbuf)) == NULL) + goto done; + + /* RFC 2616 "tolerates" asctime() */ + time(&t); + lt = localtime(&t); + tmbuf[0] = '\0'; + if (asctime_r(lt, tmbuf) != NULL) + tmbuf[strlen(tmbuf) - 1] = '\0'; /* skip final '\n' */ + + /* Do not send details of the Internal Server Error */ + if (code != 500) + text = msg; + + /* A CSS stylesheet allows minimal customization by the user */ + if ((style = rlay->proto->style) == NULL) + style = "body { background-color: #a00000; color: white; }"; + + /* Generate simple HTTP+HTML error document */ + if (asprintf(&httpmsg, + "HTTP/1.x %03d %s\r\n" + "Date: %s\r\n" + "Server: %s\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "<!DOCTYPE HTML PUBLIC " + "\"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" + "<html>\n" + "<head>\n" + "<title>%03d %s</title>\n" + "<style type=\"text/css\"><!--\n%s\n--></style>\n" + "</head>\n" + "<body>\n" + "<h1>%s</h1>\n" + "<p>%s</p>\n" + "<hr><address>%s at %s port %d</address>\n" + "</body>\n" + "</html>\n", + code, httperr, tmbuf, HOSTSTATED_SERVERNAME, + code, httperr, style, httperr, text, + HOSTSTATED_SERVERNAME, hbuf, ntohs(rlay->conf.port)) == -1) + goto done; + + /* Dump the message without checking for success */ + relay_dump(&con->in, httpmsg, strlen(httpmsg)); + free(httpmsg); + + done: + if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) + relay_close(con, msg); + else { + relay_close(con, httpmsg); + free(httpmsg); + } } void diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 0f08383bfd5..ae6380116ed 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.54 2007/11/19 15:31:36 reyk Exp $ */ +/* $OpenBSD: relayd.c,v 1.55 2007/11/20 15:54:55 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -500,6 +500,8 @@ purge_config(struct hoststated *env, u_int8_t what) TAILQ_REMOVE(env->protos, proto, entry); purge_tree(&proto->request_tree); purge_tree(&proto->response_tree); + if (proto->style != NULL) + free(proto->style); free(proto); } free(env->protos); diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index d863f6972e9..bae09455edf 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.55 2007/11/20 15:44:21 pyr Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.56 2007/11/20 15:54:55 reyk Exp $ .\" .\" Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -594,6 +594,21 @@ section above. .It Ic log Ar key Log the name and the value of the entity. .El +.It Ic return error Op Ar option +Return an error reponse to the client if an internal operation or the +forward connection to the client failed. +By default, the connection will be silently dropped. +The effect of this option depends on the protocol, HTTP will send a +error header and page to the client before closing the connection. +Additional valid options are: +.Bl -tag -width Ds +.It Ic style Ar string +Specify a Cascading Style Sheet (CSS) to be used for the returned +HTTP error pages, for example: +.Bd -literal -offset indent +body { background: #a00000; color: white; } +.Ed +.El .It Ic tcp Ar option Enable or disable the specified TCP/IP options; see .Xr tcp 4 diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 5091bbb785b..eddc059a11b 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.75 2007/11/20 15:44:21 pyr Exp $ */ +/* $OpenBSD: relayd.h,v 1.76 2007/11/20 15:54:55 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -25,6 +25,7 @@ #define PF_SOCKET "/dev/pf" #define HOSTSTATED_USER "_hoststated" #define HOSTSTATED_ANCHOR "hoststated" +#define HOSTSTATED_SERVERNAME "OpenBSD hoststated" #define CHECK_TIMEOUT 200 #define CHECK_INTERVAL 10 #define EMPTY_TABLE UINT_MAX @@ -305,6 +306,7 @@ TAILQ_HEAD(addresslist, address); #define F_LOOKUP_PATH 0x00004000 #define F_DEMOTED 0x00008000 #define F_UDP 0x00010000 +#define F_RETURN 0x00020000 struct host_config { objid_t id; @@ -499,6 +501,7 @@ struct protocol { int cache; enum prototype type; int lateconnect; + char *style; int request_nodes; struct proto_tree request_tree; @@ -664,6 +667,7 @@ const char *table_check(enum table_check); const char *print_availability(u_long, u_long); const char *print_host(struct sockaddr_storage *, char *, size_t); const char *print_time(struct timeval *, struct timeval *, char *, size_t); +const char *print_httperror(u_int); /* buffer.c */ struct buf *buf_open(size_t); |