Reconnect if the sts cap is encountered.

This commit is contained in:
Sadie Powell 2021-06-21 12:05:16 +01:00
parent ade78e84f1
commit 9487bda9a0
7 changed files with 139 additions and 4 deletions

View File

@ -546,6 +546,8 @@ typedef struct server
GSList *favlist; /* list of channels & keys to join */
struct sts_profile *stsprofile; /* pending sts profile that caused a reconnect */
unsigned int motd_skipped:1;
unsigned int connected:1;
unsigned int connecting:1;

View File

@ -47,6 +47,7 @@
#include "ctcp.h"
#include "hexchatc.h"
#include "chanopt.h"
#include "sts.h"
void
@ -1728,6 +1729,7 @@ static const char * const supported_caps[] = {
"setname",
"invite-notify",
"account-tag",
/* "sts", Handled manually */
/* ZNC */
"znc.in/server-time-iso",
@ -1778,7 +1780,9 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
char buffer[500]; /* buffer for requesting capabilities and emitting the signal */
gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
char **extensions;
int i;
int i, stsvalue;
GHashTable *stscap;
const char *stsvaluestr;
if (g_str_has_prefix (extensions_str, "* "))
{
@ -1811,6 +1815,57 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
value++;
}
#ifdef USE_OPENSSL
/** If the server has a STS profile then we should respect it. */
if (!g_strcmp0 (extension, "sts"))
{
stscap = sts_parse_cap (value);
if (serv->use_ssl)
{
/* Retrieve the duration; if its valid then store/update it. */
stsvaluestr = (const char*) g_hash_table_lookup (stscap, "duration");
if (stsvaluestr)
{
stsvalue = atoi (stsvaluestr);
if (stsvalue > 0)
sts_update_expiry(serv->hostname, stsvalue, serv->stsprofile);
}
} else
{
stsvaluestr = (const char*) g_hash_table_lookup (stscap, "port");
if (stsvaluestr)
{
stsvalue = atoi (stsvaluestr);
if (stsvalue > 0 && stsvalue < 65536)
{
/* We are connecting on plain text and have found a valid STS profile. */
serv->stsprofile = sts_new ();
strcpy (serv->stsprofile->host, serv->hostname);
serv->stsprofile->port = stsvalue;
/* Reconfigure with the new port and security settings. */
serv->port = serv->stsprofile->port;
serv->accept_invalid_cert = FALSE;
serv->use_ssl = TRUE;
/* We're reconnecting so don't request caps. */
want_cap = FALSE;
/* Reconnect to the server. */
EMIT_SIGNAL (XP_TE_STSREDIRNEW, serv->server_session, serv->hostname, NULL, NULL, NULL, 0);
stsvalue = prefs.hex_net_reconnect_delay;
prefs.hex_net_reconnect_delay = 0;
serv->auto_reconnect (serv, TRUE, -1);
prefs.hex_net_reconnect_delay = stsvalue;
break;
}
}
}
g_hash_table_destroy (stscap);
}
#endif
/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
if (!g_strcmp0 (extension, "sasl") &&
((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
@ -1842,6 +1897,28 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
g_strfreev (extensions);
#ifdef USE_OPENSSL
/* Check up on the status of the STS profile. */
if (!serv->waiting_on_cap && serv->ctx && serv->stsprofile)
{
/* We have reconnected using SSL; is the profile complete? */
if (serv->stsprofile->expiry)
{
/* The profile is fine; store it. */
sts_store (serv->stsprofile);
serv->stsprofile = NULL;
}
else
{
/* The profile redirected us to a broken server. We MUST block connection as per spec. */
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, "STS profile mismatch", NULL, NULL, NULL, 0);
serv->disconnect (serv, TRUE, -1);
return;
}
}
#endif
if (want_cap)
{
/* buffer + 9 = emit buffer without "CAP REQ :" */

View File

@ -3469,6 +3469,10 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
use_ssl_noverify = TRUE;
offset++; /* args move up by 1 word */
}
/** If the server was previously used by a failed STS connection then free the incomplete profile. */
if (serv->stsprofile)
g_clear_pointer (&serv->stsprofile, g_free);
#endif
if (!parse_irc_url (word[2 + offset], &server_name, &port, &channel, &key, &use_ssl))

View File

@ -1757,6 +1757,7 @@ server_new (void)
serv->id = id++;
serv->sok = -1;
serv->stsprofile = NULL;
strcpy (serv->nick, prefs.hex_irc_nick1);
server_set_defaults (serv);
@ -1943,6 +1944,7 @@ server_free (server *serv)
g_free (serv->bad_nick_prefixes);
g_free (serv->last_away_reason);
g_free (serv->encoding);
g_free (serv->stsprofile);
g_iconv_close (serv->read_converter);
g_iconv_close (serv->write_converter);

View File

@ -32,7 +32,7 @@ struct sts_profile *
sts_find (const char* host)
{
time_t now;
GList *next;
GSList *next;
struct sts_profile *nextprofile;
now = time (NULL);
@ -71,7 +71,7 @@ sts_load (void)
continue;
}
profiles = g_slist_append (profiles, profile);
sts_store (profile);
}
}
@ -87,6 +87,26 @@ sts_new (void)
return profile;
}
GHashTable *
sts_parse_cap (const char* cap)
{
char **entries, **currentry;
char *value;
GHashTable *table;
table = g_hash_table_new (g_str_hash, g_str_equal);
entries = g_strsplit (cap, ",", 0);
for (currentry = entries; *currentry; ++currentry)
{
value = strchr (*currentry, '=');
if (value)
g_hash_table_insert (table, g_strndup (*currentry, value - *currentry), g_strdup (value + 1));
}
g_free (entries);
return table;
}
void
sts_save (void)
{
@ -101,7 +121,7 @@ sts_save (void)
return; /* Filesystem not writable. */
now = time (NULL);
for (next = profiles; next; next = profiles->next)
for (next = profiles; next; next = next->next)
{
nextprofile = (struct sts_profile *)next->data;
if (now >= nextprofile->expiry)
@ -114,3 +134,18 @@ sts_save (void)
close (fh);
}
void
sts_store (struct sts_profile *profile)
{
profiles = g_slist_append (profiles, profile);
}
void sts_update_expiry (const char *host, time_t newexpiry, struct sts_profile *incomplete_profile)
{
struct sts_profile *profile;
profile = incomplete_profile ? incomplete_profile : sts_find (host);
if (profile)
profile->expiry = time (NULL) + newexpiry;
}

View File

@ -50,7 +50,16 @@ void sts_load (void);
/* Creates a new empty STS profile. */
struct sts_profile * sts_new (void);
/** Parses the key/value pairs from a STS cap into a hash table */
GHashTable *sts_parse_cap (const char* cap);
/* Saves STS profiles to sts.conf */
void sts_save (void);
/* Stores a new STS profile in the hash table */
void sts_store (struct sts_profile *profile);
/* Updates the expiry time for a STS profile. */
void sts_update_expiry (const char *host, time_t newexpiry, struct sts_profile *incomplete_profile);
#endif

View File

@ -790,6 +790,12 @@ pevt_stsredir_help
%C29*%O$tUsing the previously saved STS profile for %C29$1%O
n2
NEW STS Redirection
XP_TE_STSREDIRNEW
pevt_stsredir_help
%C29*%O$tReconnecting using the new STS profile for %C29$1%O
1
Stop Connection
XP_TE_STOPCONNECT
pevt_sconnect_help