aClient *local[MAXCONNECTIONS];
int highest_fd = 0, readcalls = 0, udpfd = -1, resfd = -1;
-static struct sockaddr_in mysk;
static void polludp ();
static struct sockaddr *connect_inet (aConfItem *, aClient *, int *);
*/
int inetport (aClient *cptr, char *name, int port)
{
- static struct sockaddr_in server;
- int ad[4], len = sizeof (server);
- char ipname[20];
+ struct sockaddr_storage ss;
+ socklen_t len;
+ int family;
+ int opt;
if (BadPtr (name))
name = "*";
- ad[0] = ad[1] = ad[2] = ad[3] = 0;
-
- /*
- * do it this way because building ip# from separate values for each
- * byte requires endian knowledge or some nasty messing. Also means
- * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
- */
- (void) sscanf (name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
- (void) sprintf (ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
if (cptr != &me) {
(void) sprintf (cptr->sockhost, "%-.42s.%u", name,
(unsigned int) port);
(void) strcpy (cptr->name, me.name);
}
+
+ /*
+ * Determine address family. If the bind address contains ':',
+ * it is IPv6. "*" or plain IPv4 uses AF_INET6 with dual-stack
+ * (IPV6_V6ONLY=0) so we can accept both v4 and v6 on one socket.
+ */
+ memset (&ss, 0, sizeof (ss));
+ if (strchr (name, ':')) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ss;
+ family = AF_INET6;
+ s6->sin6_family = AF_INET6;
+ s6->sin6_port = htons (port);
+ inet_pton (AF_INET6, name, &s6->sin6_addr);
+ len = sizeof (struct sockaddr_in6);
+ }
+ else if (!strcmp (name, "*") || !strcmp (name, "0.0.0.0")) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ss;
+ family = AF_INET6;
+ s6->sin6_family = AF_INET6;
+ s6->sin6_port = htons (port);
+ s6->sin6_addr = in6addr_any;
+ len = sizeof (struct sockaddr_in6);
+ }
+ else {
+ struct sockaddr_in *s4 = (struct sockaddr_in *) &ss;
+ family = AF_INET;
+ s4->sin_family = AF_INET;
+ s4->sin_port = htons (port);
+ inet_pton (AF_INET, name, &s4->sin_addr);
+ len = sizeof (struct sockaddr_in);
+ }
+
/*
* At first, open a new socket
*/
if (cptr->fd == -1)
- cptr->fd = socket (AF_INET, SOCK_STREAM, 0);
+ cptr->fd = socket (family, SOCK_STREAM, 0);
if (cptr->fd < 0) {
report_error ("opening stream socket %s:%s", cptr);
return -1;
}
set_sock_opts (cptr->fd, cptr);
+
+ /* For dual-stack AF_INET6 listeners, disable IPV6_V6ONLY */
+ if (family == AF_INET6) {
+ opt = 0;
+ (void) setsockopt (cptr->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &opt, sizeof (opt));
+ }
+
/*
* Bind a port to listen for new connections if port is non-null,
* else assume it is already open and try get something from it.
*/
if (port) {
- server.sin_family = AF_INET;
- /* per-port bindings, fixes /stats l */
- inet_pton (AF_INET, ipname, &server.sin_addr);
- server.sin_port = htons (port);
- /*
- */
- if (bind (cptr->fd, (struct sockaddr *) &server, sizeof (server)) ==
- -1) {
+ if (bind (cptr->fd, (struct sockaddr *) &ss, len) == -1) {
report_error ("binding stream socket %s:%s", cptr);
(void) close (cptr->fd);
return -1;
}
}
- if (getsockname (cptr->fd, (struct sockaddr *) &server, &len)) {
+ if (getsockname (cptr->fd, (struct sockaddr *) &ss, &len)) {
report_error ("getsockname failed for %s:%s", cptr);
(void) close (cptr->fd);
return -1;
}
if (cptr == &me) { /* Report port to stdout during startup */
char buf[1024];
+ int bound_port;
+
+ if (family == AF_INET6)
+ bound_port = ntohs (((struct sockaddr_in6 *) &ss)->sin6_port);
+ else
+ bound_port = ntohs (((struct sockaddr_in *) &ss)->sin_port);
(void) sprintf (buf, rpl_str (RPL_MYPORTIS), me.name, "*",
- ntohs (server.sin_port));
+ bound_port);
(void) write (0, buf, strlen (buf));
}
if (cptr->fd > highest_fd)
highest_fd = cptr->fd;
- if (name)
- irc_addr_from_str (&cptr->ip, ipname);
- else
- IRC_ADDR_COPY (&cptr->ip, &me.ip);
- cptr->port = (int) ntohs (server.sin_port);
+ irc_addr_from_str (&cptr->ip, name);
+ cptr->port = port;
(void) listen (cptr->fd, cfg_listen_size);
local[cptr->fd] = cptr;
*/
static int check_init (aClient *cptr, char *sockn)
{
- struct sockaddr_in sk;
- int len = sizeof (struct sockaddr_in);
-
+ struct sockaddr_storage sk;
+ socklen_t len = sizeof (sk);
/* If descriptor is a tty, special checking... */
if (isatty (cptr->fd)) {
strncpyzt (sockn, me.sockhost, HOSTLEN);
- memset ((char *) &sk, 0, sizeof (struct sockaddr_in));
+ memset (&sk, 0, sizeof (sk));
}
else if (getpeername (cptr->fd, (struct sockaddr *) &sk, &len) == -1) {
report_error ("connect failure: %s %s", cptr);
cptr->hostp = NULL;
strncpyzt (sockn, me.sockhost, HOSTLEN);
}
- cptr->port = (int) ntohs (sk.sin_port);
+ if (sk.ss_family == AF_INET6)
+ cptr->port = (int) ntohs (((struct sockaddr_in6 *) &sk)->sin6_port);
+ else
+ cptr->port = (int) ntohs (((struct sockaddr_in *) &sk)->sin_port);
return 0;
}
if (isatty (fd)) /* If descriptor is a tty, special checking... */
get_sockhost (acptr, cptr->sockhost);
else {
- struct sockaddr_in addr;
- int len = sizeof (struct sockaddr_in);
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof (addr);
if (getpeername (fd, (struct sockaddr *) &addr, &len) == -1) {
report_error ("Failed in connecting to %s :%s", cptr);
send (fd, zlinebuf, strlen (zlinebuf), 0);
goto add_con_refuse;
}
- acptr->port = ntohs (addr.sin_port);
+ if (addr.ss_family == AF_INET6)
+ acptr->port = ntohs (((struct sockaddr_in6 *) &addr)->sin6_port);
+ else
+ acptr->port = ntohs (((struct sockaddr_in *) &addr)->sin_port);
/* Let's do a check here to see if somebody matches a CN pair.
* if they don't, we can send them the connection noticed, because
lin.flags = ASYNC_CLIENT;
lin.value.cptr = acptr;
Debug ((DEBUG_DNS, "lookup %s", inetntoa (&acptr->ip)));
- acptr->hostp = gethost_byaddr ((char *) &acptr->ip, &lin);
+ acptr->hostp = gethost_byaddr ((char *) &acptr->ip.addr.v4, &lin);
if (!acptr->hostp)
SetDNS (acptr);
else if (acptr->cc)
static struct sockaddr * connect_inet (aConfItem *aconf, aClient *cptr, int *lenp)
{
- static struct sockaddr_in server;
+ static struct sockaddr_storage ss;
struct hostent *hp;
+ int family;
+ int sport;
+
+ /*
+ * Resolve the target address if needed.
+ */
+ if (irc_addr_is_zero (&aconf->ipnum)) {
+ char *s = strchr (aconf->host, '@');
+ if (s) s++;
+ else s = aconf->host;
+ if (!irc_addr_from_str (&aconf->ipnum, s)) {
+ IRC_ADDR_ZERO (&aconf->ipnum);
+ hp = cptr->hostp;
+ if (!hp) {
+ Debug ((DEBUG_FATAL, "%s: unknown host", aconf->host));
+ return NULL;
+ }
+ aconf->ipnum = irc_addr_from_v4 ((struct in_addr *) hp->h_addr);
+ }
+ }
+
+ family = aconf->ipnum.family ? aconf->ipnum.family : AF_INET;
/*
* Might as well get sockhost from here, the connection is attempted
* with it so if it fails its useless.
*/
- cptr->fd = socket (AF_INET, SOCK_STREAM, 0);
+ cptr->fd = socket (family, SOCK_STREAM, 0);
if (cptr->fd >= MAXCLIENTS) {
sendto_ops ("No more connections allowed (%s)", cptr->name);
return NULL;
}
- mysk.sin_port = 0;
- memset ((char *) &server, 0, sizeof (server));
- server.sin_family = AF_INET;
- get_sockhost (cptr, aconf->host);
-
if (cptr->fd == -1) {
report_error ("opening stream socket to server %s:%s", cptr);
return NULL;
}
get_sockhost (cptr, aconf->host);
- server.sin_port = 0;
- server.sin_addr = me.ip.addr.v4;
- server.sin_family = AF_INET;
+
/*
- ** Bind to a local IP# (with unknown port - let unix decide) so
- ** we have some chance of knowing the IP# that gets used for a host
- ** with more than one IP#.
- */
- /* No we don't bind it, not all OS's can handle connecting with
- ** an already bound socket, different ip# might occur anyway
- ** leading to a freezing select() on this side for some time.
- ** I had this on my Linux 1.1.88 --Run
+ * Bind to local IP if configured (virtual host support).
*/
- /* We do now. Virtual interface stuff --ns */
- if (!irc_addr_is_zero (&me.ip))
- if (bind (cptr->fd, (struct sockaddr *) &server, sizeof (server)) ==
- -1) {
+ if (!irc_addr_is_zero (&me.ip)) {
+ struct sockaddr_storage bind_ss;
+ socklen_t bind_len;
+ memset (&bind_ss, 0, sizeof (bind_ss));
+ if (family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &bind_ss;
+ s6->sin6_family = AF_INET6;
+ if (me.ip.family == AF_INET6)
+ memcpy (&s6->sin6_addr, &me.ip.addr.v6, sizeof (struct in6_addr));
+ /* else leave as in6addr_any — can't bind v4 addr to v6 socket */
+ bind_len = sizeof (struct sockaddr_in6);
+ }
+ else {
+ struct sockaddr_in *s4 = (struct sockaddr_in *) &bind_ss;
+ s4->sin_family = AF_INET;
+ s4->sin_addr = me.ip.addr.v4;
+ bind_len = sizeof (struct sockaddr_in);
+ }
+ if (bind (cptr->fd, (struct sockaddr *) &bind_ss, bind_len) == -1) {
report_error ("error binding to local port for %s:%s", cptr);
return NULL;
}
- memset ((char *) &server, 0, sizeof (server));
- server.sin_family = AF_INET;
+ }
+
/*
- * By this point we should know the IP# of the host listed in the
- * conf line, whether as a result of the hostname lookup or the ip#
- * being present instead. If we dont know it, then the connect fails.
+ * Build the destination sockaddr.
*/
- if (isdigit (*aconf->host) && irc_addr_is_zero (&aconf->ipnum))
- irc_addr_from_str (&aconf->ipnum, aconf->host);
- if (irc_addr_is_zero (&aconf->ipnum)) {
- hp = cptr->hostp;
- if (!hp) {
- Debug ((DEBUG_FATAL, "%s: unknown host", aconf->host));
- return NULL;
- }
- aconf->ipnum = irc_addr_from_v4 ((struct in_addr *) hp->h_addr);
+ memset (&ss, 0, sizeof (ss));
+ sport = (aconf->port > 0) ? aconf->port : portnum;
+ if (family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ss;
+ s6->sin6_family = AF_INET6;
+ memcpy (&s6->sin6_addr, &aconf->ipnum.addr.v6, sizeof (struct in6_addr));
+ s6->sin6_port = htons (sport);
+ *lenp = sizeof (struct sockaddr_in6);
+ }
+ else {
+ struct sockaddr_in *s4 = (struct sockaddr_in *) &ss;
+ s4->sin_family = AF_INET;
+ memcpy (&s4->sin_addr, &aconf->ipnum.addr.v4, sizeof (struct in_addr));
+ s4->sin_port = htons (sport);
+ *lenp = sizeof (struct sockaddr_in);
}
- memcpy ((char *) &server.sin_addr, (char *) &aconf->ipnum.addr.v4, sizeof (struct in_addr));
IRC_ADDR_COPY (&cptr->ip, &aconf->ipnum);
- server.sin_port = htons (((aconf->port > 0) ? aconf->port : portnum));
- *lenp = sizeof (server);
- return (struct sockaddr *) &server;
+ return (struct sockaddr *) &ss;
}