diff --git a/doc/ircd.conf.example b/doc/ircd.conf.example index 2305c302..5d7f984e 100644 --- a/doc/ircd.conf.example +++ b/doc/ircd.conf.example @@ -310,10 +310,6 @@ connect "irc.uplink.com" { flags = compressed, topicburst; #fingerprint = "c77106576abf7f9f90cca0f63874a60f2e40a64b"; - - /* If the connection is IPv6, uncomment below. - * Use 0::1, not ::1, for IPv6 localhost. */ - #aftype = ipv6; }; connect "ssl.uplink.com" { @@ -454,7 +450,7 @@ opm { * to be effective. * If omitted, it defaults to serverinfo::vhost6. */ - #listen_ipv6 = "0::1"; + #listen_ipv6 = "::1"; /* IPv6 port to listen on. * This should not be the same as any existing listeners. diff --git a/doc/reference.conf b/doc/reference.conf index aff7787c..731eadf8 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -571,17 +571,14 @@ connect "irc.uplink.com" { }; connect "ipv6.lame.server" { - /* Hosts that are IPv6 addresses must be in :: shortened form - * if applicable. Addresses starting with a colon get an extra - * zero prepended, for example: 0::1 - */ + host = "192.0.2.1"; host = "2001:db8:3::8"; send_password = "password"; accept_password = "password"; port = 6666; - /* aftype: controls whether the connection uses "ipv4" or "ipv6". - * Default is ipv4. + /* aftype: controls whether the outgoing connection uses "ipv4" or "ipv6". + * Default is to try either at random. */ aftype = ipv6; class = "server"; @@ -930,7 +927,7 @@ opm { * to be effective. * If omitted, it defaults to serverinfo::vhost6. */ - #listen_ipv6 = "0::1"; + #listen_ipv6 = "::1"; /* IPv6 port to listen on. * This should not be the same as any existing listeners. diff --git a/doc/sgml/oper-guide/config.sgml b/doc/sgml/oper-guide/config.sgml index 0d585124..798d6fcf 100644 --- a/doc/sgml/oper-guide/config.sgml +++ b/doc/sgml/oper-guide/config.sgml @@ -105,7 +105,7 @@ serverinfo { vhost - An optional text field which defines an IP from which to connect outward to other IRC servers. + An optional text field which defines an IPv4 address from which to connect outward to other IRC servers. @@ -113,7 +113,7 @@ serverinfo { vhost6 - An optional text field which defines an IPv6 IP from which to connect outward to other IRC servers. + An optional text field which defines an IPv6 address from which to connect outward to other IRC servers. @@ -599,10 +599,6 @@ connect "name" { Furthermore, if a hostname is used, it must have an A or AAAA record (no CNAME) and it must be the primary hostname for inbound connections to work. - - IPv6 addresses must be in :: shortened form; addresses which - then start with a colon must be prepended with a zero, - for example 0::1. @@ -662,7 +658,7 @@ connect "name" { aftype - The protocol that should be used to connect with, either ipv4 or ipv6. This defaults to ipv4 unless host is a numeric IPv6 address. + The protocol that must be used to connect with, either ipv4 or ipv6. This defaults to neither, allowing connection using either address family. diff --git a/include/s_conf.h b/include/s_conf.h index dbbdce19..4c3bd5ee 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -282,14 +282,10 @@ struct server_info char *description; char *network_name; int hub; - struct sockaddr_in ip; + struct rb_sockaddr_storage bind4; int default_max_clients; #ifdef RB_IPV6 - struct sockaddr_in6 ip6; -#endif - int specific_ipv4_vhost; -#ifdef RB_IPV6 - int specific_ipv6_vhost; + struct rb_sockaddr_storage bind6; #endif char *ssl_private_key; char *ssl_ca_cert; diff --git a/include/s_newconf.h b/include/s_newconf.h index 22c24784..4218a828 100644 --- a/include/s_newconf.h +++ b/include/s_newconf.h @@ -178,7 +178,13 @@ extern const char *get_oper_privs(int flags); struct server_conf { char *name; - char *host; + char *connect_host; + struct rb_sockaddr_storage connect4; + uint16_t dns_query_connect4; +#ifdef RB_IPV6 + struct rb_sockaddr_storage connect6; + uint16_t dns_query_connect6; +#endif char *passwd; char *spasswd; char *certfp; @@ -188,17 +194,20 @@ struct server_conf time_t hold; int aftype; - struct rb_sockaddr_storage my_ipnum; + char *bind_host; + struct rb_sockaddr_storage bind4; + uint16_t dns_query_bind4; +#ifdef RB_IPV6 + struct rb_sockaddr_storage bind6; + uint16_t dns_query_bind6; +#endif char *class_name; struct Class *class; rb_dlink_node node; - - uint16_t dns_query; }; #define SERVER_ILLEGAL 0x0001 -#define SERVER_VHOSTED 0x0002 #define SERVER_ENCRYPTED 0x0004 #define SERVER_COMPRESSED 0x0008 #define SERVER_TB 0x0010 @@ -206,7 +215,6 @@ struct server_conf #define SERVER_SSL 0x0040 #define ServerConfIllegal(x) ((x)->flags & SERVER_ILLEGAL) -#define ServerConfVhosted(x) ((x)->flags & SERVER_VHOSTED) #define ServerConfEncrypted(x) ((x)->flags & SERVER_ENCRYPTED) #define ServerConfCompressed(x) ((x)->flags & SERVER_COMPRESSED) #define ServerConfTb(x) ((x)->flags & SERVER_TB) diff --git a/ircd/newconf.c b/ircd/newconf.c index 253d02b6..47a5924c 100644 --- a/ircd/newconf.c +++ b/ircd/newconf.c @@ -243,27 +243,31 @@ conf_set_serverinfo_network_name(void *data) static void conf_set_serverinfo_vhost(void *data) { - if(rb_inet_pton(AF_INET, (char *) data, &ServerInfo.ip.sin_addr) <= 0) + struct rb_sockaddr_storage addr; + + if(rb_inet_pton_sock(data, (struct sockaddr *)&addr) <= 0 || GET_SS_FAMILY(&addr) != AF_INET) { conf_report_error("Invalid IPv4 address for server vhost (%s)", (char *) data); return; } - ServerInfo.ip.sin_family = AF_INET; - ServerInfo.specific_ipv4_vhost = 1; + + ServerInfo.bind4 = addr; } static void conf_set_serverinfo_vhost6(void *data) { + #ifdef RB_IPV6 - if(rb_inet_pton(AF_INET6, (char *) data, &ServerInfo.ip6.sin6_addr) <= 0) + struct rb_sockaddr_storage addr; + + if(rb_inet_pton_sock(data, (struct sockaddr *)&addr) <= 0 || GET_SS_FAMILY(&addr) != AF_INET6) { conf_report_error("Invalid IPv6 address for server vhost (%s)", (char *) data); return; } - ServerInfo.specific_ipv6_vhost = 1; - ServerInfo.ip6.sin6_family = AF_INET6; + ServerInfo.bind6 = addr; #else conf_report_error("Warning -- ignoring serverinfo::vhost6 -- IPv6 support not available."); #endif @@ -1301,7 +1305,12 @@ conf_end_connect(struct TopConf *tc) return 0; } - if(EmptyString(yy_server->host)) + if(EmptyString(yy_server->connect_host) + && GET_SS_FAMILY(&yy_server->connect4) != AF_INET +#ifdef RB_IPV6 + && GET_SS_FAMILY(&yy_server->connect6) != AF_INET6 +#endif + ) { conf_report_error("Ignoring connect block for %s -- missing host.", yy_server->name); @@ -1326,23 +1335,57 @@ conf_end_connect(struct TopConf *tc) static void conf_set_connect_host(void *data) { - rb_free(yy_server->host); - yy_server->host = rb_strdup(data); - if (strchr(yy_server->host, ':')) - yy_server->aftype = AF_INET6; + struct rb_sockaddr_storage addr; + + if(rb_inet_pton_sock(data, (struct sockaddr *)&addr) <= 0) + { + rb_free(yy_server->connect_host); + yy_server->connect_host = rb_strdup(data); + } + else if(GET_SS_FAMILY(&addr) == AF_INET) + { + yy_server->connect4 = addr; + } +#ifdef RB_IPV6 + else if(GET_SS_FAMILY(&addr) == AF_INET6) + { + yy_server->connect6 = addr; + } +#endif + else + { + conf_report_error("Unsupported IP address for server connect host (%s)", + (char *)data); + return; + } } static void conf_set_connect_vhost(void *data) { - if(rb_inet_pton_sock(data, (struct sockaddr *)&yy_server->my_ipnum) <= 0) + struct rb_sockaddr_storage addr; + + if(rb_inet_pton_sock(data, (struct sockaddr *)&addr) <= 0) { - conf_report_error("Invalid IP address for server connect vhost (%s)", - (char *) data); + rb_free(yy_server->bind_host); + yy_server->bind_host = rb_strdup(data); + } + else if(GET_SS_FAMILY(&addr) == AF_INET) + { + yy_server->bind4 = addr; + } +#ifdef RB_IPV6 + else if(GET_SS_FAMILY(&addr) == AF_INET6) + { + yy_server->bind6 = addr; + } +#endif + else + { + conf_report_error("Unsupported IP address for server connect vhost (%s)", + (char *)data); return; } - - yy_server->flags |= SERVER_VHOSTED; } static void @@ -2071,7 +2114,7 @@ conf_end_opm(struct TopConf *tc) else { char ip[HOSTIPLEN]; - if(!rb_inet_ntop_sock((struct sockaddr *)&ServerInfo.ip, ip, sizeof(ip))) + if(!rb_inet_ntop_sock((struct sockaddr *)&ServerInfo.bind4, ip, sizeof(ip))) conf_report_error("No opm::listen_ipv4 nor serverinfo::vhost directive; cannot listen on IPv4"); else conf_create_opm_listener(ip, yy_opm_port_ipv4); @@ -2085,7 +2128,7 @@ conf_end_opm(struct TopConf *tc) else { char ip[HOSTIPLEN]; - if(!rb_inet_ntop_sock((struct sockaddr *)&ServerInfo.ip6, ip, sizeof(ip))) + if(!rb_inet_ntop_sock((struct sockaddr *)&ServerInfo.bind6, ip, sizeof(ip))) conf_report_error("No opm::listen_ipv6 nor serverinfo::vhost directive; cannot listen on IPv6"); else conf_create_opm_listener(ip, yy_opm_port_ipv6); diff --git a/ircd/s_conf.c b/ircd/s_conf.c index dcb4911f..976c0ce7 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -680,11 +680,11 @@ set_default_conf(void) ServerInfo.description = NULL; ServerInfo.network_name = NULL; - memset(&ServerInfo.ip, 0, sizeof(ServerInfo.ip)); - ServerInfo.specific_ipv4_vhost = 0; + memset(&ServerInfo.bind4, 0, sizeof(ServerInfo.bind4)); + SET_SS_FAMILY(&ServerInfo.bind4, AF_UNSPEC); #ifdef RB_IPV6 - memset(&ServerInfo.ip6, 0, sizeof(ServerInfo.ip6)); - ServerInfo.specific_ipv6_vhost = 0; + memset(&ServerInfo.bind6, 0, sizeof(ServerInfo.bind6)); + SET_SS_FAMILY(&ServerInfo.bind6, AF_UNSPEC); #endif AdminInfo.name = NULL; diff --git a/ircd/s_newconf.c b/ircd/s_newconf.c index 3815f839..a9c66179 100644 --- a/ircd/s_newconf.c +++ b/ircd/s_newconf.c @@ -324,8 +324,24 @@ struct server_conf * make_server_conf(void) { struct server_conf *server_p = rb_malloc(sizeof(struct server_conf)); - server_p->aftype = AF_INET; - return server_p; + + SET_SS_FAMILY(&server_p->connect4, AF_UNSPEC); + SET_SS_LEN(&server_p->connect4, sizeof(struct sockaddr_in)); + + SET_SS_FAMILY(&server_p->bind4, AF_UNSPEC); + SET_SS_LEN(&server_p->bind4, sizeof(struct sockaddr_in)); + +#ifdef RB_IPV6 + SET_SS_FAMILY(&server_p->connect6, AF_UNSPEC); + SET_SS_LEN(&server_p->connect6, sizeof(struct sockaddr_in6)); + + SET_SS_FAMILY(&server_p->bind6, AF_UNSPEC); + SET_SS_LEN(&server_p->bind6, sizeof(struct sockaddr_in6)); +#endif + + server_p->aftype = AF_UNSPEC; + + return rb_malloc(sizeof(struct server_conf)); } void @@ -348,13 +364,14 @@ free_server_conf(struct server_conf *server_p) } rb_free(server_p->name); - rb_free(server_p->host); + rb_free(server_p->connect_host); + rb_free(server_p->bind_host); rb_free(server_p->class_name); rb_free(server_p); } /* - * conf_dns_callback + * conf_connect_dns_callback * inputs - pointer to struct ConfItem * - pointer to adns reply * output - none @@ -364,14 +381,59 @@ free_server_conf(struct server_conf *server_p) * if successful save hp in the conf item it was called with */ static void -conf_dns_callback(const char *result, int status, int aftype, void *data) +conf_connect_dns_callback(const char *result, int status, int aftype, void *data) { struct server_conf *server_p = data; - if(status == 1) - rb_inet_pton_sock(result, (struct sockaddr *)&server_p->my_ipnum); + if(aftype == AF_INET) + { + if(status == 1) + rb_inet_pton_sock(result, (struct sockaddr *)&server_p->connect4); - server_p->dns_query = 0; + server_p->dns_query_connect4 = 0; + } +#ifdef RB_IPV6 + else if(aftype == AF_INET6) + { + if(status == 1) + rb_inet_pton_sock(result, (struct sockaddr *)&server_p->connect6); + + server_p->dns_query_connect6 = 0; + } +#endif +} + +/* + * conf_bind_dns_callback + * inputs - pointer to struct ConfItem + * - pointer to adns reply + * output - none + * side effects - called when resolver query finishes + * if the query resulted in a successful search, hp will contain + * a non-null pointer, otherwise hp will be null. + * if successful save hp in the conf item it was called with + */ +static void +conf_bind_dns_callback(const char *result, int status, int aftype, void *data) +{ + struct server_conf *server_p = data; + + if(aftype == AF_INET) + { + if(status == 1) + rb_inet_pton_sock(result, (struct sockaddr *)&server_p->bind4); + + server_p->dns_query_bind4 = 0; + } +#ifdef RB_IPV6 + else if(aftype == AF_INET6) + { + if(status == 1) + rb_inet_pton_sock(result, (struct sockaddr *)&server_p->bind6); + + server_p->dns_query_bind6 = 0; + } +#endif } void @@ -395,11 +457,25 @@ add_server_conf(struct server_conf *server_p) server_p->class_name = rb_strdup("default"); } - if(strpbrk(server_p->host, "*?")) - return; + if(server_p->connect_host && !strpbrk(server_p->connect_host, "*?")) + { + server_p->dns_query_connect4 = + lookup_hostname(server_p->connect_host, AF_INET, conf_connect_dns_callback, server_p); +#ifdef RB_IPV6 + server_p->dns_query_connect6 = + lookup_hostname(server_p->connect_host, AF_INET6, conf_connect_dns_callback, server_p); +#endif + } - server_p->dns_query = - lookup_hostname(server_p->host, GET_SS_FAMILY(&server_p->my_ipnum), conf_dns_callback, server_p); + if(server_p->bind_host) + { + server_p->dns_query_bind4 = + lookup_hostname(server_p->bind_host, AF_INET, conf_bind_dns_callback, server_p); +#ifdef RB_IPV6 + server_p->dns_query_bind6 = + lookup_hostname(server_p->bind_host, AF_INET6, conf_bind_dns_callback, server_p); +#endif + } } struct server_conf * diff --git a/ircd/s_serv.c b/ircd/s_serv.c index 734efb9e..28ed4286 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -363,6 +363,8 @@ check_server(const char *name, struct Client *client_p) RB_DLINK_FOREACH(ptr, server_conf_list.head) { + struct rb_sockaddr_storage client_addr; + tmp_p = ptr->data; if(ServerConfIllegal(tmp_p)) @@ -373,10 +375,19 @@ check_server(const char *name, struct Client *client_p) name_matched = true; - /* XXX: Fix me for IPv6 */ - /* XXX sockhost is the IPv4 ip as a string */ - if(match(tmp_p->host, client_p->host) || - match(tmp_p->host, client_p->sockhost)) + if(rb_inet_pton_sock(client_p->sockhost, (struct sockaddr *)&client_addr) <= 0) + SET_SS_FAMILY(&client_addr, AF_UNSPEC); + + if((tmp_p->connect_host && match(tmp_p->connect_host, client_p->host)) + || (GET_SS_FAMILY(&client_addr) == GET_SS_FAMILY(&tmp_p->connect4) + && comp_with_mask_sock((struct sockaddr *)&client_addr, + (struct sockaddr *)&tmp_p->connect4, 32)) +#ifdef RB_IPV6 + || (GET_SS_FAMILY(&client_addr) == GET_SS_FAMILY(&tmp_p->connect6) + && comp_with_mask_sock((struct sockaddr *)&client_addr, + (struct sockaddr *)&tmp_p->connect6, 128)) +#endif + ) { host_matched = true; @@ -1010,7 +1021,8 @@ int serv_connect(struct server_conf *server_p, struct Client *by) { struct Client *client_p; - struct rb_sockaddr_storage myipnum; + struct rb_sockaddr_storage sa_connect; + struct rb_sockaddr_storage sa_bind; char note[HOSTLEN + 10]; rb_fde_t *F; @@ -1018,8 +1030,39 @@ serv_connect(struct server_conf *server_p, struct Client *by) if(server_p == NULL) return 0; +#ifdef RB_IPV6 + if(server_p->aftype != AF_UNSPEC + && GET_SS_FAMILY(&server_p->connect4) == AF_INET + && GET_SS_FAMILY(&server_p->connect6) == AF_INET6) + { + if(rand() % 2 == 0) + { + sa_connect = server_p->connect4; + sa_bind = server_p->bind4; + } + else + { + sa_connect = server_p->connect6; + sa_bind = server_p->bind6; + } + } + else if(server_p->aftype == AF_INET || GET_SS_FAMILY(&server_p->connect4) == AF_INET) +#endif + { + sa_connect = server_p->connect4; + sa_bind = server_p->bind4; + } +#ifdef RB_IPV6 + else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6) + { + sa_connect = server_p->connect6; + sa_bind = server_p->bind6; + } +#endif + /* log */ - rb_inet_ntop_sock((struct sockaddr *)&server_p->my_ipnum, buf, sizeof(buf)); + buf[0] = 0; + rb_inet_ntop_sock((struct sockaddr *)&sa_connect, buf, sizeof(buf)); ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf); /* @@ -1037,7 +1080,12 @@ serv_connect(struct server_conf *server_p, struct Client *by) } /* create a socket for the server connection */ - if((F = rb_socket(GET_SS_FAMILY(&server_p->my_ipnum), SOCK_STREAM, 0, NULL)) == NULL) + if(GET_SS_FAMILY(&sa_connect) == AF_UNSPEC) + { + ilog_error("unspecified socket address family"); + return 0; + } + else if((F = rb_socket(GET_SS_FAMILY(&sa_connect), SOCK_STREAM, 0, NULL)) == NULL) { ilog_error("opening a stream socket"); return 0; @@ -1052,11 +1100,14 @@ serv_connect(struct server_conf *server_p, struct Client *by) /* Copy in the server, hostname, fd */ rb_strlcpy(client_p->name, server_p->name, sizeof(client_p->name)); - rb_strlcpy(client_p->host, server_p->host, sizeof(client_p->host)); + if(server_p->connect_host) + rb_strlcpy(client_p->host, server_p->connect_host, sizeof(client_p->host)); + else + rb_strlcpy(client_p->host, buf, sizeof(client_p->host)); rb_strlcpy(client_p->sockhost, buf, sizeof(client_p->sockhost)); client_p->localClient->F = F; /* shove the port number into the sockaddr */ - SET_SS_PORT(&server_p->my_ipnum, htons(server_p->port)); + SET_SS_PORT(&sa_connect, htons(server_p->port)); /* * Set up the initial server evilness, ripped straight from @@ -1091,58 +1142,22 @@ serv_connect(struct server_conf *server_p, struct Client *by) SetConnecting(client_p); rb_dlinkAddTail(client_p, &client_p->node, &global_client_list); - if(ServerConfVhosted(server_p)) + if(GET_SS_FAMILY(&sa_bind) == AF_UNSPEC) { - memcpy(&myipnum, &server_p->my_ipnum, sizeof(myipnum)); - SET_SS_FAMILY(&myipnum, GET_SS_FAMILY(&server_p->my_ipnum)); - SET_SS_PORT(&myipnum, 0); - - } - else if(GET_SS_FAMILY(&server_p->my_ipnum) == AF_INET && ServerInfo.specific_ipv4_vhost) - { - memcpy(&myipnum, &ServerInfo.ip, sizeof(myipnum)); - SET_SS_FAMILY(&myipnum, AF_INET); - SET_SS_PORT(&myipnum, 0); - SET_SS_LEN(&myipnum, sizeof(struct sockaddr_in)); - } - + if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind4)) + sa_bind = ServerInfo.bind4; #ifdef RB_IPV6 - else if((GET_SS_FAMILY(&server_p->my_ipnum) == AF_INET6) && ServerInfo.specific_ipv6_vhost) - { - memcpy(&myipnum, &ServerInfo.ip6, sizeof(myipnum)); - SET_SS_FAMILY(&myipnum, AF_INET6); - SET_SS_PORT(&myipnum, 0); - SET_SS_LEN(&myipnum, sizeof(struct sockaddr_in6)); - } + if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind6)) + sa_bind = ServerInfo.bind6; #endif - else - { - if(ServerConfSSL(server_p)) - { - rb_connect_tcp(client_p->localClient->F, - (struct sockaddr *)&server_p->my_ipnum, NULL, 0, - serv_connect_ssl_callback, client_p, - ConfigFileEntry.connect_timeout); - } - else - rb_connect_tcp(client_p->localClient->F, - (struct sockaddr *)&server_p->my_ipnum, NULL, 0, - serv_connect_callback, client_p, - ConfigFileEntry.connect_timeout); - - return 1; } - if(ServerConfSSL(server_p)) - rb_connect_tcp(client_p->localClient->F, (struct sockaddr *)&server_p->my_ipnum, - (struct sockaddr *)&myipnum, - GET_SS_LEN(&myipnum), serv_connect_ssl_callback, client_p, - ConfigFileEntry.connect_timeout); - else - rb_connect_tcp(client_p->localClient->F, (struct sockaddr *)&server_p->my_ipnum, - (struct sockaddr *)&myipnum, - GET_SS_LEN(&myipnum), serv_connect_callback, client_p, - ConfigFileEntry.connect_timeout); + rb_connect_tcp(client_p->localClient->F, + (struct sockaddr *)&sa_connect, + GET_SS_FAMILY(&sa_bind) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind, + GET_SS_LEN(&sa_bind), + ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback, + client_p, ConfigFileEntry.connect_timeout); return 1; }