use GIO resolver

change: rename resolve_prefer_ipv6 -> irssiproxy_prefer_ipv6
This commit is contained in:
Ailin Nemui 2025-07-27 20:06:10 +02:00
commit 51f2a4f7fb
18 changed files with 313 additions and 339 deletions

View file

@ -15,7 +15,6 @@
%9Examples:%9 %9Examples:%9
/TOGGLE resolve_prefer_ipv6
/TOGGLE channels_rejoin_unavailable ON /TOGGLE channels_rejoin_unavailable ON
%9See also:%9 SET %9See also:%9 SET

View file

@ -373,9 +373,6 @@
After connected to server, Irssi can automatically change your user After connected to server, Irssi can automatically change your user
mode. You can set it with /SET usermode <mode>, default is +i. mode. You can set it with /SET usermode <mode>, default is +i.
/SET resolve_prefer_ipv6 - If ON, prefer IPv6 for hosts that
have both v4 and v6 addresses.
5.5 Automatic reconnecting 5.5 Automatic reconnecting
If you get disconnected from server, Irssi will try to reconnect If you get disconnected from server, Irssi will try to reconnect

View file

@ -8,6 +8,7 @@ project('irssi', 'c',
glib_internal_version = 'glib-2.74.3' # keep this in sync with subprojects/glib.wrap glib_internal_version = 'glib-2.74.3' # keep this in sync with subprojects/glib.wrap
glib_pcre2_internal_version = 'pcre2-10.40' glib_pcre2_internal_version = 'pcre2-10.40'
glib_libffi_internal_version = 'libffi'
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
rootinc = include_directories('.') rootinc = include_directories('.')
dep = [] dep = []
@ -217,11 +218,6 @@ if not glib_dep.found()
prov_lib = cc.find_library('iconv', dirs : '/usr/local/lib') prov_lib = cc.find_library('iconv', dirs : '/usr/local/lib')
glib_internal_usr_local = true glib_internal_usr_local = true
endif 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 glib_internal_dependencies += prov_lib
endif endif
@ -254,7 +250,7 @@ if not glib_dep.found()
glib_internal_configure_t = custom_target('glib-internal-configure', glib_internal_configure_t = custom_target('glib-internal-configure',
command : [ meson_cmd, 'setup', '--prefix=/irssi-glib-internal', command : [ meson_cmd, 'setup', '--prefix=/irssi-glib-internal',
'--buildtype=' + get_option('buildtype'), '--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, glib_internal_configure_args,
(meson.current_build_dir() / 'build-subprojects' / 'glib'), (meson.current_build_dir() / 'build-subprojects' / 'glib'),
(meson.current_source_dir() / 'subprojects' / glib_internal_version) ], (meson.current_source_dir() / 'subprojects' / glib_internal_version) ],
@ -263,9 +259,13 @@ if not glib_dep.found()
depends : glib_internal_download_t,) depends : glib_internal_download_t,)
glib_internal_build_t = custom_target('glib-internal-build', glib_internal_build_t = custom_target('glib-internal-build',
command : [ ninja, '-C', meson.current_build_dir() / 'build-subprojects' / 'glib', 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', 'subprojects' / glib_pcre2_internal_version / 'libpcre2-8.a',
'glib' / 'libglib-2.0.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, console : true,
output : ['glib-internal-build'], output : ['glib-internal-build'],
depends : glib_internal_configure_t,) 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' ], 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 else
gmodule_dep = dependency('gmodule-2.0', static : want_static_dependency, include_type : 'system') 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 endif
dep += glib_dep dep += glib_dep
dep += gmodule_dep dep += gmodule_dep
dep += gobject_dep
dep += gio_dep
if glib_internal and want_static_dependency and want_fuzzer if glib_internal and want_static_dependency and want_fuzzer
openssl_proj = subproject('openssl', default_options : ['default_library=static', 'asm=disabled']) openssl_proj = subproject('openssl', default_options : ['default_library=static', 'asm=disabled'])

View file

@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ #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_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697 #define DEFAULT_SERVER_ADD_TLS_PORT 6697
@ -38,6 +38,7 @@
#include <glib.h> #include <glib.h>
#include <gmodule.h> #include <gmodule.h>
#include <gio/gio.h>
typedef guint64 uoff_t; typedef guint64 uoff_t;
#define PRIuUOFF_T G_GUINT64_FORMAT #define PRIuUOFF_T G_GUINT64_FORMAT

View file

@ -101,10 +101,11 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
host = g_hash_table_lookup(optlist, "host"); host = g_hash_table_lookup(optlist, "host");
if (host != NULL && *host != '\0') { if (host != NULL && *host != '\0') {
IPADDR ip4, ip6; IPADDR ip4 = { 0 };
IPADDR ip6 = { 0 };
if (net_gethostbyname(host, &ip4, &ip6) == 0) if (net_gethostbyname_first_ips(host, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, &ip4,
server_connect_own_ip_save(conn, &ip4, &ip6); &ip6) == 0)
server_connect_own_ip_save(conn, &ip4, &ip6);
} }
cmd_params_free(free_arg); cmd_params_free(free_arg);

View file

@ -22,86 +22,54 @@
#include <signal.h> #include <signal.h>
#include <irssi/src/core/pidwait.h> #include <irssi/src/core/network.h>
#include <irssi/src/core/net-nonblock.h> #include <irssi/src/core/net-nonblock.h>
/* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is typedef struct {
written to pipe when found PID of the resolver child is returned */ NetGethostbynameContinuationFunc cont;
int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int reverse_lookup) void *cont_data;
{ } NET_GETHOSTBYNAME_CALLBACK_DATA;
RESOLVED_IP_REC rec;
const char *errorstr;
int pid;
(void) reverse_lookup; /* Kept for API backward compatibility */ static void net_gethostbyname_callback(GResolver *resolver, GAsyncResult *result,
NET_GETHOSTBYNAME_CALLBACK_DATA *data)
{
/* GList<GInetAddress> */
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); g_return_val_if_fail(addr != NULL, FALSE);
pid = fork(); resolver = g_resolver_get_default();
if (pid > 0) { cancellable = g_cancellable_new();
/* parent */ data = g_new0(NET_GETHOSTBYNAME_CALLBACK_DATA, 1);
pidwait_add(pid); data->cont = cont;
return pid; data->cont_data = cont_data;
} g_resolver_lookup_by_name_with_flags_async(resolver, addr, flags, cancellable,
(GAsyncReadyCallback) net_gethostbyname_callback,
if (pid != 0) { data);
/* failed! */ return cancellable;
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);
} }

