Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 192938)
+++ channels/chan_sip.c	(working copy)
@@ -3245,6 +3245,28 @@
 	return sip_debug_test_addr(sip_real_dst(p));
 }
 
+/*! \brief Return sip_transport enum representing transport types in const char *transport */
+static enum sip_transport get_transport_str2enum(const char *transport)
+{
+	int res = 0;
+
+	if (ast_strlen_zero(transport)) {
+		return res;
+	}
+
+	if (!strcasecmp(transport, "udp")) {
+		res |= SIP_TRANSPORT_UDP;
+	}
+	if (!strcasecmp(transport, "tcp")) {
+		res |= SIP_TRANSPORT_TCP;
+	}
+	if (!strcasecmp(transport, "tls")) {
+		res |= SIP_TRANSPORT_TLS;
+	}
+
+	return res;
+}
+
 /*! \brief Return configuration of transports for a device */
 static inline const char *get_transport_list(unsigned int transports) {
 	switch (transports) {
@@ -4081,10 +4103,9 @@
  * general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] 
  * \endverbatim
  * 
- * \todo This function needs to look for ;transport= too
  */
 static int parse_uri(char *uri, char *scheme,
-	char **ret_name, char **pass, char **domain, char **port, char **options)
+	char **ret_name, char **pass, char **domain, char **port, char **options, char **transport)
 {
 	char *name = NULL;
 	int error = 0;
@@ -4103,6 +4124,17 @@
 			error = -1;
 		}
 	}
+	if (transport) {
+		char *t, *type = "";
+		*transport = "";
+		if ((t = strstr(uri, "transport="))) {
+			strsep(&t, "=");
+			if ((type = strsep(&t, ";"))) {
+				*transport = type;
+			}
+		}
+	}
+
 	if (!domain) {
 		/* if we don't want to split around domain, keep everything as a name,
 		 * so we need to do nothing here, except remember why.
@@ -11564,16 +11596,16 @@
 		We still need to be able to send to the remote agent through the proxy.
        */
 	if (tcp) {
-		if (!parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL)) {
+		if (!parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL, NULL)) {
 			use_tls = TRUE;
 		} else {
-			if (parse_uri(contact2, "sip:", &contact, NULL, &host, &pt, NULL))
+			if (parse_uri(contact2, "sip:", &contact, NULL, &host, &pt, NULL, NULL))
 				ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
 		}
 		port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT;
 		/*! \todo XXX why are we setting TLS port if there's no port given? parse_uri needs to return the transport. */
 	} else {
-		if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL))
+		if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL, NULL))
 			ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
 		port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
 	}
@@ -11610,21 +11642,22 @@
 	return __set_address_from_contact(pvt->fullcontact, &pvt->sa, pvt->socket.type == SIP_TRANSPORT_TLS ? 1 : 0);
 }
 
-
 /*! \brief Parse contact header and save registration (peer registration) */
 static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
 {
-	char contact[SIPBUFSIZE]; 
+	char contact[SIPBUFSIZE];
 	char data[SIPBUFSIZE];
 	const char *expires = get_header(req, "Expires");
 	int expire = atoi(expires);
-	char *curi, *host, *pt, *curi2;
+	char *curi, *host, *pt, *curi2, *transport;
 	int port;
+	int transport_type;
 	const char *useragent;
 	struct hostent *hp;
 	struct ast_hostent ahp;
 	struct sockaddr_in oldsin, testsin;
 
+
 	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
 
 	if (ast_strlen_zero(expires)) {	/* No expires header, try look in Contact: */
@@ -11639,8 +11672,6 @@
 		}
 	}
 
-	if (peer->socket.type == req->socket.type)
-		copy_socket_data(&peer->socket, &req->socket);
 	copy_socket_data(&pvt->socket, &req->socket);
 
 	/* Look for brackets */
@@ -11667,7 +11698,7 @@
 				unref_peer(peer, "remove register expire ref"));
 
 		destroy_association(peer);
-		
+
 		register_peer_exten(peer, FALSE);	/* Remove extension from regexten= setting in sip.conf */
 		ast_string_field_set(peer, fullcontact, "");
 		ast_string_field_set(peer, useragent, "");
@@ -11688,24 +11719,42 @@
 	ast_string_field_build(pvt, our_contact, "<%s>", curi);
 
 	/* Make sure it's a SIP URL */
