From 88dc3b4156edac295e399c77f5a7f88bdd5e3cae Mon Sep 17 00:00:00 2001
From: Vladislav Grishenko <themiron@mail.ru>
Date: Fri, 1 Jun 2018 04:07:45 +0500
Subject: sstp: implement configurable http error response incl. redirect

---
 accel-pppd/accel-ppp.conf   |  1 +
 accel-pppd/accel-ppp.conf.5 | 17 +++++++++
 accel-pppd/ctrl/sstp/sstp.c | 84 +++++++++++++++++++++++++++------------------
 3 files changed, 69 insertions(+), 33 deletions(-)

diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index b1870109..1d38ea15 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -118,6 +118,7 @@ verbose=1
 #ssl-pemfile=/etc/ssl/sstp-cert.pem
 #ssl-keyfile=/etc/ssl/sstp-key.pem
 #host-name=domain.tld
+#http-error=allow
 #timeout=60
 #hello-interval=60
 #ip-pool=sstp
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index a06e7817..4c1bee76 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -717,6 +717,23 @@ certificate or used directly for non-ssl mode.
 .BI "host-name=" string
 If this option is given, only sstp connection to specified host and with the same TLS SNI will be allowed.
 .TP
+.BI "http-error=" deny|allow|http[s]://host.tld[/path]
+Specify http layer error behavior for non-sstp requests.
+.br
+.B deny
+- reset connection without any error response.
+.br
+.B allow
+- respond with http-specific status codes.
+.br
+.B http[s]://host.tld[/path]
+- respond with http redirect to the specified location.
+If
+.B /path
+is not specified, requested uri will be appended automatically
+.br
+Default value is allow.
+.TP
 .BI "ifname=" ifname
 If this option is given ppp interface will be renamed using
 .B ifname
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c
index c7cbfea2..2218df6b 100644
--- a/accel-pppd/ctrl/sstp/sstp.c
+++ b/accel-pppd/ctrl/sstp/sstp.c
@@ -166,6 +166,8 @@ static struct hash_t conf_hash_sha1 = { .len = 0 };
 static struct hash_t conf_hash_sha256 = { .len = 0 };
 //static int conf_bypass_auth = 0;
 static const char *conf_hostname = NULL;
+static int conf_http_mode = -1;
+static const char *conf_http_url = NULL;
 
 static mempool_t conn_pool;
 
@@ -840,72 +842,73 @@ static int http_send_response(struct sstp_conn_t *conn, char *proto, char *statu
 
 static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len)
 {
-	char linebuf[1024], protobuf[sizeof("HTTP/1.x")];
-	char *line, *method, *request, *proto, *host, *ptr;
+	char httpbuf[1024], linebuf[1024];
+	char *line, *method, *request, *proto, *host;
 	struct buffer_t buf;
-	int ret = -1;
+	int host_error;
 
 	buf.head = data;
 	buf.end = data + len;
 	buf_set_length(&buf, len);
 
-	line = http_getline(&buf, linebuf, sizeof(linebuf));
+	line = http_getline(&buf, httpbuf, sizeof(httpbuf));
 	if (!line)
 		return -1;
 	if (conf_verbose)
 		log_ppp_info2("recv [HTTP <%s>]\n", line);
 
-	host = NULL;
-
 	if (vstrsep(line, " ", &method, &request, &proto) < 3) {
-		http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL);
-		goto error;
+		if (conf_http_mode)
+			http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL);
+		return -1;
 	}
 	if (strncasecmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) {
-		http_send_response(conn, "HTTP/1.1", "505 HTTP Version Not Supported", NULL);
-		goto error;
-	}
-	if (strcasecmp(method, SSTP_HTTP_METHOD) != 0) {
-		http_send_response(conn, proto, "405 Method Not Allowed", NULL);
-		goto error;
+		if (conf_http_mode)
+			http_send_response(conn, "HTTP/1.1", "505 HTTP Version Not Supported", NULL);
+		return -1;
 	}
-	if (strcasecmp(request, SSTP_HTTP_URI) != 0) {
-		http_send_response(conn, proto, "404 Not Found", NULL);
-		goto error;
+	if (strcasecmp(method, SSTP_HTTP_METHOD) != 0 && strcasecmp(method, "GET") != 0) {
+		if (conf_http_mode)
+			http_send_response(conn, proto, "405 Method Not Allowed", NULL);
+		return -1;
 	}
 
-	snprintf(protobuf, sizeof(protobuf), "%s", proto);
-	proto = protobuf;
-
+	host_error = conf_hostname ? -1 : 0;
 	while ((line = http_getline(&buf, linebuf, sizeof(linebuf))) != NULL) {
 		if (*line == '\0')
 			break;
 		if (conf_verbose)
 			log_ppp_info2("recv [HTTP <%s>]\n", line);
 
-		if (!host && conf_hostname) {
+		if (host_error < 0) {
 			host = http_getvalue(line, "Host", sizeof("Host") - 1);
 			if (host) {
-				ptr = _strdup(host);
-				host = strsep(&ptr, ":");
+				host = strsep(&host, ":");
+				host_error = (strcasecmp(host, conf_hostname) != 0);
 			}
 		}
 	}
 
-	if (conf_hostname && strcasecmp(host ? : "", conf_hostname) != 0) {
-		http_send_response(conn, proto, "434 Requested host unavailable", NULL);
-		goto error;
+	if (host_error) {
+		if (conf_http_mode)
+			http_send_response(conn, proto, "404 Not Found", NULL);
+		return -1;
 	}
 
-	if (http_send_response(conn, proto, "200 OK",
-			"Content-Length: 18446744073709551615\r\n")) {
-		goto error;
+	if (strcasecmp(method, SSTP_HTTP_METHOD) != 0 || strcasecmp(request, SSTP_HTTP_URI) != 0) {
+		if (conf_http_mode > 0) {
+			if (_asprintf(&line, "Location: %s%s\r\n",
+			    conf_http_url, (conf_http_mode == 2) ? request : "") < 0)
+				return -1;
+			http_send_response(conn, proto, "301 Moved Permanently", line);
+			_free(line);
+		} else if (conf_http_mode < 0)
+			http_send_response(conn, proto, "404 Not Found", NULL);
+		return -1;
 	}
-	ret = 0;
 
-error:
-	_free(host);
-	return ret;
+	return http_send_response(conn, proto, "200 OK",
+			"Content-Length: 18446744073709551615\r\n");
 }
 
 static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf)
@@ -2386,6 +2389,21 @@ static void load_config(void)
 
 	conf_hostname = conf_get_opt("sstp", "host-name");
 
+	opt = conf_get_opt("sstp", "http-error");
+	if (opt) {
+		if (strcmp(opt, "deny") == 0)
+			conf_http_mode = 0;
+		else if (strcmp(opt, "allow") == 0)
+			conf_http_mode = -1;
+		else if (strstr(opt, "://") != NULL) {
+			conf_http_url = opt;
+			opt = strstr(opt, "://") + 3;
+			while (*opt == '/')
+				opt++;
+			conf_http_mode = strchr(opt, '/') ? 1 : 2;
+		}
+	}
+
 	opt = conf_get_opt("sstp", "cert-hash-proto");
 	if (opt) {
 		conf_hash_protocol = 0;
-- 
cgit v1.2.3