From f2795abcc59f4c2a7f509071ec4398759b47e964 Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Tue, 6 Apr 2021 19:31:10 +0200 Subject: [PATCH] actually use the tls settings on upgrade, and disconnect gracefully --- src/core/servers-setup.c | 9 ++++++- src/core/session.c | 32 +++++++++++++++++-------- src/fe-common/core/fe-server.c | 8 ++----- src/irc/core/channels-query.c | 2 ++ src/irc/core/irc-servers-reconnect.c | 3 ++- src/irc/core/irc-servers.c | 35 ++++++++++++++++++++++------ src/irc/core/irc-servers.h | 8 ++++++- src/irc/core/irc-session.c | 29 ++++++++++++++++++++++- src/irc/core/netsplit.c | 3 +++ src/irc/notifylist/notifylist.c | 3 +++ src/perl/irc/Server.xs | 13 +++++++++-- 11 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index ea15e2a8..c6612e84 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -338,7 +338,14 @@ SERVER_CONNECT_REC *server_create_conn_opt(int chat_type, const char *dest, int SERVER_CONNECT_REC *server_create_conn(int chat_type, const char *dest, int port, const char *chatnet, const char *password, const char *nick) { - return server_create_conn_opt(chat_type, dest, port, chatnet, password, nick, NULL); + SERVER_CONNECT_REC *ret; + GHashTable *opt; + + opt = g_hash_table_new(NULL, NULL); + ret = server_create_conn_opt(chat_type, dest, port, chatnet, password, nick, opt); + g_hash_table_destroy(opt); + + return ret; } /* Find matching server from setup. Try to find record with a same port, diff --git a/src/core/session.c b/src/core/session.c index 6f3908e5..5fe481fb 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -254,23 +254,35 @@ static void session_restore_server(CONFIG_NODE *node) proto = chat_protocol_find(chat_type); if (proto == NULL || proto->not_initialized) { - if (handle < 0) close(handle); + if (handle >= 0) + close(handle); return; } conn = server_create_conn(proto->id, address, port, chatnet, password, nick); - if (conn != NULL) { - conn->reconnection = TRUE; - conn->connect_handle = i_io_channel_new(handle); + if (conn == NULL) + return; - server = proto->server_init_connect(conn); - server->version = g_strdup(config_node_get_str(node, "version", NULL)); - server->session_reconnect = TRUE; - signal_emit("session restore server", 2, server, node); + conn->use_tls = config_node_get_bool(node, "use_tls", FALSE); + conn->tls_cert = g_strdup(config_node_get_str(node, "tls_cert", NULL)); + conn->tls_pkey = g_strdup(config_node_get_str(node, "tls_pkey", NULL)); + conn->tls_verify = config_node_get_bool(node, "tls_verify", TRUE); + conn->tls_cafile = g_strdup(config_node_get_str(node, "tls_cafile", NULL)); + conn->tls_capath = g_strdup(config_node_get_str(node, "tls_capath", NULL)); + conn->tls_ciphers = g_strdup(config_node_get_str(node, "tls_ciphers", NULL)); + conn->tls_pinned_cert = g_strdup(config_node_get_str(node, "tls_pinned_cert", NULL)); + conn->tls_pinned_pubkey = g_strdup(config_node_get_str(node, "tls_pinned_pubkey", NULL)); - proto->server_connect(server); - } + conn->reconnection = TRUE; + conn->connect_handle = i_io_channel_new(handle); + + server = proto->server_init_connect(conn); + server->version = g_strdup(config_node_get_str(node, "version", NULL)); + server->session_reconnect = TRUE; + signal_emit("session restore server", 2, server, node); + + proto->server_connect(server); } static void sig_session_save(CONFIG_REC *config) diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index 0bf9a104..5e51e511 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -165,14 +165,10 @@ static void cmd_server_add_modify(const char *data, gboolean add) else if (g_hash_table_lookup(optlist, "4")) rec->family = AF_INET; - if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl")) { + if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl")) rec->use_tls = TRUE; - } - else if (g_hash_table_lookup(optlist, "notls") || g_hash_table_lookup(optlist, "nossl")) { + else if (g_hash_table_lookup(optlist, "notls") || g_hash_table_lookup(optlist, "nossl")) rec->use_tls = FALSE; - /* tls_verify implies use_tls, disable it explicitly */ - rec->tls_verify = FALSE; - } value = g_hash_table_lookup(optlist, "tls_cert"); if (value == NULL) diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c index 97e982b7..6cf3dd2b 100644 --- a/src/irc/core/channels-query.c +++ b/src/irc/core/channels-query.c @@ -90,6 +90,8 @@ static void sig_disconnected(IRC_SERVER_REC *server) return; rec = server->chanqueries; + if (rec == NULL) + return; g_return_if_fail(rec != NULL); g_hash_table_destroy(rec->accountqueries); diff --git a/src/irc/core/irc-servers-reconnect.c b/src/irc/core/irc-servers-reconnect.c index f0e6f345..cc34c6d3 100644 --- a/src/irc/core/irc-servers-reconnect.c +++ b/src/irc/core/irc-servers-reconnect.c @@ -64,7 +64,8 @@ static void sig_server_reconnect_save_status(IRC_SERVER_CONNECT_REC *conn, return; g_free_not_null(conn->channels); - conn->channels = irc_server_get_channels(server); + conn->channels = + irc_server_get_channels(server, settings_get_choice("rejoin_channels_on_reconnect")); g_free_not_null(conn->usermode); conn->usermode = g_strdup(server->wanted_usermode); diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index bcad3f9e..00a6458b 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -469,6 +469,26 @@ void irc_server_connect(SERVER_REC *server) { g_return_if_fail(server != NULL); + if (server->connrec->connect_handle != NULL) { + IRC_SERVER_CONNECT_REC *conn; + int tls_disconnect; + + conn = ((IRC_SERVER_REC *) server)->connrec; + tls_disconnect = conn->use_tls || conn->starttls; + + if (tls_disconnect) { + /* we cannot use it, it is encrypted. force a reconnect */ + g_io_channel_unref(conn->connect_handle); + conn->connect_handle = NULL; + server->session_reconnect = FALSE; + server_connect_ref((SERVER_CONNECT_REC *) conn); + server_disconnect(server); + server_connect((SERVER_CONNECT_REC *) conn); + server_connect_unref((SERVER_CONNECT_REC *) conn); + return; + } + } + if (!server_start_connect(server)) { server_connect_unref(server->connrec); g_free(server); @@ -782,20 +802,17 @@ void irc_servers_start_cmd_timeout(void) /* Return a string of all channels (and keys, if any have them) in server, like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */ -char *irc_server_get_channels(IRC_SERVER_REC *server) +char *irc_server_get_channels(IRC_SERVER_REC *server, int rejoin_channels_mode) { GSList *tmp; GString *chans, *keys; char *ret; int use_keys; - int rejoin_channels_mode; g_return_val_if_fail(server != NULL, FALSE); - rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect"); - /* do we want to rejoin channels in the first place? */ - if(rejoin_channels_mode == 0) + if (rejoin_channels_mode == REJOIN_CHANNELS_MODE_OFF) return g_strdup(""); chans = g_string_new(NULL); @@ -806,7 +823,9 @@ char *irc_server_get_channels(IRC_SERVER_REC *server) for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { CHANNEL_REC *channel = tmp->data; CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet); - if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) { + if ((setup != NULL && setup->autojoin && + rejoin_channels_mode == REJOIN_CHANNELS_MODE_AUTO) || + rejoin_channels_mode == REJOIN_CHANNELS_MODE_ON) { g_string_append_printf(chans, "%s,", channel->name); g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key); if (channel->key != NULL) @@ -819,7 +838,9 @@ char *irc_server_get_channels(IRC_SERVER_REC *server) REJOIN_REC *rec = tmp->data; CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag); - if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) { + if ((setup != NULL && setup->autojoin && + rejoin_channels_mode == REJOIN_CHANNELS_MODE_AUTO) || + rejoin_channels_mode == REJOIN_CHANNELS_MODE_ON) { g_string_append_printf(chans, "%s,", rec->channel); g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" : rec->key); diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index 16e2061e..04d1f693 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -157,9 +157,15 @@ void irc_server_connect(SERVER_REC *server); /* Purge server output, either all or for specified target */ void irc_server_purge_output(IRC_SERVER_REC *server, const char *target); +enum { + REJOIN_CHANNELS_MODE_OFF = 0, /* */ + REJOIN_CHANNELS_MODE_ON, + REJOIN_CHANNELS_MODE_AUTO +}; + /* Return a string of all channels (and keys, if any have them) in server, like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */ -char *irc_server_get_channels(IRC_SERVER_REC *server); +char *irc_server_get_channels(IRC_SERVER_REC *server, int rejoin_channels_mode); void irc_server_send_starttls(IRC_SERVER_REC *server); /* INTERNAL: */ diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c index 6b817099..70683c4f 100644 --- a/src/irc/core/irc-session.c +++ b/src/irc/core/irc-session.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config, GSList *tmp; CONFIG_NODE *isupport; struct _isupport_data isupport_data; + int tls_disconnect; if (!IS_IRC_SERVER(server)) return; @@ -58,7 +60,15 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config, break; } } - net_sendbuffer_flush(server->handle); + /* we cannot upgrade TLS (yet?) */ + tls_disconnect = server->connrec->use_tls || server->connrec->starttls; + if (tls_disconnect) { + config_node_set_str(config, node, "rejoin_channels", + irc_server_get_channels(server, REJOIN_CHANNELS_MODE_ON)); + irc_send_cmd_now(server, "QUIT :[TLS] Client upgrade"); + } + + net_sendbuffer_flush(server->handle); config_node_set_str(config, node, "real_address", server->real_address); config_node_set_str(config, node, "userhost", server->userhost); @@ -71,18 +81,26 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config, config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username); config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password); + config_node_set_int(config, node, "starttls", + server->connrec->disallow_starttls ? 0 : + server->connrec->starttls ? 1 : + -1); + config_node_set_bool(config, node, "isupport_sent", server->isupport_sent); isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK); isupport_data.config = config; isupport_data.node = isupport; g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data); + + /* we have to defer the disconnect to irc_server_connect */ } static void sig_session_restore_server(IRC_SERVER_REC *server, CONFIG_NODE *node) { GSList *tmp; + int starttls_mode; if (!IS_IRC_SERVER(server)) return; @@ -105,6 +123,14 @@ static void sig_session_restore_server(IRC_SERVER_REC *server, g_free(server->connrec->sasl_password); server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL)); + server->connrec->channels = g_strdup(config_node_get_str(node, "rejoin_channels", NULL)); + + starttls_mode = config_node_get_int(node, "starttls", -1); + if (starttls_mode == 0) + server->connrec->disallow_starttls = 1; + if (starttls_mode == 1) + server->connrec->starttls = 1; + if (server->isupport == NULL) { server->isupport = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); @@ -123,6 +149,7 @@ static void sig_session_restore_server(IRC_SERVER_REC *server, } irc_server_init_isupport(server); + /* we will reconnect in irc_server_connect if the connection was TLS */ } static void sig_session_restore_nick(IRC_CHANNEL_REC *channel, diff --git a/src/irc/core/netsplit.c b/src/irc/core/netsplit.c index 1463f962..38b63fe5 100644 --- a/src/irc/core/netsplit.c +++ b/src/irc/core/netsplit.c @@ -381,6 +381,9 @@ static void sig_disconnected(IRC_SERVER_REC *server) if (!IS_IRC_SERVER(server)) return; + if (server->splits == NULL) + return; + g_hash_table_foreach(server->splits, (GHFunc) netsplit_destroy_hash, server); g_hash_table_destroy(server->splits); diff --git a/src/irc/notifylist/notifylist.c b/src/irc/notifylist/notifylist.c index 158b4dea..73737b37 100644 --- a/src/irc/notifylist/notifylist.c +++ b/src/irc/notifylist/notifylist.c @@ -209,6 +209,9 @@ static void notifylist_deinit_server(IRC_SERVER_REC *server) return; mserver = MODULE_DATA(server); + if (!mserver) + return; + while (mserver->notify_users != NULL) { rec = mserver->notify_users->data; diff --git a/src/perl/irc/Server.xs b/src/perl/irc/Server.xs index 54361ec9..fef3698f 100644 --- a/src/perl/irc/Server.xs +++ b/src/perl/irc/Server.xs @@ -1,5 +1,6 @@ #define PERL_NO_GET_CONTEXT #include "module.h" +#include static GSList *register_hash2list(HV *hv) { @@ -47,12 +48,20 @@ MODULE = Irssi::Irc::Server PACKAGE = Irssi::Irc::Server PREFIX = irc_server_ PROTOTYPES: ENABLE void -irc_server_get_channels(server) +irc_server_get_channels(server, rejoin_channels_mode = "") Irssi::Irc::Server server + char *rejoin_channels_mode PREINIT: char *ret; + int mode; + SETTINGS_REC *setting; PPCODE: - ret = irc_server_get_channels(server); + setting = settings_get_record("rejoin_channels_on_reconnect"); + mode = strarray_find(setting->choices, rejoin_channels_mode); + if (mode < 0) + mode = setting->default_value.v_int; + + ret = irc_server_get_channels(server, mode); XPUSHs(sv_2mortal(new_pv(ret))); g_free(ret);