Improve support for IPv4 vs IPv6 virtual hosts (fixes SF bugs #1087699, #1087668).
authorMichael Poole <mdpoole@troilus.org>
Wed, 29 Dec 2004 03:08:09 +0000 (03:08 +0000)
committerMichael Poole <mdpoole@troilus.org>
Wed, 29 Dec 2004 03:08:09 +0000 (03:08 +0000)
git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1290 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ChangeLog
doc/example.conf
include/s_bsd.h
include/uping.h
ircd/ircd_auth.c
ircd/ircd_lexer.l
ircd/ircd_parser.y
ircd/ircd_res.c
ircd/os_generic.c
ircd/s_bsd.c
ircd/uping.c

index 1767505c66843299abe88226d07bc3ff55cb624f..405c659a38216aa6e6298417836abe7ed042419f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,46 @@
+2004-12-28  Michael Poole <mdpoole@troilus.org>
+
+       * include/s_bsd.h (VirtualHost): Replace with separate variables
+       for IPv4 and IPv6 virtual hosts.
+
+       * include/uping.h (uping_echo): Remove declaration.
+
+       * ircd/ircd_auth.c (iauth_reconnect): Select VirtualHost_v4 or
+       VirtualHost_v6 based on iauth server address family.
+
+       * ircd/ircd_lexer.l: Do not recognize RESOLVER token.
+
+       * ircd/ircd_parser.y (ResolverAddr): Remove declaration.
+       (RESOLVER): Remove definition.
+       (generalresolver): Remove.
+       (generalvhost): Assign address to either VirtualHost_v4 or
+       VirtualHost_v6, depending on format.
+
+       * ircd/ircd_res.c (res_socket): Replace with separate variables
+       for IPv4 and IPv6 resolver sockets.
+       (ResolverAddr): Remove definition.
+       (restart_resolver): Attempt to set up both IPv4 and IPv6 sockets.
+       (send_res_msg): Select outbound FD based on resolver address type.
+       (res_readreply): Recognize either inbound socket FD.
+
+       * ircd/os_generic.c (sockaddr_from_irc): Require irc != NULL.
+       (os_socket): Require local != NULL.
+
+       * ircd/s_bsd.c (VirtualHost): Replace with separate variables
+       for IPv4 and IPv6 virtual hosts.
+       (connect_inet): Select virtual host based on destination address.
+
+       * ircd/uping.c (UPingFIleDescriptor): Remove.
+       (upingSock): Split into separate IPv4 and IPv6 variables.
+       (uping_echo_callback): Incorporate uping_echo() body here, so the
+       proper socket FD can be used.
+       (uping_init): Attempt to set up both v4 and v6 UPING sockets.
+       (uping_server): Create uping socket with appropriate local address.
+
+       * doc/example.conf (General): Update example config file to
+       reflect removal of Resolver setting and support for separate IPv4
+       and IPv6 VHost settings.
+
 2004-12-28  Michael Poole <mdpoole@troilus.org>
 
        * ircd/sys.h (BITS_ZERO_ON_*, HAVE_RELIABLE_SIGNALS, DOCURSES,
index c652d17343d537bedee64b87e61bcb82b74a5dff..bbd61b9f102d0cb1f52fa23034a4aed7bee0bbf9 100644 (file)
@@ -52,8 +52,8 @@
 # First some information about the server.
 # General {
 #         name = "servername";
-#         vhost = "virtualhost";
-#         resolver = "ipaddress";
+#         vhost = "ipv4vhost";
+#         vhost = "ipv6vhost";
 #         description = "description";
 #         numeric = numericnumber;
 # };
@@ -65,8 +65,9 @@
 # override it.  See Port{} for listener virtual hosting.  If in doubt,
 # leave it out.
 #
-# You may need to specify the resolver address if your compile
-# defaults to using IPv6 but your resolvers are all IPv4 hosts.
+# You may specify both an IPv4 virtual host and an IPv6 virtual host,
+# to indicate which address should be used for outbound connections
+# of the respective type.
 #
 # Note that <server numeric> has to be unique on the network your server
 # is running on, must be between 0 and 4095, and is not updated on a rehash.
index c4c1387e742d35b7ea10719e897d70d81903aef4..1518ff45b11c754d5594f4763b55893e09d42fef 100644 (file)
@@ -51,7 +51,8 @@ extern const char* const REGISTER_ERROR_MSG;
 
 extern int            HighestFd;
 extern struct Client* LocalClientArray[MAXCONNECTIONS];
-extern struct irc_sockaddr VirtualHost;
+extern struct irc_sockaddr VirtualHost_v4;
+extern struct irc_sockaddr VirtualHost_v6;
 
 /*
  * Proto types
index 2f92d9557c9caf45a3f1adc52779599c5ea9f95d..d0fa0bec9cf9b65060d031bd68ab8c8f8735f4bd 100644 (file)
@@ -75,7 +75,6 @@ extern void uping_send(struct UPing* pptr);
 extern void uping_read(struct UPing* pptr);
 extern void uping_end(struct UPing* pptr);
 extern void uping_cancel(struct Client *sptr, struct Client *acptr);
-extern void uping_echo(void);
 extern struct UPing* uping_begin(void);
 extern int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count);
 
index afbb9377ca7ca372a07c9b9a88ed6a548aa9475a..4a59e677f1c86fe19028115762399ee63e1da38a 100644 (file)
@@ -445,6 +445,7 @@ static void iauth_schedule_reconnect(struct IAuth *iauth)
  */
 static void iauth_reconnect(struct IAuth *iauth)
 {
+  struct irc_sockaddr *local;
   IOResult result;
   int fd;
 
@@ -456,7 +457,8 @@ static void iauth_reconnect(struct IAuth *iauth)
     gethost_byname(i_host(iauth), &i_query(iauth));
     return;
   }
-  fd = os_socket(&VirtualHost, SOCK_STREAM, "IAuth");
+  local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6;
+  fd = os_socket(local, SOCK_STREAM, "IAuth");
   if (fd < 0)
     return;
   if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
index 7b847d69673e33f34c5cce21d90cd13336020b93..8aa1925b9e74b753140be9a51d3e61d860b10bce 100644 (file)
@@ -76,7 +76,6 @@ static struct lexer_token {
   TOKEN(OPER),
   TOKEN(LOCAL),
   TOKEN(VHOST),
-  TOKEN(RESOLVER),
   TOKEN(MASK),
   TOKEN(HIDDEN),
   TOKEN(MOTD),
index 8b383548c469deb253c3d6c3c6421af4ad579a9e..3514f69f0227862fdaf9d2b5bbb27da8a44b8625 100644 (file)
@@ -65,7 +65,6 @@
   extern struct ServerConf* serverConfList;
   extern struct s_map*      GlobalServiceMapList;
   extern struct qline*      GlobalQuarantineList;
-  extern struct irc_sockaddr ResolverAddr;
 
   int yylex(void);
   /* Now all the globals we need :/... */
@@ -136,7 +135,6 @@ static void parse_error(char *pattern,...) {
 %token NO
 %token OPER
 %token VHOST
-%token RESOLVER
 %token HIDDEN
 %token MOTD
 %token JUPE
@@ -273,7 +271,7 @@ generalblock: GENERAL '{' generalitems '}'
     parse_error("Your General block must contain a numeric (between 1 and 4095).");
 } ';' ;
 generalitems: generalitem generalitems | generalitem;
-generalitem: generalnumeric | generalname | generalvhost | generalresolver | generaldesc | error;
+generalitem: generalnumeric | generalname | generalvhost | generaldesc | error;
 generalnumeric: NUMERIC '=' NUMBER ';'
 {
   if (localConf.numeric == 0)
@@ -301,12 +299,13 @@ generaldesc: DESCRIPTION '=' QSTRING ';'
 
 generalvhost: VHOST '=' QSTRING ';'
 {
-  ircd_aton(&VirtualHost.addr, $3);
-};
-
-generalresolver: RESOLVER '=' QSTRING ';'
-{
-  ircd_aton(&ResolverAddr.addr, $3);
+  struct irc_in_addr addr;
+  if (!ircd_aton(&addr, $3))
+      parse_error("Invalid virtual host '%s'.", $3);
+  else if (irc_in_addr_is_ipv4(&addr))
+    memcpy(&VirtualHost_v4.addr, &addr, sizeof(addr));
+  else
+    memcpy(&VirtualHost_v6.addr, &addr, sizeof(addr));
 };
 
 adminblock: ADMIN '{' adminitems '}'
index 962e211b70e7f530a49d99bbee962e02ebde3df7..c9960f6b17780c3a53123e933e38f48009bc42f6 100644 (file)
 #error this code needs to be able to address individual octets 
 #endif
 
-/** Resolver UDP socket. */
-static struct Socket res_socket;
+/** IPv4 resolver UDP socket. */
+static struct Socket res_socket_v4;
+/** IPv6 resolver UDP socket. */
+static struct Socket res_socket_v6;
 /** Next DNS lookup timeout. */
 static struct Timer res_timeout;
 /** Check for whether the resolver has been initialized yet. */
@@ -140,8 +142,6 @@ extern struct irc_sockaddr irc_nsaddr_list[IRCD_MAXNS];
 extern int irc_nscount;
 extern char irc_domain[HOSTLEN];
 
-struct irc_sockaddr ResolverAddr;
-
 /** Check whether \a inp is a nameserver we use.
  * @param[in] inp Nameserver address.
  * @return Non-zero if we trust \a inp; zero if not.
@@ -171,17 +171,24 @@ restart_resolver(void)
   if (!request_list.next)
     request_list.next = request_list.prev = &request_list;
 
-  if (!s_active(&res_socket))
+  if (!s_active(&res_socket_v4))
   {
-    struct irc_sockaddr *local;
-    int fd;
-    local = irc_in_addr_valid(&ResolverAddr.addr) ? &ResolverAddr : &VirtualHost;
-    fd = os_socket(local, SOCK_DGRAM, "Resolver UDP socket");
-    if (fd < 0) return;
-    if (!socket_add(&res_socket, res_readreply, NULL, SS_DATAGRAM,
-                    SOCK_EVENT_READABLE, fd)) return;
-    timer_init(&res_timeout);
+    int fd = os_socket(&VirtualHost_v4, SOCK_DGRAM, "Resolver UDPv4 socket");
+    if (fd >= 0)
+      socket_add(&res_socket_v4, res_readreply, NULL,
+                 SS_DATAGRAM, SOCK_EVENT_READABLE, fd);
+  }
+
+  if (!s_active(&res_socket_v6))
+  {
+    int fd = os_socket(&VirtualHost_v6, SOCK_DGRAM, "Resolver UDPv6 socket");
+    if (fd >= 0)
+      socket_add(&res_socket_v6, res_readreply, NULL,
+                 SS_DATAGRAM, SOCK_EVENT_READABLE, fd);
   }
+
+  if (s_active(&res_socket_v4) || s_active(&res_socket_v6))
+    timer_init(&res_timeout);
 }
 
 /** Append local domain to hostname if needed.
@@ -365,9 +372,11 @@ send_res_msg(const char *msg, int len, int rcount)
   if (max_queries == 0)
     max_queries = 1;
 
-  for (i = 0; i < max_queries; i++)
-    if (os_sendto_nonb(s_fd(&res_socket), msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS)
+  for (i = 0; i < max_queries; i++) {
+    int fd = irc_in_addr_is_ipv4(&irc_nsaddr_list[i].addr) ? s_fd(&res_socket_v4) : s_fd(&res_socket_v6);
+    if (os_sendto_nonb(fd, msg, len, NULL, 0, &irc_nsaddr_list[i]) == IO_SUCCESS)
       ++sent;
+  }
 
   return(sent);
 }
@@ -733,7 +742,7 @@ res_readreply(struct Event *ev)
   unsigned int rc;
   int answer_count;
 
-  assert(ev_socket(ev) == &res_socket);
+  assert((ev_socket(ev) == &res_socket_v4) || (ev_socket(ev) == &res_socket_v6));
   sock = ev_socket(ev);
 
   if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, sizeof(buf), &rc, &lsin)
index c824f531d1abd5c2e3ee2c7ecc5bed17c1a39a6b..f72bbe900bede427e86a6a9c2b2c47bc4d9c5b28 100644 (file)
@@ -107,28 +107,18 @@ int sockaddr_from_irc(struct sockaddr_in6 *v6, const struct irc_sockaddr *irc, i
     socklen_t slen;
     int family;
 
+    assert(irc != 0);
     slen = sizeof(sin6);
-    if (0 <= compat_fd) {
-        if (0 == getsockname(compat_fd, (struct sockaddr*)&sin6, &slen))
-            family = sin6.sin6_family;
-        else if (irc_in_addr_is_ipv4(&VirtualHost.addr))
-            family = AF_INET;
-        else
-            family = AF_INET6;
-    } else {
-        if (irc_in_addr_is_ipv4(&irc->addr))
-            family = AF_INET;
-        else
-            family = AF_INET6;
-    }
+    if ((0 <= compat_fd)
+        && (0 == getsockname(compat_fd, (struct sockaddr*)&sin6, &slen)))
+        family = sin6.sin6_family;
+    else if ((irc == &VirtualHost_v4) || irc_in_addr_is_ipv4(&irc->addr))
+        family = AF_INET;
+    else
+        family = AF_INET6;
 
     memset(v6, 0, sizeof(*v6));
-    if (!irc) {
-        memset(v6, 0, sizeof(v6));
-        v6->sin6_family = AF_INET6;
-        return sizeof(*v6);
-    }
-    else if ((family == AF_INET) && irc_in_addr_is_ipv4(&irc->addr)) {
+    if (family == AF_INET) {
         struct sockaddr_in *v4 = (struct sockaddr_in*)v6;
         v4->sin_family = AF_INET;
         memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
@@ -157,6 +147,7 @@ void sockaddr_to_irc(const struct sockaddr_in *v4, struct irc_sockaddr *irc)
 
 int sockaddr_from_irc(struct sockaddr_in *v4, const struct irc_sockaddr *irc, int compat_fd)
 {
+    assert(irc != 0);
     v4->sin_family = AF_INET;
     if (irc) {
         assert(!irc->addr.in6_16[0] && !irc->addr.in6_16[1] && !irc->addr.in6_16[2] && !irc->addr.in6_16[3] && !irc->addr.in6_16[4] && (!irc->addr.in6_16[5] || irc->addr.in6_16[5] == 0xffff));
@@ -517,6 +508,7 @@ IOResult os_sendto_nonb(int fd, const char* buf, unsigned int length,
   errno = 0;
 
   size = sockaddr_from_irc(&addr, peer, fd);
+  assert((addr.sn_family == AF_INET) == irc_in_addr_is_ipv4(&peer->addr));
   if (-1 < (res = sendto(fd, buf, length, flags, (struct sockaddr*)&addr, size))) {
     if (count_out)
       *count_out = (unsigned) res;
@@ -618,6 +610,7 @@ int os_socket(const struct irc_sockaddr* local, int type, const char* port_name)
   struct sockaddr_native addr;
   int size, fd;
 
+  assert(local != 0);
   size = sockaddr_from_irc(&addr, local, -1);
   fd = socket(addr.sn_family, type, 0);
   if (fd < 0) {
index 93e8a673edbc6ecf586e6e2c1a0a495f6b7c580c..ead38dfb887866e08b27ab5f5e1391b224f8e530 100644 (file)
 struct Client*            LocalClientArray[MAXCONNECTIONS];
 /** Maximum file descriptor in current use. */
 int                       HighestFd = -1;
-/** Default local address for outbound connections. */
-struct irc_sockaddr       VirtualHost;
+/** Default local address for outbound IPv4 connections. */
+struct irc_sockaddr       VirtualHost_v4;
+/** Default local address for outbound IPv6 connections. */
+struct irc_sockaddr       VirtualHost_v6;
 /** Temporary buffer for reading data from a peer. */
 static char               readbuf[SERVER_TCP_WINDOW];
 
@@ -234,8 +236,10 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr)
    */
   if (irc_in_addr_valid(&aconf->origin.addr))
     local = &aconf->origin;
+  else if (irc_in_addr_is_ipv4(&aconf->address.addr))
+    local = &VirtualHost_v4;
   else
-    local = &VirtualHost;
+    local = &VirtualHost_v6;
   cli_fd(cptr) = os_socket(local, SOCK_STREAM, cli_name(cptr));
   if (cli_fd(cptr) < 0)
     return 0;
index 6982577515bdd7e752b5320ba7eb7b6050df1f7a..f161ff034a8494a1f1c670817c392e92c90b3a63 100644 (file)
@@ -55,8 +55,8 @@
 #define UPINGTIMEOUT 60   /**< Timeout waiting for ping responses */
 
 static struct UPing* pingList = 0; /**< Linked list of UPing structs */
-static int UPingFileDescriptor       = -1; /**< UDP listener socket for upings */
-static struct Socket upingSock; /**< Socket struct for upings */
+static struct Socket upingSock_v4; /**< Socket struct for IPv4 upings */
+static struct Socket upingSock_v6; /**< Socket struct for IPv6 upings */
 
 /** Start iteration of uping list.
  * @return Start of uping list.
@@ -88,71 +88,77 @@ static void uping_erase(struct UPing* p)
 }
 
 /** Callback for uping listener socket.
+ * Reads a uping from the socket and respond, but not more than 10
+ * times per second.
  * @param[in] ev I/O event for uping socket.
  */
 static void uping_echo_callback(struct Event* ev)
 {
+  struct Socket      *sock;
+  struct irc_sockaddr from;
+  unsigned int       len = 0;
+  static time_t      last = 0;
+  static int         counter = 0;
+  char               buf[BUFSIZE + 1];
+
   assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+  sock = ev_socket(ev);
+  assert(sock == &upingSock_v4 || sock == &upingSock_v6);
 
-  uping_echo();
+  Debug((DEBUG_DEBUG, "UPING: uping_echo"));
+
+  if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, BUFSIZE, &len, &from))
+    return;
+  /*
+   * count em even if we're getting flooded so we can tell we're getting
+   * flooded.
+   */
+  ++ServerStats->uping_recv;
+  if (len < 19)
+    return;
+  else if (CurrentTime != last) {
+    counter = 0;
+    last = CurrentTime;
+  } else if (++counter > 10)
+    return;
+  os_sendto_nonb(s_fd(sock), buf, len, NULL, 0, &from);
 }
 
 /** Initialize a UDP socket for upings.
- * @returns File descriptor of UDP socket (-1 on error).
+ * @returns 0 on success, -1 on error.
  */
 int uping_init(void)
 {
   struct irc_sockaddr from;
   int fd;
 
-  memcpy(&from, &VirtualHost, sizeof(from));
+  memcpy(&from, &VirtualHost_v4, sizeof(from));
   from.port = atoi(UDP_PORT);
 
-  fd = os_socket(&from, SOCK_DGRAM, "UDP listener socket");
+  fd = os_socket(&from, SOCK_DGRAM, "IPv4 uping listener");
   if (fd < 0)
     return -1;
-  if (!socket_add(&upingSock, uping_echo_callback, 0, SS_DATAGRAM,
-                 SOCK_EVENT_READABLE, fd)) {
+  if (!socket_add(&upingSock_v4, uping_echo_callback, 0, SS_DATAGRAM,
+                  SOCK_EVENT_READABLE, fd)) {
     Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system"));
     close(fd);
     return -1;
   }
-  UPingFileDescriptor = fd;
-  return fd;
-}
-
 
-/** Read a uping from the socket and respond (but not more than 10
- * times per second).
- */
-void uping_echo()
-{
-  struct irc_sockaddr from;
-  unsigned int       len = 0;
-  static time_t      last = 0;
-  static int         counter = 0;
-  char               buf[BUFSIZE + 1];
-
-  Debug((DEBUG_DEBUG, "UPING: uping_echo"));
+  memcpy(&from, &VirtualHost_v6, sizeof(from));
+  from.port = atoi(UDP_PORT);
 
-  if (IO_SUCCESS != os_recvfrom_nonb(UPingFileDescriptor, buf, BUFSIZE, &len, &from))
-    return;
-  /*
-   * count em even if we're getting flooded so we can tell we're getting
-   * flooded.
-   */
-  ++ServerStats->uping_recv;
-  if (CurrentTime == last) {
-    if (++counter > 10)
-      return;
-  }
-  else {
-    counter = 0;
-    last    = CurrentTime;
+  fd = os_socket(&from, SOCK_DGRAM, "IPv6 uping listener");
+  if (fd < 0)
+    return -1;
+  if (!socket_add(&upingSock_v6, uping_echo_callback, 0, SS_DATAGRAM,
+                  SOCK_EVENT_READABLE, fd)) {
+    Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system"));
+    close(fd);
+    return -1;
   }
-  if (len < 19)
-    return;
-  os_sendto_nonb(UPingFileDescriptor, buf, len, NULL, 0, &from);
+
+  return 0;
 }
 
 
@@ -363,6 +369,7 @@ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int coun
 {
   int fd;
   struct UPing* pptr;
+  struct irc_sockaddr *local;
 
   assert(0 != sptr);
   assert(0 != aconf);
@@ -376,7 +383,8 @@ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int coun
   if (IsUPing(sptr))
     uping_cancel(sptr, sptr);  /* Cancel previous ping request */
 
-  fd = os_socket(NULL, SOCK_DGRAM, "UDP ping socket");
+  local = irc_in_addr_is_ipv4(&aconf->address.addr) ? &VirtualHost_v4 : &VirtualHost_v6;
+  fd = os_socket(local, SOCK_DGRAM, "Outbound uping socket");
   if (fd < 0)
     return 0;