1
0
mirror of https://github.com/moparisthebest/hexchat synced 2024-08-13 16:53:48 -04:00
hexchat/src/common/server.c

1899 lines
45 KiB
C
Raw Normal View History

2011-02-23 22:14:30 -05:00
/* X-Chat
* Copyright (C) 1998 Peter Zelezny.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
2012-12-23 14:36:54 -05:00
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
2011-02-23 22:14:30 -05:00
*
* MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
* based on Dante source code
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
* Inferno Nettverk A/S, Norway. All rights reserved.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#define WANTSOCKET
#define WANTARPA
#include "inet.h"
2012-07-21 08:26:19 -04:00
#ifdef WIN32
#include <winbase.h>
#include <io.h>
#else
2011-02-23 22:14:30 -05:00
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
2011-02-23 22:14:30 -05:00
#endif
2012-10-24 15:33:02 -04:00
#include "hexchat.h"
2011-02-23 22:14:30 -05:00
#include "fe.h"
#include "cfgfiles.h"
#include "network.h"
#include "notify.h"
2012-10-24 15:33:02 -04:00
#include "hexchatc.h"
2011-02-23 22:14:30 -05:00
#include "inbound.h"
#include "outbound.h"
#include "text.h"
#include "util.h"
#include "url.h"
#include "proto-irc.h"
#include "servlist.h"
#include "server.h"
#ifdef USE_OPENSSL
#include <openssl/ssl.h> /* SSL_() */
#include <openssl/err.h> /* ERR_() */
#include "ssl.h"
#endif
#ifdef USE_LIBPROXY
#include <proxy.h>
#endif
#ifdef USE_OPENSSL
/* local variables */
static struct session *g_sess = NULL;
#endif
static GSList *away_list = NULL;
GSList *serv_list = NULL;
static void auto_reconnect (server *serv, int send_quit, int err);
static void server_disconnect (session * sess, int sendquit, int err);
static int server_cleanup (server * serv);
static void server_connect (server *serv, char *hostname, int port, int no_login);
#ifdef USE_LIBPROXY
extern pxProxyFactory *libproxy_factory;
#endif
/* actually send to the socket. This might do a character translation or
send via SSL. server/dcc both use this function. */
int
tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len)
2011-02-23 22:14:30 -05:00
{
int ret;
gsize buf_encoded_len;
gchar *buf_encoded = text_convert_invalid (buf, len, write_converter, "?", &buf_encoded_len);
2011-02-23 22:14:30 -05:00
#ifdef USE_OPENSSL
if (!ssl)
ret = send (sok, buf_encoded, buf_encoded_len, 0);
else
ret = _SSL_send (ssl, buf_encoded, buf_encoded_len);
2011-02-23 22:14:30 -05:00
#else
ret = send (sok, buf_encoded, buf_encoded_len, 0);
2011-02-23 22:14:30 -05:00
#endif
g_free (buf_encoded);
2011-02-23 22:14:30 -05:00
return ret;
}
static int
server_send_real (server *serv, char *buf, int len)
{
fe_add_rawlog (serv, buf, len, TRUE);
url_check_line (buf);
return tcp_send_real (serv->ssl, serv->sok, serv->write_converter, buf, len);
2011-02-23 22:14:30 -05:00
}
/* new throttling system, uses the same method as the Undernet
ircu2.10 server; under test, a 200-line paste didn't flood
off the client */
static int
tcp_send_queue (server *serv)
{
char *buf, *p;
int len, i, pri;
GSList *list;
time_t now = time (0);
/* did the server close since the timeout was added? */
if (!is_server (serv))
return 0;
/* try priority 2,1,0 */
pri = 2;
while (pri >= 0)
{
list = serv->outbound_queue;
while (list)
{
buf = (char *) list->data;
if (buf[0] == pri)
{
buf++; /* skip the priority byte */
len = strlen (buf);
if (serv->next_send < now)
serv->next_send = now;
if (serv->next_send - now >= 10)
{
/* check for clock skew */
if (now >= serv->prev_now)
return 1; /* don't remove the timeout handler */
/* it is skewed, reset to something sane */
serv->next_send = now;
}
for (p = buf, i = len; i && *p != ' '; p++, i--);
serv->next_send += (2 + i / 120);
serv->sendq_len -= len;
serv->prev_now = now;
fe_set_throttle (serv);
server_send_real (serv, buf, len);
buf--;
serv->outbound_queue = g_slist_remove (serv->outbound_queue, buf);
g_free (buf);
2011-02-23 22:14:30 -05:00
list = serv->outbound_queue;
} else
{
list = list->next;
}
}
/* now try pri 0 */
pri--;
}
return 0; /* remove the timeout handler */
}
int
tcp_send_len (server *serv, char *buf, int len)
{
char *dbuf;
int noqueue = !serv->outbound_queue;
2012-10-22 09:55:43 -04:00
if (!prefs.hex_net_throttle)
2011-02-23 22:14:30 -05:00
return server_send_real (serv, buf, len);
dbuf = g_malloc (len + 2); /* first byte is the priority */
2011-02-23 22:14:30 -05:00
dbuf[0] = 2; /* pri 2 for most things */
memcpy (dbuf + 1, buf, len);
dbuf[len + 1] = 0;
/* privmsg and notice get a lower priority */
2012-06-16 07:01:47 -04:00
if (g_ascii_strncasecmp (dbuf + 1, "PRIVMSG", 7) == 0 ||
g_ascii_strncasecmp (dbuf + 1, "NOTICE", 6) == 0)
2011-02-23 22:14:30 -05:00
{
dbuf[0] = 1;
}
else
{
/* WHO/MODE get the lowest priority */
2012-06-16 07:01:47 -04:00
if (g_ascii_strncasecmp (dbuf + 1, "WHO ", 4) == 0 ||
2011-02-23 22:14:30 -05:00
/* but only MODE queries, not changes */
2012-06-16 07:01:47 -04:00
(g_ascii_strncasecmp (dbuf + 1, "MODE", 4) == 0 &&
2011-02-23 22:14:30 -05:00
strchr (dbuf, '-') == NULL &&
strchr (dbuf, '+') == NULL))
dbuf[0] = 0;
}
serv->outbound_queue = g_slist_append (serv->outbound_queue, dbuf);
serv->sendq_len += len; /* tcp_send_queue uses strlen */
if (tcp_send_queue (serv) && noqueue)
fe_timeout_add (500, tcp_send_queue, serv);
return 1;
}
void
tcp_sendf (server *serv, const char *fmt, ...)
2011-02-23 22:14:30 -05:00
{
va_list args;
/* keep this buffer in BSS. Converting UTF-8 to ISO-8859-x might make the
string shorter, so allow alot more than 512 for now. */
static char send_buf[1540]; /* good code hey (no it's not overflowable) */
int len;
va_start (args, fmt);
2014-12-17 18:49:59 -05:00
len = g_vsnprintf (send_buf, sizeof (send_buf) - 1, fmt, args);
2011-02-23 22:14:30 -05:00
va_end (args);
send_buf[sizeof (send_buf) - 1] = '\0';
if (len < 0 || len > (sizeof (send_buf) - 1))
len = strlen (send_buf);
tcp_send_len (serv, send_buf, len);
}
static int
close_socket_cb (gpointer sok)
{
closesocket (GPOINTER_TO_INT (sok));
return 0;
}
static void
close_socket (int sok)
{
/* close the socket in 5 seconds so the QUIT message is not lost */
fe_timeout_add (5000, close_socket_cb, GINT_TO_POINTER (sok));
}
/* handle 1 line of text received from the server */
static void
server_inline (server *serv, char *line, gssize len)
2011-02-23 22:14:30 -05:00
{
gsize len_utf8;
line = text_convert_invalid (line, len, serv->read_converter, unicode_fallback_string, &len_utf8);
2011-02-23 22:14:30 -05:00
fe_add_rawlog (serv, line, len_utf8, FALSE);
2011-02-23 22:14:30 -05:00
/* let proto-irc.c handle it */
serv->p_inline (serv, line, len_utf8);
2011-02-23 22:14:30 -05:00
g_free (line);
2011-02-23 22:14:30 -05:00
}
/* read data from socket */
static gboolean
server_read (GIOChannel *source, GIOCondition condition, server *serv)
{
int sok = serv->sok;
int error, i, len;
char lbuf[2050];
while (1)
{
#ifdef USE_OPENSSL
if (!serv->ssl)
#endif
len = recv (sok, lbuf, sizeof (lbuf) - 2, 0);
#ifdef USE_OPENSSL
else
len = _SSL_recv (serv->ssl, lbuf, sizeof (lbuf) - 2);
#endif
if (len < 1)
{
error = 0;
if (len < 0)
{
if (would_block ())
return TRUE;
error = sock_error ();
}
if (!serv->end_of_motd)
{
server_disconnect (serv->server_session, FALSE, error);
if (!servlist_cycle (serv))
{
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnect)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, error);
}
} else
{
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnect)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, error);
else
server_disconnect (serv->server_session, FALSE, error);
}
return TRUE;
}
i = 0;
lbuf[len] = 0;
while (i < len)
{
switch (lbuf[i])
{
case '\r':
break;
case '\n':
serv->linebuf[serv->pos] = 0;
server_inline (serv, serv->linebuf, serv->pos);
serv->pos = 0;
break;
default:
serv->linebuf[serv->pos] = lbuf[i];
if (serv->pos >= (sizeof (serv->linebuf) - 1))
fprintf (stderr,
2012-10-14 21:29:13 -04:00
"*** HEXCHAT WARNING: Buffer overflow - shit server!\n");
2011-02-23 22:14:30 -05:00
else
serv->pos++;
}
i++;
}
}
}
static void
server_connected (server * serv)
{
prefs.wait_on_exit = TRUE;
serv->ping_recv = time (0);
serv->lag_sent = 0;
2011-02-23 22:14:30 -05:00
serv->connected = TRUE;
set_nonblocking (serv->sok);
serv->iotag = fe_input_add (serv->sok, FIA_READ|FIA_EX, server_read, serv);
if (!serv->no_login)
{
EMIT_SIGNAL (XP_TE_CONNECTED, serv->server_session, NULL, NULL, NULL,
NULL, 0);
if (serv->network)
{
serv->p_login (serv,
(!(((ircnet *)serv->network)->flags & FLAG_USE_GLOBAL) &&
(((ircnet *)serv->network)->user)) ?
(((ircnet *)serv->network)->user) :
2012-10-22 08:50:36 -04:00
prefs.hex_irc_user_name,
2011-02-23 22:14:30 -05:00
(!(((ircnet *)serv->network)->flags & FLAG_USE_GLOBAL) &&
(((ircnet *)serv->network)->real)) ?
(((ircnet *)serv->network)->real) :
2012-10-22 08:50:36 -04:00
prefs.hex_irc_real_name);
2011-02-23 22:14:30 -05:00
} else
{
2012-10-22 08:50:36 -04:00
serv->p_login (serv, prefs.hex_irc_user_name, prefs.hex_irc_real_name);
2011-02-23 22:14:30 -05:00
}
} else
{
EMIT_SIGNAL (XP_TE_SERVERCONNECTED, serv->server_session, NULL, NULL,
NULL, NULL, 0);
}
server_set_name (serv, serv->servername);
fe_server_event (serv, FE_SE_CONNECT, 0);
}
#ifdef WIN32
static gboolean
server_close_pipe (int *pipefd) /* see comments below */
{
close (pipefd[0]); /* close WRITE end first to cause an EOF on READ */
close (pipefd[1]); /* in giowin32, and end that thread. */
g_free (pipefd);
2011-02-23 22:14:30 -05:00
return FALSE;
}
#endif
static void
server_stopconnecting (server * serv)
{
if (serv->iotag)
{
fe_input_remove (serv->iotag);
serv->iotag = 0;
}
if (serv->joindelay_tag)
{
fe_timeout_remove (serv->joindelay_tag);
serv->joindelay_tag = 0;
}
#ifndef WIN32
/* kill the child process trying to connect */
kill (serv->childpid, SIGKILL);
waitpid (serv->childpid, NULL, 0);
close (serv->childwrite);
close (serv->childread);
#else
PostThreadMessage (serv->childpid, WM_QUIT, 0, 0);
{
/* if we close the pipe now, giowin32 will crash. */
int *pipefd = g_new (int, 2);
2011-02-23 22:14:30 -05:00
pipefd[0] = serv->childwrite;
pipefd[1] = serv->childread;
g_idle_add ((GSourceFunc)server_close_pipe, pipefd);
}
#endif
#ifdef USE_OPENSSL
if (serv->ssl_do_connect_tag)
{
fe_timeout_remove (serv->ssl_do_connect_tag);
serv->ssl_do_connect_tag = 0;
}
#endif
fe_progressbar_end (serv);
serv->connecting = FALSE;
fe_server_event (serv, FE_SE_DISCONNECT, 0);
}
#ifdef USE_OPENSSL
#define SSLTMOUT 90 /* seconds */
static void
ssl_cb_info (SSL * s, int where, int ret)
{
/* char buf[128];*/
return; /* FIXME: make debug level adjustable in serverlist or settings */
2014-12-17 18:49:59 -05:00
/* g_snprintf (buf, sizeof (buf), "%s (%d)", SSL_state_string_long (s), where);
2011-02-23 22:14:30 -05:00
if (g_sess)
EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
else
fprintf (stderr, "%s\n", buf);*/
}
static int
ssl_cb_verify (int ok, X509_STORE_CTX * ctx)
{
char subject[256];
char issuer[256];
char buf[512];
X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), subject,
sizeof (subject));
X509_NAME_oneline (X509_get_issuer_name (ctx->current_cert), issuer,
sizeof (issuer));
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Subject: %s", subject);
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Issuer: %s", issuer);
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
return (TRUE); /* always ok */
}
static int
ssl_do_connect (server * serv)
{
char buf[128];
g_sess = serv->server_session;
2015-09-11 01:48:29 -04:00
/* Set SNI hostname before connect */
SSL_set_tlsext_host_name(serv->ssl, serv->hostname);
2011-02-23 22:14:30 -05:00
if (SSL_connect (serv->ssl) <= 0)
{
char err_buf[128];
int err;
g_sess = NULL;
if ((err = ERR_get_error ()) > 0)
{
ERR_error_string (err, err_buf);
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "(%d) %s", err, err_buf);
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
NULL, NULL, 0);
if (ERR_GET_REASON (err) == SSL_R_WRONG_VERSION_NUMBER)
PrintText (serv->server_session, _("Are you sure this is a SSL capable server and port?\n"));
server_cleanup (serv);
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnectonfail)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, -1);
return (0); /* remove it (0) */
}
}
g_sess = NULL;
if (SSL_is_init_finished (serv->ssl))
{
struct cert_info cert_info;
struct chiper_info *chiper_info;
int verify_error;
int i;
if (!_SSL_get_cert_info (&cert_info, serv->ssl))
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Certification info:");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Subject:");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
for (i = 0; cert_info.subject_word[i]; i++)
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " %s", cert_info.subject_word[i]);
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
}
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Issuer:");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
for (i = 0; cert_info.issuer_word[i]; i++)
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " %s", cert_info.issuer_word[i]);
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
}
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Public key algorithm: %s (%d bits)",
2011-02-23 22:14:30 -05:00
cert_info.algorithm, cert_info.algorithm_bits);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
/*if (cert_info.rsa_tmp_bits)
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf),
2011-02-23 22:14:30 -05:00
" Public key algorithm uses ephemeral key with %d bits",
cert_info.rsa_tmp_bits);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
}*/
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Sign algorithm %s",
2011-02-23 22:14:30 -05:00
cert_info.sign_algorithm/*, cert_info.sign_algorithm_bits*/);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Valid since %s to %s",
2011-02-23 22:14:30 -05:00
cert_info.notbefore, cert_info.notafter);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
} else
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " * No Certificate");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
}
chiper_info = _SSL_get_cipher_info (serv->ssl); /* static buffer */
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Cipher info:");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL,
0);
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), " Version: %s, cipher %s (%u bits)",
2011-02-23 22:14:30 -05:00
chiper_info->version, chiper_info->chiper,
chiper_info->chiper_bits);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL,
0);
verify_error = SSL_get_verify_result (serv->ssl);
switch (verify_error)
{
case X509_V_OK:
2014-11-19 21:43:01 -05:00
{
X509 *cert = SSL_get_peer_certificate (serv->ssl);
int hostname_err;
if ((hostname_err = _SSL_check_hostname(cert, serv->hostname)) != 0)
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Verify E: Failed to validate hostname? (%d)%s",
2014-11-19 21:43:01 -05:00
hostname_err, serv->accept_invalid_cert ? " -- Ignored" : "");
if (serv->accept_invalid_cert)
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0);
else
goto conn_fail;
}
break;
}
2014-12-17 18:49:59 -05:00
/* g_snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
2011-02-23 22:14:30 -05:00
/* EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0); */
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
case X509_V_ERR_CERT_HAS_EXPIRED:
if (serv->accept_invalid_cert)
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "* Verify E: %s.? (%d) -- Ignored",
2011-02-23 22:14:30 -05:00
X509_verify_cert_error_string (verify_error),
verify_error);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
NULL, 0);
break;
}
default:
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "%s.? (%d)",
2011-02-23 22:14:30 -05:00
X509_verify_cert_error_string (verify_error),
verify_error);
2014-11-19 21:43:01 -05:00
conn_fail:
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL, NULL,
NULL, 0);
server_cleanup (serv);
return (0);
}
server_stopconnecting (serv);
/* activate gtk poll */
server_connected (serv);
return (0); /* remove it (0) */
} else
{
if (serv->ssl->session && serv->ssl->session->time + SSLTMOUT < time (NULL))
{
2014-12-17 18:49:59 -05:00
g_snprintf (buf, sizeof (buf), "SSL handshake timed out");
2011-02-23 22:14:30 -05:00
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
NULL, NULL, 0);
server_cleanup (serv); /* ->connecting = FALSE */
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnectonfail)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, -1);
return (0); /* remove it (0) */
}
return (1); /* call it more (1) */
}
}
#endif
static int
timeout_auto_reconnect (server *serv)
{
if (is_server (serv)) /* make sure it hasnt been closed during the delay */
{
serv->recondelay_tag = 0;
if (!serv->connected && !serv->connecting && serv->server_session)
{
server_connect (serv, serv->hostname, serv->port, FALSE);
}
}
return 0; /* returning 0 should remove the timeout handler */
}
static void
auto_reconnect (server *serv, int send_quit, int err)
{
session *s;
GSList *list;
int del;
if (serv->server_session == NULL)
return;
list = sess_list;
while (list) /* make sure auto rejoin can work */
{
s = list->data;
if (s->type == SESS_CHANNEL && s->channel[0])
{
strcpy (s->waitchannel, s->channel);
strcpy (s->willjoinchannel, s->channel);
}
list = list->next;
}
if (serv->connected)
server_disconnect (serv->server_session, send_quit, err);
2012-10-22 09:55:43 -04:00
del = prefs.hex_net_reconnect_delay * 1000;
2011-02-23 22:14:30 -05:00
if (del < 1000)
del = 500; /* so it doesn't block the gui */
#ifndef WIN32
if (err == -1 || err == 0 || err == ECONNRESET || err == ETIMEDOUT)
#else
if (err == -1 || err == 0 || err == WSAECONNRESET || err == WSAETIMEDOUT)
#endif
serv->reconnect_away = serv->is_away;
/* is this server in a reconnect delay? remove it! */
if (serv->recondelay_tag)
{
fe_timeout_remove (serv->recondelay_tag);
serv->recondelay_tag = 0;
}
serv->recondelay_tag = fe_timeout_add (del, timeout_auto_reconnect, serv);
fe_server_event (serv, FE_SE_RECONDELAY, del);
}
static void
server_flush_queue (server *serv)
{
list_free (&serv->outbound_queue);
serv->sendq_len = 0;
fe_set_throttle (serv);
}
/* connect() successed */
static void
server_connect_success (server *serv)
{
#ifdef USE_OPENSSL
#define SSLDOCONNTMOUT 300
if (serv->use_ssl)
{
char *err;
/* it'll be a memory leak, if connection isn't terminated by
server_cleanup() */
serv->ssl = _SSL_socket (serv->ctx, serv->sok);
if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify, NULL)))
2011-02-23 22:14:30 -05:00
{
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, err, NULL,
NULL, NULL, 0);
server_cleanup (serv); /* ->connecting = FALSE */
return;
}
/* FIXME: it'll be needed by new servers */
/* send(serv->sok, "STLS\r\n", 6, 0); sleep(1); */
set_nonblocking (serv->sok);
serv->ssl_do_connect_tag = fe_timeout_add (SSLDOCONNTMOUT,
ssl_do_connect, serv);
return;
}
serv->ssl = NULL;
#endif
server_stopconnecting (serv); /* ->connecting = FALSE */
/* activate glib poll */
server_connected (serv);
}
/* receive info from the child-process about connection progress */
static gboolean
server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
{
session *sess = serv->server_session;
char tbuf[128];
char outbuf[512];
char host[100];
char ip[100];
waitline2 (source, tbuf, sizeof tbuf);
switch (tbuf[0])
{
case '0': /* print some text */
waitline2 (source, tbuf, sizeof tbuf);
PrintText (serv->server_session, tbuf);
break;
case '1': /* unknown host */
server_stopconnecting (serv);
closesocket (serv->sok4);
if (serv->proxy_sok4 != -1)
closesocket (serv->proxy_sok4);
if (serv->sok6 != -1)
closesocket (serv->sok6);
if (serv->proxy_sok6 != -1)
closesocket (serv->proxy_sok6);
EMIT_SIGNAL (XP_TE_UKNHOST, sess, NULL, NULL, NULL, NULL, 0);
if (!servlist_cycle (serv))
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnectonfail)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, -1);
break;
case '2': /* connection failed */
waitline2 (source, tbuf, sizeof tbuf);
server_stopconnecting (serv);
closesocket (serv->sok4);
if (serv->proxy_sok4 != -1)
closesocket (serv->proxy_sok4);
if (serv->sok6 != -1)
closesocket (serv->sok6);
if (serv->proxy_sok6 != -1)
closesocket (serv->proxy_sok6);
EMIT_SIGNAL (XP_TE_CONNFAIL, sess, errorstring (atoi (tbuf)), NULL,
NULL, NULL, 0);
if (!servlist_cycle (serv))
2012-10-22 09:55:43 -04:00
if (prefs.hex_net_auto_reconnectonfail)
2011-02-23 22:14:30 -05:00
auto_reconnect (serv, FALSE, -1);
break;
case '3': /* gethostbyname finished */
waitline2 (source, host, sizeof host);
waitline2 (source, ip, sizeof ip);
waitline2 (source, outbuf, sizeof outbuf);
EMIT_SIGNAL (XP_TE_CONNECT, sess, host, ip, outbuf, NULL, 0);
break;
case '4': /* success */
waitline2 (source, tbuf, sizeof (tbuf));
serv->sok = atoi (tbuf);
/* close the one we didn't end up using */
if (serv->sok == serv->sok4)
closesocket (serv->sok6);
else
closesocket (serv->sok4);
if (serv->proxy_sok != -1)
{
if (serv->proxy_sok == serv->proxy_sok4)
closesocket (serv->proxy_sok6);
else
closesocket (serv->proxy_sok4);
}
{
struct sockaddr addr;
int addr_len = sizeof (addr);
guint16 port;
if (!getsockname (serv->sok, &addr, &addr_len))
{
if (addr.sa_family == AF_INET)
port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
else
port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
g_snprintf (outbuf, sizeof (outbuf), "IDENTD %"G_GUINT16_FORMAT" ", port);
if (serv->network && ((ircnet *)serv->network)->user)
g_strlcat (outbuf, ((ircnet *)serv->network)->user, sizeof (outbuf));
else
g_strlcat (outbuf, prefs.hex_irc_user_name, sizeof (outbuf));
handle_command (serv->server_session, outbuf, FALSE);
}
}
2011-02-23 22:14:30 -05:00
server_connect_success (serv);
break;
case '5': /* prefs ip discovered */
waitline2 (source, tbuf, sizeof tbuf);
prefs.local_ip = inet_addr (tbuf);
break;
2012-10-22 09:55:43 -04:00
case '7': /* gethostbyname (prefs.hex_net_bind_host) failed */
2011-02-23 22:14:30 -05:00
sprintf (outbuf,
_("Cannot resolve hostname %s\nCheck your IP Settings!\n"),
2012-10-22 09:55:43 -04:00
prefs.hex_net_bind_host);
2011-02-23 22:14:30 -05:00
PrintText (sess, outbuf);
break;
case '8':
PrintText (sess, _("Proxy traversal failed.\n"));
server_disconnect (sess, FALSE, -1);
break;
case '9':
waitline2 (source, tbuf, sizeof tbuf);
EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, tbuf, NULL, NULL, NULL, 0);
break;
}
return TRUE;
}
/* kill all sockets & iotags of a server. Stop a connection attempt, or
disconnect if already connected. */
static int
server_cleanup (server * serv)
{
2012-10-30 16:25:58 -04:00
fe_set_lag (serv, 0);
2011-02-23 22:14:30 -05:00
if (serv->iotag)
{
fe_input_remove (serv->iotag);
serv->iotag = 0;
}
if (serv->joindelay_tag)
{
fe_timeout_remove (serv->joindelay_tag);
serv->joindelay_tag = 0;
}
#ifdef USE_OPENSSL
if (serv->ssl)
{
SSL_shutdown (serv->ssl);
SSL_free (serv->ssl);
2011-02-23 22:14:30 -05:00
serv->ssl = NULL;
}
#endif
if (serv->connecting)
{
server_stopconnecting (serv);
closesocket (serv->sok4);
if (serv->proxy_sok4 != -1)
closesocket (serv->proxy_sok4);
if (serv->sok6 != -1)
closesocket (serv->sok6);
if (serv->proxy_sok6 != -1)
closesocket (serv->proxy_sok6);
return 1;
}
if (serv->connected)
{
close_socket (serv->sok);
if (serv->proxy_sok)
close_socket (serv->proxy_sok);
serv->connected = FALSE;
serv->end_of_motd = FALSE;
return 2;
}
/* is this server in a reconnect delay? remove it! */
if (serv->recondelay_tag)
{
fe_timeout_remove (serv->recondelay_tag);
serv->recondelay_tag = 0;
return 3;
}
return 0;
}
static void
server_disconnect (session * sess, int sendquit, int err)
{
server *serv = sess->server;
GSList *list;
char tbuf[64];
gboolean shutup = FALSE;
/* send our QUIT reason */
if (sendquit && serv->connected)
{
server_sendquit (sess);
}
fe_server_event (serv, FE_SE_DISCONNECT, 0);
/* close all sockets & io tags */
switch (server_cleanup (serv))
{
case 0: /* it wasn't even connected! */
notc_msg (sess);
return;
case 1: /* it was in the process of connecting */
sprintf (tbuf, "%d", sess->server->childpid);
EMIT_SIGNAL (XP_TE_STOPCONNECT, sess, tbuf, NULL, NULL, NULL, 0);
return;
case 3:
shutup = TRUE; /* won't print "disconnected" in channels */
}
server_flush_queue (serv);