View file

@ -3,20 +3,10 @@
#include <irssi/src/core/network.h> #include <irssi/src/core/network.h>
typedef struct { typedef void (*NetGethostbynameContinuationFunc)(RESOLVED_IP_REC *, void *);
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;
/* nonblocking gethostbyname(), PID of the resolver child is returned. */ /* nonblocking gethostbyname(), Cancellable of the resolver child is returned. */
int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe, int reverse_lookup); GCancellable *net_gethostbyname_nonblock(const char *addr, GResolverNameLookupFlags flags,
/* get the resolved IP address. returns -1 if some error occurred with read() */ NetGethostbynameContinuationFunc cont, void *cont_data);
int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec);
/* Kill the resolver child */
void net_disconnect_nonblock(int pid);
#endif #endif

View file

@ -81,27 +81,6 @@ int i_io_channel_read_block(GIOChannel *channel, void *data, int len)
return received < len ? -1 : 0; 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) static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{ {
if (ip == NULL) { if (ip == NULL) {
@ -392,95 +371,87 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
return 0; return 0;
} }
/* Get IP addresses for host, both IPv4 and IPv6 if possible. void resolved_ip_ref(RESOLVED_IP_REC *iprec)
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)
{ {
union sockaddr_union *so; iprec->refcount++;
struct addrinfo hints, *ai, *ailist; }
int ret, count_v4, count_v6, use_v4, use_v6;
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<GInetAddress> */
GList *ailist;
GError *error;
GResolver *resolver;
RESOLVED_IP_REC *iprec;
#ifdef HAVE_CAPSICUM #ifdef HAVE_CAPSICUM
if (capsicum_enabled()) if (capsicum_enabled())
return (capsicum_net_gethostbyname(addr, ip4, ip6)); return (capsicum_net_gethostbyname(addr, flags));
#endif #endif
g_return_val_if_fail(addr != NULL, -1); g_return_val_if_fail(addr != NULL, NULL);
memset(ip4, 0, sizeof(IPADDR)); error = NULL;
memset(ip6, 0, sizeof(IPADDR)); resolver = g_resolver_get_default();
ailist = g_resolver_lookup_by_name_with_flags(resolver, addr, flags, NULL, &error);
memset(&hints, 0, sizeof(struct addrinfo)); iprec = g_new0(RESOLVED_IP_REC, 1);
hints.ai_socktype = SOCK_STREAM; if (error != NULL) {
hints.ai_flags = AI_ADDRCONFIG; iprec->error = error;
} else {
/* save error to host_error for later use */ iprec->ailist = ailist;
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++;
} }
g_object_unref(resolver);
resolved_ip_ref(iprec);
if (count_v4 == 0 && count_v6 == 0) return iprec;
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;
} }
/* Get name for host, *name should be g_free()'d unless it's NULL. int net_gethostbyname_first_ips(const char *addr, GResolverNameLookupFlags flags, IPADDR *ip4,
Return values are the same as with net_gethostbyname() */ IPADDR *ip6)
int net_gethostbyaddr(IPADDR *ip, char **name)
{ {
union sockaddr_union so; RESOLVED_IP_REC *iprec;
int host_error;
char hostname[NI_MAXHOST];
g_return_val_if_fail(ip != NULL, -1); iprec = net_gethostbyname(addr, flags);
g_return_val_if_fail(name != NULL, -1); 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)); addr = curr->data;
sin_set_ip(&so, ip); 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 */ resolved_ip_unref(iprec);
host_error = getnameinfo((struct sockaddr *)&so, sizeof(so), return 0;
hostname, sizeof(hostname), } else {
NULL, 0, resolved_ip_unref(iprec);
NI_NAMEREQD); return -1;
if (host_error != 0) }
return host_error;
*name = g_strdup(hostname);
return 0;
} }
int net_ip2host(IPADDR *ip, char *host) int net_ip2host(IPADDR *ip, char *host)
@ -519,29 +490,6 @@ int net_geterror(GIOChannel *handle)
return data; 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 */ /* Get name of TCP service */
char *net_getservbyport(int port) char *net_getservbyport(int port)
{ {

View file

@ -6,6 +6,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <irssi/src/common.h>
#ifndef AF_INET6 #ifndef AF_INET6
# ifdef PF_INET6 # ifdef PF_INET6
@ -20,6 +21,13 @@ struct _IPADDR {
struct in6_addr ip; struct in6_addr ip;
}; };
typedef struct {
int refcount;
/* GList<GInetAddress> */
GList *ailist; /* needs to be freed */
GError *error; /* needs to be freed */
} RESOLVED_IP_REC;
/* maxmimum string length of IP address */ /* maxmimum string length of IP address */
#define MAX_IP_LEN INET6_ADDRSTRLEN #define MAX_IP_LEN INET6_ADDRSTRLEN
@ -29,9 +37,7 @@ extern IPADDR ip4_any;
GIOChannel *i_io_channel_new(int handle); GIOChannel *i_io_channel_new(int handle);
/* Returns 1 if IPADDRs are the same. */ /* OTR */
/* Deprecated since it is unused. It will be deleted in a later release. */
int net_ip_compare(IPADDR *ip1, IPADDR *ip2) G_GNUC_DEPRECATED;
int i_io_channel_write_block(GIOChannel *channel, void *data, int len); int i_io_channel_write_block(GIOChannel *channel, void *data, int len);
int i_io_channel_read_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 */ /* Transmit data, return number of bytes sent, -1 = error */
int net_transmit(GIOChannel *handle, const char *data, int len); int net_transmit(GIOChannel *handle, const char *data, int len);
/* Get IP addresses for host, both IPv4 and IPv6 if possible. /* Get the first IP address for host, both IPv4 and IPv6 if possible. */
If ip->family is 0, the address wasn't found. int net_gethostbyname_first_ips(const char *addr, GResolverNameLookupFlags flags, IPADDR *ip4,
Returns 0 = ok, others = error code for net_gethosterror() */ IPADDR *ip6);
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 socket address/port */ /* Get socket address/port */
int net_getsockname(GIOChannel *handle, IPADDR *addr, int *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_ipv4_address(const char *host);
int is_ipv6_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 #endif

View file

@ -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 use_tls:1; /* this connection uses TLS */
unsigned int tls_verify:1; unsigned int tls_verify:1;
unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */ 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 *channels;
char *away_reason; char *away_reason;

View file

@ -21,10 +21,8 @@ unsigned int no_reconnect:1; /* Don't reconnect to server */
NET_SENDBUF_REC *handle; NET_SENDBUF_REC *handle;
int readtag; /* input tag */ int readtag; /* input tag */
/* for net_gethostbyname_return() */ GCancellable *connect_cancellable;
GIOChannel *connect_pipe[2];
int connect_tag; int connect_tag;
int connect_pid;
RAWLOG_REC *rawlog; RAWLOG_REC *rawlog;
GHashTable *module_data; GHashTable *module_data;

View file

@ -120,6 +120,10 @@ static int server_reconnect_timeout(void)
if (server->connect_tag != -1) { if (server->connect_tag != -1) {
g_source_remove(server->connect_tag); g_source_remove(server->connect_tag);
server->connect_tag = -1; 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->connection_lost = TRUE;
server_connect_failed(server, "Timeout"); server_connect_failed(server, "Timeout");
@ -168,7 +172,8 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
server_connect_ref(dest); server_connect_ref(dest);
dest->type = module_get_uniq_id("SERVER CONNECT", 0); dest->type = module_get_uniq_id("SERVER CONNECT", 0);
dest->reconnection = src->reconnection; 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 = g_strdup(src->proxy);
dest->proxy_port = src->proxy_port; dest->proxy_port = src->proxy_port;
dest->proxy_string = g_strdup(src->proxy_string); 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); dest->own_ip6 = g_new(IPADDR, 1);
memcpy(dest->own_ip6, src->own_ip6, sizeof(IPADDR)); 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->channels = g_strdup(src->channels);
dest->away_reason = g_strdup(src->away_reason); dest->away_reason = g_strdup(src->away_reason);

View file

@ -58,7 +58,8 @@ static void save_ips(IPADDR *ip4, IPADDR *ip6,
static void get_source_host_ip(void) static void get_source_host_ip(void)
{ {
const char *hostname; const char *hostname;
IPADDR ip4, ip6; IPADDR ip4 = { 0 };
IPADDR ip6 = { 0 };
if (source_host_ok) if (source_host_ok)
return; return;
@ -66,7 +67,8 @@ static void get_source_host_ip(void)
/* FIXME: This will block! */ /* FIXME: This will block! */
hostname = settings_get_str("hostname"); hostname = settings_get_str("hostname");
source_host_ok = *hostname != '\0' && 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) if (source_host_ok)
save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6); 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, static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host,
IPADDR **own_ip4, IPADDR **own_ip6) IPADDR **own_ip4, IPADDR **own_ip6)
{ {
IPADDR ip4, ip6; IPADDR ip4 = { 0 };
IPADDR ip6 = { 0 };
if (*own_ip4 == NULL && *own_ip6 == NULL) { if (*own_ip4 == NULL && *own_ip6 == NULL) {
/* resolve the IP */ /* resolve the IP */
if (net_gethostbyname(own_host, &ip4, &ip6) == 0) if (net_gethostbyname_first_ips(own_host, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
save_ips(&ip4, &ip6, own_ip4, own_ip6); &ip4, &ip6) == 0)
save_ips(&ip4, &ip6, own_ip4, own_ip6);
} }
server_connect_own_ip_save(conn, *own_ip4, *own_ip6); server_connect_own_ip_save(conn, *own_ip4, *own_ip6);

View file

@ -51,20 +51,16 @@ void server_connect_failed(SERVER_REC *server, const char *msg)
g_source_remove(server->connect_tag); g_source_remove(server->connect_tag);
server->connect_tag = -1; 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) { if (server->handle != NULL) {
net_sendbuffer_destroy(server->handle, TRUE); net_sendbuffer_destroy(server->handle, TRUE);
server->handle = NULL; 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); server_unref(server);
} }
@ -156,7 +152,7 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
error = net_geterror(handle); error = net_geterror(handle);
if (error != 0) { if (error != 0) {
server->connection_lost = TRUE; 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)); server_connect_failed(server, g_strerror(error));
return; return;
} }
@ -177,7 +173,7 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han
error = irssi_ssl_handshake(handle); error = irssi_ssl_handshake(handle);
if (error == -1) { if (error == -1) {
server->connection_lost = TRUE; 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); server_connect_failed(server, NULL);
return; return;
} }
@ -259,12 +255,12 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
server->connection_lost = TRUE; server->connection_lost = TRUE;
if (ip != NULL) { 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); server_connect_failed(server, errmsg2 ? errmsg2 : errmsg);
g_free(errmsg2); g_free(errmsg2);
} else { } else {
server->connrec->last_failed_family = 0; server->connrec->last_failed = 0;
if (!server->connrec->use_tls) if (!server->connrec->use_tls)
server->handle = net_sendbuffer_create(handle, 0); server->handle = net_sendbuffer_create(handle, 0);
if (server->connrec->use_tls) 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; const char *errormsg;
RESOLVED_IP_REC *iprec = server->connrec->resolved_host;
g_source_remove(server->connect_tag); if (iprec->error != NULL) {
server->connect_tag = -1; /* error */
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 */
ip = NULL; 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 { } else {
/* pick the one that was found. if both were found: GList *curr;
1. disprefer the last one that failed int i;
2. prefer ipv4 over ipv6 unless resolve_prefer_ipv6 is set
*/ curr = iprec->ailist;
if (iprec.ip4.family == 0 || i = 0;
(iprec.ip6.family != 0 && while (i < server->connrec->last_failed) {
(server->connrec->last_failed_family == AF_INET || if (curr != NULL) {
(settings_get_bool("resolve_prefer_ipv6") && curr = curr->next;
server->connrec->last_failed_family != AF_INET6)))) { i++;
ip = &iprec.ip6; }
/* 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 { } 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); server_real_connect(server, ip, NULL);
errormsg = NULL; errormsg = NULL;
} else { } 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 /* IP wasn't found for the host, don't try to
reconnect back to this server */ reconnect back to this server */
server->dns_error = TRUE; server->dns_error = TRUE;
} }
if (iprec.error == 0) { errormsg = iprec->error->message;
/* forced IPv4 or IPv6 address but it wasn't found */ if (errormsg == NULL)
errormsg = server->connrec->family == AF_INET ? errormsg = "Host lookup failed";
"IPv4 address not found for host" :
"IPv6 address not found for host";
} else {
/* gethostbyname() failed */
errormsg = iprec.errorstr != NULL ? iprec.errorstr :
"Host lookup failed";
}
server->connection_lost = TRUE; server->connection_lost = TRUE;
/* clear the error in resolved_host */
server->connrec->resolved_host = NULL;
server_connect_failed(server, errormsg); 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) SERVER_REC *server_connect(SERVER_CONNECT_REC *conn)
@ -399,9 +429,6 @@ void server_connect_init(SERVER_REC *server)
/* starts connecting to server */ /* starts connecting to server */
int server_start_connect(SERVER_REC *server) int server_start_connect(SERVER_REC *server)
{ {
const char *connect_address;
int fd[2];
g_return_val_if_fail(server != NULL, FALSE); g_return_val_if_fail(server != NULL, FALSE);
if (!server->connrec->unix_socket && server->connrec->port <= 0) if (!server->connrec->unix_socket && server->connrec->port <= 0)
return FALSE; return FALSE;
@ -419,30 +446,17 @@ int server_start_connect(SERVER_REC *server)
/* connect with unix socket */ /* connect with unix socket */
server_real_connect(server, NULL, server->connrec->address); server_real_connect(server, NULL, server->connrec->address);
} else { } else {
int already_resolved;
/* resolve host name */ /* resolve host name */
if (pipe(fd) != 0) { already_resolved = server_start_connect_resolve(server);
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);
server->connect_time = time(NULL); server->connect_time = time(NULL);
lookup_servers = g_slist_append(lookup_servers, server); lookup_servers = g_slist_append(lookup_servers, server);
signal_emit("server looking", 1, server); signal_emit("server looking", 1, server);
if (already_resolved) {
server_connect_use_resolved(server);
}
} }
return TRUE; return TRUE;
} }
@ -481,8 +495,9 @@ void server_disconnect(SERVER_REC *server)
if (server->connect_tag != -1) { if (server->connect_tag != -1) {
/* still connecting to server.. */ /* still connecting to server.. */
if (server->connect_pid != -1) server_connect_failed(server, NULL);
net_disconnect_nonblock(server->connect_pid); return;
} else if (server->connect_cancellable != NULL) {
server_connect_failed(server, NULL); server_connect_failed(server, NULL);
return; 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_ip4);
g_free_not_null(conn->own_ip6); 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->password);
g_free_not_null(conn->nick); g_free_not_null(conn->nick);
g_free_not_null(conn->username); g_free_not_null(conn->username);
@ -769,7 +788,6 @@ static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
void servers_init(void) void servers_init(void)
{ {
settings_add_bool("server", "resolve_prefer_ipv6", TRUE);
lookup_servers = servers = NULL; lookup_servers = servers = NULL;
signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit); signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);

