ircd: server connection configuration

Fix the server connection configuration so that it can simultaneously
handle a hostname/IPv4/IPv6 for connecting and a hostname/IPv4/IPv6
for binding. Maintains backwards compatibility for matching a hostname
with a mask.

Multiple host/vhost entries can be specified and the last value for
each address family is stored. Hostnames that resolve automatically
overwrite the IP address.

Server connections can now be made to either IPv4 or IPv6 at random
as well as preferring a specific address family.
This commit is contained in:
Simon Arlott 2016-04-24 17:05:05 +01:00
parent 65f43a4fc4
commit d4214e9445
No known key found for this signature in database
GPG key ID: C8975F2043CA5D24
9 changed files with 248 additions and 121 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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 *

View file

@ -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;
}