=== modified file 'src/ICP.h'
--- src/ICP.h	2012-04-25 05:29:20 +0000
+++ src/ICP.h	2012-05-01 00:08:22 +0000
@@ -110,6 +110,9 @@
 /// \ingroup ServerProtocolICPAPI
 struct icpUdpData {
 
+    /// config details of the peer this packet is being sent to (if any)
+    const peer *peerDetail; // XXX: cbdata pointer?
+
     /// IP address for the remote end. Because we reply to packets from unknown non-peers.
     Ip::Address address;
 
@@ -126,10 +129,14 @@
     struct timeval queue_time;
 };
 
-extern Comm::ConnectionPointer icpIncomingConn;
-extern Comm::ConnectionPointer icpOutgoingConn;
+/// whether any ICP ports are currently open
+extern bool icpActive;
+
 extern Ip::Address theIcpPublicHostID;
 
+/// Locate an open ICP port which can use for packets to the given destination
+extern Comm::ConnectionPointer icpLocateOutConn(const Ip::Address &sendingTo);
+
 /// \ingroup ServerProtocolICPAPI
 extern HttpRequest* icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from);
 
@@ -143,7 +150,7 @@
 extern icp_opcode icpGetCommonOpcode();
 
 /// \ingroup ServerProtocolICPAPI
-SQUIDCEXTERN int icpUdpSend(int, const Ip::Address &, icp_common_t *, log_type, int);
+SQUIDCEXTERN int icpUdpSend(const peer *, const Ip::Address &, icp_common_t *, log_type, int);
 
 /// \ingroup ServerProtocolICPAPI
 SQUIDCEXTERN log_type icpLogFromICPCode(icp_opcode opcode);

