diff --git a/CHANGES b/CHANGES index 2ef538eab140b267ea6d93172c2c4969bcf1c0cc..6b49cb61c4a2550e1106191e3009e08c456e388d 100644 --- a/CHANGES +++ b/CHANGES @@ -50,6 +50,13 @@ chan_mobile * Added ECAM command support for Sony Ericsson phones. +chan_sip +------------------ + * Added support for RFC 3327 "Path" headers. This can be enabled in sip.conf + using the 'supportpath' setting, either on a global basis or on a peer basis. + This setting enables Asterisk to route outgoing out-of-dialog requests via a + set of proxies by using a pre-loaded route-set defined by the Path headers in + the REGISTER request. See Realtime updates for more configuration information. Features ------------------- @@ -95,6 +102,15 @@ Core reason to any string. It also allows for custom strings to be read as the redirecting reason from SIP Diversion headers. +Realtime +------------------ + * Dynamic realtime tables for SIP Users can now include a 'path' field. This + will store the path information for that peer when it registers. Realtime + tables can also use the 'supportpath' field to enable Path header support. + + * LDAP realtime configurations for SIP Users now have the AstAccountPathSupport + objectIdentifier. This maps to the supportpath option in sip.conf. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 10 to Asterisk 11 -------------------- ------------------------------------------------------------------------------ diff --git a/CREDITS b/CREDITS index aa3c1ab532b470a7bc167b0e13a829fbc173f215..a7673963cafa4b634531dfc69b1df56963c2f4e3 100644 --- a/CREDITS +++ b/CREDITS @@ -22,7 +22,7 @@ * John Todd, TalkPlus, Inc. and JR Richardson, Ntegrated Solutions. for funding the development of SIP Session Timers support. - * Omnitor AB, Gunnar Hellström, for funding work with videocaps, + * Omnitor AB, Gunnar Hellstr�m, for funding work with videocaps, T.140 RED, originate with video/text and many more contributions. @@ -54,7 +54,7 @@ === HARDWARE DONORS === - We'd like to thank the followwing for granting access to hardware for testing. + We'd like to thank the following for granting access to hardware for testing. * Thanks to QuickNet Technologies for their donation of an Internet PhoneJack and Linejack card to the project. @@ -63,13 +63,12 @@ * Thanks to VoipSupply for their donation of Sipura ATAs to the project for T.38 testing. (http://www.voipsupply.com) - * Thanks to Grandstream for their donation of ATAs to the project for T.38 testing. (http://www.grandstream.com) === MISCELLANEOUS PATCHES === - We'd like to thank the flollowing for their patches + We'd like to thank the following for their patches * Jim Dixon - Zapata Telephony and app_rpt http://www.zapatatelephony.org/app_rpt.html @@ -240,7 +239,8 @@ ControlPlayback, and multiple bug fixes See http://voip-info.org/users/view/sergee serg(AT)voipsolutions.ru - * Klaus Darillon - the SIPremoveHeader function in chan_sip + * Klaus Darillon - the SIPremoveHeader function in chan_sip and SIP Path + Support. * Moises Silva (moy) - for writing LibOpenR2, and providing support for it in chan_dahdi moises.silva(AT)gmail.com @@ -252,7 +252,7 @@ cdr_tds rewrite, countless other improvements, fixes, and good ideas. sean(AT)malleable.com - * Jan Kaláb - Calendaring support for Exchange Server 2007+ via + * Jan Kal�b - Calendaring support for Exchange Server 2007+ via Exchange Web Services. * University of Oslo (uio.no), Norway - SIP Max-Forwards setting diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 1adf7a00faeb921755a9d7964ea22bda216f7bb6..9538441926baca0e7213196628b4b25c76ee2956 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1187,6 +1187,8 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a static void free_old_route(struct sip_route *route); static void list_route(struct sip_route *route); static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp); +static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, char *pathbuf); +static int copy_route(struct sip_route **dst, const struct sip_route *src); static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr, struct sip_request *req, const char *uri); static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag); @@ -1360,7 +1362,7 @@ static void set_socket_transport(struct sip_socket *socket, int transport); static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags); /* Realtime device support */ -static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms); +static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path); static void update_peer(struct sip_peer *p, int expire); static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config); static const char *get_name_from_variable(const struct ast_variable *var); @@ -1442,6 +1444,7 @@ static int add_digit(struct sip_request *req, char digit, unsigned int duration, static int add_rpid(struct sip_request *req, struct sip_pvt *p); static int add_vidupdate(struct sip_request *req); static void add_route(struct sip_request *req, struct sip_route *route); +static void make_route_list(struct sip_route *route, char *r, int rem); static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field); static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field); static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field); @@ -5112,7 +5115,7 @@ static int sip_sendtext(struct ast_channel *ast, const char *text) that name and store that in the "regserver" field in the sippeers table to facilitate multi-server setups. */ -static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms) +static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path) { char port[10]; char ipaddr[INET6_ADDRSTRLEN]; @@ -5135,10 +5138,11 @@ static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr ast_copy_string(ipaddr, ast_sockaddr_isnull(addr) ? "" : ast_sockaddr_stringify_addr(addr), sizeof(ipaddr)); ast_copy_string(port, ast_sockaddr_port(addr) ? ast_sockaddr_stringify_port(addr) : "", sizeof(port)); - if (ast_strlen_zero(sysname)) /* No system name, disable this */ + if (ast_strlen_zero(sysname)) { /* No system name, disable this */ sysname = NULL; - else if (sip_cfg.rtsave_sysname) + } else if (sip_cfg.rtsave_sysname) { syslabel = "regserver"; + } /* XXX IMPORTANT: Anytime you add a new parameter to be updated, you * must also add it to contrib/scripts/asterisk.ldap-schema, @@ -5146,18 +5150,38 @@ static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr * and to configs/res_ldap.conf.sample as described in * bugs 15156 and 15895 */ - if (fc) { - ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, - "port", port, "regseconds", regseconds, - deprecated_username ? "username" : "defaultuser", defaultuser, - "useragent", useragent, "lastms", str_lastms, - fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */ + + /* This is ugly, we need something better ;-) */ + if (sip_cfg.rtsave_path) { + if (fc) { + ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, + "port", port, "regseconds", regseconds, + deprecated_username ? "username" : "defaultuser", defaultuser, + "useragent", useragent, "lastms", str_lastms, + "path", path, /* Path data can be NULL */ + fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */ + } else { + ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, + "port", port, "regseconds", regseconds, + "useragent", useragent, "lastms", str_lastms, + deprecated_username ? "username" : "defaultuser", defaultuser, + "path", path, /* Path data can be NULL */ + syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */ + } } else { - ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, - "port", port, "regseconds", regseconds, - "useragent", useragent, "lastms", str_lastms, - deprecated_username ? "username" : "defaultuser", defaultuser, - syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */ + if (fc) { + ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, + "port", port, "regseconds", regseconds, + deprecated_username ? "username" : "defaultuser", defaultuser, + "useragent", useragent, "lastms", str_lastms, + fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */ + } else { + ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr, + "port", port, "regseconds", regseconds, + "useragent", useragent, "lastms", str_lastms, + deprecated_username ? "username" : "defaultuser", defaultuser, + syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */ + } } } @@ -5252,6 +5276,10 @@ static void sip_destroy_peer(struct sip_peer *peer) ast_variables_destroy(peer->chanvars); peer->chanvars = NULL; } + if (peer->path) { + free_old_route(peer->path); + peer->path = NULL; + } register_peer_exten(peer, FALSE); ast_free_acl_list(peer->acl); @@ -5294,7 +5322,9 @@ static void update_peer(struct sip_peer *p, int expire) int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS); if (sip_cfg.peer_rtupdate && (p->is_realtime || rtcachefriends)) { - realtime_update_peer(p->name, &p->addr, p->username, p->fullcontact, p->useragent, expire, p->deprecated_username, p->lastms); + char path[SIPBUFSIZE * 2]; + make_route_list(p->path, path, sizeof(path)); + realtime_update_peer(p->name, &p->addr, p->username, p->fullcontact, p->useragent, expire, p->deprecated_username, p->lastms, path); } } @@ -5989,6 +6019,8 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog) return 0; } +static int __set_address_from_contact(const char *fullcontact, struct ast_sockaddr *addr, int tcp); + /*! \brief Create address structure from peer reference. * This function copies data from peer to the dialog, so we don't have to look up the peer * again from memory or database during the life time of the dialog. @@ -6029,6 +6061,12 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) dialog->rtptimeout = peer->rtptimeout; dialog->rtpholdtimeout = peer->rtpholdtimeout; dialog->rtpkeepalive = peer->rtpkeepalive; + copy_route(&dialog->route, peer->path); + if (dialog->route) { + /* Parse SIP URI of first route-set hop and use it as target address */ + __set_address_from_contact(dialog->route->hop, &dialog->sa, dialog->socket.type == SIP_TRANSPORT_TLS ? 1 : 0); + } + if (dialog_initialize_rtp(dialog)) { return -1; } @@ -6391,7 +6429,7 @@ static int sip_call(struct ast_channel *ast, const char *dest, int timeout) ast_clear_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38); if (p->options->transfer) { - char buf[SIPBUFSIZE/2]; + char buf[SIPBUFSIZE / 2]; if (referer) { if (sipdebug) @@ -11342,12 +11380,14 @@ static int process_sdp_a_image(const char *a, struct sip_pvt *p) * is supported for this dialog. */ static int add_supported(struct sip_pvt *pvt, struct sip_request *req) { + char supported_value[SIPBUFSIZE]; int res; - if (st_get_mode(pvt, 0) != SESSION_TIMER_MODE_REFUSE) { - res = add_header(req, "Supported", "replaces, timer"); - } else { - res = add_header(req, "Supported", "replaces"); - } + + sprintf(supported_value, "replaces%s%s", + (st_get_mode(pvt, 0) != SESSION_TIMER_MODE_REFUSE) ? ", timer" : "", + ast_test_flag(&pvt->flags[0], SIP_USEPATH) ? ", path" : ""); + res = add_header(req, "Supported", supported_value); + return res; } @@ -11523,12 +11563,21 @@ static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const st /*! \brief Add route header into request per learned route */ static void add_route(struct sip_request *req, struct sip_route *route) { - char r[SIPBUFSIZE*2], *p; - int n, rem = sizeof(r); + char r[SIPBUFSIZE * 2]; if (!route) return; + make_route_list(route, r, sizeof(r)); + add_header(req, "Route", r); +} + +/*! \brief Make the comma separated list of route headers from the route list */ +static void make_route_list(struct sip_route *route, char *r, int rem) +{ + char *p; + int n; + p = r; for (;route ; route = route->next) { n = strlen(route->hop); @@ -11545,7 +11594,6 @@ static void add_route(struct sip_request *req, struct sip_route *route) rem -= (n+2); } *p = '\0'; - add_header(req, "Route", r); } /*! \brief Set destination from SIP URI @@ -11822,6 +11870,9 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg snprintf(contact, sizeof(contact), "%s%s%s;expires=%d", brackets ? "" : "<", contact_uri, brackets ? "" : ">", p->expiry); add_header(resp, "Contact", contact); /* Not when we unregister */ } + if (p->method == SIP_REGISTER && ast_test_flag(&p->flags[0], SIP_USEPATH)) { + copy_header(resp, req, "Path"); + } } else if (!ast_strlen_zero(p->our_contact) && resp_needs_contact(msg, p->method)) { add_header(resp, "Contact", p->our_contact); } @@ -15337,6 +15388,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char * add_header(&req, "To", to); add_header(&req, "Call-ID", p->callid); add_header(&req, "CSeq", tmp); + add_supported(p, &req); if (!ast_strlen_zero(global_useragent)) add_header(&req, "User-Agent", global_useragent); @@ -15648,6 +15700,7 @@ static void destroy_association(struct sip_peer *peer) ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "regserver", "", "useragent", "", "lastms", "0", SENTINEL); } else { ast_db_del("SIP/Registry", peer->name); + ast_db_del("SIP/RegistryPath", peer->name); ast_db_del("SIP/PeerMethods", peer->name); } } @@ -15751,6 +15804,7 @@ static int sip_poke_peer_s(const void *data) static void reg_source_db(struct sip_peer *peer) { char data[256]; + char path[SIPBUFSIZE * 2]; struct ast_sockaddr sa; int expire; char full_addr[128]; @@ -15809,6 +15863,9 @@ static void reg_source_db(struct sip_peer *peer) sip_unref_peer(peer, "remove registration ref"), sip_ref_peer(peer, "add registration ref")); register_peer_exten(peer, TRUE); + if (!ast_db_get("SIP/RegistryPath", peer->name, path, sizeof(path))) { + build_path(NULL, peer, NULL, path); + } } /*! \brief Save contact header for 200 OK on INVITE */ @@ -16104,11 +16161,21 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st } } pvt->expiry = expire; + if (!build_path(pvt, peer, req, NULL)) { + /* Tell the dialog to use the Path header in the response */ + ast_set2_flag(&pvt->flags[0], 1, SIP_USEPATH); + } snprintf(data, sizeof(data), "%s:%d:%s:%s", ast_sockaddr_stringify(&peer->addr), expire, peer->username, peer->fullcontact); /* We might not immediately be able to reconnect via TCP, but try caching it anyhow */ - if (!peer->rt_fromcontact || !sip_cfg.peer_rtupdate) + if (!peer->rt_fromcontact || !sip_cfg.peer_rtupdate) { + char path[SIPBUFSIZE * 2]; + if (peer->path) { + make_route_list(peer->path, path, sizeof(path)); + ast_db_put("SIP/RegistryPath", peer->name, path); + } ast_db_put("SIP/Registry", peer->name, data); + } manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\n", peer->name, ast_sockaddr_stringify(&peer->addr)); /* Is this a new IP address for us? */ @@ -16144,10 +16211,10 @@ static void free_old_route(struct sip_route *route) static void list_route(struct sip_route *route) { if (!route) { - ast_verbose("list_route: no route\n"); + ast_verbose("list_route: no route/path\n"); } else { for (;route; route = route->next) - ast_verbose("list_route: hop: <%s>\n", route->hop); + ast_verbose("list_route: route/path hop: <%s>\n", route->hop); } } @@ -16277,6 +16344,134 @@ static void build_route(struct sip_pvt *p, struct sip_request *req, int backward } } +/*! \internal \brief Create a new route + * \retval NULL on error + * \retval sip_route on success + */ +static struct sip_route *create_route(const char *hop, struct sip_route *prev) +{ + struct sip_route *route; + int len; + + if (ast_strlen_zero(hop)) { + return NULL; + } + len = strlen(hop) + 1; + + /* ast_calloc is not needed because all fields are initialized in + * this block */ + route = ast_malloc(sizeof(*route) + len); + if (!route) { + return NULL; + } + ast_copy_string(route->hop, hop, len); + + route->next = NULL; + if (prev) { + prev->next = route; + } + return route; +} + +/*! \internal \brief copy route-set + * \retval non-zero on failure + * \retval 0 on success + */ +static int copy_route(struct sip_route **dst, const struct sip_route *src) +{ + struct sip_route *thishop, *head, *tail; + + /* Build a tailq, then assign it to **d when done. */ + head = NULL; + tail = head; + for (; src; src = src->next) { + thishop = create_route(src->hop, tail); + if (!thishop) { + return -1; + } + if (!head) { + head = thishop; + } + tail = thishop; + + ast_debug(2, "copy_route: copied hop: <%s>\n", thishop->hop); + } + *dst = head; + + return 0; +} + +/*! \brief Build route list from Path header + * RFC 3327 requires that the Path header contains SIP URIs with lr paramter. + * Thus, we do not care about strict routing SIP routers + */ +static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, char *pathbuf) +{ + struct sip_route *thishop, *head, *tail; + int start = 0; + int len; + char *pr; + + if (peer->path) { + free_old_route(peer->path); + peer->path = NULL; + } + + if (!ast_test_flag(&peer->flags[0], SIP_USEPATH)) { + ast_debug(2, "build_path: do not use Path headers\n"); + return -1; + } + ast_debug(2, "build_path: try to build pre-loaded route-set by parsing Path headers\n"); + + /* Build a tailq, then assign it to peer->path when done. */ + head = NULL; + tail = head; + /* 1st we pass through all the hops in any Path headers */ + for (;;) { + /* Either loop over the request's Path headers or parse the buffer */ + if (req) { + pr = ast_strdupa(__get_header(req, "Path", &start)); + if (*pr == '\0') { + break; + } + } else if (pathbuf) { + if (start == 0) { + pr = ast_strdupa(pathbuf); + start++; + } else { + break; + } + } else { + break; + } + for (; (pr = strchr(pr, '<')) ; pr += (len + 1)) { + /* Parse out each route entry */ + ++pr; + len = strcspn(pr, ">"); + *(pr + len) = '\0'; + thishop = create_route(pr, tail); + if (!thishop) { + return -1; + } + + if (!head) { + head = thishop; + } + tail = thishop; + ast_debug(2, "build_path: Path hop: <%s>\n", thishop->hop); + } + } + + /* Store as new route */ + peer->path = head; + + /* For debugging dump what we ended up with */ + if (p && sip_debug_test_pvt(p)) { + list_route(peer->path); + } + return 0; +} + /*! \brief builds the sip_pvt's nonce field which is used for the authentication * challenge. When forceupdate is not set, the nonce is only updated if * the current one is stale. In this case, a stalenonce is one which @@ -19984,6 +20179,20 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct ast_cli(fd, " Ign SDP ver : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_IGNORESDPVERSION))); ast_cli(fd, " Trust RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID))); ast_cli(fd, " Send RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_SENDRPID))); + ast_cli(fd, " Path support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_USEPATH))); + ast_cli(fd, " Path : "); + if (!peer->path) { + ast_cli(fd, "N/A\n"); + } else { + struct sip_route *r = peer->path; + int first = 1; + while (r) { + ast_cli(fd, "%s<%s>", first ? "" : ", ", r->hop); + first = 0; + r = r->next; + } + ast_cli(fd, "\n"); + } ast_cli(fd, " Subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))); ast_cli(fd, " Overlap dial : %s\n", allowoverlap2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP))); if (peer->outboundproxy) @@ -20577,6 +20786,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_ ast_cli(a->fd, " Allow promisc. redir: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR))); ast_cli(a->fd, " Enable call counters: %s\n", AST_CLI_YESNO(global_callcounter)); ast_cli(a->fd, " SIP domain support: %s\n", AST_CLI_YESNO(!AST_LIST_EMPTY(&domain_list))); + ast_cli(a->fd, " Path support : %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USEPATH))); ast_cli(a->fd, " Realm. auth: %s\n", AST_CLI_YESNO(credentials != NULL)); if (credentials) { struct sip_auth *auth; @@ -20750,6 +20960,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_ ast_cli(a->fd, " Update: %s\n", AST_CLI_YESNO(sip_cfg.peer_rtupdate)); ast_cli(a->fd, " Ignore Reg. Expire: %s\n", AST_CLI_YESNO(sip_cfg.ignore_regexpire)); ast_cli(a->fd, " Save sys. name: %s\n", AST_CLI_YESNO(sip_cfg.rtsave_sysname)); + ast_cli(a->fd, " Save path header: %s\n", AST_CLI_YESNO(sip_cfg.rtsave_path)); ast_cli(a->fd, " Auto Clear: %d (%s)\n", sip_cfg.rtautoclear, ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR) ? "Enabled" : "Disabled"); } ast_cli(a->fd, "\n----\n"); @@ -23342,7 +23553,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, const char *res { int expires, expires_ms; struct sip_registry *r; - r=p->registry; + r = p->registry; switch (resp) { case 401: /* Unauthorized */ @@ -29492,6 +29703,11 @@ static int sip_poke_peer(struct sip_peer *peer, int force) ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY); + copy_route(&p->route, peer->path); + if (p->route) { + /* Parse SIP URI of first route-set hop and use it as target address */ + __set_address_from_contact(p->route->hop, &p->sa, p->socket.type == SIP_TRANSPORT_TLS ? 1 : 0); + } /* Send OPTIONs to peer's fullcontact */ if (!ast_strlen_zero(peer->fullcontact)) { @@ -29946,6 +30162,9 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask if (!strcasecmp(v->name, "trustrpid")) { ast_set_flag(&mask[0], SIP_TRUSTRPID); ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID); + } else if (!strcasecmp(v->name, "supportpath")) { + ast_set_flag(&mask[0], SIP_USEPATH); + ast_set2_flag(&flags[0], ast_true(v->value), SIP_USEPATH); } else if (!strcasecmp(v->name, "sendrpid")) { ast_set_flag(&mask[0], SIP_SENDRPID); if (!strcasecmp(v->value, "pai")) { @@ -31528,6 +31747,8 @@ static int reload_config(enum channelreloadreason reason) ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS); } else if (!strcasecmp(v->name, "rtsavesysname")) { sip_cfg.rtsave_sysname = ast_true(v->value); + } else if (!strcasecmp(v->name, "rtsavepath")) { + sip_cfg.rtsave_path = ast_true(v->value); } else if (!strcasecmp(v->name, "rtupdate")) { sip_cfg.peer_rtupdate = ast_true(v->value); } else if (!strcasecmp(v->name, "ignoreregexpire")) { diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index f74446ab848ed609154cc56d6055a939a6cd6b34..3af35b04859fa8568a035adcc8bed1b99eb34d43 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -294,6 +294,7 @@ #define SIP_PROG_INBAND_NO (1 << 25) #define SIP_PROG_INBAND_YES (2 << 25) +#define SIP_USEPATH (1 << 27) /*!< GDP: Trust and use incoming Path headers? */ #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 */ @@ -304,7 +305,7 @@ #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) + SIP_USEREQPHONE | SIP_INSECURE | SIP_USEPATH) /*@}*/ /*! \name SIPflags2 @@ -737,6 +738,7 @@ struct __show_chan_arg { struct sip_settings { int peer_rtupdate; /*!< G: Update database with registration data for peer? */ int rtsave_sysname; /*!< G: Save system name at registration? */ + int rtsave_path; /*!< G: Save path header on registration */ int ignore_regexpire; /*!< G: Ignore expiration of peer */ int rtautoclear; /*!< Realtime ?? */ int directrtpsetup; /*!< Enable support for Direct RTP setup (no re-invites) */ @@ -1368,6 +1370,7 @@ struct sip_peer { int timer_t1; /*!< The maximum T1 value for the peer */ int timer_b; /*!< The maximum timer B (transaction timeouts) */ int fromdomainport; /*!< The From: domain port */ + struct sip_route *path; /*!< Head of linked list of out-of-dialog outgoing routing steps (fm Path headers) */ /*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 */ diff --git a/configs/res_ldap.conf.sample b/configs/res_ldap.conf.sample index 9a2accb80e663d27911e8766ca2c757daa1f732a..ac345cdd4ef3b236e6a691de2910a5027be89f0f 100644 --- a/configs/res_ldap.conf.sample +++ b/configs/res_ldap.conf.sample @@ -121,6 +121,7 @@ ipaddr = AstAccountIPAddress defaultuser = AstAccountDefaultUser regserver = AstAccountRegistrationServer lastms = AstAccountLastQualifyMilliseconds +supportpath = AstAccountPathSupport additionalFilter=(objectClass=AsteriskSIPUser) ; diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index d54398c442b3277329aa39a08763c68c3680c20c..a0ceabeb7e3a2697538f5c9cf6079cf0f0e59e3f 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -442,6 +442,21 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;outboundproxy=[2001:db8::1]:5062 ; IPv6 address literal with explicit port ; ; (could also be tcp,udp) - defining transports on the proxy line only ; ; applies for the global proxy, otherwise use the transport= option + +;supportpath=yes ; This activates parsing and handling of Path header as defined in RFC 3327. This enables + ; Asterisk to route outgoing out-of-dialog requests via a set of proxies by using a pre-loaded + ; route-set defined by the Path headers in the REGISTER request. + ; NOTE: There are multiple things to consider with this setting: + ; * As this influences routing of SIP requests make sure to not trust Path headers provided + ; by the user's SIP client (the proxy in front of Asterisk should remove existing user + ; provided Path headers). + ; * When a peer has both a path and outboundproxy set, the path will be added to Route: header + ; but routing to next hop is done using the outboundproxy. + ; * If set globally, not only will all peers use the Path header, but outbound REGISTER + ; requests from Asterisk will add path to the Supported header. + +;rtsavepath=yes ; If using dynamic realtime, store the path headers + ;matchexternaddrlocally = yes ; Only substitute the externaddr or externhost setting if it matches ; your localnet setting. Unless you have some sort of strange network ; setup you will not need to enable this. diff --git a/contrib/realtime/mysql/sippeers.sql b/contrib/realtime/mysql/sippeers.sql index e0dbe1a430492662e8d4fae8af77222a4859d7f3..1b01b280912e5a17ac2814eeca33b70d41cfbc74 100644 --- a/contrib/realtime/mysql/sippeers.sql +++ b/contrib/realtime/mysql/sippeers.sql @@ -79,6 +79,7 @@ CREATE TABLE IF NOT EXISTS `sippeers` ( `callingpres` enum('allowed_not_screened','allowed_passed_screen','allowed_failed_screen','allowed','prohib_not_screened','prohib_passed_screen','prohib_failed_screen','prohib') DEFAULT NULL, `mohinterpret` varchar(40) DEFAULT NULL, `mohsuggest` varchar(40) DEFAULT NULL, + `path` varchar(256) DEFAULT NULL, `parkinglot` varchar(40) DEFAULT NULL, `hasvoicemail` enum('yes','no') DEFAULT NULL, `subscribemwi` enum('yes','no') DEFAULT NULL, @@ -90,6 +91,7 @@ CREATE TABLE IF NOT EXISTS `sippeers` ( `ignoresdpversion` enum('yes','no') DEFAULT NULL, `allowtransfer` enum('yes','no') DEFAULT NULL, `dynamic` enum('yes','no') DEFAULT NULL, + `supportpath` enum('yes','no') DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `ipaddr` (`ipaddr`,`port`), diff --git a/contrib/realtime/postgresql/realtime.sql b/contrib/realtime/postgresql/realtime.sql index cba8d3895db4a3af738ee7075a839c21be20d200..96cbb16c2ac749f1535ae6e4053fff08c190c3f4 100644 --- a/contrib/realtime/postgresql/realtime.sql +++ b/contrib/realtime/postgresql/realtime.sql @@ -38,6 +38,7 @@ amaflags character varying(7), callgroup character varying(10), callerid character varying(80), canreinvite character varying(3) DEFAULT 'yes', +supportpath character varying(3) DEFAULT 'no', context character varying(80), defaultip character varying(15), dtmfmode character varying(7), @@ -70,6 +71,7 @@ cancallforward character varying(3) DEFAULT 'yes', lastms integer DEFAULT 0 NOT NULL, defaultuser character varying(80), fullcontact character varying(80), +path character varying(256), regserver character varying(30), useragent character varying(40), callbackextension character varying(40) diff --git a/contrib/scripts/asterisk.ldap-schema b/contrib/scripts/asterisk.ldap-schema index 3d81a1b931d91845c950ac32f2202a340556df2d..85f9103e297fb2516a5e5a833f3488bcd163caf2 100644 --- a/contrib/scripts/asterisk.ldap-schema +++ b/contrib/scripts/asterisk.ldap-schema @@ -112,7 +112,7 @@ objectIdentifier AstAccountSetVar AstAttrType:66 objectIdentifier AstAccountAllowOverlap AstAttrType:67 objectIdentifier AstAccountVideoSupport AstAttrType:68 objectIdentifier AstAccountIgnoreSDPVersion AstAttrType:69 - +objectIdentifier AstAccountPathSupport AstAttrType:70 ############################################################################# # Object Class OIDs @@ -640,6 +640,13 @@ attributetype ( AstAccountIgnoreSDPVersion SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15) +attributetype ( AstAccountPathSupport + NAME 'AstAccountPathSupport' + DESC 'Asterisk account support Path RFC 3327' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15) + ############################################################################# # Object Class definitions # @@ -762,7 +769,8 @@ objectclass ( AsteriskSIPUser AstAccountTransport $ AstAccountType $ AstAccountUserAgent $ - AstAccountVideoSupport + AstAccountVideoSupport $ + AstAccountPathSupport ) ) diff --git a/contrib/scripts/asterisk.ldif b/contrib/scripts/asterisk.ldif index 0546cdd7ccd0dee481accce2c26f7eb9396179f4..562105730a506514d3e58c64443f1b97d35dd9b1 100644 --- a/contrib/scripts/asterisk.ldif +++ b/contrib/scripts/asterisk.ldif @@ -108,6 +108,7 @@ olcObjectIdentifier: AstAccountSetVar AstAttrType:66 olcObjectIdentifier: AstAccountAllowOverlap AstAttrType:67 olcObjectIdentifier: AstAccountVideoSupport AstAttrType:68 olcObjectIdentifier: AstAccountIgnoreSDPVersion AstAttrType:69 +olcObjectIdentifier: AstAccountPathSupport AstAttrType:70 # # ############################################################################# @@ -636,6 +637,13 @@ olcAttributeTypes: ( AstAccountIgnoreSDPVersion SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15) # +olcAttributeTypes: ( AstAccountPathSupport + NAME 'AstAccountPathSupport' + DESC 'Asterisk account support Path RFC 3327' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15) +# ############################################################################# # Object Class definitions # @@ -758,7 +766,8 @@ olcObjectClasses: ( AsteriskSIPUser AstAccountTransport $ AstAccountType $ AstAccountUserAgent $ - AstAccountVideoSupport + AstAccountVideoSupport $ + AstAccountPathSupport ) ) #