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
/TOGGLE resolve_prefer_ipv6
/TOGGLE channels_rejoin_unavailable ON
%9See also:%9 SET

View file

@ -373,9 +373,6 @@
After connected to server, Irssi can automatically change your user
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
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_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'])

View file

@ -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 <glib.h>
#include <gmodule.h>
#include <gio/gio.h>
typedef guint64 uoff_t;
#define PRIuUOFF_T G_GUINT64_FORMAT

View file

@ -101,9 +101,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 ip4, ip6;
if (net_gethostbyname(host, &ip4, &ip6) == 0)
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);
}

View file

@ -22,86 +22,54 @@
#include <signal.h>
#include <irssi/src/core/pidwait.h>
#include <irssi/src/core/network.h>
#include <irssi/src/core/net-nonblock.h>
/* 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<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);
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;
}

View file

@ -3,20 +3,10 @@
#include <irssi/src/core/network.h>
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

View file

@ -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<GInetAddress> */
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));
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);
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++;
return 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;
}
/* 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);
/* 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);
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));
}
}
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)
{

View file

@ -6,6 +6,7 @@
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <irssi/src/common.h>
#ifndef AF_INET6
# ifdef PF_INET6
@ -20,6 +21,13 @@ struct _IPADDR {
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 */
#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

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

View file

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

View file

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

View file

@ -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,11 +81,13 @@ 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)
if (net_gethostbyname_first_ips(own_host, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
&ip4, &ip6) == 0)
save_ips(&ip4, &ip6, 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);
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;
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) {
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);
resolved_ip_unref(iprec);
}
}
g_free(iprec.errorstr);
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);

View file

@ -2,6 +2,7 @@
#define IRSSI_CORE_SERVERS_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. */
#define SERVER(server) \

View file

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

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? */
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);
}

View file

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