summaryrefslogtreecommitdiff
path: root/usr.sbin/relayd/relay.c
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2007-11-20 15:54:56 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2007-11-20 15:54:56 +0000
commite6c7ec852fff7782025440ba3dc423ef88e77468 (patch)
treeded00acf8d5fff247ba0242a56098a6f229155c4 /usr.sbin/relayd/relay.c
parent06cda98f2ac65d76367d70562e5e25990af9200b (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/relay.c')
-rw-r--r--usr.sbin/relayd/relay.c127
1 files changed, 113 insertions, 14 deletions
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