ws_ssl.c 5.13 KB
Newer Older
Daniel O'Connell's avatar
Daniel O'Connell committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Copyright (c) 2014 Cesanta Software
// All rights reserved
//
// This example demostrates proxying of WebSocket traffic, regardless of the
// protocol (ws:// or wss://).
// To use this example:
//    1. configure your browser to use a proxy on port 2014
//    2. import certs/ws1_ca.pem and certs/ws2_ca.pem into the trusted
//       certificates list on your browser
//    3. make && ./ws_ssl
//    4. Point your browser to http://ws_ssl.com
//  A page with 4 sections should appear, showing websocket echoes

#include "net_skeleton.h"
#include "mongoose.h"
#include "ssl_wrapper.h"

18 19 20 21 22 23 24
#define S1_PEM  "certs/ws1_server.pem"
#define C1_PEM  "certs/ws1_client.pem"
#define CA1_PEM "certs/ws1_ca.pem"
#define S2_PEM  "certs/ws2_server.pem"
#define C2_PEM  "certs/ws2_client.pem"
#define CA2_PEM "certs/ws2_ca.pem"

Daniel O'Connell's avatar
Daniel O'Connell committed
25
struct config {
26 27 28 29
  const char *uri;
  const char *wrapper_server_addr;
  const char *wrapper_client_addr;
  const char *target_addr;
Daniel O'Connell's avatar
Daniel O'Connell committed
30 31 32
};

static struct config s_wrappers[] = {
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  {
    "ws1:80",
    "tcp://127.0.0.1:7001",
    "tcp://127.0.0.1:7001",
    "tcp://127.0.0.1:9001"
  },
  {
    "ws1:443",
    "ssl://127.0.0.1:7002:" S1_PEM,
    "tcp://127.0.0.1:7002",
    "tcp://127.0.0.1:9001"
  },
  {
    "ws2:80",
    "tcp://127.0.0.1:7003",
    "tcp://127.0.0.1:7003",
    "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
  },
  {
    "ws2:443",
    "ssl://127.0.0.1:7004:" S2_PEM,
    "tcp://127.0.0.1:7004",
    "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
  },
Daniel O'Connell's avatar
Daniel O'Connell committed
57
};
58

Daniel O'Connell's avatar
Daniel O'Connell committed
59 60 61 62 63 64 65 66
static int s_received_signal = 0;

static void signal_handler(int sig_num) {
  signal(sig_num, signal_handler);
  s_received_signal = sig_num;
}

static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
67 68
  int i;

Daniel O'Connell's avatar
Daniel O'Connell committed
69 70 71 72 73 74
  switch (ev) {
    case MG_AUTH:
      return MG_TRUE;

    case MG_REQUEST:
      printf("==> [%s] [%s]\n", conn->request_method, conn->uri);
75

76 77
      if (strcmp(conn->request_method, "CONNECT") == 0) {
        // Iterate over configured wrappers, see if we can use one of them
Daniel O'Connell's avatar
Daniel O'Connell committed
78
        for (i = 0; i < (int) ARRAY_SIZE(s_wrappers); i++) {
79 80
          if (strcmp(conn->uri, s_wrappers[i].uri) == 0) {
            mg_forward(conn, s_wrappers[i].wrapper_client_addr);
Daniel O'Connell's avatar
Daniel O'Connell committed
81 82 83 84 85 86 87 88 89 90
            return MG_MORE;
          }
        }

        // No suitable wrappers found. Disallow that CONNECT request.
        mg_send_status(conn, 405);
        return MG_TRUE;
      }

      // Not a CONNECT request, serve HTML file.
91
      mg_send_file(conn, "ws_ssl.html", NULL);
Daniel O'Connell's avatar
Daniel O'Connell committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
      return MG_MORE;

    default:
      return MG_FALSE;
  }
}

static int ws_handler(struct mg_connection *conn, enum mg_event ev) {
  switch (ev) {
    case MG_AUTH:
      return MG_TRUE;
    case MG_REQUEST:
      if (conn->is_websocket) {
        // Simple websocket echo server
        mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT,
                           conn->content, conn->content_len);
      } else {
        mg_printf_data(conn, "%s", "websocket connection expected");
      }
      return MG_TRUE;
    default:
      return MG_FALSE;
  }
}

static void *serve_thread_func(void *param) {
  struct mg_server *server = (struct mg_server *) param;
  printf("Listening on port %s\n", mg_get_option(server, "listening_port"));
  while (s_received_signal == 0) {
    mg_poll_server(server, 1000);
  }
  mg_destroy_server(&server);
  return NULL;
}

static void *wrapper_thread_func(void *param) {
128
  struct config *c = (struct config *) param;
Sergey Lyubka's avatar
Sergey Lyubka committed
129 130 131
  const char *err_msg;
  void *wrapper;

132 133
  wrapper = ssl_wrapper_init(c->wrapper_server_addr, c->target_addr, &err_msg);
  if (wrapper == NULL) {
Sergey Lyubka's avatar
Sergey Lyubka committed
134 135 136
    fprintf(stderr, "Error: %s\n", err_msg);
    exit(EXIT_FAILURE);
  }
137
  //((struct ns_mgr *) wrapper)->hexdump_file = "/dev/stderr";
Sergey Lyubka's avatar
Sergey Lyubka committed
138 139
  ssl_wrapper_serve(wrapper, &s_received_signal);

Daniel O'Connell's avatar
Daniel O'Connell committed
140 141 142 143 144 145 146 147 148
  return NULL;
}

int main(void) {
  struct mg_server *proxy_server = mg_create_server(NULL, ev_handler);
  struct mg_server *ws1_server = mg_create_server(NULL, ws_handler);
  struct mg_server *ws2_server = mg_create_server(NULL, ws_handler);
  size_t i;

149 150
  ((struct ns_mgr *) proxy_server)->hexdump_file = "/dev/stderr";

Daniel O'Connell's avatar
Daniel O'Connell committed
151 152 153 154 155 156 157 158 159
  // Configure proxy server to listen on port 2014
  mg_set_option(proxy_server, "listening_port", "2014");
  //mg_set_option(proxy_server, "enable_proxy", "yes");

  // Configure two websocket echo servers:
  //    ws1 is WS, listening on 9001
  //    ws2 is WSS, listening on 9002
  // Note that HTML page thinks that ws1 is WSS, and ws2 is WS,
  // where in reality it is vice versa and proxy server makes the decision.
160 161 162
  mg_set_option(ws1_server, "listening_port", "tcp://127.0.0.1:9001");
  mg_set_option(ws2_server, "listening_port",
                "ssl://127.0.0.1:9002:" S2_PEM ":" CA2_PEM);
Daniel O'Connell's avatar
Daniel O'Connell committed
163 164 165 166

  // Setup signal handlers
  signal(SIGTERM, signal_handler);
  signal(SIGINT, signal_handler);
167

Daniel O'Connell's avatar
Daniel O'Connell committed
168 169 170 171 172 173 174 175
  // Start SSL wrappers, each in it's own thread
  for (i = 0; i < ARRAY_SIZE(s_wrappers); i++) {
    ns_start_thread(wrapper_thread_func, &s_wrappers[i]);
  }

  // Start websocket servers in separate threads
  mg_start_thread(serve_thread_func, ws1_server);
  mg_start_thread(serve_thread_func, ws2_server);
176

Daniel O'Connell's avatar
Daniel O'Connell committed
177 178 179 180 181 182
  // Finally, start proxy server in this thread: this call blocks
  serve_thread_func(proxy_server);

  printf("Existing on signal %d\n", s_received_signal);
  return EXIT_SUCCESS;
}