From 52e26805e78416095db46494ad6f1c9c599aa2de Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Tue, 21 Jun 2005 01:27:54 +0000 Subject: begin properly supporting 'Supported' and 'Required' headers (bug #4351) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5952 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_sip.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 6 deletions(-) (limited to 'channels') diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 6054b65fc..d15f3ddc7 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -183,12 +183,81 @@ static struct cfalias { { "Via", "v" }, }; +/* 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 + +#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) + +/* List of well-known SIP options. If we get this in a require, + we should check the list and answer accordingly. */ +const struct cfsip_options { + int id; /* Bitmap ID */ + int supported; /* Supported by Asterisk ? */ + char *text; /* Text id, as in standard */ +} sip_options[] = { + /* Replaces: header for transfer */ + { SIP_OPT_REPLACES, SUPPORTED, "replaces" }, + /* RFC3262: PRACK 100% reliability */ + { SIP_OPT_100REL, NOT_SUPPORTED, "100rel" }, + /* SIP Session Timers */ + { SIP_OPT_TIMER, NOT_SUPPORTED, "timer" }, + /* RFC3959: SIP Early session support */ + { SIP_OPT_EARLY_SESSION, NOT_SUPPORTED, "early-session" }, + /* SIP Join header support */ + { SIP_OPT_JOIN, NOT_SUPPORTED, "join" }, + /* RFC3327: Path support */ + { SIP_OPT_PATH, NOT_SUPPORTED, "path" }, + /* RFC3840: Callee preferences */ + { SIP_OPT_PREF, NOT_SUPPORTED, "pref" }, + /* RFC3312: Precondition support */ + { SIP_OPT_PRECONDITION, NOT_SUPPORTED, "precondition" }, + /* RFC3323: Privacy with proxies*/ + { SIP_OPT_PRIVACY, NOT_SUPPORTED, "privacy" }, + /* Not yet RFC, but registred with IANA */ + { SIP_OPT_SDP_ANAT, NOT_SUPPORTED, "sdp_anat" }, + /* RFC3329: Security agreement mechanism */ + { SIP_OPT_SEC_AGREE, NOT_SUPPORTED, "sec_agree" }, + /* SIMPLE events: draft-ietf-simple-event-list-07.txt */ + { SIP_OPT_EVENTLIST, NOT_SUPPORTED, "eventlist" }, + /* GRUU: Globally Routable User Agent URI's */ + { SIP_OPT_GRUU, NOT_SUPPORTED, "gruu" }, + /* Target-dialog: draft-ietf-sip-target-dialog-00.txt */ + { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "target-dialog" }, +}; -#define DEFAULT_SIP_PORT 5060 /* From RFC 3261 (former 2543) */ -#define SIP_MAX_PACKET 4096 /* Also from RFC 3261 (2543), should sub headers tho */ +/* SIP Methods we support */ #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY" +/* SIP Extensions we support */ +#define SUPPORTED_EXTENSIONS "replaces" + +#define DEFAULT_SIP_PORT 5060 /* From RFC 3261 (former 2543) */ +#define SIP_MAX_PACKET 4096 /* Also from RFC 3261 (2543), should sub headers tho */ + static char default_useragent[AST_MAX_EXTENSION] = DEFAULT_USERAGENT; #define DEFAULT_CONTEXT "default" @@ -417,6 +486,7 @@ static struct sip_pvt { ast_group_t pickupgroup; /* Pickup group */ int lastinvite; /* Last Cseq of invite */ unsigned int flags; /* SIP_ flags */ + unsigned int sipoptions; /* Supported SIP sipoptions on the other end */ int capability; /* Special capability (codec) */ int jointcapability; /* Supported capability at both ends (codecs ) */ int peercapability; /* Supported peer capability */ @@ -539,7 +609,8 @@ struct sip_user { struct ast_codec_pref prefs; /* codec prefs */ ast_group_t callgroup; /* Call group */ ast_group_t pickupgroup; /* Pickup Group */ - unsigned int flags; /* SIP_ flags */ + unsigned int flags; /* SIP flags */ + unsigned int sipoptions; /* Supported SIP options */ struct ast_flags flags_page2; /* SIP_PAGE2 flags */ int amaflags; /* AMA flags for billing */ int callingpres; /* Calling id presentation */ @@ -582,7 +653,8 @@ struct sip_peer { struct ast_codec_pref prefs; /* codec prefs */ int lastmsgssent; time_t lastmsgcheck; /* Last time we checked for MWI */ - unsigned int flags; /* SIP_ flags */ + unsigned int flags; /* SIP flags */ + unsigned int sipoptions; /* Supported SIP options */ struct ast_flags flags_page2; /* SIP_PAGE2 flags */ int expire; /* When to expire this peer registration */ int expiry; /* Duration of registration */ @@ -693,6 +765,7 @@ static struct sip_auth *authl; /* Authentication list */ static struct ast_frame *sip_read(struct ast_channel *ast); static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req); static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); +static int transmit_response_with_unsupported(struct sip_pvt *p, char *msg, struct sip_request *req, char *unsupported); static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable, char *header, int stale); static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); @@ -728,6 +801,7 @@ static int sip_senddigit(struct ast_channel *ast, char digit); static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno); /* Add realm authentication in list */ static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, char *realm); /* Find authentication for a specific realm */ +static void append_date(struct sip_request *req); /* Append date to SIP packet */ /* Definition of this channel for channel registration */ static const struct ast_channel_tech sip_tech = { @@ -781,6 +855,52 @@ static char *get_in_brackets(char *c) return c; } +/*--- parse_sip_options: Parse supported header in incoming packet */ +unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported) +{ + char *next = NULL; + char *sep = NULL; + char *temp = ast_strdupa(supported); + int i; + unsigned int profile = 0; + + if (!supported || ast_strlen_zero(supported) ) + return 0; + + if (option_debug > 2 && sipdebug) + ast_log(LOG_DEBUG, "Begin: parsing SIP \"Supported: %s\"\n", supported); + + next = temp; + while (next) { + char res=0; + if ( (sep = strchr(next, ',')) != NULL) { + *sep = '\0'; + sep++; + } + while (*next == ' ') /* Skip spaces */ + next++; + if (option_debug > 2 && sipdebug) + ast_log(LOG_DEBUG, "Found SIP option: -%s-\n", next); + for (i=0; (i < (sizeof(sip_options) / sizeof(sip_options[0]))) && !res; i++) { + if (!strcasecmp(next, sip_options[i].text)) { + profile |= sip_options[i].id; + res = 1; + if (option_debug > 2 && sipdebug) + ast_log(LOG_DEBUG, "Matched SIP option: %s\n", next); + } + } + if (!res) + if (option_debug > 2 && sipdebug) + ast_log(LOG_DEBUG, "Found no match for SIP option: %s (Please file bug report!)\n", next); + next = sep; + } + if (pvt) + pvt->sipoptions = profile; + + ast_log(LOG_DEBUG, "* SIP extension value: %d for call %s\n", profile, pvt->callid); + return(profile); +} + /*--- sip_debug_test_addr: See if we pass debug IP filter */ static inline int sip_debug_test_addr(struct sockaddr_in *addr) { @@ -3685,7 +3805,17 @@ static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *r return __transmit_response(p, msg, req, 0); } -/*--- transmit_response: Transmit response, Make sure you get a reply */ +/*--- transmit_response_with_unsupported: Transmit response, no retransmits */ +static int transmit_response_with_unsupported(struct sip_pvt *p, char *msg, struct sip_request *req, char *unsupported) +{ + struct sip_request resp; + respprep(&resp, p, msg, req); + append_date(&resp); + add_header(&resp, "Unsupported", unsupported); + return send_response(p, &resp, 0, 0); +} + +/*--- transmit_response_reliable: Transmit response, Make sure you get a reply */ static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req, int fatal) { return __transmit_response(p, msg, req, fatal ? 2 : 1); @@ -5079,6 +5209,7 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req register_peer_exten(p, 0); p->fullcontact[0] = '\0'; p->useragent[0] = '\0'; + p->sipoptions = 0; p->lastms = 0; if (option_verbose > 2) @@ -5156,6 +5287,9 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d expires %d\n", p->name, ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry); register_peer_exten(p, 1); } + + /* Save SIP options profile */ + p->sipoptions = pvt->sipoptions; /* Save User agent */ useragent = get_header(req, "User-Agent"); @@ -6112,6 +6246,10 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, int sipme if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, user->md5secret, sipmethod, uri, reliable, ignore))) { sip_cancel_destroy(p); ast_copy_flags(p, user, SIP_PROMISCREDIR | SIP_DTMF | SIP_REINVITE); + /* Copy SIP extensions profile from INVITE */ + if (p->sipoptions) + user->sipoptions = p->sipoptions; + /* If we have a call limit, set flag */ if (user->incominglimit) ast_set_flag(p, SIP_CALL_LIMIT); @@ -6165,6 +6303,11 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, int sipme ast_verbose("Found peer '%s'\n", peer->name); /* Take the peer */ ast_copy_flags(p, peer, SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_NAT | SIP_PROG_INBAND | SIP_OSPAUTH); + + /* Copy SIP extensions profile to peer */ + if (p->sipoptions) + peer->sipoptions = p->sipoptions; + /* replace callerid if rpid found, and not restricted */ if (!ast_strlen_zero(rpid_num) && ast_test_flag(p, SIP_TRUSTRPID)) { if (*calleridname) @@ -6937,6 +7080,16 @@ static int _sip_show_peer(int type, int fd, struct mansession *s, struct message ast_cli(fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port)); ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); ast_cli(fd, " Def. Username: %s\n", peer->username); + ast_cli(fd, " SIP Options : "); + if (peer->sipoptions) { + for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) { + if (peer->sipoptions & sip_options[x].id) + ast_cli(fd, "%s ", sip_options[x].text); + } + } else + ast_cli(fd, "(none)"); + + ast_cli(fd, "\n"); ast_cli(fd, " Codecs : "); ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); ast_cli(fd, "%s\n", codec_buf); @@ -7395,7 +7548,17 @@ static int sip_show_channel(int fd, int argc, char *argv[]) ast_cli(fd, " Last Message: %s\n", cur->lastmsg); ast_cli(fd, " Promiscuous Redir: %s\n", ast_test_flag(cur, SIP_PROMISCREDIR) ? "Yes" : "No"); ast_cli(fd, " Route: %s\n", cur->route ? cur->route->hop : "N/A"); - ast_cli(fd, " DTMF Mode: %s\n\n", dtmfmode2str(ast_test_flag(cur, SIP_DTMF))); + ast_cli(fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(cur, SIP_DTMF))); + ast_cli(fd, " SIP Options : "); + if (cur->sipoptions) { + int x; + for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) { + if (cur->sipoptions & sip_options[x].id) + ast_cli(fd, "%s ", sip_options[x].text); + } + } else + ast_cli(fd, "(none)\n"); + ast_cli(fd, "\n\n"); found++; } cur = cur->next; @@ -8721,6 +8884,27 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int struct ast_channel *c=NULL; int gotdest; struct ast_frame af = { AST_FRAME_NULL, }; + char *supported; + char *required; + unsigned int required_profile = 0; + + /* Find out what they support */ + if (!p->sipoptions) { + supported = get_header(req, "Supported"); + if (supported) + parse_sip_options(p, supported); + } + required = get_header(req, "Required"); + if (required && !ast_strlen_zero(required)) { + required_profile = parse_sip_options(NULL, required); + if (required_profile) { /* They require something */ + /* At this point we support no extensions, so fail */ + transmit_response_with_unsupported(p, "420 Bad extension", req, required); + ast_set_flag(p, SIP_NEEDDESTROY); + return -1; + + } + } /* Check if this is a loop */ /* This happens since we do not properly support SIP domain -- cgit v1.2.3