Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 125053)
+++ channels/chan_sip.c	(working copy)
@@ -1323,9 +1323,10 @@
 							before strolling to the Grokyzpå
 							(A bit unsure of this, please correct if
 							you know more) */
-	struct sip_st_dlg *stimer;		/*!< SIP Session-Timers */              
-  
-	int red; 
+	struct sip_st_dlg *stimer;		/*!< SIP Session-Timers */
+	unsigned int srvlookups; 		/*!< Count of how many srv records attempted for this call */
+	unsigned int srvcount;			/*!< Count of the srv records that exist in dns for this call */
+	int red;
 };
 
 /*! Max entires in the history list for a sip_pvt */
@@ -1583,6 +1584,7 @@
 	struct sockaddr_in us;		/*!< Who the server thinks we are */
 	int noncecount;			/*!< Nonce-count */
 	char lastmsg[256];		/*!< Last Message sent/received */
+	unsigned int newsrv:1;	/*!< Flag for trying a new srv record */
 };
 
 struct sip_threadinfo {
@@ -4384,7 +4386,6 @@
 			portno = ntohs(sin->sin_port);
 		}
 	} else {
-
 		/* Let's see if we can find the host in DNS. First try DNS SRV records,
 		   then hostname lookup */
 		hostn = peername;
@@ -9711,6 +9712,7 @@
 	char addr[80];
 	struct sip_pvt *p;
 	int res;
+	size_t srvcount = 0;
 
 	/* exit if we are already in process with this registrar ?*/
 	if (r == NULL || ((auth == NULL) && (r->regstate == REG_STATE_REGSENT || r->regstate == REG_STATE_AUTHSENT))) {
@@ -9721,7 +9723,10 @@
 	if (r->dnsmgr == NULL) {
 		char transport[MAXHOSTNAMELEN];
 		snprintf(transport, sizeof(transport), "_sip._%s", get_transport(r->transport)); /* have to use static get_transport function */
-		ast_dnsmgr_lookup(r->hostname, &r->us, &r->dnsmgr, global_srvlookup ? transport : NULL);
+		ast_dnsmgr_lookup(r->hostname, &r->us, &r->dnsmgr, (global_srvlookup && r->newsrv) ? transport : NULL);
+
+		if (global_srvlookup)
+			srvcount = ast_dnsmgr_lookup_count(r->hostname, &r->us, &r->dnsmgr, r->newsrv ? transport : NULL);
 	}
 
 	if (r->call) {	/* We have a registration */
@@ -9818,6 +9823,8 @@
 		build_contact(p);
 	}
 
+	p->srvcount = srvcount;
+
 	/* set up a timeout */
 	if (auth == NULL)  {
 		if (r->timeout > -1)
@@ -16197,39 +16204,46 @@
 	int expires, expires_ms;
 	struct sip_registry *r;
 	r=p->registry;
-	
+
+	/* when a 400 type response is sent that doesn't
+	 * mean an unauthorized response that could be to an 
+	 * initial registration, set a flag on the registration 
+	 * structure to look for a new srv record from dns ...
+	 */
+	if (resp > 400 && resp < 500 && resp != 401 && resp != 407)
+		r->newsrv = TRUE;
+	else
+		r->newsrv = FALSE;
+
 	switch (resp) {
 	case 401:	/* Unauthorized */
 		if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
+			/* if registration has failed, then we should attempt
+			 * reregistering with a new srv record from dns ...
+			 */
+			r->newsrv = TRUE;
 			ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s@%s' (Tries %d)\n", p->registry->username, p->registry->hostname, p->authtries);
-			p->needdestroy = 1;
 		}
 		break;
 	case 403:	/* Forbidden */
 		ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname);
-		AST_SCHED_DEL(sched, r->timeout);
 		r->regstate = REG_STATE_NOAUTH;
-		p->needdestroy = 1;
 		break;
 	case 404:	/* Not found */
 		ast_log(LOG_WARNING, "Got 404 Not found on SIP register to service %s@%s, giving up\n", p->registry->username, p->registry->hostname);
-		p->needdestroy = 1;
 		if (r->call)
 			r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 404");
 		r->regstate = REG_STATE_REJECTED;
-		AST_SCHED_DEL(sched, r->timeout);
 		break;
 	case 407:	/* Proxy auth */
 		if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
+			r->newsrv = TRUE;
 			ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s' (tries '%d')\n", get_header(&p->initreq, "From"), p->authtries);
-			p->needdestroy = 1;
 		}
 		break;
 	case 408:	/* Request timeout */
-		p->needdestroy = 1;
 		if (r->call)
 			r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 408");
-		AST_SCHED_DEL(sched, r->timeout);
 		break;
 	case 423:	/* Interval too brief */
 		r->expiry = atoi(get_header(req, "Min-Expires"));
@@ -16252,11 +16266,9 @@
 		break;
 	case 479:	/* SER: Not able to process the URI - address is wrong in register*/
 		ast_log(LOG_WARNING, "Got error 479 on register to %s@%s, giving up (check config)\n", p->registry->username, p->registry->hostname);
-		p->needdestroy = 1;
 		if (r->call)
 			r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 479");
 		r->regstate = REG_STATE_REJECTED;
-		AST_SCHED_DEL(sched, r->timeout);
 		break;
 	case 200:	/* 200 OK */
 		if (!r) {
@@ -16336,6 +16348,17 @@
 		   scheduled a callback and recorded it in there! */
 		/* since we never bumped the count, we shouldn't decrement it! registry_unref(r, "unref registry ptr r"); if this gets deleted, p->registry will be a bad pointer! */ 
 	}
+
+	if (r->newsrv && (!global_srvlookup || p->srvlookups > p->srvcount)) {
+		p->needdestroy = 1;
+		AST_SCHED_DEL(sched, r->timeout);
+		return -1;
+	}
+
+	if (r->newsrv) {
+		++p->srvlookups;
+	}
+
 	return 1;
 }
 
