Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		*/
    		peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (peer) {
    
    		/* Already in the list, remove it and it will be added back (or FREE'd)  */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		found++;
    
    		if (!(peer->objflags & ASTOBJ_FLAG_MARKED))
    			firstpass = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
     	} else {
    
    		if (!(peer = ast_calloc(1, sizeof(*peer))))
    
    			ast_debug(3,"-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
    
    	/* Note that our peer HAS had its reference count incrased */
    
    	if (firstpass) {
    		peer->lastmsgssent = -1;
    		oldha = peer->ha;
    		peer->ha = NULL;
    		set_peer_defaults(peer);	/* Set peer defaults */
    	}
    
    			ast_copy_string(peer->name, name, sizeof(peer->name));
    
    	/* If we have channel variables, remove them (reload) */
    	if (peer->chanvars) {
    		ast_variables_destroy(peer->chanvars);
    		peer->chanvars = NULL;
    
    		/* XXX should unregister ? */
    
    
    	/* If we have realm authentication information, remove them (reload) */
    	clear_realm_authentication(peer->auth);
    	peer->auth = NULL;
    
    
    	for (; v || ((v = alt) && !(alt=NULL)); v = v->next) {
    
    		if (handle_common_options(&peerflags[0], &mask[0], v))
    
    			continue;
    		if (realtime && !strcasecmp(v->name, "regseconds")) {
    
    			ast_get_time_t(v->value, &regseconds, 0, NULL);
    
    		} else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) {
    			inet_aton(v->value, &(peer->addr.sin_addr));
    		} else if (realtime && !strcasecmp(v->name, "name"))
    			ast_copy_string(peer->name, v->value, sizeof(peer->name));
    
    		else if (realtime && !strcasecmp(v->name, "fullcontact")) {
    			ast_copy_string(peer->fullcontact, v->value, sizeof(peer->fullcontact));
    
    			peer->rt_fromcontact = TRUE;
    
    		} else if (!strcasecmp(v->name, "secret")) 
    
    			ast_copy_string(peer->secret, v->value, sizeof(peer->secret));
    		else if (!strcasecmp(v->name, "md5secret")) 
    			ast_copy_string(peer->md5secret, v->value, sizeof(peer->md5secret));
    		else if (!strcasecmp(v->name, "auth"))
    			peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno);
    		else if (!strcasecmp(v->name, "callerid")) {
    			ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num));
    
    		} else if (!strcasecmp(v->name, "fullname")) {
    			ast_copy_string(peer->cid_name, v->value, sizeof(peer->cid_name));
    		} else if (!strcasecmp(v->name, "cid_number")) {
    			ast_copy_string(peer->cid_num, v->value, sizeof(peer->cid_num));
    
    		} else if (!strcasecmp(v->name, "context")) {
    
    			ast_copy_string(peer->context, v->value, sizeof(peer->context));
    
    		} else if (!strcasecmp(v->name, "subscribecontext")) {
    			ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext));
    
    		} else if (!strcasecmp(v->name, "fromdomain")) {
    
    			ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain));
    
    		} else if (!strcasecmp(v->name, "usereqphone")) {
    			ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE);
    		} else if (!strcasecmp(v->name, "fromuser")) {
    
    			ast_copy_string(peer->fromuser, v->value, sizeof(peer->fromuser));
    
    		} else if (!strcasecmp(v->name, "outboundproxy")) {
    			char *port, *next, *force, *proxyname;
    			int forceopt = FALSE;
    			/* Set peer channel variable */
    			next = proxyname = ast_strdupa(v->value);
    			if ((port = strchr(proxyname, ':'))) {
    				*port++ = '\0';
    				next = port;
    			}
    			if ((force = strchr(next, ','))) {
    				*force++ = '\0';
    				forceopt = strcmp(force, "force");
    			}
    			/* Allocate proxy object */
    			peer->outboundproxy = proxy_allocate(proxyname, port, forceopt);
    		} else if (!strcasecmp(v->name, "host")) {
    
    			if (!strcasecmp(v->value, "dynamic")) {
    
    				/* They'll register with us */
    
    				if (!found || !peer->host_dynamic) {
    
    					/* Initialize stuff if this is a new peer, or if it used to
    					 * not be dynamic before the reload. */
    
    					memset(&peer->addr.sin_addr, 0, 4);
    					if (peer->addr.sin_port) {
    						/* If we've already got a port, make it the default rather than absolute */
    						peer->defaddr.sin_port = peer->addr.sin_port;
    						peer->addr.sin_port = 0;
    
    				peer->host_dynamic = TRUE;
    
    			} else {
    				/* Non-dynamic.  Make sure we become that way if we're not */
    				if (peer->expire > -1)
    					ast_sched_del(sched, peer->expire);
    				peer->expire = -1;
    
    				peer->host_dynamic = FALSE;
    
    				if (ast_get_ip_or_srv(&peer->addr, v->value, global_srvlookup ? "_sip._udp" : NULL)) {
    					unref_peer(peer);
    					return NULL;
    				}
    
    
    				ast_copy_string(peer->tohost, v->value, sizeof(peer->tohost));
    				if (!peer->addr.sin_port)
    					peer->addr.sin_port = htons(STANDARD_SIP_PORT);
    
    			}
    		} else if (!strcasecmp(v->name, "defaultip")) {
    			if (ast_get_ip(&peer->defaddr, v->value)) {
    
    				return NULL;
    			}
    		} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
    
    			int ha_error = 0;
    
    			peer->ha = ast_append_ha(v->name, v->value, peer->ha, &ha_error);
    			if (ha_error)
    				ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
    
    		} else if (!strcasecmp(v->name, "port")) {
    
    			if (!realtime && peer->host_dynamic)
    
    				peer->defaddr.sin_port = htons(atoi(v->value));
    			else
    				peer->addr.sin_port = htons(atoi(v->value));
    		} else if (!strcasecmp(v->name, "callingpres")) {
    			peer->callingpres = ast_parse_caller_presentation(v->value);
    			if (peer->callingpres == -1)
    				peer->callingpres = atoi(v->value);
    
    		} else if (!strcasecmp(v->name, "username") | !strcmp(v->name, "defaultuser")) {	/* "username" is deprecated */
    
    			ast_copy_string(peer->username, v->value, sizeof(peer->username));
    		} else if (!strcasecmp(v->name, "language")) {
    			ast_copy_string(peer->language, v->value, sizeof(peer->language));
    		} else if (!strcasecmp(v->name, "regexten")) {
    			ast_copy_string(peer->regexten, v->value, sizeof(peer->regexten));
    
    		} else if (!strcasecmp(v->name, "callbackextension")) {
    			ast_copy_string(callback, v->value, sizeof(callback));
    
    		} else if (!strcasecmp(v->name, "callcounter")) {
    
    			peer->call_limit = ast_true(v->value) ? 999 : 0;
    
    		} else if (!strcasecmp(v->name, "call-limit")) {
    
    			peer->call_limit = atoi(v->value);
    			if (peer->call_limit < 0)
    				peer->call_limit = 0;
    
    		} else if (!strcasecmp(v->name, "busylevel")) {
    
    			peer->busy_level = atoi(v->value);
    			if (peer->busy_level < 0)
    				peer->busy_level = 0;
    
    		} else if (!strcasecmp(v->name, "amaflags")) {
    			format = ast_cdr_amaflags2int(v->value);
    			if (format < 0) {
    				ast_log(LOG_WARNING, "Invalid AMA Flags for peer: %s at line %d\n", v->value, v->lineno);
    			} else {
    				peer->amaflags = format;
    			}
    		} else if (!strcasecmp(v->name, "accountcode")) {
    			ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode));
    
    		} else if (!strcasecmp(v->name, "mohinterpret")) {
    
    			ast_copy_string(peer->mohinterpret, v->value, sizeof(peer->mohinterpret));
    		} else if (!strcasecmp(v->name, "mohsuggest")) {
    			ast_copy_string(peer->mohsuggest, v->value, sizeof(peer->mohsuggest));
    
    		} else if (!strcasecmp(v->name, "mailbox")) {
    
    		} else if (!strcasecmp(v->name, "subscribemwi")) {
    			ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SUBSCRIBEMWIONLY);
    
    		} else if (!strcasecmp(v->name, "vmexten")) {
    			ast_copy_string(peer->vmexten, v->value, sizeof(peer->vmexten));
    
    		} else if (!strcasecmp(v->name, "callgroup")) {
    			peer->callgroup = ast_get_group(v->value);
    
    		} else if (!strcasecmp(v->name, "allowtransfer")) {
    			peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
    
    		} else if (!strcasecmp(v->name, "pickupgroup")) {
    			peer->pickupgroup = ast_get_group(v->value);
    		} else if (!strcasecmp(v->name, "allow")) {
    
    			int error =  ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, TRUE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    		} else if (!strcasecmp(v->name, "disallow")) {
    
    			int error =  ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, FALSE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    Joshua Colp's avatar
    Joshua Colp committed
    		} else if (!strcasecmp(v->name, "registertrying")) {
    			ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_REGISTERTRYING);
    
    		} else if (!strcasecmp(v->name, "autoframing")) {
    			peer->autoframing = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "rtptimeout")) {
    			if ((sscanf(v->value, "%d", &peer->rtptimeout) != 1) || (peer->rtptimeout < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtptimeout = global_rtptimeout;
    			}
    		} else if (!strcasecmp(v->name, "rtpholdtimeout")) {
    			if ((sscanf(v->value, "%d", &peer->rtpholdtimeout) != 1) || (peer->rtpholdtimeout < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtpholdtimeout = global_rtpholdtimeout;
    			}
    		} else if (!strcasecmp(v->name, "rtpkeepalive")) {
    			if ((sscanf(v->value, "%d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtpkeepalive = global_rtpkeepalive;
    			}
    
          } else if (!strcasecmp(v->name, "timert1")) {
             if ((sscanf(v->value, "%d", &peer->timer_t1) != 1) || (peer->timer_t1 < 0)) {
                ast_log(LOG_WARNING, "'%s' is not a valid T1 time at line %d.  Using default.\n", v->value, v->lineno);
                peer->timer_t1 = global_t1;
             }
          } else if (!strcasecmp(v->name, "timerb")) {
             if ((sscanf(v->value, "%d", &peer->timer_b) != 1) || (peer->timer_b < 0)) {
                ast_log(LOG_WARNING, "'%s' is not a valid Timer B time at line %d.  Using default.\n", v->value, v->lineno);
                peer->timer_b = global_timer_b;
             }
    
    		} else if (!strcasecmp(v->name, "setvar")) {
    
    			peer->chanvars = add_var(v->value, peer->chanvars);
    
    		} else if (!strcasecmp(v->name, "qualify")) {
    			if (!strcasecmp(v->value, "no")) {
    				peer->maxms = 0;
    			} else if (!strcasecmp(v->value, "yes")) {
    
    				peer->maxms = default_qualify ? default_qualify : DEFAULT_MAXMS;
    
    			} else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
    				ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
    				peer->maxms = 0;
    
    		} else if (!strcasecmp(v->name, "qualifyfreq")) {
    			int i;
    			if (sscanf(v->value, "%d", &i) == 1)
    				peer->qualifyfreq = i * 1000;  
    			else {
    				ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n",v->value, v->lineno, config);
    				peer->qualifyfreq = global_qualifyfreq;
    			}
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!strcasecmp(v->name, "maxcallbitrate")) {
    			peer->maxcallbitrate = atoi(v->value);
    			if (peer->maxcallbitrate < 0)
    				peer->maxcallbitrate = default_maxcallbitrate;
    
    	if (!sip_cfg.ignore_regexpire && peer->host_dynamic && realtime) {
    
    
    		if ((nowtime - regseconds) > 0) {
    
    			memset(&peer->addr, 0, sizeof(peer->addr));
    
    			ast_debug(1, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
    
    	ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags);
    	ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags);
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
    		global_allowsubscribe = TRUE;	/* No global ban any more */
    
    	if (!found && peer->host_dynamic && !peer->is_realtime)
    
    		reg_source_db(peer);
    
    
    	/* If they didn't request that MWI is sent *only* on subscribe, go ahead and
    	 * subscribe to it now. */
    	if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) && 
    
    		!AST_LIST_EMPTY(&peer->mailboxes)) {
    		add_peer_mwi_subs(peer);
    
    		/* Send MWI from the event cache only.  This is so we can send initial
    		 * MWI if app_voicemail got loaded before chan_sip.  If it is the other
    		 * way, then we will get events when app_voicemail gets loaded. */
    		sip_send_mwi_to_peer(peer, NULL, 1);
    	}
    
    
    	ASTOBJ_UNMARK(peer);
    
    	ast_free_ha(oldha);
    
    	if (!ast_strlen_zero(callback)) { /* build string from peer info */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		char *reg_string;
    
    
    		asprintf(&reg_string, "%s:%s@%s/%s", peer->username, peer->secret, peer->tohost, callback);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (reg_string) {
    			sip_register(reg_string, 0); /* XXX TODO: count in registry_count */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    }
    
    
    /*! \brief Re-read SIP.conf config file
    \note	This function reloads all config data, except for
    
    	active peers (with registrations). They will only
    	change configuration data at restart, not at reload.
    	SIP debug and recordhistory state will not change
     */
    
    static int reload_config(enum channelreloadreason reason)
    
    	struct ast_config *cfg, *ucfg;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_variable *v;
    	struct sip_peer *peer;
    	struct sip_user *user;
    
    	char *cat, *stringp, *context, *oldregcontext;
    	char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
    
    	struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : CONFIG_FLAG_FILEUNCHANGED };
    
    	int auto_sip_domains = FALSE;
    
    	struct sockaddr_in old_bindaddr = bindaddr;
    
    	int registry_count = 0, peer_count = 0, user_count = 0;
    
    	cfg = ast_config_load(config, config_flags);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* We *must* have a config file otherwise stop immediately */
    	if (!cfg) {
    
    		ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
    		return -1;
    
    	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
    		ucfg = ast_config_load("users.conf", config_flags);
    		if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
    
    		/* Must reread both files, because one changed */
    		ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
    		cfg = ast_config_load(config, config_flags);
    	} else {
    		ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
    		ucfg = ast_config_load("users.conf", config_flags);
    
    
    	if (reason != CHANNEL_MODULE_LOAD) {
    		ast_debug(4, "--------------- SIP reload started\n");
    
    		clear_realm_authentication(authl);
    		clear_sip_domains();
    		authl = NULL;
    
    		/* First, destroy all outstanding registry calls */
    		/* This is needed, since otherwise active registry entries will not be destroyed */
    		ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
    			ASTOBJ_RDLOCK(iterator);
    			if (iterator->call) {
    				ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
    				/* This will also remove references to the registry */
    				iterator->call = sip_destroy(iterator->call);
    			}
    			ASTOBJ_UNLOCK(iterator);
    
    		} while(0));
    
    		/* Then, actually destroy users and registry */
    		ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
    		ast_debug(4, "--------------- Done destroying user list\n");
    		ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
    		ast_debug(4, "--------------- Done destroying registry list\n");
    		ASTOBJ_CONTAINER_MARKALL(&peerl);
    	}
    
    
    	/* Initialize copy of current global_regcontext for later use in removing stale contexts */
    	ast_copy_string(oldcontexts, global_regcontext, sizeof(oldcontexts));
    	oldregcontext = oldcontexts;
    
    
    	/* Clear all flags before setting default values */
    
    	/* Preserve debugging settings for console */
    
    	sipdebug &= sip_debug_console;
    
    	ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
    	ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
    
    	/* Reset IP addresses  */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(&bindaddr, 0, sizeof(bindaddr));
    
    	memset(&stunaddr, 0, sizeof(stunaddr));
    	memset(&internip, 0, sizeof(internip));
    	/* Free memory for local network address mask */
    	ast_free_ha(localaddr);
    
    	memset(&localaddr, 0, sizeof(localaddr));
    
    	memset(&externip, 0, sizeof(externip));
    
    	memset(&default_prefs, 0 , sizeof(default_prefs));
    
    	memset(&global_outboundproxy, 0, sizeof(struct sip_proxy));
    	global_outboundproxy.ip.sin_port = htons(STANDARD_SIP_PORT);
    	global_outboundproxy.ip.sin_family = AF_INET;	/* Type of address: IPv4 */
    
    	bindaddr.sin_port = htons(STANDARD_SIP_PORT);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	externip.sin_port = htons(STANDARD_SIP_PORT);
    
    	global_srvlookup = DEFAULT_SRVLOOKUP;
    
    	global_tos_sip = DEFAULT_TOS_SIP;
    	global_tos_audio = DEFAULT_TOS_AUDIO;
    	global_tos_video = DEFAULT_TOS_VIDEO;
    
    	global_tos_text = DEFAULT_TOS_TEXT;
    
    	global_cos_sip = DEFAULT_COS_SIP;
    	global_cos_audio = DEFAULT_COS_AUDIO;
    	global_cos_video = DEFAULT_COS_VIDEO;
    	global_cos_text = DEFAULT_COS_TEXT;
    
    
    	externhost[0] = '\0';			/* External host name (for behind NAT DynDNS support) */
    	externexpire = 0;			/* Expiration for DNS re-issuing */
    
    
    	/* Reset channel settings to default before re-configuring */
    	allow_external_domains = DEFAULT_ALLOW_EXT_DOM;				/* Allow external invites */
    
    	global_regextenonqualify = DEFAULT_REGEXTENONQUALIFY;
    
    	expiry = DEFAULT_EXPIRY;
    	global_notifyringing = DEFAULT_NOTIFYRINGING;
    
    	global_limitonpeers = FALSE;		/*!< Match call limit on peers only */
    
    	global_notifyhold = FALSE;		/*!< Keep track of hold status for a peer */
    
    	global_directrtpsetup = FALSE;		/* Experimental feature, disabled by default */
    
    	global_allowsubscribe = FALSE;
    
    	snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
    	snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
    
    	snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
    
    	ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
    
    	ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
    
    	ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
    
    	compactheaders = DEFAULT_COMPACTHEADERS;
    	global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
    	global_regattempts_max = 0;
    	pedanticsipchecking = DEFAULT_PEDANTIC;
    	autocreatepeer = DEFAULT_AUTOCREATEPEER;
    
    	global_allowguest = DEFAULT_ALLOWGUEST;
    
    	global_match_auth_username = FALSE;		/*!< Match auth username if available instead of From: Default off. */
    
    	global_rtptimeout = 0;
    	global_rtpholdtimeout = 0;
    
    	global_rtpkeepalive = 0;
    
    	global_allowtransfer = TRANSFER_OPENFORALL;	/* Merrily accept all transfers by default */
    
    	ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE);	/* Default for peers, users: TRUE */
    	ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP);		/* Default for peers, users: TRUE */
    
    	sip_cfg.peer_rtupdate = TRUE;
    
    
    	/* Initialize some reasonable defaults at SIP reload (used both for channel and as default for peers and users */
    	ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context));
    	default_subscribecontext[0] = '\0';
    	default_language[0] = '\0';
    	default_fromdomain[0] = '\0';
    	default_qualify = DEFAULT_QUALIFY;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
    
    	ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
    	ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
    
    	ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
    
    	ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);			/*!< Default DTMF setting: RFC2833 */
    	ast_set_flag(&global_flags[0], SIP_NAT_RFC3581);			/*!< NAT support if requested by device with rport */
    	ast_set_flag(&global_flags[0], SIP_CAN_REINVITE);			/*!< Allow re-invites */
    
    
    	/* Debugging settings, always default to off */
    	dumphistory = FALSE;
    	recordhistory = FALSE;
    
    	sipdebug &= ~sip_debug_config;
    
    
    	/* Misc settings for the channel */
    
    	global_relaxdtmf = FALSE;
    	global_callevents = FALSE;
    
    	global_t1 = SIP_TIMER_T1;
    	global_timer_b = 64 * SIP_TIMER_T1;
    
    	global_t1min = DEFAULT_T1MIN;
    	global_qualifyfreq = DEFAULT_QUALIFYFREQ;
    
    	global_matchexterniplocally = FALSE;
    
    
    	/* Copy the default jb config over global_jbconf */
    	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
    
    
    	ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT);
    
    	ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT);
    
    
    	/* Read the [general] config section of sip.conf (or from realtime config) */
    
    	for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
    
    		if (handle_common_options(&global_flags[0], &dummy[0], v))
    
    		/* handle jb conf */
    		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
    			continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(v->name, "context")) {
    
    			ast_copy_string(default_context, v->value, sizeof(default_context));
    
    		} else if (!strcasecmp(v->name, "subscribecontext")) {
    			ast_copy_string(default_subscribecontext, v->value, sizeof(default_subscribecontext));
    
      		} else if (!strcasecmp(v->name, "callcounter")) {
    			global_callcounter = ast_true(v->value) ? 1 : 0;
    
      		} else if (!strcasecmp(v->name, "allowguest")) {
    
    			global_allowguest = ast_true(v->value) ? 1 : 0;
    
    		} else if (!strcasecmp(v->name, "realm")) {
    
    			ast_copy_string(global_realm, v->value, sizeof(global_realm));
    
    		} else if (!strcasecmp(v->name, "useragent")) {
    
    			ast_copy_string(global_useragent, v->value, sizeof(global_useragent));
    
    			ast_debug(1, "Setting SIP channel User-Agent Name to %s\n", global_useragent);
    
    		} else if (!strcasecmp(v->name, "sdpsession")) {
    			ast_copy_string(global_sdpsession, v->value, sizeof(global_sdpsession));
    		} else if (!strcasecmp(v->name, "sdpowner")) {
    			/* Field cannot contain spaces */
    			if (!strstr(v->value, " "))
    				ast_copy_string(global_sdpowner, v->value, sizeof(global_sdpowner));
    			else
    				ast_log(LOG_WARNING, "'%s' must not contain spaces at line %d.  Using default.\n", v->value, v->lineno);
    
    		} else if (!strcasecmp(v->name, "allowtransfer")) {
    			global_allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
    
    		} else if (!strcasecmp(v->name, "rtcachefriends")) {
    
    			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, "rtupdate")) {
    
    			sip_cfg.peer_rtupdate = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "ignoreregexpire")) {
    
    			sip_cfg.ignore_regexpire = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "t1min")) {
    			global_t1min = atoi(v->value);
    
    		} else if (!strcasecmp(v->name, "rtautoclear")) {
    			int i = atoi(v->value);
    
    				global_rtautoclear = i;
    			else
    				i = 0;
    
    			ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR);
    
    		} else if (!strcasecmp(v->name, "usereqphone")) {
    
    			ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE);	
    
    		} else if (!strcasecmp(v->name, "relaxdtmf")) {
    
    			global_relaxdtmf = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "vmexten")) {
    
    			ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten));
    
    		} else if (!strcasecmp(v->name, "rtptimeout")) {
    
    			if ((sscanf(v->value, "%d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    
    			}
    		} else if (!strcasecmp(v->name, "rtpholdtimeout")) {
    
    			if ((sscanf(v->value, "%d", &global_rtpholdtimeout) != 1) || (global_rtpholdtimeout < 0)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    
    		} else if (!strcasecmp(v->name, "rtpkeepalive")) {
    			if ((sscanf(v->value, "%d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
    				global_rtpkeepalive = 0;
    			}
    
    		} else if (!strcasecmp(v->name, "compactheaders")) {
    			compactheaders = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "notifymimetype")) {
    
    			ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
    
    		} else if (!strncasecmp(v->name, "limitonpeer", 11) || !strcasecmp(v->name, "counteronpeer")) {
    
    		} else if (!strcasecmp(v->name, "directrtpsetup")) {
    			global_directrtpsetup = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "notifyringing")) {
    			global_notifyringing = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "notifyhold")) {
    			global_notifyhold = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "alwaysauthreject")) {
    			global_alwaysauthreject = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "mohinterpret")) {
    
    			ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
    		} else if (!strcasecmp(v->name, "mohsuggest")) {
    			ast_copy_string(default_mohsuggest, v->value, sizeof(default_mohsuggest));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "language")) {
    
    			ast_copy_string(default_language, v->value, sizeof(default_language));
    
    		} else if (!strcasecmp(v->name, "regcontext")) {
    
    			ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
    			stringp = newcontexts;
    			/* Let's remove any contexts that are no longer defined in regcontext */
    			cleanup_stale_contexts(stringp, oldregcontext);
    			/* Create contexts if they don't exist already */
    			while ((context = strsep(&stringp, "&"))) {
    
    				ast_copy_string(used_context, context, sizeof(used_context));
    
    				if (!ast_context_find(context))
    					ast_context_create(NULL, context,"SIP");
    			}
    
    			ast_copy_string(global_regcontext, v->value, sizeof(global_regcontext));
    
    		} else if (!strcasecmp(v->name, "regextenonqualify")) {
    			global_regextenonqualify = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "callerid")) {
    
    			ast_copy_string(default_callerid, v->value, sizeof(default_callerid));
    
    		} else if (!strcasecmp(v->name, "fromdomain")) {
    
    			ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain));
    
    		} else if (!strcasecmp(v->name, "outboundproxy")) {
    
    			char *name, *port = NULL, *force;
    
    			name = ast_strdupa(v->value);
    			if ((port = strchr(name, ':'))) {
    				*port++ = '\0';
    				global_outboundproxy.ip.sin_port = htons(atoi(port));
    			}
    
    			if ((force = strchr(port ? port : name, ','))) {
    				*force++ = '\0';
    				global_outboundproxy.force = (!strcasecmp(force, "force"));
    			}
    			ast_copy_string(global_outboundproxy.name, name, sizeof(global_outboundproxy.name));
    			proxy_update(&global_outboundproxy);
    
    
    		} else if (!strcasecmp(v->name, "autocreatepeer")) {
    			autocreatepeer = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "match_auth_username")) {
    
    		} else if (!strcasecmp(v->name, "srvlookup")) {
    
    			global_srvlookup = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "pedantic")) {
    			pedanticsipchecking = ast_true(v->value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) {
    			max_expiry = atoi(v->value);
    			if (max_expiry < 1)
    				max_expiry = DEFAULT_MAX_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "minexpirey") || !strcasecmp(v->name, "minexpiry")) {
    			min_expiry = atoi(v->value);
    			if (min_expiry < 1)
    				min_expiry = DEFAULT_MIN_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "defaultexpiry") || !strcasecmp(v->name, "defaultexpirey")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			default_expiry = atoi(v->value);
    			if (default_expiry < 1)
    				default_expiry = DEFAULT_DEFAULT_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "sipdebug")) {
    
    			if (ast_true(v->value))
    
    				sipdebug |= sip_debug_config;
    
    		} else if (!strcasecmp(v->name, "dumphistory")) {
    			dumphistory = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "recordhistory")) {
    			recordhistory = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "registertimeout")) {
    
    			global_reg_timeout = atoi(v->value);
    			if (global_reg_timeout < 1)
    				global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
    
    		} else if (!strcasecmp(v->name, "registerattempts")) {
    
    		} else if (!strcasecmp(v->name, "stunaddr")) {
    			stunaddr.sin_port = htons(3478);
    			if (ast_parse_arg(v->value, PARSE_INADDR, &stunaddr))
    				ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "bindaddr")) {
    
    			if (ast_parse_arg(v->value, PARSE_INADDR, &bindaddr))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
    
    		} else if (!strcasecmp(v->name, "localnet")) {
    
    
    			if (!(na = ast_append_ha("d", v->value, localaddr, &ha_error)))
    
    				ast_log(LOG_WARNING, "Invalid localnet value: %s\n", v->value);
    
    			if (ha_error)
    				ast_log(LOG_ERROR, "Bad localnet configuration value line %d : %s\n", v->lineno, v->value);
    
    			if (ast_parse_arg(v->value, PARSE_INADDR, &externip))
    
    				ast_log(LOG_WARNING, "Invalid address for externip keyword: %s\n", v->value);
    
    			externexpire = 0;
    		} else if (!strcasecmp(v->name, "externhost")) {
    
    			ast_copy_string(externhost, v->value, sizeof(externhost));
    
    			if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
    
    				ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost);
    
    		} else if (!strcasecmp(v->name, "externrefresh")) {
    
    				ast_log(LOG_WARNING, "Invalid externrefresh value '%s', must be an integer >0 at line %d\n", v->value, v->lineno);
    				externrefresh = 10;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "allow")) {
    
    			int error =  ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, TRUE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "disallow")) {
    
    			int error =  ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, FALSE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    		} else if (!strcasecmp(v->name, "autoframing")) {
    			global_autoframing = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "allowexternaldomains")) {
    			allow_external_domains = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "autodomain")) {
    			auto_sip_domains = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "domain")) {
    			char *domain = ast_strdupa(v->value);
    			char *context = strchr(domain, ',');
    
    			if (context)
    				*context++ = '\0';
    
    
    			if (ast_strlen_zero(context))
    				ast_debug(1, "No context specified at line %d for domain '%s'\n", v->lineno, domain);
    
    			if (ast_strlen_zero(domain))
    				ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno);
    			else
    				add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, context ? ast_strip(context) : "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "register")) {
    
    			if (sip_register(v->value, v->lineno) == 0)
    				registry_count++;
    
    		} else if (!strcasecmp(v->name, "tos_sip")) {
    			if (ast_str2tos(v->value, &global_tos_sip))
    
    				ast_log(LOG_WARNING, "Invalid tos_sip value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_audio")) {
    			if (ast_str2tos(v->value, &global_tos_audio))
    
    				ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_video")) {
    			if (ast_str2tos(v->value, &global_tos_video))
    
    				ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_text")) {
    			if (ast_str2tos(v->value, &global_tos_text))
    
    				ast_log(LOG_WARNING, "Invalid tos_text value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "cos_sip")) {
    
    			if (ast_str2cos(v->value, &global_cos_sip))
    				ast_log(LOG_WARNING, "Invalid cos_sip value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "cos_audio")) {
    
    			if (ast_str2cos(v->value, &global_cos_audio))
    				ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "cos_video")) {
    
    			if (ast_str2cos(v->value, &global_cos_video))
    				ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "cos_text")) {
    
    			if (ast_str2cos(v->value, &global_cos_text))
    				ast_log(LOG_WARNING, "Invalid cos_text value at line %d, refer to QoS documentation\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "bindport")) {
    
    			int i;
    			if (sscanf(v->value, "%d", &i) == 1) {
    				bindaddr.sin_port = htons(i);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
    			}
    
    		} else if (!strcasecmp(v->name, "qualify")) {
    			if (!strcasecmp(v->value, "no")) {
    				default_qualify = 0;
    			} else if (!strcasecmp(v->value, "yes")) {
    				default_qualify = DEFAULT_MAXMS;
    			} else if (sscanf(v->value, "%d", &default_qualify) != 1) {
    				ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
    				default_qualify = 0;
    			}
    
    		} else if (!strcasecmp(v->name, "qualifyfreq")) {
    			int i;
    			if (sscanf(v->value, "%d", &i) == 1)
    				global_qualifyfreq = i * 1000;
    			else {
    				ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n", v->value, v->lineno, config);
    				global_qualifyfreq = DEFAULT_QUALIFYFREQ;
    			}
    
    		} else if (!strcasecmp(v->name, "callevents")) {
    
    			global_callevents = ast_true(v->value);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!strcasecmp(v->name, "maxcallbitrate")) {
    			default_maxcallbitrate = atoi(v->value);
    			if (default_maxcallbitrate < 0)
    				default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
    
    		} else if (!strcasecmp(v->name, "matchexterniplocally")) {
    			global_matchexterniplocally = ast_true(v->value);
    
    	if (!allow_external_domains && AST_LIST_EMPTY(&domain_list)) {
    		ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
    		allow_external_domains = 1;
    
    	/* Build list of authentication to various SIP realms, i.e. service providers */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     	for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
    
     		/* Format for authentication is auth = username:password@realm */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     		if (!strcasecmp(v->name, "auth"))
    
     			authl = add_realm_authentication(authl, v->value, v->lineno);
     	}
    	
    
    	if (ucfg) {
    		struct ast_variable *gen;
    		int genhassip, genregistersip;
    
    		
    		genhassip = ast_true(ast_variable_retrieve(ucfg, "general", "hassip"));
    		genregistersip = ast_true(ast_variable_retrieve(ucfg, "general", "registersip"));
    		gen = ast_variable_browse(ucfg, "general");
    		cat = ast_category_browse(ucfg, NULL);
    		while (cat) {
    			if (strcasecmp(cat, "general")) {
    				hassip = ast_variable_retrieve(ucfg, cat, "hassip");
    				registersip = ast_variable_retrieve(ucfg, cat, "registersip");
    				if (ast_true(hassip) || (!hassip && genhassip)) {
    					peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
    					if (peer) {
    
    						ast_device_state_changed("SIP/%s", peer->name);
    
    						ASTOBJ_CONTAINER_LINK(&peerl,peer);
    
    						peer_count++;
    					}
    				}
    				if (ast_true(registersip) || (!registersip && genregistersip)) {
    					char tmp[256];
    
    					const char *host = ast_variable_retrieve(ucfg, cat, "host");
    					const char *username = ast_variable_retrieve(ucfg, cat, "username");
    					const char *secret = ast_variable_retrieve(ucfg, cat, "secret");
    					const char *contact = ast_variable_retrieve(ucfg, cat, "contact");
    
    					if (!host)
    						host = ast_variable_retrieve(ucfg, "general", "host");
    					if (!username)
    						username = ast_variable_retrieve(ucfg, "general", "username");
    					if (!secret)
    						secret = ast_variable_retrieve(ucfg, "general", "secret");
    					if (!contact)
    						contact = "s";
    					if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) {
    						if (!ast_strlen_zero(secret))
    							snprintf(tmp, sizeof(tmp), "%s:%s@%s/%s", username, secret, host, contact);
    						else
    							snprintf(tmp, sizeof(tmp), "%s@%s/%s", username, host, contact);
    						if (sip_register(tmp, 0) == 0)
    							registry_count++;
    					}
    				}
    			}
    			cat = ast_category_browse(ucfg, cat);
    		}
    		ast_config_destroy(ucfg);
    	}
    	
    
    
    	/* Load peers, users and friends */
    
    	cat = NULL;
    	while ( (cat = ast_category_browse(cfg, cat)) ) {
    		const char *utype;
    		if (!strcasecmp(cat, "general") || !strcasecmp(cat, "authentication"))
    			continue;
    		utype = ast_variable_retrieve(cfg, cat, "type");
    		if (!utype) {
    			ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
    			continue;
    		} else {
    			int is_user = 0, is_peer = 0;
    			if (!strcasecmp(utype, "user"))
    				is_user = 1;
    			else if (!strcasecmp(utype, "friend"))
    				is_user = is_peer = 1;
    			else if (!strcasecmp(utype, "peer"))
    				is_peer = 1;
    			else {
    				ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
    				continue;
    			}
    			if (is_user) {
    				user = build_user(cat, ast_variable_browse(cfg, cat), 0);
    				if (user) {
    					ASTOBJ_CONTAINER_LINK(&userl,user);
    
    				peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
    
    				if (peer) {
    					ASTOBJ_CONTAINER_LINK(&peerl,peer);
    
    	bindaddr.sin_family = AF_INET;
    	internip = bindaddr;
    	if (ast_find_ourip(&internip.sin_addr, bindaddr)) {
    
    		ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n");
    
    		ast_config_destroy(cfg);
    
    	if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		close(sipsock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sipsock = -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipsock < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sipsock = socket(AF_INET, SOCK_DGRAM, 0);
    		if (sipsock < 0) {
    			ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
    
    			ast_config_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    			/* Allow SIP clients on the same host to access us: */
    			const int reuseFlag = 1;
    
    			setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR,
    				   (const char*)&reuseFlag,
    				   sizeof reuseFlag);
    
    
    			ast_enable_packet_fragmentation(sipsock);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
    				ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
    
    				ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
    
    Mark Spencer's avatar
    Mark Spencer committed
    				close(sipsock);
    				sipsock = -1;
    
    				ast_verb(2, "SIP Listening on %s:%d\n",
    
    						ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
    
    				ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
    
    	if (stunaddr.sin_addr.s_addr != 0) {
    		ast_debug(1, "stun to %s:%d\n",
    			ast_inet_ntoa(stunaddr.sin_addr) , ntohs(stunaddr.sin_port));
    		ast_stun_request(sipsock, &stunaddr,
    			NULL, &externip);
    		ast_debug(1, "STUN sees us at %s:%d\n", 
    			ast_inet_ntoa(externip.sin_addr) , ntohs(externip.sin_port));
    	}
    
    	/* Add default domains - host name, IP address and IP:port */
    	/* Only do this if user added any sip domain with "localdomains" */
    	/* In order to *not* break backwards compatibility */
    	/* 	Some phones address us at IP only, some with additional port number */
    	if (auto_sip_domains) {
    		char temp[MAXHOSTNAMELEN];
    
    		/* First our default IP address */
    
    		if (bindaddr.sin_addr.s_addr)
    			add_sip_domain(ast_inet_ntoa(bindaddr.sin_addr), SIP_DOMAIN_AUTO, NULL);
    		else
    
    			ast_log(LOG_NOTICE, "Can't add wildcard IP address to domain list, please add IP address to domain manually.\n");
    
    		/* Our extern IP address, if configured */
    
    		if (externip.sin_addr.s_addr)
    			add_sip_domain(ast_inet_ntoa(externip.sin_addr), SIP_DOMAIN_AUTO, NULL);
    
    
    		/* Extern host name (NAT traversal support) */
    		if (!ast_strlen_zero(externhost))
    			add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL);
    		
    		/* Our host name */
    		if (!gethostname(temp, sizeof(temp)))
    			add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL);
    	}
    
    
    	/* Release configuration from memory */
    
    	ast_config_destroy(cfg);
    
    
    	/* Load the list of manual NOTIFY types to support */
    	if (notify_types)
    
    		ast_config_destroy(notify_types);
    
    	notify_types = ast_config_load(notify_config, config_flags);
    
    	manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "ChannelType: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\nUser_Count: %d\r\n", channelreloadreason2txt(reason), registry_count, peer_count, user_count);
    
    static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan)
    {
    	struct sip_pvt *p;
    	struct ast_udptl *udptl = NULL;
    	
    	p = chan->tech_pvt;
    	if (!p)
    		return NULL;
    	
    
    	if (p->udptl && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
    		udptl = p->udptl;
    
    	return udptl;
    }
    
    static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
    {
    	struct sip_pvt *p;
    	
    	p = chan->tech_pvt;
    	if (!p)
    		return -1;
    
    	if (udptl)
    		ast_udptl_get_peer(udptl, &p->udptlredirip);
    	else
    		memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
    	if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
    		if (!p->pendinginvite) {
    
    			ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
    
    			transmit_reinvite_with_sdp(p, TRUE);
    
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    
    			ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
    
    			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
    		}
    	}
    	/* Reset lastrtprx timer */
    	p->lastrtprx = p->lastrtptx = time(NULL);
    
    /*! \brief Handle T38 reinvite 
    	\todo Make sure we don't destroy the call if we can't handle the re-invite. 
    	Nothing should be changed until we have processed the SDP and know that we
    	can handle it.
    */
    
    static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite)
    {
    	struct sip_pvt *p;
    	int flag = 0;
    	
    	p = chan->tech_pvt;
    	if (!p || !pvt->udptl)
    		return -1;
    	
    	/* Setup everything on the other side like offered/responded from first side */
    
    
    	/*! \todo check if this is not set earlier when setting up the PVT. If not
    		maybe it should move there. */
    
    	p->t38.jointcapability = p->t38.peercapability = pvt->t38.jointcapability;
    
    	ast_udptl_set_far_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
    	ast_udptl_set_local_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
    	ast_udptl_set_error_correction_scheme(p->udptl, ast_udptl_get_error_correction_scheme(pvt->udptl));