-	/*! \todo This code assumes that the Contact is using the same transport as the
-		REGISTER request. That might not be true at all. You can receive
-		sips: requests over any transport. Needs to be fixed.
-		Does not parse the ;transport uri parameter at this point, which might be handy
-		in some situations.
-	*/
 	if (pvt->socket.type == SIP_TRANSPORT_TLS) {
-		if (parse_uri(curi, "sips:", &curi, NULL, &host, &pt, NULL)) {
-			if (parse_uri(curi2, "sip:", &curi, NULL, &host, &pt, NULL))
+		if (parse_uri(curi, "sips:", &curi, NULL, &host, &pt, NULL, &transport)) {
+			if (parse_uri(curi2, "sip:", &curi, NULL, &host, &pt, NULL, &transport))
 				ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n");
 		}
 		port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT;
 	} else {
-		if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL))
+		if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL, &transport))
 			ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n");
 		port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
 	}
 
+	/* handle the transport type specified in Contact header. */
+	if ((transport_type = get_transport_str2enum(transport))) {
+		/* if the port is not specified but the transport is, make sure to set the
+		 * default port to match the specified transport.  This may or may not be the
+		 * same transport used by the pvt struct for the Register dialog. */
+		if (ast_strlen_zero(pt)) {
+			port = (transport_type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT;
+		}
+	} else {
+		transport_type = pvt->socket.type;
+	}
+
+	/* if the peer's socket type is different than the Registration
+	 * transport type, change it.  If it got this far, it is a
+	 * supported type, but check just in case */
+	if ((peer->socket.type != transport_type) && (peer->transports & transport_type)) {
+		peer->socket.type = transport_type;
+		peer->socket.fd = -1;
+		if (peer->socket.tcptls_session) {
+			ao2_ref(peer->socket.tcptls_session, -1);
+			peer->socket.tcptls_session = NULL;
+		}
+	}
+
 	oldsin = peer->addr;
 
 	/* If we were already linked into the peers_by_ip container unlink ourselves so nobody can find us */
@@ -11744,6 +11793,16 @@
 		peer->addr = pvt->recv;
 	}
 
+	/* if the Contact header information copied into peer->addr matches the
+	 * received address, and the transport types are the same, then copy socket
+	 * data into the peer struct */
+	if ((peer->socket.type == pvt->socket.type) &&
+		(peer->addr.sin_addr.s_addr == pvt->recv.sin_addr.s_addr) &&
+		(peer->addr.sin_port == pvt->recv.sin_port)){
+
+		copy_socket_data(&peer->socket, &pvt->socket);
+	}
+
 	/* Now that our address has been updated put ourselves back into the container for lookups */
 	ao2_t_link(peers_by_ip, peer, "ao2_link into peers_by_ip table");
 