Index: include/asterisk/acl.h
===================================================================
--- include/asterisk/acl.h	(revision 125053)
+++ include/asterisk/acl.h	(working copy)
@@ -68,6 +68,8 @@
 
 int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service);
 
+int ast_get_ip_or_srv_count(struct sockaddr_in *sin, const char *value, const char *service, size_t *count);
+
 int ast_ouraddrfor(struct in_addr *them, struct in_addr *us);
 
 int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr);
Index: include/asterisk/dnsmgr.h
===================================================================
--- include/asterisk/dnsmgr.h	(revision 125053)
+++ include/asterisk/dnsmgr.h	(working copy)
@@ -79,6 +79,21 @@
 int ast_dnsmgr_lookup(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service);
 
 /*!
+ * \brief Return the number of records matching a particular lookup.
+ *
+ * \arg name the hostname
+ * \arg result where to store the IP address as the DNS manager refreshes it
+ * \arg dnsmgr Where to store the allocate DNS manager entry
+ *
+ * This function will act just like ast_dnsmgr_lookup with the 
+ * exception of the relevance of it's return value.
+ *
+ * \retval 0 no records found
+ * \retval non-zero the number of records
+ */
+size_t ast_dnsmgr_lookup_count(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service);
+
+/*!
  * \brief Force a refresh of a dnsmgr entry
  *
  * \retval non-zero if the result is different than the previous result
Index: include/asterisk/srv.h
===================================================================
--- include/asterisk/srv.h	(revision 125053)
+++ include/asterisk/srv.h	(working copy)
@@ -42,4 +42,6 @@
 */
 extern int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service);
 
+extern int ast_get_srv_count(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service, size_t *count);
+
 #endif /* _ASTERISK_SRV_H */
Index: main/dns.c
===================================================================
--- main/dns.c	(revision 125053)
+++ main/dns.c	(working copy)
@@ -279,7 +279,7 @@
 			ast_debug(1, "No matches found in DNS for %s\n", dname);
 			ret = 0;
 		} else
-			ret = 1;
+			ret = res;
 	}
 #ifdef HAVE_RES_NINIT
 #ifdef HAVE_RES_NDESTROY
Index: main/acl.c
===================================================================
--- main/acl.c	(revision 125053)
+++ main/acl.c	(working copy)
@@ -355,7 +355,7 @@
 	return res;
 }
 
-int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
+static int __ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service, size_t *count)
 {
 	struct hostent *hp;
 	struct ast_hostent ahp;
@@ -364,7 +364,7 @@
 	int tportno = ntohs(sin->sin_port);
 	if (service) {
 		snprintf(srv, sizeof(srv), "%s.%s", service, value);
-		if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
+		if (ast_get_srv_count(NULL, host, sizeof(host), &tportno, srv, count) > 0) {
 			sin->sin_port = htons(tportno);
 			value = host;
 		}
@@ -379,6 +379,14 @@
 	return 0;
 }
 
+int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service) {
+	return __ast_get_ip_or_srv(sin, value, service, NULL);
+}
+
+int ast_get_ip_or_srv_count(struct sockaddr_in *sin, const char *value, const char *service, size_t *count) {
+	return __ast_get_ip_or_srv(sin, value, service, count);
+}
+
 struct dscp_codepoint {
 	char *name;
 	unsigned int space;
Index: main/dnsmgr.c
===================================================================
--- main/dnsmgr.c	(revision 125053)
+++ main/dnsmgr.c	(working copy)
@@ -123,8 +123,22 @@
 	ast_free(entry);
 }
 
-int ast_dnsmgr_lookup(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service)
+/* dns manager lookup helper function
+ *
+ * return values:
+ * 	0  - success, name given is an IP address, no dns needed
+ *
+ * 	-1 - bad arguments, name had no length, 
+ * 	     there was no sockaddr_in or no dnsmgr
+ *
+ * 	1  - no dns records found
+ *
+ * 	>1 - count of dns records found plus one
+ */
+static int __ast_dnsmgr_lookup(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service)
 {
+	size_t count;
+
 	if (ast_strlen_zero(name) || !result || !dnsmgr)
 		return -1;
 
@@ -139,7 +153,7 @@
 	ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name);
 
 	/* do a lookup now but add a manager so it will automagically get updated in the background */
-	ast_get_ip_or_srv(result, name, service);
+	ast_get_ip_or_srv_count(result, name, service, &count);
 	
 	/* if dnsmgr is not enable don't bother adding an entry */
 	if (!enabled)
@@ -147,9 +161,27 @@
 	
 	ast_verb(3, "adding dns manager for '%s'\n", name);
 	*dnsmgr = ast_dnsmgr_get(name, result, service);
-	return !*dnsmgr;
+	return ( !*dnsmgr ) ? !*dnsmgr : count + 1;
 }
 
+size_t ast_dnsmgr_lookup_count(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service) {
+	size_t ret = (unsigned) __ast_dnsmgr_lookup(name, result, dnsmgr, service);
+
+	if (ret > 1)
+		return ret - 1;
+
+	return 0;
+}
+
+int ast_dnsmgr_lookup(const char *name, struct sockaddr_in *result, struct ast_dnsmgr_entry **dnsmgr, const char *service) {
+	int ret = __ast_dnsmgr_lookup(name, result, dnsmgr, service);
+
+	if (ret > 1)
+		return 0;
+
+	return ret;
+}
+
 /*
  * Refresh a dnsmgr entry
  */
Index: main/srv.c
===================================================================
--- main/srv.c	(revision 125053)
+++ main/srv.c	(working copy)
@@ -53,6 +53,9 @@
 #define T_SRV 33
 #endif
 
+#define FALSE	0
+#define TRUE	1
+
 struct srv_entry {
 	unsigned short priority;
 	unsigned short weight;
@@ -113,6 +116,8 @@
 	struct srv_context *c = (struct srv_context *) context;
 	struct srv_entry *entry = NULL;
 	struct srv_entry *current;
+	int broken = FALSE;
+	int numrecords = 0;
 
 	if (parse_srv(answer, len, fullanswer, &entry))
 		return -1;
@@ -121,14 +126,18 @@
 		c->have_weights = 1;
 
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
+		numrecords++;
 		/* insert this entry just before the first existing
 		   entry with a higher priority */
 		if (current->priority <= entry->priority)
 			continue;
 
+		if (broken)
+			continue;
+
+		broken = TRUE;
 		AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
 		entry = NULL;
-		break;
 	}
 	AST_LIST_TRAVERSE_SAFE_END;
 
@@ -137,7 +146,7 @@
 	if (entry)
 		AST_LIST_INSERT_TAIL(&c->entries, entry, list);
 
-	return 1;
+	return numrecords;
 }
 
 /* Do the bizarre SRV record weight-handling algorithm
@@ -197,7 +206,7 @@
 	AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
 }
 
-int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
+static int __ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service, size_t *count)
 {
 	struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
 	struct srv_entry *current;
@@ -208,6 +217,9 @@
 
 	ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
 
+	if (count)
+		*count = ret;
+
 	if (context.have_weights)
 		process_weights(&context);
 
@@ -217,7 +229,7 @@
 	/* TODO: there could be a "." entry in the returned list of
 	   answers... if so, this requires special handling */
 
-	/* the list of entries will be sorted in the proper selection order
+	/* the list aof entries will be sorted in the proper selection order
 	   already, so we just need the first one (if any) */
 
 	if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
@@ -236,3 +248,11 @@
 
 	return ret;
 }
+
+int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service) {
+	return __ast_get_srv(chan, host, hostlen, port, service, NULL);
+}
+
+int ast_get_srv_count(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service, size_t *count) {
+	return __ast_get_srv(chan, host, hostlen, port, service, count);
+}
