Skip to content
Snippets Groups Projects
http.c 61.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		timeout = session_keep_alive;
    		if (timeout <= 0) {
    			/* Persistent connections not enabled. */
    			break;
    		}
    	}
    
    done:
    	ast_atomic_fetchadd_int(&session_count, -1);
    
    
    		ast_debug(1, "HTTP closing session.  Top level\n");
    
    /*!
     * \brief Add a new URI redirect
     * The entries in the redirect list are sorted by length, just like the list
     * of URI handlers.
     */
    static void add_redirect(const char *value)
    {
    	char *target, *dest;
    	struct http_uri_redirect *redirect, *cur;
    
    	unsigned int target_len;
    	unsigned int total_len;
    
    	dest = ast_skip_blanks(dest);
    	target = strsep(&dest, " ");
    	target = ast_skip_blanks(target);
    	target = strsep(&target, " "); /* trim trailing whitespace */
    
    
    	if (!dest) {
    		ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
    		return;
    	}
    
    
    	target_len = strlen(target) + 1;
    	total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
    
    	if (!(redirect = ast_calloc(1, total_len))) {
    
    	redirect->dest = redirect->target + target_len;
    	strcpy(redirect->target, target);
    	strcpy(redirect->dest, dest);
    
    	AST_RWLIST_WRLOCK(&uri_redirects);
    
    	target_len--; /* So we can compare directly with strlen() */
    
    	if (AST_RWLIST_EMPTY(&uri_redirects)
    		|| strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
    
    		AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
    		AST_RWLIST_UNLOCK(&uri_redirects);
    
    	AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
    
    		if (AST_RWLIST_NEXT(cur, entry)
    			&& strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
    
    			AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
    
    			AST_RWLIST_UNLOCK(&uri_redirects);
    
    	AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
    
    	AST_RWLIST_UNLOCK(&uri_redirects);
    
    static int __ast_http_load(int reload)
    {
    	struct ast_config *cfg;
    	struct ast_variable *v;
    	int enabled=0;
    
    	char newprefix[MAX_PREFIX] = "";
    
    	char server_name[MAX_SERVER_NAME_LENGTH];
    
    	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
    
    	uint32_t bindport = DEFAULT_PORT;
    
    	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
    
    	int num_addrs = 0;
    
    	int http_tls_was_enabled = 0;
    
    	cfg = ast_config_load2("http.conf", "http", config_flags);
    
    	if (!cfg || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
    
    	http_tls_was_enabled = (reload && http_tls_cfg.enabled);
    
    
    
    	ast_free(http_tls_cfg.certfile);
    
    	http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
    
    	ast_free(http_tls_cfg.capath);
    	http_tls_cfg.capath = ast_strdup("");
    
    	ast_free(http_tls_cfg.pvtfile);
    
    David Vossel's avatar
    David Vossel committed
    	http_tls_cfg.pvtfile = ast_strdup("");
    
    
    	/* Apply modern intermediate settings according to the Mozilla OpSec team as of July 30th, 2015 but disable TLSv1 */
    	ast_set_flag(&http_tls_cfg.flags, AST_SSL_DISABLE_TLSV1 | AST_SSL_SERVER_CIPHER_ORDER);
    
    
    	ast_free(http_tls_cfg.cipher);
    
    	http_tls_cfg.cipher = ast_strdup("ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA");
    
    	AST_RWLIST_WRLOCK(&uri_redirects);
    
    	while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
    
    	AST_RWLIST_UNLOCK(&uri_redirects);
    
    	ast_sockaddr_setnull(&https_desc.local_address);
    
    
    	session_limit = DEFAULT_SESSION_LIMIT;
    	session_inactivity = DEFAULT_SESSION_INACTIVITY;
    
    	session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
    
    
    	snprintf(server_name, sizeof(server_name), "Asterisk/%s", ast_get_version());
    
    
    	v = ast_variable_browse(cfg, "general");
    	for (; v; v = v->next) {
    		/* read tls config options while preventing unsupported options from being set */
    		if (strcasecmp(v->name, "tlscafile")
    			&& strcasecmp(v->name, "tlscapath")
    			&& strcasecmp(v->name, "tlscadir")
    			&& strcasecmp(v->name, "tlsverifyclient")
    			&& strcasecmp(v->name, "tlsdontverifyserver")
    			&& strcasecmp(v->name, "tlsclientmethod")
    			&& strcasecmp(v->name, "sslclientmethod")
    			&& !ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
    			continue;
    		}
    
    		if (!strcasecmp(v->name, "servername")) {
    			if (!ast_strlen_zero(v->value)) {
    				ast_copy_string(server_name, v->value, sizeof(server_name));
    			} else {
    				server_name[0] = '\0';
    			}
    		} else if (!strcasecmp(v->name, "enabled")) {
    
    			enabled = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "enablestatic")) {
    			newenablestatic = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "bindport")) {
    			if (ast_parse_arg(v->value, PARSE_UINT32 | PARSE_IN_RANGE | PARSE_DEFAULT,
    				&bindport, DEFAULT_PORT, 0, 65535)) {
    				ast_log(LOG_WARNING, "Invalid port %s specified. Using default port %" PRId32 "\n",
    					v->value, DEFAULT_PORT);
    
    		} else if (!strcasecmp(v->name, "bindaddr")) {
    			if (!(num_addrs = ast_sockaddr_resolve(&addrs, v->value, 0, AST_AF_UNSPEC))) {
    				ast_log(LOG_WARNING, "Invalid bind address %s\n", v->value);
    			}
    		} else if (!strcasecmp(v->name, "prefix")) {
    			if (!ast_strlen_zero(v->value)) {
    				newprefix[0] = '/';
    				ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
    
    				newprefix[0] = '\0';
    
    		} else if (!strcasecmp(v->name, "redirect")) {
    			add_redirect(v->value);
    		} else if (!strcasecmp(v->name, "sessionlimit")) {
    			if (ast_parse_arg(v->value, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE,
    				&session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
    				ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
    					v->name, v->value, v->lineno);
    			}
    		} else if (!strcasecmp(v->name, "session_inactivity")) {
    			if (ast_parse_arg(v->value, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE,
    				&session_inactivity, DEFAULT_SESSION_INACTIVITY, 1, INT_MAX)) {
    				ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
    					v->name, v->value, v->lineno);
    			}
    		} else if (!strcasecmp(v->name, "session_keep_alive")) {
    			if (sscanf(v->value, "%30d", &session_keep_alive) != 1
    				|| session_keep_alive < 0) {
    				session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
    				ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
    					v->name, v->value, v->lineno);
    			}
    		} else {
    			ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
    
    	ast_config_destroy(cfg);
    
    
    	if (strcmp(prefix, newprefix)) {
    
    		ast_copy_string(prefix, newprefix, sizeof(prefix));
    
    
    	ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
    
    
    	if (num_addrs && enabled) {
    		int i;
    		for (i = 0; i < num_addrs; ++i) {
    			ast_sockaddr_copy(&http_desc.local_address, &addrs[i]);
    			if (!ast_sockaddr_port(&http_desc.local_address)) {
    				ast_sockaddr_set_port(&http_desc.local_address, bindport);
    			}
    			ast_tcptls_server_start(&http_desc);
    			if (http_desc.accept_fd == -1) {
    				ast_log(LOG_WARNING, "Failed to start HTTP server for address %s\n", ast_sockaddr_stringify(&addrs[i]));
    				ast_sockaddr_setnull(&http_desc.local_address);
    			} else {
    				ast_verb(1, "Bound HTTP server to address %s\n", ast_sockaddr_stringify(&addrs[i]));
    				break;
    			}
    		}
    		/* When no specific TLS bindaddr is specified, we just use
    		 * the non-TLS bindaddress here.
    		 */
    		if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
    
    			ast_sockaddr_copy(&https_desc.local_address, &http_desc.local_address);
    
    			/* Of course, we can't use the same port though.
    			 * Since no bind address was specified, we just use the
    			 * default TLS port
    			 */
    			ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
    		}
    	}
    
    	if (http_tls_was_enabled && !http_tls_cfg.enabled) {
    		ast_tcptls_server_stop(&https_desc);
    	} else if (http_tls_cfg.enabled && !ast_sockaddr_isnull(&https_desc.local_address)) {
    
    		/* We can get here either because a TLS-specific address was specified
    		 * or because we copied the non-TLS address here. In the case where
    		 * we read an explicit address from the config, there may have been
    		 * no port specified, so we'll just use the default TLS port.
    		 */
    		if (!ast_sockaddr_port(&https_desc.local_address)) {
    			ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);
    		}
    		if (ast_ssl_setup(https_desc.tls_cfg)) {
    			ast_tcptls_server_start(&https_desc);
    		}
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "http show status";
    
    Jason Parker's avatar
    Jason Parker committed
    			"Usage: http show status\n"
    			"       Lists status of internal HTTP engine\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc != 3) {
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SHOWUSAGE;
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd, "HTTP Server Status:\n");
    	ast_cli(a->fd, "Prefix: %s\n", prefix);
    
    	ast_cli(a->fd, "Server: %s\n", http_server_name);
    
    	if (ast_sockaddr_isnull(&http_desc.old_address)) {
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "Server Disabled\n\n");
    
    		ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",
    			ast_sockaddr_stringify(&http_desc.old_address));
    
    		if (http_tls_cfg.enabled) {
    
    			ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",
    				ast_sockaddr_stringify(&https_desc.old_address));
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd, "Enabled URI's:\n");
    
    	AST_RWLIST_RDLOCK(&uris);
    	if (AST_RWLIST_EMPTY(&uris)) {
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "None.\n");
    
    		AST_RWLIST_TRAVERSE(&uris, urih, entry)
    			ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
    
    	}
    	AST_RWLIST_UNLOCK(&uris);
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd, "\nEnabled Redirects:\n");
    
    	AST_RWLIST_RDLOCK(&uri_redirects);
    
    	AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
    
    	if (AST_RWLIST_EMPTY(&uri_redirects)) {
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "  None.\n");
    
    	AST_RWLIST_UNLOCK(&uri_redirects);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    static struct ast_cli_entry cli_http[] = {
    
    	AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
    
    static void http_shutdown(void)
    {
    
    David M. Lee's avatar
    David M. Lee committed
    	struct http_uri_redirect *redirect;
    
    	ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));
    
    David M. Lee's avatar
    David M. Lee committed
    
    	ast_tcptls_server_stop(&http_desc);
    	if (http_tls_cfg.enabled) {
    		ast_tcptls_server_stop(&https_desc);
    	}
    	ast_free(http_tls_cfg.certfile);
    
    	ast_free(http_tls_cfg.capath);
    
    David M. Lee's avatar
    David M. Lee committed
    	ast_free(http_tls_cfg.pvtfile);
    	ast_free(http_tls_cfg.cipher);
    
    	ast_http_uri_unlink(&statusuri);
    	ast_http_uri_unlink(&staticuri);
    
    	AST_RWLIST_WRLOCK(&uri_redirects);
    	while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
    		ast_free(redirect);
    	}
    	AST_RWLIST_UNLOCK(&uri_redirects);
    
    int ast_http_init(void)
    {
    	ast_http_uri_link(&statusuri);
    
    	ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
    
    	ast_register_cleanup(http_shutdown);