=== modified file 'src/anyp/PortCfg.h'
--- src/anyp/PortCfg.h	2012-04-25 05:29:20 +0000
+++ src/anyp/PortCfg.h	2012-05-06 01:32:19 +0000
@@ -12,7 +12,7 @@
 {
 
 struct PortCfg {
-    PortCfg(const char *aProtocol);
+    explicit PortCfg(const char *aProtocol);
     ~PortCfg();
     AnyP::PortCfg *clone() const;
 

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2012-04-26 01:04:17 +0000
+++ src/cache_cf.cc	2012-05-06 05:29:10 +0000
@@ -3763,19 +3763,28 @@
 }
 
 void
-add_http_port(char *portspec)
+add_generic_port(char *portspec, const char *protocol)
 {
-    AnyP::PortCfg *s = new AnyP::PortCfg("http_port");
+    AnyP::PortCfg *s = new AnyP::PortCfg(protocol);
     parsePortSpecification(s, portspec);
     // we may need to merge better if the above returns a list with clones
     assert(s->next == NULL);
-    s->next = Config.Sockaddr.http;
-    Config.Sockaddr.http = s;
+    if (!strcmp(protocol,"http")) {
+        s->next = Config.Sockaddr.http;
+        Config.Sockaddr.http = s;
+    } else if(!strcmp(protocol,"icp")) {
+        s->next = Config.Sockaddr.icp;
+        Config.Sockaddr.icp = s;
+    }
 }
 
 static void
-parsePortCfg(AnyP::PortCfg ** head, const char *protocol)
+parsePortCfg(AnyP::PortCfg ** head, const char *type)
 {
+    char protocol[15];
+    memset(protocol, '\0', sizeof(protocol));
+    memcpy(protocol, type, min(sizeof(protocol)-1,strlen(type)-5));
+
     char *token = strtok(NULL, w_space);
 
     if (!token) {
@@ -3834,7 +3843,7 @@
         if (s->defaultsite)
             storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
 
-        if (s->protocol && strcmp(s->protocol,"http") != 0)
+        if (s->protocol && strncmp(s->protocol, n, strlen(s->protocol)) != 0)
             storeAppendPrintf(e, " protocol=%s", s->protocol);
 
         if (s->allow_direct)

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2012-04-25 05:29:20 +0000
+++ src/cf.data.pre	2012-05-08 01:30:05 +0000
@@ -5592,18 +5592,19 @@
 COMMENT_END
 
 NAME: snmp_port
-TYPE: u_short
-LOC: Config.Port.snmp
-DEFAULT: 0
+TYPE: PortCfg
+DEFAULT: none
+LOC: Config.Sockaddr.snmp
 IFDEF: SQUID_SNMP
 DOC_START
-	The port number where Squid listens for SNMP requests. To enable
+	The port where Squid listens for SNMP requests. To enable
 	SNMP support set this to a suitable port number. Port number
-	3401 is often used for the Squid SNMP agent. By default it's
-	set to "0" (disabled)
+	3401 is often used for the Squid SNMP agent.
+	By default SNMP is disabled.
 
 	Example:
 		snmp_port 3401
+		snmp_port 192.1.2.3:3401
 DOC_END
 
 NAME: snmp_access
@@ -5626,36 +5627,11 @@
  snmp_access deny all
 DOC_END
 
-NAME: snmp_incoming_address
-TYPE: address
-LOC: Config.Addrs.snmp_incoming
-DEFAULT: any_addr
-IFDEF: SQUID_SNMP
-DOC_NONE
-
-NAME: snmp_outgoing_address
-TYPE: address
-LOC: Config.Addrs.snmp_outgoing
-DEFAULT: no_addr
+NAME: snmp_incoming_address snmp_outgoing_address
+TYPE: obsolete
 IFDEF: SQUID_SNMP
 DOC_START
-	Just like 'udp_incoming_address', but for the SNMP port.
-
-	snmp_incoming_address	is used for the SNMP socket receiving
-				messages from SNMP agents.
-	snmp_outgoing_address	is used for SNMP packets returned to SNMP
-				agents.
-
-	The default snmp_incoming_address is to listen on all
-	available network interfaces.
-
-	If snmp_outgoing_address is not set it will use the same socket
-	as snmp_incoming_address. Only change this if you want to have
-	SNMP replies sent using another address than where this Squid
-	listens for SNMP queries.
-
-	NOTE, snmp_incoming_address and snmp_outgoing_address can not have
-	the same value since they both use port 3401.
+	Use snmp_port instead. It takes address:port as a parameter.
 DOC_END
 
 COMMENT_START
@@ -5664,27 +5640,27 @@
 COMMENT_END
 
 NAME: icp_port udp_port
-TYPE: u_short
-DEFAULT: 0
-LOC: Config.Port.icp
+TYPE: PortCfg
+DEFAULT: none
+LOC: Config.Sockaddr.icp
 DOC_START
-	The port number where Squid sends and receives ICP queries to
+	The address port where Squid sends and receives ICP queries to
 	and from neighbor caches.  The standard UDP port for ICP is 3130.
-	Default is disabled (0).
+	Default is disabled.
 
 	Example:
-		icp_port @DEFAULT_ICP_PORT@
+		icp_port 3130
 DOC_END
 
 NAME: htcp_port
 IFDEF: USE_HTCP
-TYPE: u_short
-DEFAULT: 0
-LOC: Config.Port.htcp
+TYPE: PortCfg
+DEFAULT: none
+LOC: Config.Sockaddr.htcp
 DOC_START
-	The port number where Squid sends and receives HTCP queries to
+	The address and port where Squid sends and receives HTCP queries to
 	and from neighbor caches.  To turn it on you want to set it to
-	4827. By default it is set to "0" (disabled).
+	4827. By default it is disabled.
 
 	Example:
 		htcp_port 4827
@@ -5702,41 +5678,49 @@
 DOC_END
 
 NAME: udp_incoming_address
+TYPE: obsolete
+DOC_START
+	Use dns_incoming_address for DNS listening address.
+	Use icp_port, htcp_port, or snmp_port for other UDP protocols address:port settings.
+DOC_END
+
+NAME: dns_incoming_address
 TYPE: address
-LOC:Config.Addrs.udp_incoming
+LOC:Config.dns.incomingAddr
 DEFAULT: any_addr
 DOC_START
-	udp_incoming_address	is used for UDP packets received from other
-				caches.
+	dns_incoming_address	is used to listen for DNS packets.
 
 	The default behavior is to not bind to any specific address.
 
-	Only change this if you want to have all UDP queries received on
+	Only change this if you want to have all DNS queries received on
 	a specific interface/address.
 
-	NOTE: udp_incoming_address is used by the ICP, HTCP, and DNS
-	modules. Altering it will affect all of them in the same manner.
-
-	see also; udp_outgoing_address
-
-	NOTE, udp_incoming_address and udp_outgoing_address can not
+	see also; dns_outgoing_address
+
+	NOTE, dns_incoming_address and dns_outgoing_address can not
 	have the same value since they both use the same port.
 DOC_END
 
 NAME: udp_outgoing_address
+TYPE: obsolete
+DOC_START
+	Use dns_outgoing_address for DNS sending address.
+	See icp_port, htcp_port, or snmp_port for details on outgoing address for those protocols.
+DOC_END
+
+NAME: dns_outgoing_address
 TYPE: address
-LOC: Config.Addrs.udp_outgoing
+LOC: Config.dns.outgoingAddr
 DEFAULT: no_addr
 DOC_START
-	udp_outgoing_address	is used for UDP packets sent out to other
-				caches.
+	dns_outgoing_address	is used for DNS packets sent.
 
 	The default behavior is to not bind to any specific address.
 
-	Instead it will use the same socket as udp_incoming_address.
-	Only change this if you want to have UDP queries sent using another
-	address than where this Squid listens for UDP queries from other
-	caches.
+	Instead it will use the same socket as dns_incoming_address.
+	Only change this if you want to have DNS queries sent using another
+	address than where this Squid listens for DNS responses.
 
 	NOTE: udp_outgoing_address is used by the ICP, HTCP, and DNS
 	modules. Altering it will affect all of them in the same manner.

=== modified file 'src/comm/ModPoll.cc'
--- src/comm/ModPoll.cc	2012-04-26 01:04:17 +0000
+++ src/comm/ModPoll.cc	2012-05-06 00:28:20 +0000
@@ -172,11 +172,10 @@
 static int
 fdIsUdpListen(int fd)
 {
-    if (icpIncomingConn != NULL && icpIncomingConn->fd == fd)
-        return 1;
-
-    if (icpOutgoingConn != NULL && icpOutgoingConn->fd == fd)
-        return 1;
+    for (const AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (s->listenConn != NULL && s->listenConn->fd == fd)
+            return 1;
+    }
 
     return 0;
 }
@@ -283,11 +282,10 @@
     int nevents;
     udp_io_events = 0;
 
-    if (Comm::IsConnOpen(icpIncomingConn))
-        fds[nfds++] = icpIncomingConn->fd;
-
-    if (icpIncomingConn != icpOutgoingConn && Comm::IsConnOpen(icpOutgoingConn))
-        fds[nfds++] = icpOutgoingConn->fd;
+    for (const AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (Comm::IsConnOpen(s->listenConn))
+            fds[nfds++] = s->listenConn->fd;
+    }
 
     if (nfds == 0)
         return;
@@ -319,11 +317,9 @@
 
     // XXX: only poll sockets that won't be deferred. But how do we identify them?
 
-    for (j = 0; j < NHttpSockets; j++) {
-        if (HttpSockets[j] < 0)
-            continue;
-
-        fds[nfds++] = HttpSockets[j];
+    for (const AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) {
+        if (Comm::IsConnOpen(s->listenConn))
+            fds[nfds++] = s->listenConn->fd;
     }
 
     nevents = comm_check_incoming_poll_handlers(nfds, fds);

=== modified file 'src/comm/ModSelect.cc'
--- src/comm/ModSelect.cc	2012-04-25 05:29:20 +0000
+++ src/comm/ModSelect.cc	2012-04-25 05:49:52 +0000
@@ -170,11 +170,10 @@
 static int
 fdIsUdpListener(int fd)
 {
-    if (icpIncomingConn != NULL && fd == icpIncomingConn->fd)
-        return 1;
-
-    if (icpOutgoingConn != NULL && fd == icpOutgoingConn->fd)
-        return 1;
+    for (const AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (s->listenConn != NULL && s->listenConn->fd == fd)
+            return 1;
+    }
 
     return 0;
 }
@@ -278,11 +277,10 @@
     int nevents;
     udp_io_events = 0;
 
-    if (Comm::IsConnOpen(icpIncomingConn))
-        fds[nfds++] = icpIncomingConn->fd;
-
-    if (Comm::IsConnOpen(icpOutgoingConn) && icpIncomingConn != icpOutgoingConn)
-        fds[nfds++] = icpOutgoingConn->fd;
+    for (const AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (Comm::IsConnOpen(s->listenConn))
+            fds[nfds++] = s->listenConn->fd;
+    }
 
     if (nfds == 0)
         return;

=== modified file 'src/dns_internal.cc'
--- src/dns_internal.cc	2012-03-05 11:36:38 +0000
+++ src/dns_internal.cc	2012-03-21 07:07:46 +0000
@@ -842,10 +842,10 @@
 
     Comm::ConnectionPointer conn = new Comm::Connection();
 
-    if (!Config.Addrs.udp_outgoing.IsNoAddr())
-        conn->local = Config.Addrs.udp_outgoing;
+    if (!Config.dns.outgoingAddr.IsNoAddr())
+        conn->local = Config.dns.outgoingAddr;
     else
-        conn->local = Config.Addrs.udp_incoming;
+        conn->local = Config.dns.incomingAddr;
 
     conn->remote = nameservers[ns].S;
 
@@ -1480,10 +1480,10 @@
     if (DnsSocketA < 0 && DnsSocketB < 0) {
         Ip::Address addrV6; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
 
-        if (!Config.Addrs.udp_outgoing.IsNoAddr())
-            addrV6 = Config.Addrs.udp_outgoing;
+        if (!Config.dns.outgoingAddr.IsNoAddr())
+            addrV6 = Config.dns.outgoingAddr;
         else
-            addrV6 = Config.Addrs.udp_incoming;
+            addrV6 = Config.dns.incomingAddr;
 
         Ip::Address addrV4 = addrV6;
         addrV4.SetIPv4();
@@ -1639,6 +1639,8 @@
     idns_query *q = cbdataAlloc(idns_query);
     memcpy(q->name, master->name, sizeof(q->name));
     memcpy(q->orig, master->orig, sizeof(q->orig));
+    q->callback = NULL;
+    q->callback_data = NULL;
     q->master = master;
     q->query_id = idnsQueryID();
     q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, Config.dns.packet_max);

=== modified file 'src/htcp.cc'
--- src/htcp.cc	2012-04-25 05:29:20 +0000
+++ src/htcp.cc	2012-05-06 05:52:23 +0000
@@ -231,11 +231,9 @@
     RR_RESPONSE
 };
 
-static void htcpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo);
+static void htcpPortOpened(const Comm::ConnectionPointer &conn, int errNo);
 static uint32_t msg_id_counter = 0;
 
-static Comm::ConnectionPointer htcpOutgoingConn = NULL;
-static Comm::ConnectionPointer htcpIncomingConn = NULL;
 #define N_QUERIED_KEYS 8192
 static uint32_t queried_id[N_QUERIED_KEYS];
 static cache_key queried_keys[N_QUERIED_KEYS][SQUID_MD5_DIGEST_LENGTH];
@@ -244,7 +242,7 @@
 static MemAllocator *htcpDetailPool = NULL;
 
 static int old_squid_format = 0;
-
+bool htcpActive = false;
 
 static ssize_t htcpBuildPacket(char *buf, size_t buflen, htcpStuff * stuff);
 static htcpSpecifier *htcpUnpackSpecifier(char *buf, int sz);
@@ -272,7 +270,7 @@
 
 static void htcpRecv(int fd, void *data);
 
-static void htcpSend(const char *buf, int len, Ip::Address &to);
+static void htcpSend(const Comm::ConnectionPointer &htcpOutgoingConn, const char *buf, int len, Ip::Address &to);
 
 static void htcpTstReply(htcpDataHeader *, StoreEntry *, htcpSpecifier *, Ip::Address &);
 
@@ -600,7 +598,7 @@
 }
 
 static void
-htcpSend(const char *buf, int len, Ip::Address &to)
+htcpSend(const Comm::ConnectionPointer &htcpOutgoingConn, const char *buf, int len, Ip::Address &to)
 {
     debugs(31, 3, HERE << to);
     htcpHexdump("htcpSend", buf, len);
@@ -848,6 +846,20 @@
     return (checklist.fastCheck() == ACCESS_ALLOWED);
 }
 
+/// Locate an open HTCP port which can use for packets to the given destination
+static Comm::ConnectionPointer
+htcpLocateOutConn(const Ip::Address &sendingTo)
+{
+    // NP: we simplify the split-stack/dual-stack port management by just going split-stack
+    //     even on systems where one port would have been fine.
+    for (AnyP::PortCfg *s = Config.Sockaddr.htcp; s; s = s->next) {
+        if (Comm::IsConnOpen(s->listenConn) && sendingTo.IsIPv4() == s->s.IsIPv4()) {
+            return s->listenConn;
+        }
+    }
+    return Comm::ConnectionPointer();
+}
+
 static void
 htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Address &from)
 {
@@ -865,6 +877,10 @@
     debugs(31, 3, "htcpTstReply: response = " << stuff.response);
     stuff.msg_id = dhdr->msg_id;
 
+    const Comm::ConnectionPointer htcpOutgoingConn = htcpLocateOutConn(from);
+    if (htcpOutgoingConn == NULL)
+        return;
+
     if (spec) {
         mb.init();
         packerToMemInit(&p, &mb);
@@ -933,7 +949,7 @@
         return;
     }
 
-    htcpSend(pkt, (int) pktlen, from);
+    htcpSend(htcpOutgoingConn, pkt, (int) pktlen, from);
 }
 
 static void