View file

@ -2,6 +2,7 @@
#define IRSSI_CORE_SERVERS_H #define IRSSI_CORE_SERVERS_H
#include <irssi/src/core/modules.h> #include <irssi/src/core/modules.h>
#include <irssi/src/core/network.h>
/* Returns SERVER_REC if it's server, NULL if it isn't. */ /* Returns SERVER_REC if it's server, NULL if it isn't. */
#define SERVER(server) \ #define SERVER(server) \

View file

@ -166,7 +166,7 @@ void fe_common_core_init(void)
settings_add_bool("lookandfeel", "use_msgs_window", FALSE); settings_add_bool("lookandfeel", "use_msgs_window", FALSE);
g_get_charset(&str); g_get_charset(&str);
settings_add_str("lookandfeel", "term_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(); themes_init();
theme_register(fecommon_core_formats); theme_register(fecommon_core_formats);
@ -258,13 +258,19 @@ void fe_common_core_deinit(void)
g_log_set_default_handler(logger_old, NULL); 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; const char *domains;
char *c, *cur; char *c, *cur;
int len = 0; int len = 0;
int print_it = 0; /* -1 for exclude, 0 for undecided, 1 for include */ int print_it = 0; /* -1 for exclude, 0 for undecided, 1 for include */
int incl; 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 /* Go through each item in glib_log_domains setting to determine whether
* or not we want to print message from this domain */ * 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 we got a valid item, process it */
if (len > 0 && (!strncmp(domain, c, len) || !strncasecmp("all", c, len) || if (len > 0 && (!strncasecmp(domain, c, len) || !strncasecmp("all", c, len) ||
!strncmp("*", c, len))) !strncmp("*", c, len) || !strncasecmp(domainstar, c, len) ||
!strncasecmp(domainlevel, c, len) ||
!strncasecmp(alllevel, c, len) || !strncasecmp(starlevel, c, len)))
print_it = incl; print_it = incl;
/* Go past any spaces towards the next item */ /* Go past any spaces towards the next item */
@ -300,6 +308,11 @@ static gboolean glib_domain_wanted(const char *domain)
len = 0; len = 0;
} while (*c != '\0' && print_it != -1); } while (*c != '\0' && print_it != -1);
g_free(domainlevel);
g_free(alllevel);
g_free(starlevel);
g_free(domainstar);
return (print_it == 1); 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"); domain = (log_domain ? log_domain : "default");
/* Only print the message if we decided to */ /* Only print the message if we decided to */
if (!glib_domain_wanted(domain)) if (!glib_domain_wanted(domain, reason))
return; return;
if (windows == NULL) if (windows == NULL)