@@ -11763,7 +11822,7 @@
 	if (peer->is_realtime && !ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
 		peer->expire = -1;
 	} else {
-		peer->expire = ast_sched_add(sched, (expire + 10) * 1000, expire_register, 
+		peer->expire = ast_sched_add(sched, (expire + 10) * 1000, expire_register,
 				ref_peer(peer, "add registration ref"));
 		if (peer->expire == -1) {
 			unref_peer(peer, "remote registration ref");
@@ -11775,7 +11834,7 @@
 		XXX WHY???? XXX
 		\todo Fix this immediately.
 	*/
-	if (!peer->rt_fromcontact && (peer->socket.type & SIP_TRANSPORT_UDP)) 
+	if (!peer->rt_fromcontact && (peer->socket.type & SIP_TRANSPORT_UDP))
 		ast_db_put("SIP/Registry", peer->name, data);
 	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\nPort: %d\r\n", peer->name,  ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port));
 
@@ -13602,12 +13661,12 @@
 	/*! \todo Samme logical error as in many places above. Need a generic function for this.
  	*/
 	if (p->socket.type == SIP_TRANSPORT_TLS) {
-		if (parse_uri(of, "sips:", &of, &dummy, &domain, &dummy, &dummy)) {
-			if (parse_uri(of2, "sip:", &of, &dummy, &domain, &dummy, &dummy))
+		if (parse_uri(of, "sips:", &of, &dummy, &domain, &dummy, &dummy, NULL)) {
+			if (parse_uri(of2, "sip:", &of, &dummy, &domain, &dummy, &dummy, NULL))
 				ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
 		}
 	} else {
-		if (parse_uri(of, "sip:", &of, &dummy, &domain, &dummy, &dummy))
+		if (parse_uri(of, "sip:", &of, &dummy, &domain, &dummy, &dummy, NULL))
 			ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
 	}
 
@@ -23210,9 +23269,10 @@
 {
 	struct sip_peer *peer = NULL;
 	struct ast_ha *oldha = NULL;
-	int found=0;
-	int firstpass=1;
-	int format=0;		/* Ama flags */
+	int found = 0;
+	int firstpass = 1;
+	int format = 0;		/* Ama flags */
+	int original_transport = 0;
 	time_t regseconds = 0;
 	struct ast_flags peerflags[2] = {{(0)}};
 	struct ast_flags mask[2] = {{(0)}};
@@ -23275,8 +23335,9 @@
 	/* If we have realm authentication information, remove them (reload) */
 	clear_realm_authentication(peer->auth);
 	peer->auth = NULL;
+	original_transport = peer->socket.type;
+	peer->socket.type = 0;
 	peer->transports = 0;
-	peer->socket.type = 0;
 
 	for (; v || ((v = alt) && !(alt=NULL)); v = v->next) {
 		if (handle_common_options(&peerflags[0], &mask[0], v))
@@ -23299,7 +23360,6 @@
 
 				if (!peer->socket.type) { /*!< The first transport listed should be used for outgoing */
 					peer->socket.type = peer->transports;
-					peer->socket.fd = -1;
 				}
 			}
 		} else if (realtime && !strcasecmp(v->name, "regseconds")) {
@@ -23612,7 +23672,24 @@
 		peer->transports = default_transports;
 		/* Set default primary transport */
 		peer->socket.type = default_primary_transport;
+	}
+
+	/* If the original peer socket type before rebuilding the peer is NOT equal to the new one,
+	 * override the new one with the original if a registration is present and the original type
+	 * is still supported by the peer.
+	 *
+	 * The original socket information is used to keep the socket type consistent with what was
+	 * specified during peer Registration.  The default set during build_peer should only be used when a
+	 * registration is not present. */
+	if ((original_transport != peer->socket.type) && ((peer->transports & original_transport)) && (peer->expire > -1)) {
+			peer->socket.type = original_transport;
+	} else if (original_transport != peer->socket.type) {
+		/* if the transport type changes, clear all socket data */
 		peer->socket.fd = -1;
+		if (peer->socket.tcptls_session) {
+			ao2_ref(peer->socket.tcptls_session, -1);
+			peer->socket.tcptls_session = NULL;
+		}
 	}
 
 	if (fullcontact->used > 0) {
@@ -23637,7 +23714,7 @@
 		if ((params = strchr(_srvlookup, ';'))) {
 			*params++ = '\0';
 		}
-		
+
 		snprintf(transport, sizeof(transport), "_sip._%s", get_transport(peer->socket.type));
 
 		if (ast_dnsmgr_lookup(_srvlookup, &peer->addr, &peer->dnsmgr, sip_cfg.srvlookup ? transport : NULL)) {
Index: configs/sip.conf.sample
===================================================================
--- configs/sip.conf.sample	(revision 192938)
+++ configs/sip.conf.sample	(working copy)
@@ -899,11 +899,14 @@
 ;remotesecret=guessit             ; Our password to their service
 ;defaultuser=yourusername         ; Authentication user for outbound proxies
 ;fromuser=yourusername            ; Many SIP providers require this!
-;fromdomain=provider.sip.domain        
+;fromdomain=provider.sip.domain 
 ;host=box.provider.com
-;transport=udp,tcp                ; This sets the transport type to udp for outgoing, and will
-;                                 ;   accept both tcp and udp. Default is udp. The first transport
-;                                 ;   listed will always be used for outgoing connections.
+;transport=udp,tcp                ; This sets the default transport type to udp for outgoing, and will
+;                                 ; accept both tcp and udp. The default transport type is only used for
+;                                 ; outbound messages until a Registration takes place.  During the
+;                                 ; peer Registration the transport type may change to another supported
+;                                 ; type if the peer requests so.
+
 ;usereqphone=yes                  ; This provider requires ";user=phone" on URI
 ;callcounter=yes                  ; Enable call counter
 ;busylevel=2                      ; Signal busy at 2 or more calls