@@ -949,6 +965,10 @@
     if (dhdr->F1 == 0)
         return;
 
+    const Comm::ConnectionPointer htcpOutgoingConn = htcpLocateOutConn(from);
+    if (htcpOutgoingConn == NULL)
+        return;
+
     memset(&stuff, '\0', sizeof(stuff));
 
     stuff.op = HTCP_CLR;
@@ -970,7 +990,7 @@
         return;
     }
 
-    htcpSend(pkt, (int) pktlen, from);
+    htcpSend(htcpOutgoingConn, pkt, (int) pktlen, from);
 }
 
 static void
@@ -1314,7 +1334,11 @@
             continue;
         }
 
-        htcpSend(buf, sz, p->in_addr);
+        const Comm::ConnectionPointer htcpOutgoingConn = htcpLocateOutConn(p->in_addr);
+        if (htcpOutgoingConn == NULL)
+            continue;
+
+        htcpSend(htcpOutgoingConn, buf, sz, p->in_addr);
     }
 }
 
@@ -1472,57 +1496,22 @@
 void
 htcpOpenPorts(void)
 {
-    if (Config.Port.htcp <= 0) {
+    if (!Config.Sockaddr.htcp) {
         debugs(31, DBG_IMPORTANT, "HTCP Disabled.");
         return;
     }
 
-    htcpIncomingConn = new Comm::Connection;
-    htcpIncomingConn->local = Config.Addrs.udp_incoming;
-    htcpIncomingConn->local.SetPort(Config.Port.htcp);
-
-    if (!Ip::EnableIpv6 && !htcpIncomingConn->local.SetIPv4()) {
-        debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpIncomingConn->local << " is not an IPv4 address.");
-        fatal("HTCP port cannot be opened.");
-    }
-    /* split-stack for now requires default IPv4-only HTCP */
-    if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpIncomingConn->local.IsAnyAddr()) {
-        htcpIncomingConn->local.SetIPv4();
-    }
-
-    AsyncCall::Pointer call = asyncCall(31, 2,
-                                        "htcpIncomingConnectionOpened",
-                                        Comm::UdpOpenDialer(&htcpIncomingConnectionOpened));
-
-    Ipc::StartListening(SOCK_DGRAM,
-                        IPPROTO_UDP,
-                        htcpIncomingConn,
-                        Ipc::fdnInHtcpSocket, call);
-
-    if (!Config.Addrs.udp_outgoing.IsNoAddr()) {
-        htcpOutgoingConn = new Comm::Connection;
-        htcpOutgoingConn->local = Config.Addrs.udp_outgoing;
-        htcpOutgoingConn->local.SetPort(Config.Port.htcp);
-
-        if (!Ip::EnableIpv6 && !htcpOutgoingConn->local.SetIPv4()) {
-            debugs(31, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << htcpOutgoingConn->local << " is not an IPv4 address.");
-            fatal("HTCP port cannot be opened.");
-        }
-        /* split-stack for now requires default IPv4-only HTCP */
-        if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && htcpOutgoingConn->local.IsAnyAddr()) {
-            htcpOutgoingConn->local.SetIPv4();
-        }
-
-        enter_suid();
-        comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, htcpOutgoingConn, "Outgoing HTCP Socket");
-        leave_suid();
-
-        if (!Comm::IsConnOpen(htcpOutgoingConn))
-            fatal("Cannot open Outgoing HTCP Socket");
-
-        Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
-
-        debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
+    for (AnyP::PortCfg *s = Config.Sockaddr.htcp; s; s = s->next) {
+        s->listenConn  = new Comm::Connection;
+
+        AsyncCall::Pointer call = asyncCall(31, 2,
+                                            "htcpPortOpened",
+                                            Comm::UdpOpenDialer(&htcpPortOpened));
+
+        Ipc::StartListening(SOCK_DGRAM,
+                            IPPROTO_UDP,
+                            s->listenConn,
+                            Ipc::fdnInHtcpSocket, call);
     }
 
     if (!htcpDetailPool) {
@@ -1530,25 +1519,27 @@
     }
 }
 
