diff options
39 files changed, 995 insertions, 218 deletions
@@ -25,6 +25,15 @@ res_fax res_pjsip ------------------ + * Endpoint IP Access Controls + Added new configuration Endpoint options: + "acl" - list of IP ACL section names in acl.conf + "deny" - List of IP addresses to deny access from + "permit" - List of IP addresses to permit access from + "contact_acl" - List of Contact ACL section names in acl.conf + "contact_deny" - List of Contact header addresses to deny + "contact_permit" - List of Contact header addresses to permit + * Added new status Updated to AMI event ContactStatus on update registration * Added "reg_server" to contacts. @@ -32,6 +41,22 @@ res_pjsip into the "reg_server" field in the ps_contacts table to facilitate multi-server setups. +res_hep +------------------ + * Added a new option, 'uuid_type', that sets the preferred source of the Homer + correlation UUID. The valid options are: + - call-id: Use the PJSIP SIP Call-ID header value + - channel: Use the Asterisk channel name + The default value is 'call-id'. In the event that a HEP module cannot find a + valid value using the specified 'uuid_type', the module may fallback to a + more readily available source for the correlation UUID. + +res_pjsip_info_empty +-------------------- + * A new module that can respond to empty Content-Type INFO packets during call. + Some SBCs will terminate a call if their empty INFO packets are not responded + to within a predefined time. + app_confbridge ------------------ * Added a bridge profile option called regcontext that allows you to @@ -825,60 +825,56 @@ install-logrotate: rm -f contrib/scripts/asterisk.logrotate.tmp config: - @if [ "${OSARCH}" = "linux-gnu" -o "${OSARCH}" = "kfreebsd-gnu" ]; then \ - if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \ - ./build_tools/install_subst contrib/init.d/rc.redhat.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \ - if [ ! -f "$(DESTDIR)/etc/sysconfig/asterisk" ] ; then \ - $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ - fi ; \ - if [ -z "$(DESTDIR)" ] ; then \ - /sbin/chkconfig --add asterisk ; \ - fi ; \ - elif [ -f /etc/debian_version ] ; then \ - ./build_tools/install_subst contrib/init.d/rc.debian.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ - if [ ! -f "$(DESTDIR)/etc/default/asterisk" ] ; then \ - $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/default/asterisk" ; \ - fi ; \ - if [ -z "$(DESTDIR)" ] ; then \ - /usr/sbin/update-rc.d asterisk defaults 50 91 ; \ - fi ; \ - elif [ -f /etc/gentoo-release ] ; then \ - ./build_tools/install_subst contrib/init.d/rc.gentoo.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ - if [ -z "$(DESTDIR)" ] ; then \ - /sbin/rc-update add asterisk default ; \ - fi ; \ - elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ] ; then \ - ./build_tools/install_subst contrib/init.d/rc.mandriva.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \ - if [ ! -f /etc/sysconfig/asterisk ] ; then \ - $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ - fi ; \ - if [ -z "$(DESTDIR)" ] ; then \ - /sbin/chkconfig --add asterisk ; \ - fi ; \ - elif [ -f /etc/SuSE-release -o -f /etc/novell-release ] ; then \ - ./build_tools/install_subst contrib/init.d/rc.suse.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ - if [ ! -f /etc/sysconfig/asterisk ] ; then \ - $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ - fi ; \ - if [ -z "$(DESTDIR)" ] ; then \ - /sbin/chkconfig --add asterisk ; \ - fi ; \ - elif [ -f /etc/arch-release -o -f /etc/arch-release ] ; then \ - ./build_tools/install_subst contrib/init.d/rc.archlinux.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ - elif [ -d "$(DESTDIR)/Library/LaunchDaemons" ]; then \ - if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist" ]; then \ - ./build_tools/install_subst contrib/init.d/org.asterisk.asterisk.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist"; \ - fi; \ - if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist" ]; then \ - ./build_tools/install_subst contrib/init.d/org.asterisk.muted.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist"; \ - fi; \ - elif [ -f /etc/slackware-version ]; then \ - echo "Slackware is not currently supported, although an init script does exist for it."; \ - else \ - echo "We could not install init scripts for your distribution." ; \ - fi \ + if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \ + ./build_tools/install_subst contrib/init.d/rc.redhat.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \ + if [ ! -f "$(DESTDIR)/etc/sysconfig/asterisk" ] ; then \ + $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ + fi ; \ + if [ -z "$(DESTDIR)" ] ; then \ + /sbin/chkconfig --add asterisk ; \ + fi ; \ + elif [ -f /etc/debian_version ] ; then \ + ./build_tools/install_subst contrib/init.d/rc.debian.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ + if [ ! -f "$(DESTDIR)/etc/default/asterisk" ] ; then \ + $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/default/asterisk" ; \ + fi ; \ + if [ -z "$(DESTDIR)" ] ; then \ + /usr/sbin/update-rc.d asterisk defaults 50 91 ; \ + fi ; \ + elif [ -f /etc/gentoo-release ] ; then \ + ./build_tools/install_subst contrib/init.d/rc.gentoo.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ + if [ -z "$(DESTDIR)" ] ; then \ + /sbin/rc-update add asterisk default ; \ + fi ; \ + elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ] ; then \ + ./build_tools/install_subst contrib/init.d/rc.mandriva.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \ + if [ ! -f /etc/sysconfig/asterisk ] ; then \ + $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ + fi ; \ + if [ -z "$(DESTDIR)" ] ; then \ + /sbin/chkconfig --add asterisk ; \ + fi ; \ + elif [ -f /etc/SuSE-release -o -f /etc/novell-release ] ; then \ + ./build_tools/install_subst contrib/init.d/rc.suse.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ + if [ ! -f /etc/sysconfig/asterisk ] ; then \ + $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \ + fi ; \ + if [ -z "$(DESTDIR)" ] ; then \ + /sbin/chkconfig --add asterisk ; \ + fi ; \ + elif [ -f /etc/arch-release -o -f /etc/arch-release ] ; then \ + ./build_tools/install_subst contrib/init.d/rc.archlinux.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \ + elif [ -d "$(DESTDIR)/Library/LaunchDaemons" ]; then \ + if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist" ]; then \ + ./build_tools/install_subst contrib/init.d/org.asterisk.asterisk.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist"; \ + fi; \ + if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist" ]; then \ + ./build_tools/install_subst contrib/init.d/org.asterisk.muted.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist"; \ + fi; \ + elif [ -f /etc/slackware-version ]; then \ + echo "Slackware is not currently supported, although an init script does exist for it."; \ else \ - echo "We could not install init scripts for your operating system." ; \ + echo "We could not install init scripts for your distribution." ; \ fi sounds: diff --git a/apps/app_followme.c b/apps/app_followme.c index e5a5ee3c5..af6bb1039 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -66,6 +66,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stasis_channels.h" #include "asterisk/max_forwards.h" +#define REC_FORMAT "sln" + /*** DOCUMENTATION <application name="FollowMe" language="en_US"> <synopsis> @@ -1421,7 +1423,7 @@ static int app_exec(struct ast_channel *chan, const char *data) snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s", ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan)); - if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, "sln", &duration, + if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, REC_FORMAT, &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) { goto outrun; } @@ -1522,7 +1524,18 @@ outrun: ast_free(nm); } if (!ast_strlen_zero(targs->namerecloc)) { - unlink(targs->namerecloc); + int ret; + char fn[PATH_MAX]; + + snprintf(fn, sizeof(fn), "%s.%s", targs->namerecloc, + REC_FORMAT); + ret = unlink(fn); + if (ret != 0) { + ast_log(LOG_NOTICE, "Failed to delete recorded name file %s: %d (%s)\n", + fn, errno, strerror(errno)); + } else { + ast_debug(2, "deleted recorded prompt %s.\n", fn); + } } ast_free((char *) targs->predial_callee); ast_party_connected_line_free(&targs->connected_in); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 7cc91486a..f64845472 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -14528,10 +14528,12 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, add_header(&req, "Require", "replaces"); } - /* Add Session-Timers related headers */ - if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE + /* Add Session-Timers related headers if not already there */ + if (ast_strlen_zero(sip_get_header(&req, "Session-Expires")) && + (sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) && + (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE || (st_get_mode(p, 0) == SESSION_TIMER_MODE_ACCEPT - && st_get_se(p, FALSE) != DEFAULT_MIN_SE)) { + && st_get_se(p, FALSE) != DEFAULT_MIN_SE))) { char i2astr[10]; if (!p->stimer->st_interval) { diff --git a/configs/basic-pbx/asterisk.conf b/configs/basic-pbx/asterisk.conf index 576cc976b..ff66ceea7 100644 --- a/configs/basic-pbx/asterisk.conf +++ b/configs/basic-pbx/asterisk.conf @@ -1,26 +1,13 @@ -[directories] -astetcdir => /etc/asterisk -astmoddir => /usr/lib/asterisk/modules -astvarlibdir => /var/lib/asterisk -astdbdir => /var/lib/asterisk -astkeydir => /var/lib/asterisk -astdatadir => /var/lib/asterisk -astagidir => /var/lib/asterisk/agi-bin -astspooldir => /var/spool/asterisk -astrundir => /var/run/asterisk -astlogdir => /var/log/asterisk -astsbindir => /usr/sbin - [options] ; If we want to start Asterisk with a default verbosity for the verbose -; or debug logger channel types, then we use these settings. +; or debug logger channel types, then we use these settings (by default +; they are disabled). ;verbose = 5 -;debug = 5 +;debug = 2 ; User and group to run asterisk as. NOTE: This will require changes to ; directory and device permissions. -;runuser = asterisk ; The user to run as. -;rungroup = asterisk ; The group to run as. +;runuser = asterisk ; The user to run as. The default is root. +;rungroup = asterisk ; The group to run as. The default is root -defaultlanguage = en -documentation_language = en_US +;defaultlanguage = es diff --git a/configs/samples/hep.conf.sample b/configs/samples/hep.conf.sample index 40b17aa0e..6e409d151 100644 --- a/configs/samples/hep.conf.sample +++ b/configs/samples/hep.conf.sample @@ -13,4 +13,8 @@ capture_password = foo ; If specified, the authorization passsword capture_id = 1234 ; A unique integer identifier for this ; server. This ID will be embedded sent ; with each packet from this server. +uuid_type = call-id ; Specify the preferred source for the Homer + ; correlation UUID. Valid options are: + ; - 'call-id' for the PJSIP SIP Call-ID + ; - 'channel' for the Asterisk channel name diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index a17dab7a7..3e007e48c 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -624,7 +624,7 @@ ; identified. ; "username": Identify by the From or To username and domain ; "auth_username": Identify by the Authorization username and realm - : In all cases, if an exact match on username and domain/realm fails, + ; In all cases, if an exact match on username and domain/realm fails, ; the match will be retried with just the username. ; (default: "username") ;redirect_method=user ; How redirects received from an endpoint are handled @@ -13612,7 +13612,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13658,7 +13658,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13682,7 +13682,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13727,7 +13727,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13751,7 +13751,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -17614,7 +17614,7 @@ if ac_fn_c_try_compile "$LINENO"; then : $as_echo "yes" >&6; } AST_LEAK_SANITIZER=1 else - AST_LEAK_SANITIZER= + AST_LEAK_SANITIZER=0 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -17646,7 +17646,7 @@ if ac_fn_c_try_compile "$LINENO"; then : $as_echo "yes" >&6; } AST_UNDEFINED_SANITIZER=1 else - AST_UNDEFINED_SANITIZER= + AST_UNDEFINED_SANITIZER=0 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } diff --git a/configure.ac b/configure.ac index 822303708..f2e42ba1f 100644 --- a/configure.ac +++ b/configure.ac @@ -1116,7 +1116,7 @@ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([], [int x = 1;])], AC_MSG_RESULT(yes) [AST_LEAK_SANITIZER=1], - [AST_LEAK_SANITIZER=] + [AST_LEAK_SANITIZER=0] AC_MSG_RESULT(no) ) CFLAGS="${saved_sanitize_CFLAGS}" @@ -1132,7 +1132,7 @@ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([], [int x = 1;])], AC_MSG_RESULT(yes) [AST_UNDEFINED_SANITIZER=1], - [AST_UNDEFINED_SANITIZER=] + [AST_UNDEFINED_SANITIZER=0] AC_MSG_RESULT(no) ) CFLAGS="${saved_sanitize_CFLAGS}" diff --git a/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py b/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py new file mode 100644 index 000000000..56bd28499 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py @@ -0,0 +1,32 @@ +"""Add PJSIP Endpoint IP Access Control options + +Revision ID: bca7113d796f +Revises: 6be31516058d +Create Date: 2016-05-13 12:37:03.786359 + +""" + +# revision identifiers, used by Alembic. +revision = 'bca7113d796f' +down_revision = '6be31516058d' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('ps_endpoints', sa.Column('deny', sa.String(95))) + op.add_column('ps_endpoints', sa.Column('permit', sa.String(95))) + op.add_column('ps_endpoints', sa.Column('acl', sa.String(40))) + op.add_column('ps_endpoints', sa.Column('contact_deny', sa.String(95))) + op.add_column('ps_endpoints', sa.Column('contact_permit', sa.String(95))) + op.add_column('ps_endpoints', sa.Column('contact_acl', sa.String(40))) + + +def downgrade(): + op.drop_column('ps_endpoints', 'contact_acl') + op.drop_column('ps_endpoints', 'contact_permit') + op.drop_column('ps_endpoints', 'contact_deny') + op.drop_column('ps_endpoints', 'acl') + op.drop_column('ps_endpoints', 'permit') + op.drop_column('ps_endpoints', 'deny') diff --git a/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py new file mode 100644 index 000000000..76faf394c --- /dev/null +++ b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py @@ -0,0 +1,32 @@ +"""ps_contacts add authenticate_qualify + +Revision ID: 6be31516058d +Revises: 81b01a191a46 +Create Date: 2016-05-03 14:57:12.538179 + +""" + +# revision identifiers, used by Alembic. +revision = '6be31516058d' +down_revision = '81b01a191a46' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import ENUM + +YESNO_NAME = 'yesno_values' +YESNO_VALUES = ['yes', 'no'] + +def upgrade(): + ############################# Enums ############################## + + # yesno_values have already been created, so use postgres enum object + # type to get around "already created" issue - works okay with mysql + yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False) + + op.add_column('ps_contacts', sa.Column('authenticate_qualify', yesno_values)) + + +def downgrade(): + op.drop_column('ps_contacts', 'authenticate_qualify') + diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h index a8832cdb8..1cf750c08 100644 --- a/include/asterisk/parking.h +++ b/include/asterisk/parking.h @@ -211,12 +211,19 @@ int ast_parking_is_exten_park(const char *context, const char *exten); * \brief Park the bridge and/or callers that this channel is in * * \param parker The bridge_channel parking the bridge - * \param exten Optional. The extension the channel or bridge was parked at if the - * call succeeds. + * \param[out] exten Optional. The parking exten to access the parking lot. * \param length Optional. If \c exten is specified, the size of the buffer. * * \note This is safe to be called outside of the \ref AstBridging Bridging API. * + * \note The exten parameter was intended to return the extension the channel or + * bridge was parked at if the call succeeds. However, accessing that information + * is very difficult to do with the new asynchronous design. That information may + * not be available anywhere by the time this function currently returns. + * + * Only, chan_skinny is known to call this function and use the exten parameter + * for the phone display. + * * \retval 0 on success * \retval non-zero on error */ diff --git a/include/asterisk/res_hep.h b/include/asterisk/res_hep.h index 8839fd60a..bd0129eea 100644 --- a/include/asterisk/res_hep.h +++ b/include/asterisk/res_hep.h @@ -49,6 +49,11 @@ enum hepv3_capture_type { HEPV3_CAPTURE_TYPE_IAX = 0x10, }; +enum hep_uuid_type { + HEP_UUID_TYPE_CALL_ID = 0, + HEP_UUID_TYPE_CHANNEL, +}; + /*! \brief HEPv3 Capture Info */ struct hepv3_capture_info { /*! The source address of the packet */ @@ -104,6 +109,15 @@ struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t */ int hepv3_send_packet(struct hepv3_capture_info *capture_info); +/*! + * \brief Get the preferred UUID type + * + * \since 13.10.0 + * + * \retval The type of UUID the packet should use + */ +enum hep_uuid_type hepv3_get_uuid_type(void); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index f985e3254..d20d27e70 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -734,6 +734,10 @@ struct ast_sip_endpoint { unsigned int usereqphone; /*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */ unsigned int rpid_immediate; + /* Access control list */ + struct ast_acl_list *acl; + /* Restrict what IPs are allowed in the Contact header (for registration) */ + struct ast_acl_list *contact_acl; }; /*! diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index 0e2f69ba8..2ca75a69c 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -688,7 +688,7 @@ void ast_str_trim_blanks(struct ast_str *buf), if (!buf) { return; } - while (buf->__AST_STR_USED && buf->__AST_STR_STR[buf->__AST_STR_USED - 1] < 33) { + while (buf->__AST_STR_USED && ((unsigned char) buf->__AST_STR_STR[buf->__AST_STR_USED - 1]) < 33) { buf->__AST_STR_STR[--(buf->__AST_STR_USED)] = '\0'; } } diff --git a/main/logger.c b/main/logger.c index 9db33c954..9f03b4efa 100644 --- a/main/logger.c +++ b/main/logger.c @@ -388,6 +388,7 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo } chan->type = LOGTYPE_SYSLOG; + openlog("asterisk", LOG_PID, chan->facility); } else { if (!(chan->fileptr = fopen(chan->filename, "a"))) { /* Can't do real logging here since we're called with a lock diff --git a/main/strings.c b/main/strings.c index 9e885ebc3..db78a6cd2 100644 --- a/main/strings.c +++ b/main/strings.c @@ -234,8 +234,8 @@ int ast_strings_match(const char *left, const char *op, const char *right) { char *internal_op = (char *)op; char *internal_right = (char *)right; - float left_num; - float right_num; + double left_num; + double right_num; int scan_numeric = 0; if (!(left && right)) { @@ -297,7 +297,7 @@ regex: } equals: - scan_numeric = (sscanf(left, "%f", &left_num) && sscanf(internal_right, "%f", &right_num)); + scan_numeric = (sscanf(left, "%lf", &left_num) && sscanf(internal_right, "%lf", &right_num)); if (internal_op[0] == '=') { if (ast_strlen_zero(left) && ast_strlen_zero(internal_right)) { diff --git a/main/udptl.c b/main/udptl.c index e8410cc8b..a568cd1ec 100644 --- a/main/udptl.c +++ b/main/udptl.c @@ -769,6 +769,18 @@ struct ast_frame *ast_udptl_read(struct ast_udptl *udptl) return &ast_null_frame; } + /* + * If early media isn't turned on for the channel driver, it's going to + * drop this frame. By that time though, udptl has already incremented + * the expected sequence number so if the CPE re-sends, the second frame + * will be dropped as a dup even though the first frame never went through. + * So we drop the frame here if the channel isn't up. 'tag' is set by the + * channel drivers on T38_ENABLED or T38_PEER_REINVITE. + */ + if (udptl->tag == NULL) { + return &ast_null_frame; + } + if (udptl->nat) { /* Send to whoever sent to us */ if (ast_sockaddr_cmp(&udptl->them, &addr)) { diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c index a37b83146..a86f3129c 100644 --- a/res/ari/resource_bridges.c +++ b/res/ari/resource_bridges.c @@ -381,7 +381,7 @@ static int ari_bridges_play_helper(const char *args_media, return -1; } - if (ast_asprintf(playback_url, "/playback/%s", + if (ast_asprintf(playback_url, "/playbacks/%s", stasis_app_playback_get_id(playback)) == -1) { playback_url = NULL; ast_ari_response_alloc_failed(response); diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c index f722802d8..9e2db9de6 100644 --- a/res/ari/resource_channels.c +++ b/res/ari/resource_channels.c @@ -524,7 +524,7 @@ static void ari_channels_handle_play( return; } - if (ast_asprintf(&playback_url, "/playback/%s", + if (ast_asprintf(&playback_url, "/playbacks/%s", stasis_app_playback_get_id(playback)) == -1) { playback_url = NULL; ast_ari_response_error( diff --git a/res/res_ari.c b/res/res_ari.c index f39db16cd..4a0a22d79 100644 --- a/res/res_ari.c +++ b/res/res_ari.c @@ -304,10 +304,11 @@ void ast_ari_response_alloc_failed(struct ast_ari_response *response) void ast_ari_response_created(struct ast_ari_response *response, const char *url, struct ast_json *message) { + RAII_VAR(struct stasis_rest_handlers *, root, get_root_handler(), ao2_cleanup); response->message = message; response->response_code = 201; response->response_text = "Created"; - ast_str_append(&response->headers, 0, "Location: %s\r\n", url); + ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url); } static void add_allow_header(struct stasis_rest_handlers *handler, diff --git a/res/res_fax.c b/res/res_fax.c index 2fa64bc0f..6282b13d7 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -626,6 +626,8 @@ static const struct ast_datastore_info fax_datastore = { static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details); static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags); static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan); +static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan); + /*! \brief Copies fax detection and gateway framehooks during masquerades * @@ -2835,6 +2837,23 @@ static void destroy_gateway(void *data) ao2_cleanup(gateway->peer_write_format); } +static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) { + struct ast_fax_session_details *v21_details; + struct ast_fax_session *v21_session; + + if (!chan || !(v21_details = session_details_new())) { + return NULL; + } + + v21_details->caps = AST_FAX_TECH_V21_DETECT; + if (!(v21_session = fax_session_new(v21_details, chan, NULL, NULL))) { + ao2_ref(v21_details, -1); + return NULL; + } + + return v21_session; +} + /*! \brief Create a new fax gateway object. * \param chan the channel the gateway object will be attached to * \param details the fax session details @@ -2843,30 +2862,16 @@ static void destroy_gateway(void *data) static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details) { struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway); - struct ast_fax_session_details *v21_details; if (!gateway) { return NULL; } - if (!(v21_details = session_details_new())) { + if (!(gateway->chan_v21_session = fax_v21_session_new(chan))) { + ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(chan)); ao2_ref(gateway, -1); return NULL; } - v21_details->caps = AST_FAX_TECH_V21_DETECT; - if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) { - ao2_ref(v21_details, -1); - ao2_ref(gateway, -1); - return NULL; - } - - if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) { - ao2_ref(v21_details, -1); - ao2_ref(gateway, -1); - return NULL; - } - ao2_ref(v21_details, -1); - gateway->framehook = -1; details->caps = AST_FAX_TECH_GATEWAY; @@ -3360,6 +3365,11 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_channel_unlock(peer); gateway->bridged = 1; + if (!(gateway->peer_v21_session = fax_v21_session_new(peer))) { + ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(peer)); + ast_framehook_detach(chan, gateway->framehook); + return f; + } } if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) { @@ -3486,6 +3496,10 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d .disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */ }; + if (global_fax_debug) { + details->option.debug = AST_FAX_OPTFLAG_TRUE; + } + ast_string_field_set(details, result, "SUCCESS"); ast_string_field_set(details, resultstr, "gateway operation started successfully"); ast_string_field_set(details, error, "NO_ERROR"); diff --git a/res/res_hep.c b/res/res_hep.c index 69a8ab391..723b27df8 100644 --- a/res/res_hep.c +++ b/res/res_hep.c @@ -60,6 +60,15 @@ </enumlist> </description> </configOption> + <configOption name="uuid_type" default="call-id"> + <synopsis>The preferred type of UUID to pass to Homer.</synopsis> + <description> + <enumlist> + <enum name="call-id"><para>Use the PJSIP Call-Id</para></enum> + <enum name="channel"><para>Use the Asterisk channel name</para></enum> + </enumlist> + </description> + </configOption> <configOption name="capture_address" default="192.168.1.1:9061"> <synopsis>The address and port of the Homer server to send packets to.</synopsis> </configOption> @@ -231,6 +240,7 @@ struct hep_generic { struct hepv3_global_config { unsigned int enabled; /*!< Whether or not sending is enabled */ unsigned int capture_id; /*!< Capture ID for this agent */ + enum hep_uuid_type uuid_type; /*!< The preferred type of the UUID */ AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(capture_address); /*!< Address to send to */ AST_STRING_FIELD(capture_password); /*!< Password for Homer server */ @@ -329,6 +339,25 @@ static void *module_config_alloc(void) return config; } +/*! \brief Handler for the uuid_type attribute */ +static int uuid_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct hepv3_global_config *global_config = obj; + + if (strcasecmp(var->name, "uuid_type")) { + return -1; + } + + if (!strcasecmp(var->value, "channel")) { + global_config->uuid_type = HEP_UUID_TYPE_CHANNEL; + } else if (!strcasecmp(var->value, "call-id")) { + global_config->uuid_type = HEP_UUID_TYPE_CALL_ID; + } else { + return -1; + } + return 0; +} + /*! \brief HEPv3 run-time data destructor */ static void hepv3_data_dtor(void *obj) { @@ -376,6 +405,13 @@ static void capture_info_dtor(void *obj) ast_free(info->payload); } +enum hep_uuid_type hepv3_get_uuid_type(void) +{ + RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup); + + return config->general->uuid_type; +} + struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len) { struct hepv3_capture_info *info; @@ -607,6 +643,7 @@ static int load_module(void) aco_option_register(&cfg_info, "capture_address", ACO_EXACT, global_options, DEFAULT_HEP_SERVER, OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_address)); aco_option_register(&cfg_info, "capture_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_password)); aco_option_register(&cfg_info, "capture_id", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, STRFLDSET(struct hepv3_global_config, capture_id)); + aco_option_register_custom(&cfg_info, "uuid_type", ACO_EXACT, global_options, "call-id", uuid_type_handler, 0); if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) { goto error; diff --git a/res/res_hep.exports.in b/res/res_hep.exports.in index d09d3f409..df0f2b4f7 100644 --- a/res/res_hep.exports.in +++ b/res/res_hep.exports.in @@ -2,6 +2,7 @@ global: LINKER_SYMBOL_PREFIX*hepv3_send_packet; LINKER_SYMBOL_PREFIX*hepv3_create_capture_info; + LINKER_SYMBOL_PREFIX*hepv3_get_uuid_type; local: *; }; diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c index b5cf0b81e..936db9300 100644 --- a/res/res_hep_pjsip.c +++ b/res/res_hep_pjsip.c @@ -51,13 +51,18 @@ static char *assign_uuid(const pj_str_t *call_id, const pj_str_t *local_tag, con RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); pjsip_dialog *dlg; char *uuid = NULL; + enum hep_uuid_type uuid_type = hepv3_get_uuid_type(); - if ((dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE)) + if ((uuid_type == HEP_UUID_TYPE_CHANNEL) + && (dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE)) && (session = ast_sip_dialog_get_session(dlg)) && (session->channel)) { uuid = ast_strdup(ast_channel_name(session->channel)); - } else { + } + + /* If we couldn't get the channel or we never wanted it, default to the call-id */ + if (!uuid) { uuid = ast_malloc(pj_strlen(call_id) + 1); if (uuid) { @@ -77,13 +82,35 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) pjsip_cid_hdr *cid_hdr; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; + pjsip_tpmgr_fla2_param prm; capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start)); if (!capture_info) { return PJ_SUCCESS; } - pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); + /* Attempt to determine what IP address will we send this packet out of */ + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = tdata->tp_info.transport->key.type; + pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); + prm.local_if = PJ_TRUE; + + /* If we can't get the local address use what we have already */ + if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) { + pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); + } else { + if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { + snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } else { + snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } + } pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3); cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg); @@ -115,17 +142,39 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) char remote_buf[256]; char *uuid; struct hepv3_capture_info *capture_info; + pjsip_tpmgr_fla2_param prm; capture_info = hepv3_create_capture_info(&rdata->pkt_info.packet, rdata->pkt_info.len); if (!capture_info) { return PJ_SUCCESS; } - if (rdata->tp_info.transport->addr_len) { - pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); + if (!rdata->pkt_info.src_addr_len) { + return PJ_SUCCESS; } - if (rdata->pkt_info.src_addr_len) { - pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3); + pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3); + + /* Attempt to determine what IP address we probably received this packet on */ + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = rdata->tp_info.transport->key.type; + pj_strset2(&prm.dst_host, rdata->pkt_info.src_name); + prm.local_if = PJ_TRUE; + + /* If we can't get the local address use what we have already */ + if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), rdata->tp_info.pool, &prm) != PJ_SUCCESS) { + pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); + } else { + if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { + snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } else { + snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } } uuid = assign_uuid(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag); diff --git a/res/res_hep_rtcp.c b/res/res_hep_rtcp.c index 787512bbf..49a92539f 100644 --- a/res/res_hep_rtcp.c +++ b/res/res_hep_rtcp.c @@ -36,6 +36,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/res_hep.h" #include "asterisk/module.h" #include "asterisk/netsock2.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" #include "asterisk/stasis.h" #include "asterisk/rtp_engine.h" #include "asterisk/json.h" @@ -43,6 +45,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") static struct stasis_subscription *stasis_rtp_subscription; +static char *assign_uuid(struct ast_json *json_channel) +{ + const char *channel_name = ast_json_string_get(ast_json_object_get(json_channel, "name")); + enum hep_uuid_type uuid_type = hepv3_get_uuid_type(); + char *uuid = NULL; + + if (!channel_name) { + return NULL; + } + + if (uuid_type == HEP_UUID_TYPE_CALL_ID && ast_begins_with(channel_name, "PJSIP")) { + struct ast_channel *chan = ast_channel_get_by_name(channel_name); + char buf[128]; + + if (chan && !ast_func_read(chan, "CHANNEL(pjsip,call-id)", buf, sizeof(buf))) { + uuid = ast_strdup(buf); + } + + ast_channel_cleanup(chan); + } + + /* If we couldn't get the call-id or didn't want it, just use the channel name */ + if (!uuid) { + uuid = ast_strdup(channel_name); + } + + return uuid; +} + static void rtcp_message_handler(struct stasis_message *message) { @@ -94,7 +125,7 @@ static void rtcp_message_handler(struct stasis_message *message) ast_sockaddr_parse(&capture_info->src_addr, ast_json_string_get(from), PARSE_PORT_REQUIRE); ast_sockaddr_parse(&capture_info->dst_addr, ast_json_string_get(to), PARSE_PORT_REQUIRE); - capture_info->uuid = ast_strdup(ast_json_string_get(ast_json_object_get(json_channel, "name"))); + capture_info->uuid = assign_uuid(json_channel); if (!capture_info->uuid) { ao2_ref(capture_info, -1); return; diff --git a/res/res_pjsip.c b/res/res_pjsip.c index ae7155b6b..9162431a1 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -846,6 +846,56 @@ channel is hung up. By default this option is set to 0, which means do not check. </para></description> </configOption> + <configOption name="acl"> + <synopsis>List of IP ACL section names in acl.conf</synopsis> + <description><para> + This matches sections configured in <literal>acl.conf</literal>. The value is + defined as a list of comma-delimited section names. + </para></description> + </configOption> + <configOption name="deny"> + <synopsis>List of IP addresses to deny access from</synopsis> + <description><para> + The value is a comma-delimited list of IP addresses. IP addresses may + have a subnet mask appended. The subnet mask may be written in either + CIDR or dotted-decimal notation. Separate the IP address and subnet + mask with a slash ('/') + </para></description> + </configOption> + <configOption name="permit"> + <synopsis>List of IP addresses to permit access from</synopsis> + <description><para> + The value is a comma-delimited list of IP addresses. IP addresses may + have a subnet mask appended. The subnet mask may be written in either + CIDR or dotted-decimal notation. Separate the IP address and subnet + mask with a slash ('/') + </para></description> + </configOption> + <configOption name="contact_acl"> + <synopsis>List of Contact ACL section names in acl.conf</synopsis> + <description><para> + This matches sections configured in <literal>acl.conf</literal>. The value is + defined as a list of comma-delimited section names. + </para></description> + </configOption> + <configOption name="contact_deny"> + <synopsis>List of Contact header addresses to deny</synopsis> + <description><para> + The value is a comma-delimited list of IP addresses. IP addresses may + have a subnet mask appended. The subnet mask may be written in either + CIDR or dotted-decimal notation. Separate the IP address and subnet + mask with a slash ('/') + </para></description> + </configOption> + <configOption name="contact_permit"> + <synopsis>List of Contact header addresses to permit</synopsis> + <description><para> + The value is a comma-delimited list of IP addresses. IP addresses may + have a subnet mask appended. The subnet mask may be written in either + CIDR or dotted-decimal notation. Separate the IP address and subnet + mask with a slash ('/') + </para></description> + </configOption> </configObject> <configObject name="auth"> <synopsis>Authentication type</synopsis> @@ -1092,6 +1142,13 @@ If <literal>0</literal> no timeout. Time in fractional seconds. </para></description> </configOption> + <configOption name="authenticate_qualify" default="no"> + <synopsis>Authenticates a qualify request if needed</synopsis> + <description><para> + If true and a qualify request receives a challenge or authenticate response + authentication is attempted before declaring the contact available. + </para></description> + </configOption> <configOption name="outbound_proxy"> <synopsis>Outbound proxy used when sending OPTIONS request</synopsis> <description><para> diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index d2c087487..b9208976f 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -378,6 +378,10 @@ static struct ast_sip_transport_state *find_or_create_temporary_state(struct ast new_state->type = transport->type; pjsip_tls_setting_default(&new_state->tls); +#ifdef HAVE_PJSIP_TLS_TRANSPORT_PROTO + /* proto must be forced to 0 to enable all protocols otherwise only TLS will work */ + new_state->tls.proto = 0; +#endif new_state->tls.ciphers = new_state->ciphers; ao2_ref(new_state, +1); @@ -558,11 +562,17 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) } } else if (transport->type == AST_TRANSPORT_TCP) { pjsip_tcp_transport_cfg cfg; + int option = 1; pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family); cfg.bind_addr = temp_state->state->host; cfg.async_cnt = transport->async_operations; set_qos(transport, &cfg.qos_params); + cfg.sockopt_params.options[0].level = pj_SOL_TCP(); + cfg.sockopt_params.options[0].optname = pj_TCP_NODELAY(); + cfg.sockopt_params.options[0].optval = &option; + cfg.sockopt_params.options[0].optlen = sizeof(option); + cfg.sockopt_params.cnt = 1; for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { if (perm_state && perm_state->state && perm_state->state->factory diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index db1d0794b..ef06456a7 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -1121,6 +1121,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400); ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout)); + ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify)); ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy)); ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent)); ast_sorcery_object_field_register(sorcery, "contact", "reg_server", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, reg_server)); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 8e7e95a5d..2e0df75e3 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -265,6 +265,65 @@ static const struct ast_sorcery_observer endpoint_observers = { .deleted = endpoint_deleted_observer, }; +static int endpoint_acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + int error = 0; + int ignore; + + if (ast_strlen_zero(var->value)) return 0; + + if (!strncmp(var->name, "contact_", 8)) { + ast_append_acl(var->name + 8, var->value, &endpoint->contact_acl, &error, &ignore); + } else { + ast_append_acl(var->name, var->value, &endpoint->acl, &error, &ignore); + } + + return error; +} + +static int acl_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + struct ast_acl_list *acl_list; + struct ast_acl *first_acl; + + if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->acl)) { + AST_LIST_LOCK(acl_list); + first_acl = AST_LIST_FIRST(acl_list); + if (ast_strlen_zero(first_acl->name)) { + *buf = "deny/permit"; + } else { + *buf = first_acl->name; + } + AST_LIST_UNLOCK(acl_list); + } + + *buf = ast_strdup(*buf); + return 0; +} + +static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + struct ast_acl_list *acl_list; + struct ast_acl *first_acl; + + if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->contact_acl)) { + AST_LIST_LOCK(acl_list); + first_acl = AST_LIST_FIRST(acl_list); + if (ast_strlen_zero(first_acl->name)) { + *buf = "deny/permit"; + } else { + *buf = first_acl->name; + } + AST_LIST_UNLOCK(acl_list); + } + + *buf = ast_strdup(*buf); + return 0; +} + static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -275,8 +334,8 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, endpoint->dtmf = AST_SIP_DTMF_INBAND; } else if (!strcasecmp(var->value, "info")) { endpoint->dtmf = AST_SIP_DTMF_INFO; - } else if (!strcasecmp(var->value, "auto")) { - endpoint->dtmf = AST_SIP_DTMF_AUTO; + } else if (!strcasecmp(var->value, "auto")) { + endpoint->dtmf = AST_SIP_DTMF_AUTO; } else if (!strcasecmp(var->value, "none")) { endpoint->dtmf = AST_SIP_DTMF_NONE; } else { @@ -298,7 +357,7 @@ static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf) case AST_SIP_DTMF_INFO : *buf = "info"; break; case AST_SIP_DTMF_AUTO : - *buf = "auto"; break; + *buf = "auto"; break; default: *buf = "none"; } @@ -1762,6 +1821,12 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, message_context)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accountcode", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, accountcode)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "deny", "", endpoint_acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "permit", "", endpoint_acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "acl", "", endpoint_acl_handler, acl_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_deny", "", endpoint_acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c index cbe955728..2ab954eb0 100644 --- a/res/res_pjsip/pjsip_distributor.c +++ b/res/res_pjsip/pjsip_distributor.c @@ -21,6 +21,7 @@ #include <pjsip.h> #include "asterisk/res_pjsip.h" +#include "asterisk/acl.h" #include "include/res_pjsip_private.h" #include "asterisk/taskprocessor.h" #include "asterisk/threadpool.h" @@ -230,20 +231,33 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG || pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || rdata->msg_info.to->tag.slen != 0) { - return pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag, + dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag, remote_tag, PJ_TRUE); + if (dlg) { + return dlg; + } } - /* Incoming CANCEL without a to-tag can't use same method for finding the - * dialog. Instead, we have to find the matching INVITE transaction and - * then get the dialog from the transaction + /* + * There may still be a matching dialog if this is + * 1) an incoming CANCEL request without a to-tag + * 2) an incoming response to a dialog-creating request. */ - pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS, - pjsip_get_invite_method(), rdata); + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { + /* CANCEL requests will need to match the INVITE we initially received. Any + * other request type will either have been matched already or is not in + * dialog + */ + pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS, + pjsip_get_invite_method(), rdata); + } else { + pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, + &rdata->msg_info.cseq->method, rdata); + } tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (!tsx) { - ast_log(LOG_ERROR, "Could not find matching INVITE transaction for CANCEL request\n"); + ast_debug(3, "Could not find matching transaction for %s\n", rdata->msg_info.info); return NULL; } @@ -380,19 +394,21 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void) return artificial_endpoint; } -static void log_unidentified_request(pjsip_rx_data *rdata, unsigned int count, unsigned int period) +static void log_failed_request(pjsip_rx_data *rdata, char *msg, unsigned int count, unsigned int period) { char from_buf[PJSIP_MAX_URL_SIZE]; char callid_buf[PJSIP_MAX_URL_SIZE]; + char method_buf[PJSIP_MAX_URL_SIZE]; pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from_buf, PJSIP_MAX_URL_SIZE); ast_copy_pj_str(callid_buf, &rdata->msg_info.cid->id, PJSIP_MAX_URL_SIZE); + ast_copy_pj_str(method_buf, &rdata->msg_info.msg->line.req.method.name, PJSIP_MAX_URL_SIZE); if (count) { - ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found" + ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s" " after %u tries in %.3f ms\n", - from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, count, period / 1000.0); + method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg, count, period / 1000.0); } else { - ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found", - from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf); + ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s\n", + method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg); } } @@ -405,7 +421,7 @@ static void check_endpoint(pjsip_rx_data *rdata, struct unidentified_request *un unid->count++; if (ms < (unidentified_period * 1000) && unid->count >= unidentified_count) { - log_unidentified_request(rdata, unid->count, ms); + log_failed_request(rdata, "No matching endpoint found", unid->count, ms); ast_sip_report_invalid_endpoint(name, rdata); } ao2_unlock(unid); @@ -479,7 +495,7 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) ao2_ref(unid, -1); ao2_unlock(unidentified_requests); } else { - log_unidentified_request(rdata, 0, 0); + log_failed_request(rdata, "No matching endpoint found", 0, 0); ast_sip_report_invalid_endpoint(name, rdata); } } @@ -487,6 +503,79 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) return PJ_FALSE; } +static int apply_endpoint_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint) +{ + struct ast_sockaddr addr; + + if (ast_acl_list_is_empty(endpoint->acl)) { + return 0; + } + + memset(&addr, 0, sizeof(addr)); + ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID); + ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port); + + if (ast_apply_acl(endpoint->acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) { + log_failed_request(rdata, "Not match Endpoint ACL", 0, 0); + ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_acl"); + return 1; + } + return 0; +} + +static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs) +{ + pjsip_sip_uri *sip_uri; + char host[256]; + + if (!contact || contact->star) { + *addrs = NULL; + return 0; + } + if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { + *addrs = NULL; + return 0; + } + sip_uri = pjsip_uri_get_uri(contact->uri); + ast_copy_pj_str(host, &sip_uri->host, sizeof(host)); + return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC); +} + +static int apply_endpoint_contact_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint) +{ + int num_contact_addrs; + int forbidden = 0; + struct ast_sockaddr *contact_addrs; + int i; + pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; + + if (ast_acl_list_is_empty(endpoint->contact_acl)) { + return 0; + } + + while ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { + num_contact_addrs = extract_contact_addr(contact, &contact_addrs); + if (num_contact_addrs <= 0) { + continue; + } + for (i = 0; i < num_contact_addrs; ++i) { + if (ast_apply_acl(endpoint->contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) { + log_failed_request(rdata, "Not match Endpoint Contact ACL", 0, 0); + ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_contact_acl"); + forbidden = 1; + break; + } + } + ast_free(contact_addrs); + if (forbidden) { + /* No use checking other contacts if we already have failed ACL check */ + break; + } + } + + return forbidden; +} + static pj_bool_t authenticate(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); @@ -494,6 +583,15 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) ast_assert(endpoint != NULL); + if (endpoint!=artificial_endpoint) { + if (apply_endpoint_acl(rdata, endpoint) || apply_endpoint_contact_acl(rdata, endpoint)) { + if (!is_ack) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + } + return PJ_TRUE; + } + } + if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) { pjsip_tx_data *tdata; struct unidentified_request *unid; @@ -515,10 +613,12 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) pjsip_tx_data_dec_ref(tdata); return PJ_FALSE; case AST_SIP_AUTHENTICATION_FAILED: + log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); return PJ_TRUE; case AST_SIP_AUTHENTICATION_ERROR: + log_failed_request(rdata, "Error to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); pjsip_tx_data_dec_ref(tdata); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index f832e6f78..b9339f60b 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -1036,17 +1036,11 @@ int ast_sip_initialize_sorcery_qualify(void) return 0; } -static int qualify_and_schedule_cb(void *obj, void *arg, int flags) +static void qualify_and_schedule_contact(struct ast_sip_contact *contact) { - struct ast_sip_contact *contact = obj; - struct ast_sip_aor *aor = arg; int initial_interval; int max_time = ast_sip_get_max_initial_qualify_time(); - contact->qualify_frequency = aor->qualify_frequency; - contact->qualify_timeout = aor->qualify_timeout; - contact->authenticate_qualify = aor->authenticate_qualify; - /* Delay initial qualification by a random fraction of the specified interval */ if (max_time && max_time < contact->qualify_frequency) { initial_interval = max_time; @@ -1062,26 +1056,47 @@ static int qualify_and_schedule_cb(void *obj, void *arg, int flags) } else { update_contact_status(contact, UNKNOWN); } +} + +static int qualify_and_schedule_cb_with_aor(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + struct ast_sip_aor *aor = arg; + + contact->qualify_frequency = aor->qualify_frequency; + contact->qualify_timeout = aor->qualify_timeout; + contact->authenticate_qualify = aor->authenticate_qualify; + + qualify_and_schedule_contact(contact); + + return 0; +} + +static int qualify_and_schedule_cb_without_aor(void *obj, void *arg, int flags) +{ + qualify_and_schedule_contact((struct ast_sip_contact *) obj); return 0; } /*! * \internal - * \brief Qualify and schedule an endpoint's contacts + * \brief Qualify and schedule an aor's contacts * - * \details For the given endpoint retrieve its list of aors, qualify all - * contacts, and schedule for checks if configured. + * \details For the given aor check if it has permanent contacts, + * qualify all contacts and schedule for checks if configured. */ static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags) { struct ast_sip_aor *aor = obj; struct ao2_container *contacts; - contacts = ast_sip_location_retrieve_aor_contacts(aor); - if (contacts) { - ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor); - ao2_ref(contacts, -1); + if (aor->permanent_contacts) { + contacts = ast_sip_location_retrieve_aor_contacts(aor); + if (contacts) { + ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor); + ao2_ref(contacts, -1); + } } return 0; @@ -1104,6 +1119,7 @@ static void qualify_and_schedule_all(void) { struct ast_variable *var = ast_variable_new("qualify_frequency >", "0", ""); struct ao2_container *aors; + struct ao2_container *contacts; if (!var) { return; @@ -1111,16 +1127,22 @@ static void qualify_and_schedule_all(void) aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor", AST_RETRIEVE_FLAG_MULTIPLE, var); - ast_variables_destroy(var); - ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL); - if (!aors) { - return; + if (aors) { + ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL); + ao2_ref(aors, -1); } - ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL); - ao2_ref(aors, -1); + contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), + "contact", AST_RETRIEVE_FLAG_MULTIPLE, var); + if (contacts) { + ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_without_aor, NULL); + ao2_ref(contacts, -1); + } + + ast_variables_destroy(var); + } static int format_contact_status(void *obj, void *arg, int flags) @@ -1186,7 +1208,7 @@ static void aor_observer_modified(const void *obj) contacts = ast_sip_location_retrieve_aor_contacts(aor); if (contacts) { - ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor); + ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor); ao2_ref(contacts, -1); } } diff --git a/res/res_pjsip_dtmf_info.c b/res/res_pjsip_dtmf_info.c index ede515d1c..17297e117 100644 --- a/res/res_pjsip_dtmf_info.c +++ b/res/res_pjsip_dtmf_info.c @@ -103,13 +103,13 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj if (!body || !body->len) { /* need to return 200 OK on empty body */ send_response(session, rdata, 200); - return 0; + return 1; } res = body->print_body(body, buf, body->len); if (res < 0) { send_response(session, rdata, 500); - return 0; + return 1; } buf[res] = '\0'; @@ -150,11 +150,12 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj } send_response(session, rdata, event ? 200 : 500); - return event ? 0 : -1; + return 1; } static struct ast_sip_session_supplement dtmf_info_supplement = { .method = "INFO", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST, .incoming_request = dtmf_info_incoming_request, }; diff --git a/res/res_pjsip_empty_info.c b/res/res_pjsip_empty_info.c new file mode 100644 index 000000000..09109ba2c --- /dev/null +++ b/res/res_pjsip_empty_info.c @@ -0,0 +1,89 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Digium, Inc. + * + * Bradley Latus <brad.latus@gmail.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_pjsip</depend> + <depend>res_pjsip_session</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_REGISTER_FILE() + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" +#include "asterisk/module.h" + +static void send_response(struct ast_sip_session *session, + struct pjsip_rx_data *rdata, int code) +{ + pjsip_tx_data *tdata; + pjsip_dialog *dlg = session->inv_session->dlg; + + if (pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata) == PJ_SUCCESS) { + struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_dlg_send_response(dlg, tsx, tdata); + } +} + +static int empty_info_incoming_request(struct ast_sip_session *session, + struct pjsip_rx_data *rdata) +{ + if (!rdata->msg_info.ctype) { + /* Need to return 200 OK on empty body */ + /* Some SBCs use empty INFO as a KEEPALIVE */ + send_response(session, rdata, 200); + return 1; + } + + /* Let another module respond */ + return 0; + +} + +static struct ast_sip_session_supplement empty_info_supplement = { + .method = "INFO", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST, + .incoming_request = empty_info_incoming_request, +}; + +static int load_module(void) +{ + CHECK_PJSIP_SESSION_MODULE_LOADED(); + + ast_sip_session_register_supplement(&empty_info_supplement); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&empty_info_supplement); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Empty INFO Support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, +); diff --git a/res/res_pjsip_one_touch_record_info.c b/res/res_pjsip_one_touch_record_info.c index 8b1ff9dab..e15b0d815 100644 --- a/res/res_pjsip_one_touch_record_info.c +++ b/res/res_pjsip_one_touch_record_info.c @@ -72,13 +72,13 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip if (!session->channel) { send_response(session, 481, rdata); - return 0; + return 1; } /* Is this endpoint configured with One Touch Recording? */ if (!session->endpoint->info.recording.enabled || ast_strlen_zero(feature)) { send_response(session, 403, rdata); - return 0; + return 1; } ast_channel_lock(session->channel); @@ -87,7 +87,7 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip if (feature_res || ast_strlen_zero(feature_code)) { send_response(session, 403, rdata); - return 0; + return 1; } for (digit = feature_code; *digit; ++digit) { @@ -97,11 +97,12 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip send_response(session, 200, rdata); - return 0; + return 1; } static struct ast_sip_session_supplement info_supplement = { .method = "INFO", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST, .incoming_request = handle_incoming_request, }; diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c index 60f9bbb17..ab8a6c9bd 100644 --- a/res/res_pjsip_outbound_publish.c +++ b/res/res_pjsip_outbound_publish.c @@ -406,22 +406,30 @@ static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_hand ao2_ref(states, -1); } -struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name) +static struct ast_sip_outbound_publish_state *sip_publish_state_get(const char *id) { - RAII_VAR(struct ao2_container *, states, - ao2_global_obj_ref(current_states), ao2_cleanup); - RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup); + struct ao2_container *states = ao2_global_obj_ref(current_states); + struct ast_sip_outbound_publish_state *res; if (!states) { return NULL; } - state = ao2_find(states, name, OBJ_SEARCH_KEY); + res = ao2_find(states, id, OBJ_SEARCH_KEY); + ao2_ref(states, -1); + return res; +} + +struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name) +{ + struct ast_sip_outbound_publish_state *state = sip_publish_state_get(name); + if (!state) { return NULL; } ao2_ref(state->client, +1); + ao2_ref(state, -1); return state->client; } @@ -687,8 +695,15 @@ static int explicit_publish_destroy(void *data) { struct ast_sip_outbound_publish_client *client = data; - pjsip_publishc_destroy(client->client); - ao2_ref(client, -1); + /* + * If there is no pjsip publishing client then we obviously don't need + * to destroy it. Also, the ref for the Asterisk publishing client that + * pjsip had would not exist or should already be gone as well. + */ + if (client->client) { + pjsip_publishc_destroy(client->client); + ao2_ref(client, -1); + } return 0; } @@ -868,6 +883,11 @@ static int sip_outbound_publish_client_alloc(void *data) /*! \brief Callback function for publish client responses */ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param) { +#define DESTROY_CLIENT() do { \ + pjsip_publishc_destroy(client->client); \ + client->client = NULL; \ + ao2_ref(client, -1); } while (0) + RAII_VAR(struct ast_sip_outbound_publish_client *, client, ao2_bump(param->token), ao2_cleanup); RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(client->publish), ao2_cleanup); SCOPED_AO2LOCK(lock, client); @@ -885,8 +905,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param) ao2_ref(client, -1); } /* Once the destroy is called this callback will not get called any longer, so drop the client ref */ - pjsip_publishc_destroy(client->client); - ao2_ref(client, -1); + DESTROY_CLIENT(); return; } @@ -904,9 +923,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param) client->auth_attempts++; if (client->auth_attempts == publish->max_auth_attempts) { - pjsip_publishc_destroy(client->client); - client->client = NULL; - + DESTROY_CLIENT(); ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n", ast_sorcery_object_get_id(publish)); @@ -918,9 +935,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param) client->auth_attempts = 0; if (param->code == 412) { - pjsip_publishc_destroy(client->client); - client->client = NULL; - + DESTROY_CLIENT(); if (sip_outbound_publish_client_alloc(client)) { ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n", ast_sorcery_object_get_id(publish)); @@ -935,10 +950,9 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param) expires = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL); if (!expires || !expires->ivalue) { + DESTROY_CLIENT(); ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n", ast_sorcery_object_get_id(publish)); - pjsip_publishc_destroy(client->client); - client->client = NULL; goto end; } @@ -1065,25 +1079,89 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc( return state; } -/*! \brief Apply function which finds or allocates a state structure */ -static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj) +static int initialize_publish_client(struct ast_sip_outbound_publish *publish, + struct ast_sip_outbound_publish_state *state) { - RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup); - RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup); - struct ast_sip_outbound_publish *applied = obj; + if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) { + ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n", + ast_sorcery_object_get_id(publish)); + return -1; + } - if (ast_strlen_zero(applied->server_uri)) { + return 0; +} + +static int validate_publish_config(struct ast_sip_outbound_publish *publish) +{ + if (ast_strlen_zero(publish->server_uri)) { ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n", - ast_sorcery_object_get_id(applied)); + ast_sorcery_object_get_id(publish)); return -1; - } else if (ast_strlen_zero(applied->event)) { + } else if (ast_strlen_zero(publish->event)) { ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n", - ast_sorcery_object_get_id(applied)); + ast_sorcery_object_get_id(publish)); return -1; } + return 0; +} + +static int current_state_reusable(struct ast_sip_outbound_publish *publish, + struct ast_sip_outbound_publish_state *current_state) +{ + struct ast_sip_outbound_publish *old_publish; + if (!can_reuse_publish(current_state->client->publish, publish)) { + /* + * Something significant has changed in the configuration, so we are + * unable to use the old state object. The current state needs to go + * away and a new one needs to be created. + */ + return 0; + } + + /* + * We can reuse the current state object so keep it, but swap out the + * underlying publish object with the new one. + */ + old_publish = current_state->client->publish; + current_state->client->publish = publish; + if (initialize_publish_client(publish, current_state)) { + /* + * If the state object fails to re-initialize then swap + * the old publish info back in. + */ + current_state->client->publish = publish; + return -1; + } + + /* + * Since we swapped out the publish object the new one needs a ref + * while the old one needs to go away. + */ + ao2_ref(current_state->client->publish, +1); + ao2_cleanup(old_publish); + + /* Tell the caller that the current state object should be used */ + return 1; +} + +/*! \brief Apply function which finds or allocates a state structure */ +static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj) +{ +#define ADD_TO_NEW_STATES(__obj) \ + do { if (__obj) { \ + ao2_link(new_states, __obj); \ + ao2_ref(__obj, -1); } } while (0) + + struct ast_sip_outbound_publish *applied = obj; + struct ast_sip_outbound_publish_state *current_state, *new_state; + int res; + + /* + * New states are being loaded or reloaded. We'll need to add the new + * object if created/updated, or keep the old object if an error occurs. + */ if (!new_states) { - /* make sure new_states has been allocated as we will be adding to it */ new_states = ao2_container_alloc_options( AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS, outbound_publish_state_hash, outbound_publish_state_cmp); @@ -1094,35 +1172,44 @@ static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *o } } - if (states) { - state = ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY); - if (state) { - if (can_reuse_publish(state->client->publish, applied)) { - ao2_replace(state->client->publish, applied); - } else { - ao2_ref(state, -1); - state = NULL; - } - } + /* If there is current state we'll want to maintain it if any errors occur */ + current_state = sip_publish_state_get(ast_sorcery_object_get_id(applied)); + + if ((res = validate_publish_config(applied))) { + ADD_TO_NEW_STATES(current_state); + return res; } - if (!state) { - state = sip_outbound_publish_state_alloc(applied); - if (!state) { - ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n", - ast_sorcery_object_get_id(applied)); - return -1; - }; + if (current_state && (res = current_state_reusable(applied, current_state))) { + /* + * The current state object was able to be reused, or an error + * occurred. Either way we keep the current state and be done. + */ + ADD_TO_NEW_STATES(current_state); + return res == 1 ? 0 : -1; } - if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) { - ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n", + /* + * No current state was found or it was unable to be reused. Either way + * we'll need to create a new state object. + */ + new_state = sip_outbound_publish_state_alloc(applied); + if (!new_state) { + ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n", ast_sorcery_object_get_id(applied)); + ADD_TO_NEW_STATES(current_state); + return -1; + }; + + if (initialize_publish_client(applied, new_state)) { + ADD_TO_NEW_STATES(current_state); + ao2_ref(new_state, -1); return -1; } - ao2_link(new_states, state); - return 0; + ADD_TO_NEW_STATES(new_state); + ao2_cleanup(current_state); + return res; } static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) @@ -1141,6 +1228,7 @@ static int load_module(void) if (ast_sorcery_object_register(ast_sip_get_sorcery(), "outbound-publish", sip_outbound_publish_alloc, NULL, sip_outbound_publish_apply)) { + ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n"); return AST_MODULE_LOAD_DECLINE; } @@ -1200,7 +1288,7 @@ static int unload_module(void) /* wait for items to unpublish */ ast_verb(5, "Waiting to complete unpublishing task(s)\n"); - while (unloading.count) { + while (unloading.count && !res) { res = ast_cond_timedwait(&unloading.cond, &unloading.lock, &end); } ast_mutex_unlock(&unloading.lock); @@ -1213,6 +1301,7 @@ static int unload_module(void) "in the allowed time\n", unloading.count); } else { ast_verb(5, "All items successfully unpublished\n"); + ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish"); } return res; diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 8a40cce23..6f17b2072 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -1096,7 +1096,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, "<%s:%s@%s%.*s%s:%d%s%s%s%s>", - (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip", + ((pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) && PJSIP_URI_SCHEME_IS_SIPS(uri)) ? "sips" : "sip", user, (type & PJSIP_TRANSPORT_IPV6) ? "[" : "", (int)local_addr.slen, @@ -1912,6 +1912,26 @@ static const struct ast_sorcery_instance_observer observer_callbacks_registratio .object_type_loaded = registration_loaded_observer, }; +static void registration_deleted_observer(const void *obj) +{ + const struct sip_outbound_registration *registration = obj; + struct ao2_container *states; + + states = ao2_global_obj_ref(current_states); + if (!states) { + /* Global container has gone. Likely shutting down. */ + return; + } + + ao2_find(states, ast_sorcery_object_get_id(registration), OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY); + + ao2_ref(states, -1); +} + +static const struct ast_sorcery_observer registration_observer = { + .deleted = registration_deleted_observer, +}; + static int unload_module(void) { int remaining; @@ -2011,7 +2031,9 @@ static int load_module(void) if (ast_sorcery_instance_observer_add(ast_sip_get_sorcery(), &observer_callbacks_registrations) || ast_sorcery_observer_add(ast_sip_get_sorcery(), "auth", - &observer_callbacks_auth)) { + &observer_callbacks_auth) + || ast_sorcery_observer_add(ast_sip_get_sorcery(), "registration", + ®istration_observer)) { ast_log(LOG_ERROR, "Unable to register observers.\n"); unload_module(); return AST_MODULE_LOAD_FAILURE; diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c index 3218b0a0c..002d976d4 100644 --- a/res/res_pjsip_publish_asterisk.c +++ b/res/res_pjsip_publish_asterisk.c @@ -862,6 +862,7 @@ static int load_module(void) ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication"); if (ast_sorcery_object_register(ast_sip_get_sorcery(), "asterisk-publication", asterisk_publication_config_alloc, NULL, NULL)) { + ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n"); return AST_MODULE_LOAD_DECLINE; } @@ -919,6 +920,7 @@ static int unload_module(void) ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler); ast_sip_unregister_event_publisher_handler(&asterisk_devicestate_publisher_handler); ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler); + ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication"); return 0; } diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c index e5de9f7bb..1aec0be95 100644 --- a/res/res_sorcery_astdb.c +++ b/res/res_sorcery_astdb.c @@ -79,6 +79,58 @@ static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, v return ast_db_put(family, ast_sorcery_object_get_id(object), value); } +/*! \brief Internal helper function which returns a filtered objectset. + * + * The following are filtered out of the objectset: + * \li Fields that are not registered with sorcery. + * + * \param objectset Objectset to filter. + * \param sorcery The sorcery instance that is requesting an objectset. + * \param type The object type + * + * \return The filtered objectset + */ +static struct ast_variable *sorcery_astdb_filter_objectset(struct ast_variable *objectset, const struct ast_sorcery *sorcery, + const char *type) +{ + struct ast_variable *previous = NULL, *field = objectset; + struct ast_sorcery_object_type *object_type; + + object_type = ast_sorcery_get_object_type(sorcery, type); + if (!object_type) { + ast_log(LOG_WARNING, "Unknown sorcery object type %s. Expect errors\n", type); + return objectset; + } + + while (field) { + struct ast_variable *removed; + + if (ast_sorcery_is_object_field_registered(object_type, field->name)) { + previous = field; + field = field->next; + continue; + } + + ast_debug(1, "Filtering out astdb field '%s' from retrieval\n", field->name); + + if (previous) { + previous->next = field->next; + } else { + objectset = field->next; + } + + removed = field; + field = field->next; + removed->next = NULL; + + ast_variables_destroy(removed); + } + + ao2_cleanup(object_type); + + return objectset; +} + /*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects) { @@ -97,23 +149,25 @@ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorc const char *key = entry->key + strlen(family) + 2; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ast_json_error error; - RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy); void *object = NULL; if (!(json = ast_json_load_string(entry->data, &error))) { return NULL; } + if (ast_json_to_ast_variables(json, &existing) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) { return NULL; } + existing = sorcery_astdb_filter_objectset(existing, sorcery, type); + if (fields && !ast_variable_lists_match(existing, fields, 0)) { continue; } if (!(object = ast_sorcery_alloc(sorcery, type, key)) || - ast_sorcery_objectset_apply(sorcery, object, objset)) { + ast_sorcery_objectset_apply(sorcery, object, existing)) { ao2_cleanup(object); return NULL; } @@ -149,6 +203,7 @@ static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void * if (ast_db_get_allocated(family, id, &value) || !(json = ast_json_load_string(value, &error)) || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) + || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) || !(object = ast_sorcery_alloc(sorcery, type, id)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id); @@ -261,6 +316,7 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void continue; } else if (!(json = ast_json_load_string(entry->data, &error)) || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) + || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) || !(object = ast_sorcery_alloc(sorcery, type, key)) || ast_sorcery_objectset_apply(sorcery, object, objset)) { regfree(&expression); |