diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 838db914..debe6655 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -69,7 +69,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, g_hash_table_lookup(optlist, proto->chatnet); conn = server_create_conn(proto != NULL ? proto->id : -1, addr, atoi(portstr), chatnet, password, nick); - if (proto == NULL) + if (proto == NULL) proto = chat_protocol_find_id(conn->chat_type); if (proto->not_initialized) { @@ -80,6 +80,9 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, return NULL; } + if (strchr(addr, '/') != NULL) + conn->unix_socket = TRUE; + if (g_hash_table_lookup(optlist, "6") != NULL) conn->family = AF_INET6; else if (g_hash_table_lookup(optlist, "4") != NULL) diff --git a/src/core/chatnets.c b/src/core/chatnets.c index 502d11d7..f7b0ec62 100644 --- a/src/core/chatnets.c +++ b/src/core/chatnets.c @@ -118,8 +118,7 @@ static void sig_connected(SERVER_REC *server) g_return_if_fail(IS_SERVER(server)); - if (server->connrec->chatnet == NULL || - server->session_reconnect) + if (server->connrec->chatnet == NULL || server->session_reconnect) return; rec = chatnet_find(server->connrec->chatnet); diff --git a/src/core/net-nonblock.c b/src/core/net-nonblock.c index 7392f429..cf0b9596 100644 --- a/src/core/net-nonblock.c +++ b/src/core/net-nonblock.c @@ -186,7 +186,8 @@ static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe) { RESOLVED_IP_REC iprec; GIOChannel *handle; - IPADDR *ip; + IPADDR *ip; + int error; g_return_if_fail(rec != NULL); @@ -202,7 +203,7 @@ static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe) ip = iprec.ip4.family != 0 ? &iprec.ip4 : &iprec.ip6; handle = iprec.error == -1 ? NULL : - net_connect_ip(ip, rec->port, rec->my_ip); + net_connect_ip(ip, rec->port, rec->my_ip, &error); g_free_not_null(rec->my_ip); diff --git a/src/core/network.c b/src/core/network.c index e53b0189..5b4fa1ed 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -21,6 +21,8 @@ #include "module.h" #include "network.h" +#include + #ifndef INADDR_NONE # define INADDR_NONE INADDR_BROADCAST #endif @@ -133,7 +135,7 @@ int sin_get_port(union sockaddr_union *so) } /* Connect to socket */ -GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) +GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip, int *error) { IPADDR ip4, ip6, *ip; int family; @@ -141,8 +143,11 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) g_return_val_if_fail(addr != NULL, NULL); family = my_ip == NULL ? 0 : my_ip->family; - if (net_gethostbyname(addr, &ip4, &ip6) == -1) + if (net_gethostbyname(addr, &ip4, &ip6) == -1) { + if (error != NULL) + *error = errno; return NULL; + } if (my_ip == NULL) { /* prefer IPv4 addresses */ @@ -165,11 +170,11 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) } } - return net_connect_ip(ip, port, my_ip); + return net_connect_ip(ip, port, my_ip, error); } /* Connect to socket with ip address */ -GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) +GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip, int *error) { union sockaddr_union so; int handle, ret, opt = 1; @@ -184,8 +189,11 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) so.sin.sin_family = ip->family; handle = socket(ip->family, SOCK_STREAM, 0); - if (handle == -1) + if (handle == -1) { + if (error != NULL) + *error = errno; return NULL; + } /* set socket options */ #ifndef WIN32 @@ -217,6 +225,44 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) #endif { + if (error != NULL) + *error = errno; + close(handle); + return NULL; + } + + return g_io_channel_new(handle); +} + +/* Connect to named UNIX socket */ +GIOChannel *net_connect_unix(const char *path, int *error) +{ + struct sockaddr_un sa; + int handle, ret; + + /* create the socket */ + handle = socket(PF_UNIX, SOCK_STREAM, 0); + if (handle == -1) { + if (error != NULL) + *error = errno; + return NULL; + } + + /* set socket options */ +#ifndef WIN32 + fcntl(handle, F_SETFL, O_NONBLOCK); +#endif + + /* connect */ + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, path, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path)-1] = '\0'; + + ret = connect(handle, (struct sockaddr *) &sa, sizeof(sa)); + if (ret < 0 && errno != EINPROGRESS) { + if (error != NULL) + *error = errno; close(handle); return NULL; } diff --git a/src/core/network.h b/src/core/network.h index 4c25740f..c65c23ca 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -43,9 +43,11 @@ struct _IPADDR { int net_ip_compare(IPADDR *ip1, IPADDR *ip2); /* Connect to socket */ -GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip); +GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip, int *error); /* Connect to socket with ip address */ -GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip); +GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip, int *error); +/* Connect to named UNIX socket */ +GIOChannel *net_connect_unix(const char *path, int *error); /* Disconnect socket */ void net_disconnect(GIOChannel *handle); /* Try to let the other side close the connection, if it still isn't diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index a59880e4..cc5afa57 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -23,8 +23,12 @@ char *nick; char *username; char *realname; +GIOChannel *connect_handle; /* connect using this handle */ + /* when reconnecting, the old server status */ unsigned int reconnection:1; /* we're trying to reconnect */ unsigned int no_autojoin_channels:1; /* don't autojoin any channels */ +unsigned int unix_socket:1; /* Connect using named unix socket */ +unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */ char *channels; char *away_reason; diff --git a/src/core/servers.c b/src/core/servers.c index 1e5c0b84..e16d323c 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -161,16 +161,47 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle) server_connect_finished(server); } +static void server_real_connect(SERVER_REC *server, IPADDR *ip, + const char *unix_socket) +{ + GIOChannel *handle; + IPADDR *own_ip; + int port, error; + + g_return_if_fail(ip != NULL || unix_socket != NULL); + + signal_emit("server connecting", 2, server, ip); + + if (ip != NULL) { + own_ip = ip == NULL ? NULL : + (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : + server->connrec->own_ip4); + port = server->connrec->proxy != NULL ? + server->connrec->proxy_port : server->connrec->port; + handle = net_connect_ip(ip, port, own_ip, &error); + } else { + handle = net_connect_unix(unix_socket, &error); + } + + if (handle == NULL) { + /* failed */ + server->connection_lost = TRUE; + server_connect_failed(server, g_strerror(error)); + } else { + server->handle = net_sendbuffer_create(handle, 0); + server->connect_tag = + g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ, + (GInputFunction) + server_connect_callback_init, + server); + } +} + static void server_connect_callback_readpipe(SERVER_REC *server) { - SERVER_CONNECT_REC *conn; RESOLVED_IP_REC iprec; - GIOChannel *handle; - IPADDR *ip, *own_ip; + IPADDR *ip; const char *errormsg; - int port; - - g_return_if_fail(IS_SERVER(server)); g_source_remove(server->connect_tag); server->connect_tag = -1; @@ -204,33 +235,18 @@ static void server_connect_callback_readpipe(SERVER_REC *server) &iprec.ip6 : &iprec.ip4; } - conn = server->connrec; - port = conn->proxy != NULL ? conn->proxy_port : conn->port; - own_ip = ip == NULL ? NULL : - (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4); - - handle = NULL; if (ip != NULL) { - signal_emit("server connecting", 2, server, ip); - if (server->handle == NULL) - handle = net_connect_ip(ip, port, own_ip); - else - handle = net_sendbuffer_handle(server->handle); - } - - if (handle == NULL) { - /* failed */ - if (ip == NULL && (iprec.error == 0 || - net_hosterror_notfound(iprec.error))) { - /* IP wasn't found for the host, don't try to reconnect - back to this server */ + /* host lookup ok */ + server_real_connect(server, ip, NULL); + errormsg = NULL; + } else { + if (iprec.error == 0 || net_hosterror_notfound(iprec.error)) { + /* IP wasn't found for the host, don't try to + reconnect back to this server */ server->dns_error = TRUE; } - if (ip != NULL) { - /* connect() failed */ - errormsg = g_strerror(errno); - } else if (iprec.error == 0) { + 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" : @@ -240,18 +256,12 @@ static void server_connect_callback_readpipe(SERVER_REC *server) errormsg = iprec.errorstr != NULL ? iprec.errorstr : "Host lookup failed"; } + server->connection_lost = TRUE; server_connect_failed(server, errormsg); - g_free_not_null(iprec.errorstr); - return; } - if (server->handle == NULL) - server->handle = net_sendbuffer_create(handle, 0); - server->connect_tag = - g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ, - (GInputFunction) server_connect_callback_init, - server); + g_free(iprec.errorstr); } /* initializes server record but doesn't start connecting */ @@ -282,6 +292,7 @@ void server_connect_init(SERVER_REC *server) } server->tag = server_create_tag(server->connrec); + server->connect_tag = -1; } /* starts connecting to server */ @@ -291,34 +302,56 @@ int server_start_connect(SERVER_REC *server) int fd[2]; g_return_val_if_fail(server != NULL, FALSE); - if (server->connrec->port <= 0) return FALSE; + if (!server->connrec->unix_socket && server->connrec->port <= 0) + return FALSE; server_connect_init(server); - - if (pipe(fd) != 0) { - g_warning("server_connect(): pipe() failed."); - g_free(server->tag); - g_free(server->nick); - return FALSE; - } - - server->connect_pipe[0] = g_io_channel_unix_new(fd[0]); - server->connect_pipe[1] = g_io_channel_unix_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]); - server->connect_tag = - g_input_add(server->connect_pipe[0], G_INPUT_READ, - (GInputFunction) server_connect_callback_readpipe, - server); server->rawlog = rawlog_create(); - lookup_servers = g_slist_append(lookup_servers, server); + if (server->connrec->session_reconnect) { + /* /UPGRADE connection - the session_connect is meant + for us only once, move it into server->session_connect */ + server->connrec->session_reconnect = FALSE; + server->session_reconnect = TRUE; + } - signal_emit("server looking", 1, server); + if (server->connrec->connect_handle != NULL) { + /* already connected */ + GIOChannel *handle = server->connrec->connect_handle; + + server->connrec->connect_handle = NULL; + server->handle = net_sendbuffer_create(handle, 0); + server_connect_finished(server); + } else if (server->connrec->unix_socket) { + /* connect with unix socket */ + server_real_connect(server, NULL, server->connrec->address); + } else { + /* 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] = g_io_channel_unix_new(fd[0]); + server->connect_pipe[1] = g_io_channel_unix_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]); + server->connect_tag = + g_input_add(server->connect_pipe[0], G_INPUT_READ, + (GInputFunction) + server_connect_callback_readpipe, + server); + + lookup_servers = g_slist_append(lookup_servers, server); + + signal_emit("server looking", 1, server); + } return TRUE; } @@ -486,6 +519,9 @@ void server_connect_unref(SERVER_CONNECT_REC *conn) CHAT_PROTOCOL(conn)->destroy_server_connect(conn); + if (conn->connect_handle != NULL) + net_disconnect(conn->connect_handle); + g_free_not_null(conn->proxy); g_free_not_null(conn->proxy_string); g_free_not_null(conn->proxy_string_after); diff --git a/src/core/session.c b/src/core/session.c index 4e7a832c..94bedfb6 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -276,11 +276,10 @@ static void session_restore_server(CONFIG_NODE *node) chatnet, password, nick); if (conn != NULL) { conn->reconnection = TRUE; + conn->connect_handle = g_io_channel_unix_new(handle); + conn->session_reconnect = TRUE; server = proto->server_connect(conn); - server->handle = net_sendbuffer_create(g_io_channel_unix_new(handle), 0); - server->session_reconnect = TRUE; - signal_emit("session restore server", 2, server, node); } } diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index d8f9d137..db068f3b 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -238,9 +238,12 @@ static void sig_server_connecting(SERVER_REC *server, IPADDR *ip) char ipaddr[MAX_IP_LEN]; g_return_if_fail(server != NULL); - g_return_if_fail(ip != NULL); - net_ip2host(ip, ipaddr); + if (ip == NULL) + ipaddr[0] = '\0'; + else + net_ip2host(ip, ipaddr); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTING, server->connrec->address, ipaddr, server->connrec->port); } diff --git a/src/fe-common/core/fe-windows.c b/src/fe-common/core/fe-windows.c index 4ac94c5f..bfedcaad 100644 --- a/src/fe-common/core/fe-windows.c +++ b/src/fe-common/core/fe-windows.c @@ -610,6 +610,7 @@ void windows_init(void) read_settings(); signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_add("server connected", (SIGNAL_FUNC) sig_server_looking); signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); signal_add("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); signal_add("setup changed", (SIGNAL_FUNC) read_settings); @@ -621,6 +622,7 @@ void windows_deinit(void) if (daycheck == 1) signal_remove("print text", (SIGNAL_FUNC) sig_print_text); signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_looking); signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); diff --git a/src/irc/core/irc-channels.c b/src/irc/core/irc-channels.c index 5adf5fb1..b4ec55a2 100644 --- a/src/irc/core/irc-channels.c +++ b/src/irc/core/irc-channels.c @@ -160,7 +160,7 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server, return NULL; } -static void sig_server_looking(SERVER_REC *server) +static void sig_server_connected(SERVER_REC *server) { if (!IS_IRC_SERVER(server)) return; @@ -198,7 +198,7 @@ static void sig_channel_destroyed(IRC_CHANNEL_REC *channel) void irc_channels_init(void) { - signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_add_first("server connected", (SIGNAL_FUNC) sig_server_connected); signal_add("channel created", (SIGNAL_FUNC) sig_channel_created); signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); @@ -216,7 +216,7 @@ void irc_channels_init(void) void irc_channels_deinit(void) { - signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created); signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 0a198e48..e294f9fc 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -87,16 +87,6 @@ static void send_message(SERVER_REC *server, const char *target, g_free(str); } -static void sig_server_looking(IRC_SERVER_REC *server) -{ - if (!IS_IRC_SERVER(server)) - return; - - server->isnickflag = isnickflag_func; - server->ischannel = ischannel_func; - server->send_message = send_message; -} - static void server_init(IRC_SERVER_REC *server) { IRC_SERVER_CONNECT_REC *conn; @@ -256,7 +246,12 @@ static void sig_connected(IRC_SERVER_REC *server) if (!IS_IRC_SERVER(server)) return; - server->splits = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + server->isnickflag = isnickflag_func; + server->ischannel = ischannel_func; + server->send_message = send_message; + + server->splits = g_hash_table_new((GHashFunc) g_istr_hash, + (GCompareFunc) g_istr_equal); if (!server->session_reconnect) server_init(server); @@ -575,7 +570,6 @@ void irc_servers_init(void) cmd_tag = g_timeout_add(500, (GSourceFunc) servers_cmd_timeout, NULL); - signal_add_first("server looking", (SIGNAL_FUNC) sig_server_looking); signal_add_first("server connected", (SIGNAL_FUNC) sig_connected); signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit); @@ -598,7 +592,6 @@ void irc_servers_deinit(void) { g_source_remove(cmd_tag); - signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); signal_remove("server connected", (SIGNAL_FUNC) sig_connected); signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit); diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c index 08d45ea9..b19df58b 100644 --- a/src/irc/dcc/dcc.c +++ b/src/irc/dcc/dcc.c @@ -247,7 +247,7 @@ GIOChannel *dcc_connect_ip(IPADDR *ip, int port) own_ip = &temp_ip; } - return net_connect_ip(ip, port, own_ip); + return net_connect_ip(ip, port, own_ip, NULL); } /* Server connected - update server for DCC records that have