📄 outbound.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 outbound.c * @brief Implementation of SIP NAT traversal and outbound * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Kai Vehmanen <Kai.Vehmanen@nokia.com> * @author Martti Mela <Martti Mela@nokia.com> * * @date Created: Wed May 10 12:11:54 EEST 2006 ppessi */#include "config.h"#define NTA_OUTGOING_MAGIC_T struct outbound#include "outbound.h"#include <sofia-sip/hostdomain.h>#include <sofia-sip/sip.h>#include <sofia-sip/sip_protos.h>#include <sofia-sip/sip_util.h>#include <sofia-sip/sip_status.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/tport.h>#include <sofia-sip/nta_tport.h>#include <sofia-sip/su_md5.h>#include <sofia-sip/su_uniqueid.h>#include <sofia-sip/token64.h>#define SU_LOG (nua_log)#include <sofia-sip/su_debug.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <assert.h>struct outbound { su_home_t ob_home[1]; outbound_owner_vtable const *ob_oo; /**< Callbacks */ outbound_owner_t *ob_owner; /**< Backpointer */ su_root_t *ob_root; /**< Root for timers and stuff */ nta_agent_t *ob_nta; /**< SIP transactions */ char ob_cookie[32]; /**< Our magic cookie */ struct outbound_prefs { unsigned interval; /**< Default keepalive interval for datagram */ unsigned stream_interval; /**< Default keepalive interval for streams */ unsigned gruuize:1; /**< Establish a GRUU */ unsigned outbound:1; /**< Try to use outbound */ unsigned natify:1; /**< Try to detect NAT */ unsigned okeepalive:1; /**< Connection keepalive with OPTIONS */ unsigned validate:1; /**< Validate registration with OPTIONS */ /* How to detect NAT binding or connect to outbound: */ unsigned use_connect:1; /**< Use HTTP connect */ unsigned use_rport:1; /**< Use received/rport */ unsigned use_socks:1; /**< Detect and use SOCKS V5 */ unsigned use_upnp:1; /**< Detect and use UPnP */ unsigned use_stun:1; /**< Detect and try to use STUN */ unsigned :0; } ob_prefs; struct outbound_info { /* See enum outbound_feature: */ /* 0 do not support, 1 - perhaps supports, 2 - supports, 3 - requires */ unsigned gruu:2, outbound:2, pref:2; } ob_info; /** Source of Contact header */ unsigned ob_by_stack:1; /** Self-generated contacts */ unsigned ob_contacts:1; /* The registration state machine. */ /** Initial REGISTER containing ob_rcontact has been sent */ unsigned ob_registering:1; /** 2XX response to REGISTER containg ob_rcontact has been received */ unsigned ob_registered:1; /** The registration has been validated: * We have successfully sent OPTIONS to ourselves. */ unsigned ob_validated:1; /** The registration has been validated once. * We have successfully sent OPTIONS to ourselves, so do not give * up if OPTIONS probe fails. */ unsigned ob_once_validated:1; unsigned ob_proxy_override:1; /**< Override stack default proxy */ unsigned :0; url_string_t *ob_proxy; /**< Outbound-specific proxy */ char const *ob_instance; /**< Our instance ID */ int32_t ob_reg_id; /**< Flow-id */ sip_contact_t *ob_rcontact; /**< Our contact */ sip_contact_t *ob_dcontact; /**< Contact for dialogs */ sip_contact_t *ob_previous; /**< Stale contact */ sip_contact_t *ob_gruu; /**< Contact added to requests */ sip_via_t *ob_via; /**< Via header used to generate contacts */ sip_contact_t *ob_obp; /**< Contacts from outbound proxy */ char *ob_nat_detected; /**< Our public address */ char *ob_nat_port; /**< Our public port number */ void *ob_stun; /**< Stun context */ void *ob_upnp; /**< UPnP context */ struct { char *sipstun; /**< Stun server usable for keep-alives */ unsigned interval; /**< Interval. */ su_timer_t *timer; /**< Keep-alive timer */ msg_t *msg; /**< Keep-alive OPTIONS message */ nta_outgoing_t *orq; /**< Keep-alive OPTIONS transaction */ auth_client_t *auc[1]; /**< Authenticator for OPTIONS */ /** Progress of registration validation */ unsigned validating:1, validated:1,:0; } ob_keepalive; /**< Keepalive informatio */};staticint outbound_nat_detect(outbound_t *ob, sip_t const *request, sip_t const *response);/** Return values for outbound_nat_detect(). */enum { ob_nat_error = -1, /* or anything below zero */ ob_no_nat = 0, ob_nat_detected = 1, ob_nat_changed = 2};/* ---------------------------------------------------------------------- */#define SIP_METHOD_UNKNOWN sip_method_unknown, NULL/** Content-Type sent in OPTIONS probing connectivity */char const * const outbound_content_type = "application/vnd.nokia-register-usage";staticint outbound_check_for_nat(outbound_t *ob, sip_t const *request, sip_t const *response);enum outbound_feature { outbound_feature_unsupported = 0, outbound_feature_unsure = 1, outbound_feature_supported = 2, outbound_feature_required = 3};static enum outbound_feature feature_level(sip_t const *sip, char const *tag, int level);static int outbound_contacts_from_via(outbound_t *ob, sip_via_t const *via);/* ---------------------------------------------------------------------- *//** Create a new outbound object */outbound_t *outbound_new(outbound_owner_t *owner, outbound_owner_vtable const *owner_methods, su_root_t *root, nta_agent_t *agent, char const *instance){ outbound_t *ob; if (!owner || !owner_methods || !root || !agent) return NULL; ob = su_home_clone((su_home_t *)owner, sizeof *ob); if (ob) { su_md5_t md5[1]; uint8_t digest[SU_MD5_DIGEST_SIZE]; su_guid_t guid[1]; ob->ob_owner = owner; ob->ob_oo = owner_methods; ob->ob_root = root; ob->ob_nta = agent; if (instance) ob->ob_instance = su_sprintf(ob->ob_home, "+sip.instance=\"<%s>\"", instance); ob->ob_reg_id = 0; /* Generate a random cookie (used as Call-ID) for us */ su_md5_init(md5); su_guid_generate(guid); if (instance) su_md5_update(md5, (void *)instance, strlen(instance)); su_md5_update(md5, (void *)guid, sizeof guid); su_md5_digest(md5, digest); token64_e(ob->ob_cookie, sizeof ob->ob_cookie, digest, sizeof digest); if (instance && !ob->ob_instance) su_home_unref(ob->ob_home), ob = NULL; } return ob;}void outbound_unref(outbound_t *ob){ if (ob->ob_keepalive.timer) su_timer_destroy(ob->ob_keepalive.timer), ob->ob_keepalive.timer = NULL; if (ob->ob_keepalive.orq) nta_outgoing_destroy(ob->ob_keepalive.orq), ob->ob_keepalive.orq = NULL; if (ob->ob_keepalive.msg) msg_destroy(ob->ob_keepalive.msg), ob->ob_keepalive.msg = NULL; su_home_unref(ob->ob_home);}#include <sofia-sip/bnf.h>/** Set various outbound and nat-traversal related options. */int outbound_set_options(outbound_t *ob, char const *_options, unsigned interval, unsigned stream_interval){ struct outbound_prefs prefs[1] = {{ 0 }}; char *s, *options = su_strdup(NULL, _options); int invalid; prefs->interval = interval; prefs->stream_interval = stream_interval;#define MATCH(v) (len == sizeof(#v) - 1 && strncasecmp(#v, s, len) == 0) if (options) { for (s = options; s[0]; s++) if (s[0] == '-') s[0] = '_'; } prefs->gruuize = 1; prefs->outbound = 0; prefs->natify = 1; prefs->okeepalive = 1; prefs->validate = 1; prefs->use_rport = 1; for (s = options; s && s[0]; ) { size_t len = span_token(s); int value = 1; if (len > 3 && strncasecmp(s, "no_", 3) == 0) value = 0, s += 3, len -= 3; else if (len > 4 && strncasecmp(s, "not_", 4) == 0) value = 0, s += 4, len -= 4; if (len == 0) break; else if (MATCH(gruuize)) prefs->gruuize = value; else if (MATCH(outbound)) prefs->outbound = value; else if (MATCH(natify)) prefs->natify = value; else if (MATCH(validate)) prefs->validate = value; else if (MATCH(options_keepalive)) prefs->okeepalive = value; else if (MATCH(use_connect)) prefs->use_connect = value; else if (MATCH(use_rport)) prefs->use_rport = value; else if (MATCH(use_socks)) prefs->use_socks = value; else if (MATCH(use_upnp)) prefs->use_upnp = value; else if (MATCH(use_stun)) prefs->use_stun = value; else SU_DEBUG_1(("outbound(%p): unknown option \"%.*s\"\n", (void *)ob->ob_owner, (int)len, s)); s += len; len = strspn(s, " \t\n\r,;"); if (len == 0) break; s += len; } invalid = s && s[0]; su_free(NULL, options); if (invalid) { SU_DEBUG_1(("outbound(%p): invalid options \"%s\"\n", (void *)ob->ob_owner, options)); return -1; } if (prefs->natify && !(prefs->outbound || prefs->use_connect || prefs->use_rport || prefs->use_socks || prefs->use_upnp || prefs->use_stun)) { SU_DEBUG_1(("outbound(%p): no nat traversal method given\n", (void *)ob->ob_owner)); } ob->ob_prefs = *prefs; ob->ob_reg_id = prefs->outbound ? 1 : 0; return 0;}/** Override stack default proxy for outbound */int outbound_set_proxy(outbound_t *ob, url_string_t *proxy){ url_string_t *new_proxy = NULL, *old_proxy = ob->ob_proxy; if (proxy) new_proxy = (url_string_t *)url_as_string(ob->ob_home, proxy->us_url); if (proxy == NULL || new_proxy != NULL) { ob->ob_proxy_override = 1; ob->ob_proxy = new_proxy; su_free(ob->ob_home, old_proxy); return 0; } return -1;}/* ---------------------------------------------------------------------- *//** Obtain contacts for REGISTER */int outbound_get_contacts(outbound_t *ob, sip_contact_t **return_current_contact, sip_contact_t **return_previous_contact){ if (ob) { if (ob->ob_contacts) *return_current_contact = ob->ob_rcontact; else { sip_contact_t *contact = *return_current_contact; if (contact) { if (ob->ob_rcontact) msg_header_free_all(ob->ob_home, (msg_header_t*)ob->ob_rcontact); ob->ob_rcontact = sip_contact_dup(ob->ob_home, contact); } } *return_previous_contact = ob->ob_previous; } return 0;}/** REGISTER request has been sent */int outbound_start_registering(outbound_t *ob){ if (ob) ob->ob_registering = 1; return 0;}/** Process response to REGISTER request */int outbound_register_response(outbound_t *ob, int terminating, sip_t const *request, sip_t const *response){ int status, reregister; if (!ob) return 0; if (terminating) { ob->ob_registering = ob->ob_registered = 0; return 0; /* Cleanup is done separately */ } if (!response || !request) return 0; assert(request->sip_request); assert(response->sip_status); reregister = outbound_check_for_nat(ob, request, response); if (reregister) return reregister; status = response->sip_status->st_status; if (status < 300) { if (request->sip_contact && response->sip_contact) ob->ob_registered = ob->ob_registering; else ob->ob_registered = 0; if (ob->ob_previous) msg_header_free(ob->ob_home, (void *)ob->ob_previous); ob->ob_previous = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -