diff options
Diffstat (limited to 'channels')
-rw-r--r-- | channels/Makefile | 1 | ||||
-rw-r--r-- | channels/chan_sip.c | 2408 | ||||
-rw-r--r-- | channels/sip/config_parser.c | 657 | ||||
-rw-r--r-- | channels/sip/include/config_parser.h | 50 | ||||
-rw-r--r-- | channels/sip/include/reqresp_parser.h | 57 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 1280 | ||||
-rw-r--r-- | channels/sip/include/sip_utils.h | 34 | ||||
-rw-r--r-- | channels/sip/reqresp_parser.c | 398 |
8 files changed, 2810 insertions, 2075 deletions
diff --git a/channels/Makefile b/channels/Makefile index e0838a9bf..a279c3b6b 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -69,6 +69,7 @@ dist-clean:: rm -f h323/Makefile $(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): iax2-parser.o iax2-provision.o +$(if $(filter chan_sip,$(EMBEDDED_MODS)),modules.link,chan_sip.so): sip/config_parser.o sip/reqresp_parser.o $(if $(filter chan_dahdi,$(EMBEDDED_MODS)),modules.link,chan_dahdi.so): sig_analog.o sig_pri.o ifneq ($(filter chan_h323,$(EMBEDDED_MODS)),) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index a1536ff7a..b48cbe355 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -218,7 +218,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/paths.h" /* need ast_config_AST_SYSTEM_NAME */ #include "asterisk/lock.h" -#include "asterisk/channel.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/pbx.h" @@ -230,7 +229,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" -#include "asterisk/app.h" #include "asterisk/musiconhold.h" #include "asterisk/dsp.h" #include "asterisk/features.h" @@ -239,8 +237,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/causes.h" #include "asterisk/utils.h" #include "asterisk/file.h" -#include "asterisk/astobj.h" -#include "asterisk/test.h" /* Uncomment the define below, if you are having refcount related memory leaks. With this uncommented, this module will generate a file, /tmp/refs, which contains @@ -256,8 +252,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj2.h" #include "asterisk/dnsmgr.h" #include "asterisk/devicestate.h" -#include "asterisk/linkedlists.h" -#include "asterisk/stringfields.h" #include "asterisk/monitor.h" #include "asterisk/netsock.h" #include "asterisk/localtime.h" @@ -266,10 +260,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/translate.h" #include "asterisk/ast_version.h" #include "asterisk/event.h" -#include "asterisk/tcptls.h" #include "asterisk/stun.h" #include "asterisk/cel.h" -#include "asterisk/strings.h" +#include "sip/include/sip.h" +#include "sip/include/config_parser.h" +#include "sip/include/reqresp_parser.h" +#include "sip/include/sip_utils.h" /*** DOCUMENTATION <application name="SIPDtmfMode" language="en_US"> @@ -546,83 +542,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </manager> ***/ -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -/* Arguments for find_peer */ -#define FINDUSERS (1 << 0) -#define FINDPEERS (1 << 1) -#define FINDALLDEVICES (FINDUSERS | FINDPEERS) - -#define SIPBUFSIZE 512 /*!< Buffer size for many operations */ - -#define XMIT_ERROR -2 - -#define SIP_RESERVED ";/?:@&=+$,# " /*!< Reserved characters in the username part of the URI */ - -#define DEFAULT_DEFAULT_EXPIRY 120 -#define DEFAULT_MIN_EXPIRY 60 -#define DEFAULT_MAX_EXPIRY 3600 -#define DEFAULT_MWI_EXPIRY 3600 -#define DEFAULT_REGISTRATION_TIMEOUT 20 -#define DEFAULT_MAX_FORWARDS "70" - -/* guard limit must be larger than guard secs */ -/* guard min must be < 1000, and should be >= 250 */ -#define EXPIRY_GUARD_SECS 15 /*!< How long before expiry do we reregister */ -#define EXPIRY_GUARD_LIMIT 30 /*!< Below here, we use EXPIRY_GUARD_PCT instead of - EXPIRY_GUARD_SECS */ -#define EXPIRY_GUARD_MIN 500 /*!< This is the minimum guard time applied. If - GUARD_PCT turns out to be lower than this, it - will use this time instead. - This is in milliseconds. */ -#define EXPIRY_GUARD_PCT 0.20 /*!< Percentage of expires timeout to use when - below EXPIRY_GUARD_LIMIT */ -#define DEFAULT_EXPIRY 900 /*!< Expire slowly */ - static int min_expiry = DEFAULT_MIN_EXPIRY; /*!< Minimum accepted registration time */ static int max_expiry = DEFAULT_MAX_EXPIRY; /*!< Maximum accepted registration time */ static int default_expiry = DEFAULT_DEFAULT_EXPIRY; static int mwi_expiry = DEFAULT_MWI_EXPIRY; -#define DEFAULT_QUALIFY_GAP 100 -#define DEFAULT_QUALIFY_PEERS 1 - - -#define CALLERID_UNKNOWN "Anonymous" -#define FROMDOMAIN_INVALID "anonymous.invalid" - -#define DEFAULT_MAXMS 2000 /*!< Qualification: Must be faster than 2 seconds by default */ -#define DEFAULT_QUALIFYFREQ 60 * 1000 /*!< Qualification: How often to check for the host to be up */ -#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */ - -#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */ -#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */ -#define DEFAULT_TIMER_T1 500 /*!< SIP timer T1 (according to RFC 3261) */ -#define SIP_TRANS_TIMEOUT 64 * DEFAULT_TIMER_T1 /*!< SIP request timeout (rfc 3261) 64*T1 - \todo Use known T1 for timeout (peerpoke) - */ -#define DEFAULT_TRANS_TIMEOUT -1 /*!< Use default SIP transaction timeout */ -#define PROVIS_KEEPALIVE_TIMEOUT 60000 /*!< How long to wait before retransmitting a provisional response (rfc 3261 13.3.1.1) */ -#define MAX_AUTHTRIES 3 /*!< Try authentication three times, then fail */ - -#define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ -#define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */ -#define SIP_MIN_PACKET 4096 /*!< Initialize size of memory to allocate for packets */ -#define MAX_HISTORY_ENTRIES 50 /*!< Max entires in the history list for a sip_pvt */ - -#define INITIAL_CSEQ 101 /*!< Our initial sip sequence number */ - -#define DEFAULT_MAX_SE 1800 /*!< Session-Timer Default Session-Expires period (RFC 4028) */ -#define DEFAULT_MIN_SE 90 /*!< Session-Timer Default Min-SE period (RFC 4028) */ - -#define SDP_MAX_RTPMAP_CODECS 32 /*!< Maximum number of codecs allowed in received SDP */ - /*! \brief Global jitterbuffer configuration - by default, jb is disabled */ static struct ast_jb_conf default_jbconf = { @@ -631,47 +555,13 @@ static struct ast_jb_conf default_jbconf = .resync_threshold = -1, .impl = "" }; -static struct ast_jb_conf global_jbconf; /*!< Global jitterbuffer configuration */ - -static const char config[] = "sip.conf"; /*!< Main configuration file */ -static const char notify_config[] = "sip_notify.conf"; /*!< Configuration file for sending Notify with CLI commands to reconfigure or reboot phones */ +static struct ast_jb_conf global_jbconf; /*!< Global jitterbuffer configuration */ -#define RTP 1 -#define NO_RTP 0 - -/*! \brief Authorization scheme for call transfers - -\note Not a bitfield flag, since there are plans for other modes, - like "only allow transfers for authenticated devices" */ -enum transfermodes { - TRANSFER_OPENFORALL, /*!< Allow all SIP transfers */ - TRANSFER_CLOSED, /*!< Allow no SIP transfers */ -}; - - -/*! \brief The result of a lot of functions */ -enum sip_result { - AST_SUCCESS = 0, /*!< FALSE means success, funny enough */ - AST_FAILURE = -1, /*!< Failure code */ -}; - -/*! \brief States for the INVITE transaction, not the dialog - \note this is for the INVITE that sets up the dialog -*/ -enum invitestates { - INV_NONE = 0, /*!< No state at all, maybe not an INVITE dialog */ - INV_CALLING = 1, /*!< Invite sent, no answer */ - INV_PROCEEDING = 2, /*!< We got/sent 1xx message */ - INV_EARLY_MEDIA = 3, /*!< We got 18x message with to-tag back */ - INV_COMPLETED = 4, /*!< Got final response with error. Wait for ACK, then CONFIRMED */ - INV_CONFIRMED = 5, /*!< Confirmed response - we've got an ack (Incoming calls only) */ - INV_TERMINATED = 6, /*!< Transaction done - either successful (AST_STATE_UP) or failed, but done - The only way out of this is a BYE from one side */ - INV_CANCELLED = 7, /*!< Transaction cancelled by client or server in non-terminated state */ -}; +static const char config[] = "sip.conf"; /*!< Main configuration file */ +static const char notify_config[] = "sip_notify.conf"; /*!< Configuration file for sending Notify with CLI commands to reconfigure or reboot phones */ /*! \brief Readable descriptions of device states. - \note Should be aligned to above table as index */ + * \note Should be aligned to above table as index */ static const struct invstate2stringtable { const enum invitestates state; const char *desc; @@ -686,52 +576,11 @@ static const struct invstate2stringtable { {INV_CANCELLED, "Cancelled"} }; -/*! \brief When sending a SIP message, we can send with a few options, depending on - type of SIP request. UNRELIABLE is moslty used for responses to repeated requests, - where the original response would be sent RELIABLE in an INVITE transaction */ -enum xmittype { - XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits. - If it fails, it's critical and will cause a teardown of the session */ - XMIT_RELIABLE = 1, /*!< Transmit SIP message reliably, with re-transmits */ - XMIT_UNRELIABLE = 0, /*!< Transmit SIP message without bothering with re-transmits */ -}; - -/*! \brief Results from the parse_register() function */ -enum parse_register_result { - PARSE_REGISTER_DENIED, - PARSE_REGISTER_FAILED, - PARSE_REGISTER_UPDATE, - PARSE_REGISTER_QUERY, -}; - -/*! \brief Type of subscription, based on the packages we do support, see \ref subscription_types */ -enum subscriptiontype { - NONE = 0, - XPIDF_XML, - DIALOG_INFO_XML, - CPIM_PIDF_XML, - PIDF_XML, - MWI_NOTIFICATION -}; - -/*! \brief The number of media types in enum \ref media_type below. */ -#define OFFERED_MEDIA_COUNT 4 - -/*! \brief Media types generate different "dummy answers" for not accepting the offer of - a media stream. We need to add definitions for each RTP profile. Secure RTP is not - the same as normal RTP and will require a new definition */ -enum media_type { - SDP_AUDIO, /*!< RTP/AVP Audio */ - SDP_VIDEO, /*!< RTP/AVP Video */ - SDP_IMAGE, /*!< Image udptl, not TCP or RTP */ - SDP_TEXT, /*!< RTP/AVP Realtime Text */ -}; - /*! \brief Subscription types that we support. We support - - dialoginfo updates (really device status, not dialog info as was the original intent of the standard) - - SIMPLE presence used for device status - - Voicemail notification subscriptions -*/ + * - dialoginfo updates (really device status, not dialog info as was the original intent of the standard) + * - SIMPLE presence used for device status + * - Voicemail notification subscriptions + */ static const struct cfsubscription_types { enum subscriptiontype type; const char * const event; @@ -747,237 +596,42 @@ static const struct cfsubscription_types { { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */ }; - -/*! \brief Authentication types - proxy or www authentication - \note Endpoints, like Asterisk, should always use WWW authentication to - allow multiple authentications in the same call - to the proxy and - to the end point. -*/ -enum sip_auth_type { - PROXY_AUTH = 407, - WWW_AUTH = 401, -}; - -/*! \brief Authentication result from check_auth* functions */ -enum check_auth_result { - AUTH_DONT_KNOW = -100, /*!< no result, need to check further */ - /* XXX maybe this is the same as AUTH_NOT_FOUND */ - - AUTH_SUCCESSFUL = 0, - AUTH_CHALLENGE_SENT = 1, - AUTH_SECRET_FAILED = -1, - AUTH_USERNAME_MISMATCH = -2, - AUTH_NOT_FOUND = -3, /*!< returned by register_verify */ - AUTH_FAKE_AUTH = -4, - AUTH_UNKNOWN_DOMAIN = -5, - AUTH_PEER_NOT_DYNAMIC = -6, - AUTH_ACL_FAILED = -7, - AUTH_BAD_TRANSPORT = -8, - AUTH_RTP_FAILED = 9, -}; - -/*! \brief States for outbound registrations (with register= lines in sip.conf */ -enum sipregistrystate { - REG_STATE_UNREGISTERED = 0, /*!< We are not registered - * \note Initial state. We should have a timeout scheduled for the initial - * (or next) registration transmission, calling sip_reregister - */ - - REG_STATE_REGSENT, /*!< Registration request sent - * \note sent initial request, waiting for an ack or a timeout to - * retransmit the initial request. - */ - - REG_STATE_AUTHSENT, /*!< We have tried to authenticate - * \note entered after transmit_register with auth info, - * waiting for an ack. - */ - - REG_STATE_REGISTERED, /*!< Registered and done */ - - REG_STATE_REJECTED, /*!< Registration rejected - * \note only used when the remote party has an expire larger than - * our max-expire. This is a final state from which we do not - * recover (not sure how correctly). - */ - - REG_STATE_TIMEOUT, /*!< Registration timed out - * \note XXX unused */ - - REG_STATE_NOAUTH, /*!< We have no accepted credentials - * \note fatal - no chance to proceed */ - - REG_STATE_FAILED, /*!< Registration failed after several tries - * \note fatal - no chance to proceed */ -}; - -/*! \brief Modes in which Asterisk can be configured to run SIP Session-Timers */ -enum st_mode { - SESSION_TIMER_MODE_INVALID = 0, /*!< Invalid value */ - SESSION_TIMER_MODE_ACCEPT, /*!< Honor inbound Session-Timer requests */ - SESSION_TIMER_MODE_ORIGINATE, /*!< Originate outbound and honor inbound requests */ - SESSION_TIMER_MODE_REFUSE /*!< Ignore inbound Session-Timers requests */ -}; - -/*! \brief The entity playing the refresher role for Session-Timers */ -enum st_refresher { - SESSION_TIMER_REFRESHER_AUTO, /*!< Negotiated */ - SESSION_TIMER_REFRESHER_UAC, /*!< Session is refreshed by the UAC */ - SESSION_TIMER_REFRESHER_UAS /*!< Session is refreshed by the UAS */ -}; - -/*! \brief Define some implemented SIP transports - \note Asterisk does not support SCTP or UDP/DTLS -*/ -enum sip_transport { - SIP_TRANSPORT_UDP = 1, /*!< Unreliable transport for SIP, needs retransmissions */ - SIP_TRANSPORT_TCP = 1 << 1, /*!< Reliable, but unsecure */ - SIP_TRANSPORT_TLS = 1 << 2, /*!< TCP/TLS - reliable and secure transport for signalling */ -}; - -/*! \brief definition of a sip proxy server +/*! \brief The core structure to setup dialogs. We parse incoming messages by using + * structure and then route the messages according to the type. * - * For outbound proxies, a sip_peer will contain a reference to a - * dynamically allocated instance of a sip_proxy. A sip_pvt may also - * contain a reference to a peer's outboundproxy, or it may contain - * a reference to the sip_cfg.outboundproxy. + * \note Note that sip_methods[i].id == i must hold or the code breaks */ -struct sip_proxy { - char name[MAXHOSTNAMELEN]; /*!< DNS name of domain/host or IP */ - struct sockaddr_in ip; /*!< Currently used IP address and port */ - time_t last_dnsupdate; /*!< When this was resolved */ - enum sip_transport transport; - int force; /*!< If it's an outbound proxy, Force use of this outbound proxy for all outbound requests */ - /* Room for a SRV record chain based on the name */ -}; - -/*! \brief argument for the 'show channels|subscriptions' callback. */ -struct __show_chan_arg { - int fd; - int subscriptions; - int numchans; /* return value */ -}; - - -/*! \brief States whether a SIP message can create a dialog in Asterisk. */ -enum can_create_dialog { - CAN_NOT_CREATE_DIALOG, - CAN_CREATE_DIALOG, - CAN_CREATE_DIALOG_UNSUPPORTED_METHOD, -}; - -/*! \brief SIP Request methods known by Asterisk - - \note Do _NOT_ make any changes to this enum, or the array following it; - if you think you are doing the right thing, you are probably - not doing the right thing. If you think there are changes - needed, get someone else to review them first _before_ - submitting a patch. If these two lists do not match properly - bad things will happen. -*/ - -enum sipmethod { - SIP_UNKNOWN, /*!< Unknown response */ - SIP_RESPONSE, /*!< Not request, response to outbound request */ - SIP_REGISTER, /*!< Registration to the mothership, tell us where you are located */ - SIP_OPTIONS, /*!< Check capabilities of a device, used for "ping" too */ - SIP_NOTIFY, /*!< Status update, Part of the event package standard, result of a SUBSCRIBE or a REFER */ - SIP_INVITE, /*!< Set up a session */ - SIP_ACK, /*!< End of a three-way handshake started with INVITE. */ - SIP_PRACK, /*!< Reliable pre-call signalling. Not supported in Asterisk. */ - SIP_BYE, /*!< End of a session */ - SIP_REFER, /*!< Refer to another URI (transfer) */ - SIP_SUBSCRIBE, /*!< Subscribe for updates (voicemail, session status, device status, presence) */ - SIP_MESSAGE, /*!< Text messaging */ - SIP_UPDATE, /*!< Update a dialog. We can send UPDATE; but not accept it */ - SIP_INFO, /*!< Information updates during a session */ - SIP_CANCEL, /*!< Cancel an INVITE */ - SIP_PUBLISH, /*!< Not supported in Asterisk */ - SIP_PING, /*!< Not supported at all, no standard but still implemented out there */ -}; - -/*! \brief Settings for the 'notifycid' option, see sip.conf.sample for details. */ -enum notifycid_setting { - DISABLED = 0, - ENABLED = 1, - IGNORE_CONTEXT = 2, -}; - -/*! \brief The core structure to setup dialogs. We parse incoming messages by using - structure and then route the messages according to the type. - - \note Note that sip_methods[i].id == i must hold or the code breaks */ static const struct cfsip_methods { enum sipmethod id; int need_rtp; /*!< when this is the 'primary' use for a pvt structure, does it need RTP? */ char * const text; enum can_create_dialog can_create; } sip_methods[] = { - { SIP_UNKNOWN, RTP, "-UNKNOWN-", CAN_CREATE_DIALOG }, - { SIP_RESPONSE, NO_RTP, "SIP/2.0", CAN_NOT_CREATE_DIALOG }, - { SIP_REGISTER, NO_RTP, "REGISTER", CAN_CREATE_DIALOG }, - { SIP_OPTIONS, NO_RTP, "OPTIONS", CAN_CREATE_DIALOG }, - { SIP_NOTIFY, NO_RTP, "NOTIFY", CAN_CREATE_DIALOG }, - { SIP_INVITE, RTP, "INVITE", CAN_CREATE_DIALOG }, - { SIP_ACK, NO_RTP, "ACK", CAN_NOT_CREATE_DIALOG }, - { SIP_PRACK, NO_RTP, "PRACK", CAN_NOT_CREATE_DIALOG }, - { SIP_BYE, NO_RTP, "BYE", CAN_NOT_CREATE_DIALOG }, - { SIP_REFER, NO_RTP, "REFER", CAN_CREATE_DIALOG }, - { SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE", CAN_CREATE_DIALOG }, - { SIP_MESSAGE, NO_RTP, "MESSAGE", CAN_CREATE_DIALOG }, - { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG }, - { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG }, - { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG }, - { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }, - { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD } + { SIP_UNKNOWN, RTP, "-UNKNOWN-",CAN_CREATE_DIALOG }, + { SIP_RESPONSE, NO_RTP, "SIP/2.0", CAN_NOT_CREATE_DIALOG }, + { SIP_REGISTER, NO_RTP, "REGISTER", CAN_CREATE_DIALOG }, + { SIP_OPTIONS, NO_RTP, "OPTIONS", CAN_CREATE_DIALOG }, + { SIP_NOTIFY, NO_RTP, "NOTIFY", CAN_CREATE_DIALOG }, + { SIP_INVITE, RTP, "INVITE", CAN_CREATE_DIALOG }, + { SIP_ACK, NO_RTP, "ACK", CAN_NOT_CREATE_DIALOG }, + { SIP_PRACK, NO_RTP, "PRACK", CAN_NOT_CREATE_DIALOG }, + { SIP_BYE, NO_RTP, "BYE", CAN_NOT_CREATE_DIALOG }, + { SIP_REFER, NO_RTP, "REFER", CAN_CREATE_DIALOG }, + { SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE",CAN_CREATE_DIALOG }, + { SIP_MESSAGE, NO_RTP, "MESSAGE", CAN_CREATE_DIALOG }, + { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG }, + { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG }, + { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG }, + { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }, + { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD } }; -static unsigned int chan_idx; - -/*! Define SIP option tags, used in Require: and Supported: headers - We need to be aware of these properties in the phones to use - the replace: header. We should not do that without knowing - that the other end supports it... - This is nothing we can configure, we learn by the dialog - Supported: header on the REGISTER (peer) or the INVITE - (other devices) - We are not using many of these today, but will in the future. - This is documented in RFC 3261 -*/ -#define SUPPORTED 1 -#define NOT_SUPPORTED 0 - -/* SIP options */ -#define SIP_OPT_REPLACES (1 << 0) -#define SIP_OPT_100REL (1 << 1) -#define SIP_OPT_TIMER (1 << 2) -#define SIP_OPT_EARLY_SESSION (1 << 3) -#define SIP_OPT_JOIN (1 << 4) -#define SIP_OPT_PATH (1 << 5) -#define SIP_OPT_PREF (1 << 6) -#define SIP_OPT_PRECONDITION (1 << 7) -#define SIP_OPT_PRIVACY (1 << 8) -#define SIP_OPT_SDP_ANAT (1 << 9) -#define SIP_OPT_SEC_AGREE (1 << 10) -#define SIP_OPT_EVENTLIST (1 << 11) -#define SIP_OPT_GRUU (1 << 12) -#define SIP_OPT_TARGET_DIALOG (1 << 13) -#define SIP_OPT_NOREFERSUB (1 << 14) -#define SIP_OPT_HISTINFO (1 << 15) -#define SIP_OPT_RESPRIORITY (1 << 16) -#define SIP_OPT_FROMCHANGE (1 << 17) -#define SIP_OPT_RECLISTINV (1 << 18) -#define SIP_OPT_RECLISTSUB (1 << 19) -#define SIP_OPT_OUTBOUND (1 << 20) -#define SIP_OPT_UNKNOWN (1 << 21) - - /*! \brief List of well-known SIP options. If we get this in a require, we should check the list and answer accordingly. */ static const struct cfsip_options { - int id; /*!< Bitmap ID */ - int supported; /*!< Supported by Asterisk ? */ - char * const text; /*!< Text id, as in standard */ + int id; /*!< Bitmap ID */ + int supported; /*!< Supported by Asterisk ? */ + char * const text; /*!< Text id, as in standard */ } sip_options[] = { /* XXX used in 3 places */ /* RFC3262: PRACK 100% reliability */ { SIP_OPT_100REL, NOT_SUPPORTED, "100rel" }, @@ -1050,176 +704,30 @@ static const struct sip_reasons { { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"} }; -static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text) -{ - enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN; - int i; - - for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) { - if (!strcasecmp(text, sip_reason_table[i].text)) { - ast = sip_reason_table[i].code; - break; - } - } - - return ast; -} - -static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code) -{ - if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) { - return sip_reason_table[code].text; - } - - return "unknown"; -} - -/*! \brief SIP Methods we support - \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have - allowsubscribe and allowrefer on in sip.conf. -*/ -#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" - -/*! \brief SIP Extensions we support - \note This should be generated based on the previous array - in combination with settings. - \todo We should not have "timer" if it's disabled in the configuration file. -*/ -#define SUPPORTED_EXTENSIONS "replaces, timer" - -/*! \brief Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS */ -#define STANDARD_SIP_PORT 5060 -/*! \brief Standard SIP TLS port from RFC 3261. DO NOT CHANGE THIS */ -#define STANDARD_TLS_PORT 5061 - -/*! \note in many SIP headers, absence of a port number implies port 5060, - * and this is why we cannot change the above constant. - * There is a limited number of places in asterisk where we could, - * in principle, use a different "default" port number, but - * we do not support this feature at the moment. - * You can run Asterisk with SIP on a different port with a configuration - * option. If you change this value in the source code, the signalling will be incorrect. - * - */ - -/*! \name DefaultValues Default values, set and reset in reload_config before reading configuration - - These are default values in the source. There are other recommended values in the - sip.conf.sample for new installations. These may differ to keep backwards compatibility, - yet encouraging new behaviour on new installations - */ -/*@{*/ -#define DEFAULT_CONTEXT "default" /*!< The default context for [general] section as well as devices */ -#define DEFAULT_MOHINTERPRET "default" /*!< The default music class */ -#define DEFAULT_MOHSUGGEST "" -#define DEFAULT_VMEXTEN "asterisk" /*!< Default voicemail extension */ -#define DEFAULT_CALLERID "asterisk" /*!< Default caller ID */ -#define DEFAULT_MWI_FROM "" -#define DEFAULT_NOTIFYMIME "application/simple-message-summary" -#define DEFAULT_ALLOWGUEST TRUE -#define DEFAULT_RTPKEEPALIVE 0 /*!< Default RTPkeepalive setting */ -#define DEFAULT_CALLCOUNTER FALSE /*!< Do not enable call counters by default */ -#define DEFAULT_SRVLOOKUP TRUE /*!< Recommended setting is ON */ -#define DEFAULT_COMPACTHEADERS FALSE /*!< Send compact (one-character) SIP headers. Default off */ -#define DEFAULT_TOS_SIP 0 /*!< Call signalling packets should be marked as DSCP CS3, but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_TOS_AUDIO 0 /*!< Audio packets should be marked as DSCP EF (Expedited Forwarding), but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_TOS_VIDEO 0 /*!< Video packets should be marked as DSCP AF41, but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_TOS_TEXT 0 /*!< Text packets should be marked as XXXX XXXX, but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_COS_SIP 4 /*!< Level 2 class of service for SIP signalling */ -#define DEFAULT_COS_AUDIO 5 /*!< Level 2 class of service for audio media */ -#define DEFAULT_COS_VIDEO 6 /*!< Level 2 class of service for video media */ -#define DEFAULT_COS_TEXT 5 /*!< Level 2 class of service for text media (T.140) */ -#define DEFAULT_ALLOW_EXT_DOM TRUE /*!< Allow external domains */ -#define DEFAULT_REALM "asterisk" /*!< Realm for HTTP digest authentication */ -#define DEFAULT_DOMAINSASREALM FALSE /*!< Use the domain option to guess the realm for registration and invite requests */ -#define DEFAULT_NOTIFYRINGING TRUE /*!< Notify devicestate system on ringing state */ -#define DEFAULT_NOTIFYCID DISABLED /*!< Include CID with ringing notifications */ -#define DEFAULT_PEDANTIC FALSE /*!< Avoid following SIP standards for dialog matching */ -#define DEFAULT_AUTOCREATEPEER FALSE /*!< Don't create peers automagically */ -#define DEFAULT_MATCHEXTERNIPLOCALLY FALSE /*!< Match extern IP locally default setting */ -#define DEFAULT_QUALIFY FALSE /*!< Don't monitor devices */ -#define DEFAULT_CALLEVENTS FALSE /*!< Extra manager SIP call events */ -#define DEFAULT_ALWAYSAUTHREJECT FALSE /*!< Don't reject authentication requests always */ -#define DEFAULT_REGEXTENONQUALIFY FALSE -#define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */ -#define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */ -#ifndef DEFAULT_USERAGENT -#define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ -#define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */ -#define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */ -#define DEFAULT_ENGINE "asterisk" /*!< Default RTP engine to use for sessions */ -#define DEFAULT_CAPABILITY (AST_FORMAT_ULAW | AST_FORMAT_TESTLAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263); -#endif -/*@}*/ /*! \name DefaultSettings Default setttings are used as a channel setting and as a default when configuring devices */ /*@{*/ -static char default_language[MAX_LANGUAGE]; /*!< Default language setting for new channels */ -static char default_callerid[AST_MAX_EXTENSION]; /*!< Default caller ID for sip messages */ -static char default_mwi_from[80]; /*!< Default caller ID for MWI updates */ -static char default_fromdomain[AST_MAX_EXTENSION]; /*!< Default domain on outound messages */ -static char default_notifymime[AST_MAX_EXTENSION]; /*!< Default MIME media type for MWI notify messages */ -static char default_vmexten[AST_MAX_EXTENSION]; /*!< Default From Username on MWI updates */ -static int default_qualify; /*!< Default Qualify= setting */ +static char default_language[MAX_LANGUAGE]; /*!< Default language setting for new channels */ +static char default_callerid[AST_MAX_EXTENSION]; /*!< Default caller ID for sip messages */ +static char default_mwi_from[80]; /*!< Default caller ID for MWI updates */ +static char default_fromdomain[AST_MAX_EXTENSION]; /*!< Default domain on outound messages */ +static char default_notifymime[AST_MAX_EXTENSION]; /*!< Default MIME media type for MWI notify messages */ +static char default_vmexten[AST_MAX_EXTENSION]; /*!< Default From Username on MWI updates */ +static int default_qualify; /*!< Default Qualify= setting */ static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */ -static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting +static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting * a bridged channel on hold */ -static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ -static char default_engine[256]; /*!< Default RTP engine */ -static int default_maxcallbitrate; /*!< Maximum bitrate for call */ -static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ -static unsigned int default_transports; /*!< Default Transports (enum sip_transport) that are acceptable */ -static unsigned int default_primary_transport; /*!< Default primary Transport (enum sip_transport) for outbound connections to devices */ - +static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ +static char default_engine[256]; /*!< Default RTP engine */ +static int default_maxcallbitrate; /*!< Maximum bitrate for call */ +static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ +static unsigned int default_transports; /*!< Default Transports (enum sip_transport) that are acceptable */ +static unsigned int default_primary_transport; /*!< Default primary Transport (enum sip_transport) for outbound connections to devices */ /*@}*/ -/*! \name GlobalSettings - Global settings apply to the channel (often settings you can change in the general section - of sip.conf -*/ -/*@{*/ -/*! \brief a place to store all global settings for the sip channel driver - - These are settings that will be possibly to apply on a group level later on. - \note Do not add settings that only apply to the channel itself and can't - be applied to devices (trunks, services, phones) -*/ -struct sip_settings { - int peer_rtupdate; /*!< G: Update database with registration data for peer? */ - int rtsave_sysname; /*!< G: Save system name at registration? */ - int ignore_regexpire; /*!< G: Ignore expiration of peer */ - int rtautoclear; /*!< Realtime ?? */ - int directrtpsetup; /*!< Enable support for Direct RTP setup (no re-invites) */ - int pedanticsipchecking; /*!< Extra checking ? Default off */ - int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */ - int srvlookup; /*!< SRV Lookup on or off. Default is on */ - int allowguest; /*!< allow unauthenticated peers to connect? */ - int alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ - int compactheaders; /*!< send compact sip headers */ - int allow_external_domains; /*!< Accept calls to external SIP domains? */ - int callevents; /*!< Whether we send manager events or not */ - int regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */ - int matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */ - char regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */ - unsigned int disallowed_methods; /*!< methods that we should never try to use */ - int notifyringing; /*!< Send notifications on ringing */ - int notifyhold; /*!< Send notifications on hold */ - enum notifycid_setting notifycid; /*!< Send CID with ringing notifications */ - enum transfermodes allowtransfer; /*!< SIP Refer restriction scheme */ - int allowsubscribe; /*!< Flag for disabling ALL subscriptions, this is FALSE only if all peers are FALSE - the global setting is in globals_flags[1] */ - char realm[MAXHOSTNAMELEN]; /*!< Default realm */ - int domainsasrealm; /*!< Use domains lists as realms */ - struct sip_proxy outboundproxy; /*!< Outbound proxy */ - char default_context[AST_MAX_CONTEXT]; - char default_subscribecontext[AST_MAX_CONTEXT]; - struct ast_ha *contact_ha; /*! \brief Global list of addresses dynamic peers are not allowed to use */ - format_t capability; /*!< Supported codecs */ -}; - static struct sip_settings sip_cfg; /*!< SIP configuration data. \note in the future we could have multiple of these (per domain, per device group etc) */ @@ -1229,65 +737,63 @@ static struct sip_settings sip_cfg; /*!< SIP configuration data. ast_uri_decode(str); \ } \ -static int global_match_auth_username; /*!< Match auth username if available instead of From: Default off. */ - -static int global_relaxdtmf; /*!< Relax DTMF */ -static int global_prematuremediafilter; /*!< Enable/disable premature frames in a call (causing 183 early media) */ -static int global_rtptimeout; /*!< Time out call if no RTP */ -static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */ -static int global_rtpkeepalive; /*!< Send RTP keepalives */ -static int global_reg_timeout; /*!< Global time between attempts for outbound registrations */ -static int global_regattempts_max; /*!< Registration attempts before giving up */ -static int global_shrinkcallerid; /*!< enable or disable shrinking of caller id */ -static int global_callcounter; /*!< Enable call counters for all devices. This is currently enabled by setting the peer - call-limit to INT_MAX. When we remove the call-limit from the code, we can make it - with just a boolean flag in the device structure */ -static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */ -static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */ -static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */ -static unsigned int global_tos_text; /*!< IP type of service for text RTP packets */ -static unsigned int global_cos_sip; /*!< 802.1p class of service for SIP packets */ -static unsigned int global_cos_audio; /*!< 802.1p class of service for audio RTP packets */ -static unsigned int global_cos_video; /*!< 802.1p class of service for video RTP packets */ -static unsigned int global_cos_text; /*!< 802.1p class of service for text RTP packets */ -static unsigned int recordhistory; /*!< Record SIP history. Off by default */ -static unsigned int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ -static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */ -static char global_sdpsession[AST_MAX_EXTENSION]; /*!< SDP session name for the SIP channel */ -static char global_sdpowner[AST_MAX_EXTENSION]; /*!< SDP owner name for the SIP channel */ -static int global_authfailureevents; /*!< Whether we send authentication failure manager events or not. Default no. */ -static int global_t1; /*!< T1 time */ -static int global_t1min; /*!< T1 roundtrip time minimum */ -static int global_timer_b; /*!< Timer B - RFC 3261 Section 17.1.1.2 */ -static unsigned int global_autoframing; /*!< Turn autoframing on or off. */ -static int global_qualifyfreq; /*!< Qualify frequency */ -static int global_qualify_gap; /*!< Time between our group of peer pokes */ -static int global_qualify_peers; /*!< Number of peers to poke at a given time */ - +static unsigned int chan_idx; /*!< used in naming sip channel */ +static int global_match_auth_username; /*!< Match auth username if available instead of From: Default off. */ + +static int global_relaxdtmf; /*!< Relax DTMF */ +static int global_prematuremediafilter; /*!< Enable/disable premature frames in a call (causing 183 early media) */ +static int global_rtptimeout; /*!< Time out call if no RTP */ +static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */ +static int global_rtpkeepalive; /*!< Send RTP keepalives */ +static int global_reg_timeout; /*!< Global time between attempts for outbound registrations */ +static int global_regattempts_max; /*!< Registration attempts before giving up */ +static int global_shrinkcallerid; /*!< enable or disable shrinking of caller id */ +static int global_callcounter; /*!< Enable call counters for all devices. This is currently enabled by setting the peer + * call-limit to INT_MAX. When we remove the call-limit from the code, we can make it + * with just a boolean flag in the device structure */ +static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */ +static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */ +static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */ +static unsigned int global_tos_text; /*!< IP type of service for text RTP packets */ +static unsigned int global_cos_sip; /*!< 802.1p class of service for SIP packets */ +static unsigned int global_cos_audio; /*!< 802.1p class of service for audio RTP packets */ +static unsigned int global_cos_video; /*!< 802.1p class of service for video RTP packets */ +static unsigned int global_cos_text; /*!< 802.1p class of service for text RTP packets */ +static unsigned int recordhistory; /*!< Record SIP history. Off by default */ +static unsigned int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ +static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */ +static char global_sdpsession[AST_MAX_EXTENSION]; /*!< SDP session name for the SIP channel */ +static char global_sdpowner[AST_MAX_EXTENSION]; /*!< SDP owner name for the SIP channel */ +static int global_authfailureevents; /*!< Whether we send authentication failure manager events or not. Default no. */ +static int global_t1; /*!< T1 time */ +static int global_t1min; /*!< T1 roundtrip time minimum */ +static int global_timer_b; /*!< Timer B - RFC 3261 Section 17.1.1.2 */ +static unsigned int global_autoframing; /*!< Turn autoframing on or off. */ +static int global_qualifyfreq; /*!< Qualify frequency */ +static int global_qualify_gap; /*!< Time between our group of peer pokes */ +static int global_qualify_peers; /*!< Number of peers to poke at a given time */ static enum st_mode global_st_mode; /*!< Mode of operation for Session-Timers */ static enum st_refresher global_st_refresher; /*!< Session-Timer refresher */ static int global_min_se; /*!< Lowest threshold for session refresh interval */ static int global_max_se; /*!< Highest threshold for session refresh interval */ -static int global_dynamic_exclude_static = 0; /*!< Exclude static peers from contact registrations */ +static int global_dynamic_exclude_static = 0; /*!< Exclude static peers from contact registrations */ /*@}*/ - /*! \name Object counters @{ - * \bug These counters are not handled in a thread-safe way ast_atomic_fetchadd_int() - * should be used to modify these values. */ -static int speerobjs = 0; /*!< Static peers */ -static int rpeerobjs = 0; /*!< Realtime peers */ -static int apeerobjs = 0; /*!< Autocreated peer objects */ -static int regobjs = 0; /*!< Registry objects */ + * \bug These counters are not handled in a thread-safe way ast_atomic_fetchadd_int() + * should be used to modify these values. */ +static int speerobjs = 0; /*!< Static peers */ +static int rpeerobjs = 0; /*!< Realtime peers */ +static int apeerobjs = 0; /*!< Autocreated peer objects */ +static int regobjs = 0; /*!< Registry objects */ /* }@ */ -static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */ -static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */ - -static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */ +static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */ +static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */ +static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */ AST_MUTEX_DEFINE_STATIC(netlock); @@ -1307,583 +813,42 @@ static enum channelreloadreason sip_reloadreason; /*!< Reason for last rel static struct sched_context *sched; /*!< The scheduling context */ static struct io_context *io; /*!< The IO context */ static int *sipsock_read_id; /*!< ID of IO entry for sipsock FD */ - -#define DEC_CALL_LIMIT 0 -#define INC_CALL_LIMIT 1 -#define DEC_CALL_RINGING 2 -#define INC_CALL_RINGING 3 - -/*! \brief The SIP socket definition */ -struct sip_socket { - enum sip_transport type; /*!< UDP, TCP or TLS */ - int fd; /*!< Filed descriptor, the actual socket */ - uint16_t port; - struct ast_tcptls_session_instance *tcptls_session; /* If tcp or tls, a socket manager */ -}; - -/*! \brief sip_request: The data grabbed from the UDP socket - * - * \verbatim - * Incoming messages: we first store the data from the socket in data[], - * adding a trailing \0 to make string parsing routines happy. - * Then call parse_request() and req.method = find_sip_method(); - * to initialize the other fields. The \r\n at the end of each line is - * replaced by \0, so that data[] is not a conforming SIP message anymore. - * After this processing, rlPart1 is set to non-NULL to remember - * that we can run get_header() on this kind of packet. - * - * parse_request() splits the first line as follows: - * Requests have in the first line method uri SIP/2.0 - * rlPart1 = method; rlPart2 = uri; - * Responses have in the first line SIP/2.0 NNN description - * rlPart1 = SIP/2.0; rlPart2 = NNN + description; - * - * For outgoing packets, we initialize the fields with init_req() or init_resp() - * (which fills the first line to "METHOD uri SIP/2.0" or "SIP/2.0 code text"), - * and then fill the rest with add_header() and add_line(). - * The \r\n at the end of the line are still there, so the get_header() - * and similar functions don't work on these packets. - * \endverbatim - */ -struct sip_request { - ptrdiff_t rlPart1; /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */ - ptrdiff_t rlPart2; /*!< Offset of the Request URI or Response Status */ - int len; /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */ - int headers; /*!< # of SIP Headers */ - int method; /*!< Method of this request */ - int lines; /*!< Body Content */ - unsigned int sdp_start; /*!< the line number where the SDP begins */ - unsigned int sdp_count; /*!< the number of lines of SDP */ - char debug; /*!< print extra debugging if non zero */ - char has_to_tag; /*!< non-zero if packet has To: tag */ - char ignore; /*!< if non-zero This is a re-transmit, ignore it */ - ptrdiff_t header[SIP_MAX_HEADERS]; /*!< Array of offsets into the request string of each SIP header*/ - ptrdiff_t line[SIP_MAX_LINES]; /*!< Array of offsets into the request string of each SDP line*/ - struct ast_str *data; - /* XXX Do we need to unref socket.ser when the request goes away? */ - struct sip_socket socket; /*!< The socket used for this request */ - AST_LIST_ENTRY(sip_request) next; -}; - -/* \brief given a sip_request and an offset, return the char * that resides there - * - * It used to be that rlPart1, rlPart2, and the header and line arrays were character - * pointers. They are now offsets into the ast_str portion of the sip_request structure. - * To avoid adding a bunch of redundant pointer arithmetic to the code, this macro is - * provided to retrieve the string at a particular offset within the request's buffer - */ -#define REQ_OFFSET_TO_STR(req,offset) (ast_str_buffer((req)->data) + ((req)->offset)) - -/*! \brief structure used in transfers */ -struct sip_dual { - struct ast_channel *chan1; /*!< First channel involved */ - struct ast_channel *chan2; /*!< Second channel involved */ - struct sip_request req; /*!< Request that caused the transfer (REFER) */ - int seqno; /*!< Sequence number */ -}; - struct sip_pkt; - -/*! \brief Parameters to the transmit_invite function */ -struct sip_invite_param { - int addsipheaders; /*!< Add extra SIP headers */ - const char *uri_options; /*!< URI options to add to the URI */ - const char *vxml_url; /*!< VXML url for Cisco phones */ - char *auth; /*!< Authentication */ - char *authheader; /*!< Auth header */ - enum sip_auth_type auth_type; /*!< Authentication type */ - const char *replaces; /*!< Replaces header for call transfers */ - int transfer; /*!< Flag - is this Invite part of a SIP transfer? (invite/replaces) */ -}; - -/*! \brief Structure to save routing information for a SIP session */ -struct sip_route { - struct sip_route *next; - char hop[0]; -}; - -/*! \brief Modes for SIP domain handling in the PBX */ -enum domain_mode { - SIP_DOMAIN_AUTO, /*!< This domain is auto-configured */ - SIP_DOMAIN_CONFIG, /*!< This domain is from configuration */ -}; - -/*! \brief Domain data structure. - \note In the future, we will connect this to a configuration tree specific - for this domain -*/ -struct domain { - char domain[MAXHOSTNAMELEN]; /*!< SIP domain we are responsible for */ - char context[AST_MAX_EXTENSION]; /*!< Incoming context for this domain */ - enum domain_mode mode; /*!< How did we find this domain? */ - AST_LIST_ENTRY(domain) list; /*!< List mechanics */ -}; - -static AST_LIST_HEAD_STATIC(domain_list, domain); /*!< The SIP domain list */ - - -/*! \brief sip_history: Structure for saving transactions within a SIP dialog */ -struct sip_history { - AST_LIST_ENTRY(sip_history) list; - char event[0]; /* actually more, depending on needs */ -}; +static AST_LIST_HEAD_STATIC(domain_list, domain); /*!< The SIP domain list */ AST_LIST_HEAD_NOLOCK(sip_history_head, sip_history); /*!< history list, entry in sip_pvt */ -/*! \brief sip_auth: Credentials for authentication to other SIP services */ -struct sip_auth { - char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */ - char username[256]; /*!< Username */ - char secret[256]; /*!< Secret */ - char md5secret[256]; /*!< MD5Secret */ - struct sip_auth *next; /*!< Next auth structure in list */ -}; - -/*! \name SIPflags - Various flags for the flags field in the pvt structure - Trying to sort these up (one or more of the following): - D: Dialog - P: Peer/user - G: Global flag - When flags are used by multiple structures, it is important that - they have a common layout so it is easy to copy them. -*/ -/*@{*/ -#define SIP_OUTGOING (1 << 0) /*!< D: Direction of the last transaction in this dialog */ -#define SIP_RINGING (1 << 2) /*!< D: Have sent 180 ringing */ -#define SIP_PROGRESS_SENT (1 << 3) /*!< D: Have sent 183 message progress */ -#define SIP_NEEDREINVITE (1 << 4) /*!< D: Do we need to send another reinvite? */ -#define SIP_PENDINGBYE (1 << 5) /*!< D: Need to send bye after we ack? */ -#define SIP_GOTREFER (1 << 6) /*!< D: Got a refer? */ -#define SIP_CALL_LIMIT (1 << 7) /*!< D: Call limit enforced for this call */ -#define SIP_INC_COUNT (1 << 8) /*!< D: Did this dialog increment the counter of in-use calls? */ -#define SIP_INC_RINGING (1 << 9) /*!< D: Did this connection increment the counter of in-use calls? */ -#define SIP_DEFER_BYE_ON_TRANSFER (1 << 10) /*!< D: Do not hangup at first ast_hangup */ - -#define SIP_PROMISCREDIR (1 << 11) /*!< DP: Promiscuous redirection */ -#define SIP_TRUSTRPID (1 << 12) /*!< DP: Trust RPID headers? */ -#define SIP_USEREQPHONE (1 << 13) /*!< DP: Add user=phone to numeric URI. Default off */ -#define SIP_USECLIENTCODE (1 << 14) /*!< DP: Trust X-ClientCode info message */ - -/* DTMF flags - see str2dtmfmode() and dtmfmode2str() */ -#define SIP_DTMF (7 << 15) /*!< DP: DTMF Support: five settings, uses three bits */ -#define SIP_DTMF_RFC2833 (0 << 15) /*!< DP: DTMF Support: RTP DTMF - "rfc2833" */ -#define SIP_DTMF_INBAND (1 << 15) /*!< DP: DTMF Support: Inband audio, only for ULAW/ALAW - "inband" */ -#define SIP_DTMF_INFO (2 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" */ -#define SIP_DTMF_AUTO (3 << 15) /*!< DP: DTMF Support: AUTO switch between rfc2833 and in-band DTMF */ -#define SIP_DTMF_SHORTINFO (4 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" - short variant */ - -/* NAT settings */ -#define SIP_NAT_FORCE_RPORT (1 << 18) /*!< DP: Force rport even if not present in the request */ -#define SIP_NAT_RPORT_PRESENT (1 << 19) /*!< DP: rport was present in the request */ - -/* re-INVITE related settings */ -#define SIP_REINVITE (7 << 20) /*!< DP: four settings, uses three bits */ -#define SIP_REINVITE_NONE (0 << 20) /*!< DP: no reinvite allowed */ -#define SIP_DIRECT_MEDIA (1 << 20) /*!< DP: allow peers to be reinvited to send media directly p2p */ -#define SIP_DIRECT_MEDIA_NAT (2 << 20) /*!< DP: allow media reinvite when new peer is behind NAT */ -#define SIP_REINVITE_UPDATE (4 << 20) /*!< DP: use UPDATE (RFC3311) when reinviting this peer */ - -/* "insecure" settings - see insecure2str() */ -#define SIP_INSECURE (3 << 23) /*!< DP: three settings, uses two bits */ -#define SIP_INSECURE_NONE (0 << 23) /*!< DP: secure mode */ -#define SIP_INSECURE_PORT (1 << 23) /*!< DP: don't require matching port for incoming requests */ -#define SIP_INSECURE_INVITE (1 << 24) /*!< DP: don't require authentication for incoming INVITEs */ - -/* Sending PROGRESS in-band settings */ -#define SIP_PROG_INBAND (3 << 25) /*!< DP: three settings, uses two bits */ -#define SIP_PROG_INBAND_NEVER (0 << 25) -#define SIP_PROG_INBAND_NO (1 << 25) -#define SIP_PROG_INBAND_YES (2 << 25) - -#define SIP_SENDRPID (3 << 29) /*!< DP: Remote Party-ID Support */ -#define SIP_SENDRPID_NO (0 << 29) -#define SIP_SENDRPID_PAI (1 << 29) /*!< Use "P-Asserted-Identity" for rpid */ -#define SIP_SENDRPID_RPID (2 << 29) /*!< Use "Remote-Party-ID" for rpid */ -#define SIP_G726_NONSTANDARD (1 << 31) /*!< DP: Use non-standard packing for G726-32 data */ - -/*! \brief Flags to copy from peer/user to dialog */ -#define SIP_FLAGS_TO_COPY \ - (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_SENDRPID | SIP_DTMF | SIP_REINVITE | \ - SIP_PROG_INBAND | SIP_USECLIENTCODE | SIP_NAT_FORCE_RPORT | SIP_G726_NONSTANDARD | \ - SIP_USEREQPHONE | SIP_INSECURE) -/*@}*/ - -/*! \name SIPflags2 - a second page of flags (for flags[1] */ -/*@{*/ -/* realtime flags */ -#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */ -#define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */ -#define SIP_PAGE2_RPID_UPDATE (1 << 3) -#define SIP_PAGE2_Q850_REASON (1 << 4) /*!< DP: Get/send cause code via Reason header */ - -/* Space for addition of other realtime flags in the future */ -#define SIP_PAGE2_CONSTANT_SSRC (1 << 7) /*!< GDP: Don't change SSRC on reinvite */ -#define SIP_PAGE2_SYMMETRICRTP (1 << 8) /*!< GDP: Whether symmetric RTP is enabled or not */ -#define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ - -#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 10) -#define SIP_PAGE2_RPID_IMMEDIATE (1 << 11) -#define SIP_PAGE2_RPORT_PRESENT (1 << 12) /*!< Was rport received in the Via header? */ -#define SIP_PAGE2_PREFERRED_CODEC (1 << 13) /*!< GDP: Only respond with single most preferred joint codec */ -#define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */ -#define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */ -#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */ -#define SIP_PAGE2_ALLOWOVERLAP (1 << 17) /*!< DP: Allow overlap dialing ? */ -#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */ -#define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */ - -#define SIP_PAGE2_T38SUPPORT (3 << 20) /*!< GDP: T.38 Fax Support */ -#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T.38 Fax Support (no error correction) */ -#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T.38 Fax Support (FEC error correction) */ -#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (3 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */ - -#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */ -#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */ -#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 23) /*!< D: One directional hold */ -#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 23) /*!< D: Inactive hold */ - -#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< DP: Compensate for buggy RFC2833 implementations */ -#define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< DP: Buggy CISCO MWI fix */ -#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 27) /*!< 29: Has a dialog been established? */ -#define SIP_PAGE2_FAX_DETECT (1 << 28) /*!< DP: Fax Detection support */ -#define SIP_PAGE2_REGISTERTRYING (1 << 29) /*!< DP: Send 100 Trying on REGISTER attempts */ -#define SIP_PAGE2_UDPTL_DESTINATION (1 << 30) /*!< DP: Use source IP of RTP as destination if NAT is enabled */ -#define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 31) /*!< DP: Always set up video, even if endpoints don't support it */ - -#define SIP_PAGE2_FLAGS_TO_COPY \ - (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ - SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | \ - SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \ - SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \ - SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE | SIP_PAGE2_SYMMETRICRTP | SIP_PAGE2_CONSTANT_SSRC |\ - SIP_PAGE2_Q850_REASON) - -/*@}*/ - -/*! \brief debugging state - * We store separately the debugging requests from the config file - * and requests from the CLI. Debugging is enabled if either is set - * (which means that if sipdebug is set in the config file, we can - * only turn it off by reloading the config). - */ -enum sip_debug_e { - sip_debug_none = 0, - sip_debug_config = 1, - sip_debug_console = 2, -}; - static enum sip_debug_e sipdebug; /*! \brief extra debugging for 'text' related events. - * At the moment this is set together with sip_debug_console. - * \note It should either go away or be implemented properly. + * At the moment this is set together with sip_debug_console. + * \note It should either go away or be implemented properly. */ static int sipdebug_text; -/*! \brief T38 States for a call */ -enum t38state { - T38_DISABLED = 0, /*!< Not enabled */ - T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ - T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ - T38_ENABLED /*!< Negotiated (enabled) */ -}; - -/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */ -struct t38properties { - enum t38state state; /*!< T.38 state */ - struct ast_control_t38_parameters our_parms; - struct ast_control_t38_parameters their_parms; -}; - -/*! \brief Parameters to know status of transfer */ -enum referstatus { - REFER_IDLE, /*!< No REFER is in progress */ - REFER_SENT, /*!< Sent REFER to transferee */ - REFER_RECEIVED, /*!< Received REFER from transferrer */ - REFER_CONFIRMED, /*!< Refer confirmed with a 100 TRYING (unused) */ - REFER_ACCEPTED, /*!< Accepted by transferee */ - REFER_RINGING, /*!< Target Ringing */ - REFER_200OK, /*!< Answered by transfer target */ - REFER_FAILED, /*!< REFER declined - go on */ - REFER_NOAUTH /*!< We had no auth for REFER */ -}; - -/*! \brief generic struct to map between strings and integers. - * Fill it with x-s pairs, terminate with an entry with s = NULL; - * Then you can call map_x_s(...) to map an integer to a string, - * and map_s_x() for the string -> integer mapping. - */ -struct _map_x_s { - int x; - const char *s; -}; - static const struct _map_x_s referstatusstrings[] = { - { REFER_IDLE, "<none>" }, - { REFER_SENT, "Request sent" }, - { REFER_RECEIVED, "Request received" }, - { REFER_CONFIRMED, "Confirmed" }, - { REFER_ACCEPTED, "Accepted" }, - { REFER_RINGING, "Target ringing" }, - { REFER_200OK, "Done" }, - { REFER_FAILED, "Failed" }, - { REFER_NOAUTH, "Failed - auth failure" }, - { -1, NULL} /* terminator */ -}; - -/*! \brief Structure to handle SIP transfers. Dynamically allocated when needed - \note OEJ: Should be moved to string fields */ -struct sip_refer { - char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */ - char refer_to_domain[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO domain */ - char refer_to_urioption[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO uri options */ - char refer_to_context[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO context */ - char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ - char referred_by_name[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ - char refer_contact[AST_MAX_EXTENSION]; /*!< Place to store Contact info from a REFER extension */ - char replaces_callid[SIPBUFSIZE]; /*!< Replace info: callid */ - char replaces_callid_totag[SIPBUFSIZE/2]; /*!< Replace info: to-tag */ - char replaces_callid_fromtag[SIPBUFSIZE/2]; /*!< Replace info: from-tag */ - struct sip_pvt *refer_call; /*!< Call we are referring. This is just a reference to a - * dialog owned by someone else, so we should not destroy - * it when the sip_refer object goes. - */ - int attendedtransfer; /*!< Attended or blind transfer? */ - int localtransfer; /*!< Transfer to local domain? */ - enum referstatus status; /*!< REFER status */ -}; - -/*! \brief Struct to handle custom SIP notify requests. Dynamically allocated when needed */ -struct sip_notify { - struct ast_variable *headers; - struct ast_str *content; -}; - -/*! \brief Structure that encapsulates all attributes related to running - * SIP Session-Timers feature on a per dialog basis. - */ -struct sip_st_dlg { - int st_active; /*!< Session-Timers on/off */ - int st_interval; /*!< Session-Timers negotiated session refresh interval */ - int st_schedid; /*!< Session-Timers ast_sched scheduler id */ - enum st_refresher st_ref; /*!< Session-Timers session refresher */ - int st_expirys; /*!< Session-Timers number of expirys */ - int st_active_peer_ua; /*!< Session-Timers on/off in peer UA */ - int st_cached_min_se; /*!< Session-Timers cached Min-SE */ - int st_cached_max_se; /*!< Session-Timers cached Session-Expires */ - enum st_mode st_cached_mode; /*!< Session-Timers cached M.O. */ - enum st_refresher st_cached_ref; /*!< Session-Timers cached refresher */ - unsigned char quit_flag:1; /*!< Stop trying to lock; just quit */ -}; - - -/*! \brief Structure that encapsulates all attributes related to configuration - * of SIP Session-Timers feature on a per user/peer basis. - */ -struct sip_st_cfg { - enum st_mode st_mode_oper; /*!< Mode of operation for Session-Timers */ - enum st_refresher st_ref; /*!< Session-Timer refresher */ - int st_min_se; /*!< Lowest threshold for session refresh interval */ - int st_max_se; /*!< Highest threshold for session refresh interval */ -}; - -/*! \brief Structure for remembering offered media in an INVITE, to make sure we reply - to all media streams. In theory. In practise, we try our best. */ -struct offered_media { - int offered; - char codecs[128]; -}; - -/*! \brief Structure used for each SIP dialog, ie. a call, a registration, a subscribe. - * Created and initialized by sip_alloc(), the descriptor goes into the list of - * descriptors (dialoglist). - */ -struct sip_pvt { - struct sip_pvt *next; /*!< Next dialog in chain */ - enum invitestates invitestate; /*!< Track state of SIP_INVITEs */ - int method; /*!< SIP method that opened this dialog */ - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(callid); /*!< Global CallID */ - AST_STRING_FIELD(randdata); /*!< Random data */ - AST_STRING_FIELD(accountcode); /*!< Account code */ - AST_STRING_FIELD(realm); /*!< Authorization realm */ - AST_STRING_FIELD(nonce); /*!< Authorization nonce */ - AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ - AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ - AST_STRING_FIELD(domain); /*!< Authorization domain */ - AST_STRING_FIELD(from); /*!< The From: header */ - AST_STRING_FIELD(useragent); /*!< User agent in SIP request */ - AST_STRING_FIELD(exten); /*!< Extension where to start */ - AST_STRING_FIELD(context); /*!< Context for this call */ - AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */ - AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */ - AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */ - AST_STRING_FIELD(fromuser); /*!< User to show in the user field */ - AST_STRING_FIELD(fromname); /*!< Name to show in the user field */ - AST_STRING_FIELD(tohost); /*!< Host we should put in the "to" field */ - AST_STRING_FIELD(todnid); /*!< DNID of this call (overrides host) */ - AST_STRING_FIELD(language); /*!< Default language for this call */ - AST_STRING_FIELD(mohinterpret); /*!< MOH class to use when put on hold */ - AST_STRING_FIELD(mohsuggest); /*!< MOH class to suggest when putting a peer on hold */ - AST_STRING_FIELD(rdnis); /*!< Referring DNIS */ - AST_STRING_FIELD(redircause); /*!< Referring cause */ - AST_STRING_FIELD(theirtag); /*!< Their tag */ - AST_STRING_FIELD(username); /*!< [user] name */ - AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */ - AST_STRING_FIELD(authname); /*!< Who we use for authentication */ - AST_STRING_FIELD(uri); /*!< Original requested URI */ - AST_STRING_FIELD(okcontacturi); /*!< URI from the 200 OK on INVITE */ - AST_STRING_FIELD(peersecret); /*!< Password */ - AST_STRING_FIELD(peermd5secret); - AST_STRING_FIELD(cid_num); /*!< Caller*ID number */ - AST_STRING_FIELD(cid_name); /*!< Caller*ID name */ - AST_STRING_FIELD(mwi_from); /*!< Name to place in the From header in outgoing NOTIFY requests */ - AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ - /* we only store the part in <brackets> in this field. */ - AST_STRING_FIELD(our_contact); /*!< Our contact header */ - AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ - AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ - AST_STRING_FIELD(engine); /*!< RTP engine to use */ - ); - char via[128]; /*!< Via: header */ - struct sip_socket socket; /*!< The socket used for this dialog */ - unsigned int ocseq; /*!< Current outgoing seqno */ - unsigned int icseq; /*!< Current incoming seqno */ - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup group */ - int lastinvite; /*!< Last Cseq of invite */ - struct ast_flags flags[2]; /*!< SIP_ flags */ - - /* boolean flags that don't belong in flags */ - unsigned short do_history:1; /*!< Set if we want to record history */ - unsigned short alreadygone:1; /*!< already destroyed by our peer */ - unsigned short needdestroy:1; /*!< need to be destroyed by the monitor thread */ - unsigned short outgoing_call:1; /*!< this is an outgoing call */ - unsigned short answered_elsewhere:1; /*!< This call is cancelled due to answer on another channel */ - unsigned short novideo:1; /*!< Didn't get video in invite, don't offer */ - unsigned short notext:1; /*!< Text not supported (?) */ - unsigned short session_modify:1; /*!< Session modification request true/false */ - unsigned short route_persistent:1; /*!< Is this the "real" route? */ - unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) - * or respect the other endpoint's request for frame sizes (on) - * for incoming calls - */ - char tag[11]; /*!< Our tag for this session */ - int timer_t1; /*!< SIP timer T1, ms rtt */ - int timer_b; /*!< SIP timer B, ms */ - unsigned int sipoptions; /*!< Supported SIP options on the other end */ - unsigned int reqsipoptions; /*!< Required SIP options on the other end */ - struct ast_codec_pref prefs; /*!< codec prefs */ - format_t capability; /*!< Special capability (codec) */ - format_t jointcapability; /*!< Supported capability at both ends (codecs) */ - format_t peercapability; /*!< Supported peer capability */ - format_t prefcodec; /*!< Preferred codec (outbound only) */ - int noncodeccapability; /*!< DTMF RFC2833 telephony-event */ - int jointnoncodeccapability; /*!< Joint Non codec capability */ - format_t redircodecs; /*!< Redirect codecs */ - int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ - int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ - int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ - int provisional_keepalive_sched_id; /*!< Scheduler ID for provisional responses that need to be sent out to avoid cancellation */ - const char *last_provisional; /*!< The last successfully transmitted provisonal response message */ - int authtries; /*!< Times we've tried to authenticate */ - struct sip_proxy *outboundproxy; /*!< Outbound proxy for this dialog. Use ref_proxy to set this instead of setting it directly*/ - struct t38properties t38; /*!< T38 settings */ - struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */ - struct ast_udptl *udptl; /*!< T.38 UDPTL session */ - int callingpres; /*!< Calling presentation */ - int expiry; /*!< How long we take to expire */ - int sessionversion; /*!< SDP Session Version */ - int sessionid; /*!< SDP Session ID */ - long branch; /*!< The branch identifier of this session */ - long invite_branch; /*!< The branch used when we sent the initial INVITE */ - int64_t sessionversion_remote; /*!< Remote UA's SDP Session Version */ - unsigned int portinuri:1; /*!< Non zero if a port has been specified, will also disable srv lookups */ - struct sockaddr_in sa; /*!< Our peer */ - struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */ - struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */ - struct sockaddr_in tredirip; /*!< Where our Text RTP should be going if not to us */ - time_t lastrtprx; /*!< Last RTP received */ - time_t lastrtptx; /*!< Last RTP sent */ - int rtptimeout; /*!< RTP timeout time */ - struct sockaddr_in recv; /*!< Received as */ - struct sockaddr_in ourip; /*!< Our IP (as seen from the outside) */ - enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ - struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ - struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ - struct sip_notify *notify; /*!< Custom notify type */ - struct sip_auth *peerauth; /*!< Realm authentication */ - int noncecount; /*!< Nonce-count */ - unsigned int stalenonce:1; /*!< Marks the current nonce as responded too */ - char lastmsg[256]; /*!< Last Message sent/received */ - int amaflags; /*!< AMA Flags */ - int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */ - int glareinvite; /*!< A invite received while a pending invite is already present is stored here. Its seqno is the - value. Since this glare invite's seqno is not the same as the pending invite's, it must be - held in order to properly process acknowledgements for our 491 response. */ - struct sip_request initreq; /*!< Latest request that opened a new transaction - within this dialog. - NOT the request that opened the dialog */ - - int initid; /*!< Auto-congest ID if appropriate (scheduler) */ - int waitid; /*!< Wait ID for scheduler after 491 or other delays */ - int autokillid; /*!< Auto-kill ID (scheduler) */ - int t38id; /*!< T.38 Response ID */ - struct sip_refer *refer; /*!< REFER: SIP transfer data structure */ - enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */ - int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ - int laststate; /*!< SUBSCRIBE: Last known extension state */ - int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ - - struct ast_dsp *dsp; /*!< Inband DTMF or Fax CNG tone Detection dsp */ - - struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one - Used in peerpoke, mwi subscriptions */ - struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ - struct ast_rtp_instance *rtp; /*!< RTP Session */ - struct ast_rtp_instance *vrtp; /*!< Video RTP session */ - struct ast_rtp_instance *trtp; /*!< Text RTP session */ - struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ - struct sip_history_head *history; /*!< History of this SIP dialog */ - size_t history_entries; /*!< Number of entires in the history */ - struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ - AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ - struct sip_invite_param *options; /*!< Options for INVITE */ - struct sip_st_dlg *stimer; /*!< SIP Session-Timers */ - - int red; /*!< T.140 RTP Redundancy */ - int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */ - - struct sip_subscription_mwi *mwi; /*!< If this is a subscription MWI dialog, to which subscription */ - /*! The SIP methods supported by this peer. We get this information from the Allow header of the first - * message we receive from an endpoint during a dialog. - */ - unsigned int allowed_methods; - /*! Some peers are not trustworthy with their Allow headers, and so we need to override their wicked - * ways through configuration. This is a copy of the peer's disallowed_methods, so that we can apply them - * to the sip_pvt at various stages of dialog establishment - */ - unsigned int disallowed_methods; - /*! When receiving an SDP offer, it is important to take note of what media types were offered. - * By doing this, even if we don't want to answer a particular media stream with something meaningful, we can - * still put an m= line in our answer with the port set to 0. - * - * The reason for the length being 4 (OFFERED_MEDIA_COUNT) is that in this branch of Asterisk, the only media types supported are - * image, audio, text, and video. Therefore we need to keep track of which types of media were offered. - * Note that secure RTP defines new types of SDP media. - * - * If we wanted to be 100% correct, we would keep a list of all media streams offered. That way we could respond - * even to unknown media types, and we could respond to multiple streams of the same type. Such large-scale changes - * are not a good idea for released branches, though, so we're compromising by just making sure that for the common cases: - * audio and video, audio and T.38, and audio and text, we give the appropriate response to both media streams. - * - * The large-scale changes would be a good idea for implementing during an SDP rewrite. - */ - struct offered_media offered_media[OFFERED_MEDIA_COUNT]; + { REFER_IDLE, "<none>" }, + { REFER_SENT, "Request sent" }, + { REFER_RECEIVED, "Request received" }, + { REFER_CONFIRMED, "Confirmed" }, + { REFER_ACCEPTED, "Accepted" }, + { REFER_RINGING, "Target ringing" }, + { REFER_200OK, "Done" }, + { REFER_FAILED, "Failed" }, + { REFER_NOAUTH, "Failed - auth failure" }, + { -1, NULL} /* terminator */ }; +/* --- Hash tables of various objects --------*/ +#ifdef LOW_MEMORY +static int hash_peer_size = 17; +static int hash_dialog_size = 17; +static int hash_user_size = 17; +#else +static int hash_peer_size = 563; /*!< Size of peer hash table, prime number preferred! */ +static int hash_dialog_size = 563; +static int hash_user_size = 563; +#endif /*! \brief * Here we implement the container for dialogs (sip_pvt), defining @@ -1894,291 +859,10 @@ struct sip_pvt { * references to the individual items. */ static struct ao2_container *dialogs; - #define sip_pvt_lock(x) ao2_lock(x) #define sip_pvt_trylock(x) ao2_trylock(x) #define sip_pvt_unlock(x) ao2_unlock(x) -/*! \brief - * when we create or delete references, make sure to use these - * functions so we keep track of the refcounts. - * To simplify the code, we allow a NULL to be passed to dialog_unref(). - */ -#ifdef REF_DEBUG -#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) - -static struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func) -{ - if (p) - __ao2_ref_debug(p, 1, tag, file, line, func); - else - ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n"); - return p; -} - -static struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func) -{ - if (p) - __ao2_ref_debug(p, -1, tag, file, line, func); - return NULL; -} -#else -static struct sip_pvt *dialog_ref(struct sip_pvt *p, char *tag) -{ - if (p) - ao2_ref(p, 1); - else - ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n"); - return p; -} - -static struct sip_pvt *dialog_unref(struct sip_pvt *p, char *tag) -{ - if (p) - ao2_ref(p, -1); - return NULL; -} -#endif - -/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission - * Packets are linked in a list, whose head is in the struct sip_pvt they belong to. - * Each packet holds a reference to the parent struct sip_pvt. - * This structure is allocated in __sip_reliable_xmit() and only for packets that - * require retransmissions. - */ -struct sip_pkt { - struct sip_pkt *next; /*!< Next packet in linked list */ - int retrans; /*!< Retransmission number */ - int method; /*!< SIP method for this packet */ - int seqno; /*!< Sequence number */ - char is_resp; /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */ - char is_fatal; /*!< non-zero if there is a fatal error */ - int response_code; /*!< If this is a response, the response code */ - struct sip_pvt *owner; /*!< Owner AST call */ - int retransid; /*!< Retransmission ID */ - int timer_a; /*!< SIP timer A, retransmission timer */ - int timer_t1; /*!< SIP Timer T1, estimated RTT or 500 ms */ - int packetlen; /*!< Length of packet */ - struct ast_str *data; -}; - -/*! - * \brief A peer's mailbox - * - * We could use STRINGFIELDS here, but for only two strings, it seems like - * too much effort ... - */ -struct sip_mailbox { - char *mailbox; - char *context; - /*! Associated MWI subscription */ - struct ast_event_sub *event_sub; - AST_LIST_ENTRY(sip_mailbox) entry; -}; - -enum sip_peer_type { - SIP_TYPE_PEER = (1 << 0), - SIP_TYPE_USER = (1 << 1), -}; - -/*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) -*/ -/* XXX field 'name' must be first otherwise sip_addrcmp() will fail, as will astobj2 hashing of the structure */ -struct sip_peer { - char name[80]; /*!< the unique name of this object */ - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(secret); /*!< Password for inbound auth */ - AST_STRING_FIELD(md5secret); /*!< Password in MD5 */ - AST_STRING_FIELD(remotesecret); /*!< Remote secret (trunks, remote devices) */ - AST_STRING_FIELD(context); /*!< Default context for incoming calls */ - AST_STRING_FIELD(subscribecontext); /*!< Default context for subscriptions */ - AST_STRING_FIELD(username); /*!< Temporary username until registration */ - AST_STRING_FIELD(accountcode); /*!< Account code */ - AST_STRING_FIELD(tohost); /*!< If not dynamic, IP address */ - AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */ - AST_STRING_FIELD(fromuser); /*!< From: user when calling this peer */ - AST_STRING_FIELD(fromdomain); /*!< From: domain when calling this peer */ - AST_STRING_FIELD(fullcontact); /*!< Contact registered with us (not in sip.conf) */ - AST_STRING_FIELD(cid_num); /*!< Caller ID num */ - AST_STRING_FIELD(cid_name); /*!< Caller ID name */ - AST_STRING_FIELD(vmexten); /*!< Dialplan extension for MWI notify message*/ - AST_STRING_FIELD(language); /*!< Default language for prompts */ - AST_STRING_FIELD(mohinterpret); /*!< Music on Hold class */ - AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ - AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ - AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ - AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ - AST_STRING_FIELD(engine); /*!< RTP Engine to use */ - AST_STRING_FIELD(unsolicited_mailbox); /*!< Mailbox to store received unsolicited MWI NOTIFY messages information in */ - ); - struct sip_socket socket; /*!< Socket used for this peer */ - enum sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport. - If register expires, default should be reset. to this value */ - /* things that don't belong in flags */ - unsigned short transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ - unsigned short is_realtime:1; /*!< this is a 'realtime' peer */ - unsigned short rt_fromcontact:1;/*!< copy fromcontact from realtime */ - unsigned short host_dynamic:1; /*!< Dynamic Peers register with Asterisk */ - unsigned short selfdestruct:1; /*!< Automatic peers need to destruct themselves */ - unsigned short the_mark:1; /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */ - unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) - * or respect the other endpoint's request for frame sizes (on) - * for incoming calls - */ - unsigned short deprecated_username:1; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */ - struct sip_auth *auth; /*!< Realm authentication list */ - int amaflags; /*!< AMA Flags (for billing) */ - int callingpres; /*!< Calling id presentation */ - int inUse; /*!< Number of calls in use */ - int inRinging; /*!< Number of calls ringing */ - int onHold; /*!< Peer has someone on hold */ - int call_limit; /*!< Limit of concurrent calls */ - int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ - int busy_level; /*!< Level of active channels where we signal busy */ - enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ - struct ast_codec_pref prefs; /*!< codec prefs */ - int lastmsgssent; - unsigned int sipoptions; /*!< Supported SIP options */ - struct ast_flags flags[2]; /*!< SIP_ flags */ - - /*! Mailboxes that this peer cares about */ - AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes; - - int maxcallbitrate; /*!< Maximum Bitrate for a video call */ - int expire; /*!< When to expire this peer registration */ - format_t capability; /*!< Codec capability */ - int rtptimeout; /*!< RTP timeout */ - int rtpholdtimeout; /*!< RTP Hold Timeout */ - int rtpkeepalive; /*!< Send RTP packets for keepalive */ - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup group */ - struct sip_proxy *outboundproxy; /*!< Outbound proxy for this peer */ - struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ - struct sockaddr_in addr; /*!< IP address of peer */ - unsigned int portinuri:1; /*!< Whether the port should be included in the URI */ - struct sip_pvt *call; /*!< Call pointer */ - int pokeexpire; /*!< Qualification: When to expire poke (qualify= checking) */ - int lastms; /*!< Qualification: How long last response took (in ms), or -1 for no response */ - int maxms; /*!< Qualification: Max ms we will accept for the host to be up, 0 to not monitor */ - int qualifyfreq; /*!< Qualification: Qualification: How often to check for the host to be up */ - struct timeval ps; /*!< Qualification: Time for sending SIP OPTION in sip_pke_peer() */ - struct sockaddr_in defaddr; /*!< Default IP address, used until registration */ - struct ast_ha *ha; /*!< Access control list */ - struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */ - struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ - struct sip_pvt *mwipvt; /*!< Subscription for MWI */ - struct sip_st_cfg stimer; /*!< SIP Session-Timers */ - int timer_t1; /*!< The maximum T1 value for the peer */ - int timer_b; /*!< The maximum timer B (transaction timeouts) */ - - /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */ - enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */ - unsigned int disallowed_methods; -}; - - -/*! - * \brief Registrations with other SIP proxies - * - * Created by sip_register(), the entry is linked in the 'regl' list, - * and never deleted (other than at 'sip reload' or module unload times). - * The entry always has a pending timeout, either waiting for an ACK to - * the REGISTER message (in which case we have to retransmit the request), - * or waiting for the next REGISTER message to be sent (either the initial one, - * or once the previously completed registration one expires). - * The registration can be in one of many states, though at the moment - * the handling is a bit mixed. - */ -struct sip_registry { - ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1); - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(callid); /*!< Global Call-ID */ - AST_STRING_FIELD(realm); /*!< Authorization realm */ - AST_STRING_FIELD(nonce); /*!< Authorization nonce */ - AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ - AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ - AST_STRING_FIELD(domain); /*!< Authorization domain */ - AST_STRING_FIELD(username); /*!< Who we are registering as */ - AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */ - AST_STRING_FIELD(hostname); /*!< Domain or host we register to */ - AST_STRING_FIELD(secret); /*!< Password in clear text */ - AST_STRING_FIELD(md5secret); /*!< Password in md5 */ - AST_STRING_FIELD(callback); /*!< Contact extension */ - AST_STRING_FIELD(peername); /*!< Peer registering to */ - ); - enum sip_transport transport; /*!< Transport for this registration UDP, TCP or TLS */ - int portno; /*!< Optional port override */ - int expire; /*!< Sched ID of expiration */ - int configured_expiry; /*!< Configured value to use for the Expires header */ - int expiry; /*!< Negotiated value used for the Expires header */ - int regattempts; /*!< Number of attempts (since the last success) */ - int timeout; /*!< sched id of sip_reg_timeout */ - int refresh; /*!< How often to refresh */ - struct sip_pvt *call; /*!< create a sip_pvt structure for each outbound "registration dialog" in progress */ - enum sipregistrystate regstate; /*!< Registration state (see above) */ - struct timeval regtime; /*!< Last successful registration time */ - int callid_valid; /*!< 0 means we haven't chosen callid for this registry yet. */ - unsigned int ocseq; /*!< Sequence number we got to for REGISTERs for this registry */ - struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for register */ - struct sockaddr_in us; /*!< Who the server thinks we are */ - int noncecount; /*!< Nonce-count */ - char lastmsg[256]; /*!< Last Message sent/received */ -}; - -enum sip_tcptls_alert { - /*! \brief There is new data to be sent out */ - TCPTLS_ALERT_DATA, - /*! \brief A request to stop the tcp_handler thread */ - TCPTLS_ALERT_STOP, -}; - -struct tcptls_packet { - AST_LIST_ENTRY(tcptls_packet) entry; - struct ast_str *data; - size_t len; -}; -/*! \brief Definition of a thread that handles a socket */ -struct sip_threadinfo { - int stop; - int alert_pipe[2]; /*! Used to alert tcptls thread when packet is ready to be written */ - pthread_t threadid; - struct ast_tcptls_session_instance *tcptls_session; - enum sip_transport type; /*!< We keep a copy of the type here so we can display it in the connection list */ - AST_LIST_HEAD_NOLOCK(, tcptls_packet) packet_q; -}; - -/*! \brief Definition of an MWI subscription to another server */ -struct sip_subscription_mwi { - ASTOBJ_COMPONENTS_FULL(struct sip_subscription_mwi,1,1); - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(username); /*!< Who we are sending the subscription as */ - AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */ - AST_STRING_FIELD(hostname); /*!< Domain or host we subscribe to */ - AST_STRING_FIELD(secret); /*!< Password in clear text */ - AST_STRING_FIELD(mailbox); /*!< Mailbox store to put MWI into */ - ); - enum sip_transport transport; /*!< Transport to use */ - int portno; /*!< Optional port override */ - int resub; /*!< Sched ID of resubscription */ - unsigned int subscribed:1; /*!< Whether we are currently subscribed or not */ - struct sip_pvt *call; /*!< Outbound subscription dialog */ - struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for subscription */ - struct sockaddr_in us; /*!< Who the server thinks we are */ -}; - -/* --- Hash tables of various objects --------*/ - -#ifdef LOW_MEMORY -static int hash_peer_size = 17; -static int hash_dialog_size = 17; -static int hash_user_size = 17; -#else -static int hash_peer_size = 563; /*!< Size of peer hash table, prime number preferred! */ -static int hash_dialog_size = 563; -static int hash_user_size = 563; -#endif - /*! \brief The table of TCP threads */ static struct ao2_container *threadt; @@ -2196,117 +880,6 @@ static struct ast_register_list { static struct ast_subscription_mwi_list { ASTOBJ_CONTAINER_COMPONENTS(struct sip_subscription_mwi); } submwil; - -/*! \brief - * \note The only member of the peer used here is the name field - */ -static int peer_hash_cb(const void *obj, const int flags) -{ - const struct sip_peer *peer = obj; - - return ast_str_case_hash(peer->name); -} - -/*! - * \note The only member of the peer used here is the name field - */ -static int peer_cmp_cb(void *obj, void *arg, int flags) -{ - struct sip_peer *peer = obj, *peer2 = arg; - - return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0; -} - -/*! - * \note the peer's ip address field is used to create key. - */ -static int peer_iphash_cb(const void *obj, const int flags) -{ - const struct sip_peer *peer = obj; - int ret1 = peer->addr.sin_addr.s_addr; - if (ret1 < 0) - ret1 = -ret1; - - return ret1; -} - -/*! - * Match Peers by IP and Port number. - * - * This function has two modes. - * - If the peer arg does not have INSECURE_PORT set, then we will only return - * a match for a peer that matches both the IP and port. - * - If the peer arg does have the INSECURE_PORT flag set, then we will only - * return a match for a peer that matches the IP and has insecure=port - * in its configuration. - * - * This callback will be used twice when doing peer matching. There is a first - * pass for full IP+port matching, and a second pass in case there is a match - * that meets the insecure=port criteria. - * - * \note Connections coming in over TCP or TLS should never be matched by port. - * - * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields. - */ -static int peer_ipcmp_cb(void *obj, void *arg, int flags) -{ - struct sip_peer *peer = obj, *peer2 = arg; - - if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr) { - /* IP doesn't match */ - return 0; - } - - /* We matched the IP, check to see if we need to match by port as well. */ - if ((peer->transports & peer2->transports) & (SIP_TRANSPORT_TLS | SIP_TRANSPORT_TCP)) { - /* peer matching on port is not possible with TCP/TLS */ - return CMP_MATCH | CMP_STOP; - } else if (ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) { - /* We are allowing match without port for peers configured that - * way in this pass through the peers. */ - return ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) ? - (CMP_MATCH | CMP_STOP) : 0; - } - - /* Now only return a match if the port matches, as well. */ - return peer->addr.sin_port == peer2->addr.sin_port ? (CMP_MATCH | CMP_STOP) : 0; -} - - -static int threadt_hash_cb(const void *obj, const int flags) -{ - const struct sip_threadinfo *th = obj; - - return (int) th->tcptls_session->remote_address.sin_addr.s_addr; -} - -static int threadt_cmp_cb(void *obj, void *arg, int flags) -{ - struct sip_threadinfo *th = obj, *th2 = arg; - - return (th->tcptls_session == th2->tcptls_session) ? CMP_MATCH | CMP_STOP : 0; -} - -/*! - * \note The only member of the dialog used here callid string - */ -static int dialog_hash_cb(const void *obj, const int flags) -{ - const struct sip_pvt *pvt = obj; - - return ast_str_case_hash(pvt->callid); -} - -/*! - * \note The only member of the dialog used here callid string - */ -static int dialog_cmp_cb(void *obj, void *arg, int flags) -{ - struct sip_pvt *pvt = obj, *pvt2 = arg; - - return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH | CMP_STOP : 0; -} - static int temp_pvt_init(void *); static void temp_pvt_cleanup(void *); @@ -2317,7 +890,6 @@ AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup); * \todo Move the sip_auth list to AST_LIST */ static struct sip_auth *authl = NULL; - /* --- Sockets and networking --------------*/ /*! \brief Main socket for UDP SIP communication. @@ -2364,15 +936,15 @@ static struct sockaddr_in internip; * Other variables (externhost, externexpire, externrefresh) are used * to support the above functions. */ -static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */ -static struct sockaddr_in media_address; /*!< External RTP IP address if we are behind NAT */ +static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */ +static struct sockaddr_in media_address; /*!< External RTP IP address if we are behind NAT */ -static char externhost[MAXHOSTNAMELEN]; /*!< External host name */ -static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */ -static int externrefresh = 10; /*!< Refresh timer for DNS-based external address (dyndns) */ -static struct sockaddr_in stunaddr; /*!< stun server address */ -static uint16_t externtcpport; /*!< external tcp port */ -static uint16_t externtlsport; /*!< external tls port */ +static char externhost[MAXHOSTNAMELEN]; /*!< External host name */ +static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */ +static int externrefresh = 10; /*!< Refresh timer for DNS-based external address (dyndns) */ +static struct sockaddr_in stunaddr; /*!< stun server address */ +static uint16_t externtcpport; /*!< external tcp port */ +static uint16_t externtlsport; /*!< external tls port */ /*! \brief List of local networks * We store "localnet" addresses from the config file into an access list, @@ -2380,13 +952,13 @@ static uint16_t externtlsport; /*!< external tls port */ * AST_SENSE_DENY for 'local' addresses, and AST_SENSE_ALLOW for 'non local' * (i.e. presumably public) addresses. */ -static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */ +static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */ -static int ourport_tcp; /*!< The port used for TCP connections */ -static int ourport_tls; /*!< The port used for TCP/TLS connections */ +static int ourport_tcp; /*!< The port used for TCP connections */ +static int ourport_tls; /*!< The port used for TCP/TLS connections */ static struct sockaddr_in debugaddr; -static struct ast_config *notify_types = NULL; /*!< The list of manual NOTIFY types we know how to send */ +static struct ast_config *notify_types = NULL; /*!< The list of manual NOTIFY types we know how to send */ /*! some list management macros. */ @@ -2397,12 +969,6 @@ static struct ast_config *notify_types = NULL; /*!< The list of manual NOTIFY t (head) = (element)->next; \ } while (0) -enum t38_action_flag { - SDP_T38_NONE = 0, /*!< Do not modify T38 information at all */ - SDP_T38_INITIATE, /*!< Remote side has requested T38 with us */ - SDP_T38_ACCEPT, /*!< Remote side accepted our T38 request */ -}; - /*---------------------------- Forward declarations of functions in chan_sip.c */ /* Note: This is added to help splitting up chan_sip.c into several files in coming releases. */ @@ -2429,7 +995,6 @@ static const char *sip_get_callid(struct ast_channel *chan); static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin); static int sip_standard_port(enum sip_transport type, int port); static int sip_prepare_socket(struct sip_pvt *p); -static int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport); /*--- Transmitting responses and requests */ static int sipsock_read(int *id, int fd, short events, void *ignore); @@ -2555,8 +1120,6 @@ static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context); - - /*--- Device monitoring and Device/extension state/event handling */ static int cb_extensionstate(char *context, char* exten, int state, void *data); static int sip_devicestate(void *data); @@ -2684,7 +1247,6 @@ static int get_also_info(struct sip_pvt *p, struct sip_request *oreq); static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req); static int set_address_from_contact(struct sip_pvt *pvt); static void check_via(struct sip_pvt *p, struct sip_request *req); -static const char *get_calleridname(const char *input, char *output, size_t outputsize); static int get_rpid(struct sip_pvt *p, struct sip_request *oreq); static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason); static int get_destination(struct sip_pvt *p, struct sip_request *oreq); @@ -2823,7 +1385,6 @@ static const struct ast_channel_tech sip_tech = { */ static struct ast_channel_tech sip_tech_info; - /*! \brief Working TLS connection configuration */ static struct ast_tls_config sip_tls_cfg; @@ -2859,6 +1420,48 @@ static struct ast_tcptls_session_args sip_tls_desc = { \return Always returns 0 */ #define append_history(p, event, fmt , args... ) append_history_full(p, "%-15s " fmt, event, ## args) +/*! \brief + * when we create or delete references, make sure to use these + * functions so we keep track of the refcounts. + * To simplify the code, we allow a NULL to be passed to dialog_unref(). + */ +#ifdef REF_DEBUG +#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) +#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +static struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func) +{ + if (p) + __ao2_ref_debug(p, 1, tag, file, line, func); + else + ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n"); + return p; +} + +static struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func) +{ + if (p) + __ao2_ref_debug(p, -1, tag, file, line, func); + return NULL; +} +#else +static struct sip_pvt *dialog_ref(struct sip_pvt *p, char *tag) +{ + if (p) + ao2_ref(p, 1); + else + ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n"); + return p; +} + +static struct sip_pvt *dialog_unref(struct sip_pvt *p, char *tag) +{ + if (p) + ao2_ref(p, -1); + return NULL; +} +#endif + /*! \brief map from an integer value to a string. * If no match is found, return errorstring */ @@ -2885,6 +1488,30 @@ static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue) return errorvalue; } +static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text) +{ + enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN; + int i; + + for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) { + if (!strcasecmp(text, sip_reason_table[i].text)) { + ast = sip_reason_table[i].code; + break; + } + } + + return ast; +} + +static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code) +{ + if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) { + return sip_reason_table[code].text; + } + + return "unknown"; +} + /*! * \brief generic function for determining if a correct transport is being * used to contact a peer @@ -3463,7 +2090,7 @@ static int proxy_update(struct sip_proxy *proxy) * pt buffer is provided or the pt has errors when being converted * to an int value, the port provided as the standard is used. */ -static int port_str2int(const char *pt, unsigned int standard) +unsigned int port_str2int(const char *pt, unsigned int standard) { int port = standard; if (ast_strlen_zero(pt) || (sscanf(pt, "%30d", &port) != 1) || (port < 1) || (port > 65535)) { @@ -4634,105 +3261,6 @@ static char *get_in_brackets(char *tmp) return tmp; } -/*! \brief * parses a URI in its components. - * - * \note - * - If scheme is specified, drop it from the top. - * - If a component is not requested, do not split around it. - * - Multiple scheme's can be specified ',' delimited. ex: "sip:,sips:" - * - * This means that if we don't have domain, we cannot split - * name:pass and domain:port. - * It is safe to call with ret_name, pass, domain, port - * pointing all to the same place. - * Init pointers to empty string so we never get NULL dereferencing. - * Overwrites the string. - * return 0 on success, other values on error. - * \verbatim - * general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] - * \endverbatim - * - */ -static int parse_uri(char *uri, const char *scheme, - char **ret_name, char **pass, char **domain, char **port, char **options, char **transport) -{ - char *name = NULL; - int error = 0; - - /* init field as required */ - if (pass) - *pass = ""; - if (port) - *port = ""; - if (scheme) { - int l; - char *scheme2 = ast_strdupa(scheme); - char *cur = strsep(&scheme2, ","); - for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) { - l = strlen(cur); - if (!strncasecmp(uri, cur, l)) { - uri += l; - break; - } - } - if (ast_strlen_zero(cur)) { - ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme); - error = -1; - } - } - if (transport) { - char *t, *type = ""; - *transport = ""; - if ((t = strstr(uri, "transport="))) { - strsep(&t, "="); - if ((type = strsep(&t, ";"))) { - *transport = type; - } - } - } - - if (!domain) { - /* if we don't want to split around domain, keep everything as a name, - * so we need to do nothing here, except remember why. - */ - } else { - /* store the result in a temp. variable to avoid it being - * overwritten if arguments point to the same place. - */ - char *c, *dom = ""; - - if ((c = strchr(uri, '@')) == NULL) { - /* domain-only URI, according to the SIP RFC. */ - dom = uri; - name = ""; - } else { - *c++ = '\0'; - dom = c; - name = uri; - } - - /* Remove options in domain and name */ - dom = strsep(&dom, ";"); - name = strsep(&name, ";"); - - if (port && (c = strchr(dom, ':'))) { /* Remove :port */ - *c++ = '\0'; - *port = c; - } - if (pass && (c = strchr(name, ':'))) { /* user:password */ - *c++ = '\0'; - *pass = c; - } - *domain = dom; - } - if (ret_name) /* same as for domain, store the result only at the end */ - *ret_name = name; - if (options) - *options = uri ? uri : ""; - - return error; -} - /*! \brief Send message with Access-URL header, if this is an HTML URL only! */ static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen) { @@ -7727,188 +6255,35 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si return NULL; } -/*! \brief Parse register=> line in sip.conf and add to registry */ +/*! \brief create sip_registry object from register=> line in sip.conf and link into reg container */ static int sip_register(const char *value, int lineno) { struct sip_registry *reg; - int portnum = 0; - enum sip_transport transport = SIP_TRANSPORT_UDP; - char buf[256] = ""; - char *userpart = NULL, *hostpart = NULL; - /* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] */ - AST_DECLARE_APP_ARGS(pre1, - AST_APP_ARG(peer); - AST_APP_ARG(userpart); - ); - AST_DECLARE_APP_ARGS(pre2, - AST_APP_ARG(transport); - AST_APP_ARG(blank); - AST_APP_ARG(userpart); - ); - AST_DECLARE_APP_ARGS(user1, - AST_APP_ARG(userpart); - AST_APP_ARG(secret); - AST_APP_ARG(authuser); - ); - AST_DECLARE_APP_ARGS(host1, - AST_APP_ARG(hostpart); - AST_APP_ARG(expiry); - ); - AST_DECLARE_APP_ARGS(host2, - AST_APP_ARG(hostpart); - AST_APP_ARG(extension); - ); - AST_DECLARE_APP_ARGS(host3, - AST_APP_ARG(host); - AST_APP_ARG(port); - ); - if (!value) - return -1; - ast_copy_string(buf, value, sizeof(buf)); - - /*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] - * becomes - * userpart => [peer?][transport://]user[@domain][:secret[:authuser]] - * hostpart => host[:port][/extension][~expiry] - */ - if ((hostpart = strrchr(buf, '@'))) { - *hostpart++ = '\0'; - userpart = buf; - } - - if (ast_strlen_zero(userpart) || ast_strlen_zero(hostpart)) { - ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno); + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n"); return -1; } - /*! - * pre1.peer => peer - * pre1.userpart => [transport://]user[@domain][:secret[:authuser]] - * hostpart => host[:port][/extension][~expiry] - */ - AST_NONSTANDARD_RAW_ARGS(pre1, userpart, '?'); - if (ast_strlen_zero(pre1.userpart)) { - pre1.userpart = pre1.peer; - pre1.peer = NULL; - } - - /*! - * pre1.peer => peer - * pre2.transport = transport - * pre2.userpart => user[@domain][:secret[:authuser]] - * hostpart => host[:port][/extension][~expiry] - */ - AST_NONSTANDARD_RAW_ARGS(pre2, pre1.userpart, '/'); - if (ast_strlen_zero(pre2.userpart)) { - pre2.userpart = pre2.transport; - pre2.transport = NULL; - } else { - pre2.transport[strlen(pre2.transport) - 1] = '\0'; /* Remove trailing : */ - } + ast_atomic_fetchadd_int(®objs, 1); + ASTOBJ_INIT(reg); - if (!ast_strlen_zero(pre2.blank)) { - ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno); + if (sip_parse_register_line(reg, value, lineno)) { + registry_unref(reg, "failure to parse, unref the reg pointer"); return -1; } - /*! - * pre1.peer => peer - * pre2.transport = transport - * user1.userpart => user[@domain] - * user1.secret => secret - * user1.authuser => authuser - * hostpart => host[:port][/extension][~expiry] - */ - AST_NONSTANDARD_RAW_ARGS(user1, pre2.userpart, ':'); - - /*! - * pre1.peer => peer - * pre2.transport = transport - * user1.userpart => user[@domain] - * user1.secret => secret - * user1.authuser => authuser - * host1.hostpart => host[:port][/extension] - * host1.expiry => [expiry] - */ - AST_NONSTANDARD_RAW_ARGS(host1, hostpart, '~'); - - /*! - * pre1.peer => peer - * pre2.transport = transport - * user1.userpart => user[@domain] - * user1.secret => secret - * user1.authuser => authuser - * host2.hostpart => host[:port] - * host2.extension => [extension] - * host1.expiry => [expiry] - */ - AST_NONSTANDARD_RAW_ARGS(host2, host1.hostpart, '/'); - - /*! - * pre1.peer => peer - * pre2.transport = transport - * user1.userpart => user[@domain] - * user1.secret => secret - * user1.authuser => authuser - * host3.host => host - * host3.port => port - * host2.extension => extension - * host1.expiry => expiry - */ - AST_NONSTANDARD_RAW_ARGS(host3, host2.hostpart, ':'); - - if (host3.port) { - if (!(portnum = port_str2int(host3.port, 0))) { - ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", host3.port, lineno); - } + /* set default expiry if necessary */ + if (reg->refresh && !reg->expiry && !reg->configured_expiry) { + reg->refresh = reg->expiry = reg->configured_expiry = default_expiry; } - /* set transport type */ - if (!pre2.transport) { - transport = SIP_TRANSPORT_UDP; - } else if (!strncasecmp(pre2.transport, "tcp", 3)) { - transport = SIP_TRANSPORT_TCP; - } else if (!strncasecmp(pre2.transport, "tls", 3)) { - transport = SIP_TRANSPORT_TLS; - } else if (!strncasecmp(pre2.transport, "udp", 3)) { - transport = SIP_TRANSPORT_UDP; - } else { - transport = SIP_TRANSPORT_UDP; - ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", pre2.transport, lineno); - } + /* Add the new registry entry to the list */ + ASTOBJ_CONTAINER_LINK(®l, reg); - /* if no portnum specified, set default for transport */ - if (!portnum) { - if (transport == SIP_TRANSPORT_TLS) { - portnum = STANDARD_TLS_PORT; - } else { - portnum = STANDARD_SIP_PORT; - } - } - - if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { - ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n"); - return -1; - } + /* release the reference given by ASTOBJ_INIT. The container has another reference */ + registry_unref(reg, "unref the reg pointer"); - ast_atomic_fetchadd_int(®objs, 1); - ASTOBJ_INIT(reg); - ast_string_field_set(reg, callback, ast_strip_quoted(S_OR(host2.extension, "s"), "\"", "\"")); - ast_string_field_set(reg, username, ast_strip_quoted(S_OR(user1.userpart, ""), "\"", "\"")); - ast_string_field_set(reg, hostname, ast_strip_quoted(S_OR(host3.host, ""), "\"", "\"")); - ast_string_field_set(reg, authuser, ast_strip_quoted(S_OR(user1.authuser, ""), "\"", "\"")); - ast_string_field_set(reg, secret, ast_strip_quoted(S_OR(user1.secret, ""), "\"", "\"")); - ast_string_field_set(reg, peername, ast_strip_quoted(S_OR(pre1.peer, ""), "\"", "\"")); - - reg->transport = transport; - reg->timeout = reg->expire = -1; - reg->refresh = reg->expiry = reg->configured_expiry = (host1.expiry ? atoi(ast_strip_quoted(host1.expiry, "\"", "\"")) : default_expiry); - reg->portno = portnum; - reg->callid_valid = FALSE; - reg->ocseq = INITIAL_CSEQ; - ASTOBJ_CONTAINER_LINK(®l, reg); /* Add the new registry entry to the list */ - registry_unref(reg, "unref the reg pointer"); /* release the reference given by ASTOBJ_INIT. The container has another reference */ return 0; } @@ -12762,7 +11137,7 @@ static int __set_address_from_contact(const char *fullcontact, struct sockaddr_i * We still need to be able to send to the remote agent through the proxy. */ - if (parse_uri(contact, "sip:,sips:", &contact, NULL, &host, &pt, NULL, &transport)) { + if (parse_uri(contact, "sip:,sips:", &contact, NULL, &host, &pt, &transport)) { ast_log(LOG_WARNING, "Invalid contact uri %s (missing sip: or sips:), attempting to use anyway\n", fullcontact); } @@ -12884,7 +11259,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st ast_string_field_build(pvt, our_contact, "<%s>", curi); /* Make sure it's a SIP URL */ - if (parse_uri(curi, "sip:,sips:", &curi, NULL, &host, &pt, NULL, &transport)) { + if (parse_uri(curi, "sip:,sips:", &curi, NULL, &host, &pt, &transport)) { ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n"); } @@ -13543,7 +11918,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr c = get_in_brackets(tmp); c = remove_uri_parameters(c); - if (parse_uri(c, "sip:,sips:", &name, NULL, &domain, NULL, NULL, NULL)) { + if (parse_uri(c, "sip:,sips:", &name, NULL, &domain, NULL, NULL)) { ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(sin->sin_addr)); return -1; } @@ -14051,7 +12426,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) uri = get_in_brackets(tmp); - if (parse_uri(uri, "sip:,sips:", &uri, NULL, &domain, NULL, NULL, NULL)) { + if (parse_uri(uri, "sip:,sips:", &uri, NULL, &domain, NULL, NULL)) { ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri); return -1; } @@ -14068,7 +12443,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf)); if (!ast_strlen_zero(tmpf)) { from = get_in_brackets(tmpf); - if (parse_uri(from, "sip:,sips:", &from, NULL, &domain, NULL, NULL, NULL)) { + if (parse_uri(from, "sip:,sips:", &from, NULL, &domain, NULL, NULL)) { ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", from); return -1; } @@ -14419,7 +12794,7 @@ static int get_also_info(struct sip_pvt *p, struct sip_request *oreq) ast_copy_string(tmp, get_header(req, "Also"), sizeof(tmp)); c = get_in_brackets(tmp); - if (parse_uri(c, "sip:,sips:", &c, NULL, &a, NULL, NULL, NULL)) { + if (parse_uri(c, "sip:,sips:", &c, NULL, &a, NULL, NULL)) { ast_log(LOG_WARNING, "Huh? Not a SIP header in Also: transfer (%s)?\n", c); return -1; } @@ -14557,188 +12932,6 @@ static void check_via(struct sip_pvt *p, struct sip_request *req) } } -/*! \brief Get caller id name from SIP headers, copy into output buffer - * - * \retval input string pointer placed after display-name field if possible - */ -static const char *get_calleridname(const char *input, char *output, size_t outputsize) -{ - /* From RFC3261: - * - * From = ( "From" / "f" ) HCOLON from-spec - * from-spec = ( name-addr / addr-spec ) *( SEMI from-param ) - * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - * display-name = *(token LWS)/ quoted-string - * token = 1*(alphanum / "-" / "." / "!" / "%" / "*" - * / "_" / "+" / "`" / "'" / "~" ) - * quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE - * qdtext = LWS / %x21 / %x23-5B / %x5D-7E - * / UTF8-NONASCII - * quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) - * - * HCOLON = *WSP ":" SWS - * SWS = [LWS] - * LWS = *[*WSP CRLF] 1*WSP - * WSP = (SP / HTAB) - * - * Deviations from it: - * - following CRLF's in LWS is not done (here at least) - * - ascii NUL is never legal as it terminates the C-string - * - utf8-nonascii is not checked for validity - */ - char *orig_output = output; - const char *orig_input = input; - - /* clear any empty characters in the beginning */ - input = ast_skip_blanks(input); - - /* no data at all or no storage room? */ - if (!input || *input == '<' || !outputsize || !output) { - return orig_input; - } - - /* make sure the output buffer is initilized */ - *orig_output = '\0'; - - /* make room for '\0' at the end of the output buffer */ - outputsize--; - - /* quoted-string rules */ - if (input[0] == '"') { - input++; /* skip the first " */ - - for (;((outputsize > 0) && *input); input++) { - if (*input == '"') { /* end of quoted-string */ - break; - } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */ - input++; - if (!*input || (unsigned char)*input > 0x7f || *input == 0xa || *input == 0xd) { - continue; /* not a valid quoted-pair, so skip it */ - } - } else if (((*input != 0x9) && ((unsigned char) *input < 0x20)) || - (*input == 0x7f)) { - continue; /* skip this invalid character. */ - } - - *output++ = *input; - outputsize--; - } - - /* if this is successful, input should be at the ending quote */ - if (!input || *input != '"') { - ast_log(LOG_WARNING, "No ending quote for display-name was found\n"); - *orig_output = '\0'; - return orig_input; - } - - /* make sure input is past the last quote */ - input++; - - /* terminate outbuf */ - *output = '\0'; - } else { /* either an addr-spec or tokenLWS-combo */ - for (;((outputsize > 0) && *input); input++) { - /* token or WSP (without LWS) */ - if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z') - || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.' - || *input == '!' || *input == '%' || *input == '*' || *input == '_' - || *input == '+' || *input == '`' || *input == '\'' || *input == '~' - || *input == 0x9 || *input == ' ') { - *output++ = *input; - outputsize -= 1; - } else if (*input == '<') { /* end of tokenLWS-combo */ - /* we could assert that the previous char is LWS, but we don't care */ - break; - } else if (*input == ':') { - /* This invalid character which indicates this is addr-spec rather than display-name. */ - *orig_output = '\0'; - return orig_input; - } else { /* else, invalid character we can skip. */ - continue; /* skip this character */ - } - } - - /* set NULL while trimming trailing whitespace */ - do { - *output-- = '\0'; - } while (*output == 0x9 || *output == ' '); /* we won't go past orig_output as first was a non-space */ - } - - return input; -} - -AST_TEST_DEFINE(get_calleridname_test) -{ - int res = AST_TEST_PASS; - const char *in1 = "\" quoted-text internal \\\" quote \"<stuff>"; - const char *in2 = " token text with no quotes <stuff>"; - const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>"; - const char *noendquote = " \"quoted-text no end <stuff>"; - const char *addrspec = " \"sip:blah@blah <stuff>"; - const char *after_dname; - char dname[40]; - - switch (cmd) { - case TEST_INIT: - info->name = "sip_get_calleridname_test"; - info->category = "channels/chan_sip/"; - info->summary = "decodes callerid name from sip header"; - info->description = "Decodes display-name field of sip header. Checks for valid output and expected failure cases."; - return AST_TEST_NOT_RUN; - case TEST_EXECUTE: - break; - } - - /* quoted-text with backslash escaped quote */ - after_dname = get_calleridname(in1, dname, sizeof(dname)); - ast_test_status_update(&args->status_update, "display-name1: %s\nafter: %s\n", dname, after_dname); - if (strcmp(dname, " quoted-text internal \" quote ")) { - ast_test_status_update(&args->status_update, "display-name1 test failed\n"); - ast_str_append(&args->ast_test_error_str, 0, "quoted-text with internal backslash decode failed. \n"); - res = AST_TEST_FAIL; - } - - /* token text */ - after_dname = get_calleridname(in2, dname, sizeof(dname)); - ast_test_status_update(&args->status_update, "display-name2: %s\nafter: %s\n", dname, after_dname); - if (strcmp(dname, "token text with no quotes")) { - ast_test_status_update(&args->status_update, "display-name2 test failed\n"); - ast_str_append(&args->ast_test_error_str, 0, "token text with decode failed. \n"); - res = AST_TEST_FAIL; - } - - /* quoted-text buffer overflow */ - after_dname = get_calleridname(overflow1, dname, sizeof(dname)); - ast_test_status_update(&args->status_update, "overflow display-name1: %s\nafter: %s\n", dname, after_dname); - if (*dname != '\0' && after_dname != overflow1) { - ast_test_status_update(&args->status_update, "overflow display-name1 test failed\n"); - ast_str_append(&args->ast_test_error_str, 0, "quoted-text buffer overflow check failed. \n"); - res = AST_TEST_FAIL; - } - - /* quoted-text buffer with no terminating end quote */ - after_dname = get_calleridname(noendquote, dname, sizeof(dname)); - ast_test_status_update(&args->status_update, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname); - if (*dname != '\0' && after_dname != noendquote) { - ast_test_status_update(&args->status_update, "no end quote for quoted-text display-name failed\n"); - ast_str_append(&args->ast_test_error_str, 0, "quoted-text buffer check no terminating end quote failed. \n"); - res = AST_TEST_FAIL; - } - - /* addr-spec rather than display-name. */ - after_dname = get_calleridname(addrspec, dname, sizeof(dname)); - ast_test_status_update(&args->status_update, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname); - if (*dname != '\0' && after_dname != addrspec) { - ast_test_status_update(&args->status_update, "detection of addr-spec failed\n"); - ast_str_append(&args->ast_test_error_str, 0, "detection of addr-spec failed. \n"); - res = AST_TEST_FAIL; - } - - - return res; -} - - /*! \brief Validate device authentication */ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, struct sip_request *req, int sipmethod, struct sockaddr_in *sin, @@ -14955,7 +13148,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ ast_string_field_set(p, from, of); /* ignore all fields but name */ - if (parse_uri(of, "sip:,sips:", &of, &dummy, &domain, &dummy, &dummy, NULL)) { + if (parse_uri(of, "sip:,sips:", &of, &dummy, &domain, &dummy, NULL)) { ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); } @@ -23409,55 +21602,6 @@ create_tcptls_session_fail: } /*! - * \brief Small function to parse a config line for a host with a transport - * i.e. tls://www.google.com:8056 - */ -static int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport) -{ - char *port; - - if ((*hostname = strstr(line, "://"))) { - *hostname += 3; - - if (!strncasecmp(line, "tcp", 3)) - *transport = SIP_TRANSPORT_TCP; - else if (!strncasecmp(line, "tls", 3)) - *transport = SIP_TRANSPORT_TLS; - else if (!strncasecmp(line, "udp", 3)) - *transport = SIP_TRANSPORT_UDP; - else - ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", line, lineno); - } else { - *hostname = line; - *transport = SIP_TRANSPORT_UDP; - } - - if ((line = strrchr(*hostname, '@'))) - line++; - else - line = *hostname; - - if ((port = strrchr(line, ':'))) { - *port++ = '\0'; - - if (!sscanf(port, "%5u", portnum)) { - ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno); - port = NULL; - } - } - - if (!port) { - if (*transport & SIP_TRANSPORT_TLS) { - *portnum = STANDARD_TLS_PORT; - } else { - *portnum = STANDARD_SIP_PORT; - } - } - - return 0; -} - -/*! * \brief Get cached MWI info * \retval 0 At least one message is waiting * \retval 1 no messages waiting @@ -26969,6 +25113,118 @@ static int reload(void) return 1; } +/*! \brief + * \note The only member of the peer used here is the name field + */ +static int peer_hash_cb(const void *obj, const int flags) +{ + const struct sip_peer *peer = obj; + + return ast_str_case_hash(peer->name); +} + +/*! + * \note The only member of the peer used here is the name field + */ +static int peer_cmp_cb(void *obj, void *arg, int flags) +{ + struct sip_peer *peer = obj, *peer2 = arg; + + return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0; +} + +/*! + * \note the peer's ip address field is used to create key. + */ +static int peer_iphash_cb(const void *obj, const int flags) +{ + const struct sip_peer *peer = obj; + int ret1 = peer->addr.sin_addr.s_addr; + if (ret1 < 0) + ret1 = -ret1; + + return ret1; +} + +/*! + * Match Peers by IP and Port number. + * + * This function has two modes. + * - If the peer arg does not have INSECURE_PORT set, then we will only return + * a match for a peer that matches both the IP and port. + * - If the peer arg does have the INSECURE_PORT flag set, then we will only + * return a match for a peer that matches the IP and has insecure=port + * in its configuration. + * + * This callback will be used twice when doing peer matching. There is a first + * pass for full IP+port matching, and a second pass in case there is a match + * that meets the insecure=port criteria. + * + * \note Connections coming in over TCP or TLS should never be matched by port. + * + * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields. + */ +static int peer_ipcmp_cb(void *obj, void *arg, int flags) +{ + struct sip_peer *peer = obj, *peer2 = arg; + + if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr) { + /* IP doesn't match */ + return 0; + } + + /* We matched the IP, check to see if we need to match by port as well. */ + if ((peer->transports & peer2->transports) & (SIP_TRANSPORT_TLS | SIP_TRANSPORT_TCP)) { + /* peer matching on port is not possible with TCP/TLS */ + return CMP_MATCH | CMP_STOP; + } else if (ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) { + /* We are allowing match without port for peers configured that + * way in this pass through the peers. */ + return ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) ? + (CMP_MATCH | CMP_STOP) : 0; + } + + /* Now only return a match if the port matches, as well. */ + return peer->addr.sin_port == peer2->addr.sin_port ? (CMP_MATCH | CMP_STOP) : 0; +} + + +static int threadt_hash_cb(const void *obj, const int flags) +{ + const struct sip_threadinfo *th = obj; + + return (int) th->tcptls_session->remote_address.sin_addr.s_addr; +} + +static int threadt_cmp_cb(void *obj, void *arg, int flags) +{ + struct sip_threadinfo *th = obj, *th2 = arg; + + return (th->tcptls_session == th2->tcptls_session) ? CMP_MATCH | CMP_STOP : 0; +} + +/*! + * \note The only member of the dialog used here callid string + */ +static int dialog_hash_cb(const void *obj, const int flags) +{ + const struct sip_pvt *pvt = obj; + + return ast_str_case_hash(pvt->callid); +} + +/*! + * \note The only member of the dialog used here callid string + */ +static int dialog_cmp_cb(void *obj, void *arg, int flags) +{ + struct sip_pvt *pvt = obj, *pvt2 = arg; + + return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH | CMP_STOP : 0; +} + + + /*! \brief SIP Cli commands definition */ static struct ast_cli_entry cli_sip[] = { AST_CLI_DEFINE(sip_show_channels, "List active SIP channels or subscriptions"), @@ -26999,13 +25255,15 @@ static struct ast_cli_entry cli_sip[] = { /*! \brief SIP test registration */ static void sip_register_tests(void) { - AST_TEST_REGISTER(get_calleridname_test); + sip_config_parser_register_tests(); + sip_request_parser_register_tests(); } /*! \brief SIP test registration */ static void sip_unregister_tests(void) { - AST_TEST_UNREGISTER(get_calleridname_test); + sip_config_parser_unregister_tests(); + sip_request_parser_unregister_tests(); } /*! \brief PBX load module - initialization */ diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c new file mode 100644 index 000000000..c925d30cc --- /dev/null +++ b/channels/sip/config_parser.c @@ -0,0 +1,657 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief sip config parsing functions and unit tests + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "include/sip.h" +#include "include/config_parser.h" +#include "include/sip_utils.h" + +/*! \brief Parse register=> line in sip.conf + * + * \retval 0 on success + * \retval -1 on failure + */ +int sip_parse_register_line(struct sip_registry *reg, const char *value, int lineno) +{ + int portnum = 0; + enum sip_transport transport = SIP_TRANSPORT_UDP; + char buf[256] = ""; + char *userpart = NULL, *hostpart = NULL; + /* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] */ + AST_DECLARE_APP_ARGS(pre1, + AST_APP_ARG(peer); + AST_APP_ARG(userpart); + ); + AST_DECLARE_APP_ARGS(pre2, + AST_APP_ARG(transport); + AST_APP_ARG(blank); + AST_APP_ARG(userpart); + ); + AST_DECLARE_APP_ARGS(user1, + AST_APP_ARG(userpart); + AST_APP_ARG(secret); + AST_APP_ARG(authuser); + ); + AST_DECLARE_APP_ARGS(host1, + AST_APP_ARG(hostpart); + AST_APP_ARG(expiry); + ); + AST_DECLARE_APP_ARGS(host2, + AST_APP_ARG(hostpart); + AST_APP_ARG(extension); + ); + AST_DECLARE_APP_ARGS(host3, + AST_APP_ARG(host); + AST_APP_ARG(port); + ); + + if (!value) { + return -1; + } + + if (!reg) { + return -1; + } + ast_copy_string(buf, value, sizeof(buf)); + + /*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] + * becomes + * userpart => [peer?][transport://]user[@domain][:secret[:authuser]] + * hostpart => host[:port][/extension][~expiry] + */ + if ((hostpart = strrchr(buf, '@'))) { + *hostpart++ = '\0'; + userpart = buf; + } + + if (ast_strlen_zero(userpart) || ast_strlen_zero(hostpart)) { + ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno); + return -1; + } + + /*! + * pre1.peer => peer + * pre1.userpart => [transport://]user[@domain][:secret[:authuser]] + * hostpart => host[:port][/extension][~expiry] + */ + AST_NONSTANDARD_RAW_ARGS(pre1, userpart, '?'); + if (ast_strlen_zero(pre1.userpart)) { + pre1.userpart = pre1.peer; + pre1.peer = NULL; + } + + /*! + * pre1.peer => peer + * pre2.transport = transport + * pre2.userpart => user[@domain][:secret[:authuser]] + * hostpart => host[:port][/extension][~expiry] + */ + AST_NONSTANDARD_RAW_ARGS(pre2, pre1.userpart, '/'); + if (ast_strlen_zero(pre2.userpart)) { + pre2.userpart = pre2.transport; + pre2.transport = NULL; + } else { + pre2.transport[strlen(pre2.transport) - 1] = '\0'; /* Remove trailing : */ + } + + if (!ast_strlen_zero(pre2.blank)) { + ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno); + return -1; + } + + /*! + * pre1.peer => peer + * pre2.transport = transport + * user1.userpart => user[@domain] + * user1.secret => secret + * user1.authuser => authuser + * hostpart => host[:port][/extension][~expiry] + */ + AST_NONSTANDARD_RAW_ARGS(user1, pre2.userpart, ':'); + + /*! + * pre1.peer => peer + * pre2.transport = transport + * user1.userpart => user[@domain] + * user1.secret => secret + * user1.authuser => authuser + * host1.hostpart => host[:port][/extension] + * host1.expiry => [expiry] + */ + AST_NONSTANDARD_RAW_ARGS(host1, hostpart, '~'); + + /*! + * pre1.peer => peer + * pre2.transport = transport + * user1.userpart => user[@domain] + * user1.secret => secret + * user1.authuser => authuser + * host2.hostpart => host[:port] + * host2.extension => [extension] + * host1.expiry => [expiry] + */ + AST_NONSTANDARD_RAW_ARGS(host2, host1.hostpart, '/'); + + /*! + * pre1.peer => peer + * pre2.transport = transport + * user1.userpart => user[@domain] + * user1.secret => secret + * user1.authuser => authuser + * host3.host => host + * host3.port => port + * host2.extension => extension + * host1.expiry => expiry + */ + AST_NONSTANDARD_RAW_ARGS(host3, host2.hostpart, ':'); + + if (host3.port) { + if (!(portnum = port_str2int(host3.port, 0))) { + ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", host3.port, lineno); + } + } + + /* set transport type */ + if (!pre2.transport) { + transport = SIP_TRANSPORT_UDP; + } else if (!strncasecmp(pre2.transport, "tcp", 3)) { + transport = SIP_TRANSPORT_TCP; + } else if (!strncasecmp(pre2.transport, "tls", 3)) { + transport = SIP_TRANSPORT_TLS; + } else if (!strncasecmp(pre2.transport, "udp", 3)) { + transport = SIP_TRANSPORT_UDP; + } else { + transport = SIP_TRANSPORT_UDP; + ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", pre2.transport, lineno); + } + + /* if no portnum specified, set default for transport */ + if (!portnum) { + if (transport == SIP_TRANSPORT_TLS) { + portnum = STANDARD_TLS_PORT; + } else { + portnum = STANDARD_SIP_PORT; + } + } + + /* copy into sip_registry object */ + ast_string_field_set(reg, callback, ast_strip_quoted(S_OR(host2.extension, "s"), "\"", "\"")); + ast_string_field_set(reg, username, ast_strip_quoted(S_OR(user1.userpart, ""), "\"", "\"")); + ast_string_field_set(reg, hostname, ast_strip_quoted(S_OR(host3.host, ""), "\"", "\"")); + ast_string_field_set(reg, authuser, ast_strip_quoted(S_OR(user1.authuser, ""), "\"", "\"")); + ast_string_field_set(reg, secret, ast_strip_quoted(S_OR(user1.secret, ""), "\"", "\"")); + ast_string_field_set(reg, peername, ast_strip_quoted(S_OR(pre1.peer, ""), "\"", "\"")); + + reg->transport = transport; + reg->timeout = reg->expire = -1; + reg->portno = portnum; + reg->callid_valid = FALSE; + reg->ocseq = INITIAL_CSEQ; + if (!ast_strlen_zero(host1.expiry)) { + reg->refresh = reg->expiry = reg->configured_expiry = atoi(ast_strip_quoted(host1.expiry, "\"", "\"")); + } + + return 0; +} + +AST_TEST_DEFINE(sip_parse_register_line_test) +{ + int res = AST_TEST_PASS; + struct sip_registry *reg; + const char *reg1 = "name@domain"; + const char *reg2 = "name:pass@domain"; + const char *reg3 = "name@namedomain:pass:authuser@domain"; + const char *reg4 = "name@namedomain:pass:authuser@domain/extension"; + const char *reg5 = "tcp://name@namedomain:pass:authuser@domain/extension"; + const char *reg6 = "tls://name@namedomain:pass:authuser@domain/extension~111"; + const char *reg7 = "peer?tcp://name@namedomain:pass:authuser@domain:1234/extension~111"; + const char *reg8 = "peer?name@namedomain:pass:authuser@domain:1234/extension~111"; + const char *reg9 = "peer?name:pass:authuser:1234/extension~111"; + const char *reg10 = "@domin:1234"; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_parse_register_line_test"; + info->category = "channels/chan_sip/"; + info->summary = "tests sip register line parsing"; + info->description = + " Tests parsing of various register line configurations." + " Verifies output matches expected behavior."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* ---Test reg 1, simple config --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg1, 1) || + strcmp(reg->callback, "s") || + strcmp(reg->username, "name") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "") || + strcmp(reg->secret, "") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_UDP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh || + reg->expiry || + reg->configured_expiry || + reg->portno != STANDARD_SIP_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 1: simple config failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 2, add secret --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg2, 1) || + strcmp(reg->callback, "s") || + strcmp(reg->username, "name") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_UDP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh || + reg->expiry || + reg->configured_expiry || + reg->portno != STANDARD_SIP_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 2: add secret failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 3, add userdomain and authuser --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg3, 1) || + strcmp(reg->callback, "s") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_UDP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh || + reg->expiry || + reg->configured_expiry || + reg->portno != STANDARD_SIP_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 3: add userdomain and authuser failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 4, add callback extension --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg4, 1) || + strcmp(reg->callback, "extension") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_UDP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh || + reg->expiry || + reg->configured_expiry || + reg->portno != STANDARD_SIP_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 4: add callback extension failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 5, add transport --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg5, 1) || + strcmp(reg->callback, "extension") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_TCP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh || + reg->expiry || + reg->configured_expiry || + reg->portno != STANDARD_SIP_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 5: add transport failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 6, change to tls transport, add expiry --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg6, 1) || + strcmp(reg->callback, "extension") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "") || + reg->transport != SIP_TRANSPORT_TLS || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh != 111 || + reg->expiry != 111 || + reg->configured_expiry != 111 || + reg->portno != STANDARD_TLS_PORT || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 6: change to tls transport and add expiry failed\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 7, change transport to tcp, add custom port, and add peer --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg7, 1) || + strcmp(reg->callback, "extension") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "peer") || + reg->transport != SIP_TRANSPORT_TCP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh != 111 || + reg->expiry != 111 || + reg->configured_expiry != 111 || + reg->portno != 1234 || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 7, change transport to tcp, add custom port, and add peer failed.\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 8, remove transport --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if ( + sip_parse_register_line(reg, reg8, 1) || + strcmp(reg->callback, "extension") || + strcmp(reg->username, "name@namedomain") || + strcmp(reg->hostname, "domain") || + strcmp(reg->authuser, "authuser") || + strcmp(reg->secret, "pass") || + strcmp(reg->peername, "peer") || + reg->transport != SIP_TRANSPORT_UDP || + reg->timeout != -1 || + reg->expire != -1 || + reg->refresh != 111 || + reg->expiry != 111 || + reg->configured_expiry != 111 || + reg->portno != 1234 || + reg->callid_valid != FALSE || + reg->ocseq != INITIAL_CSEQ) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 8, remove transport failed.\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 9, missing domain, expected to fail --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if (!sip_parse_register_line(reg, reg9, 1)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 9, missing domain, expected to fail but did not.\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 10, missing user, expected to fail --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if (!sip_parse_register_line(reg, reg10, 1)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 10, missing user expected to fail but did not\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + /* ---Test reg 11, no registry object, expected to fail--- */ + if (!sip_parse_register_line(NULL, reg1, 1)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 11, no registery object, expected to fail but did not.\n"); + res = AST_TEST_FAIL; + } + + /* ---Test reg 11, no registry line, expected to fail --- */ + if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) { + goto alloc_fail; + } else if (!sip_parse_register_line(reg, NULL, 1)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 11, NULL register line expected to fail but did not.\n"); + res = AST_TEST_FAIL; + } + ast_string_field_free_memory(reg); + ast_free(reg); + + + return res; + +alloc_fail: + ast_str_set(&args->ast_test_error_str, 0, "Out of memory. \n"); + return res; +} + +int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport) +{ + char *port; + + if (ast_strlen_zero(line)) { + return -1; + } + if ((*hostname = strstr(line, "://"))) { + *hostname += 3; + + if (!strncasecmp(line, "tcp", 3)) + *transport = SIP_TRANSPORT_TCP; + else if (!strncasecmp(line, "tls", 3)) + *transport = SIP_TRANSPORT_TLS; + else if (!strncasecmp(line, "udp", 3)) + *transport = SIP_TRANSPORT_UDP; + else + ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", line, lineno); + } else { + *hostname = line; + *transport = SIP_TRANSPORT_UDP; + } + + if ((line = strrchr(*hostname, '@'))) + line++; + else + line = *hostname; + + if ((port = strrchr(line, ':'))) { + *port++ = '\0'; + + if (!sscanf(port, "%5u", portnum)) { + ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno); + port = NULL; + } + } + + if (!port) { + if (*transport & SIP_TRANSPORT_TLS) { + *portnum = STANDARD_TLS_PORT; + } else { + *portnum = STANDARD_SIP_PORT; + } + } + + return 0; +} + +AST_TEST_DEFINE(sip_parse_host_line_test) +{ + int res = AST_TEST_PASS; + char *host; + int port; + enum sip_transport transport; + char host1[] = "www.blah.com"; + char host2[] = "tcp://www.blah.com"; + char host3[] = "tls://10.10.10.10"; + char host4[] = "tls://10.10.10.10:1234"; + char host5[] = "10.10.10.10:1234"; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_parse_host_line_test"; + info->category = "channels/chan_sip/"; + info->summary = "tests sip.conf host line parsing"; + info->description = + " Tests parsing of various host line configurations." + " Verifies output matches expected behavior."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* test 1, simple host */ + sip_parse_host(host1, 1, &host, &port, &transport); + if (port != STANDARD_SIP_PORT || + ast_strlen_zero(host) || strcmp(host, "www.blah.com") || + transport != SIP_TRANSPORT_UDP) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 1: simple host failed.\n"); + res = AST_TEST_FAIL; + } + + /* test 2, add tcp transport */ + sip_parse_host(host2, 1, &host, &port, &transport); + if (port != STANDARD_SIP_PORT || + ast_strlen_zero(host) || strcmp(host, "www.blah.com") || + transport != SIP_TRANSPORT_TCP) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 2: tcp host failed.\n"); + res = AST_TEST_FAIL; + } + + /* test 3, add tls transport */ + sip_parse_host(host3, 1, &host, &port, &transport); + if (port != STANDARD_TLS_PORT || + ast_strlen_zero(host) || strcmp(host, "10.10.10.10") || + transport != SIP_TRANSPORT_TLS) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 3: tls host failed. \n"); + res = AST_TEST_FAIL; + } + + /* test 4, add custom port with tls */ + sip_parse_host(host4, 1, &host, &port, &transport); + if (port != 1234 || + ast_strlen_zero(host) || strcmp(host, "10.10.10.10") || + transport != SIP_TRANSPORT_TLS) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 4: tls host with custom port failed.\n"); + res = AST_TEST_FAIL; + } + + /* test 5, simple host with custom port */ + sip_parse_host(host5, 1, &host, &port, &transport); + if (port != 1234 || + ast_strlen_zero(host) || strcmp(host, "10.10.10.10") || + transport != SIP_TRANSPORT_UDP) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 5: simple host with custom port failed.\n"); + res = AST_TEST_FAIL; + } + return res; + + /* test 6, expected failure with NULL input */ + if (sip_parse_host(NULL, 1, &host, &port, &transport)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 6: expected error on NULL input did not occur.\n"); + res = AST_TEST_FAIL; + } + return res; + +} + +/*! \brief SIP test registration */ +void sip_config_parser_register_tests(void) +{ + AST_TEST_REGISTER(sip_parse_register_line_test); + AST_TEST_REGISTER(sip_parse_host_line_test); +} + +/*! \brief SIP test registration */ +void sip_config_parser_unregister_tests(void) +{ + AST_TEST_UNREGISTER(sip_parse_register_line_test); + AST_TEST_UNREGISTER(sip_parse_host_line_test); +} + diff --git a/channels/sip/include/config_parser.h b/channels/sip/include/config_parser.h new file mode 100644 index 000000000..776851e4f --- /dev/null +++ b/channels/sip/include/config_parser.h @@ -0,0 +1,50 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief sip.conf parser header file + */ + +#include "sip.h" + +#ifndef _SIP_CONF_PARSE_H +#define _SIP_CONF_PARSE_H + + +/*! \brief Parse register=> line in sip.conf + * + * \retval 0 on success + * \retval -1 on failure + */ +int sip_parse_register_line(struct sip_registry *reg, const char *value, int lineno); + +/*! + * \brief parses a config line for a host with a transport + * i.e. tls://www.google.com:8056 + * + * \retval 0 on success + * \retval -1 on failure + */ +int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport); + +/*! \brief register config parsing tests */ +void sip_config_parser_register_tests(void); + +/*! \brief unregister config parsing tests */ +void sip_config_parser_unregister_tests(void); + +#endif diff --git a/channels/sip/include/reqresp_parser.h b/channels/sip/include/reqresp_parser.h new file mode 100644 index 000000000..c123fff2b --- /dev/null +++ b/channels/sip/include/reqresp_parser.h @@ -0,0 +1,57 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief sip request response parser header file + */ + +#ifndef _SIP_REQRESP_H +#define _SIP_REQRESP_H + +/*! \brief parses a URI in its components. + * + * \note + * - Multiple scheme's can be specified ',' delimited. ex: "sip:,sips:" + * - If a component is not requested, do not split around it. This means + * that if we don't have domain, we cannot split name:pass and domain:port. + * - It is safe to call with ret_name, pass, domain, port pointing all to + * the same place. + * - This function overwrites the the uri string. + * + * \retval 0 on success + * \retval -1 on error. + * + * \verbatim + * general form we are expecting is sip:user:password;user-parameters@host:port;uri-parameters?headers + * \endverbatim + * + */ +int parse_uri(char *uri, const char *scheme, char **ret_name, char **pass, char **domain, char **port, char **transport); + +/*! \brief Get caller id name from SIP headers, copy into output buffer + * + * \retval input string pointer placed after display-name field if possible + */ +const char *get_calleridname(const char *input, char *output, size_t outputsize); + +/*! \brief register request parsing tests */ +void sip_request_parser_register_tests(void); + +/*! \brief unregister request parsing tests */ +void sip_request_parser_unregister_tests(void); + +#endif diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h new file mode 100644 index 000000000..9b605fe98 --- /dev/null +++ b/channels/sip/include/sip.h @@ -0,0 +1,1280 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief chan_sip header file + */ + +#ifndef _SIP_H +#define _SIP_H + +#include "asterisk.h" + +#include "asterisk/stringfields.h" +#include "asterisk/linkedlists.h" +#include "asterisk/strings.h" +#include "asterisk/tcptls.h" +#include "asterisk/test.h" +#include "asterisk/channel.h" +#include "asterisk/app.h" +#include "asterisk/astobj.h" + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +/* Arguments for find_peer */ +#define FINDUSERS (1 << 0) +#define FINDPEERS (1 << 1) +#define FINDALLDEVICES (FINDUSERS | FINDPEERS) + +#define SIPBUFSIZE 512 /*!< Buffer size for many operations */ + +#define XMIT_ERROR -2 + +#define SIP_RESERVED ";/?:@&=+$,# " /*!< Reserved characters in the username part of the URI */ + +#define DEFAULT_DEFAULT_EXPIRY 120 +#define DEFAULT_MIN_EXPIRY 60 +#define DEFAULT_MAX_EXPIRY 3600 +#define DEFAULT_MWI_EXPIRY 3600 +#define DEFAULT_REGISTRATION_TIMEOUT 20 +#define DEFAULT_MAX_FORWARDS "70" + +/* guard limit must be larger than guard secs */ +/* guard min must be < 1000, and should be >= 250 */ +#define EXPIRY_GUARD_SECS 15 /*!< How long before expiry do we reregister */ +#define EXPIRY_GUARD_LIMIT 30 /*!< Below here, we use EXPIRY_GUARD_PCT instead of EXPIRY_GUARD_SECS */ +#define EXPIRY_GUARD_MIN 500 /*!< This is the minimum guard time applied. If + * GUARD_PCT turns out to be lower than this, it + * will use this time instead. + * This is in milliseconds. + */ +#define EXPIRY_GUARD_PCT 0.20 /*!< Percentage of expires timeout to use when + * below EXPIRY_GUARD_LIMIT */ +#define DEFAULT_EXPIRY 900 /*!< Expire slowly */ + +#define DEFAULT_QUALIFY_GAP 100 +#define DEFAULT_QUALIFY_PEERS 1 + +#define CALLERID_UNKNOWN "Anonymous" +#define FROMDOMAIN_INVALID "anonymous.invalid" + +#define DEFAULT_MAXMS 2000 /*!< Qualification: Must be faster than 2 seconds by default */ +#define DEFAULT_QUALIFYFREQ 60 * 1000 /*!< Qualification: How often to check for the host to be up */ +#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */ + +#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */ +#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */ +#define DEFAULT_TIMER_T1 500 /*!< SIP timer T1 (according to RFC 3261) */ +#define SIP_TRANS_TIMEOUT 64 * DEFAULT_TIMER_T1 /*!< SIP request timeout (rfc 3261) 64*T1 + * \todo Use known T1 for timeout (peerpoke) + */ +#define DEFAULT_TRANS_TIMEOUT -1 /*!< Use default SIP transaction timeout */ +#define PROVIS_KEEPALIVE_TIMEOUT 60000 /*!< How long to wait before retransmitting a provisional response (rfc 3261 13.3.1.1) */ +#define MAX_AUTHTRIES 3 /*!< Try authentication three times, then fail */ + +#define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ +#define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */ +#define SIP_MIN_PACKET 4096 /*!< Initialize size of memory to allocate for packets */ +#define MAX_HISTORY_ENTRIES 50 /*!< Max entires in the history list for a sip_pvt */ + +#define INITIAL_CSEQ 101 /*!< Our initial sip sequence number */ + +#define DEFAULT_MAX_SE 1800 /*!< Session-Timer Default Session-Expires period (RFC 4028) */ +#define DEFAULT_MIN_SE 90 /*!< Session-Timer Default Min-SE period (RFC 4028) */ + +#define SDP_MAX_RTPMAP_CODECS 32 /*!< Maximum number of codecs allowed in received SDP */ + +#define RTP 1 +#define NO_RTP 0 + +#define DEC_CALL_LIMIT 0 +#define INC_CALL_LIMIT 1 +#define DEC_CALL_RINGING 2 +#define INC_CALL_RINGING 3 + +/*! Define SIP option tags, used in Require: and Supported: headers + * We need to be aware of these properties in the phones to use + * the replace: header. We should not do that without knowing + * that the other end supports it... + * This is nothing we can configure, we learn by the dialog + * Supported: header on the REGISTER (peer) or the INVITE + * (other devices) + * We are not using many of these today, but will in the future. + * This is documented in RFC 3261 + */ +#define SUPPORTED 1 +#define NOT_SUPPORTED 0 + +/* SIP options */ +#define SIP_OPT_REPLACES (1 << 0) +#define SIP_OPT_100REL (1 << 1) +#define SIP_OPT_TIMER (1 << 2) +#define SIP_OPT_EARLY_SESSION (1 << 3) +#define SIP_OPT_JOIN (1 << 4) +#define SIP_OPT_PATH (1 << 5) +#define SIP_OPT_PREF (1 << 6) +#define SIP_OPT_PRECONDITION (1 << 7) +#define SIP_OPT_PRIVACY (1 << 8) +#define SIP_OPT_SDP_ANAT (1 << 9) +#define SIP_OPT_SEC_AGREE (1 << 10) +#define SIP_OPT_EVENTLIST (1 << 11) +#define SIP_OPT_GRUU (1 << 12) +#define SIP_OPT_TARGET_DIALOG (1 << 13) +#define SIP_OPT_NOREFERSUB (1 << 14) +#define SIP_OPT_HISTINFO (1 << 15) +#define SIP_OPT_RESPRIORITY (1 << 16) +#define SIP_OPT_FROMCHANGE (1 << 17) +#define SIP_OPT_RECLISTINV (1 << 18) +#define SIP_OPT_RECLISTSUB (1 << 19) +#define SIP_OPT_OUTBOUND (1 << 20) +#define SIP_OPT_UNKNOWN (1 << 21) + +/*! \brief SIP Methods we support + * \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have + * allowsubscribe and allowrefer on in sip.conf. + */ +#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" + +/*! \brief SIP Extensions we support + * \note This should be generated based on the previous array + * in combination with settings. + * + * \todo We should not have "timer" if it's disabled in the configuration file. + */ +#define SUPPORTED_EXTENSIONS "replaces, timer" + +/*! \brief Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS */ +#define STANDARD_SIP_PORT 5060 +/*! \brief Standard SIP TLS port from RFC 3261. DO NOT CHANGE THIS */ +#define STANDARD_TLS_PORT 5061 + +/*! \note in many SIP headers, absence of a port number implies port 5060, + * and this is why we cannot change the above constant. + * There is a limited number of places in asterisk where we could, + * in principle, use a different "default" port number, but + * we do not support this feature at the moment. + * You can run Asterisk with SIP on a different port with a configuration + * option. If you change this value in the source code, the signalling will be incorrect. + * + */ + +/*! \name DefaultValues Default values, set and reset in reload_config before reading configuration + + These are default values in the source. There are other recommended values in the + sip.conf.sample for new installations. These may differ to keep backwards compatibility, + yet encouraging new behaviour on new installations + */ +/*@{*/ +#define DEFAULT_CONTEXT "default" /*!< The default context for [general] section as well as devices */ +#define DEFAULT_MOHINTERPRET "default" /*!< The default music class */ +#define DEFAULT_MOHSUGGEST "" +#define DEFAULT_VMEXTEN "asterisk" /*!< Default voicemail extension */ +#define DEFAULT_CALLERID "asterisk" /*!< Default caller ID */ +#define DEFAULT_MWI_FROM "" +#define DEFAULT_NOTIFYMIME "application/simple-message-summary" +#define DEFAULT_ALLOWGUEST TRUE +#define DEFAULT_RTPKEEPALIVE 0 /*!< Default RTPkeepalive setting */ +#define DEFAULT_CALLCOUNTER FALSE /*!< Do not enable call counters by default */ +#define DEFAULT_SRVLOOKUP TRUE /*!< Recommended setting is ON */ +#define DEFAULT_COMPACTHEADERS FALSE /*!< Send compact (one-character) SIP headers. Default off */ +#define DEFAULT_TOS_SIP 0 /*!< Call signalling packets should be marked as DSCP CS3, but the default is 0 to be compatible with previous versions. */ +#define DEFAULT_TOS_AUDIO 0 /*!< Audio packets should be marked as DSCP EF (Expedited Forwarding), but the default is 0 to be compatible with previous versions. */ +#define DEFAULT_TOS_VIDEO 0 /*!< Video packets should be marked as DSCP AF41, but the default is 0 to be compatible with previous versions. */ +#define DEFAULT_TOS_TEXT 0 /*!< Text packets should be marked as XXXX XXXX, but the default is 0 to be compatible with previous versions. */ +#define DEFAULT_COS_SIP 4 /*!< Level 2 class of service for SIP signalling */ +#define DEFAULT_COS_AUDIO 5 /*!< Level 2 class of service for audio media */ +#define DEFAULT_COS_VIDEO 6 /*!< Level 2 class of service for video media */ +#define DEFAULT_COS_TEXT 5 /*!< Level 2 class of service for text media (T.140) */ +#define DEFAULT_ALLOW_EXT_DOM TRUE /*!< Allow external domains */ +#define DEFAULT_REALM "asterisk" /*!< Realm for HTTP digest authentication */ +#define DEFAULT_DOMAINSASREALM FALSE /*!< Use the domain option to guess the realm for registration and invite requests */ +#define DEFAULT_NOTIFYRINGING TRUE /*!< Notify devicestate system on ringing state */ +#define DEFAULT_NOTIFYCID DISABLED /*!< Include CID with ringing notifications */ +#define DEFAULT_PEDANTIC FALSE /*!< Avoid following SIP standards for dialog matching */ +#define DEFAULT_AUTOCREATEPEER FALSE /*!< Don't create peers automagically */ +#define DEFAULT_MATCHEXTERNIPLOCALLY FALSE /*!< Match extern IP locally default setting */ +#define DEFAULT_QUALIFY FALSE /*!< Don't monitor devices */ +#define DEFAULT_CALLEVENTS FALSE /*!< Extra manager SIP call events */ +#define DEFAULT_ALWAYSAUTHREJECT FALSE /*!< Don't reject authentication requests always */ +#define DEFAULT_REGEXTENONQUALIFY FALSE +#define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */ +#define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */ +#ifndef DEFAULT_USERAGENT +#define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ +#define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */ +#define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */ +#define DEFAULT_ENGINE "asterisk" /*!< Default RTP engine to use for sessions */ +#define DEFAULT_CAPABILITY (AST_FORMAT_ULAW | AST_FORMAT_TESTLAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263); +#endif +/*@}*/ + +/*! \name SIPflags + Various flags for the flags field in the pvt structure + Trying to sort these up (one or more of the following): + D: Dialog + P: Peer/user + G: Global flag + When flags are used by multiple structures, it is important that + they have a common layout so it is easy to copy them. +*/ +/*@{*/ +#define SIP_OUTGOING (1 << 0) /*!< D: Direction of the last transaction in this dialog */ +#define SIP_RINGING (1 << 2) /*!< D: Have sent 180 ringing */ +#define SIP_PROGRESS_SENT (1 << 3) /*!< D: Have sent 183 message progress */ +#define SIP_NEEDREINVITE (1 << 4) /*!< D: Do we need to send another reinvite? */ +#define SIP_PENDINGBYE (1 << 5) /*!< D: Need to send bye after we ack? */ +#define SIP_GOTREFER (1 << 6) /*!< D: Got a refer? */ +#define SIP_CALL_LIMIT (1 << 7) /*!< D: Call limit enforced for this call */ +#define SIP_INC_COUNT (1 << 8) /*!< D: Did this dialog increment the counter of in-use calls? */ +#define SIP_INC_RINGING (1 << 9) /*!< D: Did this connection increment the counter of in-use calls? */ +#define SIP_DEFER_BYE_ON_TRANSFER (1 << 10) /*!< D: Do not hangup at first ast_hangup */ + +#define SIP_PROMISCREDIR (1 << 11) /*!< DP: Promiscuous redirection */ +#define SIP_TRUSTRPID (1 << 12) /*!< DP: Trust RPID headers? */ +#define SIP_USEREQPHONE (1 << 13) /*!< DP: Add user=phone to numeric URI. Default off */ +#define SIP_USECLIENTCODE (1 << 14) /*!< DP: Trust X-ClientCode info message */ + +/* DTMF flags - see str2dtmfmode() and dtmfmode2str() */ +#define SIP_DTMF (7 << 15) /*!< DP: DTMF Support: five settings, uses three bits */ +#define SIP_DTMF_RFC2833 (0 << 15) /*!< DP: DTMF Support: RTP DTMF - "rfc2833" */ +#define SIP_DTMF_INBAND (1 << 15) /*!< DP: DTMF Support: Inband audio, only for ULAW/ALAW - "inband" */ +#define SIP_DTMF_INFO (2 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" */ +#define SIP_DTMF_AUTO (3 << 15) /*!< DP: DTMF Support: AUTO switch between rfc2833 and in-band DTMF */ +#define SIP_DTMF_SHORTINFO (4 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" - short variant */ + +/* NAT settings */ +#define SIP_NAT_FORCE_RPORT (1 << 18) /*!< DP: Force rport even if not present in the request */ +#define SIP_NAT_RPORT_PRESENT (1 << 19) /*!< DP: rport was present in the request */ + +/* re-INVITE related settings */ +#define SIP_REINVITE (7 << 20) /*!< DP: four settings, uses three bits */ +#define SIP_REINVITE_NONE (0 << 20) /*!< DP: no reinvite allowed */ +#define SIP_DIRECT_MEDIA (1 << 20) /*!< DP: allow peers to be reinvited to send media directly p2p */ +#define SIP_DIRECT_MEDIA_NAT (2 << 20) /*!< DP: allow media reinvite when new peer is behind NAT */ +#define SIP_REINVITE_UPDATE (4 << 20) /*!< DP: use UPDATE (RFC3311) when reinviting this peer */ + +/* "insecure" settings - see insecure2str() */ +#define SIP_INSECURE (3 << 23) /*!< DP: three settings, uses two bits */ +#define SIP_INSECURE_NONE (0 << 23) /*!< DP: secure mode */ +#define SIP_INSECURE_PORT (1 << 23) /*!< DP: don't require matching port for incoming requests */ +#define SIP_INSECURE_INVITE (1 << 24) /*!< DP: don't require authentication for incoming INVITEs */ + +/* Sending PROGRESS in-band settings */ +#define SIP_PROG_INBAND (3 << 25) /*!< DP: three settings, uses two bits */ +#define SIP_PROG_INBAND_NEVER (0 << 25) +#define SIP_PROG_INBAND_NO (1 << 25) +#define SIP_PROG_INBAND_YES (2 << 25) + +#define SIP_SENDRPID (3 << 29) /*!< DP: Remote Party-ID Support */ +#define SIP_SENDRPID_NO (0 << 29) +#define SIP_SENDRPID_PAI (1 << 29) /*!< Use "P-Asserted-Identity" for rpid */ +#define SIP_SENDRPID_RPID (2 << 29) /*!< Use "Remote-Party-ID" for rpid */ +#define SIP_G726_NONSTANDARD (1 << 31) /*!< DP: Use non-standard packing for G726-32 data */ + +/*! \brief Flags to copy from peer/user to dialog */ +#define SIP_FLAGS_TO_COPY \ + (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_SENDRPID | SIP_DTMF | SIP_REINVITE | \ + SIP_PROG_INBAND | SIP_USECLIENTCODE | SIP_NAT_FORCE_RPORT | SIP_G726_NONSTANDARD | \ + SIP_USEREQPHONE | SIP_INSECURE) +/*@}*/ + +/*! \name SIPflags2 + a second page of flags (for flags[1] */ +/*@{*/ +/* realtime flags */ +#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */ +#define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */ +#define SIP_PAGE2_RPID_UPDATE (1 << 3) +#define SIP_PAGE2_Q850_REASON (1 << 4) /*!< DP: Get/send cause code via Reason header */ + +/* Space for addition of other realtime flags in the future */ +#define SIP_PAGE2_CONSTANT_SSRC (1 << 7) /*!< GDP: Don't change SSRC on reinvite */ +#define SIP_PAGE2_SYMMETRICRTP (1 << 8) /*!< GDP: Whether symmetric RTP is enabled or not */ +#define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ + +#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 10) +#define SIP_PAGE2_RPID_IMMEDIATE (1 << 11) +#define SIP_PAGE2_RPORT_PRESENT (1 << 12) /*!< Was rport received in the Via header? */ +#define SIP_PAGE2_PREFERRED_CODEC (1 << 13) /*!< GDP: Only respond with single most preferred joint codec */ +#define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */ +#define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */ +#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */ +#define SIP_PAGE2_ALLOWOVERLAP (1 << 17) /*!< DP: Allow overlap dialing ? */ +#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */ +#define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */ + +#define SIP_PAGE2_T38SUPPORT (3 << 20) /*!< GDP: T.38 Fax Support */ +#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T.38 Fax Support (no error correction) */ +#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T.38 Fax Support (FEC error correction) */ +#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (3 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */ + +#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */ +#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */ +#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 23) /*!< D: One directional hold */ +#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 23) /*!< D: Inactive hold */ + +#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< DP: Compensate for buggy RFC2833 implementations */ +#define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< DP: Buggy CISCO MWI fix */ +#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 27) /*!< 29: Has a dialog been established? */ +#define SIP_PAGE2_FAX_DETECT (1 << 28) /*!< DP: Fax Detection support */ +#define SIP_PAGE2_REGISTERTRYING (1 << 29) /*!< DP: Send 100 Trying on REGISTER attempts */ +#define SIP_PAGE2_UDPTL_DESTINATION (1 << 30) /*!< DP: Use source IP of RTP as destination if NAT is enabled */ +#define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 31) /*!< DP: Always set up video, even if endpoints don't support it */ + +#define SIP_PAGE2_FLAGS_TO_COPY \ + (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ + SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | \ + SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \ + SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \ + SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE | SIP_PAGE2_SYMMETRICRTP | SIP_PAGE2_CONSTANT_SSRC |\ + SIP_PAGE2_Q850_REASON) + +/*@}*/ + +/*----------------------------------------------------------*/ +/*---- ENUMS ----*/ +/*----------------------------------------------------------*/ + +/*! \brief Authorization scheme for call transfers + * + * \note Not a bitfield flag, since there are plans for other modes, + * like "only allow transfers for authenticated devices" + */ +enum transfermodes { + TRANSFER_OPENFORALL, /*!< Allow all SIP transfers */ + TRANSFER_CLOSED, /*!< Allow no SIP transfers */ +}; + +/*! \brief The result of a lot of functions */ +enum sip_result { + AST_SUCCESS = 0, /*!< FALSE means success, funny enough */ + AST_FAILURE = -1, /*!< Failure code */ +}; + +/*! \brief States for the INVITE transaction, not the dialog + * \note this is for the INVITE that sets up the dialog + */ +enum invitestates { + INV_NONE = 0, /*!< No state at all, maybe not an INVITE dialog */ + INV_CALLING = 1, /*!< Invite sent, no answer */ + INV_PROCEEDING = 2, /*!< We got/sent 1xx message */ + INV_EARLY_MEDIA = 3, /*!< We got 18x message with to-tag back */ + INV_COMPLETED = 4, /*!< Got final response with error. Wait for ACK, then CONFIRMED */ + INV_CONFIRMED = 5, /*!< Confirmed response - we've got an ack (Incoming calls only) */ + INV_TERMINATED = 6, /*!< Transaction done - either successful (AST_STATE_UP) or failed, but done + The only way out of this is a BYE from one side */ + INV_CANCELLED = 7, /*!< Transaction cancelled by client or server in non-terminated state */ +}; + +/*! \brief When sending a SIP message, we can send with a few options, depending on + * type of SIP request. UNRELIABLE is moslty used for responses to repeated requests, + * where the original response would be sent RELIABLE in an INVITE transaction + */ +enum xmittype { + XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits. + * If it fails, it's critical and will cause a teardown of the session */ + XMIT_RELIABLE = 1, /*!< Transmit SIP message reliably, with re-transmits */ + XMIT_UNRELIABLE = 0, /*!< Transmit SIP message without bothering with re-transmits */ +}; + +/*! \brief Results from the parse_register() function */ +enum parse_register_result { + PARSE_REGISTER_DENIED, + PARSE_REGISTER_FAILED, + PARSE_REGISTER_UPDATE, + PARSE_REGISTER_QUERY, +}; + +/*! \brief Type of subscription, based on the packages we do support, see \ref subscription_types */ +enum subscriptiontype { + NONE = 0, + XPIDF_XML, + DIALOG_INFO_XML, + CPIM_PIDF_XML, + PIDF_XML, + MWI_NOTIFICATION +}; + +/*! \brief The number of media types in enum \ref media_type below. */ +#define OFFERED_MEDIA_COUNT 4 + +/*! \brief Media types generate different "dummy answers" for not accepting the offer of + a media stream. We need to add definitions for each RTP profile. Secure RTP is not + the same as normal RTP and will require a new definition */ +enum media_type { + SDP_AUDIO, /*!< RTP/AVP Audio */ + SDP_VIDEO, /*!< RTP/AVP Video */ + SDP_IMAGE, /*!< Image udptl, not TCP or RTP */ + SDP_TEXT, /*!< RTP/AVP Realtime Text */ +}; + +/*! \brief Authentication types - proxy or www authentication + * \note Endpoints, like Asterisk, should always use WWW authentication to + * allow multiple authentications in the same call - to the proxy and + * to the end point. + */ +enum sip_auth_type { + PROXY_AUTH = 407, + WWW_AUTH = 401, +}; + +/*! \brief Authentication result from check_auth* functions */ +enum check_auth_result { + AUTH_DONT_KNOW = -100, /*!< no result, need to check further */ + /* XXX maybe this is the same as AUTH_NOT_FOUND */ + AUTH_SUCCESSFUL = 0, + AUTH_CHALLENGE_SENT = 1, + AUTH_SECRET_FAILED = -1, + AUTH_USERNAME_MISMATCH = -2, + AUTH_NOT_FOUND = -3, /*!< returned by register_verify */ + AUTH_FAKE_AUTH = -4, + AUTH_UNKNOWN_DOMAIN = -5, + AUTH_PEER_NOT_DYNAMIC = -6, + AUTH_ACL_FAILED = -7, + AUTH_BAD_TRANSPORT = -8, + AUTH_RTP_FAILED = 9, +}; + +/*! \brief States for outbound registrations (with register= lines in sip.conf */ +enum sipregistrystate { + REG_STATE_UNREGISTERED = 0, /*!< We are not registered + * \note Initial state. We should have a timeout scheduled for the initial + * (or next) registration transmission, calling sip_reregister + */ + + REG_STATE_REGSENT, /*!< Registration request sent + * \note sent initial request, waiting for an ack or a timeout to + * retransmit the initial request. + */ + + REG_STATE_AUTHSENT, /*!< We have tried to authenticate + * \note entered after transmit_register with auth info, + * waiting for an ack. + */ + + REG_STATE_REGISTERED, /*!< Registered and done */ + + REG_STATE_REJECTED, /*!< Registration rejected + * \note only used when the remote party has an expire larger than + * our max-expire. This is a final state from which we do not + * recover (not sure how correctly). + */ + + REG_STATE_TIMEOUT, /*!< Registration timed out + * \note XXX unused */ + + REG_STATE_NOAUTH, /*!< We have no accepted credentials + * \note fatal - no chance to proceed */ + + REG_STATE_FAILED, /*!< Registration failed after several tries + * \note fatal - no chance to proceed */ +}; + +/*! \brief Modes in which Asterisk can be configured to run SIP Session-Timers */ +enum st_mode { + SESSION_TIMER_MODE_INVALID = 0, /*!< Invalid value */ + SESSION_TIMER_MODE_ACCEPT, /*!< Honor inbound Session-Timer requests */ + SESSION_TIMER_MODE_ORIGINATE, /*!< Originate outbound and honor inbound requests */ + SESSION_TIMER_MODE_REFUSE /*!< Ignore inbound Session-Timers requests */ +}; + +/*! \brief The entity playing the refresher role for Session-Timers */ +enum st_refresher { + SESSION_TIMER_REFRESHER_AUTO, /*!< Negotiated */ + SESSION_TIMER_REFRESHER_UAC, /*!< Session is refreshed by the UAC */ + SESSION_TIMER_REFRESHER_UAS /*!< Session is refreshed by the UAS */ +}; + +/*! \brief Define some implemented SIP transports + \note Asterisk does not support SCTP or UDP/DTLS +*/ +enum sip_transport { + SIP_TRANSPORT_UDP = 1, /*!< Unreliable transport for SIP, needs retransmissions */ + SIP_TRANSPORT_TCP = 1 << 1, /*!< Reliable, but unsecure */ + SIP_TRANSPORT_TLS = 1 << 2, /*!< TCP/TLS - reliable and secure transport for signalling */ +}; + +/*! \brief States whether a SIP message can create a dialog in Asterisk. */ +enum can_create_dialog { + CAN_NOT_CREATE_DIALOG, + CAN_CREATE_DIALOG, + CAN_CREATE_DIALOG_UNSUPPORTED_METHOD, +}; + +/*! \brief SIP Request methods known by Asterisk + * + * \note Do _NOT_ make any changes to this enum, or the array following it; + * if you think you are doing the right thing, you are probably + * not doing the right thing. If you think there are changes + * needed, get someone else to review them first _before_ + * submitting a patch. If these two lists do not match properly + * bad things will happen. + */ +enum sipmethod { + SIP_UNKNOWN, /*!< Unknown response */ + SIP_RESPONSE, /*!< Not request, response to outbound request */ + SIP_REGISTER, /*!< Registration to the mothership, tell us where you are located */ + SIP_OPTIONS, /*!< Check capabilities of a device, used for "ping" too */ + SIP_NOTIFY, /*!< Status update, Part of the event package standard, result of a SUBSCRIBE or a REFER */ + SIP_INVITE, /*!< Set up a session */ + SIP_ACK, /*!< End of a three-way handshake started with INVITE. */ + SIP_PRACK, /*!< Reliable pre-call signalling. Not supported in Asterisk. */ + SIP_BYE, /*!< End of a session */ + SIP_REFER, /*!< Refer to another URI (transfer) */ + SIP_SUBSCRIBE, /*!< Subscribe for updates (voicemail, session status, device status, presence) */ + SIP_MESSAGE, /*!< Text messaging */ + SIP_UPDATE, /*!< Update a dialog. We can send UPDATE; but not accept it */ + SIP_INFO, /*!< Information updates during a session */ + SIP_CANCEL, /*!< Cancel an INVITE */ + SIP_PUBLISH, /*!< Not supported in Asterisk */ + SIP_PING, /*!< Not supported at all, no standard but still implemented out there */ +}; + +/*! \brief Settings for the 'notifycid' option, see sip.conf.sample for details. */ +enum notifycid_setting { + DISABLED = 0, + ENABLED = 1, + IGNORE_CONTEXT = 2, +}; + +/*! \brief Modes for SIP domain handling in the PBX */ +enum domain_mode { + SIP_DOMAIN_AUTO, /*!< This domain is auto-configured */ + SIP_DOMAIN_CONFIG, /*!< This domain is from configuration */ +}; + +/*! \brief debugging state + * We store separately the debugging requests from the config file + * and requests from the CLI. Debugging is enabled if either is set + * (which means that if sipdebug is set in the config file, we can + * only turn it off by reloading the config). + */ +enum sip_debug_e { + sip_debug_none = 0, + sip_debug_config = 1, + sip_debug_console = 2, +}; + +/*! \brief T38 States for a call */ +enum t38state { + T38_DISABLED = 0, /*!< Not enabled */ + T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ + T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ + T38_ENABLED /*!< Negotiated (enabled) */ +}; + +/*! \brief Parameters to know status of transfer */ +enum referstatus { + REFER_IDLE, /*!< No REFER is in progress */ + REFER_SENT, /*!< Sent REFER to transferee */ + REFER_RECEIVED, /*!< Received REFER from transferrer */ + REFER_CONFIRMED, /*!< Refer confirmed with a 100 TRYING (unused) */ + REFER_ACCEPTED, /*!< Accepted by transferee */ + REFER_RINGING, /*!< Target Ringing */ + REFER_200OK, /*!< Answered by transfer target */ + REFER_FAILED, /*!< REFER declined - go on */ + REFER_NOAUTH /*!< We had no auth for REFER */ +}; + +enum sip_peer_type { + SIP_TYPE_PEER = (1 << 0), + SIP_TYPE_USER = (1 << 1), +}; + +enum t38_action_flag { + SDP_T38_NONE = 0, /*!< Do not modify T38 information at all */ + SDP_T38_INITIATE, /*!< Remote side has requested T38 with us */ + SDP_T38_ACCEPT, /*!< Remote side accepted our T38 request */ +}; + +enum sip_tcptls_alert { + TCPTLS_ALERT_DATA, /*!< \brief There is new data to be sent out */ + TCPTLS_ALERT_STOP, /*!< \brief A request to stop the tcp_handler thread */ +}; + + +/*----------------------------------------------------------*/ +/*---- STRUCTS ----*/ +/*----------------------------------------------------------*/ + +/*! \brief definition of a sip proxy server + * + * For outbound proxies, a sip_peer will contain a reference to a + * dynamically allocated instance of a sip_proxy. A sip_pvt may also + * contain a reference to a peer's outboundproxy, or it may contain + * a reference to the sip_cfg.outboundproxy. + */ +struct sip_proxy { + char name[MAXHOSTNAMELEN]; /*!< DNS name of domain/host or IP */ + struct sockaddr_in ip; /*!< Currently used IP address and port */ + time_t last_dnsupdate; /*!< When this was resolved */ + enum sip_transport transport; + int force; /*!< If it's an outbound proxy, Force use of this outbound proxy for all outbound requests */ + /* Room for a SRV record chain based on the name */ +}; + +/*! \brief argument for the 'show channels|subscriptions' callback. */ +struct __show_chan_arg { + int fd; + int subscriptions; + int numchans; /* return value */ +}; + +/*! \name GlobalSettings + Global settings apply to the channel (often settings you can change in the general section + of sip.conf +*/ +/*@{*/ +/*! \brief a place to store all global settings for the sip channel driver + + These are settings that will be possibly to apply on a group level later on. + \note Do not add settings that only apply to the channel itself and can't + be applied to devices (trunks, services, phones) +*/ +struct sip_settings { + int peer_rtupdate; /*!< G: Update database with registration data for peer? */ + int rtsave_sysname; /*!< G: Save system name at registration? */ + int ignore_regexpire; /*!< G: Ignore expiration of peer */ + int rtautoclear; /*!< Realtime ?? */ + int directrtpsetup; /*!< Enable support for Direct RTP setup (no re-invites) */ + int pedanticsipchecking; /*!< Extra checking ? Default off */ + int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */ + int srvlookup; /*!< SRV Lookup on or off. Default is on */ + int allowguest; /*!< allow unauthenticated peers to connect? */ + int alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ + int compactheaders; /*!< send compact sip headers */ + int allow_external_domains; /*!< Accept calls to external SIP domains? */ + int callevents; /*!< Whether we send manager events or not */ + int regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */ + int matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */ + char regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */ + unsigned int disallowed_methods; /*!< methods that we should never try to use */ + int notifyringing; /*!< Send notifications on ringing */ + int notifyhold; /*!< Send notifications on hold */ + enum notifycid_setting notifycid; /*!< Send CID with ringing notifications */ + enum transfermodes allowtransfer; /*!< SIP Refer restriction scheme */ + int allowsubscribe; /*!< Flag for disabling ALL subscriptions, this is FALSE only if all peers are FALSE + the global setting is in globals_flags[1] */ + char realm[MAXHOSTNAMELEN]; /*!< Default realm */ + int domainsasrealm; /*!< Use domains lists as realms */ + struct sip_proxy outboundproxy; /*!< Outbound proxy */ + char default_context[AST_MAX_CONTEXT]; + char default_subscribecontext[AST_MAX_CONTEXT]; + struct ast_ha *contact_ha; /*! \brief Global list of addresses dynamic peers are not allowed to use */ + format_t capability; /*!< Supported codecs */ +}; + +/*! \brief The SIP socket definition */ +struct sip_socket { + enum sip_transport type; /*!< UDP, TCP or TLS */ + int fd; /*!< Filed descriptor, the actual socket */ + uint16_t port; + struct ast_tcptls_session_instance *tcptls_session; /* If tcp or tls, a socket manager */ +}; + +/*! \brief sip_request: The data grabbed from the UDP socket + * + * \verbatim + * Incoming messages: we first store the data from the socket in data[], + * adding a trailing \0 to make string parsing routines happy. + * Then call parse_request() and req.method = find_sip_method(); + * to initialize the other fields. The \r\n at the end of each line is + * replaced by \0, so that data[] is not a conforming SIP message anymore. + * After this processing, rlPart1 is set to non-NULL to remember + * that we can run get_header() on this kind of packet. + * + * parse_request() splits the first line as follows: + * Requests have in the first line method uri SIP/2.0 + * rlPart1 = method; rlPart2 = uri; + * Responses have in the first line SIP/2.0 NNN description + * rlPart1 = SIP/2.0; rlPart2 = NNN + description; + * + * For outgoing packets, we initialize the fields with init_req() or init_resp() + * (which fills the first line to "METHOD uri SIP/2.0" or "SIP/2.0 code text"), + * and then fill the rest with add_header() and add_line(). + * The \r\n at the end of the line are still there, so the get_header() + * and similar functions don't work on these packets. + * \endverbatim + */ +struct sip_request { + ptrdiff_t rlPart1; /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */ + ptrdiff_t rlPart2; /*!< Offset of the Request URI or Response Status */ + int len; /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */ + int headers; /*!< # of SIP Headers */ + int method; /*!< Method of this request */ + int lines; /*!< Body Content */ + unsigned int sdp_start; /*!< the line number where the SDP begins */ + unsigned int sdp_count; /*!< the number of lines of SDP */ + char debug; /*!< print extra debugging if non zero */ + char has_to_tag; /*!< non-zero if packet has To: tag */ + char ignore; /*!< if non-zero This is a re-transmit, ignore it */ + ptrdiff_t header[SIP_MAX_HEADERS]; /*!< Array of offsets into the request string of each SIP header*/ + ptrdiff_t line[SIP_MAX_LINES]; /*!< Array of offsets into the request string of each SDP line*/ + struct ast_str *data; + /* XXX Do we need to unref socket.ser when the request goes away? */ + struct sip_socket socket; /*!< The socket used for this request */ + AST_LIST_ENTRY(sip_request) next; +}; + +/* \brief given a sip_request and an offset, return the char * that resides there + * + * It used to be that rlPart1, rlPart2, and the header and line arrays were character + * pointers. They are now offsets into the ast_str portion of the sip_request structure. + * To avoid adding a bunch of redundant pointer arithmetic to the code, this macro is + * provided to retrieve the string at a particular offset within the request's buffer + */ +#define REQ_OFFSET_TO_STR(req,offset) (ast_str_buffer((req)->data) + ((req)->offset)) + +/*! \brief structure used in transfers */ +struct sip_dual { + struct ast_channel *chan1; /*!< First channel involved */ + struct ast_channel *chan2; /*!< Second channel involved */ + struct sip_request req; /*!< Request that caused the transfer (REFER) */ + int seqno; /*!< Sequence number */ +}; + +/*! \brief Parameters to the transmit_invite function */ +struct sip_invite_param { + int addsipheaders; /*!< Add extra SIP headers */ + const char *uri_options; /*!< URI options to add to the URI */ + const char *vxml_url; /*!< VXML url for Cisco phones */ + char *auth; /*!< Authentication */ + char *authheader; /*!< Auth header */ + enum sip_auth_type auth_type; /*!< Authentication type */ + const char *replaces; /*!< Replaces header for call transfers */ + int transfer; /*!< Flag - is this Invite part of a SIP transfer? (invite/replaces) */ +}; + +/*! \brief Structure to save routing information for a SIP session */ +struct sip_route { + struct sip_route *next; + char hop[0]; +}; + +/*! \brief Domain data structure. + \note In the future, we will connect this to a configuration tree specific + for this domain +*/ +struct domain { + char domain[MAXHOSTNAMELEN]; /*!< SIP domain we are responsible for */ + char context[AST_MAX_EXTENSION]; /*!< Incoming context for this domain */ + enum domain_mode mode; /*!< How did we find this domain? */ + AST_LIST_ENTRY(domain) list; /*!< List mechanics */ +}; + +/*! \brief sip_history: Structure for saving transactions within a SIP dialog */ +struct sip_history { + AST_LIST_ENTRY(sip_history) list; + char event[0]; /* actually more, depending on needs */ +}; + +/*! \brief sip_auth: Credentials for authentication to other SIP services */ +struct sip_auth { + char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */ + char username[256]; /*!< Username */ + char secret[256]; /*!< Secret */ + char md5secret[256]; /*!< MD5Secret */ + struct sip_auth *next; /*!< Next auth structure in list */ +}; + +/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */ +struct t38properties { + enum t38state state; /*!< T.38 state */ + struct ast_control_t38_parameters our_parms; + struct ast_control_t38_parameters their_parms; +}; + +/*! \brief generic struct to map between strings and integers. + * Fill it with x-s pairs, terminate with an entry with s = NULL; + * Then you can call map_x_s(...) to map an integer to a string, + * and map_s_x() for the string -> integer mapping. + */ +struct _map_x_s { + int x; + const char *s; +}; + +/*! \brief Structure to handle SIP transfers. Dynamically allocated when needed + \note OEJ: Should be moved to string fields */ +struct sip_refer { + char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */ + char refer_to_domain[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO domain */ + char refer_to_urioption[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO uri options */ + char refer_to_context[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO context */ + char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ + char referred_by_name[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ + char refer_contact[AST_MAX_EXTENSION]; /*!< Place to store Contact info from a REFER extension */ + char replaces_callid[SIPBUFSIZE]; /*!< Replace info: callid */ + char replaces_callid_totag[SIPBUFSIZE/2]; /*!< Replace info: to-tag */ + char replaces_callid_fromtag[SIPBUFSIZE/2]; /*!< Replace info: from-tag */ + struct sip_pvt *refer_call; /*!< Call we are referring. This is just a reference to a + * dialog owned by someone else, so we should not destroy + * it when the sip_refer object goes. + */ + int attendedtransfer; /*!< Attended or blind transfer? */ + int localtransfer; /*!< Transfer to local domain? */ + enum referstatus status; /*!< REFER status */ +}; + +/*! \brief Struct to handle custom SIP notify requests. Dynamically allocated when needed */ +struct sip_notify { + struct ast_variable *headers; + struct ast_str *content; +}; + +/*! \brief Structure that encapsulates all attributes related to running + * SIP Session-Timers feature on a per dialog basis. + */ +struct sip_st_dlg { + int st_active; /*!< Session-Timers on/off */ + int st_interval; /*!< Session-Timers negotiated session refresh interval */ + int st_schedid; /*!< Session-Timers ast_sched scheduler id */ + enum st_refresher st_ref; /*!< Session-Timers session refresher */ + int st_expirys; /*!< Session-Timers number of expirys */ + int st_active_peer_ua; /*!< Session-Timers on/off in peer UA */ + int st_cached_min_se; /*!< Session-Timers cached Min-SE */ + int st_cached_max_se; /*!< Session-Timers cached Session-Expires */ + enum st_mode st_cached_mode; /*!< Session-Timers cached M.O. */ + enum st_refresher st_cached_ref; /*!< Session-Timers cached refresher */ + unsigned char quit_flag:1; /*!< Stop trying to lock; just quit */ +}; + + +/*! \brief Structure that encapsulates all attributes related to configuration + * of SIP Session-Timers feature on a per user/peer basis. + */ +struct sip_st_cfg { + enum st_mode st_mode_oper; /*!< Mode of operation for Session-Timers */ + enum st_refresher st_ref; /*!< Session-Timer refresher */ + int st_min_se; /*!< Lowest threshold for session refresh interval */ + int st_max_se; /*!< Highest threshold for session refresh interval */ +}; + +/*! \brief Structure for remembering offered media in an INVITE, to make sure we reply + to all media streams. In theory. In practise, we try our best. */ +struct offered_media { + int offered; + char codecs[128]; +}; + +/*! \brief Structure used for each SIP dialog, ie. a call, a registration, a subscribe. + * Created and initialized by sip_alloc(), the descriptor goes into the list of + * descriptors (dialoglist). + */ +struct sip_pvt { + struct sip_pvt *next; /*!< Next dialog in chain */ + enum invitestates invitestate; /*!< Track state of SIP_INVITEs */ + int method; /*!< SIP method that opened this dialog */ + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(callid); /*!< Global CallID */ + AST_STRING_FIELD(randdata); /*!< Random data */ + AST_STRING_FIELD(accountcode); /*!< Account code */ + AST_STRING_FIELD(realm); /*!< Authorization realm */ + AST_STRING_FIELD(nonce); /*!< Authorization nonce */ + AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ + AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ + AST_STRING_FIELD(domain); /*!< Authorization domain */ + AST_STRING_FIELD(from); /*!< The From: header */ + AST_STRING_FIELD(useragent); /*!< User agent in SIP request */ + AST_STRING_FIELD(exten); /*!< Extension where to start */ + AST_STRING_FIELD(context); /*!< Context for this call */ + AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */ + AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */ + AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */ + AST_STRING_FIELD(fromuser); /*!< User to show in the user field */ + AST_STRING_FIELD(fromname); /*!< Name to show in the user field */ + AST_STRING_FIELD(tohost); /*!< Host we should put in the "to" field */ + AST_STRING_FIELD(todnid); /*!< DNID of this call (overrides host) */ + AST_STRING_FIELD(language); /*!< Default language for this call */ + AST_STRING_FIELD(mohinterpret); /*!< MOH class to use when put on hold */ + AST_STRING_FIELD(mohsuggest); /*!< MOH class to suggest when putting a peer on hold */ + AST_STRING_FIELD(rdnis); /*!< Referring DNIS */ + AST_STRING_FIELD(redircause); /*!< Referring cause */ + AST_STRING_FIELD(theirtag); /*!< Their tag */ + AST_STRING_FIELD(username); /*!< [user] name */ + AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */ + AST_STRING_FIELD(authname); /*!< Who we use for authentication */ + AST_STRING_FIELD(uri); /*!< Original requested URI */ + AST_STRING_FIELD(okcontacturi); /*!< URI from the 200 OK on INVITE */ + AST_STRING_FIELD(peersecret); /*!< Password */ + AST_STRING_FIELD(peermd5secret); + AST_STRING_FIELD(cid_num); /*!< Caller*ID number */ + AST_STRING_FIELD(cid_name); /*!< Caller*ID name */ + AST_STRING_FIELD(mwi_from); /*!< Name to place in the From header in outgoing NOTIFY requests */ + AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ + /* we only store the part in <brackets> in this field. */ + AST_STRING_FIELD(our_contact); /*!< Our contact header */ + AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ + AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ + AST_STRING_FIELD(engine); /*!< RTP engine to use */ + ); + char via[128]; /*!< Via: header */ + struct sip_socket socket; /*!< The socket used for this dialog */ + unsigned int ocseq; /*!< Current outgoing seqno */ + unsigned int icseq; /*!< Current incoming seqno */ + ast_group_t callgroup; /*!< Call group */ + ast_group_t pickupgroup; /*!< Pickup group */ + int lastinvite; /*!< Last Cseq of invite */ + struct ast_flags flags[2]; /*!< SIP_ flags */ + + /* boolean flags that don't belong in flags */ + unsigned short do_history:1; /*!< Set if we want to record history */ + unsigned short alreadygone:1; /*!< already destroyed by our peer */ + unsigned short needdestroy:1; /*!< need to be destroyed by the monitor thread */ + unsigned short outgoing_call:1; /*!< this is an outgoing call */ + unsigned short answered_elsewhere:1; /*!< This call is cancelled due to answer on another channel */ + unsigned short novideo:1; /*!< Didn't get video in invite, don't offer */ + unsigned short notext:1; /*!< Text not supported (?) */ + unsigned short session_modify:1; /*!< Session modification request true/false */ + unsigned short route_persistent:1; /*!< Is this the "real" route? */ + unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) + * or respect the other endpoint's request for frame sizes (on) + * for incoming calls + */ + char tag[11]; /*!< Our tag for this session */ + int timer_t1; /*!< SIP timer T1, ms rtt */ + int timer_b; /*!< SIP timer B, ms */ + unsigned int sipoptions; /*!< Supported SIP options on the other end */ + unsigned int reqsipoptions; /*!< Required SIP options on the other end */ + struct ast_codec_pref prefs; /*!< codec prefs */ + format_t capability; /*!< Special capability (codec) */ + format_t jointcapability; /*!< Supported capability at both ends (codecs) */ + format_t peercapability; /*!< Supported peer capability */ + format_t prefcodec; /*!< Preferred codec (outbound only) */ + int noncodeccapability; /*!< DTMF RFC2833 telephony-event */ + int jointnoncodeccapability; /*!< Joint Non codec capability */ + format_t redircodecs; /*!< Redirect codecs */ + int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ + int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ + int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ + int provisional_keepalive_sched_id; /*!< Scheduler ID for provisional responses that need to be sent out to avoid cancellation */ + const char *last_provisional; /*!< The last successfully transmitted provisonal response message */ + int authtries; /*!< Times we've tried to authenticate */ + struct sip_proxy *outboundproxy; /*!< Outbound proxy for this dialog. Use ref_proxy to set this instead of setting it directly*/ + struct t38properties t38; /*!< T38 settings */ + struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */ + struct ast_udptl *udptl; /*!< T.38 UDPTL session */ + int callingpres; /*!< Calling presentation */ + int expiry; /*!< How long we take to expire */ + int sessionversion; /*!< SDP Session Version */ + int sessionid; /*!< SDP Session ID */ + long branch; /*!< The branch identifier of this session */ + long invite_branch; /*!< The branch used when we sent the initial INVITE */ + int64_t sessionversion_remote; /*!< Remote UA's SDP Session Version */ + unsigned int portinuri:1; /*!< Non zero if a port has been specified, will also disable srv lookups */ + struct sockaddr_in sa; /*!< Our peer */ + struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */ + struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */ + struct sockaddr_in tredirip; /*!< Where our Text RTP should be going if not to us */ + time_t lastrtprx; /*!< Last RTP received */ + time_t lastrtptx; /*!< Last RTP sent */ + int rtptimeout; /*!< RTP timeout time */ + struct sockaddr_in recv; /*!< Received as */ + struct sockaddr_in ourip; /*!< Our IP (as seen from the outside) */ + enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ + struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ + struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ + struct sip_notify *notify; /*!< Custom notify type */ + struct sip_auth *peerauth; /*!< Realm authentication */ + int noncecount; /*!< Nonce-count */ + unsigned int stalenonce:1; /*!< Marks the current nonce as responded too */ + char lastmsg[256]; /*!< Last Message sent/received */ + int amaflags; /*!< AMA Flags */ + int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */ + int glareinvite; /*!< A invite received while a pending invite is already present is stored here. Its seqno is the + value. Since this glare invite's seqno is not the same as the pending invite's, it must be + held in order to properly process acknowledgements for our 491 response. */ + struct sip_request initreq; /*!< Latest request that opened a new transaction + within this dialog. + NOT the request that opened the dialog */ + + int initid; /*!< Auto-congest ID if appropriate (scheduler) */ + int waitid; /*!< Wait ID for scheduler after 491 or other delays */ + int autokillid; /*!< Auto-kill ID (scheduler) */ + int t38id; /*!< T.38 Response ID */ + struct sip_refer *refer; /*!< REFER: SIP transfer data structure */ + enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */ + int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ + int laststate; /*!< SUBSCRIBE: Last known extension state */ + int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ + + struct ast_dsp *dsp; /*!< Inband DTMF or Fax CNG tone Detection dsp */ + + struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one + Used in peerpoke, mwi subscriptions */ + struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ + struct ast_rtp_instance *rtp; /*!< RTP Session */ + struct ast_rtp_instance *vrtp; /*!< Video RTP session */ + struct ast_rtp_instance *trtp; /*!< Text RTP session */ + struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ + struct sip_history_head *history; /*!< History of this SIP dialog */ + size_t history_entries; /*!< Number of entires in the history */ + struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ + AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ + struct sip_invite_param *options; /*!< Options for INVITE */ + struct sip_st_dlg *stimer; /*!< SIP Session-Timers */ + + int red; /*!< T.140 RTP Redundancy */ + int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */ + + struct sip_subscription_mwi *mwi; /*!< If this is a subscription MWI dialog, to which subscription */ + /*! The SIP methods supported by this peer. We get this information from the Allow header of the first + * message we receive from an endpoint during a dialog. + */ + unsigned int allowed_methods; + /*! Some peers are not trustworthy with their Allow headers, and so we need to override their wicked + * ways through configuration. This is a copy of the peer's disallowed_methods, so that we can apply them + * to the sip_pvt at various stages of dialog establishment + */ + unsigned int disallowed_methods; + /*! When receiving an SDP offer, it is important to take note of what media types were offered. + * By doing this, even if we don't want to answer a particular media stream with something meaningful, we can + * still put an m= line in our answer with the port set to 0. + * + * The reason for the length being 4 (OFFERED_MEDIA_COUNT) is that in this branch of Asterisk, the only media types supported are + * image, audio, text, and video. Therefore we need to keep track of which types of media were offered. + * Note that secure RTP defines new types of SDP media. + * + * If we wanted to be 100% correct, we would keep a list of all media streams offered. That way we could respond + * even to unknown media types, and we could respond to multiple streams of the same type. Such large-scale changes + * are not a good idea for released branches, though, so we're compromising by just making sure that for the common cases: + * audio and video, audio and T.38, and audio and text, we give the appropriate response to both media streams. + * + * The large-scale changes would be a good idea for implementing during an SDP rewrite. + */ + struct offered_media offered_media[OFFERED_MEDIA_COUNT]; +}; + +/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission + * Packets are linked in a list, whose head is in the struct sip_pvt they belong to. + * Each packet holds a reference to the parent struct sip_pvt. + * This structure is allocated in __sip_reliable_xmit() and only for packets that + * require retransmissions. + */ +struct sip_pkt { + struct sip_pkt *next; /*!< Next packet in linked list */ + int retrans; /*!< Retransmission number */ + int method; /*!< SIP method for this packet */ + int seqno; /*!< Sequence number */ + char is_resp; /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */ + char is_fatal; /*!< non-zero if there is a fatal error */ + int response_code; /*!< If this is a response, the response code */ + struct sip_pvt *owner; /*!< Owner AST call */ + int retransid; /*!< Retransmission ID */ + int timer_a; /*!< SIP timer A, retransmission timer */ + int timer_t1; /*!< SIP Timer T1, estimated RTT or 500 ms */ + int packetlen; /*!< Length of packet */ + struct ast_str *data; +}; + +/*! + * \brief A peer's mailbox + * + * We could use STRINGFIELDS here, but for only two strings, it seems like + * too much effort ... + */ +struct sip_mailbox { + char *mailbox; + char *context; + /*! Associated MWI subscription */ + struct ast_event_sub *event_sub; + AST_LIST_ENTRY(sip_mailbox) entry; +}; + +/*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) +*/ +/* XXX field 'name' must be first otherwise sip_addrcmp() will fail, as will astobj2 hashing of the structure */ +struct sip_peer { + char name[80]; /*!< the unique name of this object */ + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(secret); /*!< Password for inbound auth */ + AST_STRING_FIELD(md5secret); /*!< Password in MD5 */ + AST_STRING_FIELD(remotesecret); /*!< Remote secret (trunks, remote devices) */ + AST_STRING_FIELD(context); /*!< Default context for incoming calls */ + AST_STRING_FIELD(subscribecontext); /*!< Default context for subscriptions */ + AST_STRING_FIELD(username); /*!< Temporary username until registration */ + AST_STRING_FIELD(accountcode); /*!< Account code */ + AST_STRING_FIELD(tohost); /*!< If not dynamic, IP address */ + AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */ + AST_STRING_FIELD(fromuser); /*!< From: user when calling this peer */ + AST_STRING_FIELD(fromdomain); /*!< From: domain when calling this peer */ + AST_STRING_FIELD(fullcontact); /*!< Contact registered with us (not in sip.conf) */ + AST_STRING_FIELD(cid_num); /*!< Caller ID num */ + AST_STRING_FIELD(cid_name); /*!< Caller ID name */ + AST_STRING_FIELD(vmexten); /*!< Dialplan extension for MWI notify message*/ + AST_STRING_FIELD(language); /*!< Default language for prompts */ + AST_STRING_FIELD(mohinterpret); /*!< Music on Hold class */ + AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ + AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ + AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ + AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ + AST_STRING_FIELD(engine); /*!< RTP Engine to use */ + AST_STRING_FIELD(unsolicited_mailbox); /*!< Mailbox to store received unsolicited MWI NOTIFY messages information in */ + ); + struct sip_socket socket; /*!< Socket used for this peer */ + enum sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport. + If register expires, default should be reset. to this value */ + /* things that don't belong in flags */ + unsigned short transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ + unsigned short is_realtime:1; /*!< this is a 'realtime' peer */ + unsigned short rt_fromcontact:1;/*!< copy fromcontact from realtime */ + unsigned short host_dynamic:1; /*!< Dynamic Peers register with Asterisk */ + unsigned short selfdestruct:1; /*!< Automatic peers need to destruct themselves */ + unsigned short the_mark:1; /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */ + unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) + * or respect the other endpoint's request for frame sizes (on) + * for incoming calls + */ + unsigned short deprecated_username:1; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */ + struct sip_auth *auth; /*!< Realm authentication list */ + int amaflags; /*!< AMA Flags (for billing) */ + int callingpres; /*!< Calling id presentation */ + int inUse; /*!< Number of calls in use */ + int inRinging; /*!< Number of calls ringing */ + int onHold; /*!< Peer has someone on hold */ + int call_limit; /*!< Limit of concurrent calls */ + int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ + int busy_level; /*!< Level of active channels where we signal busy */ + enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ + struct ast_codec_pref prefs; /*!< codec prefs */ + int lastmsgssent; + unsigned int sipoptions; /*!< Supported SIP options */ + struct ast_flags flags[2]; /*!< SIP_ flags */ + + /*! Mailboxes that this peer cares about */ + AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes; + + int maxcallbitrate; /*!< Maximum Bitrate for a video call */ + int expire; /*!< When to expire this peer registration */ + format_t capability; /*!< Codec capability */ + int rtptimeout; /*!< RTP timeout */ + int rtpholdtimeout; /*!< RTP Hold Timeout */ + int rtpkeepalive; /*!< Send RTP packets for keepalive */ + ast_group_t callgroup; /*!< Call group */ + ast_group_t pickupgroup; /*!< Pickup group */ + struct sip_proxy *outboundproxy;/*!< Outbound proxy for this peer */ + struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ + struct sockaddr_in addr; /*!< IP address of peer */ + unsigned int portinuri:1; /*!< Whether the port should be included in the URI */ + struct sip_pvt *call; /*!< Call pointer */ + int pokeexpire; /*!< Qualification: When to expire poke (qualify= checking) */ + int lastms; /*!< Qualification: How long last response took (in ms), or -1 for no response */ + int maxms; /*!< Qualification: Max ms we will accept for the host to be up, 0 to not monitor */ + int qualifyfreq; /*!< Qualification: Qualification: How often to check for the host to be up */ + struct timeval ps; /*!< Qualification: Time for sending SIP OPTION in sip_pke_peer() */ + struct sockaddr_in defaddr; /*!< Default IP address, used until registration */ + struct ast_ha *ha; /*!< Access control list */ + struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */ + struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ + struct sip_pvt *mwipvt; /*!< Subscription for MWI */ + struct sip_st_cfg stimer; /*!< SIP Session-Timers */ + int timer_t1; /*!< The maximum T1 value for the peer */ + int timer_b; /*!< The maximum timer B (transaction timeouts) */ + + /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */ + enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */ + unsigned int disallowed_methods; +}; + +/*! + * \brief Registrations with other SIP proxies + * + * Created by sip_register(), the entry is linked in the 'regl' list, + * and never deleted (other than at 'sip reload' or module unload times). + * The entry always has a pending timeout, either waiting for an ACK to + * the REGISTER message (in which case we have to retransmit the request), + * or waiting for the next REGISTER message to be sent (either the initial one, + * or once the previously completed registration one expires). + * The registration can be in one of many states, though at the moment + * the handling is a bit mixed. + */ +struct sip_registry { + ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1); + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(callid); /*!< Global Call-ID */ + AST_STRING_FIELD(realm); /*!< Authorization realm */ + AST_STRING_FIELD(nonce); /*!< Authorization nonce */ + AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ + AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ + AST_STRING_FIELD(domain); /*!< Authorization domain */ + AST_STRING_FIELD(username); /*!< Who we are registering as */ + AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */ + AST_STRING_FIELD(hostname); /*!< Domain or host we register to */ + AST_STRING_FIELD(secret); /*!< Password in clear text */ + AST_STRING_FIELD(md5secret);/*!< Password in md5 */ + AST_STRING_FIELD(callback); /*!< Contact extension */ + AST_STRING_FIELD(peername); /*!< Peer registering to */ + ); + enum sip_transport transport; /*!< Transport for this registration UDP, TCP or TLS */ + int portno; /*!< Optional port override */ + int expire; /*!< Sched ID of expiration */ + int configured_expiry; /*!< Configured value to use for the Expires header */ + int expiry; /*!< Negotiated value used for the Expires header */ + int regattempts; /*!< Number of attempts (since the last success) */ + int timeout; /*!< sched id of sip_reg_timeout */ + int refresh; /*!< How often to refresh */ + struct sip_pvt *call; /*!< create a sip_pvt structure for each outbound "registration dialog" in progress */ + enum sipregistrystate regstate; /*!< Registration state (see above) */ + struct timeval regtime; /*!< Last successful registration time */ + int callid_valid; /*!< 0 means we haven't chosen callid for this registry yet. */ + unsigned int ocseq; /*!< Sequence number we got to for REGISTERs for this registry */ + struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for register */ + struct sockaddr_in us; /*!< Who the server thinks we are */ + int noncecount; /*!< Nonce-count */ + char lastmsg[256]; /*!< Last Message sent/received */ +}; + +struct tcptls_packet { + AST_LIST_ENTRY(tcptls_packet) entry; + struct ast_str *data; + size_t len; +}; +/*! \brief Definition of a thread that handles a socket */ +struct sip_threadinfo { + int stop; + int alert_pipe[2]; /*! Used to alert tcptls thread when packet is ready to be written */ + pthread_t threadid; + struct ast_tcptls_session_instance *tcptls_session; + enum sip_transport type; /*!< We keep a copy of the type here so we can display it in the connection list */ + AST_LIST_HEAD_NOLOCK(, tcptls_packet) packet_q; +}; + +/*! \brief Definition of an MWI subscription to another server */ +struct sip_subscription_mwi { + ASTOBJ_COMPONENTS_FULL(struct sip_subscription_mwi,1,1); + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(username); /*!< Who we are sending the subscription as */ + AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */ + AST_STRING_FIELD(hostname); /*!< Domain or host we subscribe to */ + AST_STRING_FIELD(secret); /*!< Password in clear text */ + AST_STRING_FIELD(mailbox); /*!< Mailbox store to put MWI into */ + ); + enum sip_transport transport; /*!< Transport to use */ + int portno; /*!< Optional port override */ + int resub; /*!< Sched ID of resubscription */ + unsigned int subscribed:1; /*!< Whether we are currently subscribed or not */ + struct sip_pvt *call; /*!< Outbound subscription dialog */ + struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for subscription */ + struct sockaddr_in us; /*!< Who the server thinks we are */ +}; +#endif diff --git a/channels/sip/include/sip_utils.h b/channels/sip/include/sip_utils.h new file mode 100644 index 000000000..3a91564d1 --- /dev/null +++ b/channels/sip/include/sip_utils.h @@ -0,0 +1,34 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief sip utils header file + */ + +#ifndef _SIP_UTILS_H +#define _SIP_UTILS_H + + +/*! \brief converts ascii port to int representation. If no + * pt buffer is provided or the pt has errors when being converted + * to an int value, the port provided as the standard is used. + * + * \retval positive numeric port + */ +unsigned int port_str2int(const char *pt, unsigned int standard); + +#endif diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c new file mode 100644 index 000000000..6fec362cc --- /dev/null +++ b/channels/sip/reqresp_parser.c @@ -0,0 +1,398 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * 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. + */ + +/*! + * \file + * \brief sip request parsing functions and unit tests + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "include/sip.h" +#include "include/reqresp_parser.h" + +/*! \brief * parses a URI in its components.*/ +int parse_uri(char *uri, const char *scheme, char **ret_name, char **pass, char **domain, char **port, char **transport) +{ + char *name = NULL; + char *tmp; /* used as temporary place holder */ + int error = 0; + + /* check for valid input */ + if (ast_strlen_zero(uri)) { + return -1; + } + + /* strip [?headers] from end of uri */ + if ((tmp = strrchr(uri, '?'))) { + *tmp = '\0'; + } + + /* init field as required */ + if (pass) + *pass = ""; + if (port) + *port = ""; + if (scheme) { + int l; + char *scheme2 = ast_strdupa(scheme); + char *cur = strsep(&scheme2, ","); + for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) { + l = strlen(cur); + if (!strncasecmp(uri, cur, l)) { + uri += l; + break; + } + } + if (ast_strlen_zero(cur)) { + ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme); + error = -1; + } + } + if (transport) { + char *t, *type = ""; + *transport = ""; + if ((t = strstr(uri, "transport="))) { + strsep(&t, "="); + if ((type = strsep(&t, ";"))) { + *transport = type; + } + } + } + + if (!domain) { + /* if we don't want to split around domain, keep everything as a name, + * so we need to do nothing here, except remember why. + */ + } else { + /* store the result in a temp. variable to avoid it being + * overwritten if arguments point to the same place. + */ + char *c, *dom = ""; + + if ((c = strchr(uri, '@')) == NULL) { + /* domain-only URI, according to the SIP RFC. */ + dom = uri; + name = ""; + } else { + *c++ = '\0'; + dom = c; + name = uri; + } + + /* Remove parameters in domain and name */ + dom = strsep(&dom, ";"); + name = strsep(&name, ";"); + + if (port && (c = strchr(dom, ':'))) { /* Remove :port */ + *c++ = '\0'; + *port = c; + } + if (pass && (c = strchr(name, ':'))) { /* user:password */ + *c++ = '\0'; + *pass = c; + } + *domain = dom; + } + if (ret_name) /* same as for domain, store the result only at the end */ + *ret_name = name; + + return error; +} + +AST_TEST_DEFINE(sip_parse_uri_test) +{ + int res = AST_TEST_PASS; + char *name, *pass, *domain, *port, *transport; + char uri1[] = "sip:name@host"; + char uri2[] = "sip:name@host;transport=tcp"; + char uri3[] = "sip:name:secret@host;transport=tcp"; + char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah"; + switch (cmd) { + case TEST_INIT: + info->name = "sip_uri_parse_test"; + info->category = "channels/chan_sip/"; + info->summary = "tests sip uri parsing"; + info->description = + " Tests parsing of various URIs" + " Verifies output matches expected behavior."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* Test 1, simple URI */ + name = pass = domain = port = transport = NULL; + if (parse_uri(uri1, "sip:,sips:", &name, &pass, &domain, &port, &transport) || + strcmp(name, "name") || + !ast_strlen_zero(pass) || + strcmp(domain, "host") || + !ast_strlen_zero(port) || + !ast_strlen_zero(transport)) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 1: simple uri failed. \n"); + res = AST_TEST_FAIL; + } + + /* Test 2, add tcp transport */ + name = pass = domain = port = transport = NULL; + if (parse_uri(uri2, "sip:,sips:", &name, &pass, &domain, &port, &transport) || + strcmp(name, "name") || + !ast_strlen_zero(pass) || + strcmp(domain, "host") || + !ast_strlen_zero(port) || + strcmp(transport, "tcp")) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 2: uri with addtion of tcp transport failed. \n"); + res = AST_TEST_FAIL; + } + + /* Test 3, add secret */ + name = pass = domain = port = transport = NULL; + if (parse_uri(uri3, "sip:,sips:", &name, &pass, &domain, &port, &transport) || + strcmp(name, "name") || + strcmp(pass, "secret") || + strcmp(domain, "host") || + !ast_strlen_zero(port) || + strcmp(transport, "tcp")) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 3: uri with addition of secret failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 4, add port and unparsed header field*/ + name = pass = domain = port = transport = NULL; + if (parse_uri(uri4, "sip:,sips:", &name, &pass, &domain, &port, &transport) || + strcmp(name, "name") || + strcmp(pass, "secret") || + strcmp(domain, "host") || + strcmp(port, "port") || + strcmp(transport, "tcp")) { + + ast_str_append(&args->ast_test_error_str, 0, "Test 4: add port and unparsed header field failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 5, verify parse_uri does not crash when given a NULL uri */ + name = pass = domain = port = transport = NULL; + if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &domain, &port, &transport)) { + ast_str_append(&args->ast_test_error_str, 0, "Test 5: passing a NULL uri failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 6, verify parse_uri does not crash when given a NULL output parameters */ + name = pass = domain = port = transport = NULL; + if (parse_uri(uri4, "sip:,sips:", NULL, NULL, NULL, NULL, NULL)) { + ast_str_append(&args->ast_test_error_str, 0, "Test 6: passing NULL output parameters failed.\n"); + res = AST_TEST_FAIL; + } + + return res; +} + +/*! \brief Get caller id name from SIP headers, copy into output buffer + * + * \retval input string pointer placed after display-name field if possible + */ +const char *get_calleridname(const char *input, char *output, size_t outputsize) +{ + /* From RFC3261: + * + * From = ( "From" / "f" ) HCOLON from-spec + * from-spec = ( name-addr / addr-spec ) *( SEMI from-param ) + * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT + * display-name = *(token LWS)/ quoted-string + * token = 1*(alphanum / "-" / "." / "!" / "%" / "*" + * / "_" / "+" / "`" / "'" / "~" ) + * quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE + * qdtext = LWS / %x21 / %x23-5B / %x5D-7E + * / UTF8-NONASCII + * quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) + * + * HCOLON = *WSP ":" SWS + * SWS = [LWS] + * LWS = *[*WSP CRLF] 1*WSP + * WSP = (SP / HTAB) + * + * Deviations from it: + * - following CRLF's in LWS is not done (here at least) + * - ascii NUL is never legal as it terminates the C-string + * - utf8-nonascii is not checked for validity + */ + char *orig_output = output; + const char *orig_input = input; + + /* clear any empty characters in the beginning */ + input = ast_skip_blanks(input); + + /* no data at all or no storage room? */ + if (!input || *input == '<' || !outputsize || !output) { + return orig_input; + } + + /* make sure the output buffer is initilized */ + *orig_output = '\0'; + + /* make room for '\0' at the end of the output buffer */ + outputsize--; + + /* quoted-string rules */ + if (input[0] == '"') { + input++; /* skip the first " */ + + for (;((outputsize > 0) && *input); input++) { + if (*input == '"') { /* end of quoted-string */ + break; + } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */ + input++; + if (!*input || (unsigned char)*input > 0x7f || *input == 0xa || *input == 0xd) { + continue; /* not a valid quoted-pair, so skip it */ + } + } else if (((*input != 0x9) && ((unsigned char) *input < 0x20)) || + (*input == 0x7f)) { + continue; /* skip this invalid character. */ + } + + *output++ = *input; + outputsize--; + } + + /* if this is successful, input should be at the ending quote */ + if (!input || *input != '"') { + ast_log(LOG_WARNING, "No ending quote for display-name was found\n"); + *orig_output = '\0'; + return orig_input; + } + + /* make sure input is past the last quote */ + input++; + + /* terminate outbuf */ + *output = '\0'; + } else { /* either an addr-spec or tokenLWS-combo */ + for (;((outputsize > 0) && *input); input++) { + /* token or WSP (without LWS) */ + if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z') + || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.' + || *input == '!' || *input == '%' || *input == '*' || *input == '_' + || *input == '+' || *input == '`' || *input == '\'' || *input == '~' + || *input == 0x9 || *input == ' ') { + *output++ = *input; + outputsize -= 1; + } else if (*input == '<') { /* end of tokenLWS-combo */ + /* we could assert that the previous char is LWS, but we don't care */ + break; + } else if (*input == ':') { + /* This invalid character which indicates this is addr-spec rather than display-name. */ + *orig_output = '\0'; + return orig_input; + } else { /* else, invalid character we can skip. */ + continue; /* skip this character */ + } + } + + /* set NULL while trimming trailing whitespace */ + do { + *output-- = '\0'; + } while (*output == 0x9 || *output == ' '); /* we won't go past orig_output as first was a non-space */ + } + + return input; +} + +AST_TEST_DEFINE(get_calleridname_test) +{ + int res = AST_TEST_PASS; + const char *in1 = "\" quoted-text internal \\\" quote \"<stuff>"; + const char *in2 = " token text with no quotes <stuff>"; + const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>"; + const char *noendquote = " \"quoted-text no end <stuff>"; + const char *addrspec = " \"sip:blah@blah <stuff>"; + const char *after_dname; + char dname[40]; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_get_calleridname_test"; + info->category = "channels/chan_sip/"; + info->summary = "decodes callerid name from sip header"; + info->description = "Decodes display-name field of sip header. Checks for valid output and expected failure cases."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* quoted-text with backslash escaped quote */ + after_dname = get_calleridname(in1, dname, sizeof(dname)); + ast_test_status_update(&args->status_update, "display-name1: %s\nafter: %s\n", dname, after_dname); + if (strcmp(dname, " quoted-text internal \" quote ")) { + ast_test_status_update(&args->status_update, "display-name1 test failed\n"); + ast_str_append(&args->ast_test_error_str, 0, "quoted-text with internal backslash decode failed. \n"); + res = AST_TEST_FAIL; + } + + /* token text */ + after_dname = get_calleridname(in2, dname, sizeof(dname)); + ast_test_status_update(&args->status_update, "display-name2: %s\nafter: %s\n", dname, after_dname); + if (strcmp(dname, "token text with no quotes")) { + ast_test_status_update(&args->status_update, "display-name2 test failed\n"); + ast_str_append(&args->ast_test_error_str, 0, "token text with decode failed. \n"); + res = AST_TEST_FAIL; + } + + /* quoted-text buffer overflow */ + after_dname = get_calleridname(overflow1, dname, sizeof(dname)); + ast_test_status_update(&args->status_update, "overflow display-name1: %s\nafter: %s\n", dname, after_dname); + if (*dname != '\0' && after_dname != overflow1) { + ast_test_status_update(&args->status_update, "overflow display-name1 test failed\n"); + ast_str_append(&args->ast_test_error_str, 0, "quoted-text buffer overflow check failed. \n"); + res = AST_TEST_FAIL; + } + + /* quoted-text buffer with no terminating end quote */ + after_dname = get_calleridname(noendquote, dname, sizeof(dname)); + ast_test_status_update(&args->status_update, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname); + if (*dname != '\0' && after_dname != noendquote) { + ast_test_status_update(&args->status_update, "no end quote for quoted-text display-name failed\n"); + ast_str_append(&args->ast_test_error_str, 0, "quoted-text buffer check no terminating end quote failed. \n"); + res = AST_TEST_FAIL; + } + + /* addr-spec rather than display-name. */ + after_dname = get_calleridname(addrspec, dname, sizeof(dname)); + ast_test_status_update(&args->status_update, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname); + if (*dname != '\0' && after_dname != addrspec) { + ast_test_status_update(&args->status_update, "detection of addr-spec failed\n"); + ast_str_append(&args->ast_test_error_str, 0, "detection of addr-spec failed. \n"); + res = AST_TEST_FAIL; + } + + return res; +} + + +void sip_request_parser_register_tests(void) +{ + AST_TEST_REGISTER(get_calleridname_test); + AST_TEST_REGISTER(sip_parse_uri_test); +} +void sip_request_parser_unregister_tests(void) +{ + AST_TEST_UNREGISTER(sip_parse_uri_test); + AST_TEST_UNREGISTER(get_calleridname_test); +} |