-static void
-htcpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int)
+void
+htcpPortOpened(const Comm::ConnectionPointer &conn, int errNo)
 {
-    if (!Comm::IsConnOpen(conn))
-        fatal("Cannot open HTCP Socket");
-
-    Comm::SetSelect(conn->fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
-
-    debugs(31, DBG_CRITICAL, "Accepting HTCP messages on " << conn->local);
-
-    if (Config.Addrs.udp_outgoing.IsNoAddr()) {
-        htcpOutgoingConn = conn;
-        debugs(31, DBG_IMPORTANT, "Sending HTCP messages from " << htcpOutgoingConn->local);
-    }
+    if (Comm::IsConnOpen(conn)) {
+        Comm::SetSelect(conn->fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
+        debugs(31, DBG_CRITICAL, "Accepting HTCP messages on " << conn->local);
+        fd_note(conn->fd, "HTCP Port");
+        htcpActive = true;
+    } else
+        debugs(31, DBG_CRITICAL, "ERROR: Cannot open HTCP port " << conn->local);
 }
 
 int
 htcpQuery(StoreEntry * e, HttpRequest * req, peer * p)
 {
+    const Comm::ConnectionPointer htcpOutgoingConn = htcpLocateOutConn(p->in_addr);
+    if (htcpOutgoingConn == NULL) {
+        debugs(15, DBG_CRITICAL, "HTCP is disabled! Cannot send HTCP request to peer.");
+        return 0;
+    }
+
     cache_key *save_key;
     static char pkt[8192];
     ssize_t pktlen;
@@ -1559,9 +1550,6 @@
     MemBuf mb;
     http_state_flags flags;
 
-    if (!Comm::IsConnOpen(htcpIncomingConn))
-        return 0;
-
     old_squid_format = p->options.htcp_oldsquid;
     memset(&flags, '\0', sizeof(flags));
     snprintf(vbuf, sizeof(vbuf), "%d/%d",
@@ -1588,7 +1576,7 @@
         return -1;
     }
 
-    htcpSend(pkt, (int) pktlen, p->in_addr);
+    htcpSend(htcpOutgoingConn, pkt, (int) pktlen, p->in_addr);
 
     queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id;
     save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS];
@@ -1614,7 +1602,8 @@
     MemBuf mb;
     http_state_flags flags;
 
-    if (!Comm::IsConnOpen(htcpIncomingConn))
+    const Comm::ConnectionPointer htcpOutgoingConn = htcpLocateOutConn(p->in_addr);
+    if (htcpOutgoingConn == NULL)
         return;
 
     old_squid_format = p->options.htcp_oldsquid;
@@ -1667,51 +1656,24 @@
         return;
     }
 
-    htcpSend(pkt, (int) pktlen, p->in_addr);
+    htcpSend(htcpOutgoingConn, pkt, (int) pktlen, p->in_addr);
 }
 
-/*
- * htcpSocketShutdown only closes the 'in' socket if it is
- * different than the 'out' socket.
+/**
+ * Close all HTCP ports.
+ * This terminates all HTCP traffic.
  */
 void
-htcpSocketShutdown(void)
-{
-    if (!Comm::IsConnOpen(htcpIncomingConn))
-        return;
-
-    debugs(12, DBG_IMPORTANT, "Stop accepting HTCP on " << htcpIncomingConn->local);
-    /*
-     * Here we just unlink htcpIncomingConn because the HTCP 'in'
-     * and 'out' sockets might be just one FD.  This prevents this
-     * function from executing repeatedly.  When we are really ready to
-     * exit or restart, main will comm_close the 'out' descriptor.
-     */
-    htcpIncomingConn = NULL;
-
-    /*
-     * Normally we only write to the outgoing HTCP socket, but
-     * we also have a read handler there to catch messages sent
-     * to that specific interface.  During shutdown, we must
-     * disable reading on the outgoing socket.
-     */
-    /* XXX Don't we need this handler to read replies while shutting down?
-     * I think there should be a separate hander for reading replies..
-     */
-    assert(Comm::IsConnOpen(htcpOutgoingConn));
-
-    Comm::SetSelect(htcpOutgoingConn->fd, COMM_SELECT_READ, NULL, NULL, 0);
-}
-
-void
 htcpClosePorts(void)
 {
-    htcpSocketShutdown();
+    for (AnyP::PortCfg *s = Config.Sockaddr.htcp; s; s = s->next) {
+        if (!Comm::IsConnOpen(s->listenConn))
+            continue;
 
-    if (htcpOutgoingConn != NULL) {
-        debugs(12, DBG_IMPORTANT, "Stop sending HTCP from " << htcpOutgoingConn->local);
-        htcpOutgoingConn = NULL;
+        s->listenConn->close();
+        debugs(12, DBG_IMPORTANT, "Stop accepting HTCP on " << s->listenConn->local);
     }
+    htcpActive = false;
 }
 
 static void

=== modified file 'src/htcp.h'
--- src/htcp.h	2012-04-25 05:29:20 +0000
+++ src/htcp.h	2012-04-25 05:50:54 +0000
@@ -55,6 +55,9 @@
     } cto;
 };
 
+/// whether any HTCP ports are currently open
+extern bool htcpActive;
+
 /// \bug redundant typedef
 typedef class HtcpReplyData htcpReplyData;
 

=== modified file 'src/icp_v2.cc'
--- src/icp_v2.cc	2012-04-25 05:29:20 +0000
+++ src/icp_v2.cc	2012-04-25 05:51:18 +0000
@@ -56,7 +56,7 @@
 #include "ipcache.h"
 #include "rfc1738.h"
 
-static void icpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo);
+static void icpPortOpened(const Comm::ConnectionPointer &conn, int errNo);
 
 /// \ingroup ServerProtocolICPInternal2
 static void icpLogIcp(const Ip::Address &, log_type, int, const char *, int);
@@ -75,11 +75,8 @@
 static icpUdpData *IcpQueueHead = NULL;
 /// \ingroup ServerProtocolICPInternal2
 static icpUdpData *IcpQueueTail = NULL;
+bool icpActive = false;
 
-/// \ingroup ServerProtocolICPInternal2
-Comm::ConnectionPointer icpIncomingConn = NULL;
-/// \ingroup ServerProtocolICPInternal2
-Comm::ConnectionPointer icpOutgoingConn = NULL;
 
 /* icp_common_t */
 _icp_common_t::_icp_common_t() : opcode(ICP_INVALID), version(0), length(0), reqnum(0), flags(0), pad(0), shostid(0)
@@ -223,7 +220,7 @@
     while ((q = IcpQueueHead) != NULL) {
         int delay = tvSubUsec(q->queue_time, current_time);
         /* increment delay to prevent looping */
-        const int x = icpUdpSend(fd, q->address, (icp_common_t *) q->msg, q->logcode, ++delay);
+        const int x = icpUdpSend(q->peerDetail, q->address, (icp_common_t *) q->msg, q->logcode, ++delay);
         IcpQueueHead = q->next;
         xfree(q);
 
@@ -277,31 +274,48 @@
     return (icp_common_t *)buf;
 }
 
+Comm::ConnectionPointer
+icpLocateOutConn(const Ip::Address &sendingTo)
+{
+    // NP: we simplify the split-stack/dual-stack port management by just going split-stack
+    //     even on systems where one port would have been fine.
+    for (AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (Comm::IsConnOpen(s->listenConn) && sendingTo.IsIPv4() == s->s.IsIPv4()) {
+            return s->listenConn;
+        }
+    }
+    return Comm::ConnectionPointer();
+}
+
 int
-icpUdpSend(int fd,
-           const Ip::Address &to,
-           icp_common_t * msg,
-           log_type logcode,
-           int delay)
+icpUdpSend(const peer *p, const Ip::Address &from, icp_common_t * msg, log_type logcode, int delay)
 {
+    const Comm::ConnectionPointer icpOutConn = icpLocateOutConn(p?p->in_addr:from);
+    if (icpOutConn == NULL)
+        return 0;
+
     icpUdpData *queue;
     int x;
     int len;
     len = (int) ntohs(msg->length);
-    debugs(12, 5, "icpUdpSend: FD " << fd << " sending " <<
-           icp_opcode_str[msg->opcode] << ", " << len << " bytes to " << to);
-
-    x = comm_udp_sendto(fd, to, msg, len);
+    debugs(12, 5, HERE << "FD " << icpOutConn->fd << ", " << icpOutConn->local << " sending " <<
+           icp_opcode_str[msg->opcode] << ", " << len << " bytes to " << (p?p->in_addr:from));
+
+    if (p && p->type == PEER_MULTICAST)
+        mcastSetTtl(icpOutConn->fd, p->mcast.ttl);
+
+    x = comm_udp_sendto(icpOutConn->fd, (p?p->in_addr:from), msg, len);
 
     if (x >= 0) {
         /* successfully written */
-        icpLogIcp(to, logcode, len, (char *) (msg + 1), delay);
+        icpLogIcp((p?p->in_addr:from), logcode, len, (char *) (msg + 1), delay);
         icpCount(msg, SENT, (size_t) len, delay);
         safe_free(msg);
     } else if (0 == delay) {
         /* send failed, but queue it */
         queue = (icpUdpData *) xcalloc(1, sizeof(icpUdpData));
-        queue->address = to;
+        queue->peerDetail = p;
+        queue->address = (p?p->in_addr:from);
         queue->msg = msg;
         queue->len = (int) ntohs(msg->length);
         queue->queue_time = current_time;
@@ -318,7 +332,7 @@
             IcpQueueTail = queue;
         }
 
-        Comm::SetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, NULL, 0);
+        Comm::SetSelect(icpOutConn->fd, COMM_SELECT_WRITE, icpUdpSendQueue, NULL, 0);
         statCounter.icp.replies_queued++;
     } else {
         /* don't queue it */
@@ -392,7 +406,8 @@
 icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from)
 {
     icp_common_t *reply = _icp_common_t::createMessage(opcode, flags, url, reqnum, pad);
-    icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0);
+
+    icpUdpSend((peer *)NULL, from, reply, icpLogFromICPCode(opcode), 0);
 }
 
 void
@@ -643,9 +658,14 @@
 
         icp_version = (int) buf[1];	/* cheat! */
 
-        if (icpOutgoingConn->local == from)
+        // TODO: find a faster way to ignore packets that we sent
+        bool ourPacket = false;
+        for (const AnyP::PortCfg *s = Config.Sockaddr.icp; s && !ourPacket; s=s->next)
+            ourPacket = (s->listenConn->local == from);
+
+        if (ourPacket)
             // ignore ICP packets which loop back (multicast usually)
-            debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself");
+            debugs(12, 4, HERE << "Ignoring ICP packet sent by myself");
         else if (icp_version == ICP_VERSION_2)
             icpHandleIcpV2(sock, from, buf, len);
         else if (icp_version == ICP_VERSION_3)
@@ -659,120 +679,57 @@
 void
 icpOpenPorts(void)
 {
-    uint16_t port;
-
-    if ((port = Config.Port.icp) <= 0)
+    if (!Config.Sockaddr.icp) {
+        debugs(31, DBG_IMPORTANT, "ICP Disabled.");
         return;
-
-    icpIncomingConn = new Comm::Connection;
-    icpIncomingConn->local = Config.Addrs.udp_incoming;
-    icpIncomingConn->local.SetPort(port);
-
-    if (!Ip::EnableIpv6 && !icpIncomingConn->local.SetIPv4()) {
-        debugs(12, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpIncomingConn->local << " is not an IPv4 address.");
-        fatal("ICP port cannot be opened.");
-    }
-    /* split-stack for now requires default IPv4-only ICP */
-    if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpIncomingConn->local.IsAnyAddr()) {
-        icpIncomingConn->local.SetIPv4();
-    }
-
-    AsyncCall::Pointer call = asyncCall(12, 2,
-                                        "icpIncomingConnectionOpened",
-                                        Comm::UdpOpenDialer(&icpIncomingConnectionOpened));
-
-    Ipc::StartListening(SOCK_DGRAM,
-                        IPPROTO_UDP,
-                        icpIncomingConn,
-                        Ipc::fdnInIcpSocket, call);
-
-    if ( !Config.Addrs.udp_outgoing.IsNoAddr() ) {
-        icpOutgoingConn = new Comm::Connection;
-        icpOutgoingConn->local = Config.Addrs.udp_outgoing;
-        icpOutgoingConn->local.SetPort(port);
-
-        if (!Ip::EnableIpv6 && !icpOutgoingConn->local.SetIPv4()) {
-            debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << icpOutgoingConn->local << " is not an IPv4 address.");
-            fatal("ICP port cannot be opened.");
-        }
-        /* split-stack for now requires default IPv4-only ICP */
-        if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && icpOutgoingConn->local.IsAnyAddr()) {
-            icpOutgoingConn->local.SetIPv4();
-        }
-
-        enter_suid();
-        comm_open_listener(SOCK_DGRAM, IPPROTO_UDP, icpOutgoingConn, "Outgoing ICP Port");
-        leave_suid();
-
-        if (!Comm::IsConnOpen(icpOutgoingConn))
-            fatal("Cannot open Outgoing ICP Port");
-
-        debugs(12, DBG_CRITICAL, "Sending ICP messages from " << icpOutgoingConn->local);
-
-        Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
-        fd_note(icpOutgoingConn->fd, "Outgoing ICP socket");
+    }
+
+    for (AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        s->listenConn  = new Comm::Connection;
+
+        AsyncCall::Pointer call = asyncCall(12, 2,
+                                            "icpPortOpened",
+                                            Comm::UdpOpenDialer(&icpPortOpened));
+
+        Ipc::StartListening(SOCK_DGRAM,
+                            IPPROTO_UDP,
+                            s->listenConn,
+                            Ipc::fdnInIcpSocket, call);
     }
 }
 
 static void
-icpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo)
+icpPortOpened(const Comm::ConnectionPointer &conn, int errNo)
 {
-    if (!Comm::IsConnOpen(conn))
-        fatal("Cannot open ICP Port");
-
-    Comm::SetSelect(conn->fd, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
-
-    for (const wordlist *s = Config.mcast_group_list; s; s = s->next)
-        ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL); // XXX: pass the conn for mcastJoinGroups usage.
-
-    debugs(12, DBG_IMPORTANT, "Accepting ICP messages on " << conn->local);
-
-    fd_note(conn->fd, "Incoming ICP port");
-
-    if (Config.Addrs.udp_outgoing.IsNoAddr()) {
-        icpOutgoingConn = conn;
-        debugs(12, DBG_IMPORTANT, "Sending ICP messages from " << icpOutgoingConn->local);
-    }
+    if (Comm::IsConnOpen(conn)) {
+        Comm::SetSelect(conn->fd, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
+        for (const wordlist *s = Config.mcast_group_list; s; s = s->next) {
+            // XXX: yuck. we need a better way to do this.
+            Comm::ConnectionPointer *tmp = new Comm::ConnectionPointer(conn);
+            ipcache_nbgethostbyname(s->key, mcastJoinGroups, new generic_cbdata(tmp));
+        }
+        debugs(12, DBG_IMPORTANT, "Accepting ICP messages on " << conn->local);
+        fd_note(conn->fd, "ICP port");
+        icpActive = true;
+    } else
+        debugs(12, DBG_CRITICAL, "ERROR: Cannot open ICP port " << conn->local);
 }
 
 /**
- * icpConnectionShutdown only closes the 'in' socket if it is
- * different than the 'out' socket.
+ * Close all ICP ports.
+ * This terminates all ICP traffic.
  */
 void
-icpConnectionShutdown(void)
-{
-    if (!Comm::IsConnOpen(icpIncomingConn))
-        return;
-
-    debugs(12, DBG_IMPORTANT, "Stop receiving ICP on " << icpIncomingConn->local);
-
-    /** Release the 'in' socket for lazy closure.
-     * in and out sockets may be sharing one same FD.
-     * This prevents this function from executing repeatedly.
-     */
-    icpIncomingConn = NULL;
-
-    /**
-     * Normally we only write to the outgoing ICP socket, but
-     * we also have a read handler there to catch messages sent
-     * to that specific interface.  During shutdown, we must
-     * disable reading on the outgoing socket.
-     */
-    assert(Comm::IsConnOpen(icpOutgoingConn));
-
-    Comm::SetSelect(icpOutgoingConn->fd, COMM_SELECT_READ, NULL, NULL, 0);
-}
-
-void
 icpClosePorts(void)
 {
-    icpConnectionShutdown();
+    for (AnyP::PortCfg *s = Config.Sockaddr.icp; s; s = s->next) {
+        if (!Comm::IsConnOpen(s->listenConn))
+            continue;
 
-    if (icpOutgoingConn != NULL) {
-        debugs(12, DBG_IMPORTANT, "Stop sending ICP from " << icpOutgoingConn->local);
-        icpOutgoingConn = NULL;
+        s->listenConn->close();
+        debugs(12, DBG_IMPORTANT, "Stop accepting ICP on " << s->listenConn->local);
     }
+    icpActive = false;
 }
 
 static void

=== modified file 'src/main.cc'
--- src/main.cc	2012-04-25 05:29:20 +0000
+++ src/main.cc	2012-04-25 05:53:33 +0000
@@ -139,7 +139,6 @@
 #endif
 
 static char *opt_syslog_facility = NULL;
-static int icpPortNumOverride = 1;	/* Want to detect "-u 0" */
 static int configured_once = 0;
 #if MALLOC_DBG
 static int malloc_debug_level = 0;
@@ -393,7 +392,7 @@
         case 'a':
             /** \par a
              * Add optional HTTP port as given following the option */
-            add_http_port(optarg);
+            add_generic_port(optarg,"http");
             break;
 
         case 'd':
@@ -532,13 +531,8 @@
 
         case 'u':
             /** \par u
-             * Store the ICP port number given in global option icpPortNumOverride
-             * ensuring its a positive number. */
-            icpPortNumOverride = atoi(optarg);
-
-            if (icpPortNumOverride < 0)
-                icpPortNumOverride = 0;
-
+             * Add optional ICP port as given following the option */
+            add_generic_port(optarg,"icp");
             break;
 
         case 'v':
@@ -707,9 +701,9 @@
     }
     if (IamWorkerProcess()) {
         clientHttpConnectionsClose();
-        icpConnectionShutdown();
+        icpClosePorts();
 #if USE_HTCP
-        htcpSocketShutdown();
+        htcpClosePorts();
 #endif
 
         icmpEngine.Close();
@@ -961,9 +955,6 @@
 
     setEffectiveUser();
 
-    if (icpPortNumOverride != 1)
-        Config.Port.icp = (unsigned short) icpPortNumOverride;
-
     _db_init(Debug::cache_log, Debug::debugOptions);
 
     fd_open(fileno(debug_log), FD_LOG, Debug::cache_log);

=== modified file 'src/multicast.cc'
--- src/multicast.cc	2012-01-20 18:55:04 +0000
+++ src/multicast.cc	2012-03-21 07:07:47 +0000
@@ -54,14 +54,20 @@
 }
 
 void
-mcastJoinGroups(const ipcache_addrs *ia, const DnsLookupDetails &, void *datanotused)
+mcastJoinGroups(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
 {
 #ifdef IP_MULTICAST_TTL
+    // XXX: yuck. when we have a UDP receiver Job that will be the data.
+    generic_cbdata *d = static_cast<generic_cbdata *>(data);
+    Comm::ConnectionPointer *conn;
+    d->unwrap(&conn); // deletes itself
+
     struct ip_mreq mr;
     int i;
 
     if (ia == NULL) {
         debugs(7, 0, "comm_join_mcast_groups: Unknown host");
+        delete conn;
         return;
     }
 
@@ -77,13 +83,13 @@
 
         mr.imr_interface.s_addr = INADDR_ANY;
 
-        if (setsockopt(icpIncomingConn->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mr, sizeof(struct ip_mreq)) < 0)
-            debugs(7, DBG_IMPORTANT, "ERROR: Join failed for " << icpIncomingConn << ", Multicast IP=" << ia->in_addrs[i]);
+        if (setsockopt((*conn)->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mr, sizeof(struct ip_mreq)) < 0)
+            debugs(7, DBG_IMPORTANT, "ERROR: Join failed for " << (*conn)->local << ", Multicast IP=" << ia->in_addrs[i]);
 
         char c = 0;
-        if (setsockopt(icpIncomingConn->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &c, 1) < 0)
-            debugs(7, DBG_IMPORTANT, "ERROR: " << icpIncomingConn << " can't disable multicast loopback: " << xstrerror());
+        if (setsockopt((*conn)->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &c, 1) < 0)
+            debugs(7, DBG_IMPORTANT, "ERROR: " << (*conn)->local << " can't disable multicast loopback: " << xstrerror());
     }
-
+    delete conn;
 #endif
 }

=== modified file 'src/neighbors.cc'
--- src/neighbors.cc	2012-04-25 05:29:20 +0000
+++ src/neighbors.cc	2012-04-25 05:32:36 +0000
@@ -520,7 +520,11 @@
                         "Peer Cache Statistics",
                         neighborDumpPeers, 0, 1);
 
-    if (Comm::IsConnOpen(icpIncomingConn)) {
+#if USE_HTCP
+    if (htcpActive || icpActive) {
+#else
+    if (icpActive) {
+#endif
         Mgr::RegisterAction("non_peers",
                             "List of Unknown sites sending ICP messages",
                             neighborDumpNonPeers, 0, 1);
@@ -537,7 +541,11 @@
 
     neighborsRegisterWithCacheManager();
 
-    if (Comm::IsConnOpen(icpIncomingConn)) {
+#if USE_HTCP
+    if (htcpActive || icpActive) {
+#else
+    if (icpActive) {
+#endif
 
         for (thisPeer = Config.peers; thisPeer; thisPeer = next) {
             next = thisPeer->next;
@@ -622,29 +630,22 @@
 
 #if USE_HTCP
         if (p->options.htcp && !p->options.htcp_only_clr) {
-            if (Config.Port.htcp <= 0) {
-                debugs(15, DBG_CRITICAL, "HTCP is disabled! Cannot send HTCP request to peer.");
-                continue;
-            }
-
             debugs(15, 3, "neighborsUdpPing: sending HTCP query");
             if (htcpQuery(entry, request, p) <= 0)
                 continue; // unable to send.
         } else
 #endif
         {
-            if (Config.Port.icp <= 0 || !Comm::IsConnOpen(icpOutgoingConn)) {
+            if (!icpActive) {
                 debugs(15, DBG_CRITICAL, "ICP is disabled! Cannot send ICP request to peer.");
                 continue;
             } else {
 
-                if (p->type == PEER_MULTICAST)
-                    mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
-
                 if (p->icp.port == echo_port) {
                     debugs(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping");
                     query = _icp_common_t::createMessage(ICP_DECHO, 0, url, reqnum, 0);
-                    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
+                    if (icpUdpSend(p, p->in_addr, query, LOG_ICP_QUERY, 0) <= 0)
+                        continue; // unable to send.
                 } else {
                     flags = 0;
 
@@ -654,7 +655,8 @@
 
                     query = _icp_common_t::createMessage(ICP_QUERY, flags, url, reqnum, 0);
 
-                    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
+                    if (icpUdpSend(p, p->in_addr, query, LOG_ICP_QUERY, 0) <= 0)
+                        continue; // unable to send.
                 }
             }
         }
@@ -1388,11 +1390,10 @@
     mem->start_ping = current_time;
     mem->ping_reply_callback = peerCountHandleIcpReply;
     mem->ircb_data = psstate;
-    mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
     p->mcast.id = mem->id;
     reqnum = icpSetCacheKey((const cache_key *)fake->key);
     query = _icp_common_t::createMessage(ICP_QUERY, 0, url, reqnum, 0);
-    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
+    icpUdpSend(p, p->in_addr, query, LOG_ICP_QUERY, 0);
     fake->ping_status = PING_WAITING;
     eventAdd("peerCountMcastPeersDone",
              peerCountMcastPeersDone,

=== modified file 'src/protos.h'
--- src/protos.h	2012-04-25 05:29:20 +0000
+++ src/protos.h	2012-04-25 05:32:36 +0000
@@ -66,7 +66,7 @@
 class MemBuf;
 SQUIDCEXTERN void wordlistCat(const wordlist *, MemBuf * mb);
 SQUIDCEXTERN void self_destruct(void);
-SQUIDCEXTERN void add_http_port(char *portspec);
+SQUIDCEXTERN void add_generic_port(char *portspec, const char *protocol);
 extern int xatoi(const char *token);
 extern long xatol(const char *token);
 

=== modified file 'src/send-announce.cc'
--- src/send-announce.cc	2012-01-20 18:55:04 +0000
+++ src/send-announce.cc	2012-03-21 07:07:47 +0000
@@ -34,6 +34,7 @@
  */
 
 #include "squid-old.h"
+#include "anyp/PortCfg.h"
 #include "comm/Connection.h"
 #include "event.h"
 #include "fde.h"
@@ -46,10 +47,7 @@
 void
 start_announce(void *datanotused)
 {
-    if (0 == Config.onoff.announce)
-        return;
-
-    if (!Comm::IsConnOpen(icpOutgoingConn))
+    if (!Config.onoff.announce || !icpActive)
         return;
 
     ipcache_nbgethostbyname(Config.Announce.host, send_announce, NULL);
@@ -75,15 +73,29 @@
         return;
     }
 
-    debugs(27, 1, "Sending Announcement to " << host);
+    // XXX: re-work this send functionality to support
+    //      - multiple IP results,
+    //      - multiple listening ports,
+    //      - multiple transport types,
+    //      - non-ICP probe ports
+    // Does IRcache announce need the whole matrix for backward compatibility or can we alter/extend the lines here freely?
+    Ip::Address S = ia->in_addrs[0];
+    S.SetPort(port);
+
+    debugs(27, DBG_IMPORTANT, "Sending Announcement to " << host);
     sndbuf[0] = '\0';
     snprintf(tbuf, 256, "cache_version SQUID/%s\n", version_string);
     strcat(sndbuf, tbuf);
-    assert(Config.Sockaddr.http);
-    snprintf(tbuf, 256, "Running on %s %d %d\n",
+    const Comm::ConnectionPointer conn = icpLocateOutConn(S);
+    if (!Comm::IsConnOpen(conn)) {
+        debugs(27, DBG_IMPORTANT, "send_announce: Cannot send to " << host << " primary IP '" << S << "'");
+        return;
+    }
+
+    snprintf(tbuf, 256, "Running on %s %d %u\n",
              getMyHostname(),
              getMyPort(),
-             (int) Config.Port.icp);
+             conn->local.GetPort()); // NP: advertise the port this peer should be able to contact
     strcat(sndbuf, tbuf);
 
     if (Config.adminEmail) {
@@ -110,10 +122,6 @@
         }
     }
 
-    Ip::Address S = ia->in_addrs[0];
-    S.SetPort(port);
-    assert(Comm::IsConnOpen(icpOutgoingConn));
-
-    if (comm_udp_sendto(icpOutgoingConn->fd, S, sndbuf, strlen(sndbuf) + 1) < 0)
-        debugs(27, 1, "ERROR: Failed to announce to " << S << " from " << icpOutgoingConn->local << ": " << xstrerror());
+    if (comm_udp_sendto(conn->fd, S, sndbuf, strlen(sndbuf) + 1) < 0)
+        debugs(27, DBG_IMPORTANT, "ERROR: Failed to announce to " << S << " from " << conn->local << ": " << xstrerror());
 }

=== modified file 'src/snmp_core.cc'
--- src/snmp_core.cc	2012-04-25 05:29:20 +0000
+++ src/snmp_core.cc	2012-04-25 05:32:36 +0000
@@ -31,6 +31,7 @@
  */
 #include "squid-old.h"
 #include "acl/FilledChecklist.h"
+#include "anyp/PortCfg.h"
 #include "base/CbcPointer.h"
 #include "comm.h"
 #include "comm/Connection.h"
@@ -45,9 +46,7 @@
 
 mib_tree_entry *mib_tree_head;
 mib_tree_entry *mib_tree_last;
-
-Comm::ConnectionPointer snmpIncomingConn;
-Comm::ConnectionPointer snmpOutgoingConn;
+bool snmpActive = false;
 
 static mib_tree_entry * snmpAddNodeStr(const char *base_str, int o, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType = atNone);
 static mib_tree_entry *snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType, int children,...);
@@ -68,14 +67,7 @@
 static mib_tree_entry *snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current);
 extern "C" void snmpSnmplibDebug(int lvl, char *buf);
 
-/*
- * The functions used during startup:
- * snmpInit
- * snmpConnectionOpen
- * snmpConnectionClose
- */
-
-/*
+/**
  * Turns the MIB into a Tree structure. Called during the startup process.
  */
 void
@@ -269,81 +261,46 @@
 void
 snmpOpenPorts(void)
 {
-    debugs(49, 5, "snmpConnectionOpen: Called");
-
-    if (Config.Port.snmp <= 0)
+    if (!Config.Sockaddr.snmp)
         return;
 
-    snmpIncomingConn = new Comm::Connection;
-    snmpIncomingConn->local = Config.Addrs.snmp_incoming;
-    snmpIncomingConn->local.SetPort(Config.Port.snmp);
-
-    if (!Ip::EnableIpv6 && !snmpIncomingConn->local.SetIPv4()) {
-        debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << snmpIncomingConn->local << " is not an IPv4 address.");
-        fatal("SNMP port cannot be opened.");
-    }
-    /* split-stack for now requires IPv4-only SNMP */
-    if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && snmpIncomingConn->local.IsAnyAddr()) {
-        snmpIncomingConn->local.SetIPv4();
-    }
-
-    AsyncCall::Pointer call = asyncCall(49, 2, "snmpIncomingConnectionOpened",
-                                        Comm::UdpOpenDialer(&snmpPortOpened));
-    Ipc::StartListening(SOCK_DGRAM, IPPROTO_UDP, snmpIncomingConn, Ipc::fdnInSnmpSocket, call);
-
-    if (!Config.Addrs.snmp_outgoing.IsNoAddr()) {
-        snmpOutgoingConn = new Comm::Connection;
-        snmpOutgoingConn->local = Config.Addrs.snmp_outgoing;
-        snmpOutgoingConn->local.SetPort(Config.Port.snmp);
-
-        if (!Ip::EnableIpv6 && !snmpOutgoingConn->local.SetIPv4()) {
-            debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << snmpOutgoingConn->local << " is not an IPv4 address.");
-            fatal("SNMP port cannot be opened.");
-        }
-        /* split-stack for now requires IPv4-only SNMP */
-        if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && snmpOutgoingConn->local.IsAnyAddr()) {
-            snmpOutgoingConn->local.SetIPv4();
-        }
-        AsyncCall::Pointer call = asyncCall(49, 2, "snmpOutgoingConnectionOpened",
+    for (AnyP::PortCfg *s = Config.Sockaddr.snmp; s; s = s->next) {
+        s->listenConn  = new Comm::Connection;
+
+        AsyncCall::Pointer call = asyncCall(12, 2,
+                                            "snmpPortOpened",
                                             Comm::UdpOpenDialer(&snmpPortOpened));
-        Ipc::StartListening(SOCK_DGRAM, IPPROTO_UDP, snmpOutgoingConn, Ipc::fdnOutSnmpSocket, call);
-    } else {
-        snmpOutgoingConn = snmpIncomingConn;
-        debugs(1, DBG_IMPORTANT, "Sending SNMP messages from " << snmpOutgoingConn->local);
+
+        Ipc::StartListening(SOCK_DGRAM,
+                            IPPROTO_UDP,
+                            s->listenConn,
+                            Ipc::fdnInSnmpSocket, call);
     }
 }
 
 static void
 snmpPortOpened(const Comm::ConnectionPointer &conn, int errNo)
 {
-    if (!Comm::IsConnOpen(conn))
-        fatalf("Cannot open SNMP %s Port",(conn->fd == snmpIncomingConn->fd?"receiving":"sending"));
-
-    Comm::SetSelect(conn->fd, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
-
-    if (conn->fd == snmpIncomingConn->fd)
-        debugs(1, DBG_IMPORTANT, "Accepting SNMP messages on " << snmpIncomingConn->local);
-    else if (conn->fd == snmpOutgoingConn->fd)
-        debugs(1, DBG_IMPORTANT, "Sending SNMP messages from " << snmpOutgoingConn->local);
-    else
-        fatalf("Lost SNMP port (%d) on FD %d", (int)conn->local.GetPort(), conn->fd);
+    if (Comm::IsConnOpen(conn)) {
+        Comm::SetSelect(conn->fd, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
+        debugs(1, DBG_IMPORTANT, "Accepting SNMP messages on " << conn->local);
+        fd_note(conn->fd, "SNMP port");
+        snmpActive = true;
+    } else
+        debugs(49, DBG_CRITICAL, "ERROR: Cannot open SNMP port " << conn->local);
 }
 
 void
 snmpClosePorts(void)
 {
-    if (Comm::IsConnOpen(snmpIncomingConn)) {
-        debugs(49, DBG_IMPORTANT, "Closing SNMP receiving port " << snmpIncomingConn->local);
-        snmpIncomingConn->close();
-    }
-    snmpIncomingConn = NULL;
+    for (AnyP::PortCfg *s = Config.Sockaddr.snmp; s; s = s->next) {
+        if (!Comm::IsConnOpen(s->listenConn))
+            continue;
 
-    if (Comm::IsConnOpen(snmpOutgoingConn) && snmpIncomingConn != snmpOutgoingConn) {
-        // Perform OUT port closure so as not to step on IN port when sharing a conn.
-        debugs(49, DBG_IMPORTANT, "Closing SNMP sending port " << snmpOutgoingConn->local);
-        snmpOutgoingConn->close();
+        s->listenConn->close();
+        debugs(12, DBG_IMPORTANT, "Stop accepting SNMP on " << s->listenConn->local);
     }
-    snmpOutgoingConn = NULL;
+    snmpActive = false;
 }
 
 /*

=== modified file 'src/snmp_core.h'
--- src/snmp_core.h	2011-06-04 12:48:45 +0000
+++ src/snmp_core.h	2012-03-06 12:50:45 +0000
@@ -35,6 +35,7 @@
 extern struct snmp_pdu* snmpAgentResponse(struct snmp_pdu* PDU);
 extern AggrType snmpAggrType(oid* Current, snint CurrentLen);
 
-extern Comm::ConnectionPointer snmpOutgoingConn;
+/// whether any SNMP ports are currently open
+extern bool snmpActive;
 
 #endif /* SQUID_SNMP_CORE_H */

=== modified file 'src/structs.h'
--- src/structs.h	2012-04-26 01:04:17 +0000
+++ src/structs.h	2012-05-08 01:31:14 +0000
@@ -235,22 +235,17 @@
     acl_size_t *ReplyBodySize;
 
     struct {
-        unsigned short icp;
-#if USE_HTCP
-
-        unsigned short htcp;
-#endif
-#if SQUID_SNMP
-
-        unsigned short snmp;
-#endif
-    } Port;
-
-    struct {
         AnyP::PortCfg *http;
 #if USE_SSL
         AnyP::PortCfg *https;
 #endif
+        AnyP::PortCfg *icp;
+#if USE_HTCP
+        AnyP::PortCfg *htcp;
+#endif
+#if SQUID_SNMP
+        AnyP::PortCfg *snmp;
+#endif
     } Sockaddr;
 #if SQUID_SNMP
 
@@ -352,13 +347,6 @@
     } Announce;
 
     struct {
-
-        Ip::Address udp_incoming;
-        Ip::Address udp_outgoing;
-#if SQUID_SNMP
-        Ip::Address snmp_incoming;
-        Ip::Address snmp_outgoing;
-#endif
         /* FIXME INET6 : this should really be a CIDR value */
         Ip::Address client_netmask;
     } Addrs;
@@ -643,6 +631,8 @@
     int client_ip_max_connections;
 
     struct {
+        Ip::Address incomingAddr;
+        Ip::Address outgoingAddr;
         int v4_first;       ///< Place IPv4 first in the order of DNS results.
         ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
     } dns;

=== modified file 'src/tools.cc'
--- src/tools.cc	2012-04-25 05:29:20 +0000
+++ src/tools.cc	2012-04-25 05:55:34 +0000
@@ -96,8 +96,14 @@
 
     // clear icp_port's
     icpClosePorts();
+#if USE_HTCP
+    htcpClosePorts();
+#endif
+#if SQUID_SNMP
+    snmpClosePorts();
+#endif
 
-    // XXX: Why not the HTCP, SNMP, DNS ports as well?
+    // XXX: Why not the DNS ports as well?
     // XXX: why does this differ from main closeServerConnections() anyway ?
 }
 