View file

@ -711,16 +711,19 @@ static void add_listen(const char *ircnet, int port, const char *port_or_path)
/* bind to specific host/ip? */ /* bind to specific host/ip? */
my_ip = NULL; my_ip = NULL;
if (*settings_get_str("irssiproxy_bind") != '\0') { if (*settings_get_str("irssiproxy_bind") != '\0') {
if (net_gethostbyname(settings_get_str("irssiproxy_bind"), if (net_gethostbyname_first_ips(settings_get_str("irssiproxy_bind"),
&ip4, &ip6) != 0) { G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT, &ip4,
&ip6) != 0) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: can not resolve '%s' - aborting", "Proxy: can not resolve '%s' - aborting",
settings_get_str("irssiproxy_bind")); settings_get_str("irssiproxy_bind"));
return; return;
} }
my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 || my_ip = ip6.family == 0 ? &ip4 :
settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4; ip4.family == 0 || settings_get_bool("irssiproxy_prefer_ipv6") ?
&ip6 :
&ip4;
} }
handle = net_listen(my_ip, &port); handle = net_listen(my_ip, &port);
} }

View file

@ -74,6 +74,7 @@ static void irc_proxy_setup_changed(void)
void irc_proxy_init(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_ports", "");
settings_add_str("irssiproxy", "irssiproxy_password", ""); settings_add_str("irssiproxy", "irssiproxy_password", "");
settings_add_str("irssiproxy", "irssiproxy_bind", ""); settings_add_str("irssiproxy", "irssiproxy_bind", "");