From 51f2a4f7fb51f57fe32ce5bccdbb5ed6cd9013c3 Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Sun, 27 Jul 2025 20:06:10 +0200 Subject: [PATCH] use GIO resolver change: rename resolve_prefer_ipv6 -> irssiproxy_prefer_ipv6 --- docs/help/in/toggle.in | 1 - docs/manual.txt | 3 - meson.build | 35 ++++- src/common.h | 3 +- src/core/chat-commands.c | 9 +- src/core/net-nonblock.c | 120 +++++++----------- src/core/net-nonblock.h | 18 +-- src/core/network.c | 182 ++++++++++---------------- src/core/network.h | 30 ++--- src/core/server-connect-rec.h | 4 +- src/core/server-rec.h | 4 +- src/core/servers-reconnect.c | 11 +- src/core/servers-setup.c | 14 +- src/core/servers.c | 190 +++++++++++++++------------- src/core/servers.h | 1 + src/fe-common/core/fe-common-core.c | 23 +++- src/irc/proxy/listen.c | 11 +- src/irc/proxy/proxy.c | 1 + 18 files changed, 317 insertions(+), 343 deletions(-) diff --git a/docs/help/in/toggle.in b/docs/help/in/toggle.in index f87a6462..9469ba5c 100644 --- a/docs/help/in/toggle.in +++ b/docs/help/in/toggle.in @@ -15,7 +15,6 @@ %9Examples:%9 - /TOGGLE resolve_prefer_ipv6 /TOGGLE channels_rejoin_unavailable ON %9See also:%9 SET diff --git a/docs/manual.txt b/docs/manual.txt index d55e362e..073abe18 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -373,9 +373,6 @@ After connected to server, Irssi can automatically change your user mode. You can set it with /SET usermode , default is +i. - /SET resolve_prefer_ipv6 - If ON, prefer IPv6 for hosts that - have both v4 and v6 addresses. - 5.5 Automatic reconnecting If you get disconnected from server, Irssi will try to reconnect diff --git a/meson.build b/meson.build index 9499a126..4f78600e 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,7 @@ project('irssi', 'c', glib_internal_version = 'glib-2.74.3' # keep this in sync with subprojects/glib.wrap glib_pcre2_internal_version = 'pcre2-10.40' +glib_libffi_internal_version = 'libffi' cc = meson.get_compiler('c') rootinc = include_directories('.') dep = [] @@ -217,11 +218,6 @@ if not glib_dep.found() prov_lib = cc.find_library('iconv', dirs : '/usr/local/lib') glib_internal_usr_local = true endif - if cc.has_function('libiconv_open', dependencies : prov_lib) - glib_internal_configure_args += '-Diconv=gnu' - else - glib_internal_configure_args += '-Diconv=native' - endif glib_internal_dependencies += prov_lib endif @@ -254,7 +250,7 @@ if not glib_dep.found() glib_internal_configure_t = custom_target('glib-internal-configure', command : [ meson_cmd, 'setup', '--prefix=/irssi-glib-internal', '--buildtype=' + get_option('buildtype'), - '-Dlibmount=disabled', '-Dselinux=disabled', '-Ddefault_library=static', '-Dforce_fallback_for=pcre2', + '-Dlibmount=disabled', '-Dselinux=disabled', '-Ddefault_library=static', '-Dforce_fallback_for=pcre2,libffi', glib_internal_configure_args, (meson.current_build_dir() / 'build-subprojects' / 'glib'), (meson.current_source_dir() / 'subprojects' / glib_internal_version) ], @@ -263,9 +259,13 @@ if not glib_dep.found() depends : glib_internal_download_t,) glib_internal_build_t = custom_target('glib-internal-build', command : [ ninja, '-C', meson.current_build_dir() / 'build-subprojects' / 'glib', + 'subprojects' / glib_libffi_internal_version / 'src' / 'libffi.a', 'subprojects' / glib_pcre2_internal_version / 'libpcre2-8.a', 'glib' / 'libglib-2.0.a', - 'gmodule' / 'libgmodule-2.0.a'], + 'gmodule' / 'libgmodule-2.0.a', + 'gobject' / 'libgobject-2.0.a', + 'gio' / 'libgio-2.0.a', + ], console : true, output : ['glib-internal-build'], depends : glib_internal_configure_t,) @@ -295,11 +295,32 @@ if not glib_dep.found() ], link_args : [ meson.current_build_dir() / 'build-subprojects' / 'glib' / 'gmodule' / 'libgmodule-2.0.a' ], ) + gobject_dep = declare_dependency(sources : glib_internal_build_t, + compile_args : [ + '-isystem' + (meson.current_build_dir() / 'build-subprojects' / 'glib'), + ], + link_args : [ + meson.current_build_dir() / 'build-subprojects' / 'glib' / 'subprojects' / glib_libffi_internal_version / 'src' / 'libffi.a', + meson.current_build_dir() / 'build-subprojects' / 'glib' / 'gobject' / 'libgobject-2.0.a' + ], + ) + gio_dep = declare_dependency(sources : glib_internal_build_t, + dependencies : cc.find_library('z'), + compile_args : [ + '-isystem' + (meson.current_source_dir() / 'subprojects' / glib_internal_version / 'gio'), + '-isystem' + (meson.current_build_dir() / 'build-subprojects' / 'glib'), + ], + link_args : [ meson.current_build_dir() / 'build-subprojects' / 'glib' / 'gio' / 'libgio-2.0.a' ], + ) else gmodule_dep = dependency('gmodule-2.0', static : want_static_dependency, include_type : 'system') + gobject_dep = dependency('gobject-2.0', static : want_static_dependency, include_type : 'system') + gio_dep = dependency('gio-2.0', static : want_static_dependency, include_type : 'system') endif dep += glib_dep dep += gmodule_dep +dep += gobject_dep +dep += gio_dep if glib_internal and want_static_dependency and want_fuzzer openssl_proj = subproject('openssl', default_options : ['default_library=static', 'asm=disabled']) diff --git a/src/common.h b/src/common.h index e514f279..06b63a15 100644 --- a/src/common.h +++ b/src/common.h @@ -6,7 +6,7 @@ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ -#define IRSSI_ABI_VERSION 56 +#define IRSSI_ABI_VERSION 57 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697 @@ -38,6 +38,7 @@ #include #include +#include typedef guint64 uoff_t; #define PRIuUOFF_T G_GUINT64_FORMAT diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 10da0d2c..9e2b80e8 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -101,10 +101,11 @@ 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 ip4, ip6; - - if (net_gethostbyname(host, &ip4, &ip6) == 0) - server_connect_own_ip_save(conn, &ip4, &ip6); + IPADDR ip4 = { 0 }; + IPADDR ip6 = { 0 }; + if (net_gethostbyname_first_ips(host, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, &ip4, + &ip6) == 0) + server_connect_own_ip_save(conn, &ip4, &ip6); } cmd_params_free(free_arg); diff --git a/src/core/net-nonblock.c b/src/core/net-nonblock.c index 643884ee..494d9894 100644 --- a/src/core/net-nonblock.c +++ b/src/core/net-nonblock.c @@ -22,86 +22,54 @@ #include -#include +#include #include -/* 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 reverse_lookup) -{ - RESOLVED_IP_REC rec; - const char *errorstr; - int pid; +typedef struct { + NetGethostbynameContinuationFunc cont; + void *cont_data; +} NET_GETHOSTBYNAME_CALLBACK_DATA; - (void) reverse_lookup; /* Kept for API backward compatibility */ +static void net_gethostbyname_callback(GResolver *resolver, GAsyncResult *result, + NET_GETHOSTBYNAME_CALLBACK_DATA *data) +{ + /* GList */ + GList *ailist; + GError *error; + RESOLVED_IP_REC *iprec; + + error = NULL; + ailist = g_resolver_lookup_by_name_with_flags_finish(resolver, result, &error); + iprec = g_new0(RESOLVED_IP_REC, 1); + if (error != NULL) { + iprec->error = error; + } else { + iprec->ailist = ailist; + } + g_object_unref(resolver); + resolved_ip_ref(iprec); + + data->cont(iprec, data->cont_data); + g_free(data); +} + +/* nonblocking gethostbyname() */ +GCancellable *net_gethostbyname_nonblock(const char *addr, GResolverNameLookupFlags flags, + NetGethostbynameContinuationFunc cont, void *cont_data) +{ + GResolver *resolver; + GCancellable *cancellable; + NET_GETHOSTBYNAME_CALLBACK_DATA *data; g_return_val_if_fail(addr != NULL, FALSE); - pid = fork(); - if (pid > 0) { - /* parent */ - pidwait_add(pid); - return pid; - } - - if (pid != 0) { - /* failed! */ - g_warning("net_connect_thread(): fork() failed! " - "Using blocking resolving"); - } - - /* child */ - srand(time(NULL)); - - memset(&rec, 0, sizeof(rec)); - rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6); - if (rec.error == 0) { - errorstr = NULL; - } else { - errorstr = net_gethosterror(rec.error); - rec.errlen = errorstr == NULL ? 0 : strlen(errorstr)+1; - } - - i_io_channel_write_block(pipe, &rec, sizeof(rec)); - if (rec.errlen != 0) - i_io_channel_write_block(pipe, (void *) errorstr, rec.errlen); - - if (pid == 0) - _exit(99); - - /* we used blocking lookup */ - return 0; -} - -/* get the resolved IP address */ -int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec) -{ - rec->error = -1; - rec->errorstr = NULL; - - fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK); - - /* get ip+error */ - if (i_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) { - rec->errorstr = g_strdup_printf("Host name lookup: %s", - g_strerror(errno)); - return -1; - } - - if (rec->error) { - /* read error string, if we can't read everything for some - reason, just ignore it. */ - rec->errorstr = g_malloc0(rec->errlen+1); - i_io_channel_read_block(pipe, rec->errorstr, rec->errlen); - } - - return 0; -} - -/* Kill the resolver child */ -void net_disconnect_nonblock(int pid) -{ - g_return_if_fail(pid > 0); - - kill(pid, SIGKILL); + resolver = g_resolver_get_default(); + cancellable = g_cancellable_new(); + data = g_new0(NET_GETHOSTBYNAME_CALLBACK_DATA, 1); + data->cont = cont; + data->cont_data = cont_data; + g_resolver_lookup_by_name_with_flags_async(resolver, addr, flags, cancellable, + (GAsyncReadyCallback) net_gethostbyname_callback, + data); + return cancellable; } diff --git a/src/core/net-nonblock.h b/src/core/net-nonblock.h index c93dd9e3..c4cb7824 100644 --- a/src/core/net-nonblock.h +++ b/src/core/net-nonblock.h @@ -3,20 +3,10 @@ #include -typedef struct { - 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 - need to free() it yourself unless it's NULL */ -} RESOLVED_IP_REC; +typedef void (*NetGethostbynameContinuationFunc)(RESOLVED_IP_REC *, void *); -/* nonblocking gethostbyname(), PID of the resolver child is returned. */ -int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int reverse_lookup); -/* get the resolved IP address. returns -1 if some error occurred with read() */ -int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec); - -/* Kill the resolver child */ -void net_disconnect_nonblock(int pid); +/* nonblocking gethostbyname(), Cancellable of the resolver child is returned. */ +GCancellable *net_gethostbyname_nonblock(const char *addr, GResolverNameLookupFlags flags, + NetGethostbynameContinuationFunc cont, void *cont_data); #endif diff --git a/src/core/network.c b/src/core/network.c index fa70d62a..c763a7c6 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -81,27 +81,6 @@ int i_io_channel_read_block(GIOChannel *channel, void *data, int len) return received < len ? -1 : 0; } -IPADDR ip4_any = { - AF_INET, -#if defined(IN6ADDR_ANY_INIT) - IN6ADDR_ANY_INIT -#else - { INADDR_ANY } -#endif -}; - -int net_ip_compare(IPADDR *ip1, IPADDR *ip2) -{ - if (ip1->family != ip2->family) - return 0; - - if (ip1->family == AF_INET6) - return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0; - - return memcmp(&ip1->ip, &ip2->ip, 4) == 0; -} - - static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip) { if (ip == NULL) { @@ -392,95 +371,87 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port) return 0; } -/* 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) +void resolved_ip_ref(RESOLVED_IP_REC *iprec) { - union sockaddr_union *so; - struct addrinfo hints, *ai, *ailist; - int ret, count_v4, count_v6, use_v4, use_v6; + iprec->refcount++; +} + +int resolved_ip_unref(RESOLVED_IP_REC *iprec) +{ + if (--iprec->refcount > 0) { + return TRUE; + } + + g_resolver_free_addresses(iprec->ailist); + if (iprec->error != NULL) { + g_error_free(iprec->error); + } + g_free(iprec); + + return FALSE; +} + +/* Get IP addresses for host, both IPv4 and IPv6 if possible. */ +static RESOLVED_IP_REC *net_gethostbyname(const char *addr, GResolverNameLookupFlags flags) +{ + /* GList */ + GList *ailist; + GError *error; + GResolver *resolver; + RESOLVED_IP_REC *iprec; #ifdef HAVE_CAPSICUM if (capsicum_enabled()) - return (capsicum_net_gethostbyname(addr, ip4, ip6)); + return (capsicum_net_gethostbyname(addr, flags)); #endif - g_return_val_if_fail(addr != NULL, -1); + g_return_val_if_fail(addr != NULL, NULL); - memset(ip4, 0, sizeof(IPADDR)); - memset(ip6, 0, sizeof(IPADDR)); - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - - /* save error to host_error for later use */ - ret = getaddrinfo(addr, NULL, &hints, &ailist); - if (ret != 0) - return ret; - - /* count IPs */ - count_v4 = count_v6 = 0; - for (ai = ailist; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family == AF_INET) - count_v4++; - else if (ai->ai_family == AF_INET6) - count_v6++; + error = NULL; + resolver = g_resolver_get_default(); + ailist = g_resolver_lookup_by_name_with_flags(resolver, addr, flags, NULL, &error); + iprec = g_new0(RESOLVED_IP_REC, 1); + if (error != NULL) { + iprec->error = error; + } else { + iprec->ailist = ailist; } + g_object_unref(resolver); + resolved_ip_ref(iprec); - if (count_v4 == 0 && count_v6 == 0) - return EAI_NONAME; /* shouldn't happen? */ - - /* if there are multiple addresses, return random one */ - use_v4 = count_v4 <= 1 ? 0 : rand() % count_v4; - use_v6 = count_v6 <= 1 ? 0 : rand() % count_v6; - - count_v4 = count_v6 = 0; - for (ai = ailist; ai != NULL; ai = ai->ai_next) { - so = (union sockaddr_union *) ai->ai_addr; - - if (ai->ai_family == AF_INET) { - if (use_v4 == count_v4) - sin_get_ip(so, ip4); - count_v4++; - } else if (ai->ai_family == AF_INET6) { - if (use_v6 == count_v6) - sin_get_ip(so, ip6); - count_v6++; - } - } - freeaddrinfo(ailist); - return 0; + return iprec; } -/* 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) +int net_gethostbyname_first_ips(const char *addr, GResolverNameLookupFlags flags, IPADDR *ip4, + IPADDR *ip6) { - union sockaddr_union so; - int host_error; - char hostname[NI_MAXHOST]; + RESOLVED_IP_REC *iprec; - g_return_val_if_fail(ip != NULL, -1); - g_return_val_if_fail(name != NULL, -1); + iprec = net_gethostbyname(addr, flags); + if (iprec->error == NULL) { + GList *curr; - *name = NULL; + for (curr = iprec->ailist; curr->next; curr = curr->next) { + unsigned short family; + GInetAddress *addr; - memset(&so, 0, sizeof(so)); - sin_set_ip(&so, ip); + addr = curr->data; + family = g_inet_address_get_family(addr); + if (ip4->family == 0 && family == AF_INET) { + ip4->family = AF_INET; + memcpy(&ip4->ip, g_inet_address_to_bytes(addr), sizeof(ip4->ip)); + } else if (ip6->family == 0 && family == AF_INET6) { + ip6->family = AF_INET6; + memcpy(&ip6->ip, g_inet_address_to_bytes(addr), sizeof(ip6->ip)); + } + } - /* save error to host_error for later use */ - host_error = getnameinfo((struct sockaddr *)&so, sizeof(so), - hostname, sizeof(hostname), - NULL, 0, - NI_NAMEREQD); - if (host_error != 0) - return host_error; - - *name = g_strdup(hostname); - - return 0; + resolved_ip_unref(iprec); + return 0; + } else { + resolved_ip_unref(iprec); + return -1; + } } int net_ip2host(IPADDR *ip, char *host) @@ -519,29 +490,6 @@ int net_geterror(GIOChannel *handle) return data; } -/* get error of net_gethostname() */ -const char *net_gethosterror(int error) -{ - g_return_val_if_fail(error != 0, NULL); - - if (error == EAI_SYSTEM) { - return strerror(errno); - } else { - return gai_strerror(error); - } -} - -/* return TRUE if host lookup failed because it didn't exist (ie. not - some error with name server) */ -int net_hosterror_notfound(int error) -{ -#ifdef EAI_NODATA /* NODATA is deprecated */ - return error != 1 && (error == EAI_NONAME || error == EAI_NODATA); -#else - return error != 1 && (error == EAI_NONAME); -#endif -} - /* Get name of TCP service */ char *net_getservbyport(int port) { diff --git a/src/core/network.h b/src/core/network.h index 5cd85ccc..647cd8c7 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifndef AF_INET6 # ifdef PF_INET6 @@ -20,6 +21,13 @@ struct _IPADDR { struct in6_addr ip; }; +typedef struct { + int refcount; + /* GList */ + GList *ailist; /* needs to be freed */ + GError *error; /* needs to be freed */ +} RESOLVED_IP_REC; + /* maxmimum string length of IP address */ #define MAX_IP_LEN INET6_ADDRSTRLEN @@ -29,9 +37,7 @@ extern IPADDR ip4_any; GIOChannel *i_io_channel_new(int handle); -/* Returns 1 if IPADDRs are the same. */ -/* Deprecated since it is unused. It will be deleted in a later release. */ -int net_ip_compare(IPADDR *ip1, IPADDR *ip2) G_GNUC_DEPRECATED; +/* OTR */ int i_io_channel_write_block(GIOChannel *channel, void *data, int len); int i_io_channel_read_block(GIOChannel *channel, void *data, int len); @@ -60,18 +66,9 @@ 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 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); -/* get error of net_gethostname() */ -const char *net_gethosterror(int error); -/* return TRUE if host lookup failed because it didn't exist (ie. not - some error with name server) */ -int net_hosterror_notfound(int error); +/* Get the first IP address for host, both IPv4 and IPv6 if possible. */ +int net_gethostbyname_first_ips(const char *addr, GResolverNameLookupFlags flags, IPADDR *ip4, + IPADDR *ip6); /* Get socket address/port */ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port); @@ -90,4 +87,7 @@ char *net_getservbyport(int port); int is_ipv4_address(const char *host); int is_ipv6_address(const char *host); +void resolved_ip_ref(RESOLVED_IP_REC *iprec); +int resolved_ip_unref(RESOLVED_IP_REC *iprec); + #endif diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index 16513109..de1f06df 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -45,6 +45,8 @@ unsigned int unix_socket:1; /* Connect using named unix socket */ unsigned int use_tls:1; /* this connection uses TLS */ unsigned int tls_verify:1; unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */ -unsigned short last_failed_family; /* #641: if we failed to connect to ipv6, try ipv4 and vice versa */ +int last_connected; +int last_failed; +RESOLVED_IP_REC *resolved_host; char *channels; char *away_reason; diff --git a/src/core/server-rec.h b/src/core/server-rec.h index 6c7c63e3..e43245a7 100644 --- a/src/core/server-rec.h +++ b/src/core/server-rec.h @@ -21,10 +21,8 @@ unsigned int no_reconnect:1; /* Don't reconnect to server */ NET_SENDBUF_REC *handle; int readtag; /* input tag */ -/* for net_gethostbyname_return() */ -GIOChannel *connect_pipe[2]; +GCancellable *connect_cancellable; int connect_tag; -int connect_pid; RAWLOG_REC *rawlog; GHashTable *module_data; diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index a9d9422b..64af6287 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -120,6 +120,10 @@ static int server_reconnect_timeout(void) if (server->connect_tag != -1) { g_source_remove(server->connect_tag); server->connect_tag = -1; + } else if (server->connect_cancellable != NULL) { + g_cancellable_cancel(server->connect_cancellable); + g_object_unref(server->connect_cancellable); + server->connect_cancellable = NULL; } server->connection_lost = TRUE; server_connect_failed(server, "Timeout"); @@ -168,7 +172,8 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) server_connect_ref(dest); dest->type = module_get_uniq_id("SERVER CONNECT", 0); dest->reconnection = src->reconnection; - dest->last_failed_family = src->last_failed_family; + dest->last_connected = src->last_connected; + dest->last_failed = src->last_failed; dest->proxy = g_strdup(src->proxy); dest->proxy_port = src->proxy_port; dest->proxy_string = g_strdup(src->proxy_string); @@ -207,6 +212,10 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->own_ip6 = g_new(IPADDR, 1); memcpy(dest->own_ip6, src->own_ip6, sizeof(IPADDR)); } + dest->resolved_host = src->resolved_host; + if (dest->resolved_host != NULL) { + resolved_ip_ref(dest->resolved_host); + } dest->channels = g_strdup(src->channels); dest->away_reason = g_strdup(src->away_reason); diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index ff3d6584..97206328 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -58,7 +58,8 @@ static void save_ips(IPADDR *ip4, IPADDR *ip6, static void get_source_host_ip(void) { const char *hostname; - IPADDR ip4, ip6; + IPADDR ip4 = { 0 }; + IPADDR ip6 = { 0 }; if (source_host_ok) return; @@ -66,7 +67,8 @@ static void get_source_host_ip(void) /* FIXME: This will block! */ hostname = settings_get_str("hostname"); source_host_ok = *hostname != '\0' && - net_gethostbyname(hostname, &ip4, &ip6) == 0; + net_gethostbyname_first_ips(hostname, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, + &ip4, &ip6) == 0; if (source_host_ok) save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6); @@ -79,12 +81,14 @@ static void get_source_host_ip(void) static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host, IPADDR **own_ip4, IPADDR **own_ip6) { - IPADDR ip4, ip6; + IPADDR ip4 = { 0 }; + IPADDR ip6 = { 0 }; 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); + if (net_gethostbyname_first_ips(own_host, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, + &ip4, &ip6) == 0) + save_ips(&ip4, &ip6, own_ip4, own_ip6); } server_connect_own_ip_save(conn, *own_ip4, *own_ip6); diff --git a/src/core/servers.c b/src/core/servers.c index 811081e8..e62f09ec 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -51,20 +51,16 @@ void server_connect_failed(SERVER_REC *server, const char *msg) g_source_remove(server->connect_tag); server->connect_tag = -1; } + if (server->connect_cancellable != NULL) { + g_cancellable_cancel(server->connect_cancellable); + g_object_unref(server->connect_cancellable); + server->connect_cancellable = NULL; + } if (server->handle != NULL) { net_sendbuffer_destroy(server->handle, TRUE); server->handle = NULL; } - if (server->connect_pipe[0] != NULL) { - g_io_channel_shutdown(server->connect_pipe[0], TRUE, NULL); - g_io_channel_unref(server->connect_pipe[0]); - g_io_channel_shutdown(server->connect_pipe[1], TRUE, NULL); - g_io_channel_unref(server->connect_pipe[1]); - server->connect_pipe[0] = NULL; - server->connect_pipe[1] = NULL; - } - server_unref(server); } @@ -156,7 +152,7 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle) error = net_geterror(handle); if (error != 0) { server->connection_lost = TRUE; - server->connrec->last_failed_family = server->connrec->chosen_family; + server->connrec->last_failed = server->connrec->last_connected; server_connect_failed(server, g_strerror(error)); return; } @@ -177,7 +173,7 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han error = irssi_ssl_handshake(handle); if (error == -1) { server->connection_lost = TRUE; - server->connrec->last_failed_family = server->connrec->chosen_family; + server->connrec->last_failed = server->connrec->last_connected; server_connect_failed(server, NULL); return; } @@ -259,12 +255,12 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, server->connection_lost = TRUE; if (ip != NULL) { - server->connrec->last_failed_family = ip->family; + server->connrec->last_failed = server->connrec->last_connected; } server_connect_failed(server, errmsg2 ? errmsg2 : errmsg); g_free(errmsg2); } else { - server->connrec->last_failed_family = 0; + server->connrec->last_failed = 0; if (!server->connrec->use_tls) server->handle = net_sendbuffer_create(handle, 0); if (server->connrec->use_tls) @@ -276,48 +272,47 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, } } -static void server_connect_callback_readpipe(SERVER_REC *server) +static int server_start_connect_resolve(SERVER_REC *server); + +static void server_connect_use_resolved(SERVER_REC *server) { - RESOLVED_IP_REC iprec; - IPADDR *ip; + IPADDR *ip; const char *errormsg; + RESOLVED_IP_REC *iprec = server->connrec->resolved_host; - g_source_remove(server->connect_tag); - server->connect_tag = -1; - - net_gethostbyname_return(server->connect_pipe[0], &iprec); - - g_io_channel_shutdown(server->connect_pipe[0], TRUE, NULL); - g_io_channel_unref(server->connect_pipe[0]); - g_io_channel_shutdown(server->connect_pipe[1], TRUE, NULL); - g_io_channel_unref(server->connect_pipe[1]); - - server->connect_pipe[0] = NULL; - server->connect_pipe[1] = NULL; - - /* figure out if we should use IPv4 or v6 address */ - if (iprec.error != 0) { - /* error */ + if (iprec->error != NULL) { + /* error */ ip = NULL; - } else if (server->connrec->family == AF_INET) { - /* force IPv4 connection */ - ip = iprec.ip4.family == 0 ? NULL : &iprec.ip4; - } else if (server->connrec->family == AF_INET6) { - /* force IPv6 connection */ - ip = iprec.ip6.family == 0 ? NULL : &iprec.ip6; } else { - /* pick the one that was found. if both were found: - 1. disprefer the last one that failed - 2. prefer ipv4 over ipv6 unless resolve_prefer_ipv6 is set - */ - if (iprec.ip4.family == 0 || - (iprec.ip6.family != 0 && - (server->connrec->last_failed_family == AF_INET || - (settings_get_bool("resolve_prefer_ipv6") && - server->connrec->last_failed_family != AF_INET6)))) { - ip = &iprec.ip6; + GList *curr; + int i; + + curr = iprec->ailist; + i = 0; + while (i < server->connrec->last_failed) { + if (curr != NULL) { + curr = curr->next; + i++; + } + /* curr is different now */ + if (curr == NULL) { + resolved_ip_unref(server->connrec->resolved_host); + server->connrec->resolved_host = NULL; + server->connrec->last_failed = 0; + /* retry resolve */ + server_start_connect_resolve(server); + return; + } + } + if (curr != NULL) { + GInetAddress *addr; + addr = curr->data; + server->connrec->last_connected = i + 1; + ip = g_new0(IPADDR, 1); + ip->family = g_inet_address_get_family(addr); + memcpy(&ip->ip, g_inet_address_to_bytes(addr), sizeof(ip->ip)); } else { - ip = &iprec.ip4; + ip = NULL; } } @@ -326,28 +321,63 @@ static void server_connect_callback_readpipe(SERVER_REC *server) server_real_connect(server, ip, NULL); errormsg = NULL; } else { - if (iprec.error == 0 || net_hosterror_notfound(iprec.error)) { + if (iprec->error->code == G_RESOLVER_ERROR_NOT_FOUND) { /* IP wasn't found for the host, don't try to reconnect back to this server */ server->dns_error = TRUE; } - if (iprec.error == 0) { - /* forced IPv4 or IPv6 address but it wasn't found */ - errormsg = server->connrec->family == AF_INET ? - "IPv4 address not found for host" : - "IPv6 address not found for host"; - } else { - /* gethostbyname() failed */ - errormsg = iprec.errorstr != NULL ? iprec.errorstr : - "Host lookup failed"; - } + errormsg = iprec->error->message; + if (errormsg == NULL) + errormsg = "Host lookup failed"; server->connection_lost = TRUE; + /* clear the error in resolved_host */ + server->connrec->resolved_host = NULL; server_connect_failed(server, errormsg); - } - g_free(iprec.errorstr); + resolved_ip_unref(iprec); + } +} + +static void server_connect_callback_resolved(RESOLVED_IP_REC *iprec, SERVER_REC *server) +{ + server->connect_cancellable = NULL; + + if (server->connrec->resolved_host != NULL) { + resolved_ip_unref(server->connrec->resolved_host); + } + server->connrec->resolved_host = iprec; + if (iprec->error == NULL && iprec->ailist == NULL) { + server->connection_lost = TRUE; + server->dns_error = TRUE; + server_connect_failed(server, "Host lookup failed"); + } else { + server_connect_use_resolved(server); + } +} + +static int server_start_connect_resolve(SERVER_REC *server) +{ + const char *connect_address; + GResolverNameLookupFlags net_gethostbyname_flags; + + connect_address = + server->connrec->proxy != NULL ? server->connrec->proxy : server->connrec->address; + net_gethostbyname_flags = G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT; + if (server->connrec->family == AF_INET) { + net_gethostbyname_flags = G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY; + } else if (server->connrec->family == AF_INET6) { + net_gethostbyname_flags = G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY; + } + if (server->connrec->resolved_host == NULL) { + server->connect_cancellable = net_gethostbyname_nonblock( + connect_address, net_gethostbyname_flags, + (NetGethostbynameContinuationFunc) server_connect_callback_resolved, server); + return FALSE; + } else { + return TRUE; + } } SERVER_REC *server_connect(SERVER_CONNECT_REC *conn) @@ -399,9 +429,6 @@ void server_connect_init(SERVER_REC *server) /* starts connecting to server */ int server_start_connect(SERVER_REC *server) { - const char *connect_address; - int fd[2]; - g_return_val_if_fail(server != NULL, FALSE); if (!server->connrec->unix_socket && server->connrec->port <= 0) return FALSE; @@ -419,30 +446,17 @@ int server_start_connect(SERVER_REC *server) /* connect with unix socket */ server_real_connect(server, NULL, server->connrec->address); } else { + int already_resolved; /* resolve host name */ - if (pipe(fd) != 0) { - g_warning("server_connect(): pipe() failed."); - g_free(server->tag); - g_free(server->nick); - return FALSE; - } - - server->connect_pipe[0] = i_io_channel_new(fd[0]); - server->connect_pipe[1] = i_io_channel_new(fd[1]); - - connect_address = server->connrec->proxy != NULL ? - server->connrec->proxy : server->connrec->address; - server->connect_pid = - net_gethostbyname_nonblock(connect_address, - server->connect_pipe[1], 0); - server->connect_tag = - i_input_add(server->connect_pipe[0], I_INPUT_READ, - (GInputFunction) server_connect_callback_readpipe, server); + already_resolved = server_start_connect_resolve(server); server->connect_time = time(NULL); lookup_servers = g_slist_append(lookup_servers, server); signal_emit("server looking", 1, server); + if (already_resolved) { + server_connect_use_resolved(server); + } } return TRUE; } @@ -481,8 +495,9 @@ void server_disconnect(SERVER_REC *server) if (server->connect_tag != -1) { /* still connecting to server.. */ - if (server->connect_pid != -1) - net_disconnect_nonblock(server->connect_pid); + server_connect_failed(server, NULL); + return; + } else if (server->connect_cancellable != NULL) { server_connect_failed(server, NULL); return; } @@ -649,6 +664,10 @@ void server_connect_unref(SERVER_CONNECT_REC *conn) g_free_not_null(conn->own_ip4); g_free_not_null(conn->own_ip6); + if (conn->resolved_host != NULL) { + resolved_ip_unref(conn->resolved_host); + } + g_free_not_null(conn->password); g_free_not_null(conn->nick); g_free_not_null(conn->username); @@ -769,7 +788,6 @@ static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto) void servers_init(void) { - settings_add_bool("server", "resolve_prefer_ipv6", TRUE); lookup_servers = servers = NULL; signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit); diff --git a/src/core/servers.h b/src/core/servers.h index d52603eb..5e5a7fd2 100644 --- a/src/core/servers.h +++ b/src/core/servers.h @@ -2,6 +2,7 @@ #define IRSSI_CORE_SERVERS_H #include +#include /* Returns SERVER_REC if it's server, NULL if it isn't. */ #define SERVER(server) \ diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c index 9724354f..06240d5c 100644 --- a/src/fe-common/core/fe-common-core.c +++ b/src/fe-common/core/fe-common-core.c @@ -166,7 +166,7 @@ void fe_common_core_init(void) settings_add_bool("lookandfeel", "use_msgs_window", FALSE); g_get_charset(&str); settings_add_str("lookandfeel", "term_charset", str); - settings_add_str("lookandfeel", "glib_log_domains", "all"); + settings_add_str("lookandfeel", "glib_log_domains", "all -glib-gio:debug"); themes_init(); theme_register(fecommon_core_formats); @@ -258,13 +258,19 @@ void fe_common_core_deinit(void) g_log_set_default_handler(logger_old, NULL); } -static gboolean glib_domain_wanted(const char *domain) +static gboolean glib_domain_wanted(const char *domain, const char *level) { const char *domains; char *c, *cur; int len = 0; int print_it = 0; /* -1 for exclude, 0 for undecided, 1 for include */ int incl; + char *domainlevel, *alllevel, *starlevel, *domainstar; + + domainlevel = g_strdup_printf("%s:%s", domain, level); + alllevel = g_strdup_printf("all:%s", level); + starlevel = g_strdup_printf("*:%s", level); + domainstar = g_strdup_printf("%s:*", domain); /* Go through each item in glib_log_domains setting to determine whether * or not we want to print message from this domain */ @@ -287,8 +293,10 @@ static gboolean glib_domain_wanted(const char *domain) } /* If we got a valid item, process it */ - if (len > 0 && (!strncmp(domain, c, len) || !strncasecmp("all", c, len) || - !strncmp("*", c, len))) + if (len > 0 && (!strncasecmp(domain, c, len) || !strncasecmp("all", c, len) || + !strncmp("*", c, len) || !strncasecmp(domainstar, c, len) || + !strncasecmp(domainlevel, c, len) || + !strncasecmp(alllevel, c, len) || !strncasecmp(starlevel, c, len))) print_it = incl; /* Go past any spaces towards the next item */ @@ -300,6 +308,11 @@ static gboolean glib_domain_wanted(const char *domain) len = 0; } while (*c != '\0' && print_it != -1); + g_free(domainlevel); + g_free(alllevel); + g_free(starlevel); + g_free(domainstar); + return (print_it == 1); } @@ -333,7 +346,7 @@ static void i_log_func(const char *log_domain, GLogLevelFlags log_level, const c domain = (log_domain ? log_domain : "default"); /* Only print the message if we decided to */ - if (!glib_domain_wanted(domain)) + if (!glib_domain_wanted(domain, reason)) return; if (windows == NULL) diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c index 19aba87d..1fed639e 100644 --- a/src/irc/proxy/listen.c +++ b/src/irc/proxy/listen.c @@ -711,16 +711,19 @@ static void add_listen(const char *ircnet, int port, const char *port_or_path) /* bind to specific host/ip? */ my_ip = NULL; if (*settings_get_str("irssiproxy_bind") != '\0') { - if (net_gethostbyname(settings_get_str("irssiproxy_bind"), - &ip4, &ip6) != 0) { + if (net_gethostbyname_first_ips(settings_get_str("irssiproxy_bind"), + G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, &ip4, + &ip6) != 0) { printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: can not resolve '%s' - aborting", settings_get_str("irssiproxy_bind")); return; } - my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 || - settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4; + my_ip = ip6.family == 0 ? &ip4 : + ip4.family == 0 || settings_get_bool("irssiproxy_prefer_ipv6") ? + &ip6 : + &ip4; } handle = net_listen(my_ip, &port); } diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c index d32b6009..240df971 100644 --- a/src/irc/proxy/proxy.c +++ b/src/irc/proxy/proxy.c @@ -74,6 +74,7 @@ static void irc_proxy_setup_changed(void) void irc_proxy_init(void) { + settings_add_bool("irssiproxy", "irssiproxy_prefer_ipv6", TRUE); settings_add_str("irssiproxy", "irssiproxy_ports", ""); settings_add_str("irssiproxy", "irssiproxy_password", ""); settings_add_str("irssiproxy", "irssiproxy_bind", "");