diff --git a/docs/help/in/connect.in b/docs/help/in/connect.in index e5ba96e1..f4671ab4 100644 --- a/docs/help/in/connect.in +++ b/docs/help/in/connect.in @@ -1,8 +1,9 @@ @SYNTAX:connect@ - -ircnet the IRCNet - -host the host + -4, -6: specify explicitly whether to use IPv4 or IPv6 address + -ircnet: the IRCNet + -host: the host This command makes irssi to connect to specified server. Current connections are kept and a new one is created. diff --git a/docs/help/in/server.in b/docs/help/in/server.in index 3d3ebee5..f75462cb 100644 --- a/docs/help/in/server.in +++ b/docs/help/in/server.in @@ -1,6 +1,7 @@ @SYNTAX:server@ + -4, -6: specify explicitly whether to use IPv4 or IPv6 address -auto: Automatically connect to server at startup (default) -noauto: Don't connect to server at startup -ircnet: Specify what IRC network this server belongs to diff --git a/src/common.h b/src/common.h index 8a638097..1f0b3a3a 100644 --- a/src/common.h +++ b/src/common.h @@ -47,10 +47,14 @@ #include "nls.h" #define g_free_not_null(a) \ - if (a) g_free(a); + G_STMT_START { \ + if (a) g_free(a); \ + } G_STMT_END #define g_free_and_null(a) \ - if (a) { g_free(a); (a) = NULL; } + G_STMT_START { \ + if (a) { g_free(a); (a) = NULL; } \ + } G_STMT_END #define G_INPUT_READ (1 << 0) #define G_INPUT_WRITE (1 << 1) diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 94d65e0e..11cc9c30 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -89,13 +89,10 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr) host = g_hash_table_lookup(optlist, "host"); if (host != NULL && *host != '\0') { - IPADDR ip; + IPADDR ip4, ip6; - if (net_gethostbyname(host, &ip, conn->family) == 0) { - if (conn->own_ip == NULL) - conn->own_ip = g_new(IPADDR, 1); - memcpy(conn->own_ip, &ip, sizeof(IPADDR)); - } + if (net_gethostbyname(host, &ip4, &ip6) == 0) + server_connect_own_ip_save(conn, &ip4, &ip6); } cmd_params_free(free_arg); diff --git a/src/core/chatnet-rec.h b/src/core/chatnet-rec.h index 3044643a..e3ed8aa0 100644 --- a/src/core/chatnet-rec.h +++ b/src/core/chatnet-rec.h @@ -9,4 +9,4 @@ char *realname; char *own_host; /* address to use when connecting this server */ char *autosendcmd; /* command to send after connecting to this ircnet */ -IPADDR *own_ip; /* resolved own_address if not NULL */ +IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */ diff --git a/src/core/net-nonblock.c b/src/core/net-nonblock.c index c6d512f6..7392f429 100644 --- a/src/core/net-nonblock.c +++ b/src/core/net-nonblock.c @@ -73,7 +73,7 @@ static int g_io_channel_read_block(GIOChannel *channel, void *data, int len) /* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is written to pipe when found PID of the resolver child is returned */ -int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int family) +int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe) { RESOLVED_IP_REC rec; const char *errorstr; @@ -100,7 +100,7 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int family) /* child */ memset(&rec, 0, sizeof(rec)); - rec.error = net_gethostbyname(addr, &rec.ip, family); + rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6); if (rec.error == 0) { errorstr = NULL; } else { @@ -186,6 +186,7 @@ static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe) { RESOLVED_IP_REC iprec; GIOChannel *handle; + IPADDR *ip; g_return_if_fail(rec != NULL); @@ -199,8 +200,9 @@ static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe) g_io_channel_close(rec->pipes[1]); g_io_channel_unref(rec->pipes[1]); + ip = iprec.ip4.family != 0 ? &iprec.ip4 : &iprec.ip6; handle = iprec.error == -1 ? NULL : - net_connect_ip(&iprec.ip, rec->port, rec->my_ip); + net_connect_ip(ip, rec->port, rec->my_ip); g_free_not_null(rec->my_ip); @@ -242,8 +244,7 @@ int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip, rec->pipes[1] = g_io_channel_unix_new(fd[1]); /* start nonblocking host name lookup */ - net_gethostbyname_nonblock(server, rec->pipes[1], - my_ip == NULL ? 0 : my_ip->family); + net_gethostbyname_nonblock(server, rec->pipes[1]); rec->tag = g_input_add(rec->pipes[0], G_INPUT_READ, (GInputFunction) simple_readpipe, rec); diff --git a/src/core/net-nonblock.h b/src/core/net-nonblock.h index 7ee85823..a0e5cddf 100644 --- a/src/core/net-nonblock.h +++ b/src/core/net-nonblock.h @@ -4,7 +4,7 @@ #include "network.h" typedef struct { - IPADDR ip; /* resolved ip addres */ + IPADDR ip4, ip6; /* resolved ip addresses */ int error; /* error, 0 = no error, -1 = error: */ int errlen; /* error text length */ char *errorstr; /* error string - dynamically allocated, you'll @@ -24,7 +24,7 @@ typedef void (*NET_CALLBACK) (GIOChannel *, void *); typedef void (*NET_HOST_CALLBACK) (RESOLVED_NAME_REC *, void *); /* nonblocking gethostbyname(), PID of the resolver child is returned. */ -int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int family); +int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe); /* Get host's name, call func when finished */ int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data); /* get the resolved IP address. returns -1 if some error occured with read() */ diff --git a/src/core/network.c b/src/core/network.c index 274ad06f..b9bf8f3f 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -47,8 +47,6 @@ union sockaddr_union { # define g_io_channel_new(handle) g_io_channel_unix_new(handle) #endif -static unsigned short default_family = 0; - /* Cygwin need this, don't know others.. */ /*#define BLOCKING_SOCKETS 1*/ @@ -138,16 +136,37 @@ int sin_get_port(union sockaddr_union *so) /* Connect to socket */ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) { - IPADDR ip; + IPADDR ip4, ip6, *ip; int family; g_return_val_if_fail(addr != NULL, NULL); family = my_ip == NULL ? 0 : my_ip->family; - if (net_gethostbyname(addr, &ip, family) == -1) + if (net_gethostbyname(addr, &ip4, &ip6) == -1) return NULL; - return net_connect_ip(&ip, port, my_ip); + if (my_ip == NULL) { + /* prefer IPv4 addresses */ + ip = ip4.family != 0 ? &ip4 : &ip6; + } else if (IPADDR_IS_V6(my_ip)) { + /* my_ip is IPv6 address, use it if possible */ + if (ip6.family != 0) + ip = &ip6; + else { + my_ip = NULL; + ip = &ip4; + } + } else { + /* my_ip is IPv4 address, use it if possible */ + if (ip4.family != 0) + ip = &ip4; + else { + my_ip = NULL; + ip = &ip6; + } + } + + return net_connect_ip(ip, port, my_ip); } /* Connect to socket with ip address */ @@ -156,6 +175,11 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) union sockaddr_union so; int handle, ret, opt = 1; + if (my_ip != NULL && ip->family != my_ip->family) { + g_warning("net_connect_ip(): ip->family != my_ip->family"); + my_ip = NULL; + } + /* create the socket */ memset(&so, 0, sizeof(so)); so.sin.sin_family = ip->family; @@ -334,28 +358,28 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port) return 0; } -/* Get IP address for host. family specifies if we should prefer to - IPv4 or IPv6 address (0 = default, AF_INET or AF_INET6). - returns 0 = ok, others = error code for net_gethosterror() */ -int net_gethostbyname(const char *addr, IPADDR *ip, int family) +/* Get IP addresses for host, both IPv4 and IPv6 if possible. + If ip->family is 0, the address wasn't found. + Returns 0 = ok, others = error code for net_gethosterror() */ +int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) { #ifdef HAVE_IPV6 union sockaddr_union *so; - struct addrinfo hints, *ai; + struct addrinfo hints, *ai, *origai; char hbuf[NI_MAXHOST]; - int host_error; + int host_error, count; #else struct hostent *hp; #endif g_return_val_if_fail(addr != NULL, -1); -#ifdef HAVE_IPV6 - memset(ip, 0, sizeof(IPADDR)); + memset(ip4, 0, sizeof(IPADDR)); + memset(ip6, 0, sizeof(IPADDR)); +#ifdef HAVE_IPV6 memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; - hints.ai_family = family != 0 ? family : default_family; /* save error to host_error for later use */ host_error = getaddrinfo(addr, NULL, &hints, &ai); @@ -366,27 +390,31 @@ int net_gethostbyname(const char *addr, IPADDR *ip, int family) sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) return 1; - so = (union sockaddr_union *) ai->ai_addr; - sin_get_ip(so, ip); - freeaddrinfo(ai); + origai = ai; count = 0; + while (ai != NULL && count < 2) { + so = (union sockaddr_union *) ai->ai_addr; + + if (ai->ai_family == AF_INET6 && ip6->family == 0) { + sin_get_ip(so, ip6); + count++; + } else if (ai->ai_family == AF_INET && ip4->family == 0) { + sin_get_ip(so, ip4); + count++; + } + ai = ai->ai_next; + } + freeaddrinfo(origai); #else hp = gethostbyname(addr); if (hp == NULL) return h_errno; - ip->family = AF_INET; - memcpy(&ip->addr, hp->h_addr, 4); + ip4->family = AF_INET; + memcpy(&ip4->addr, hp->h_addr, 4); #endif return 0; } -/* Set the default address family to use with host resolving - (AF_INET or AF_INET6) */ -void net_host_resolver_set_default_family(unsigned short family) -{ - default_family = family; -} - /* Get name for host, *name should be g_free()'d unless it's NULL. Return values are the same as with net_gethostbyname() */ int net_gethostbyaddr(IPADDR *ip, char **name) diff --git a/src/core/network.h b/src/core/network.h index d8d1f9b7..f414beff 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -33,7 +33,7 @@ struct _IPADDR { # define MAX_IP_LEN 20 #endif -#define is_ipv6_addr(ip) ((ip)->family != AF_INET) +#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET) /* returns 1 if IPADDRs are the same */ int net_ip_compare(IPADDR *ip1, IPADDR *ip2); @@ -58,13 +58,10 @@ int net_receive(GIOChannel *handle, char *buf, int len); /* Transmit data, return number of bytes sent, -1 = error */ int net_transmit(GIOChannel *handle, const char *data, int len); -/* Get IP address for host. family specifies if we should prefer to - IPv4 or IPv6 address (0 = default, AF_INET or AF_INET6). - returns 0 = ok, others = error code for net_gethosterror() */ -int net_gethostbyname(const char *addr, IPADDR *ip, int family); -/* Set the default address family to use with host resolving - (AF_INET or AF_INET6) */ -void net_host_resolver_set_default_family(unsigned short family); +/* Get IP addresses for host, both IPv4 and IPv6 if possible. + If ip->family is 0, the address wasn't found. + Returns 0 = ok, others = error code for net_gethosterror() */ +int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6); /* Get name for host, *name should be g_free()'d unless it's NULL. Return values are the same as with net_gethostbyname() */ int net_gethostbyaddr(IPADDR *ip, char **name); diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index 90b2ed7c..0186f964 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -13,7 +13,7 @@ char *address; int port; char *chatnet; -IPADDR *own_ip; +IPADDR *own_ip4, *own_ip6; char *password; char *nick; diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h index 8fa774ed..b528e570 100644 --- a/src/core/server-setup-rec.h +++ b/src/core/server-setup-rec.h @@ -9,7 +9,7 @@ int port; char *password; char *own_host; /* address to use when connecting this server */ -IPADDR *own_ip; /* resolved own_address if not NULL */ +IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */ time_t last_connect; /* to avoid reconnecting too fast.. */ diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index 147f6436..3f51bfda 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -142,9 +142,13 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->username = g_strdup(src->username); dest->realname = g_strdup(src->realname); - if (src->own_ip != NULL) { - dest->own_ip = g_new(IPADDR, 1); - memcpy(dest->own_ip, src->own_ip, sizeof(IPADDR)); + if (src->own_ip4 != NULL) { + dest->own_ip4 = g_new(IPADDR, 1); + memcpy(dest->own_ip4, src->own_ip4, sizeof(IPADDR)); + } + if (src->own_ip6 != NULL) { + dest->own_ip6 = g_new(IPADDR, 1); + memcpy(dest->own_ip6, src->own_ip6, sizeof(IPADDR)); } dest->channels = g_strdup(src->channels); diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index 46d7f64a..39f9be74 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -33,48 +33,61 @@ GSList *setupservers; char *old_source_host; int source_host_ok; /* Use source_host_ip .. */ -IPADDR *source_host_ip; /* Resolved address */ +IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */ + +static void save_ips(IPADDR *ip4, IPADDR *ip6, + IPADDR **save_ip4, IPADDR **save_ip6) +{ + if (ip4->family == 0) + g_free_and_null(*save_ip4); + else { + if (*save_ip4 == NULL) + *save_ip4 = g_new(IPADDR, 1); + memcpy(*save_ip4, &ip4, sizeof(IPADDR)); + } + + if (ip6->family == 0) + g_free_and_null(*save_ip6); + else { + if (*save_ip6 == NULL) + *save_ip6 = g_new(IPADDR, 1); + memcpy(*save_ip6, &ip6, sizeof(IPADDR)); + } +} static void get_source_host_ip(void) { - IPADDR ip; + const char *hostname; + IPADDR ip4, ip6; if (source_host_ok) return; /* FIXME: This will block! */ - source_host_ok = *settings_get_str("hostname") != '\0' && - net_gethostbyname(settings_get_str("hostname"), &ip, 0) == 0; - if (source_host_ok) { - if (source_host_ip == NULL) - source_host_ip = g_new(IPADDR, 1); - memcpy(source_host_ip, &ip, sizeof(IPADDR)); + hostname = settings_get_str("hostname"); + source_host_ok = *hostname != '\0' && + net_gethostbyname(hostname, &ip4, &ip6) == 0; + + if (source_host_ok) + save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6); + else { + g_free_and_null(source_host_ip4); + g_free_and_null(source_host_ip6); } } -static void conn_set_ip(SERVER_CONNECT_REC *conn, - IPADDR **own_ip, const char *own_host) +static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host, + IPADDR **own_ip4, IPADDR **own_ip6) { - IPADDR ip; + IPADDR ip4, ip6; - if (*own_ip != NULL) { - /* use already resolved IP */ - if (conn->own_ip == NULL) - conn->own_ip = g_new(IPADDR, 1); - memcpy(conn->own_ip, *own_ip, sizeof(IPADDR)); - return; + if (*own_ip4 == NULL && *own_ip6 == NULL) { + /* resolve the IP */ + if (net_gethostbyname(own_host, &ip4, &ip6) == 0) + save_ips(&ip4, &ip6, own_ip4, own_ip6); } - - /* resolve the IP and use it */ - if (net_gethostbyname(own_host, &ip, conn->family) == 0) { - if (conn->own_ip == NULL) - conn->own_ip = g_new(IPADDR, 1); - memcpy(conn->own_ip, &ip, sizeof(IPADDR)); - - *own_ip = g_new(IPADDR, 1); - memcpy(*own_ip, &ip, sizeof(IPADDR)); - } + server_connect_own_ip_save(conn, *own_ip4, *own_ip6); } /* Fill information to connection from server setup record */ @@ -84,8 +97,10 @@ void server_setup_fill_reconn(SERVER_CONNECT_REC *conn, g_return_if_fail(IS_SERVER_CONNECT(conn)); g_return_if_fail(IS_SERVER_SETUP(sserver)); - if (sserver->own_host != NULL) - conn_set_ip(conn, &sserver->own_ip, sserver->own_host); + if (sserver->own_host != NULL) { + conn_set_ip(conn, sserver->own_host, + &sserver->own_ip4, &sserver->own_ip6); + } if (sserver->chatnet != NULL && conn->chatnet == NULL) conn->chatnet = g_strdup(sserver->chatnet); @@ -119,10 +134,13 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn, } /* source IP */ - get_source_host_ip(); - if (source_host_ok) { - conn->own_ip = g_new(IPADDR, 1); - memcpy(conn->own_ip, source_host_ip, sizeof(IPADDR)); + if (source_host_ip4 != NULL) { + conn->own_ip4 = g_new(IPADDR, 1); + memcpy(conn->own_ip4, source_host_ip4, sizeof(IPADDR)); + } + if (source_host_ip6 != NULL) { + conn->own_ip6 = g_new(IPADDR, 1); + memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR)); } } @@ -161,8 +179,10 @@ static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn, g_free(conn->realname); conn->realname = g_strdup(chatnet->realname);; } - if (chatnet->own_host != NULL) - conn_set_ip(conn, &chatnet->own_ip, chatnet->own_host); + if (chatnet->own_host != NULL) { + conn_set_ip(conn, chatnet->own_host, + &chatnet->own_ip4, &chatnet->own_ip6); + } signal_emit("server setup fill chatnet", 2, conn, chatnet); } @@ -414,7 +434,8 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec) signal_emit("server setup destroyed", 1, rec); g_free_not_null(rec->own_host); - g_free_not_null(rec->own_ip); + g_free_not_null(rec->own_ip4); + g_free_not_null(rec->own_ip6); g_free_not_null(rec->chatnet); g_free_not_null(rec->password); g_free(rec->address); @@ -453,17 +474,11 @@ static void read_servers(void) static void read_settings(void) { - unsigned short family; - - family = settings_get_bool("resolve_prefer_ipv6") ? AF_INET6 : AF_INET; - net_host_resolver_set_default_family(family); - if (old_source_host == NULL || strcmp(old_source_host, settings_get_str("hostname")) != 0) { g_free_not_null(old_source_host); old_source_host = g_strdup(settings_get_str("hostname")); - g_free_and_null(source_host_ip); source_host_ok = FALSE; get_source_host_ip(); } @@ -472,7 +487,6 @@ static void read_settings(void) void servers_setup_init(void) { settings_add_str("server", "hostname", ""); - settings_add_bool("server", "resolve_prefer_ipv6", FALSE); settings_add_str("server", "nick", NULL); settings_add_str("server", "user_name", NULL); @@ -484,7 +498,7 @@ void servers_setup_init(void) settings_add_str("proxy", "proxy_string", "CONNECT %s %d"); setupservers = NULL; - source_host_ip = NULL; + source_host_ip4 = source_host_ip6 = NULL; old_source_host = NULL; read_settings(); @@ -495,7 +509,8 @@ void servers_setup_init(void) void servers_setup_deinit(void) { - g_free_not_null(source_host_ip); + g_free_not_null(source_host_ip4); + g_free_not_null(source_host_ip6); g_free_not_null(old_source_host); while (setupservers != NULL) diff --git a/src/core/servers-setup.h b/src/core/servers-setup.h index 9e0c7b8d..d1a8bdd9 100644 --- a/src/core/servers-setup.h +++ b/src/core/servers-setup.h @@ -16,7 +16,7 @@ struct _SERVER_SETUP_REC { extern GSList *setupservers; -extern IPADDR *source_host_ip; /* Resolved address */ +extern IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */ extern int source_host_ok; /* Use source_host_ip .. */ /* Fill reconnection specific information to connection diff --git a/src/core/servers.c b/src/core/servers.c index da16d648..ae998800 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -26,6 +26,7 @@ #include "net-sendbuffer.h" #include "misc.h" #include "rawlog.h" +#include "settings.h" #include "servers.h" #include "servers-reconnect.h" @@ -154,8 +155,10 @@ static void server_connect_callback_readpipe(SERVER_REC *server) { SERVER_CONNECT_REC *conn; RESOLVED_IP_REC iprec; - const char *errormsg; GIOChannel *handle; + IPADDR *ip, *own_ip; + const char *errormsg; + int port; g_return_if_fail(IS_SERVER(server)); @@ -172,14 +175,23 @@ static void server_connect_callback_readpipe(SERVER_REC *server) server->connect_pipe[0] = NULL; server->connect_pipe[1] = NULL; - if (iprec.error == 0) - signal_emit("server connecting", 2, server, &iprec.ip); + /* figure out if we should use IPv4 or v6 address */ + ip = iprec.error != 0 ? NULL : iprec.ip6.family == 0 || + server->connrec->family == AF_INET ? + &iprec.ip4 : &iprec.ip6; + if (iprec.ip4.family != 0 && server->connrec->family == 0 && + !settings_get_bool("resolve_prefer_ipv6")) + ip = &iprec.ip4; - conn = server->connrec; - handle = iprec.error != 0 ? NULL : - net_connect_ip(&iprec.ip, conn->proxy != NULL ? - conn->proxy_port : conn->port, - conn->own_ip != NULL ? conn->own_ip : NULL); + conn = server->connrec; + port = conn->proxy != NULL ? conn->proxy_port : conn->port; + own_ip = ip == NULL ? NULL : + (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4); + + if (ip != NULL) + signal_emit("server connecting", 2, server, ip); + + handle = ip == NULL ? NULL : net_connect_ip(ip, port, own_ip); if (handle == NULL) { /* failed */ if (iprec.error == 0 || !net_hosterror_notfound(iprec.error)) { @@ -259,15 +271,11 @@ int server_start_connect(SERVER_REC *server) server->connect_pipe[0] = g_io_channel_unix_new(fd[0]); server->connect_pipe[1] = g_io_channel_unix_new(fd[1]); - if (server->connrec->family == 0 && server->connrec->own_ip != NULL) - server->connrec->family = server->connrec->own_ip->family; - connect_address = server->connrec->proxy != NULL ? server->connrec->proxy : server->connrec->address; server->connect_pid = net_gethostbyname_nonblock(connect_address, - server->connect_pipe[1], - server->connrec->family); + server->connect_pipe[1]); server->connect_tag = g_input_add(server->connect_pipe[0], G_INPUT_READ, (GInputFunction) server_connect_callback_readpipe, @@ -405,7 +413,8 @@ void server_connect_free(SERVER_CONNECT_REC *conn) g_free_not_null(conn->address); g_free_not_null(conn->chatnet); - g_free_not_null(conn->own_ip); + g_free_not_null(conn->own_ip4); + g_free_not_null(conn->own_ip6); g_free_not_null(conn->password); g_free_not_null(conn->nick); @@ -417,6 +426,30 @@ void server_connect_free(SERVER_CONNECT_REC *conn) g_free(conn); } +/* Update own IPv4 and IPv6 records */ +void server_connect_own_ip_save(SERVER_CONNECT_REC *conn, + IPADDR *ip4, IPADDR *ip6) +{ + if (ip4 == NULL || ip4->family == 0) + g_free_and_null(conn->own_ip4); + if (ip6 == NULL || ip6->family == 0) + g_free_and_null(conn->own_ip6); + + if (ip4 != NULL && ip4->family != 0) { + /* IPv4 address was found */ + if (conn->own_ip4 != NULL) + conn->own_ip4 = g_new0(IPADDR, 1); + memcpy(conn->own_ip4, ip4, sizeof(IPADDR)); + } + + if (ip6 != NULL && ip6->family != 0) { + /* IPv6 address was found */ + if (conn->own_ip6 != NULL) + conn->own_ip6 = g_new0(IPADDR, 1); + memcpy(conn->own_ip6, ip6, sizeof(IPADDR)); + } +} + /* `optlist' should contain only one unknown key - the server tag. returns NULL if there was unknown -option */ SERVER_REC *cmd_options_get_server(const char *cmd, @@ -459,6 +492,7 @@ SERVER_REC *cmd_options_get_server(const char *cmd, void servers_init(void) { + settings_add_bool("server", "resolve_prefer_ipv6", FALSE); lookup_servers = servers = NULL; servers_reconnect_init(); diff --git a/src/core/servers.h b/src/core/servers.h index 7a88af2c..86b6a9e7 100644 --- a/src/core/servers.h +++ b/src/core/servers.h @@ -50,6 +50,10 @@ void server_connect_finished(SERVER_REC *server); /* connection to server failed */ void server_connect_failed(SERVER_REC *server, const char *msg); +/* Update own IPv4 and IPv6 records */ +void server_connect_own_ip_save(SERVER_CONNECT_REC *conn, + IPADDR *ip4, IPADDR *ip6); + /* `optlist' should contain only one unknown key - the server tag. returns NULL if there was unknown -option */ SERVER_REC *cmd_options_get_server(const char *cmd, diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index eba6dcf1..f2327c59 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -135,7 +135,7 @@ static void cmd_server_add(const char *data) if (*password != '\0') g_free_and_null(rec->password); if (g_hash_table_lookup(optlist, "host")) { g_free_and_null(rec->own_host); - rec->own_ip = NULL; + rec->own_ip4 = rec->own_ip6 = NULL; } } @@ -151,7 +151,7 @@ static void cmd_server_add(const char *data) value = g_hash_table_lookup(optlist, "host"); if (value != NULL && *value != '\0') { rec->own_host = g_strdup(value); - rec->own_ip = NULL; + rec->own_ip4 = rec->own_ip6 = NULL; } signal_emit("server add fill", 2, rec, optlist); diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c index 96092869..54a1b6da 100644 --- a/src/fe-common/irc/fe-ircnet.c +++ b/src/fe-common/irc/fe-ircnet.c @@ -106,7 +106,7 @@ static void cmd_ircnet_add(const char *data) if (g_hash_table_lookup(optlist, "realname")) g_free_and_null(rec->realname); if (g_hash_table_lookup(optlist, "host")) { g_free_and_null(rec->own_host); - rec->own_ip = NULL; + rec->own_ip4 = rec->own_ip6 = NULL; } if (g_hash_table_lookup(optlist, "autosendcmd")) g_free_and_null(rec->autosendcmd); } @@ -137,7 +137,7 @@ static void cmd_ircnet_add(const char *data) value = g_hash_table_lookup(optlist, "host"); if (value != NULL && *value != '\0') { rec->own_host = g_strdup(value); - rec->own_ip = NULL; + rec->own_ip4 = rec->own_ip6 = NULL; } value = g_hash_table_lookup(optlist, "autosendcmd"); diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c index c2b05595..2e45c2fc 100644 --- a/src/irc/dcc/dcc-chat.c +++ b/src/irc/dcc/dcc-chat.c @@ -362,6 +362,8 @@ static void sig_chat_connected(CHAT_DCC_REC *dcc) static void dcc_chat_connect(CHAT_DCC_REC *dcc) { + IPADDR *own_ip; + g_return_if_fail(IS_DCC_CHAT(dcc)); if (dcc->addrstr[0] == '\0' || @@ -370,8 +372,8 @@ static void dcc_chat_connect(CHAT_DCC_REC *dcc) return; } - dcc->handle = net_connect_ip(&dcc->addr, dcc->port, - source_host_ok ? source_host_ip : NULL); + own_ip = IPADDR_IS_V6(&dcc->addr) ? source_host_ip6 : source_host_ip4; + dcc->handle = net_connect_ip(&dcc->addr, dcc->port, own_ip); if (dcc->handle != NULL) { dcc->tagconn = g_input_add(dcc->handle, G_INPUT_WRITE | G_INPUT_READ, diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c index df51c837..27f795dc 100644 --- a/src/irc/dcc/dcc-get.c +++ b/src/irc/dcc/dcc-get.c @@ -212,14 +212,15 @@ static void sig_dccget_connected(GET_DCC_REC *dcc) void dcc_get_connect(GET_DCC_REC *dcc) { + IPADDR *own_ip; + if (dcc->get_type == DCC_GET_DEFAULT) { dcc->get_type = settings_get_bool("dcc_autorename") ? DCC_GET_RENAME : DCC_GET_OVERWRITE; } - - dcc->handle = net_connect_ip(&dcc->addr, dcc->port, - source_host_ok ? source_host_ip : NULL); + own_ip = IPADDR_IS_V6(&dcc->addr) ? source_host_ip6 : source_host_ip4; + dcc->handle = net_connect_ip(&dcc->addr, dcc->port, own_ip); if (dcc->handle != NULL) { dcc->tagconn = g_input_add(dcc->handle, diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c index 326d922d..9bfbdd1b 100644 --- a/src/irc/dcc/dcc.c +++ b/src/irc/dcc/dcc.c @@ -154,7 +154,7 @@ void dcc_ip2str(IPADDR *ip, char *host) { unsigned long addr; - if (is_ipv6_addr(ip)) { + if (IPADDR_IS_V6(ip)) { /* IPv6 */ net_ip2host(ip, host); } else {