From cb4e2107735dc05c1ad96b6a955c056e06b8dc1c Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Mon, 10 Feb 2014 18:28:35 +0000 Subject: [PATCH] chan_sip: Isolate code that manages struct sip_route. * Move route code to sip/route.c + sip/include/route.h * Rename functions to sip_route_* * Replace ad-hoc list code with macro's from linkedlists.h * Create sip_route_process_header() to processes Path and Record-Route headers (previously done with different code in build_route and build_path) * Add use of const where possible * Move struct uriparams, struct contact and contactliststruct from sip.h to reqresp_parser.h. sip/route.c uses reqresp_parser.h but not sip.h, this was a problem. These moved declares are not used outside of reqresp_parser. * While modifying reqprep() the lack of {} caused me trouble. I added them. * Code outside route.c treats sip_route as an opaque structure, using macro's or procedures for all access. (closes issue ASTERISK-22582) Reported by: Corey Farrell Review: https://reviewboard.asterisk.org/r/3173/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@407926 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_sip.c | 410 ++++++-------------------- channels/sip/include/reqresp_parser.h | 24 ++ channels/sip/include/route.h | 120 ++++++++ channels/sip/include/sip.h | 40 +-- channels/sip/route.c | 205 +++++++++++++ 5 files changed, 448 insertions(+), 351 deletions(-) create mode 100644 channels/sip/include/route.h create mode 100644 channels/sip/route.c diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 961c8552c5..ab8ff17e36 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -290,6 +290,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "sip/include/dialog.h" #include "sip/include/dialplan_functions.h" #include "sip/include/security_events.h" +#include "sip/include/route.h" #include "asterisk/sip_api.h" #include "asterisk/app.h" #include "asterisk/bridge.h" @@ -1233,11 +1234,8 @@ static void *registry_unref(struct sip_registry *reg, char *tag); static int update_call_counter(struct sip_pvt *fup, int event); static int auto_congest(const void *arg); static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method); -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 int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const char *pathbuf); static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr, struct sip_request *req, const char *uri); static int get_sip_pvt_from_replaces(const char *callid, const char *totag, const char *fromtag, @@ -1494,12 +1492,11 @@ static int add_text(struct sip_request *req, struct sip_pvt *p); static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode); 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 void add_route(struct sip_request *req, struct sip_route *route, int skip); 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); -static void set_destination(struct sip_pvt *p, char *uri); +static void set_destination(struct sip_pvt *p, const char *uri); static void add_date(struct sip_request *req); static void add_expires(struct sip_request *req, int expires); static void build_contact(struct sip_pvt *p); @@ -5313,10 +5310,7 @@ 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; - } + sip_route_clear(&peer->path); register_peer_exten(peer, FALSE); ast_free_acl_list(peer->acl); @@ -5360,11 +5354,14 @@ static void sip_destroy_peer(struct sip_peer *peer) 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)) { - 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); + if (sip_cfg.peer_rtupdate && (p->is_realtime || rtcachefriends)) { + struct ast_str *r = sip_route_list(&p->path, 0, 0); + if (r) { + realtime_update_peer(p->name, &p->addr, p->username, + p->fullcontact, p->useragent, expire, p->deprecated_username, + p->lastms, ast_str_buffer(r)); + ast_free(r); + } } } @@ -6101,10 +6098,10 @@ 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) { + sip_route_copy(&dialog->route, &peer->path); + if (!sip_route_empty(&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 == AST_TRANSPORT_TLS ? 1 : 0); + __set_address_from_contact(sip_route_first_uri(&dialog->route), &dialog->sa, dialog->socket.type == AST_TRANSPORT_TLS ? 1 : 0); } if (dialog_initialize_rtp(dialog)) { @@ -6686,10 +6683,7 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) ast_free(p->refer); p->refer = NULL; } - if (p->route) { - free_old_route(p->route); - p->route = NULL; - } + sip_route_clear(&p->route); deinit_req(&p->initreq); /* Clear history */ @@ -11684,39 +11678,18 @@ 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) +static void add_route(struct sip_request *req, struct sip_route *route, int skip) { - char r[SIPBUFSIZE * 2]; + struct ast_str *r; - if (!route) + if (sip_route_empty(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); - if (rem < n+3) /* we need room for "," */ - break; - if (p != r) { /* add a separator after fist route */ - *p++ = ','; - --rem; - } - *p++ = '<'; - ast_copy_string(p, route->hop, rem); /* cannot fail */ - p += n; - *p++ = '>'; - rem -= (n+2); } - *p = '\0'; + + if ((r = sip_route_list(route, 0, skip))) { + add_header(req, "Route", ast_str_buffer(r)); + ast_free(r); + } } /*! \brief Set destination from SIP URI @@ -11728,9 +11701,10 @@ static void make_route_list(struct sip_route *route, char *r, int rem) * * If there's a sips: uri scheme, TLS will be required. */ -static void set_destination(struct sip_pvt *p, char *uri) +static void set_destination(struct sip_pvt *p, const char *uri) { - char *trans, *h, *maddr, hostname[256]; + char *trans, *maddr, hostname[256]; + const char *h; int hn; int debug=sip_debug_test_pvt(p); int tls_on = FALSE; @@ -12053,26 +12027,28 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui } /* Check for strict or loose router */ - if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop, ";lr") == NULL) { + if (sip_route_is_strict(&p->route)) { is_strict = TRUE; if (sipdebug) ast_debug(1, "Strict routing enforced for session %s\n", p->callid); } - if (sipmethod == SIP_CANCEL) + if (sipmethod == SIP_CANCEL) { c = REQ_OFFSET_TO_STR(&p->initreq, rlpart2); /* Use original URI */ - else if (sipmethod == SIP_ACK) { + } else if (sipmethod == SIP_ACK) { /* Use URI from Contact: in 200 OK (if INVITE) (we only have the contacturi on INVITEs) */ - if (!ast_strlen_zero(p->okcontacturi)) - c = is_strict ? p->route->hop : p->okcontacturi; - else + if (!ast_strlen_zero(p->okcontacturi)) { + c = is_strict ? sip_route_first_uri(&p->route) : p->okcontacturi; + } else { c = REQ_OFFSET_TO_STR(&p->initreq, rlpart2); - } else if (!ast_strlen_zero(p->okcontacturi)) - c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */ - else if (!ast_strlen_zero(p->uri)) + } + } else if (!ast_strlen_zero(p->okcontacturi)) { + /* Use for BYE or REINVITE */ + c = is_strict ? sip_route_first_uri(&p->route) : p->okcontacturi; + } else if (!ast_strlen_zero(p->uri)) { c = p->uri; - else { + } else { char *n; /* We have no URI, use To: or From: header as URI (depending on direction) */ ast_copy_string(stripped, sip_get_header(orig, is_outbound ? "To" : "From"), @@ -12090,7 +12066,7 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui * final response. For a CANCEL or ACK, we have to send to the same destination * as the original INVITE. */ - if (p->route && + if (!sip_route_empty(&p->route) && !(sipmethod == SIP_CANCEL || (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)))) { if (p->socket.type != AST_TRANSPORT_UDP && p->socket.tcptls_session) { @@ -12101,9 +12077,9 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui * simply send to the received-from address. No need * for lookups. */ } else { - set_destination(p, p->route->hop); + set_destination(p, sip_route_first_uri(&p->route)); } - add_route(req, is_strict ? p->route->next : p->route); + add_route(req, &p->route, is_strict ? 1 : 0); } add_max_forwards(p, req); @@ -14117,7 +14093,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho * NOTIFY messages will use this function for preparing the request and should * have Route headers present. */ - add_route(req, p->route); + add_route(req, &p->route, 0); add_header(req, "From", from); add_header(req, "To", to); @@ -16358,10 +16334,12 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st 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) { - char path[SIPBUFSIZE * 2]; - if (peer->path) { - make_route_list(peer->path, path, sizeof(path)); - ast_db_put("SIP/RegistryPath", peer->name, path); + if (!sip_route_empty(&peer->path)) { + struct ast_str *r = sip_route_list(&peer->path, 0, 0); + if (r) { + ast_db_put("SIP/RegistryPath", peer->name, ast_str_buffer(r)); + ast_free(r); + } } ast_db_put("SIP/Registry", peer->name, data); } @@ -16394,29 +16372,6 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st return PARSE_REGISTER_UPDATE; } -/*! \brief Remove route from route list */ -static void free_old_route(struct sip_route *route) -{ - struct sip_route *next; - - while (route) { - next = route->next; - ast_free(route); - route = next; - } -} - -/*! \brief List all routes - mostly for debugging */ -static void list_route(struct sip_route *route) -{ - if (!route) { - ast_verbose("list_route: no route/path\n"); - } else { - for (;route; route = route->next) - ast_verbose("list_route: route/path hop: <%s>\n", route->hop); - } -} - /*! \brief Build route list from Record-Route header * * \param p @@ -16427,21 +16382,16 @@ static void list_route(struct sip_route *route) */ static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp) { - struct sip_route *thishop, *head, *tail; int start = 0; - int len; - const char *rr, *c; + const char *header; /* Once a persistent route is set, don't fool with it */ - if (p->route && p->route_persistent) { - ast_debug(1, "build_route: Retaining previous route: <%s>\n", p->route->hop); + if (!sip_route_empty(&p->route) && p->route_persistent) { + ast_debug(1, "build_route: Retaining previous route: <%s>\n", sip_route_first_uri(&p->route)); return; } - if (p->route) { - free_old_route(p->route); - p->route = NULL; - } + sip_route_clear(&p->route); /* We only want to create the route set the first time this is called except it is called from a provisional response.*/ @@ -16454,173 +16404,44 @@ static void build_route(struct sip_pvt *p, struct sip_request *req, int backward * in reverse order. However, we do need to maintain a correct * tail pointer because the contact is always at the end. */ - head = NULL; - tail = head; /* 1st we pass through all the hops in any Record-Route headers */ for (;;) { - /* Each Record-Route header */ - int len = 0; - const char *uri; - rr = __get_header(req, "Record-Route", &start); - if (*rr == '\0') { + header = __get_header(req, "Record-Route", &start); + if (*header == '\0') { break; } - while (!get_in_brackets_const(rr, &uri, &len)) { - len++; - rr = strchr(rr, ','); - if(rr >= uri && rr < (uri + len)) { - /* comma inside brackets*/ - const char *next_br = strchr(rr, '<'); - if (next_br && next_br < (uri + len)) { - rr++; - continue; - } - continue; - } - if ((thishop = ast_malloc(sizeof(*thishop) + len))) { - ast_copy_string(thishop->hop, uri, len); - ast_debug(2, "build_route: Record-Route hop: <%s>\n", thishop->hop); - /* Link in */ - if (backwards) { - /* Link in at head so they end up in reverse order */ - thishop->next = head; - head = thishop; - /* If this was the first then it'll be the tail */ - if (!tail) { - tail = thishop; - } - } else { - thishop->next = NULL; - /* Link in at the end */ - if (tail) { - tail->next = thishop; - } else { - head = thishop; - } - tail = thishop; - } - } - rr = strchr(uri + len, ','); - if (rr == NULL) { - /* No more field-values, we're done with this header */ - break; - } - /* Advance past comma */ - rr++; - } + sip_route_process_header(&p->route, header, backwards); } - /* Only append the contact if we are dealing with a strict router */ - if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop, ";lr") == NULL) ) { + /* Only append the contact if we are dealing with a strict router or have no route */ + if (sip_route_empty(&p->route) || sip_route_is_strict(&p->route)) { /* 2nd append the Contact: if there is one */ /* Can be multiple Contact headers, comma separated values - we just take the first */ - char *contact = ast_strdupa(sip_get_header(req, "Contact")); - if (!ast_strlen_zero(contact)) { - ast_debug(2, "build_route: Contact hop: %s\n", contact); - /* Look for <: delimited address */ - c = get_in_brackets(contact); - len = strlen(c) + 1; - if ((thishop = ast_malloc(sizeof(*thishop) + len))) { - /* ast_calloc is not needed because all fields are initialized in this block */ - ast_copy_string(thishop->hop, c, len); - thishop->next = NULL; - /* Goes at the end */ - if (tail) { - tail->next = thishop; - } else { - head = thishop; - } - } + int len; + header = sip_get_header(req, "Contact"); + if (strchr(header, '<')) { + get_in_brackets_const(header, &header, &len); + } else { + len = strlen(header); + } + if (header && len) { + sip_route_add(&p->route, header, len, 0); } } - /* Store as new route */ - p->route = head; - /* For debugging dump what we ended up with */ if (sip_debug_test_pvt(p)) { - list_route(p->route); + sip_route_dump(&p->route); } } -/*! - * \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) +static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const 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; - } + sip_route_clear(&peer->path); if (!ast_test_flag(&peer->flags[0], SIP_USEPATH)) { ast_debug(2, "build_path: do not use Path headers\n"); @@ -16628,51 +16449,26 @@ static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_reque } 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') { + if (req) { + int start = 0; + const char *header; + for (;;) { + header = __get_header(req, "Path", &start); + if (*header == '\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); + sip_route_process_header(&peer->path, header, 0); } + } else if (pathbuf) { + sip_route_process_header(&peer->path, pathbuf, 0); } - /* Store as new route */ - peer->path = head; + /* Caches result for any dialog->route copied from peer->path */ + sip_route_is_strict(&peer->path); /* For debugging dump what we ended up with */ if (p && sip_debug_test_pvt(p)) { - list_route(peer->path); + sip_route_dump(&peer->path); } return 0; } @@ -20419,6 +20215,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct } if (peer && type==0 ) { /* Normal listing */ struct ast_str *mailbox_str = ast_str_alloca(512); + struct ast_str *path; struct sip_auth_container *credentials; ao2_lock(peer); @@ -20504,18 +20301,9 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct 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"); + if ((path = sip_route_list(&peer->path, 1, 0))) { + ast_cli(fd, " Path : %s\n", ast_str_buffer(path)); + ast_free(path); } 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))); @@ -21612,6 +21400,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a sip_pvt_lock(cur); if (!strncasecmp(cur->callid, a->argv[3], len)) { + struct ast_str *strbuf; char formatbuf[SIPBUFSIZE/2]; ast_cli(a->fd, "\n"); if (cur->subscribed != NONE) { @@ -21661,18 +21450,9 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a ast_cli(a->fd, " Need Destroy: %s\n", AST_CLI_YESNO(cur->needdestroy)); ast_cli(a->fd, " Last Message: %s\n", cur->lastmsg); ast_cli(a->fd, " Promiscuous Redir: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[0], SIP_PROMISCREDIR))); - ast_cli(a->fd, " Route: "); - if (cur->route) { - struct sip_route *route; - int first = 1; - - for (route = cur->route; route; route = route->next) { - ast_cli(a->fd, "%s<%s>", first ? "" : ", ", route->hop); - first = 0; - } - ast_cli(a->fd, "\n"); - } else { - ast_cli(a->fd, "N/A\n"); + if ((strbuf = sip_route_list(&cur->route, 1, 0))) { + ast_cli(a->fd, " Route: %s\n", ast_str_buffer(strbuf)); + ast_free(strbuf); } ast_cli(a->fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF))); ast_cli(a->fd, " SIP Options: "); @@ -23398,7 +23178,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest if(set_address_from_contact(p)) { /* Bad contact - we don't know how to reach this device */ /* We need to ACK, but then send a bye */ - if (!p->route && !req->ignore) { + if (sip_route_empty(&p->route) && !req->ignore) { ast_set_flag(&p->flags[0], SIP_PENDINGBYE); } } @@ -29534,10 +29314,10 @@ 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) { + sip_route_copy(&p->route, &peer->path); + if (!sip_route_empty(&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 == AST_TRANSPORT_TLS ? 1 : 0); + __set_address_from_contact(sip_route_first_uri(&p->route), &p->sa, p->socket.type == AST_TRANSPORT_TLS ? 1 : 0); } /* Send OPTIONs to peer's fullcontact */ diff --git a/channels/sip/include/reqresp_parser.h b/channels/sip/include/reqresp_parser.h index 02b046bc72..7f9c8f6d91 100644 --- a/channels/sip/include/reqresp_parser.h +++ b/channels/sip/include/reqresp_parser.h @@ -22,6 +22,30 @@ #ifndef _SIP_REQRESP_H #define _SIP_REQRESP_H +/*! \brief uri parameters */ +struct uriparams { + char *transport; + char *user; + char *method; + char *ttl; + char *maddr; + int lr; +}; + +struct contact { + AST_LIST_ENTRY(contact) list; + char *name; + char *user; + char *pass; + char *hostport; + struct uriparams params; + char *headers; + char *expires; + char *q; +}; + +AST_LIST_HEAD_NOLOCK(contactliststruct, contact); + /*! * \brief parses a URI in its components. * diff --git a/channels/sip/include/route.h b/channels/sip/include/route.h new file mode 100644 index 0000000000..511f0ff53e --- /dev/null +++ b/channels/sip/include/route.h @@ -0,0 +1,120 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, 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_route header file + */ + +#ifndef _SIP_ROUTE_H +#define _SIP_ROUTE_H + +#include "asterisk/linkedlists.h" +#include "asterisk/strings.h" + +/*! + * \brief Opaque storage of a sip route hop + */ +struct sip_route_hop; + +/*! + * \internal \brief Internal enum to remember last calculated + */ +enum sip_route_type { + route_loose = 0, /*!< The first hop contains ;lr or does not exist */ + route_strict, /*!< The first hop exists and does not contain ;lr */ + route_invalidated, /*!< strict/loose routing needs to be rechecked */ +}; + +/*! + * \brief Structure to store route information + * + * \note This must be zero-filled on allocation + */ +struct sip_route { + AST_LIST_HEAD_NOLOCK(, sip_route_hop) list; + enum sip_route_type type; +}; + +/*! + * \brief Add a new hop to the route + * + * \param route Route + * \param uri Address of this hop + * \param len Length of hop not including null terminator + * \param inserthead If true then inserted the new route to the top of the list + * + * \retval Pointer to null terminated copy of URI on success + * \retval NULL on error + */ +const char *sip_route_add(struct sip_route *route, const char *uri, size_t len, int inserthead); + +/*! + * \brief Add routes from header + * + * \note This procedure is for headers that require use of . + */ +void sip_route_process_header(struct sip_route *route, const char *header, int inserthead); + +/*! + * \brief copy route-set + * + * \retval non-zero on failure + * \retval 0 on success + */ +void sip_route_copy(struct sip_route *dst, const struct sip_route *src); + +/*! + * \brief Free all routes in the list + */ +void sip_route_clear(struct sip_route *route); + +/*! + * \brief Verbose dump of all hops for debugging + */ +void sip_route_dump(const struct sip_route *route); + +/*! + * \brief Make the comma separated list of route hops + * + * \param route Source of route list + * \param formatcli Add's space after comma's, print's N/A if list is empty. + * \param skip Number of hops to skip + * + * \retval an allocated struct ast_str on success + * \retval NULL on failure + */ +struct ast_str *sip_route_list(const struct sip_route *route, int formatcli, int skip) + __attribute_malloc__ __attribute_warn_unused_result__; + +/*! + * \brief Check if the route is strict + * + * \note The result is cached in route->type + */ +int sip_route_is_strict(struct sip_route *route); + +/*! + * \brief Get the URI of the route's first hop + */ +const char *sip_route_first_uri(const struct sip_route *route); + +/*! + * \brief Check if route has no URI's + */ +#define sip_route_empty(route) AST_LIST_EMPTY(&(route)->list) + +#endif diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 0b88dae430..7e2d0b9604 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -39,6 +39,8 @@ #include "asterisk/netsock2.h" #include "asterisk/features_config.h" +#include "route.h" + #ifndef FALSE #define FALSE 0 #endif @@ -847,12 +849,6 @@ struct sip_invite_param { struct sip_proxy *outboundproxy; /*!< Outbound proxy URI */ }; -/*! \brief Structure to save routing information for a SIP session */ -struct sip_route { - struct sip_route *next; - char hop[0]; -}; - /*! \brief Structure to store Via information */ struct sip_via { char *via; @@ -1119,7 +1115,7 @@ struct sip_pvt { struct ast_sockaddr 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_route route; /*!< List of routing steps (fm Record-Route) */ struct sip_notify *notify; /*!< Custom notify type */ struct sip_auth_container *peerauth;/*!< Realm authentication credentials */ int noncecount; /*!< Nonce-count */ @@ -1346,7 +1342,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) */ + struct sip_route path; /*!< 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 */ @@ -1804,34 +1800,6 @@ struct sip_monitor_instance { struct sip_epa_entry *suspension_entry; }; -/*! - * \brief uri parameters - * - */ - -struct uriparams { - char *transport; - char *user; - char *method; - char *ttl; - char *maddr; - int lr; -}; - -struct contact { - AST_LIST_ENTRY(contact) list; - char *name; - char *user; - char *pass; - char *hostport; - struct uriparams params; - char *headers; - char *expires; - char *q; -}; - -AST_LIST_HEAD_NOLOCK(contactliststruct, contact); - /*! \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 { diff --git a/channels/sip/route.c b/channels/sip/route.c new file mode 100644 index 0000000000..5f0d62e5ca --- /dev/null +++ b/channels/sip/route.c @@ -0,0 +1,205 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, 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_route functions + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" + +#include "include/route.h" +#include "include/reqresp_parser.h" + +/*! + * \brief Traverse route hops + */ +#define sip_route_traverse(route,elem) AST_LIST_TRAVERSE(&(route)->list, elem, list) +#define sip_route_first(route) AST_LIST_FIRST(&(route)->list) + +/*! + * \brief Structure to save a route hop + */ +struct sip_route_hop { + AST_LIST_ENTRY(sip_route_hop) list; + char uri[0]; +}; + +const char *sip_route_add(struct sip_route *route, const char *uri, size_t len, int inserthead) +{ + struct sip_route_hop *hop; + + if (!uri || len < 1 || uri[0] == '\0') { + return NULL; + } + + /* Expand len to include null terminator */ + len++; + + /* ast_calloc is not needed because all fields are initialized in this block */ + hop = ast_malloc(sizeof(struct sip_route_hop) + len); + if (!hop) { + return NULL; + } + ast_copy_string(hop->uri, uri, len); + + if (inserthead) { + AST_LIST_INSERT_HEAD(&route->list, hop, list); + route->type = route_invalidated; + } else { + if (sip_route_empty(route)) { + route->type = route_invalidated; + } + AST_LIST_INSERT_TAIL(&route->list, hop, list); + hop->list.next = NULL; + } + + return hop->uri; +} + +void sip_route_process_header(struct sip_route *route, const char *header, int inserthead) +{ + const char *hop; + int len = 0; + const char *uri; + + if (!route) { + ast_log(LOG_ERROR, "sip_route_process_header requires non-null route"); + ast_do_crash(); + return; + } + + while (!get_in_brackets_const(header, &uri, &len)) { + header = strchr(header, ','); + if (header >= uri && header <= (uri + len)) { + /* comma inside brackets */ + const char *next_br = strchr(header, '<'); + if (next_br && next_br <= (uri + len)) { + header++; + continue; + } + continue; + } + if ((hop = sip_route_add(route, uri, len, inserthead))) { + ast_debug(2, "sip_route_process_header: <%s>\n", hop); + } + header = strchr(uri + len + 1, ','); + if (header == NULL) { + /* No more field-values, we're done with this header */ + break; + } + /* Advance past comma */ + header++; + } +} + +void sip_route_copy(struct sip_route *dst, const struct sip_route *src) +{ + struct sip_route_hop *hop; + + /* make sure dst is empty */ + sip_route_clear(dst); + + sip_route_traverse(src, hop) { + const char *uri = sip_route_add(dst, hop->uri, strlen(hop->uri), 0); + if (uri) { + ast_debug(2, "sip_route_copy: copied hop: <%s>\n", uri); + } + } + + dst->type = src->type; +} + +void sip_route_clear(struct sip_route *route) +{ + struct sip_route_hop *hop; + + while ((hop = AST_LIST_REMOVE_HEAD(&route->list, list))) { + ast_free(hop); + } + + route->type = route_loose; +} + +void sip_route_dump(const struct sip_route *route) +{ + if (sip_route_empty(route)) { + ast_verbose("sip_route_dump: no route/path\n"); + } else { + struct sip_route_hop *hop; + sip_route_traverse(route, hop) { + ast_verbose("sip_route_dump: route/path hop: <%s>\n", hop->uri); + } + } +} + +struct ast_str *sip_route_list(const struct sip_route *route, int formatcli, int skip) +{ + struct sip_route_hop *hop; + const char *comma; + struct ast_str *buf; + int i = 0 - skip; + + buf = ast_str_create(64); + if (!buf) { + return NULL; + } + + comma = formatcli ? ", " : ","; + + sip_route_traverse(route, hop) { + if (i >= 0) { + ast_str_append(&buf, 0, "%s<%s>", i ? comma : "", hop->uri); + } + i++; + } + + if (formatcli && i <= 0) { + ast_str_append(&buf, 0, "N/A"); + } + + return buf; +} + +int sip_route_is_strict(struct sip_route *route) +{ + if (!route) { + return 0; + } + + if (route->type == route_invalidated) { + struct sip_route_hop *hop = sip_route_first(route); + int ret = hop && (strstr(hop->uri, ";lr") == NULL); + route->type = ret ? route_strict : route_loose; + return ret; + } + + return (route->type == route_strict) ? 1 : 0; +} + +const char *sip_route_first_uri(const struct sip_route *route) +{ + struct sip_route_hop *hop = sip_route_first(route); + return hop ? hop->uri : NULL; +}