Newer
Older
timeout = session_keep_alive;
if (timeout <= 0) {
/* Persistent connections not enabled. */
break;
}
}
done:
ast_atomic_fetchadd_int(&session_count, -1);
if (ser->f) {
ast_debug(1, "HTTP closing session. Top level\n");
Richard Mudgett
committed
ast_tcptls_close_session_file(ser);
Brett Bryant
committed
ao2_ref(ser, -1);
return NULL;
}
Russell Bryant
committed
/*!
* \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;
Russell Bryant
committed
unsigned int target_len;
unsigned int total_len;
Russell Bryant
committed
dest = ast_strdupa(value);
dest = ast_skip_blanks(dest);
target = strsep(&dest, " ");
target = ast_skip_blanks(target);
target = strsep(&target, " "); /* trim trailing whitespace */
Russell Bryant
committed
if (!dest) {
ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
return;
}
Russell Bryant
committed
target_len = strlen(target) + 1;
total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
Russell Bryant
committed
if (!(redirect = ast_calloc(1, total_len))) {
Russell Bryant
committed
return;
Russell Bryant
committed
redirect->dest = redirect->target + target_len;
strcpy(redirect->target, target);
strcpy(redirect->dest, dest);
Russell Bryant
committed
AST_RWLIST_WRLOCK(&uri_redirects);
Russell Bryant
committed
Russell Bryant
committed
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);
Russell Bryant
committed
return;
}
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);
Russell Bryant
committed
return;
}
}
AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
Russell Bryant
committed
AST_RWLIST_UNLOCK(&uri_redirects);
Russell Bryant
committed
}
static int __ast_http_load(int reload)
{
struct ast_config *cfg;
struct ast_variable *v;
int enabled=0;
Mark Spencer
committed
int newenablestatic=0;
char newprefix[MAX_PREFIX] = "";
Ashley Sanders
committed
char server_name[MAX_SERVER_NAME_LENGTH];
Russell Bryant
committed
struct http_uri_redirect *redirect;
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 http_tls_was_enabled = 0;
Tilghman Lesher
committed
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);
http_tls_cfg.enabled = 0;
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);
/* 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))) {
Tilghman Lesher
committed
ast_free(redirect);
AST_RWLIST_UNLOCK(&uri_redirects);
Russell Bryant
committed
ast_sockaddr_setnull(&https_desc.local_address);
Richard Mudgett
committed
session_limit = DEFAULT_SESSION_LIMIT;
session_inactivity = DEFAULT_SESSION_INACTIVITY;
session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
Ashley Sanders
committed
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;
}
Richard Mudgett
committed
Ashley Sanders
committed
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);
Russell Bryant
committed
} else {
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
} 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));
Ashley Sanders
committed
ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
Mark Spencer
committed
enablestatic = newenablestatic;
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
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);
}
return 0;
}
static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_http_uri *urih;
Russell Bryant
committed
struct http_uri_redirect *redirect;
Terry Wilson
committed
switch (cmd) {
case CLI_INIT:
e->command = "http show status";
e->usage =
"Usage: http show status\n"
" Lists status of internal HTTP engine\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli(a->fd, "HTTP Server Status:\n");
ast_cli(a->fd, "Prefix: %s\n", prefix);
Ashley Sanders
committed
ast_cli(a->fd, "Server: %s\n", http_server_name);
if (ast_sockaddr_isnull(&http_desc.old_address)) {
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));
Russell Bryant
committed
AST_RWLIST_RDLOCK(&uris);
if (AST_RWLIST_EMPTY(&uris)) {
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);
Russell Bryant
committed
AST_RWLIST_RDLOCK(&uri_redirects);
AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
if (AST_RWLIST_EMPTY(&uri_redirects)) {
AST_RWLIST_UNLOCK(&uri_redirects);
Russell Bryant
committed
}
int ast_http_reload(void)
{
return __ast_http_load(1);
}
static struct ast_cli_entry cli_http[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
};
static void http_shutdown(void)
{
ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));
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);
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);
Mark Spencer
committed
ast_http_uri_link(&staticuri);
ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
ast_register_cleanup(http_shutdown);
return __ast_http_load(0);
}