diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | channels/chan_sip.c | 2 | ||||
-rwxr-xr-x | configure | 63 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | include/asterisk/autoconfig.h.in | 3 | ||||
-rw-r--r-- | include/asterisk/logger.h | 44 | ||||
-rw-r--r-- | main/logger.c | 155 | ||||
-rw-r--r-- | res/ari/ari_model_validators.c | 86 | ||||
-rw-r--r-- | res/ari/ari_model_validators.h | 23 | ||||
-rw-r--r-- | res/ari/ari_websockets.c | 9 | ||||
-rw-r--r-- | res/ari/resource_asterisk.c | 127 | ||||
-rw-r--r-- | res/ari/resource_asterisk.h | 63 | ||||
-rw-r--r-- | res/res_ari_asterisk.c | 300 | ||||
-rw-r--r-- | res/res_http_websocket.c | 34 | ||||
-rw-r--r-- | res/res_pjsip.c | 59 | ||||
-rw-r--r-- | res/res_pjsip/presence_xml.c | 31 | ||||
-rw-r--r-- | res/res_pjsip_pidf_digium_body_supplement.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 6 | ||||
-rw-r--r-- | res/res_pjsip_transport_websocket.c | 3 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 13 | ||||
-rw-r--r-- | rest-api/api-docs/asterisk.json | 128 |
21 files changed, 1135 insertions, 25 deletions
@@ -33,6 +33,9 @@ ARI deletion. Sorcery derived objects that are manipulated by this resource must have a sorcery wizard that supports the desired operations. + * A new feature has been added that allows for the rotation of log channels + through HTTP requests. + res_pjsip ------------------ diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 9ab429e83..9ba0e192b 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -3717,7 +3717,7 @@ static int __sip_xmit(struct sip_pvt *p, struct ast_str *data) } else if (p->socket.tcptls_session) { res = sip_tcptls_write(p->socket.tcptls_session, ast_str_buffer(data), ast_str_strlen(data)); } else if (p->socket.ws_session) { - if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, ast_str_buffer(data), ast_str_strlen(data)))) { + if (!(res = ast_websocket_write_string(p->socket.ws_session, ast_str_buffer(data)))) { /* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */ res = ast_str_strlen(data); } @@ -1089,6 +1089,10 @@ PBX_DAHDI DAHDI_DIR DAHDI_INCLUDE DAHDI_LIB +PBX_OPENSSL_ECDH_AUTO +OPENSSL_ECDH_AUTO_DIR +OPENSSL_ECDH_AUTO_INCLUDE +OPENSSL_ECDH_AUTO_LIB PBX_OPENSSL_EC OPENSSL_EC_DIR OPENSSL_EC_INCLUDE @@ -8696,6 +8700,18 @@ PBX_OPENSSL_EC=0 +OPENSSL_ECDH_AUTO_DESCRIP="OpenSSL Auto ECDH Support" +OPENSSL_ECDH_AUTO_OPTION=crypto +OPENSSL_ECDH_AUTO_DIR=${CRYPTO_DIR} + +PBX_OPENSSL_ECDH_AUTO=0 + + + + + + + DAHDI_DESCRIP="DAHDI" DAHDI_OPTION="dahdi" PBX_DAHDI=0 @@ -30266,6 +30282,53 @@ fi fi +if test "$PBX_OPENSSL" = "1"; +then + + if test "x${PBX_OPENSSL_ECDH_AUTO}" != "x1" -a "${USE_OPENSSL_ECDH_AUTO}" != "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_set_ecdh_auto declared in openssl/ssl.h" >&5 +$as_echo_n "checking for SSL_CTX_set_ecdh_auto declared in openssl/ssl.h... " >&6; } + saved_cppflags="${CPPFLAGS}" + if test "x${OPENSSL_ECDH_AUTO_DIR}" != "x"; then + OPENSSL_ECDH_AUTO_INCLUDE="-I${OPENSSL_ECDH_AUTO_DIR}/include" + fi + CPPFLAGS="${CPPFLAGS} ${OPENSSL_ECDH_AUTO_INCLUDE}" + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include <openssl/ssl.h> +int +main () +{ +#if !defined(SSL_CTX_set_ecdh_auto) + (void) SSL_CTX_set_ecdh_auto; + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + PBX_OPENSSL_ECDH_AUTO=1 + +$as_echo "#define HAVE_OPENSSL_ECDH_AUTO 1" >>confdefs.h + + + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + CPPFLAGS="${saved_cppflags}" + fi + +fi + if test "x${PBX_SRTP}" != "x1" -a "${USE_SRTP}" != "no"; then pbxlibdir="" diff --git a/configure.ac b/configure.ac index 971f9bf8a..329280924 100644 --- a/configure.ac +++ b/configure.ac @@ -414,6 +414,7 @@ AST_EXT_LIB_SETUP([CRYPT], [password and data encryption], [crypt]) AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto]) AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto]) AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_EC], [OpenSSL Elliptic Curve Support], [CRYPTO], [crypto]) +AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_ECDH_AUTO], [OpenSSL Auto ECDH Support], [CRYPTO], [crypto]) AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi]) AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec]) AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise]) @@ -2283,6 +2284,11 @@ then AST_EXT_LIB_CHECK([OPENSSL_EC], [ssl], [EC_KEY_new_by_curve_name], [openssl/ec.h], [-lcrypto]) fi +if test "$PBX_OPENSSL" = "1"; +then + AST_C_DECLARE_CHECK([OPENSSL_ECDH_AUTO], [SSL_CTX_set_ecdh_auto], [openssl/ssl.h]) +fi + AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h]) if test "$PBX_SRTP" = "1"; diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index 4efff1f21..542f5d2a0 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -548,6 +548,9 @@ /* Define to 1 if CRYPTO has the OpenSSL Elliptic Curve Support feature. */ #undef HAVE_OPENSSL_EC +/* Define if your system has SSL_CTX_set_ecdh_auto declared. */ +#undef HAVE_OPENSSL_ECDH_AUTO + /* Define to 1 if CRYPTO has the OpenSSL SRTP Extension Support feature. */ #undef HAVE_OPENSSL_SRTP diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index 22151157c..d06aecf20 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -45,6 +45,13 @@ extern "C" { #define AST_CALLID_BUFFER_LENGTH 13 +enum ast_logger_results { + AST_LOGGER_SUCCESS = 0, /*!< Log channel was created or deleted successfully*/ + AST_LOGGER_FAILURE = 1, /*!< Log channel already exists for create or doesn't exist for deletion of log channel */ + AST_LOGGER_DECLINE = -1, /*!< Log channel request was not accepted */ + AST_LOGGER_ALLOC_ERROR = -2 /*!< filename allocation error */ +}; + /*! \brief Used for sending a log message This is the standard logger function. Probably the only way you will invoke it would be something like this: ast_log(AST_LOG_WHATEVER, "Problem with the %s Captain. We should get some more. Will %d be enough?\n", "flux capacitor", 10); @@ -92,6 +99,36 @@ void ast_log_callid(int level, const char *file, int line, const char *function, __attribute__((format(printf, 6, 7))); /*! + * \brief Retrieve the existing log channels + * \param logentry A callback to an updater function + * \param data Data passed into the callback for manipulation + * + * For each of the logging channels, logentry will be executed with the + * channel file name, log type, status of the log, and configuration levels. + * + * \retval 0 on success + * \retval 1 on failure + * \retval -2 on allocation error + */ +int ast_logger_get_channels(int (*logentry)(const char *channel, const char *type, + const char *status, const char *configuration, void *data), void *data); + +/*! + * \brief Create a log channel + * + * \param log_channel Log channel to create + * \param components Logging config levels to add to the log channel + */ +int ast_logger_create_channel(const char *log_channel, const char *components); + +/*! + * \brief Delete the specified log channel + * + * \param log_channel The log channel to delete + */ +int ast_logger_remove_channel(const char *log_channel); + +/*! * \brief Log a backtrace of the current thread's execution stack to the Asterisk log */ void ast_log_backtrace(void); @@ -102,6 +139,13 @@ int logger_reload(void); /*! \brief Reload logger while rotating log files */ int ast_logger_rotate(void); +/*! + * \brief Rotate the specified log channel. + * + * \param log_channel The log channel to rotate + */ +int ast_logger_rotate_channel(const char *log_channel); + void __attribute__((format(printf, 5, 6))) ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...); /*! diff --git a/main/logger.c b/main/logger.c index f84221f64..6e540cb52 100644 --- a/main/logger.c +++ b/main/logger.c @@ -933,6 +933,46 @@ int ast_logger_rotate() return reload_logger(1, NULL); } +int ast_logger_rotate_channel(const char *log_channel) +{ + struct logchannel *f; + int success = AST_LOGGER_FAILURE; + + struct ast_str *filename = ast_str_create(64); + if (!filename) { + return AST_LOGGER_ALLOC_ERROR; + } + + ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel); + + AST_RWLIST_WRLOCK(&logchannels); + + ast_mkdir(ast_config_AST_LOG_DIR, 0644); + + AST_RWLIST_TRAVERSE(&logchannels, f, list) { + if (f->disabled) { + f->disabled = 0; /* Re-enable logging at reload */ + manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", + f->filename); + } + if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) { + fclose(f->fileptr); /* Close file */ + f->fileptr = NULL; + if (strcmp(ast_str_buffer(filename), f->filename) == 0) { + rotate_file(f->filename); + success = AST_LOGGER_SUCCESS; + } + } + } + + init_logger_chain(1 /* locked */, NULL); + + AST_RWLIST_UNLOCK(&logchannels); + ast_free(filename); + + return success; +} + static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int x; @@ -975,6 +1015,48 @@ static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct as return CLI_SUCCESS; } +int ast_logger_get_channels(int (*logentry)(const char *channel, const char *type, + const char *status, const char *configuration, void *data), void *data) +{ + struct logchannel *chan; + struct ast_str *configs = ast_str_create(64); + int res = AST_LOGGER_SUCCESS; + + if (!configs) { + return AST_LOGGER_ALLOC_ERROR; + } + + AST_RWLIST_RDLOCK(&logchannels); + AST_RWLIST_TRAVERSE(&logchannels, chan, list) { + unsigned int level; + + ast_str_reset(configs); + + for (level = 0; level < ARRAY_LEN(levels); level++) { + if ((chan->logmask & (1 << level)) && levels[level]) { + ast_str_append(&configs, 0, "%s ", levels[level]); + } + } + + res = logentry(chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : + (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"), chan->disabled ? + "Disabled" : "Enabled", ast_str_buffer(configs), data); + + if (res) { + AST_RWLIST_UNLOCK(&logchannels); + ast_free(configs); + configs = NULL; + return AST_LOGGER_FAILURE; + } + } + AST_RWLIST_UNLOCK(&logchannels); + + ast_free(configs); + configs = NULL; + + return AST_LOGGER_SUCCESS; +} + /*! \brief CLI command to show logging system configuration */ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { @@ -1014,6 +1096,44 @@ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struc return CLI_SUCCESS; } +int ast_logger_create_channel(const char *log_channel, const char *components) +{ + struct logchannel *chan; + struct ast_str *filename = ast_str_create(64); + int chan_exists = AST_LOGGER_SUCCESS; + + if (ast_strlen_zero(components)) { + return AST_LOGGER_DECLINE; + } + + if (!filename) { + return AST_LOGGER_ALLOC_ERROR; + } + + ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel); + + AST_RWLIST_WRLOCK(&logchannels); + + AST_RWLIST_TRAVERSE(&logchannels, chan, list) { + if (!strcmp(ast_str_buffer(filename), chan->filename)) { + chan_exists = AST_LOGGER_FAILURE; + break; + } + } + + if (!chan_exists) { + chan = make_logchannel(log_channel, components, 0, 1); + if (chan) { + AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); + global_logmask |= chan->logmask; + chan_exists = AST_LOGGER_SUCCESS; + } + } + AST_RWLIST_UNLOCK(&logchannels); + + return chan_exists; +} + static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct logchannel *chan; @@ -1063,6 +1183,41 @@ static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct return CLI_FAILURE; } +int ast_logger_remove_channel(const char *log_channel) +{ + struct logchannel *chan; + struct ast_str *filename = ast_str_create(64); + + if (!filename) { + return AST_LOGGER_ALLOC_ERROR; + } + + ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel); + + AST_RWLIST_WRLOCK(&logchannels); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&logchannels, chan, list) { + if (chan->dynamic && !strcmp(chan->filename, ast_str_buffer(filename))) { + AST_RWLIST_REMOVE_CURRENT(list); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&logchannels); + + if (!chan) { + return AST_LOGGER_FAILURE; + } + + if (chan->fileptr) { + fclose(chan->fileptr); + chan->fileptr = NULL; + } + ast_free(chan); + chan = NULL; + + return AST_LOGGER_SUCCESS; +} + static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct logchannel *chan; diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index 667589601..74611750e 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -362,6 +362,92 @@ ari_validator ast_ari_validate_config_tuple_fn(void) return ast_ari_validate_config_tuple; } +int ast_ari_validate_log_channel(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_channel = 0; + int has_configuration = 0; + int has_status = 0; + int has_type = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("configuration", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_configuration = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field configuration failed validation\n"); + res = 0; + } + } else + if (strcmp("status", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_status = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field status failed validation\n"); + res = 0; + } + } else + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LogChannel field type failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI LogChannel has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field channel\n"); + res = 0; + } + + if (!has_configuration) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field configuration\n"); + res = 0; + } + + if (!has_status) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field status\n"); + res = 0; + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI LogChannel missing required field type\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_log_channel_fn(void) +{ + return ast_ari_validate_log_channel; +} + int ast_ari_validate_module(struct ast_json *json) { int res = 1; diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index e122ded34..1803f57c9 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -225,6 +225,24 @@ int ast_ari_validate_config_tuple(struct ast_json *json); ari_validator ast_ari_validate_config_tuple_fn(void); /*! + * \brief Validator for LogChannel. + * + * Details of an Asterisk log channel + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_log_channel(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_log_channel(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_log_channel_fn(void); + +/*! * \brief Validator for Module. * * Details of an Asterisk module @@ -1283,6 +1301,11 @@ ari_validator ast_ari_validate_application_fn(void); * ConfigTuple * - attribute: string (required) * - value: string (required) + * LogChannel + * - channel: string (required) + * - configuration: string (required) + * - status: string (required) + * - type: string (required) * Module * - description: string (required) * - name: string (required) diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c index f3b764bf1..39c6dcac9 100644 --- a/res/ari/ari_websockets.c +++ b/res/ari/ari_websockets.c @@ -163,9 +163,7 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session, #ifdef AST_DEVMODE if (!session->validator(message)) { ast_log(LOG_ERROR, "Outgoing message failed validation\n"); - return ast_websocket_write(session->ws_session, - AST_WEBSOCKET_OPCODE_TEXT, VALIDATION_FAILED, - strlen(VALIDATION_FAILED)); + return ast_websocket_write_string(session->ws_session, VALIDATION_FAILED); } #endif @@ -176,9 +174,8 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session, return -1; } - ast_debug(3, "Examining ARI event: \n%s\n", str); - if (ast_websocket_write(session->ws_session, - AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str))) { + ast_debug(3, "Examining ARI event (length %zu): \n%s\n", strlen(str), str); + if (ast_websocket_write_string(session->ws_session, str)) { ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n"); return -1; } diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c index 2b6b6bc6e..30684d276 100644 --- a/res/ari/resource_asterisk.c +++ b/res/ari/resource_asterisk.c @@ -33,6 +33,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/ast_version.h" #include "asterisk/buildinfo.h" +#include "asterisk/logger.h" #include "asterisk/module.h" #include "asterisk/paths.h" #include "asterisk/pbx.h" @@ -627,6 +628,132 @@ void ast_ari_asterisk_reload_module(struct ast_variable *headers, ast_ari_response_no_content(response); } +/*! + * \brief Process logger information and append to a json array + * \param channel Resource logger channel name path + * \param type Resource log type + * \param status Resource log status + * \param configuration Resource logger levels + * \param log_data_list Resource array + * + * \retval -1 if no resource exists + * \retval 0 if resource exists + */ +static int process_log_list(const char *channel, const char *type, + const char *status, const char *configuration, void *log_data_list) +{ + struct ast_json *logger_info; + + logger_info = ast_json_pack("{s: s, s: s, s: s, s: s}", + "channel", channel, "type", type, "status", status, "configuration", + configuration); + + if (!logger_info) { + return AST_LOGGER_FAILURE; + } + + ast_json_array_append(log_data_list, logger_info); + return AST_LOGGER_SUCCESS; +} + +void ast_ari_asterisk_list_log_channels(struct ast_variable *headers, + struct ast_ari_asterisk_list_log_channels_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + int res; + + json = ast_json_array_create(); + res = ast_logger_get_channels(&process_log_list, json); + + if (res == AST_LOGGER_FAILURE) { + ast_ari_response_error(response, 500, "Internal Server Error", + "Response body is not valid"); + return; + } else if (res == AST_LOGGER_ALLOC_ERROR) { + ast_ari_response_error(response, 500, "Internal Server Error", + "Allocation Failed"); + return; + } + + ast_ari_response_ok(response, json); +} + +void ast_ari_asterisk_add_log(struct ast_variable *headers, + struct ast_ari_asterisk_add_log_args *args, + struct ast_ari_response *response) +{ + int res; + + ast_assert(response != NULL); + + res = ast_logger_create_channel(args->log_channel_name, args->configuration); + + if (res == AST_LOGGER_DECLINE) { + ast_ari_response_error(response, 400, "Bad Request", + "Configuration levels are required"); + return; + } else if (res == AST_LOGGER_FAILURE) { + ast_ari_response_error(response, 409, "Conflict", + "Log channel already exists"); + return; + } else if (res == AST_LOGGER_ALLOC_ERROR) { + ast_ari_response_error(response, 500, "Internal Server Error", + "Allocation failed"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_asterisk_rotate_log(struct ast_variable *headers, + struct ast_ari_asterisk_rotate_log_args *args, + struct ast_ari_response *response) +{ + int res; + + ast_assert(response != NULL); + + res = ast_logger_rotate_channel(args->log_channel_name); + + if (res == AST_LOGGER_FAILURE) { + ast_ari_response_error( + response, 404, "Not Found", + "Log channel does not exist"); + return; + } else if (res == AST_LOGGER_ALLOC_ERROR) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Allocation failed"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_asterisk_delete_log(struct ast_variable *headers, + struct ast_ari_asterisk_delete_log_args *args, + struct ast_ari_response *response) +{ + int res; + + ast_assert(response != NULL); + + res = ast_logger_remove_channel(args->log_channel_name); + + if (res == AST_LOGGER_FAILURE) { + ast_ari_response_error(response, 404, "Not Found", + "Log channel does not exist"); + return; + } else if (res == AST_LOGGER_ALLOC_ERROR) { + ast_ari_response_error(response, 500, "Internal Server Error", + "Allocation failed"); + return; + } + + ast_ari_response_no_content(response); +} + void ast_ari_asterisk_get_global_var(struct ast_variable *headers, struct ast_ari_asterisk_get_global_var_args *args, struct ast_ari_response *response) diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h index 1afc09317..a4a7da080 100644 --- a/res/ari/resource_asterisk.h +++ b/res/ari/resource_asterisk.h @@ -194,6 +194,69 @@ struct ast_ari_asterisk_reload_module_args { * \param[out] response HTTP response */ void ast_ari_asterisk_reload_module(struct ast_variable *headers, struct ast_ari_asterisk_reload_module_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_list_log_channels() */ +struct ast_ari_asterisk_list_log_channels_args { +}; +/*! + * \brief Gets Asterisk log channel information. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_list_log_channels(struct ast_variable *headers, struct ast_ari_asterisk_list_log_channels_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_add_log() */ +struct ast_ari_asterisk_add_log_args { + /*! The log channel to add */ + const char *log_channel_name; + /*! levels of the log channel */ + const char *configuration; +}; +/*! + * \brief Body parsing function for /asterisk/logging/{logChannelName}. + * \param body The JSON body from which to parse parameters. + * \param[out] args The args structure to parse into. + * \retval zero on success + * \retval non-zero on failure + */ +int ast_ari_asterisk_add_log_parse_body( + struct ast_json *body, + struct ast_ari_asterisk_add_log_args *args); + +/*! + * \brief Adds a log channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_add_log(struct ast_variable *headers, struct ast_ari_asterisk_add_log_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_delete_log() */ +struct ast_ari_asterisk_delete_log_args { + /*! Log channels name */ + const char *log_channel_name; +}; +/*! + * \brief Deletes a log channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_delete_log(struct ast_variable *headers, struct ast_ari_asterisk_delete_log_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_rotate_log() */ +struct ast_ari_asterisk_rotate_log_args { + /*! Log channel's name */ + const char *log_channel_name; +}; +/*! + * \brief Rotates a log channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_rotate_log(struct ast_variable *headers, struct ast_ari_asterisk_rotate_log_args *args, struct ast_ari_response *response); /*! Argument struct for ast_ari_asterisk_get_global_var() */ struct ast_ari_asterisk_get_global_var_args { /*! The variable to get */ diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c index ea8ddbb35..fe6e3d363 100644 --- a/res/res_ari_asterisk.c +++ b/res/res_ari_asterisk.c @@ -721,6 +721,273 @@ static void ast_ari_asterisk_reload_module_cb( fin: __attribute__((unused)) return; } +/*! + * \brief Parameter parsing callback for /asterisk/logging. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_list_log_channels_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_list_log_channels_args args = {}; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + ast_ari_asterisk_list_log_channels(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_list(response->message, + ast_ari_validate_log_channel_fn()); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +int ast_ari_asterisk_add_log_parse_body( + struct ast_json *body, + struct ast_ari_asterisk_add_log_args *args) +{ + struct ast_json *field; + /* Parse query parameters out of it */ + field = ast_json_object_get(body, "configuration"); + if (field) { + args->configuration = ast_json_string_get(field); + } + return 0; +} + +/*! + * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_add_log_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_add_log_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = get_params; i; i = i->next) { + if (strcmp(i->name, "configuration") == 0) { + args.configuration = (i->value); + } else + {} + } + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "logChannelName") == 0) { + args.log_channel_name = (i->value); + } else + {} + } + /* Look for a JSON request entity */ + body = ast_http_get_json(ser, headers); + if (!body) { + switch (errno) { + case EFBIG: + ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); + goto fin; + case ENOMEM: + ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); + goto fin; + case EIO: + ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); + goto fin; + } + } + if (ast_ari_asterisk_add_log_parse_body(body, &args)) { + ast_ari_response_alloc_failed(response); + goto fin; + } + ast_ari_asterisk_add_log(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 400: /* Bad request body */ + case 409: /* Log channel could not be created. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_delete_log_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_delete_log_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "logChannelName") == 0) { + args.log_channel_name = (i->value); + } else + {} + } + ast_ari_asterisk_delete_log(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Log channel does not exist. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}/rotate. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_rotate_log_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_rotate_log_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "logChannelName") == 0) { + args.log_channel_name = (i->value); + } else + {} + } + ast_ari_asterisk_rotate_log(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Log channel does not exist. */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}/rotate\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}/rotate\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} int ast_ari_asterisk_get_global_var_parse_body( struct ast_json *body, struct ast_ari_asterisk_get_global_var_args *args) @@ -989,6 +1256,35 @@ static struct stasis_rest_handlers asterisk_modules = { .children = { &asterisk_modules_moduleName, } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_logging_logChannelName_rotate = { + .path_segment = "rotate", + .callbacks = { + [AST_HTTP_PUT] = ast_ari_asterisk_rotate_log_cb, + }, + .num_children = 0, + .children = { } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_logging_logChannelName = { + .path_segment = "logChannelName", + .is_wildcard = 1, + .callbacks = { + [AST_HTTP_POST] = ast_ari_asterisk_add_log_cb, + [AST_HTTP_DELETE] = ast_ari_asterisk_delete_log_cb, + }, + .num_children = 1, + .children = { &asterisk_logging_logChannelName_rotate, } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_logging = { + .path_segment = "logging", + .callbacks = { + [AST_HTTP_GET] = ast_ari_asterisk_list_log_channels_cb, + }, + .num_children = 1, + .children = { &asterisk_logging_logChannelName, } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_variable = { .path_segment = "variable", .callbacks = { @@ -1003,8 +1299,8 @@ static struct stasis_rest_handlers asterisk = { .path_segment = "asterisk", .callbacks = { }, - .num_children = 4, - .children = { &asterisk_config,&asterisk_info,&asterisk_modules,&asterisk_variable, } + .num_children = 5, + .children = { &asterisk_config,&asterisk_info,&asterisk_modules,&asterisk_logging,&asterisk_variable, } }; static int load_module(void) diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c index 144c08d0e..db9c50a28 100644 --- a/res/res_http_websocket.c +++ b/res/res_http_websocket.c @@ -298,6 +298,24 @@ int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, ui return res; } +static const char *opcode_map[] = { + [AST_WEBSOCKET_OPCODE_CONTINUATION] = "continuation", + [AST_WEBSOCKET_OPCODE_TEXT] = "text", + [AST_WEBSOCKET_OPCODE_BINARY] = "binary", + [AST_WEBSOCKET_OPCODE_CLOSE] = "close", + [AST_WEBSOCKET_OPCODE_PING] = "ping", + [AST_WEBSOCKET_OPCODE_PONG] = "pong", +}; + +static const char *websocket_opcode2str(enum ast_websocket_opcode opcode) +{ + if (opcode < AST_WEBSOCKET_OPCODE_CONTINUATION || + opcode > AST_WEBSOCKET_OPCODE_PONG) { + return "<unknown>"; + } else { + return opcode_map[opcode]; + } +} /*! \brief Write function for websocket traffic */ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length) @@ -306,6 +324,9 @@ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, en char *frame; uint64_t length; + ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n", + websocket_opcode2str(opcode), actual_length); + if (actual_length < 126) { length = actual_length; } else if (actual_length < (1 << 16)) { @@ -1354,8 +1375,19 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read_string) int AST_OPTIONAL_API_NAME(ast_websocket_write_string) (struct ast_websocket *ws, const char *buf) { + uint64_t len = strlen(buf); + + ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len); + + /* We do not pass strlen(buf) to ast_websocket_write() directly because the + * size_t returned by strlen() may not require the same storage size + * as the uint64_t that ast_websocket_write() uses. This normally + * would not cause a problem, but since ast_websocket_write() uses + * the optional API, this function call goes through a series of macros + * that may cause a 32-bit to 64-bit conversion to go awry. + */ return ast_websocket_write(ws, AST_WEBSOCKET_OPCODE_TEXT, - (char *)buf, strlen(buf)); + (char *)buf, len); } static int load_module(void) diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 43e3da6de..405ac6838 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -40,6 +40,8 @@ #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/res_pjsip_cli.h" +#include "asterisk/test.h" +#include "asterisk/res_pjsip_presence_xml.h" /*** MODULEINFO <depend>pjproject</depend> @@ -3701,6 +3703,57 @@ static void remove_request_headers(pjsip_endpoint *endpt) } } +AST_TEST_DEFINE(xml_sanitization_end_null) +{ + char sanitized[8]; + + switch (cmd) { + case TEST_INIT: + info->name = "xml_sanitization_end_null"; + info->category = "/res/res_pjsip/"; + info->summary = "Ensure XML sanitization works as expected with a long string"; + info->description = "This test sanitizes a string which exceeds the output\n" + "buffer size. Once done the string is confirmed to be NULL terminated."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_sip_sanitize_xml("aaaaaaaaaaaa", sanitized, sizeof(sanitized)); + if (sanitized[7] != '\0') { + ast_test_status_update(test, "Sanitized XML string is not null-terminated when it should be\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(xml_sanitization_exceeds_buffer) +{ + char sanitized[8]; + + switch (cmd) { + case TEST_INIT: + info->name = "xml_sanitization_exceeds_buffer"; + info->category = "/res/res_pjsip/"; + info->summary = "Ensure XML sanitization does not exceed buffer when output won't fit"; + info->description = "This test sanitizes a string which before sanitization would\n" + "fit within the output buffer. After sanitization, however, the string would\n" + "exceed the buffer. Once done the string is confirmed to be NULL terminated."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_sip_sanitize_xml("<><><>&", sanitized, sizeof(sanitized)); + if (sanitized[7] != '\0') { + ast_test_status_update(test, "Sanitized XML string is not null-terminated when it should be\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + /*! * \internal * \brief Reload configuration within a PJSIP thread @@ -3870,6 +3923,9 @@ static int load_module(void) ast_res_pjsip_init_options_handling(0); ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); + AST_TEST_REGISTER(xml_sanitization_end_null); + AST_TEST_REGISTER(xml_sanitization_exceeds_buffer); + return AST_MODULE_LOAD_SUCCESS; } @@ -3912,6 +3968,9 @@ static int unload_pjsip(void *data) static int unload_module(void) { + AST_TEST_UNREGISTER(xml_sanitization_end_null); + AST_TEST_UNREGISTER(xml_sanitization_exceeds_buffer); + /* The thread this is called from cannot call PJSIP/PJLIB functions, * so we have to push the work to the threadpool to handle */ diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c index 12bfa078c..b98ea0237 100644 --- a/res/res_pjsip/presence_xml.c +++ b/res/res_pjsip/presence_xml.c @@ -30,45 +30,54 @@ void ast_sip_sanitize_xml(const char *input, char *output, size_t len) { char *copy = ast_strdupa(input); char *break_point; + size_t remaining = len - 1; output[0] = '\0'; - while ((break_point = strpbrk(copy, "<>\"&'\n\r"))) { + while ((break_point = strpbrk(copy, "<>\"&'\n\r")) && remaining) { char to_escape = *break_point; *break_point = '\0'; - strncat(output, copy, len); + strncat(output, copy, remaining); + + /* The strncat function will write remaining+1 if the string length is + * equal to or greater than the size provided to it. We take this into + * account by subtracting 1, which ensures that the NULL byte is written + * inside of the provided buffer. + */ + remaining = len - strlen(output) - 1; switch (to_escape) { case '<': - strncat(output, "<", len); + strncat(output, "<", remaining); break; case '>': - strncat(output, ">", len); + strncat(output, ">", remaining); break; case '"': - strncat(output, """, len); + strncat(output, """, remaining); break; case '&': - strncat(output, "&", len); + strncat(output, "&", remaining); break; case '\'': - strncat(output, "'", len); + strncat(output, "'", remaining); break; case '\r': - strncat(output, " ", len); + strncat(output, " ", remaining); break; case '\n': - strncat(output, " ", len); + strncat(output, " ", remaining); break; }; copy = break_point + 1; + remaining = len - strlen(output) - 1; } /* Be sure to copy everything after the final bracket */ - if (*copy) { - strncat(output, copy, len); + if (*copy && remaining) { + strncat(output, copy, remaining); } } diff --git a/res/res_pjsip_pidf_digium_body_supplement.c b/res/res_pjsip_pidf_digium_body_supplement.c index 86e673afa..4b1a78181 100644 --- a/res/res_pjsip_pidf_digium_body_supplement.c +++ b/res/res_pjsip_pidf_digium_body_supplement.c @@ -40,7 +40,7 @@ static int pidf_supplement_body(void *body, void *data) { struct ast_sip_exten_state_data *state_data = data; pj_xml_node *node; - char sanitized[256]; + char sanitized[1024]; if (ast_strlen_zero(state_data->user_agent) || !strstr(state_data->user_agent, "digium")) { diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 5eb72b45a..539bfc1fa 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2681,8 +2681,10 @@ static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t stat struct ast_sip_session *session = inv->mod_data[session_module.id]; const pjmedia_sdp_session *local, *remote; - if (!session->channel) { - /* If we don't have a channel. We really don't care about media updates. + if (!session || !session->channel) { + /* + * If we don't have a session or channel then we really + * don't care about media updates. * Just ignore */ return; diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c index be3046889..f2c16a118 100644 --- a/res/res_pjsip_transport_websocket.c +++ b/res/res_pjsip_transport_websocket.c @@ -63,8 +63,9 @@ static pj_status_t ws_send_msg(pjsip_transport *transport, pjsip_transport_callback callback) { struct ws_transport *wstransport = (struct ws_transport *)transport; + uint64_t len = tdata->buf.cur - tdata->buf.start; - if (ast_websocket_write(wstransport->ws_session, AST_WEBSOCKET_OPCODE_TEXT, tdata->buf.start, (int)(tdata->buf.cur - tdata->buf.start))) { + if (ast_websocket_write(wstransport->ws_session, AST_WEBSOCKET_OPCODE_TEXT, tdata->buf.start, len)) { return PJ_EUNKNOWN; } diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 00617e4e7..43615d6c8 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -1268,6 +1268,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); int res; +#ifndef HAVE_OPENSSL_ECDH_AUTO + EC_KEY *ecdh; +#endif if (!dtls_cfg->enabled) { return 0; @@ -1288,6 +1291,16 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1); +#ifdef HAVE_OPENSSL_ECDH_AUTO + SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, 1); +#else + ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh) { + SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh); + EC_KEY_free(ecdh); + } +#endif + rtp->dtls_verify = dtls_cfg->verify; SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ? diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index cc1e3b821..44bf024f8 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -297,6 +297,108 @@ ] }, { + "path": "/asterisk/logging", + "description": "Asterisk log channels", + "operations": [ + { + "httpMethod": "GET", + "summary": "Gets Asterisk log channel information.", + "nickname": "listLogChannels", + "responseClass": "List[LogChannel]" + } + ] + }, + { + "path": "/asterisk/logging/{logChannelName}", + "description": "Asterisk log channel", + "operations": [ + { + "httpMethod": "POST", + "summary": "Adds a log channel.", + "nickname": "addLog", + "responseClass": "void", + "parameters": [ + { + "name": "logChannelName", + "description": "The log channel to add", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + }, + { + "name": "configuration", + "description": "levels of the log channel", + "paramType": "query", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 400, + "reason": "Bad request body" + }, + { + "code": 409, + "reason": "Log channel could not be created." + } + ] + }, + { + "httpMethod": "DELETE", + "summary": "Deletes a log channel.", + "nickname": "deleteLog", + "responseClass": "void", + "parameters": [ + { + "name": "logChannelName", + "description": "Log channels name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Log channel does not exist." + } + ] + } + ] + }, + { + "path": "/asterisk/logging/{logChannelName}/rotate", + "description": "Asterisk log channel", + "operations": [ + { + "httpMethod": "PUT", + "summary": "Rotates a log channel.", + "nickname": "rotateLog", + "responseClass": "void", + "parameters": [ + { + "name": "logChannelName", + "description": "Log channel's name", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Log channel does not exist." + } + ] + } + ] + }, + { "path": "/asterisk/variable", "description": "Global variables", "operations": [ @@ -533,6 +635,32 @@ } } }, + "LogChannel": { + "id": "LogChannel", + "description": "Details of an Asterisk log channel", + "properties": { + "channel": { + "type": "string", + "description": "The log channel path", + "required": true + }, + "type": { + "type": "string", + "description": "Types of logs for the log channel", + "required": true + }, + "status": { + "type": "string", + "description": "Whether or not a log type is enabled", + "required": true + }, + "configuration": { + "type": "string", + "description": "The various log levels", + "required": true + } + } + }, "Variable": { "id": "Variable", "description": "The value of a channel variable", |