From ea2069df5133c50dad05aae3e56cdfcfa80fa5e9 Mon Sep 17 00:00:00 2001
From: Alexander Alashkin <alexander.alashkin@cesanta.com>
Date: Thu, 1 Dec 2016 09:04:27 +0000
Subject: [PATCH] Fix coredump in mg_reverse_proxy_handler

PUBLISHED_FROM=ca0ae588290f133ef7640ca538847a63c0cb544b
---
 mongoose.c | 43 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 37 insertions(+), 6 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index 20e36b4c9..1549f117d 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -4992,6 +4992,10 @@ struct mg_http_multipart_stream {
   int processing_part;
 };
 
+struct mg_reverse_proxy_data {
+  struct mg_connection *linked_conn;
+};
+
 struct mg_http_proto_data {
 #if MG_ENABLE_FILESYSTEM
   struct mg_http_proto_data_file file;
@@ -5005,6 +5009,7 @@ struct mg_http_proto_data {
   struct mg_http_proto_data_chuncked chunk;
   struct mg_http_endpoint *endpoints;
   mg_event_handler_t endpoint_handler;
+  struct mg_reverse_proxy_data reverse_proxy_data;
 };
 
 static void mg_http_conn_destructor(void *proto_data);
@@ -5057,6 +5062,22 @@ static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
   ep = NULL;
 }
 
+static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) {
+  if (rpd->linked_conn != NULL) {
+    /*
+     * Connection has linked one, we have to unlink & close it
+     * since _this_ connection is going to die and
+     * it doesn't make sense to keep another one
+     */
+    struct mg_http_proto_data *pd = mg_http_get_proto_data(rpd->linked_conn);
+    if (pd->reverse_proxy_data.linked_conn != NULL) {
+      pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE;
+      pd->reverse_proxy_data.linked_conn = NULL;
+    }
+    rpd->linked_conn = NULL;
+  }
+}
+
 static void mg_http_conn_destructor(void *proto_data) {
   struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data;
 #if MG_ENABLE_FILESYSTEM
@@ -5069,6 +5090,7 @@ static void mg_http_conn_destructor(void *proto_data) {
   mg_http_free_proto_data_mp_stream(&pd->mp_stream);
 #endif
   mg_http_free_proto_data_endpoints(&pd->endpoints);
+  mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data);
   free(proto_data);
 }
 
@@ -7005,22 +7027,28 @@ static int mg_http_send_port_based_redirect(
 static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev,
                                      void *ev_data) {
   struct http_message *hm = (struct http_message *) ev_data;
-  struct mg_connection *upstream = (struct mg_connection *) nc->user_data;
+  struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
+
+  if (pd == NULL || pd->reverse_proxy_data.linked_conn == NULL) {
+    DBG(("%p: upstream closed", nc));
+    return;
+  }
 
   switch (ev) {
     case MG_EV_CONNECT:
       if (*(int *) ev_data != 0) {
-        mg_http_send_error(upstream, 502, NULL);
+        mg_http_send_error(pd->reverse_proxy_data.linked_conn, 502, NULL);
       }
       break;
     /* TODO(mkm): handle streaming */
     case MG_EV_HTTP_REPLY:
-      mg_send(upstream, hm->message.p, hm->message.len);
-      upstream->flags |= MG_F_SEND_AND_CLOSE;
+      mg_send(pd->reverse_proxy_data.linked_conn, hm->message.p,
+              hm->message.len);
+      pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE;
       nc->flags |= MG_F_CLOSE_IMMEDIATELY;
       break;
     case MG_EV_CLOSE:
-      upstream->flags |= MG_F_SEND_AND_CLOSE;
+      pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE;
       break;
   }
 }
@@ -7052,7 +7080,10 @@ void mg_http_reverse_proxy(struct mg_connection *nc,
     mg_http_send_error(nc, 502, NULL);
     goto cleanup;
   }
-  be->user_data = nc;
+
+  /* link connections to each other, they must live and die together */
+  mg_http_get_proto_data(be)->reverse_proxy_data.linked_conn = nc;
+  mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be;
 
   /* send request upstream */
   mg_printf(be, "%.*s %s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p,
-- 
GitLab