📄 sip_util.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE sip_util.c * * SIP utility functions. * * @author Pekka Pessi <Pekka.Pessi@nokia.com>. * * @date Created: Tue Jun 13 02:57:51 2000 ppessi */#include "config.h"/* Avoid casting sip_t to msg_pub_t and sip_header_t to msg_header_t */#define MSG_PUB_T struct sip_s#define MSG_HDR_T union sip_header_u#include <sofia-sip/su_alloc.h>#include <sofia-sip/su_strlst.h>#include <sofia-sip/string0.h>#include "sofia-sip/sip_parser.h"#include <sofia-sip/sip_header.h>#include <sofia-sip/sip_util.h>#include <sofia-sip/sip_status.h>#include <sofia-sip/bnf.h>#include <sofia-sip/hostdomain.h>#include <stdio.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <float.h>#include <limits.h>#include <ctype.h>/** * Compare two SIP addresses ( @From or @To headers). * * @retval nonzero if matching. * @retval zero if not matching. */int sip_addr_match(sip_addr_t const *a, sip_addr_t const *b){ return (a->a_tag == NULL || b->a_tag == NULL || strcmp(a->a_tag, b->a_tag) == 0) && str0casecmp(a->a_host, b->a_host) == 0 && str0cmp(a->a_user, b->a_user) == 0 && str0cmp(a->a_url->url_scheme, b->a_url->url_scheme);}/**@ingroup sip_contact * * Create a contact header. * * Create a @Contact header object with the given URL and list of parameters. * * @param home memory home * @param url URL (string or pointer to url_t) * @param p,... NULL-terminated list of @Contact parameters * * @return * A pointer to newly created @Contact header object when successful or NULL * upon an error. * */sip_contact_t * sip_contact_create(su_home_t *home, url_string_t const *url, char const *p, ...){ su_strlst_t *l; su_home_t *lhome; sip_contact_t *m; if (url == NULL) return NULL; l = su_strlst_create_with(NULL, "<", NULL), lhome = su_strlst_home(l); if (l == NULL) return NULL; if (url_is_string(url)) su_strlst_append(l, (char const *)url); else su_strlst_append(l, url_as_string(lhome, url->us_url)); su_strlst_append(l, ">"); if (p) { va_list ap; va_start(ap, p); for (; p; p = va_arg(ap, char const *)) { su_strlst_append(l, ";"); su_strlst_append(l, p); } va_end(ap); } m = sip_contact_make(home, su_strlst_join(l, lhome, "")); su_strlst_destroy(l); return m;}/** Convert a @Via header to @Contact header. * * The @Contact URI will contain the port number if needed. If transport * protocol name starts with "TLS", "SIPS:" URI schema is used. Transport * parameter is included in the URI unless the transport protocol is UDP. * * @param home memory home * @param v @Via header field structure * (with <sent-protocol> and <sent-by> parameters) * @param user username for @Contact URI (may be NULL) * * @retval contact header structure * @retval NULL upon an error * * @sa sip_contact_create_from_via_with_transport(), * sip_contact_string_from_via() */sip_contact_t *sip_contact_create_from_via(su_home_t *home, sip_via_t const *v, char const *user){ const char *tp; if (!v) return NULL; tp = v->v_protocol; if (tp == sip_transport_udp || strcasecmp(tp, sip_transport_udp) == 0) /* Default is UDP */ tp = NULL; return sip_contact_create_from_via_with_transport(home, v, user, tp);}/** Convert a @Via header to @Contact header. * * The @Contact URI will contain the port number and transport parameters if * needed. If transport protocol name starts with "TLS", "SIPS:" URI schema * is used. * * @param home memory home * @param v @Via header field structure * (with <sent-by> parameter containing host and port) * @param user username for @Contact URI (may be NULL) * @param transport transport name for @Contact URI (may be NULL) * * @retval contact header structure * @retval NULL upon an error * * @sa sip_contact_create_from_via(), sip_contact_string_from_via() */sip_contact_t *sip_contact_create_from_via_with_transport(su_home_t *home, sip_via_t const *v, char const *user, char const *transport){ char *s = sip_contact_string_from_via(NULL, v, user, transport); sip_contact_t *m = sip_contact_make(home, s); su_free(NULL, s); return m;}/** Convert a @Via header to @Contact URL string. * * The @Contact URI will contain the port number and transport parameters if * needed. If transport protocol name starts with "TLS", "SIPS:" URI schema * is used. * * The contact URI string returned will always have angle brackets ("<" and * ">") around it. * * @param home memory home * @param v @Via header field structure * (with <sent-by> parameter containing host and port) * @param user username for @Contact URI (may be NULL) * @param transport transport name for @Contact URI (may be NULL) * * @retval string containing Contact URI with angle brackets * @retval NULL upon an error */char *sip_contact_string_from_via(su_home_t *home, sip_via_t const *v, char const *user, char const *transport){ const char *host, *port, *maddr, *comp; char const *scheme = "sip:"; int one = 1; char _transport[16]; if (!v) return NULL; host = v->v_host; if (v->v_received) host = v->v_received; port = sip_via_port(v, &one); maddr = v->v_maddr; comp = v->v_comp; if (host == NULL) return NULL; if (sip_transport_has_tls(v->v_protocol) || sip_transport_has_tls(transport)) { scheme = "sips:"; if (port && strcmp(port, SIPS_DEFAULT_SERV) == 0) port = NULL; if (port || host_is_ip_address(host)) transport = NULL; } else if (port && strcmp(port, SIP_DEFAULT_SERV) == 0 && (host_is_ip_address(host) || host_has_domain_invalid(host))) { port = NULL; } if (transport && strncasecmp(transport, "SIP/2.0/", 8) == 0) transport += 8; /* Make transport parameter lowercase */ if (transport && strlen(transport) < (sizeof _transport)) { char *s = strcpy(_transport, transport); short c; for (s = _transport; (c = *s) && c != ';'; s++) if (isupper(c)) *s = tolower(c); transport = _transport; } return su_strcat_all(home, "<", scheme, user ? user : "", user ? "@" : "", host, SIP_STRLOG(":", port), SIP_STRLOG(";transport=", transport), SIP_STRLOG(";maddr=", maddr), SIP_STRLOG(";comp=", comp), ">", NULL);}/** Check if tranport name refers to TLS */int sip_transport_has_tls(char const *transport_name){ if (!transport_name) return 0; if (transport_name == sip_transport_tls) return 1; /* transport name starts with TLS or SIP/2.0/TLS */ return strncasecmp(transport_name, "TLS", 3) == 0 || strncasecmp(transport_name, sip_transport_tls, 11) == 0;}/**Perform sanity check on a SIP message * * Check that the SIP message has all the mandatory fields. * * @param sip SIP message to be checked * * @return * When the SIP message fulfills the minimum requirements, return zero, * otherwise a negative status code. */intsip_sanity_check(sip_t const *sip){ if (!sip || !((sip->sip_request != NULL) ^ (sip->sip_status != NULL)) || !sip->sip_to || !sip->sip_from || !sip->sip_call_id || !sip->sip_cseq || !sip->sip_via || (sip->sip_flags & MSG_FLG_TRUNC)) return -1; /* Bad request */ if (sip->sip_request) { url_t const *ruri = sip->sip_request->rq_url; switch (ruri->url_type) { case url_invalid: return -1; case url_sip: case url_sips: case url_im: case url_pres: if (!ruri->url_host || strlen(ruri->url_host) == 0) return -1; break; case url_tel: if (!ruri->url_user || strlen(ruri->url_user) == 0) return -1; break; } if (sip->sip_request->rq_method != sip->sip_cseq->cs_method) return -1; if (sip->sip_request->rq_method == sip_method_unknown && str0casecmp(sip->sip_request->rq_method_name, sip->sip_cseq->cs_method_name)) return -1; } return 0;}/** Decode a string containg header field. * * The header object is initialized with the contents of the string. The * string is modified when parsing. The home is used to allocate extra * memory required when parsing, e.g., for parameter list or when there * string contains multiple header fields. * * @deprecated * Use msg_header_make() or header-specific make functions, e.g., * sip_via_make(). * * @retval 0 when successful * @retval -1 upon an error. */issize_t sip_header_field_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen){ if (h && s && s[slen] == '\0') { size_t n = span_lws(s); s += n; slen -= n; for (n = slen; n >= 1 && IS_LWS(s[n - 1]); n--) ; s[n] = '\0'; assert(SIP_HDR_TEST(h)); return h->sh_class->hc_parse(home, h, s, slen); } else return -1;}/** Encode a SIP header contents. * * @deprecated Use msg_header_field_e() instead. */issize_t sip_header_field_e(char *b, isize_t bsiz, sip_header_t const *h, int flags){ return msg_header_field_e(b, bsiz, h, flags);}/** Convert the header @a h to a string allocated from @a home. */char *sip_header_as_string(su_home_t *home, sip_header_t const *h){ ssize_t len; char *rv, s[128]; if (h == NULL) return NULL; len = sip_header_field_e(s, sizeof(s), h, 0); if (len >= 0 && (size_t)len < sizeof(s)) return su_strdup(home, s); if (len == -1) len = 2 * sizeof(s); else len += 1; for (rv = su_alloc(home, len); rv; rv = su_realloc(home, rv, len)) { ssize_t n = sip_header_field_e(rv, len, h, 0); if (n > -1 && n + 1 <= len) break; if (n > -1) /* glibc >2.1 */ len = n + 1; else /* glibc 2.0 */ len *= 2; } return rv;}/** Calculate size of a SIP header. */isize_t sip_header_size(sip_header_t const *h){ assert(h == NULL || h == SIP_NONE || h->sh_class); if (h == NULL || h == SIP_NONE) return 0; else return h->sh_class->hc_dxtra(h, h->sh_class->hc_size);}/** Duplicate a url or make a url out of string. * @deprecated Use url_hdup() instead. */url_t *sip_url_dup(su_home_t *home, url_t const *o){ return url_hdup(home, o);}/**Calculate Q value. * * Convert q-value string @a q to numeric value * in range (0..1000). Q values are used, for instance, to describe * relative priorities of registered contacts. * * @param q q-value string <code>("1" | "." 1,3DIGIT)</code> * * @return An integer in range 0 .. 1000. */unsigned sip_q_value(char const *q){ unsigned value = 0; if (!q) return 1000; if (q[0] != '0' && q[0] != '.' && q[0] != '1') return 500; /* Garbage... */ while (q[0] == '0') q++; if (q[0] >= '1' && q[0] <= '9') return 1000; if (q[0] == '\0') return 0; if (q[0] != '.') return 500; /* Garbage... */ if (q[1] >= '0' && q[1] <= '9') { value = (q[1] - '0') * 100;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -