From dbb47eb3df54789d338f440dd7af4004eb0154b7 Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Wed, 29 Dec 2004 03:08:09 +0000 Subject: [PATCH] Improve support for IPv4 vs IPv6 virtual hosts (fixes SF bugs #1087699, #1087668). git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1290 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- ChangeLog | 43 ++++++++++++++++++++++ doc/example.conf | 9 +++-- include/s_bsd.h | 3 +- include/uping.h | 1 - ircd/ircd_auth.c | 4 +- ircd/ircd_lexer.l | 1 - ircd/ircd_parser.y | 17 ++++----- ircd/ircd_res.c | 41 +++++++++++++-------- ircd/os_generic.c | 31 ++++++---------- ircd/s_bsd.c | 10 +++-- ircd/uping.c | 92 +++++++++++++++++++++++++--------------------- 11 files changed, 155 insertions(+), 97 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1767505..405c659 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2004-12-28 Michael Poole + + * 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 * ircd/sys.h (BITS_ZERO_ON_*, HAVE_RELIABLE_SIGNALS, DOCURSES, diff --git a/doc/example.conf b/doc/example.conf index c652d17..bbd61b9 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -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 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. diff --git a/include/s_bsd.h b/include/s_bsd.h index c4c1387..1518ff4 100644 --- a/include/s_bsd.h +++ b/include/s_bsd.h @@ -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 diff --git a/include/uping.h b/include/uping.h index 2f92d95..d0fa0be 100644 --- a/include/uping.h +++ b/include/uping.h @@ -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); diff --git a/ircd/ircd_auth.c b/ircd/ircd_auth.c index afbb937..4a59e67 100644 --- a/ircd/ircd_auth.c +++ b/ircd/ircd_auth.c @@ -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)) { diff --git a/ircd/ircd_lexer.l b/ircd/ircd_lexer.l index 7b847d6..8aa1925 100644 --- a/ircd/ircd_lexer.l +++ b/ircd/ircd_lexer.l @@ -76,7 +76,6 @@ static struct lexer_token { TOKEN(OPER), TOKEN(LOCAL), TOKEN(VHOST), - TOKEN(RESOLVER), TOKEN(MASK), TOKEN(HIDDEN), TOKEN(MOTD), diff --git a/ircd/ircd_parser.y b/ircd/ircd_parser.y index 8b38354..3514f69 100644 --- a/ircd/ircd_parser.y +++ b/ircd/ircd_parser.y @@ -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 '}' diff --git a/ircd/ircd_res.c b/ircd/ircd_res.c index 962e211..c9960f6 100644 --- a/ircd/ircd_res.c +++ b/ircd/ircd_res.c @@ -50,8 +50,10 @@ #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) diff --git a/ircd/os_generic.c b/ircd/os_generic.c index c824f53..f72bbe9 100644 --- a/ircd/os_generic.c +++ b/ircd/os_generic.c @@ -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) { diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 93e8a67..ead38df 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -79,8 +79,10 @@ 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; diff --git a/ircd/uping.c b/ircd/uping.c index 6982577..f161ff0 100644 --- a/ircd/uping.c +++ b/ircd/uping.c @@ -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; -- 2.20.1