Skip to content
Snippets Groups Projects
mongoose.c 130 KiB
Newer Older
Sergey Lyubka's avatar
Sergey Lyubka committed
      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
                   file_name, file_name_len);
    }
  }

  // Scan body, search for terminating boundary
  for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
    if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
      if (data_len != NULL) *data_len = (pos - 2) - hl;
      if (data != NULL) *data = buf + hl;
      return pos;
    }
  }

  return 0;
}

const char **mg_get_valid_option_names(void) {
  return static_config_options;
static int get_option_index(const char *name) {
  int i;

  for (i = 0; static_config_options[i * 2] != NULL; i++) {
    if (strcmp(static_config_options[i * 2], name) == 0) {
      return i;
  }
  return -1;
}

static void set_default_option_values(char **opts) {
  const char *value, **all_opts = mg_get_valid_option_names();
  int i;
  for (i = 0; all_opts[i * 2] != NULL; i++) {
    value = all_opts[i * 2 + 1];
    if (opts[i] == NULL && value != NULL) {
      opts[i] = mg_strdup(value);
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
static int parse_port_string(const char *str, union socket_address *sa) {
  unsigned int a, b, c, d, port;
  int len = 0;
#ifdef MONGOOSE_USE_IPV6
  char buf[100];
#endif
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
  // Also, all-zeroes in the socket address means binding to all addresses
  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
  memset(sa, 0, sizeof(*sa));
  sa->sin.sin_family = AF_INET;
  if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
    // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
    sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
    sa->sin.sin_port = htons((uint16_t) port);
#if defined(MONGOOSE_USE_IPV6)
  } else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 &&
             inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
    // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
    sa->sin6.sin6_family = AF_INET6;
    sa->sin6.sin6_port = htons((uint16_t) port);
#endif
  } else if (sscanf(str, "%u%n", &port, &len) == 1) {
    // If only port is specified, bind to IPv4, INADDR_ANY
    sa->sin.sin_port = htons((uint16_t) port);
  } else {
    port = 0;   // Parsing failure. Make port invalid.
const char *mg_set_option(struct mg_server *server, const char *name,
                          const char *value) {
  int ind = get_option_index(name);
  const char *error_msg = NULL;

  if (ind < 0) {
    error_msg = "No such option";
  } else {
    if (server->config_options[ind] != NULL) {
      free(server->config_options[ind]);
    }
    server->config_options[ind] = mg_strdup(value);
    DBG(("%s [%s]", name, value));

    if (ind == LISTENING_PORT) {
      if (server->listening_sock != INVALID_SOCKET) {
        closesocket(server->listening_sock);
      }
      parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
      server->listening_sock = open_listening_socket(&server->lsa);
      if (server->listening_sock == INVALID_SOCKET) {
        error_msg = "Cannot bind to port";
      } else {
        sockaddr_to_string(server->local_ip, sizeof(server->local_ip),
                           &server->lsa);
        if (!strcmp(value, "0")) {
          char buf[10];
          mg_snprintf(buf, sizeof(buf), "%d",
                      (int) ntohs(server->lsa.sin.sin_port));
          free(server->config_options[ind]);
          server->config_options[ind] = mg_strdup(buf);
        }
Sergey Lyubka's avatar
Sergey Lyubka committed
#ifndef _WIN32
    } else if (ind == RUN_AS_USER) {
Sergey Lyubka's avatar
Sergey Lyubka committed
      struct passwd *pw;
      if ((pw = getpwnam(value)) == NULL) {
        error_msg = "Unknown user";
      } else if (setgid(pw->pw_gid) != 0) {
        error_msg = "setgid() failed";
      } else if (setuid(pw->pw_uid) != 0) {
        error_msg = "setuid() failed";
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif
#ifdef MONGOOSE_USE_SSL
    } else if (ind == SSL_CERTIFICATE) {
      //SSL_library_init();
Sergey Lyubka's avatar
Sergey Lyubka committed
      if ((server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
        error_msg = "SSL_CTX_new() failed";
      } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, value, 1) == 0 ||
                 SSL_CTX_use_PrivateKey_file(server->ssl_ctx, value, 1) == 0) {
        error_msg = "Cannot load PEM file";
      } else {
        SSL_CTX_use_certificate_chain_file(server->ssl_ctx, value);
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif

void mg_set_http_error_handler(struct mg_server *server, mg_handler_t handler) {
  server->error_handler = handler;
}

void mg_set_auth_handler(struct mg_server *server, mg_handler_t handler) {
  server->auth_handler = handler;
}

void mg_set_listening_socket(struct mg_server *server, int sock) {
  if (server->listening_sock != INVALID_SOCKET) {
    closesocket(server->listening_sock);
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
  server->listening_sock = (sock_t) sock;
}

int mg_get_listening_socket(struct mg_server *server) {
  return server->listening_sock;
}

const char *mg_get_option(const struct mg_server *server, const char *name) {
  const char **opts = (const char **) server->config_options;
  int i = get_option_index(name);
  return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
}

struct mg_server *mg_create_server(void *server_data) {
  struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));

#ifdef _WIN32
  WSADATA data;
  WSAStartup(MAKEWORD(2, 2), &data);
#else
  // Ignore SIGPIPE signal, so if browser cancels the request, it
  // won't kill the whole process.
  signal(SIGPIPE, SIG_IGN);
#endif

  LINKED_LIST_INIT(&server->active_connections);
  LINKED_LIST_INIT(&server->uri_handlers);
Sergey Lyubka's avatar
Sergey Lyubka committed

  // Create control socket pair. Do it in a loop to protect from
  // interrupted syscalls in mg_socketpair().
  do {
    mg_socketpair(server->ctl);
  } while (server->ctl[0] == INVALID_SOCKET);
#ifdef MONGOOSE_USE_SSL
  SSL_library_init();
  server->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
#endif

  server->server_data = server_data;
  server->listening_sock = INVALID_SOCKET;
  set_default_option_values(server->config_options);
  return server;