Commit 6083b9c5 authored by Sergey Lyubka's avatar Sergey Lyubka

Updated to the recent skeleton. SSL address format changed

parent 83237a02
// Copyright (c) 2004-2013 Sergey Lyubka // Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2014 Cesanta Software Limited // Copyright (c) 2013-2014 Cesanta Software Limited
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#undef UNICODE // Use ANSI WinAPI functions #undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows #undef _UNICODE // Use multibyte encoding on Windows
#define _MBCS // Use multibyte encoding on Windows #define _MBCS // Use multibyte encoding on Windows
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP #define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux #define _XOPEN_SOURCE 600 // For PATH_MAX on linux
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h #undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
#include <sys/stat.h> #include <sys/stat.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
#include <time.h> #include <time.h>
#include "mongoose.h" #include "mongoose.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <direct.h> // For chdir() #include <direct.h> // For chdir()
#include <winsvc.h> #include <winsvc.h>
#include <shlobj.h> #include <shlobj.h>
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX MAX_PATH #define PATH_MAX MAX_PATH
#endif #endif
#ifndef S_ISDIR #ifndef S_ISDIR
#define S_ISDIR(x) ((x) & _S_IFDIR) #define S_ISDIR(x) ((x) & _S_IFDIR)
#endif #endif
#define DIRSEP '\\' #define DIRSEP '\\'
#define snprintf _snprintf #define snprintf _snprintf
#define vsnprintf _vsnprintf #define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000) #define sleep(x) Sleep((x) * 1000)
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size)) #define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
#define SIGCHLD 0 #define SIGCHLD 0
typedef struct _stat file_stat_t; typedef struct _stat file_stat_t;
#define stat(x, y) _stat((x), (y)) #define stat(x, y) _stat((x), (y))
#else #else
typedef struct stat file_stat_t; typedef struct stat file_stat_t;
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#ifdef IOS #ifdef IOS
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
#define DIRSEP '/' #define DIRSEP '/'
#define __cdecl #define __cdecl
#define abs_path(rel, abs, abs_size) realpath((rel), (abs)) #define abs_path(rel, abs, abs_size) realpath((rel), (abs))
#endif // _WIN32 #endif // _WIN32
#define MAX_OPTIONS 100 #define MAX_OPTIONS 100
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024) #define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
#ifndef MVER #ifndef MVER
#define MVER MONGOOSE_VERSION #define MVER MONGOOSE_VERSION
#endif #endif
static int exit_flag; static int exit_flag;
static char server_name[50]; // Set by init_server_name() static char server_name[50]; // Set by init_server_name()
static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments
static struct mg_server *server; // Set by start_mongoose() static struct mg_server *server; // Set by start_mongoose()
static const char *s_default_document_root = "."; static const char *s_default_document_root = ".";
static const char *s_default_listening_port = "8080"; static const char *s_default_listening_port = "8080";
static char **s_argv = { NULL }; static char **s_argv = { NULL };
static void set_options(char *argv[]); static void set_options(char *argv[]);
#if !defined(CONFIG_FILE) #if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf" #define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */ #endif /* !CONFIG_FILE */
static void __cdecl signal_handler(int sig_num) { static void __cdecl signal_handler(int sig_num) {
// Reinstantiate signal handler // Reinstantiate signal handler
signal(sig_num, signal_handler); signal(sig_num, signal_handler);
#ifndef _WIN32 #ifndef _WIN32
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX) // Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid() // reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
// fails if SIGCHLD is ignored, making system() non-functional. // fails if SIGCHLD is ignored, making system() non-functional.
if (sig_num == SIGCHLD) { if (sig_num == SIGCHLD) {
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0); do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
} else } else
#endif #endif
{ exit_flag = sig_num; } { exit_flag = sig_num; }
} }
static void vnotify(const char *fmt, va_list ap, int must_exit) { static void vnotify(const char *fmt, va_list ap, int must_exit) {
char msg[200]; char msg[200];
vsnprintf(msg, sizeof(msg), fmt, ap); vsnprintf(msg, sizeof(msg), fmt, ap);
fprintf(stderr, "%s\n", msg); fprintf(stderr, "%s\n", msg);
if (must_exit) { if (must_exit) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
static void notify(const char *fmt, ...) { static void notify(const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vnotify(fmt, ap, 0); vnotify(fmt, ap, 0);
va_end(ap); va_end(ap);
} }
static void die(const char *fmt, ...) { static void die(const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vnotify(fmt, ap, 1); vnotify(fmt, ap, 1);
va_end(ap); va_end(ap);
} }
static void show_usage_and_exit(void) { static void show_usage_and_exit(void) {
const char **names; const char **names;
int i; int i;
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n", fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
MVER, __DATE__); MVER, __DATE__);
fprintf(stderr, "Usage:\n"); fprintf(stderr, "Usage:\n");
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n"); fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif #endif
fprintf(stderr, " mongoose [config_file]\n"); fprintf(stderr, " mongoose [config_file]\n");
fprintf(stderr, " mongoose [-option value ...]\n"); fprintf(stderr, " mongoose [-option value ...]\n");
fprintf(stderr, "\nOPTIONS:\n"); fprintf(stderr, "\nOPTIONS:\n");
names = mg_get_valid_option_names(); names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) { for (i = 0; names[i] != NULL; i += 2) {
fprintf(stderr, " -%s %s\n", fprintf(stderr, " -%s %s\n",
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]); names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
} }
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#define EV_HANDLER NULL #define EV_HANDLER NULL
static char *sdup(const char *str) { static char *sdup(const char *str) {
char *p; char *p;
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) { if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
strcpy(p, str); strcpy(p, str);
} }
return p; return p;
} }
static void set_option(char **options, const char *name, const char *value) { static void set_option(char **options, const char *name, const char *value) {
int i; int i;
for (i = 0; i < MAX_OPTIONS - 3; i++) { for (i = 0; i < MAX_OPTIONS - 3; i++) {
if (options[i] == NULL) { if (options[i] == NULL) {
options[i] = sdup(name); options[i] = sdup(name);
options[i + 1] = sdup(value); options[i + 1] = sdup(value);
options[i + 2] = NULL; options[i + 2] = NULL;
break; break;
} else if (!strcmp(options[i], name)) { } else if (!strcmp(options[i], name)) {
free(options[i + 1]); free(options[i + 1]);
options[i + 1] = sdup(value); options[i + 1] = sdup(value);
break; break;
} }
} }
if (i == MAX_OPTIONS - 3) { if (i == MAX_OPTIONS - 3) {
die("%s", "Too many options specified"); die("%s", "Too many options specified");
} }
} }
static void process_command_line_arguments(char *argv[], char **options) { static void process_command_line_arguments(char *argv[], char **options) {
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
*p, cpath[PATH_MAX]; *p, cpath[PATH_MAX];
FILE *fp = NULL; FILE *fp = NULL;
size_t i, cmd_line_opts_start = 1, line_no = 0; size_t i, cmd_line_opts_start = 1, line_no = 0;
// Should we use a config file ? // Should we use a config file ?
if (argv[1] != NULL && argv[1][0] != '-') { if (argv[1] != NULL && argv[1][0] != '-') {
snprintf(cpath, sizeof(cpath), "%s", argv[1]); snprintf(cpath, sizeof(cpath), "%s", argv[1]);
cmd_line_opts_start = 2; cmd_line_opts_start = 2;
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) { } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
// No command line flags specified. Look where binary lives // No command line flags specified. Look where binary lives
snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE); snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
} else { } else {
snprintf(cpath, sizeof(cpath), "%.*s%c%s", snprintf(cpath, sizeof(cpath), "%.*s%c%s",
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE); (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
} }
abs_path(cpath, s_config_file, sizeof(s_config_file)); abs_path(cpath, s_config_file, sizeof(s_config_file));
fp = fopen(s_config_file, "r"); fp = fopen(s_config_file, "r");
// If config file was set in command line and open failed, die // If config file was set in command line and open failed, die
if (cmd_line_opts_start == 2 && fp == NULL) { if (cmd_line_opts_start == 2 && fp == NULL) {
die("Cannot open config file %s: %s", s_config_file, strerror(errno)); die("Cannot open config file %s: %s", s_config_file, strerror(errno));
} }
// Load config file settings first // Load config file settings first
if (fp != NULL) { if (fp != NULL) {
fprintf(stderr, "Loading config file %s\n", s_config_file); fprintf(stderr, "Loading config file %s\n", s_config_file);
// Loop over the lines in config file // Loop over the lines in config file
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
line_no++; line_no++;
// Ignore empty lines and comments // Ignore empty lines and comments
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++; for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
if (line[i] == '#' || line[i] == '\0') { if (line[i] == '#' || line[i] == '\0') {
continue; continue;
} }
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
printf("%s: line %d is invalid, ignoring it:\n %s", printf("%s: line %d is invalid, ignoring it:\n %s",
s_config_file, (int) line_no, line); s_config_file, (int) line_no, line);
} else { } else {
set_option(options, opt, val); set_option(options, opt, val);
} }
} }
fclose(fp); fclose(fp);
} }
// If we're under MacOS and started by launchd, then the second // If we're under MacOS and started by launchd, then the second
// argument is process serial number, -psn_..... // argument is process serial number, -psn_.....
// In this case, don't process arguments at all. // In this case, don't process arguments at all.
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) { if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
// Handle command line flags. // Handle command line flags.
// They override config file and default settings. // They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) { for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) { if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit(); show_usage_and_exit();
} }
set_option(options, &argv[i][1], argv[i + 1]); set_option(options, &argv[i][1], argv[i + 1]);
} }
} }
} }
static void init_server_name(void) { static void init_server_name(void) {
const char *descr = ""; const char *descr = "";
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s", snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
MVER, descr); MVER, descr);
} }
static int is_path_absolute(const char *path) { static int is_path_absolute(const char *path) {
#ifdef _WIN32 #ifdef _WIN32
return path != NULL && return path != NULL &&
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir ((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir (isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
#else #else
return path != NULL && path[0] == '/'; return path != NULL && path[0] == '/';
#endif #endif
} }
static char *get_option(char **options, const char *option_name) { static char *get_option(char **options, const char *option_name) {
int i; int i;
for (i = 0; options[i] != NULL; i++) for (i = 0; options[i] != NULL; i++)
if (!strcmp(options[i], option_name)) if (!strcmp(options[i], option_name))
return options[i + 1]; return options[i + 1];
return NULL; return NULL;
} }
static void *serving_thread_func(void *param) { static void *serving_thread_func(void *param) {
struct mg_server *srv = (struct mg_server *) param; struct mg_server *srv = (struct mg_server *) param;
while (exit_flag == 0) { while (exit_flag == 0) {
mg_poll_server(srv, 1000); mg_poll_server(srv, 1000);
} }
return NULL; return NULL;
} }
static int path_exists(const char *path, int is_dir) { static int path_exists(const char *path, int is_dir) {
file_stat_t st; file_stat_t st;
return path == NULL || (stat(path, &st) == 0 && return path == NULL || (stat(path, &st) == 0 &&
((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir)); ((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
} }
static void verify_existence(char **options, const char *name, int is_dir) { static void verify_existence(char **options, const char *name, int is_dir) {
const char *path = get_option(options, name); const char *path = get_option(options, name);
if (!path_exists(path, is_dir)) { if (!path_exists(path, is_dir)) {
notify("Invalid path for %s: [%s]: (%s). Make sure that path is either " notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
"absolute, or it is relative to mongoose executable.", "absolute, or it is relative to mongoose executable.",
name, path, strerror(errno)); name, path, strerror(errno));
} }
} }
static void set_absolute_path(char *options[], const char *option_name) { static void set_absolute_path(char *options[], const char *option_name) {
char path[PATH_MAX], abs[PATH_MAX], *option_value; char path[PATH_MAX], abs[PATH_MAX], *option_value;
const char *p; const char *p;
// Check whether option is already set // Check whether option is already set
option_value = get_option(options, option_name); option_value = get_option(options, option_name);
// If option is already set and it is an absolute path, // If option is already set and it is an absolute path,
// leave it as it is -- it's already absolute. // leave it as it is -- it's already absolute.
if (option_value != NULL && !is_path_absolute(option_value)) { if (option_value != NULL && !is_path_absolute(option_value)) {
// Not absolute. Use the directory where mongoose executable lives // Not absolute. Use the directory where mongoose executable lives
// be the relative directory for everything. // be the relative directory for everything.
// Extract mongoose executable directory into path. // Extract mongoose executable directory into path.
if ((p = strrchr(s_config_file, DIRSEP)) == NULL) { if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
getcwd(path, sizeof(path)); getcwd(path, sizeof(path));
} else { } else {
snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file), snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
s_config_file); s_config_file);
} }
strncat(path, "/", sizeof(path) - 1); strncat(path, "/", sizeof(path) - 1);
strncat(path, option_value, sizeof(path) - 1); strncat(path, option_value, sizeof(path) - 1);
// Absolutize the path, and set the option // Absolutize the path, and set the option
abs_path(path, abs, sizeof(abs)); abs_path(path, abs, sizeof(abs));
set_option(options, option_name, abs); set_option(options, option_name, abs);
} }
} }
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
int modify_passwords_file(const char *fname, const char *domain, int modify_passwords_file(const char *fname, const char *domain,
const char *user, const char *pass) { const char *user, const char *pass) {
int found; int found;
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX]; char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
FILE *fp, *fp2; FILE *fp, *fp2;
found = 0; found = 0;
fp = fp2 = NULL; fp = fp2 = NULL;
// Regard empty password as no password - remove user record. // Regard empty password as no password - remove user record.
if (pass != NULL && pass[0] == '\0') { if (pass != NULL && pass[0] == '\0') {
pass = NULL; pass = NULL;
} }
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname); (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
// Create the file if does not exist // Create the file if does not exist
if ((fp = fopen(fname, "a+")) != NULL) { if ((fp = fopen(fname, "a+")) != NULL) {
fclose(fp); fclose(fp);
} }
// Open the given file and temporary file // Open the given file and temporary file
if ((fp = fopen(fname, "r")) == NULL) { if ((fp = fopen(fname, "r")) == NULL) {
return 0; return 0;
} else if ((fp2 = fopen(tmp, "w+")) == NULL) { } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
fclose(fp); fclose(fp);
return 0; return 0;
} }
// Copy the stuff to temporary file // Copy the stuff to temporary file
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) { if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
continue; continue;
} }
if (!strcmp(u, user) && !strcmp(d, domain)) { if (!strcmp(u, user) && !strcmp(d, domain)) {
found++; found++;
if (pass != NULL) { if (pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL); mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
} }
} else { } else {
fprintf(fp2, "%s", line); fprintf(fp2, "%s", line);
} }
} }
// If new user, just add it // If new user, just add it
if (!found && pass != NULL) { if (!found && pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL); mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
} }
// Close files // Close files
fclose(fp); fclose(fp);
fclose(fp2); fclose(fp2);
// Put the temp file in place of real file // Put the temp file in place of real file
remove(fname); remove(fname);
rename(tmp, fname); rename(tmp, fname);
return 1; return 1;
} }
#endif #endif
static void start_mongoose(int argc, char *argv[]) { static void start_mongoose(int argc, char *argv[]) {
s_argv = argv; s_argv = argv;
if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) { if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
die("%s", "Failed to start Mongoose."); die("%s", "Failed to start Mongoose.");
} }
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
// Edit passwords file if -A option is specified // Edit passwords file if -A option is specified
if (argc > 1 && !strcmp(argv[1], "-A")) { if (argc > 1 && !strcmp(argv[1], "-A")) {
if (argc != 6) { if (argc != 6) {
show_usage_and_exit(); show_usage_and_exit();
} }
exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ? exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
EXIT_SUCCESS : EXIT_FAILURE); EXIT_SUCCESS : EXIT_FAILURE);
} }
#endif #endif
// Show usage if -h or --help options are specified // Show usage if -h or --help options are specified
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
show_usage_and_exit(); show_usage_and_exit();
} }
set_options(argv); set_options(argv);
} }
static void set_options(char *argv[]) { static void set_options(char *argv[]) {
char *options[MAX_OPTIONS]; char *options[MAX_OPTIONS];
int i; int i;
options[0] = NULL; options[0] = NULL;
set_option(options, "document_root", s_default_document_root); set_option(options, "document_root", s_default_document_root);
set_option(options, "listening_port", s_default_listening_port); set_option(options, "listening_port", s_default_listening_port);
// Update config based on command line arguments // Update config based on command line arguments
process_command_line_arguments(argv, options); process_command_line_arguments(argv, options);
// Make sure we have absolute paths for files and directories // Make sure we have absolute paths for files and directories
// https://github.com/valenok/mongoose/issues/181 // https://github.com/valenok/mongoose/issues/181
set_absolute_path(options, "document_root"); set_absolute_path(options, "document_root");
set_absolute_path(options, "dav_auth_file"); set_absolute_path(options, "dav_auth_file");
set_absolute_path(options, "cgi_interpreter"); set_absolute_path(options, "cgi_interpreter");
set_absolute_path(options, "access_log_file"); set_absolute_path(options, "access_log_file");
set_absolute_path(options, "global_auth_file"); set_absolute_path(options, "global_auth_file");
set_absolute_path(options, "ssl_certificate"); set_absolute_path(options, "ssl_certificate");
if (!path_exists(get_option(options, "document_root"), 1)) { if (!path_exists(get_option(options, "document_root"), 1)) {
set_option(options, "document_root", s_default_document_root); set_option(options, "document_root", s_default_document_root);
set_absolute_path(options, "document_root"); set_absolute_path(options, "document_root");
notify("Setting document_root to [%s]", notify("Setting document_root to [%s]",
mg_get_option(server, "document_root")); mg_get_option(server, "document_root"));
} }
// Make extra verification for certain options // Make extra verification for certain options
verify_existence(options, "document_root", 1); verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0); verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0); verify_existence(options, "ssl_certificate", 0);
for (i = 0; options[i] != NULL; i += 2) { for (i = 0; options[i] != NULL; i += 2) {
const char *msg = mg_set_option(server, options[i], options[i + 1]); const char *msg = mg_set_option(server, options[i], options[i + 1]);
if (msg != NULL) { if (msg != NULL) {
notify("Failed to set option [%s] to [%s]: %s", notify("Failed to set option [%s] to [%s]: %s",
options[i], options[i + 1], msg); options[i], options[i + 1], msg);
if (!strcmp(options[i], "listening_port")) { if (!strcmp(options[i], "listening_port")) {
mg_set_option(server, "listening_port", s_default_listening_port); mg_set_option(server, "listening_port", s_default_listening_port);
notify("Setting %s to [%s]", options[i], s_default_listening_port); notify("Setting %s to [%s]", options[i], s_default_listening_port);
} }
} }
free(options[i]); free(options[i]);
free(options[i + 1]); free(options[i + 1]);
} }
// Change current working directory to document root. This way, // Change current working directory to document root. This way,
// scripts can use relative paths. // scripts can use relative paths.
chdir(mg_get_option(server, "document_root")); chdir(mg_get_option(server, "document_root"));
// Add an ability to pass listening socket to mongoose #if 0
{ // Add an ability to pass listening socket to mongoose
const char *env = getenv("MONGOOSE_LISTENING_SOCKET"); {
if (env != NULL && atoi(env) > 0 ) { const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
mg_set_listening_socket(server, atoi(env)); if (env != NULL && atoi(env) > 0 ) {
} mg_set_listening_socket(server, atoi(env));
} }
}
// Setup signal handler: quit on Ctrl-C #endif
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler); // Setup signal handler: quit on Ctrl-C
#ifndef _WIN32 signal(SIGTERM, signal_handler);
signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler);
#endif #ifndef _WIN32
} signal(SIGCHLD, signal_handler);
#endif
int main(int argc, char *argv[]) { }
init_server_name();
start_mongoose(argc, argv); int main(int argc, char *argv[]) {
printf("%s serving [%s] on port %s\n", init_server_name();
server_name, mg_get_option(server, "document_root"), start_mongoose(argc, argv);
mg_get_option(server, "listening_port")); printf("%s serving [%s] on port %s\n",
fflush(stdout); // Needed, Windows terminals might not be line-buffered server_name, mg_get_option(server, "document_root"),
serving_thread_func(server); mg_get_option(server, "listening_port"));
printf("Exiting on signal %d ...", exit_flag); fflush(stdout); // Needed, Windows terminals might not be line-buffered
fflush(stdout); serving_thread_func(server);
mg_destroy_server(&server); printf("Exiting on signal %d ...", exit_flag);
printf("%s\n", " done."); fflush(stdout);
mg_destroy_server(&server);
return EXIT_SUCCESS; printf("%s\n", " done.");
}
return EXIT_SUCCESS;
}
...@@ -396,9 +396,9 @@ static const char *test_server(void) { ...@@ -396,9 +396,9 @@ static const char *test_server(void) {
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL); ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL); ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL); ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
conn->connection_param = buf1; conn->connection_param = buf1;
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL); ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
conn->connection_param = buf2; conn->connection_param = buf2;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); } { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
...@@ -483,7 +483,7 @@ static const char *test_mg_set_option(void) { ...@@ -483,7 +483,7 @@ static const char *test_mg_set_option(void) {
} }
static const char *test_rewrites(void) { static const char *test_rewrites(void) {
char buf1[100] = "xx"; char buf1[100] = "xx", addr[50];
struct mg_server *server = mg_create_server(NULL, evh2); struct mg_server *server = mg_create_server(NULL, evh2);
struct mg_connection *conn; struct mg_connection *conn;
const char *port; const char *port;
...@@ -492,7 +492,8 @@ static const char *test_rewrites(void) { ...@@ -492,7 +492,8 @@ static const char *test_rewrites(void) {
ASSERT(mg_set_option(server, "document_root", ".") == NULL); ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL); ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL);
ASSERT((port = mg_get_option(server, "listening_port")) != NULL); ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(port), 0)) != NULL); snprintf(addr, sizeof(addr), "127.0.0.1:%s", port);
ASSERT((conn = mg_connect(server, addr)) != NULL);
conn->connection_param = buf1; conn->connection_param = buf1;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); } { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
......
// Copyright (c) 2013-2014 Cesanta Software Limited // Copyright (c) 2013-2014 Cesanta Software Limited
// $Date: 2014-09-08 22:30:52 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
......
// Copyright (c) 2013-2014 Cesanta Software Limited // Copyright (c) 2013-2014 Cesanta Software Limited
// $Date: 2014-09-09 08:27:35 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
......
# Copyright (c) 2014 Cesanta Software # Copyright (c) 2014 Cesanta Software
# All rights reserved # All rights reserved
CFLAGS = -W -Wall -I../.. -g -O0 $(CFLAGS_EXTRA) CFLAGS = -W -Wall -I../.. -I. -g -O0 $(CFLAGS_EXTRA)
NS = ../../../net_skeleton
SW = ../../../ssl_wrapper
SOURCES = ws_ssl.c ../../mongoose.c $(NS)/net_skeleton.c $(SW)/ssl_wrapper.c SOURCES = ws_ssl.c ../../mongoose.c net_skeleton.c ssl_wrapper.c
PROG = ws_ssl PROG = ws_ssl
all: $(PROG) all: $(PROG)
$(PROG): $(SOURCES) $(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) \ $(CC) -o $(PROG) $(SOURCES) \
-I$(NS) -DNS_ENABLE_SSL -DNOEMBED_NET_SKELETON \ -DNS_ENABLE_SSL -DNOEMBED_NET_SKELETON \
-I$(SW) -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS) -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS)
clean: clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
// //
// $Date: 2014-09-09 16:03:50 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#include "net_skeleton.h" #include "net_skeleton.h"
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
// //
// $Date: 2014-09-09 16:03:50 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#ifndef NS_SKELETON_HEADER_INCLUDED #ifndef NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_HEADER_INCLUDED #define NS_SKELETON_HEADER_INCLUDED
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/products.html>. // license, as set out in <http://cesanta.com/products.html>.
// //
// $Date: 2014-09-09 16:03:50 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#include "net_skeleton.h" #include "net_skeleton.h"
#include "ssl_wrapper.h" #include "ssl_wrapper.h"
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/products.html>. // license, as set out in <http://cesanta.com/products.html>.
// //
// $Date: 2014-09-09 16:03:50 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#ifndef SSL_WRAPPER_HEADER_INCLUDED #ifndef SSL_WRAPPER_HEADER_INCLUDED
#define SSL_WRAPPER_HEADER_INCLUDED #define SSL_WRAPPER_HEADER_INCLUDED
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
// Alternatively, you can license this library under a commercial // Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
// //
// $Date: 2014-09-01 19:53:26 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#ifdef NOEMBED_NET_SKELETON #ifdef NOEMBED_NET_SKELETON
#include "net_skeleton.h" #include "net_skeleton.h"
...@@ -36,11 +36,13 @@ ...@@ -36,11 +36,13 @@
// //
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
//
// $Date: 2014-09-09 17:07:55 UTC $
#ifndef NS_SKELETON_HEADER_INCLUDED #ifndef NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_HEADER_INCLUDED #define NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_VERSION "1.1" #define NS_SKELETON_VERSION "2.0.0"
#undef UNICODE // Use ANSI WinAPI functions #undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows #undef _UNICODE // Use multibyte encoding on Windows
...@@ -133,7 +135,9 @@ typedef int sock_t; ...@@ -133,7 +135,9 @@ typedef int sock_t;
#define DBG(x) #define DBG(x)
#endif #endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#endif
#ifdef NS_ENABLE_SSL #ifdef NS_ENABLE_SSL
#ifdef __APPLE__ #ifdef __APPLE__
...@@ -174,7 +178,7 @@ void iobuf_remove(struct iobuf *, size_t data_size); ...@@ -174,7 +178,7 @@ void iobuf_remove(struct iobuf *, size_t data_size);
// Net skeleton interface // Net skeleton interface
// Events. Meaning of event parameter (evp) is given in the comment. // Events. Meaning of event parameter (evp) is given in the comment.
enum ns_event { enum ns_event {
NS_POLL, // Sent to each connection on each call to ns_server_poll() NS_POLL, // Sent to each connection on each call to ns_mgr_poll()
NS_ACCEPT, // New connection accept()-ed. union socket_address *remote_addr NS_ACCEPT, // New connection accept()-ed. union socket_address *remote_addr
NS_CONNECT, // connect() succeeded or failed. int *success_status NS_CONNECT, // connect() succeeded or failed. int *success_status
NS_RECV, // Data has benn received. int *num_bytes NS_RECV, // Data has benn received. int *num_bytes
...@@ -187,36 +191,37 @@ enum ns_event { ...@@ -187,36 +191,37 @@ enum ns_event {
struct ns_connection; struct ns_connection;
typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp); typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp);
struct ns_server { struct ns_mgr {
void *server_data;
sock_t listening_sock;
struct ns_connection *active_connections; struct ns_connection *active_connections;
ns_callback_t callback; ns_callback_t callback; // Event handler function
SSL_CTX *ssl_ctx; const char *hexdump_file; // Debug hexdump file path
SSL_CTX *client_ssl_ctx; sock_t ctl[2]; // Socketpair for mg_wakeup()
const char *hexdump_file; void *user_data; // User data
sock_t ctl[2];
}; };
struct ns_connection { struct ns_connection {
struct ns_connection *prev, *next; struct ns_connection *next, *prev; // ns_mgr::active_connections linkage
struct ns_server *server; struct ns_connection *listener; // Set only for accept()-ed connections
struct ns_mgr *mgr;
sock_t sock; sock_t sock;
union socket_address sa; union socket_address sa;
struct iobuf recv_iobuf; struct iobuf recv_iobuf;
struct iobuf send_iobuf; struct iobuf send_iobuf;
SSL *ssl; SSL *ssl;
SSL_CTX *ssl_ctx;
void *connection_data; void *connection_data;
time_t last_io_time; time_t last_io_time;
unsigned int flags; unsigned int flags;
#define NSF_FINISHED_SENDING_DATA (1 << 0) #define NSF_FINISHED_SENDING_DATA (1 << 0)
#define NSF_BUFFER_BUT_DONT_SEND (1 << 1) #define NSF_BUFFER_BUT_DONT_SEND (1 << 1)
#define NSF_SSL_HANDSHAKE_DONE (1 << 2) #define NSF_SSL_HANDSHAKE_DONE (1 << 2)
#define NSF_CONNECTING (1 << 3) #define NSF_CONNECTING (1 << 3)
#define NSF_CLOSE_IMMEDIATELY (1 << 4) #define NSF_CLOSE_IMMEDIATELY (1 << 4)
#define NSF_ACCEPTED (1 << 5) #define NSF_WANT_READ (1 << 5)
#define NSF_WANT_READ (1 << 6) #define NSF_WANT_WRITE (1 << 6)
#define NSF_WANT_WRITE (1 << 7) #define NSF_LISTENING (1 << 7)
#define NSF_UDP (1 << 8)
#define NSF_USER_1 (1 << 26) #define NSF_USER_1 (1 << 26)
#define NSF_USER_2 (1 << 27) #define NSF_USER_2 (1 << 27)
...@@ -226,20 +231,15 @@ struct ns_connection { ...@@ -226,20 +231,15 @@ struct ns_connection {
#define NSF_USER_6 (1 << 31) #define NSF_USER_6 (1 << 31)
}; };
void ns_server_init(struct ns_server *, void *server_data, ns_callback_t); void ns_mgr_init(struct ns_mgr *, void *data, ns_callback_t);
void ns_server_free(struct ns_server *); void ns_mgr_free(struct ns_mgr *);
int ns_server_poll(struct ns_server *, int milli); int ns_mgr_poll(struct ns_mgr *, int milli);
void ns_server_wakeup(struct ns_server *); void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
void ns_server_wakeup_ex(struct ns_server *, ns_callback_t, void *, size_t);
void ns_iterate(struct ns_server *, ns_callback_t cb, void *param); struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
struct ns_connection *ns_next(struct ns_server *, struct ns_connection *); struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, void *p);
struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p); struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, void *p);
struct ns_connection *ns_connect(struct ns_mgr *, const char *addr, void *p);
int ns_bind(struct ns_server *, const char *addr);
int ns_set_ssl_cert(struct ns_server *, const char *ssl_cert);
int ns_set_ssl_ca_cert(struct ns_server *, const char *ssl_ca_cert);
struct ns_connection *ns_connect(struct ns_server *, const char *host,
int port, int ssl, void *connection_param);
int ns_send(struct ns_connection *, const void *buf, int len); int ns_send(struct ns_connection *, const void *buf, int len);
int ns_printf(struct ns_connection *, const char *fmt, ...); int ns_printf(struct ns_connection *, const char *fmt, ...);
...@@ -253,6 +253,7 @@ void ns_set_close_on_exec(sock_t); ...@@ -253,6 +253,7 @@ void ns_set_close_on_exec(sock_t);
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
int ns_hexdump(const void *buf, int len, char *dst, int dst_len); int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
#ifdef __cplusplus #ifdef __cplusplus
} }
...@@ -274,6 +275,8 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); ...@@ -274,6 +275,8 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
// //
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
//
// $Date: 2014-09-09 17:07:55 UTC $
#ifndef NS_MALLOC #ifndef NS_MALLOC
...@@ -288,6 +291,9 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); ...@@ -288,6 +291,9 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
#define NS_FREE free #define NS_FREE free
#endif #endif
#define NS_UDP_RECEIVE_BUFFER_SIZE 2000
#define NS_VPRINTF_BUFFER_SIZE 500
struct ctl_msg { struct ctl_msg {
ns_callback_t callback; ns_callback_t callback;
char message[1024 * 8]; char message[1024 * 8];
...@@ -338,6 +344,16 @@ void iobuf_remove(struct iobuf *io, size_t n) { ...@@ -338,6 +344,16 @@ void iobuf_remove(struct iobuf *io, size_t n) {
} }
} }
static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) {
if (nc->flags & NSF_UDP) {
long n = send(nc->sock, buf, len, 0);
DBG(("%p %d send %ld (%d)", nc, nc->sock, n, errno));
return n < 0 ? 0 : n;
} else {
return iobuf_append(&nc->send_iobuf, buf, len);
}
}
#ifndef NS_DISABLE_THREADS #ifndef NS_DISABLE_THREADS
void *ns_start_thread(void *(*f)(void *), void *p) { void *ns_start_thread(void *(*f)(void *), void *p) {
#ifdef _WIN32 #ifdef _WIN32
...@@ -361,15 +377,15 @@ void *ns_start_thread(void *(*f)(void *), void *p) { ...@@ -361,15 +377,15 @@ void *ns_start_thread(void *(*f)(void *), void *p) {
} }
#endif // NS_DISABLE_THREADS #endif // NS_DISABLE_THREADS
static void ns_add_conn(struct ns_server *server, struct ns_connection *c) { static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) {
c->next = server->active_connections; c->next = mgr->active_connections;
server->active_connections = c; mgr->active_connections = c;
c->prev = NULL; c->prev = NULL;
if (c->next != NULL) c->next->prev = c; if (c->next != NULL) c->next->prev = c;
} }
static void ns_remove_conn(struct ns_connection *conn) { static void ns_remove_conn(struct ns_connection *conn) {
if (conn->prev == NULL) conn->server->active_connections = conn->next; if (conn->prev == NULL) conn->mgr->active_connections = conn->next;
if (conn->prev) conn->prev->next = conn->next; if (conn->prev) conn->prev->next = conn->next;
if (conn->next) conn->next->prev = conn->prev; if (conn->next) conn->next->prev = conn->prev;
} }
...@@ -412,12 +428,12 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { ...@@ -412,12 +428,12 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
return len; return len;
} }
int ns_vprintf(struct ns_connection *conn, const char *fmt, va_list ap) { int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) {
char mem[2000], *buf = mem; char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem;
int len; int len;
if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
iobuf_append(&conn->send_iobuf, buf, len); ns_out(nc, buf, len);
} }
if (buf != mem && buf != NULL) { if (buf != mem && buf != NULL) {
free(buf); free(buf);
...@@ -450,7 +466,7 @@ static void hexdump(struct ns_connection *nc, const char *path, ...@@ -450,7 +466,7 @@ static void hexdump(struct ns_connection *nc, const char *path,
ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX", ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
dst, num_bytes); dst, num_bytes);
if (num_bytes > 0 && (buf = (char *) malloc(buf_size)) != NULL) { if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) {
ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) -
(ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size);
fprintf(fp, "%s", buf); fprintf(fp, "%s", buf);
...@@ -461,17 +477,14 @@ static void hexdump(struct ns_connection *nc, const char *path, ...@@ -461,17 +477,14 @@ static void hexdump(struct ns_connection *nc, const char *path,
} }
static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) { static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) {
if (conn->server->hexdump_file != NULL && ev != NS_POLL) { if (conn->mgr->hexdump_file != NULL && ev != NS_POLL) {
int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0; int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0;
hexdump(conn, conn->server->hexdump_file, len, ev); hexdump(conn, conn->mgr->hexdump_file, len, ev);
} }
if (conn->server->callback) conn->server->callback(conn, ev, p); if (conn->mgr->callback) conn->mgr->callback(conn, ev, p);
} }
static void ns_close_conn(struct ns_connection *conn) { static void ns_destroy_conn(struct ns_connection *conn) {
DBG(("%p %d", conn, conn->flags));
ns_call(conn, NS_CLOSE, NULL);
ns_remove_conn(conn);
closesocket(conn->sock); closesocket(conn->sock);
iobuf_free(&conn->recv_iobuf); iobuf_free(&conn->recv_iobuf);
iobuf_free(&conn->send_iobuf); iobuf_free(&conn->send_iobuf);
...@@ -479,10 +492,20 @@ static void ns_close_conn(struct ns_connection *conn) { ...@@ -479,10 +492,20 @@ static void ns_close_conn(struct ns_connection *conn) {
if (conn->ssl != NULL) { if (conn->ssl != NULL) {
SSL_free(conn->ssl); SSL_free(conn->ssl);
} }
if (conn->ssl_ctx != NULL) {
SSL_CTX_free(conn->ssl_ctx);
}
#endif #endif
NS_FREE(conn); NS_FREE(conn);
} }
static void ns_close_conn(struct ns_connection *conn) {
DBG(("%p %d", conn, conn->flags));
ns_call(conn, NS_CLOSE, NULL);
ns_remove_conn(conn);
ns_destroy_conn(conn);
}
void ns_set_close_on_exec(sock_t sock) { void ns_set_close_on_exec(sock_t sock) {
#ifdef _WIN32 #ifdef _WIN32
(void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
...@@ -543,10 +566,31 @@ int ns_socketpair(sock_t sp[2]) { ...@@ -543,10 +566,31 @@ int ns_socketpair(sock_t sp[2]) {
} }
#endif // NS_DISABLE_SOCKETPAIR #endif // NS_DISABLE_SOCKETPAIR
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128" // TODO(lsm): use non-blocking resolver
static int ns_parse_port_string(const char *str, union socket_address *sa) { static int ns_resolve2(const char *host, struct in_addr *ina) {
struct hostent *he;
if ((he = gethostbyname(host)) == NULL) {
DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
} else {
memcpy(ina, he->h_addr_list[0], sizeof(*ina));
return 1;
}
return 0;
}
// Resolve FDQN "host", store IP address in the "ip".
// Return > 0 (IP address length) on success.
int ns_resolve(const char *host, char *buf, size_t n) {
struct in_addr ad;
return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0;
}
// Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT]
static int ns_parse_address(const char *str, union socket_address *sa,
int *proto, int *use_ssl, char *cert, char *ca) {
unsigned int a, b, c, d, port; unsigned int a, b, c, d, port;
int len = 0; int n = 0, len = 0;
char host[200];
#ifdef NS_ENABLE_IPV6 #ifdef NS_ENABLE_IPV6
char buf[100]; char buf[100];
#endif #endif
...@@ -557,36 +601,57 @@ static int ns_parse_port_string(const char *str, union socket_address *sa) { ...@@ -557,36 +601,57 @@ static int ns_parse_port_string(const char *str, union socket_address *sa) {
memset(sa, 0, sizeof(*sa)); memset(sa, 0, sizeof(*sa));
sa->sin.sin_family = AF_INET; sa->sin.sin_family = AF_INET;
*proto = SOCK_STREAM;
*use_ssl = 0;
cert[0] = ca[0] = '\0';
if (memcmp(str, "ssl://", 6) == 0) {
str += 6;
*use_ssl = 1;
} else if (memcmp(str, "udp://", 6) == 0) {
str += 6;
*proto = SOCK_DGRAM;
} else if (memcmp(str, "tcp://", 6) == 0) {
str += 6;
}
if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { 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 // 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_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
sa->sin.sin_port = htons((uint16_t) port); sa->sin.sin_port = htons((uint16_t) port);
#ifdef NS_ENABLE_IPV6 #ifdef NS_ENABLE_IPV6
} else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 && } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 &&
inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
// IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
sa->sin6.sin6_family = AF_INET6; sa->sin6.sin6_family = AF_INET6;
sa->sin6.sin6_port = htons((uint16_t) port); sa->sin6.sin6_port = htons((uint16_t) port);
#endif #endif
} else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) {
sa->sin.sin_port = htons((uint16_t) port);
ns_resolve2(host, &sa->sin.sin_addr);
} else if (sscanf(str, "%u%n", &port, &len) == 1) { } else if (sscanf(str, "%u%n", &port, &len) == 1) {
// If only port is specified, bind to IPv4, INADDR_ANY // If only port is specified, bind to IPv4, INADDR_ANY
sa->sin.sin_port = htons((uint16_t) port); sa->sin.sin_port = htons((uint16_t) port);
} else {
port = 0; // Parsing failure. Make port invalid.
} }
return port <= 0xffff && str[len] == '\0'; if (*use_ssl && (sscanf(str + len, ":%99[^:]:%99[^:]%n", cert, ca, &n) == 2 ||
sscanf(str + len, ":%99[^:]%n", cert, &n) == 1)) {
len += n;
}
return port < 0xffff && str[len] == '\0' ? len : 0;
} }
// 'sa' must be an initialized address to bind to // 'sa' must be an initialized address to bind to
static sock_t ns_open_listening_socket(union socket_address *sa) { static sock_t ns_open_listening_socket(union socket_address *sa, int proto) {
socklen_t len = sizeof(*sa); socklen_t sa_len = (sa->sa.sa_family == AF_INET) ?
sizeof(sa->sin) : sizeof(sa->sin6);
sock_t sock = INVALID_SOCKET; sock_t sock = INVALID_SOCKET;
#ifndef _WIN32 #ifndef _WIN32
int on = 1; int on = 1;
#endif #endif
if ((sock = socket(sa->sa.sa_family, SOCK_STREAM, 6)) != INVALID_SOCKET && if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET &&
#ifndef _WIN32 #ifndef _WIN32
// SO_RESUSEADDR is not enabled on Windows because the semantics of // SO_RESUSEADDR is not enabled on Windows because the semantics of
// SO_REUSEADDR on UNIX and Windows is different. On Windows, // SO_REUSEADDR on UNIX and Windows is different. On Windows,
...@@ -596,12 +661,11 @@ static sock_t ns_open_listening_socket(union socket_address *sa) { ...@@ -596,12 +661,11 @@ static sock_t ns_open_listening_socket(union socket_address *sa) {
// scenarios. Therefore, SO_REUSEADDR was disabled on Windows. // scenarios. Therefore, SO_REUSEADDR was disabled on Windows.
!setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
#endif #endif
!bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ? !bind(sock, &sa->sa, sa_len) &&
sizeof(sa->sin) : sizeof(sa->sin6)) && (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) {
!listen(sock, SOMAXCONN)) {
ns_set_non_blocking_mode(sock); ns_set_non_blocking_mode(sock);
// In case port was set to 0, get the real port number // In case port was set to 0, get the real port number
(void) getsockname(sock, &sa->sa, &len); (void) getsockname(sock, &sa->sa, &sa_len);
} else if (sock != INVALID_SOCKET) { } else if (sock != INVALID_SOCKET) {
closesocket(sock); closesocket(sock);
sock = INVALID_SOCKET; sock = INVALID_SOCKET;
...@@ -610,80 +674,97 @@ static sock_t ns_open_listening_socket(union socket_address *sa) { ...@@ -610,80 +674,97 @@ static sock_t ns_open_listening_socket(union socket_address *sa) {
return sock; return sock;
} }
// Certificate generation script is at
// https://github.com/cesanta/net_skeleton/blob/master/examples/gen_certs.sh
int ns_set_ssl_ca_cert(struct ns_server *server, const char *cert) {
#ifdef NS_ENABLE_SSL #ifdef NS_ENABLE_SSL
STACK_OF(X509_NAME) *list = SSL_load_client_CA_file(cert); // Certificate generation script is at
if (cert != NULL && server->ssl_ctx != NULL && list != NULL) { // https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh
SSL_CTX_set_client_CA_list(server->ssl_ctx, list);
SSL_CTX_set_verify(server->ssl_ctx, SSL_VERIFY_PEER | static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) {
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); if (ctx == NULL) {
return -1;
} else if (cert == NULL || cert[0] == '\0') {
return 0; return 0;
} }
#endif SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
return server != NULL && cert == NULL ? 0 : -1; return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2;
} }
int ns_set_ssl_cert(struct ns_server *server, const char *cert) { static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) {
#ifdef NS_ENABLE_SSL if (ctx == NULL) {
if (cert != NULL &&
(server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
return -1; return -1;
} else if (SSL_CTX_use_certificate_file(server->ssl_ctx, cert, 1) == 0 || } else if (pem_file == NULL || pem_file[0] == '\0') {
SSL_CTX_use_PrivateKey_file(server->ssl_ctx, cert, 1) == 0) { return 0;
} else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 ||
SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) {
return -2; return -2;
} else { } else {
SSL_CTX_set_mode(server->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_use_certificate_chain_file(server->ssl_ctx, cert); SSL_CTX_use_certificate_chain_file(ctx, pem_file);
return 0; return 0;
} }
#endif
return server != NULL && cert == NULL ? 0 : -3;
} }
#endif // NS_ENABLE_SSL
int ns_bind(struct ns_server *server, const char *str) { struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) {
union socket_address sa; union socket_address sa;
ns_parse_port_string(str, &sa); struct ns_connection *nc = NULL;
if (server->listening_sock != INVALID_SOCKET) { int use_ssl, proto;
closesocket(server->listening_sock); char cert[100], ca_cert[100];
sock_t sock;
ns_parse_address(str, &sa, &proto, &use_ssl, cert, ca_cert);
if (use_ssl && cert[0] == '\0') return NULL;
if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) {
} else if ((nc = ns_add_sock(srv, sock, NULL)) == NULL) {
closesocket(sock);
} else {
nc->sa = sa;
nc->flags |= NSF_LISTENING;
nc->connection_data = data;
if (proto == SOCK_DGRAM) {
nc->flags |= NSF_UDP;
}
#ifdef NS_ENABLE_SSL
if (use_ssl) {
nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if (ns_use_cert(nc->ssl_ctx, cert) != 0 ||
ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) {
ns_close_conn(nc);
nc = NULL;
}
}
#endif
DBG(("%p sock %d/%d ssl %p %p", nc, sock, proto, nc->ssl_ctx, nc->ssl));
} }
server->listening_sock = ns_open_listening_socket(&sa);
return server->listening_sock == INVALID_SOCKET ? -1 :
(int) ntohs(sa.sin.sin_port);
}
return nc;
}
static struct ns_connection *accept_conn(struct ns_server *server) { static struct ns_connection *accept_conn(struct ns_connection *ls) {
struct ns_connection *c = NULL; struct ns_connection *c = NULL;
union socket_address sa; union socket_address sa;
socklen_t len = sizeof(sa); socklen_t len = sizeof(sa);
sock_t sock = INVALID_SOCKET; sock_t sock = INVALID_SOCKET;
// NOTE(lsm): on Windows, sock is always > FD_SETSIZE // NOTE(lsm): on Windows, sock is always > FD_SETSIZE
if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) { if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) {
} else if ((c = (struct ns_connection *) NS_MALLOC(sizeof(*c))) == NULL || } else if ((c = ns_add_sock(ls->mgr, sock, NULL)) == NULL) {
memset(c, 0, sizeof(*c)) == NULL) {
closesocket(sock); closesocket(sock);
#ifdef NS_ENABLE_SSL #ifdef NS_ENABLE_SSL
} else if (server->ssl_ctx != NULL && } else if (ls->ssl_ctx != NULL &&
((c->ssl = SSL_new(server->ssl_ctx)) == NULL || ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL ||
SSL_set_fd(c->ssl, sock) != 1)) { SSL_set_fd(c->ssl, sock) != 1)) {
DBG(("SSL error")); DBG(("SSL error"));
closesocket(sock); ns_close_conn(c);
free(c);
c = NULL; c = NULL;
#endif #endif
} else { } else {
ns_set_close_on_exec(sock); c->listener = ls;
ns_set_non_blocking_mode(sock);
c->server = server;
c->sock = sock;
c->flags |= NSF_ACCEPTED;
ns_add_conn(server, c);
ns_call(c, NS_ACCEPT, &sa); ns_call(c, NS_ACCEPT, &sa);
DBG(("%p %d %p %p", c, c->sock, c->ssl, server->ssl_ctx)); DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl));
} }
return c; return c;
...@@ -720,7 +801,7 @@ void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { ...@@ -720,7 +801,7 @@ void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
// Only Windoze Vista (and newer) have inet_ntop() // Only Windoze Vista (and newer) have inet_ntop()
strncpy(buf, inet_ntoa(sa.sin.sin_addr), len); strncpy(buf, inet_ntoa(sa.sin.sin_addr), len);
#else #else
inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf, len); inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len);
#endif #endif
} }
if (flags & 2) { if (flags & 2) {
...@@ -822,7 +903,7 @@ static void ns_read_from_socket(struct ns_connection *conn) { ...@@ -822,7 +903,7 @@ static void ns_read_from_socket(struct ns_connection *conn) {
} else } else
#endif #endif
{ {
while ((n = recv(conn->sock, buf, sizeof(buf), 0)) > 0) { while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) {
DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n)); DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n));
iobuf_append(&conn->recv_iobuf, buf, n); iobuf_append(&conn->recv_iobuf, buf, n);
ns_call(conn, NS_RECV, &n); ns_call(conn, NS_RECV, &n);
...@@ -851,7 +932,7 @@ static void ns_write_to_socket(struct ns_connection *conn) { ...@@ -851,7 +932,7 @@ static void ns_write_to_socket(struct ns_connection *conn) {
} }
} else } else
#endif #endif
{ n = send(conn->sock, io->buf, io->len, 0); } { n = (int) send(conn->sock, io->buf, io->len, 0); }
DBG(("%p %d -> %d bytes", conn, conn->flags, n)); DBG(("%p %d -> %d bytes", conn, conn->flags, n));
...@@ -864,7 +945,27 @@ static void ns_write_to_socket(struct ns_connection *conn) { ...@@ -864,7 +945,27 @@ static void ns_write_to_socket(struct ns_connection *conn) {
} }
int ns_send(struct ns_connection *conn, const void *buf, int len) { int ns_send(struct ns_connection *conn, const void *buf, int len) {
return iobuf_append(&conn->send_iobuf, buf, len); return (int) ns_out(conn, buf, len);
}
static void ns_handle_udp(struct ns_connection *ls) {
struct ns_connection nc;
char buf[NS_UDP_RECEIVE_BUFFER_SIZE];
int n;
socklen_t s_len = sizeof(nc.sa);
memset(&nc, 0, sizeof(nc));
n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len);
if (n <= 0) {
DBG(("%p recvfrom: %s", ls, strerror(errno)));
} else {
nc.recv_iobuf.buf = buf;
nc.recv_iobuf.len = nc.recv_iobuf.size = n;
nc.sock = ls->sock;
nc.mgr = ls->mgr;
DBG(("%p %d bytes received", ls, n));
ns_call(&nc, NS_RECV, &n);
}
} }
static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
...@@ -876,7 +977,7 @@ static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { ...@@ -876,7 +977,7 @@ static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
} }
} }
int ns_server_poll(struct ns_server *server, int milli) { int ns_mgr_poll(struct ns_mgr *mgr, int milli) {
struct ns_connection *conn, *tmp_conn; struct ns_connection *conn, *tmp_conn;
struct timeval tv; struct timeval tv;
fd_set read_set, write_set; fd_set read_set, write_set;
...@@ -884,15 +985,11 @@ int ns_server_poll(struct ns_server *server, int milli) { ...@@ -884,15 +985,11 @@ int ns_server_poll(struct ns_server *server, int milli) {
sock_t max_fd = INVALID_SOCKET; sock_t max_fd = INVALID_SOCKET;
time_t current_time = time(NULL); time_t current_time = time(NULL);
if (server->listening_sock == INVALID_SOCKET &&
server->active_connections == NULL) return 0;
FD_ZERO(&read_set); FD_ZERO(&read_set);
FD_ZERO(&write_set); FD_ZERO(&write_set);
ns_add_to_set(server->listening_sock, &read_set, &max_fd); ns_add_to_set(mgr->ctl[1], &read_set, &max_fd);
ns_add_to_set(server->ctl[1], &read_set, &max_fd);
for (conn = server->active_connections; conn != NULL; conn = tmp_conn) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
tmp_conn = conn->next; tmp_conn = conn->next;
ns_call(conn, NS_POLL, &current_time); ns_call(conn, NS_POLL, &current_time);
if (!(conn->flags & NSF_WANT_WRITE)) { if (!(conn->flags & NSF_WANT_WRITE)) {
...@@ -918,34 +1015,38 @@ int ns_server_poll(struct ns_server *server, int milli) { ...@@ -918,34 +1015,38 @@ int ns_server_poll(struct ns_server *server, int milli) {
// now to prevent last_io_time being set to the past. // now to prevent last_io_time being set to the past.
current_time = time(NULL); current_time = time(NULL);
// Accept new connections
if (server->listening_sock != INVALID_SOCKET &&
FD_ISSET(server->listening_sock, &read_set)) {
// We're not looping here, and accepting just one connection at
// a time. The reason is that eCos does not respect non-blocking
// flag on a listening socket and hangs in a loop.
if ((conn = accept_conn(server)) != NULL) {
conn->last_io_time = current_time;
}
}
// Read wakeup messages // Read wakeup messages
if (server->ctl[1] != INVALID_SOCKET && if (mgr->ctl[1] != INVALID_SOCKET &&
FD_ISSET(server->ctl[1], &read_set)) { FD_ISSET(mgr->ctl[1], &read_set)) {
struct ctl_msg ctl_msg; struct ctl_msg ctl_msg;
int len = recv(server->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
send(server->ctl[1], ctl_msg.message, 1, 0); send(mgr->ctl[1], ctl_msg.message, 1, 0);
if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
ns_iterate(server, ctl_msg.callback, ctl_msg.message); struct ns_connection *c;
for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) {
ctl_msg.callback(c, NS_POLL, ctl_msg.message);
}
} }
} }
for (conn = server->active_connections; conn != NULL; conn = tmp_conn) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
tmp_conn = conn->next; tmp_conn = conn->next;
if (FD_ISSET(conn->sock, &read_set)) { if (FD_ISSET(conn->sock, &read_set)) {
conn->last_io_time = current_time; if (conn->flags & NSF_LISTENING) {
ns_read_from_socket(conn); if (conn->flags & NSF_UDP) {
ns_handle_udp(conn);
} else {
// We're not looping here, and accepting just one connection at
// a time. The reason is that eCos does not respect non-blocking
// flag on a listening socket and hangs in a loop.
accept_conn(conn);
}
} else {
conn->last_io_time = current_time;
ns_read_from_socket(conn);
}
} }
if (FD_ISSET(conn->sock, &write_set)) { if (FD_ISSET(conn->sock, &write_set)) {
if (conn->flags & NSF_CONNECTING) { if (conn->flags & NSF_CONNECTING) {
ns_read_from_socket(conn); ns_read_from_socket(conn);
...@@ -957,7 +1058,7 @@ int ns_server_poll(struct ns_server *server, int milli) { ...@@ -957,7 +1058,7 @@ int ns_server_poll(struct ns_server *server, int milli) {
} }
} }
for (conn = server->active_connections; conn != NULL; conn = tmp_conn) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
tmp_conn = conn->next; tmp_conn = conn->next;
num_active_connections++; num_active_connections++;
if ((conn->flags & NSF_CLOSE_IMMEDIATELY) || if ((conn->flags & NSF_CLOSE_IMMEDIATELY) ||
...@@ -971,65 +1072,62 @@ int ns_server_poll(struct ns_server *server, int milli) { ...@@ -971,65 +1072,62 @@ int ns_server_poll(struct ns_server *server, int milli) {
return num_active_connections; return num_active_connections;
} }
struct ns_connection *ns_connect(struct ns_server *server, const char *host, struct ns_connection *ns_connect(struct ns_mgr *mgr,
int port, int use_ssl, void *param) { const char *address, void *param) {
sock_t sock = INVALID_SOCKET; sock_t sock = INVALID_SOCKET;
struct sockaddr_in sin; struct ns_connection *nc = NULL;
struct hostent *he = NULL; union socket_address sa;
struct ns_connection *conn = NULL; char cert[100], ca_cert[100];
int connect_ret_val; int connect_ret_val, use_ssl, proto;
(void) use_ssl;
if (host == NULL || (he = gethostbyname(host)) == NULL || ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert);
(sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
return NULL; return NULL;
} }
sin.sin_family = AF_INET;
sin.sin_port = htons((uint16_t) port);
sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
ns_set_non_blocking_mode(sock); ns_set_non_blocking_mode(sock);
connect_ret_val = connect(sock, &sa.sa, sizeof(sa.sin));
connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin)); if (connect_ret_val != 0 && ns_is_error(connect_ret_val)) {
if (ns_is_error(connect_ret_val)) {
closesocket(sock); closesocket(sock);
return NULL; return NULL;
} else if ((conn = (struct ns_connection *) } else if ((nc = ns_add_sock(mgr, sock, param)) == NULL) {
NS_MALLOC(sizeof(*conn))) == NULL) {
closesocket(sock); closesocket(sock);
return NULL; return NULL;
} }
memset(conn, 0, sizeof(*conn)); nc->sa = sa; // Essential, cause UDP conns will use sendto()
conn->server = server; if (proto == SOCK_DGRAM) {
conn->sock = sock; nc->flags = NSF_UDP;
conn->connection_data = param; } else {
conn->flags = NSF_CONNECTING; nc->flags = NSF_CONNECTING;
conn->last_io_time = time(NULL); }
#ifdef NS_ENABLE_SSL #ifdef NS_ENABLE_SSL
if (use_ssl && if (use_ssl) {
(conn->ssl = SSL_new(server->client_ssl_ctx)) != NULL) { if ((nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL ||
SSL_set_fd(conn->ssl, sock); ns_use_cert(nc->ssl_ctx, cert) != 0 ||
ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0 ||
(nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) {
ns_close_conn(nc);
return NULL;
} else {
SSL_set_fd(nc->ssl, sock);
}
} }
#endif #endif
ns_add_conn(server, conn); return nc;
DBG(("%p %s:%d %d %p", conn, host, port, conn->sock, conn->ssl));
return conn;
} }
struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) { struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, void *p) {
struct ns_connection *conn; struct ns_connection *conn;
if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
memset(conn, 0, sizeof(*conn)); memset(conn, 0, sizeof(*conn));
ns_set_non_blocking_mode(sock); ns_set_non_blocking_mode(sock);
ns_set_close_on_exec(sock);
conn->sock = sock; conn->sock = sock;
conn->connection_data = p; conn->connection_data = p;
conn->server = s; conn->mgr = s;
conn->last_io_time = time(NULL); conn->last_io_time = time(NULL);
ns_add_conn(s, conn); ns_add_conn(s, conn);
DBG(("%p %d", conn, sock)); DBG(("%p %d", conn, sock));
...@@ -1037,40 +1135,26 @@ struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) { ...@@ -1037,40 +1135,26 @@ struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
return conn; return conn;
} }
struct ns_connection *ns_next(struct ns_server *s, struct ns_connection *conn) { struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) {
return conn == NULL ? s->active_connections : conn->next; return conn == NULL ? s->active_connections : conn->next;
} }
void ns_iterate(struct ns_server *server, ns_callback_t cb, void *param) { void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) {
struct ns_connection *conn, *tmp_conn;
for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
tmp_conn = conn->next;
cb(conn, NS_POLL, param);
}
}
void ns_server_wakeup_ex(struct ns_server *server, ns_callback_t cb,
void *data, size_t len) {
struct ctl_msg ctl_msg; struct ctl_msg ctl_msg;
if (server->ctl[0] != INVALID_SOCKET && data != NULL && if (mgr->ctl[0] != INVALID_SOCKET && data != NULL &&
len < sizeof(ctl_msg.message)) { len < sizeof(ctl_msg.message)) {
ctl_msg.callback = cb; ctl_msg.callback = cb;
memcpy(ctl_msg.message, data, len); memcpy(ctl_msg.message, data, len);
send(server->ctl[0], (char *) &ctl_msg, send(mgr->ctl[0], (char *) &ctl_msg,
offsetof(struct ctl_msg, message) + len, 0); offsetof(struct ctl_msg, message) + len, 0);
recv(server->ctl[0], (char *) &len, 1, 0); recv(mgr->ctl[0], (char *) &len, 1, 0);
} }
} }
void ns_server_wakeup(struct ns_server *server) { void ns_mgr_init(struct ns_mgr *s, void *user_data, ns_callback_t cb) {
ns_server_wakeup_ex(server, NULL, (void *) "", 0);
}
void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET; s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
s->server_data = server_data; s->user_data = user_data;
s->callback = cb; s->callback = cb;
#ifdef _WIN32 #ifdef _WIN32
...@@ -1089,33 +1173,25 @@ void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) { ...@@ -1089,33 +1173,25 @@ void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
#ifdef NS_ENABLE_SSL #ifdef NS_ENABLE_SSL
{static int init_done; if (!init_done) { SSL_library_init(); init_done++; }} {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
s->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
#endif #endif
} }
void ns_server_free(struct ns_server *s) { void ns_mgr_free(struct ns_mgr *s) {
struct ns_connection *conn, *tmp_conn; struct ns_connection *conn, *tmp_conn;
DBG(("%p", s)); DBG(("%p", s));
if (s == NULL) return; if (s == NULL) return;
// Do one last poll, see https://github.com/cesanta/mongoose/issues/286 // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
ns_server_poll(s, 0); ns_mgr_poll(s, 0);
if (s->listening_sock != INVALID_SOCKET) closesocket(s->listening_sock);
if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]); if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]); if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET; s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
for (conn = s->active_connections; conn != NULL; conn = tmp_conn) { for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
tmp_conn = conn->next; tmp_conn = conn->next;
ns_close_conn(conn); ns_close_conn(conn);
} }
#ifdef NS_ENABLE_SSL
if (s->ssl_ctx != NULL) SSL_CTX_free(s->ssl_ctx);
if (s->client_ssl_ctx != NULL) SSL_CTX_free(s->client_ssl_ctx);
s->ssl_ctx = s->client_ssl_ctx = NULL;
#endif
} }
// net_skeleton end // net_skeleton end
#endif // NOEMBED_NET_SKELETON #endif // NOEMBED_NET_SKELETON
...@@ -1264,11 +1340,6 @@ enum { ...@@ -1264,11 +1340,6 @@ enum {
#endif #endif
#ifndef MONGOOSE_NO_SSI #ifndef MONGOOSE_NO_SSI
SSI_PATTERN, SSI_PATTERN,
#endif
#ifdef NS_ENABLE_SSL
SSL_CERTIFICATE,
SSL_CA_CERTIFICATE,
SSL_MITM_CERTS,
#endif #endif
URL_REWRITES, URL_REWRITES,
NUM_OPTIONS NUM_OPTIONS
...@@ -1299,7 +1370,7 @@ static const char *static_config_options[] = { ...@@ -1299,7 +1370,7 @@ static const char *static_config_options[] = {
#ifndef MONGOOSE_NO_FILESYSTEM #ifndef MONGOOSE_NO_FILESYSTEM
"hide_files_patterns", NULL, "hide_files_patterns", NULL,
"hexdump_file", NULL, "hexdump_file", NULL,
"index_files","index.html,index.htm,index.shtml,index.cgi,index.php,index.lp", "index_files","index.html,index.htm,index.shtml,index.cgi,index.php",
#endif #endif
"listening_port", NULL, "listening_port", NULL,
#ifndef _WIN32 #ifndef _WIN32
...@@ -1307,18 +1378,13 @@ static const char *static_config_options[] = { ...@@ -1307,18 +1378,13 @@ static const char *static_config_options[] = {
#endif #endif
#ifndef MONGOOSE_NO_SSI #ifndef MONGOOSE_NO_SSI
"ssi_pattern", "**.shtml$|**.shtm$", "ssi_pattern", "**.shtml$|**.shtm$",
#endif
#ifdef NS_ENABLE_SSL
"ssl_certificate", NULL,
"ssl_ca_certificate", NULL,
"ssl_mitm_certs", NULL,
#endif #endif
"url_rewrites", NULL, "url_rewrites", NULL,
NULL NULL
}; };
struct mg_server { struct mg_server {
struct ns_server ns_server; struct ns_mgr ns_mgr;
union socket_address lsa; // Listening socket address union socket_address lsa; // Listening socket address
mg_handler_t event_handler; mg_handler_t event_handler;
char *config_options[NUM_OPTIONS]; char *config_options[NUM_OPTIONS];
...@@ -1420,6 +1486,32 @@ void *mg_start_thread(void *(*f)(void *), void *p) { ...@@ -1420,6 +1486,32 @@ void *mg_start_thread(void *(*f)(void *), void *p) {
} }
#endif // MONGOOSE_NO_THREADS #endif // MONGOOSE_NO_THREADS
#ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
int offset) {
HANDLE fh = (HANDLE) _get_osfhandle(fd);
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
CloseHandle(mh);
return p;
}
#define munmap(x, y) UnmapViewOfFile(x)
#define MAP_FAILED NULL
#define MAP_PRIVATE 0
#define PROT_READ 0
#else
#include <sys/mman.h>
#endif
void *mg_mmap(FILE *fp, size_t size) {
void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
return p == MAP_FAILED ? NULL : p;
}
void mg_munmap(void *p, size_t size) {
munmap(p, size);
}
#if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM) #if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM)
// Encode 'path' which is assumed UTF-8 string, into UNICODE string. // Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length. // wbuf and wbuf_len is a target buffer and its length.
...@@ -2093,7 +2185,7 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) { ...@@ -2093,7 +2185,7 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
if (start_process(conn->server->config_options[CGI_INTERPRETER], if (start_process(conn->server->config_options[CGI_INTERPRETER],
prog, blk.buf, blk.vars, dir, fds[1]) != 0) { prog, blk.buf, blk.vars, dir, fds[1]) != 0) {
conn->endpoint_type = EP_CGI; conn->endpoint_type = EP_CGI;
conn->endpoint.nc = ns_add_sock(&conn->server->ns_server, fds[0], conn); conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], conn);
conn->endpoint.nc->flags |= MG_CGI_CONN; conn->endpoint.nc->flags |= MG_CGI_CONN;
ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1); ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1);
conn->mg_conn.status_code = 200; conn->mg_conn.status_code = 200;
...@@ -4160,7 +4252,6 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) { ...@@ -4160,7 +4252,6 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
SSL_CTX *ctx; SSL_CTX *ctx;
DBG(("%p MITM", conn)); DBG(("%p MITM", conn));
SSL_library_init();
if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0; if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
SSL_CTX_use_certificate_file(ctx, cert, 1); SSL_CTX_use_certificate_file(ctx, cert, 1);
...@@ -4170,7 +4261,7 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) { ...@@ -4170,7 +4261,7 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
// When clear-text reply is pushed to client, switch to SSL mode. // When clear-text reply is pushed to client, switch to SSL mode.
// TODO(lsm): check for send() failure // TODO(lsm): check for send() failure
send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0); send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
DBG(("%p %lu %d SEND", c, (unsigned long)sizeof(ok) - 1, n)); //DBG(("%p %lu %d SEND", c, (unsigned long) sizeof(ok) - 1, n));
conn->ns_conn->send_iobuf.len = 0; conn->ns_conn->send_iobuf.len = 0;
conn->endpoint_type = EP_USER; // To keep-alive in close_local_endpoint() conn->endpoint_type = EP_USER; // To keep-alive in close_local_endpoint()
close_local_endpoint(conn); // Clean up current CONNECT request close_local_endpoint(conn); // Clean up current CONNECT request
...@@ -4182,12 +4273,12 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) { ...@@ -4182,12 +4273,12 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
} }
#endif #endif
int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) { int mg_forward(struct mg_connection *c, const char *addr) {
static const char ok[] = "HTTP/1.1 200 OK\r\n\r\n";
struct connection *conn = MG_CONN_2_CONN(c); struct connection *conn = MG_CONN_2_CONN(c);
struct ns_server *server = &conn->server->ns_server;
struct ns_connection *pc; struct ns_connection *pc;
if ((pc = ns_connect(server, host, port, ssl, conn)) == NULL) { if ((pc = ns_connect(&conn->server->ns_mgr, addr, conn)) == NULL) {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
return 0; return 0;
} }
...@@ -4196,12 +4287,12 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) { ...@@ -4196,12 +4287,12 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
pc->flags |= MG_PROXY_CONN; pc->flags |= MG_PROXY_CONN;
conn->endpoint_type = EP_PROXY; conn->endpoint_type = EP_PROXY;
conn->endpoint.nc = pc; conn->endpoint.nc = pc;
DBG(("%p [%s] [%s:%d] -> %p %p", DBG(("%p [%s] [%s] -> %p %p", conn, c->uri, addr, pc, conn->ns_conn->ssl));
conn, c->uri, host, port, pc, conn->ns_conn->ssl));
if (strcmp(c->request_method, "CONNECT") == 0) { if (strcmp(c->request_method, "CONNECT") == 0) {
// For CONNECT request, reply with 200 OK. Tunnel is established. // For CONNECT request, reply with 200 OK. Tunnel is established.
mg_printf(c, "%s", "HTTP/1.1 200 OK\r\n\r\n"); // TODO(lsm): check for send() failure
(void) send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
} else { } else {
// Strip "http://host:port" part from the URI // Strip "http://host:port" part from the URI
if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7; if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7;
...@@ -4212,7 +4303,7 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) { ...@@ -4212,7 +4303,7 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
} }
static void proxify_connection(struct connection *conn) { static void proxify_connection(struct connection *conn) {
char proto[10], host[500], cert[500]; char proto[10], host[500], cert[500], addr[1000];
unsigned short port = 80; unsigned short port = 80;
struct mg_connection *c = &conn->mg_conn; struct mg_connection *c = &conn->mg_conn;
int n = 0; int n = 0;
...@@ -4225,25 +4316,9 @@ static void proxify_connection(struct connection *conn) { ...@@ -4225,25 +4316,9 @@ static void proxify_connection(struct connection *conn) {
n = 0; n = 0;
} }
#ifdef NS_ENABLE_SSL snprintf(addr, sizeof(addr), "%s://%s:%hu",
// Find out whether we should be in the MITM mode conn->ns_conn->ssl != NULL ? "ssl" : "tcp", host, port);
{ if (n <= 0 || !mg_forward(c, addr)) {
const char *certs = conn->server->config_options[SSL_MITM_CERTS];
int host_len = strlen(host);
struct vec a, b;
while (conn->ns_conn->ssl == NULL && port != 80 &&
(certs = next_option(certs, &a, &b)) != NULL) {
if (a.len != host_len || mg_strncasecmp(a.ptr, host, a.len)) continue;
snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr);
mg_terminate_ssl(&conn->mg_conn, cert);
return;
}
}
#endif
if (n > 0 && mg_forward(c, host, port, conn->ns_conn->ssl != NULL)) {
} else {
conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
} }
} }
...@@ -4535,12 +4610,11 @@ static void process_response(struct connection *conn) { ...@@ -4535,12 +4610,11 @@ static void process_response(struct connection *conn) {
} }
} }
struct mg_connection *mg_connect(struct mg_server *server, const char *host, struct mg_connection *mg_connect(struct mg_server *server, const char *addr) {
int port, int use_ssl) {
struct ns_connection *nsconn; struct ns_connection *nsconn;
struct connection *conn; struct connection *conn;
nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL); nsconn = ns_connect(&server->ns_mgr, addr, NULL);
if (nsconn == NULL) return 0; if (nsconn == NULL) return 0;
if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) { if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
...@@ -4555,7 +4629,7 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *host, ...@@ -4555,7 +4629,7 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *host,
conn->server = server; conn->server = server;
conn->endpoint_type = EP_CLIENT; conn->endpoint_type = EP_CLIENT;
//conn->handler = handler; //conn->handler = handler;
conn->mg_conn.server_param = server->ns_server.server_data; conn->mg_conn.server_param = server->ns_mgr.user_data;
conn->ns_conn->flags = NSF_CONNECTING; conn->ns_conn->flags = NSF_CONNECTING;
return &conn->mg_conn; return &conn->mg_conn;
...@@ -4684,7 +4758,7 @@ static void transfer_file_data(struct connection *conn) { ...@@ -4684,7 +4758,7 @@ static void transfer_file_data(struct connection *conn) {
} }
int mg_poll_server(struct mg_server *server, int milliseconds) { int mg_poll_server(struct mg_server *server, int milliseconds) {
return ns_server_poll(&server->ns_server, milliseconds); return ns_mgr_poll(&server->ns_mgr, milliseconds);
} }
void mg_destroy_server(struct mg_server **server) { void mg_destroy_server(struct mg_server **server) {
...@@ -4692,7 +4766,7 @@ void mg_destroy_server(struct mg_server **server) { ...@@ -4692,7 +4766,7 @@ void mg_destroy_server(struct mg_server **server) {
struct mg_server *s = *server; struct mg_server *s = *server;
int i; int i;
ns_server_free(&s->ns_server); ns_mgr_free(&s->ns_mgr);
for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) { for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
free(s->config_options[i]); // It is OK to free(NULL) free(s->config_options[i]); // It is OK to free(NULL)
} }
...@@ -4701,34 +4775,15 @@ void mg_destroy_server(struct mg_server **server) { ...@@ -4701,34 +4775,15 @@ void mg_destroy_server(struct mg_server **server) {
} }
} }
struct mg_iterator {
mg_handler_t cb;
void *param;
};
static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
if (ev == NS_POLL) {
struct mg_iterator *it = (struct mg_iterator *) param;
struct connection *c = (struct connection *) nsconn->connection_data;
if (c != NULL) c->mg_conn.callback_param = it->param;
it->cb(&c->mg_conn, MG_POLL);
}
}
struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) { struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
struct connection *conn = MG_CONN_2_CONN(c); struct connection *conn = MG_CONN_2_CONN(c);
struct ns_connection *nc = ns_next(&s->ns_server, struct ns_connection *nc = ns_next(&s->ns_mgr,
c == NULL ? NULL : conn->ns_conn); c == NULL ? NULL : conn->ns_conn);
if (nc != NULL && nc->connection_data != NULL) {
return nc == NULL ? NULL : return & ((struct connection *) nc->connection_data)->mg_conn;
& ((struct connection *) nc->connection_data)->mg_conn; } else {
} return NULL;
}
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb,
void *param) {
struct mg_iterator it = { cb, param };
ns_iterate(&server->ns_server, iter, &it);
} }
static int get_var(const char *data, size_t data_len, const char *name, static int get_var(const char *data, size_t data_len, const char *name,
...@@ -4886,18 +4941,18 @@ const char *mg_set_option(struct mg_server *server, const char *name, ...@@ -4886,18 +4941,18 @@ const char *mg_set_option(struct mg_server *server, const char *name,
DBG(("%s [%s]", name, *v)); DBG(("%s [%s]", name, *v));
if (ind == LISTENING_PORT) { if (ind == LISTENING_PORT) {
int port = ns_bind(&server->ns_server, value); struct ns_connection *c = ns_bind(&server->ns_mgr, value, NULL);
if (port < 0) { if (c == NULL) {
error_msg = "Cannot bind to port"; error_msg = "Cannot bind to port";
} else { } else {
char buf[100]; char buf[100];
ns_sock_to_str(server->ns_server.listening_sock, buf, sizeof(buf), 2); ns_sock_to_str(c->sock, buf, sizeof(buf), 2);
free(*v); free(*v);
*v = mg_strdup(buf); *v = mg_strdup(buf);
} }
#ifndef MONGOOSE_NO_FILESYSTEM #ifndef MONGOOSE_NO_FILESYSTEM
} else if (ind == HEXDUMP_FILE) { } else if (ind == HEXDUMP_FILE) {
server->ns_server.hexdump_file = *v; server->ns_mgr.hexdump_file = *v;
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
} else if (ind == RUN_AS_USER) { } else if (ind == RUN_AS_USER) {
...@@ -4909,21 +4964,6 @@ const char *mg_set_option(struct mg_server *server, const char *name, ...@@ -4909,21 +4964,6 @@ const char *mg_set_option(struct mg_server *server, const char *name,
} else if (setuid(pw->pw_uid) != 0) { } else if (setuid(pw->pw_uid) != 0) {
error_msg = "setuid() failed"; error_msg = "setuid() failed";
} }
#endif
#ifdef NS_ENABLE_SSL
} else if (ind == SSL_CERTIFICATE) {
int res = ns_set_ssl_cert(&server->ns_server, value);
if (res == -2) {
error_msg = "Cannot load PEM";
} else if (res == -3) {
error_msg = "SSL not enabled";
} else if (res == -1) {
error_msg = "SSL_CTX_new() failed";
}
} else if (ind == SSL_CA_CERTIFICATE) {
if (ns_set_ssl_ca_cert(&server->ns_server, value) != 0) {
error_msg = "Error setting CA cert";
}
#endif #endif
} }
...@@ -4943,7 +4983,7 @@ static void set_ips(struct ns_connection *nc, int is_rem) { ...@@ -4943,7 +4983,7 @@ static void set_ips(struct ns_connection *nc, int is_rem) {
} }
static void on_accept(struct ns_connection *nc, union socket_address *sa) { static void on_accept(struct ns_connection *nc, union socket_address *sa) {
struct mg_server *server = (struct mg_server *) nc->server; struct mg_server *server = (struct mg_server *) nc->mgr;
struct connection *conn; struct connection *conn;
if (!check_acl(server->config_options[ACCESS_CONTROL_LIST], if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
...@@ -4957,7 +4997,7 @@ static void on_accept(struct ns_connection *nc, union socket_address *sa) { ...@@ -4957,7 +4997,7 @@ static void on_accept(struct ns_connection *nc, union socket_address *sa) {
// Initialize the rest of connection attributes // Initialize the rest of connection attributes
conn->server = server; conn->server = server;
conn->mg_conn.server_param = nc->server->server_data; conn->mg_conn.server_param = nc->mgr->user_data;
set_ips(nc, 1); set_ips(nc, 1);
set_ips(nc, 0); set_ips(nc, 0);
} }
...@@ -5007,7 +5047,7 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { ...@@ -5007,7 +5047,7 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
if (conn != NULL) { if (conn != NULL) {
conn->num_bytes_recv += * (int *) p; conn->num_bytes_recv += * (int *) p;
} }
if (nc->flags & NSF_ACCEPTED) { if (nc->listener != NULL) {
on_recv_data(conn); on_recv_data(conn);
#ifndef MONGOOSE_NO_CGI #ifndef MONGOOSE_NO_CGI
} else if (nc->flags & MG_CGI_CONN) { } else if (nc->flags & MG_CGI_CONN) {
...@@ -5110,23 +5150,25 @@ void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb, ...@@ -5110,23 +5150,25 @@ void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
va_end(ap); va_end(ap);
// "len + 1" is to include terminating \0 in the message // "len + 1" is to include terminating \0 in the message
ns_server_wakeup_ex(&server->ns_server, iter2, buf, len + 1); ns_broadcast(&server->ns_mgr, iter2, buf, len + 1);
} }
void mg_wakeup_server(struct mg_server *server) { void mg_wakeup_server(struct mg_server *server) {
ns_server_wakeup_ex(&server->ns_server, NULL, (void *) "", 0); ns_broadcast(&server->ns_mgr, NULL, (void *) "", 0);
} }
#if 0
void mg_set_listening_socket(struct mg_server *server, int sock) { void mg_set_listening_socket(struct mg_server *server, int sock) {
if (server->ns_server.listening_sock != INVALID_SOCKET) { if (server->ns_mgr.listening_sock != INVALID_SOCKET) {
closesocket(server->ns_server.listening_sock); closesocket(server->ns_mgr.listening_sock);
} }
server->ns_server.listening_sock = (sock_t) sock; server->ns_mgr.listening_sock = (sock_t) sock;
} }
int mg_get_listening_socket(struct mg_server *server) { int mg_get_listening_socket(struct mg_server *server) {
return server->ns_server.listening_sock; return server->ns_mgr.listening_sock;
} }
#endif
const char *mg_get_option(const struct mg_server *server, const char *name) { const char *mg_get_option(const struct mg_server *server, const char *name) {
const char **opts = (const char **) server->config_options; const char **opts = (const char **) server->config_options;
...@@ -5136,33 +5178,8 @@ const char *mg_get_option(const struct mg_server *server, const char *name) { ...@@ -5136,33 +5178,8 @@ const char *mg_get_option(const struct mg_server *server, const char *name) {
struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) { struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server)); struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
ns_server_init(&server->ns_server, server_data, mg_ev_handler); ns_mgr_init(&server->ns_mgr, server_data, mg_ev_handler);
set_default_option_values(server->config_options); set_default_option_values(server->config_options);
server->event_handler = handler; server->event_handler = handler;
return server; return server;
} }
#ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
int offset) {
HANDLE fh = (HANDLE) _get_osfhandle(fd);
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
CloseHandle(mh);
return p;
}
#define munmap(x, y) UnmapViewOfFile(x)
#define MAP_FAILED NULL
#define MAP_PRIVATE 0
#define PROT_READ 0
#else
#include <sys/mman.h>
#endif
void *mg_mmap(FILE *fp, size_t size) {
return mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
}
void mg_munmap(void *p, size_t size) {
munmap(p, size);
}
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
// Alternatively, you can license this library under a commercial // Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/>. // license, as set out in <http://cesanta.com/>.
// //
// $Date: 2014-09-01 19:53:26 UTC $ // $Date: 2014-09-09 17:07:55 UTC $
#ifndef MONGOOSE_HEADER_INCLUDED #ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED
...@@ -55,7 +55,7 @@ struct mg_connection { ...@@ -55,7 +55,7 @@ struct mg_connection {
int wsbits; // First byte of the websocket frame int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler() void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data void *connection_param; // Placeholder for connection-specific data
void *callback_param; // Needed by mg_iterate_over_connections() void *callback_param;
}; };
struct mg_server; // Opaque structure describing server instance struct mg_server; // Opaque structure describing server instance
...@@ -95,11 +95,10 @@ const char **mg_get_valid_option_names(void); ...@@ -95,11 +95,10 @@ const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name); const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_set_listening_socket(struct mg_server *, int sock); void mg_set_listening_socket(struct mg_server *, int sock);
int mg_get_listening_socket(struct mg_server *); int mg_get_listening_socket(struct mg_server *);
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
struct mg_connection *mg_next(struct mg_server *, struct mg_connection *); struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
void mg_wakeup_server(struct mg_server *); void mg_wakeup_server(struct mg_server *);
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...); void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
struct mg_connection *mg_connect(struct mg_server *, const char *, int, int); struct mg_connection *mg_connect(struct mg_server *, const char *);
// Connection management functions // Connection management functions
void mg_send_status(struct mg_connection *, int status_code); void mg_send_status(struct mg_connection *, int status_code);
...@@ -127,6 +126,7 @@ int mg_parse_multipart(const char *buf, int buf_len, ...@@ -127,6 +126,7 @@ int mg_parse_multipart(const char *buf, int buf_len,
char *file_name, int file_name_len, char *file_name, int file_name_len,
const char **data, int *data_len); const char **data, int *data_len);
// Utility functions // Utility functions
void *mg_start_thread(void *(*func)(void *), void *param); void *mg_start_thread(void *(*func)(void *), void *param);
char *mg_md5(char buf[33], ...); char *mg_md5(char buf[33], ...);
...@@ -134,7 +134,7 @@ int mg_authorize_digest(struct mg_connection *c, FILE *fp); ...@@ -134,7 +134,7 @@ int mg_authorize_digest(struct mg_connection *c, FILE *fp);
int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len); int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int); int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
int mg_terminate_ssl(struct mg_connection *c, const char *cert); int mg_terminate_ssl(struct mg_connection *c, const char *cert);
int mg_forward(struct mg_connection *, const char *host, int port, int use_ssl); int mg_forward(struct mg_connection *c, const char *addr);
void *mg_mmap(FILE *fp, size_t size); void *mg_mmap(FILE *fp, size_t size);
void mg_munmap(void *p, size_t size); void mg_munmap(void *p, size_t size);
...@@ -147,7 +147,6 @@ struct mg_expansion { ...@@ -147,7 +147,6 @@ struct mg_expansion {
void mg_template(struct mg_connection *, const char *text, void mg_template(struct mg_connection *, const char *text,
struct mg_expansion *expansions); struct mg_expansion *expansions);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif // __cplusplus #endif // __cplusplus
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment