#include <errno.h>
#include <fcntl.h>
-#define WANTSOCKET
-#define WANTARPA
-#include "inet.h"
-
-#ifdef WIN32
-#include <winbase.h>
-#include <io.h>
-#else
-#include <signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#endif
-
#include "hexchat.h"
#include "fe.h"
#include "cfgfiles.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_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 void auto_reconnect (server *serv, int send_quit, GError *err);
+static void server_disconnect (session * sess, int sendquit, GError *err);
static int server_cleanup (server * serv);
static void server_connect (server *serv, char *hostname, int port, int no_login);
-static void
-write_error (char *message, GError **error)
-{
- if (error == NULL || *error == NULL) {
- return;
- }
- g_printerr ("%s: %s\n", message, (*error)->message);
- g_clear_error (error);
-}
-
-/* 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)
-{
- int ret;
-
- gsize buf_encoded_len;
- gchar *buf_encoded = text_convert_invalid (buf, len, write_converter, arbitrary_encoding_fallback_string, &buf_encoded_len);
-#ifdef USE_OPENSSL
- if (!ssl)
- ret = send (sok, buf_encoded, buf_encoded_len, 0);
- else
- ret = _SSL_send (ssl, buf_encoded, buf_encoded_len);
-#else
- ret = send (sok, buf_encoded, buf_encoded_len, 0);
-#endif
- g_free (buf_encoded);
-
- return ret;
-}
-
static int
server_send_real (server *serv, char *buf, int len)
{
- fe_add_rawlog (serv, buf, len, TRUE);
+ gsize buf_encoded_len;
+ gchar *buf_encoded;
+
+ buf_encoded = text_convert_invalid (buf, len, serv->write_converter, arbitrary_encoding_fallback_string, &buf_encoded_len);
- url_check_line (buf);
+ fe_add_rawlog (serv, buf_encoded, buf_encoded_len, TRUE);
+ url_check_line (buf_encoded);
- return tcp_send_real (serv->ssl, serv->sok, serv->write_converter, buf, len);
+ // TODO: Error and cancellable
+ return g_socket_send (serv->socket, buf_encoded, buf_encoded_len, NULL, NULL);
}
/* new throttling system, uses the same method as the Undernet
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_seconds (5, close_socket_cb, GINT_TO_POINTER (sok));
-}
-
/* handle 1 line of text received from the server */
static void
/* read data from socket */
static gboolean
-server_read (GIOChannel *source, GIOCondition condition, server *serv)
+on_socket_ready (GSocket *socket, GIOCondition condition, server *serv)
{
- int sok = serv->sok;
- int error, i, len;
+ GError *error = NULL;
+ gssize len;
+ int i;
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)
+ len = g_socket_receive (socket, lbuf, sizeof (lbuf) - 2, NULL, &error);
+ if (error)
{
- error = 0;
- if (len < 0)
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
{
- if (would_block ())
- return TRUE;
- error = sock_error ();
+ /* Fall through */
}
- if (!serv->end_of_motd)
+ else if (!serv->end_of_motd)
{
server_disconnect (serv->server_session, FALSE, error);
if (!servlist_cycle (serv))
if (prefs.hex_net_auto_reconnect)
auto_reconnect (serv, FALSE, error);
}
- } else
+ }
+ else
{
if (prefs.hex_net_auto_reconnect)
auto_reconnect (serv, FALSE, error);
else
server_disconnect (serv->server_session, FALSE, error);
}
- return TRUE;
+
+ g_error_free (error);
+ return G_SOURCE_CONTINUE;
}
- i = 0;
+ i = 0;
lbuf[len] = 0;
while (i < len)
{
+ // FIXME: Unsafe?
switch (lbuf[i])
{
case '\r':
serv->ping_recv = time (0);
serv->lag_sent = 0;
serv->connected = TRUE;
- set_nonblocking (serv->sok);
- serv->iotag = fe_input_add (serv->sok, FIA_READ|FIA_EX, server_read, serv);
+
+ serv->socket_read_source = g_socket_create_source (serv->socket, G_IO_IN|G_IO_ERR, NULL);
+ g_source_set_callback (serv->socket_read_source, (GSourceFunc)on_socket_ready, serv, NULL);
+ g_source_attach (serv->socket_read_source, g_main_context_default ());
+ g_source_unref (serv->socket_read_source);
+
if (!serv->no_login)
{
EMIT_SIGNAL (XP_TE_CONNECTED, serv->server_session, NULL, NULL, NULL,
static void
server_stopconnecting (server * serv)
{
- if (serv->iotag)
- {
- fe_input_remove (serv->iotag);
- serv->iotag = 0;
- }
+ g_assert (!serv->socket_read_source);
if (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);
- 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
+ if (serv->connection_cancellable)
+ g_cancellable_cancel (serv->connection_cancellable);
fe_progressbar_end (serv);
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 */
-
-/* g_snprintf (buf, sizeof (buf), "%s (%d)", SSL_state_string_long (s), where);
- 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 *current_cert = X509_STORE_CTX_get_current_cert (ctx);
-
- if (!current_cert)
- return TRUE;
-
- X509_NAME_oneline (X509_get_subject_name (current_cert),
- subject, sizeof (subject));
- X509_NAME_oneline (X509_get_issuer_name (current_cert),
- issuer, sizeof (issuer));
-
- g_snprintf (buf, sizeof (buf), "* Subject: %s", subject);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
- g_snprintf (buf, sizeof (buf), "* Issuer: %s", issuer);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
-
- return TRUE;
-}
-
-static int
-ssl_do_connect (server * serv)
-{
- char buf[256]; // ERR_error_string() MUST have this size
-
- g_sess = serv->server_session;
-
- /* Set SNI hostname before connect */
- SSL_set_tlsext_host_name(serv->ssl, serv->hostname);
-
- 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);
- g_snprintf (buf, sizeof (buf), "(%d) %s", err, err_buf);
- 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);
-
- if (prefs.hex_net_auto_reconnectonfail)
- 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))
- {
- g_snprintf (buf, sizeof (buf), "* Certification info:");
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- g_snprintf (buf, sizeof (buf), " Subject:");
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- for (i = 0; cert_info.subject_word[i]; i++)
- {
- g_snprintf (buf, sizeof (buf), " %s", cert_info.subject_word[i]);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- }
- g_snprintf (buf, sizeof (buf), " Issuer:");
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- for (i = 0; cert_info.issuer_word[i]; i++)
- {
- g_snprintf (buf, sizeof (buf), " %s", cert_info.issuer_word[i]);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- }
- g_snprintf (buf, sizeof (buf), " Public key algorithm: %s (%d bits)",
- 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)
- {
- g_snprintf (buf, sizeof (buf),
- " 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);
- }*/
- g_snprintf (buf, sizeof (buf), " Sign algorithm %s",
- cert_info.sign_algorithm/*, cert_info.sign_algorithm_bits*/);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- g_snprintf (buf, sizeof (buf), " Valid since %s to %s",
- cert_info.notbefore, cert_info.notafter);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- } else
- {
- g_snprintf (buf, sizeof (buf), "No Certificate");
- goto conn_fail;
- }
-
- chiper_info = _SSL_get_cipher_info (serv->ssl); /* static buffer */
- g_snprintf (buf, sizeof (buf), "* Cipher info:");
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL,
- 0);
- g_snprintf (buf, sizeof (buf), " Version: %s, cipher %s (%u bits)",
- 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:
- {
- X509 *cert = SSL_get_peer_certificate (serv->ssl);
- int hostname_err;
- if ((hostname_err = _SSL_check_hostname(cert, serv->hostname)) != 0)
- {
- g_snprintf (buf, sizeof (buf), "* Verify E: Failed to validate hostname? (%d)%s",
- 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;
- }
- /* g_snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
- /* 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)
- {
- g_snprintf (buf, sizeof (buf), "* Verify E: %s.? (%d) -- Ignored",
- X509_verify_cert_error_string (verify_error),
- verify_error);
- EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
- NULL, 0);
- break;
- }
- default:
- g_snprintf (buf, sizeof (buf), "%s.? (%d)",
- X509_verify_cert_error_string (verify_error),
- verify_error);
-conn_fail:
- 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
- {
- SSL_SESSION *session = SSL_get_session (serv->ssl);
- if (session && SSL_SESSION_get_time (session) + SSLTMOUT < time (NULL))
- {
- g_snprintf (buf, sizeof (buf), "SSL handshake timed out");
- EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
- NULL, NULL, 0);
- server_cleanup (serv); /* ->connecting = FALSE */
-
- if (prefs.hex_net_auto_reconnectonfail)
- auto_reconnect (serv, FALSE, -1);
-
- return (0); /* remove it (0) */
- }
-
- return (1); /* call it more (1) */
- }
-}
-#endif
-
static int
timeout_auto_reconnect (server *serv)
{
}
static void
-auto_reconnect (server *serv, int send_quit, int err)
+auto_reconnect (server *serv, int send_quit, GError *error)
{
session *s;
int del;
}
if (serv->connected)
- server_disconnect (serv->server_session, send_quit, err);
+ server_disconnect (serv->server_session, send_quit, error);
del = prefs.hex_net_reconnect_delay * 1000;
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
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
serv->reconnect_away = serv->is_away;
/* is this server in a reconnect delay? remove it! */
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() */
- if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify, NULL)))
- {
- EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, err, NULL,
- NULL, NULL, 0);
- server_cleanup (serv); /* ->connecting = FALSE */
- return;
- }
- serv->ssl = _SSL_socket (serv->ctx, serv->sok);
- /* 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 */
+#if 0
static gboolean
server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
{
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))
- if (prefs.hex_net_auto_reconnectonfail)
- 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))
- if (prefs.hex_net_auto_reconnectonfail)
- 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_storage addr;
- int addr_len = sizeof (addr);
- guint16 port;
- ircnet *net = serv->network;
- if (!getsockname (serv->sok, (struct sockaddr *)&addr, &addr_len))
- {
- if (addr.ss_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 (net && net->user && !(net->flags & FLAG_USE_GLOBAL))
- g_strlcat (outbuf, net->user, sizeof (outbuf));
- else
- g_strlcat (outbuf, prefs.hex_irc_user_name, sizeof (outbuf));
-
- handle_command (serv->server_session, outbuf, FALSE);
- }
- }
-
- server_connect_success (serv);
- break;
- case '5': /* prefs ip discovered */
- waitline2 (source, tbuf, sizeof tbuf);
- prefs.local_ip = inet_addr (tbuf);
- break;
case '7': /* gethostbyname (prefs.hex_net_bind_host) failed */
sprintf (outbuf,
_("Cannot resolve hostname %s\nCheck your IP Settings!\n"),
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;
}
+#endif
+
/* kill all sockets & iotags of a server. Stop a connection attempt, or
disconnect if already connected. */
{
fe_set_lag (serv, 0);
- if (serv->iotag)
+ if (serv->socket_read_source)
{
- fe_input_remove (serv->iotag);
- serv->iotag = 0;
+ g_clear_pointer (&serv->socket_read_source, g_source_destroy);
}
-
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);
- serv->ssl = NULL;
- }
-#endif
+ g_clear_object (&serv->socket_client);
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;
}
static void
-server_disconnect (session * sess, int sendquit, int err)
+server_disconnect (session * sess, int sendquit, GError *err)
{
server *serv = sess->server;
GSList *list;
- char tbuf[64];
gboolean shutup = FALSE;
/* send our QUIT reason */
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);
+ EMIT_SIGNAL (XP_TE_STOPCONNECT, sess, NULL, NULL, NULL, NULL, 0);
return;
case 3:
shutup = TRUE; /* won't print "disconnected" in channels */
{
if (!shutup || sess->type == SESS_SERVER)
/* print "Disconnected" to each window using this server */
- EMIT_SIGNAL (XP_TE_DISCON, sess, errorstring (err), NULL, NULL, NULL, 0);
+ EMIT_SIGNAL (XP_TE_DISCON, sess, err ? err->message : NULL, NULL, NULL, NULL, 0);
if (!sess->channel[0] || sess->type == SESS_CHANNEL)
clear_channel (sess);
/* send a "print text" command to the parent process - MUST END IN \n! */
-static void
-proxy_error (int fd, char *msg)
-{
- write (fd, "0\n", 2);
- write (fd, msg, strlen (msg));
-}
-
-struct sock_connect
-{
- char version;
- char type;
- guint16 port;
- guint32 address;
- char username[10];
-};
-
-/* traverse_socks() returns:
- * 0 success *
- * 1 socks traversal failed */
-
-static int
-traverse_socks (int print_fd, int sok, char *serverAddr, int port)
-{
- struct sock_connect sc;
- unsigned char buf[256];
-
- sc.version = 4;
- sc.type = 1;
- sc.port = htons (port);
- sc.address = inet_addr (serverAddr);
- g_strlcpy (sc.username, prefs.hex_irc_user_name, sizeof (sc.username));
-
- send (sok, (char *) &sc, 8 + strlen (sc.username) + 1, 0);
- buf[1] = 0;
- recv (sok, buf, 10, 0);
- if (buf[1] == 90)
- return 0;
-
- g_snprintf (buf, sizeof (buf), "SOCKS\tServer reported error %d,%d.\n", buf[0], buf[1]);
- proxy_error (print_fd, buf);
- return 1;
-}
-
-struct sock5_connect1
-{
- char version;
- char nmethods;
- char method;
-};
-
-static int
-traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
-{
- struct sock5_connect1 sc1;
- unsigned char *sc2;
- unsigned int packetlen, addrlen;
- unsigned char buf[260];
- int auth = prefs.hex_net_proxy_auth && prefs.hex_net_proxy_user[0] && prefs.hex_net_proxy_pass[0];
-
- sc1.version = 5;
- sc1.nmethods = 1;
- if (auth)
- sc1.method = 2; /* Username/Password Authentication (UPA) */
- else
- sc1.method = 0; /* NO Authentication */
- send (sok, (char *) &sc1, 3, 0);
- if (recv (sok, buf, 2, 0) != 2)
- goto read_error;
-
- if (buf[0] != 5)
- {
- proxy_error (print_fd, "SOCKS\tServer is not socks version 5.\n");
- return 1;
- }
-
- /* did the server say no auth required? */
- if (buf[1] == 0)
- auth = 0;
-
- if (auth)
- {
- int len_u=0, len_p=0;
- unsigned char *u_p_buf;
-
- /* authentication sub-negotiation (RFC1929) */
- if (buf[1] != 2) /* UPA not supported by server */
- {
- proxy_error (print_fd, "SOCKS\tServer doesn't support UPA authentication.\n");
- return 1;
- }
-
- /* form the UPA request */
- len_u = strlen (prefs.hex_net_proxy_user);
- len_p = strlen (prefs.hex_net_proxy_pass);
-
- packetlen = 2 + len_u + 1 + len_p;
- u_p_buf = g_malloc0 (packetlen);
-
- u_p_buf[0] = 1;
- u_p_buf[1] = len_u;
- memcpy (u_p_buf + 2, prefs.hex_net_proxy_user, len_u);
- u_p_buf[2 + len_u] = len_p;
- memcpy (u_p_buf + 3 + len_u, prefs.hex_net_proxy_pass, len_p);
-
- send (sok, u_p_buf, packetlen, 0);
- g_free(u_p_buf);
-
- if ( recv (sok, buf, 2, 0) != 2 )
- goto read_error;
- if ( buf[1] != 0 )
- {
- proxy_error (print_fd, "SOCKS\tAuthentication failed. "
- "Is username and password correct?\n");
- return 1; /* UPA failed! */
- }
- }
- else
- {
- if (buf[1] != 0)
- {
- proxy_error (print_fd, "SOCKS\tAuthentication required but disabled in settings.\n");
- return 1;
- }
- }
-
- addrlen = strlen (serverAddr);
- packetlen = 4 + 1 + addrlen + 2;
- sc2 = g_malloc (packetlen);
- sc2[0] = 5; /* version */
- sc2[1] = 1; /* command */
- sc2[2] = 0; /* reserved */
- sc2[3] = 3; /* address type */
- sc2[4] = (unsigned char) addrlen; /* hostname length */
- memcpy (sc2 + 5, serverAddr, addrlen);
- *((unsigned short *) (sc2 + 5 + addrlen)) = htons (port);
- send (sok, sc2, packetlen, 0);
- g_free (sc2);
-
- /* consume all of the reply */
- if (recv (sok, buf, 4, 0) != 4)
- goto read_error;
- if (buf[0] != 5 || buf[1] != 0)
- {
- if (buf[1] == 2)
- g_snprintf (buf, sizeof (buf), "SOCKS\tProxy refused to connect to host (not allowed).\n");
- else
- g_snprintf (buf, sizeof (buf), "SOCKS\tProxy failed to connect to host (error %d).\n", buf[1]);
- proxy_error (print_fd, buf);
- return 1;
- }
- if (buf[3] == 1) /* IPV4 32bit address */
- {
- if (recv (sok, buf, 6, 0) != 6)
- goto read_error;
- } else if (buf[3] == 4) /* IPV6 128bit address */
- {
- if (recv (sok, buf, 18, 0) != 18)
- goto read_error;
- } else if (buf[3] == 3) /* string, 1st byte is size */
- {
- if (recv (sok, buf, 1, 0) != 1) /* read the string size */
- goto read_error;
- packetlen = buf[0] + 2; /* can't exceed 260 */
- if (recv (sok, buf, packetlen, 0) != packetlen)
- goto read_error;
- }
-
- return 0; /* success */
-
-read_error:
- proxy_error (print_fd, "SOCKS\tRead error from server.\n");
- return 1;
-}
-
-static int
-traverse_wingate (int print_fd, int sok, char *serverAddr, int port)
-{
- char buf[128];
-
- g_snprintf (buf, sizeof (buf), "%s %d\r\n", serverAddr, port);
- send (sok, buf, strlen (buf), 0);
-
- return 0;
-}
-
-/* stuff for HTTP auth is here */
-
-static void
-three_to_four (char *from, char *to)
-{
- static const char tab64[64]=
- {
- 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
- 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
- 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
- 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
- };
-
- to[0] = tab64 [ (from[0] >> 2) & 63 ];
- to[1] = tab64 [ ((from[0] << 4) | (from[1] >> 4)) & 63 ];
- to[2] = tab64 [ ((from[1] << 2) | (from[2] >> 6)) & 63 ];
- to[3] = tab64 [ from[2] & 63 ];
-};
-
-void
-base64_encode (char *to, char *from, unsigned int len)
-{
- while (len >= 3)
- {
- three_to_four (from, to);
- len -= 3;
- from += 3;
- to += 4;
- }
- if (len)
- {
- char three[3] = {0,0,0};
- unsigned int i;
- for (i = 0; i < len; i++)
- {
- three[i] = *from++;
- }
- three_to_four (three, to);
- if (len == 1)
- {
- to[2] = to[3] = '=';
- }
- else if (len == 2)
- {
- to[3] = '=';
- }
- to += 4;
- };
- to[0] = 0;
-}
-
-static int
-http_read_line (int print_fd, int sok, char *buf, int len)
-{
- len = waitline (sok, buf, len, TRUE);
- if (len >= 1)
- {
- /* print the message out (send it to the parent process) */
- write (print_fd, "0\n", 2);
-
- if (buf[len-1] == '\r')
- {
- buf[len-1] = '\n';
- write (print_fd, buf, len);
- } else
- {
- write (print_fd, buf, len);
- write (print_fd, "\n", 1);
- }
- }
-
- return len;
-}
-
-static int
-traverse_http (int print_fd, int sok, char *serverAddr, int port)
-{
- char buf[512];
- char auth_data[256];
- char auth_data2[252];
- int n, n2;
-
- n = g_snprintf (buf, sizeof (buf), "CONNECT %s:%d HTTP/1.0\r\n",
- serverAddr, port);
- if (prefs.hex_net_proxy_auth)
- {
- n2 = g_snprintf (auth_data2, sizeof (auth_data2), "%s:%s",
- prefs.hex_net_proxy_user, prefs.hex_net_proxy_pass);
- base64_encode (auth_data, auth_data2, n2);
- n += g_snprintf (buf+n, sizeof (buf)-n, "Proxy-Authorization: Basic %s\r\n", auth_data);
- }
- n += g_snprintf (buf+n, sizeof (buf)-n, "\r\n");
- send (sok, buf, n, 0);
-
- n = http_read_line (print_fd, sok, buf, sizeof (buf));
- /* "HTTP/1.0 200 OK" */
- if (n < 12)
- return 1;
- if (memcmp (buf, "HTTP/", 5) || memcmp (buf + 9, "200", 3))
- return 1;
- while (1)
- {
- /* read until blank line */
- n = http_read_line (print_fd, sok, buf, sizeof (buf));
- if (n < 1 || (n == 1 && buf[0] == '\n'))
- break;
- }
- return 0;
-}
-
-static int
-traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
-{
- switch (proxy_type)
- {
- case 1:
- return traverse_wingate (print_fd, sok, ip, port);
- case 2:
- return traverse_socks (print_fd, sok, ip, port);
- case 3:
- return traverse_socks5 (print_fd, sok, ip, port);
- case 4:
- return traverse_http (print_fd, sok, ip, port);
- }
-
- return 1;
-}
-
/* this is the child process making the connection attempt */
+#if 0
static int
server_child (server * serv)
ns_server = net_store_new ();
- /* is a hostname set? - bind to it */
- if (prefs.hex_net_bind_host[0])
- {
- ns_local = net_store_new ();
- local_ip = net_resolve (ns_local, prefs.hex_net_bind_host, 0, &real_hostname);
- if (local_ip != NULL)
- {
- g_snprintf (buf, sizeof (buf), "5\n%s\n", local_ip);
- write (serv->childwrite, buf, strlen (buf));
- net_bind (ns_local, serv->sok4, serv->sok6);
- bound = 1;
- } else
- {
- write (serv->childwrite, "7\n", 2);
- }
- net_store_destroy (ns_local);
- }
-
if (!serv->dont_use_proxy) /* blocked in serverlist? */
{
if (prefs.hex_net_proxy_type == 5)
}
}
+
serv->proxy_type = proxy_type;
/* first resolve where we want to connect to */
return 0;
/* cppcheck-suppress memleak */
}
+#endif
static void
-server_connect (server *serv, char *hostname, int port, int no_login)
+on_client_network_event (GSocketClient *client,
+ GSocketClientEvent event,
+ GSocketConnectable *connectable,
+ GIOStream *connection,
+ gpointer user_data)
{
- int pid, read_des[2];
- session *sess = serv->server_session;
+ session *sess = user_data;
+
+ // TODO: prefs.local_ip = inet_addr (tbuf);
-#ifdef USE_OPENSSL
- if (!serv->ctx && serv->use_ssl)
+ switch (event)
{
- if (!(serv->ctx = _SSL_context_init (ssl_cb_info)))
+ case G_SOCKET_CLIENT_RESOLVING:
+ EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, (char*)g_network_address_get_hostname (G_NETWORK_ADDRESS (connectable)),
+ NULL, NULL, NULL, 0);
+ break;
+ case G_SOCKET_CLIENT_RESOLVED:
+ break;
+ case G_SOCKET_CLIENT_CONNECTING: {
+ GInetSocketAddress *remote_isaddr = G_INET_SOCKET_ADDRESS (g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (connection), NULL));
+
+ g_warn_if_fail (remote_isaddr != NULL);
+ if (remote_isaddr)
{
- fprintf (stderr, "_SSL_context_init failed\n");
- exit (1);
+ GInetAddress *remote_iaddr = G_INET_ADDRESS (g_inet_socket_address_get_address (remote_isaddr));
+ char *remote_iaddr_string = g_inet_address_to_string (remote_iaddr);
+ char *hostname = (char*)g_network_address_get_hostname (G_NETWORK_ADDRESS (connectable));
+ char port_str[16];
+ g_snprintf (port_str, sizeof (port_str), "%"G_GUINT16_FORMAT, g_inet_socket_address_get_port (remote_isaddr));
+
+ EMIT_SIGNAL (XP_TE_CONNECT, sess, hostname, remote_iaddr_string, port_str, NULL, 0);
+ g_free (remote_iaddr_string);
+ g_object_unref (remote_isaddr);
}
+ break;
}
-#endif
+ case G_SOCKET_CLIENT_TLS_HANDSHAKED: {
+ // TODO: Print out certificate information
+ // EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
+ // NULL, 0);
+ break;
+ }
+ case G_SOCKET_CLIENT_CONNECTED: {
+ char buf[512];
+ ircnet *net = sess->server->network;
+ GInetSocketAddress *remote_isaddr = G_INET_SOCKET_ADDRESS (g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (connection), NULL));
+
+ g_assert (remote_isaddr);
+
+ g_snprintf (buf, sizeof (buf), "IDENTD %"G_GUINT16_FORMAT" ", g_inet_socket_address_get_port (remote_isaddr));
+ if (net && net->user && !(net->flags & FLAG_USE_GLOBAL))
+ g_strlcat (buf, net->user, sizeof (buf));
+ else
+ g_strlcat (buf, prefs.hex_irc_user_name, sizeof (buf));
+
+ handle_command (sess->server->server_session, buf, FALSE);
+
+ g_object_unref (remote_isaddr);
+ break;
+ }
+ }
+}
+
+static void
+on_client_connect_ready (GSocketClient *client, GAsyncResult *res, gpointer user_data)
+{
+ GSocketConnection *conn;
+ GError *error = NULL;
+ server *serv = user_data;
+
+ conn = g_socket_client_connect_finish (client, res, &error);
+ if (error)
+ {
+ g_debug ("Failed to connect: %s", error->message);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ server_stopconnecting (serv);
+ if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND))
+ {
+ EMIT_SIGNAL (XP_TE_UKNHOST, serv->server_session, NULL, NULL, NULL, NULL, 0);
+ }
+ else if (error->domain == G_TLS_ERROR)
+ {
+ EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, error->message, NULL, NULL, NULL, 0);
+ }
+ else
+ {
+ EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, error->message, NULL, NULL, NULL, 0);
+ }
+
+
+ if (!servlist_cycle (serv))
+ if (prefs.hex_net_auto_reconnectonfail)
+ auto_reconnect (serv, FALSE, error);
+
+ g_error_free (error);
+ return;
+ }
+
+ serv->socket_conn = g_steal_pointer (&conn);
+ serv->socket = g_socket_connection_get_socket (serv->socket_conn);
+ g_socket_set_blocking (serv->socket, FALSE);
+
+ server_connect_success (serv);
+}
+
+static void
+server_connect (server *serv, char *hostname, int port, int no_login)
+{
+ session *sess = serv->server_session;
+ GSocketConnectable *connectable;
if (!hostname[0])
+ {
+ g_debug ("server_connect: no hostname");
return;
+ }
+
+ if (port <= 0 || port > G_MAXUINT16)
+ port = serv->use_ssl ? 6697 : 6667;
+
+ connectable = g_network_address_new (hostname, port);
+
+ serv->socket_client = g_socket_client_new ();
+ g_socket_client_set_tls (serv->socket_client, serv->use_ssl);
+ g_socket_client_set_enable_proxy (serv->socket_client, !serv->dont_use_proxy);
+ g_socket_client_set_timeout (serv->socket_client, 60); // FIXME
+ g_signal_connect (serv->socket_client, "event", G_CALLBACK (on_client_network_event), sess);
- if (port < 0)
+ if (prefs.hex_net_bind_host[0])
{
- /* use default port for this server type */
- port = 6667;
-#ifdef USE_OPENSSL
- if (serv->use_ssl)
- port = 6697;
-#endif
+ GSocketAddress *local_addr = g_inet_socket_address_new_from_string (prefs.hex_net_bind_host, 0);
+ g_socket_client_set_local_address (serv->socket_client, local_addr);
+ g_object_unref (local_addr);
+ }
+
+
+ if (!serv->dont_use_proxy)
+ {
+ //GProxyResolver *proxy_resolver = g_proxy_resolver_get_default ();
+ // TODO:
}
- port &= 0xffff; /* wrap around */
- if (serv->connected || serv->connecting || serv->recondelay_tag)
- server_disconnect (sess, TRUE, -1);
+ // if (serv->connected || serv->connecting || serv->recondelay_tag)
+ // server_disconnect (sess, TRUE, -1);
fe_progressbar_start (sess);
- EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, hostname, NULL, NULL, NULL, 0);
-
safe_strcpy (serv->servername, hostname, sizeof (serv->servername));
/* overlap illegal in strncpy */
if (hostname != serv->hostname)
safe_strcpy (serv->hostname, hostname, sizeof (serv->hostname));
-#ifdef USE_OPENSSL
if (serv->use_ssl)
{
- char *cert_file;
+ char *certificate_paths[2];
+ GError *error = NULL;
serv->have_cert = FALSE;
+ guint i;
/* first try network specific cert/key */
- cert_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem",
- get_xdir (), server_get_network (serv, TRUE));
- if (SSL_CTX_use_certificate_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
- {
- if (SSL_CTX_use_PrivateKey_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
- serv->have_cert = TRUE;
- }
- else
+ certificate_paths[0] = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem",
+ get_xdir (), server_get_network (serv, TRUE));
+ /* if that doesn't exist, try <config>/certs/client.pem */
+ certificate_paths[1] = g_build_filename (get_xdir (), "certs", "client.pem", NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (certificate_paths); i++)
{
- /* if that doesn't exist, try <config>/certs/client.pem */
- cert_file = g_build_filename (get_xdir (), "certs", "client.pem", NULL);
- if (SSL_CTX_use_certificate_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
+ serv->client_cert = g_tls_certificate_new_from_file (certificate_paths[i], &error);
+ if (error)
{
- if (SSL_CTX_use_PrivateKey_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
- serv->have_cert = TRUE;
+ g_debug ("Failed opening client cert (%s): %s", certificate_paths[i], error->message);
+ g_clear_error (&error);
+ continue;
}
+
+ serv->have_cert = TRUE;
+ break;
}
- g_free (cert_file);
+
+ g_free (certificate_paths[0]);
+ g_free (certificate_paths[1]);
}
-#endif
server_set_defaults (serv);
serv->connecting = TRUE;
fe_set_away (serv);
server_flush_queue (serv);
-#ifdef WIN32
- if (_pipe (read_des, 4096, _O_BINARY) < 0)
-#else
- if (pipe (read_des) < 0)
-#endif
- return;
-#ifdef __EMX__ /* os/2 */
- setmode (read_des[0], O_BINARY);
- setmode (read_des[1], O_BINARY);
-#endif
- serv->childread = read_des[0];
- serv->childwrite = read_des[1];
-
- /* create both sockets now, drop one later */
- net_sockets (&serv->sok4, &serv->sok6);
- serv->proxy_sok4 = -1;
- serv->proxy_sok6 = -1;
-
-#ifdef WIN32
- CloseHandle (CreateThread (NULL, 0,
- (LPTHREAD_START_ROUTINE)server_child,
- serv, 0, (DWORD *)&pid));
-#else
-#ifdef LOOKUPD
- /* CL: net_resolve calls rand() when LOOKUPD is set, so prepare a different
- * seed for each child. This method gives a bigger variation in seed values
- * than calling srand(time(0)) in the child itself.
- */
- rand();
-#endif
- switch (pid = fork ())
- {
- case -1:
- return;
-
- case 0:
- /* this is the child */
- setuid (getuid ());
- server_child (serv);
- _exit (0);
- }
-#endif
- serv->childpid = pid;
-#ifdef WIN32
- serv->iotag = fe_input_add (serv->childread, FIA_READ|FIA_FD, server_read_child,
-#else
- serv->iotag = fe_input_add (serv->childread, FIA_READ, server_read_child,
-#endif
- serv);
+ g_socket_client_connect_async (serv->socket_client, connectable, NULL, (GAsyncReadyCallback)on_client_connect_ready, serv);
}
void
server_fill_her_up (serv);
serv->id = id++;
- serv->sok = -1;
strcpy (serv->nick, prefs.hex_irc_nick1);
server_set_defaults (serv);
if (serv->favlist)
g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free);
-#ifdef USE_OPENSSL
- if (serv->ctx)
- _SSL_context_free (serv->ctx);
-#endif
fe_server_callback (serv);
+++ /dev/null
-/*
- * ssl.c v0.0.3
- * Copyright (C) 2000 -- DaP <profeta@freemail.c3.hu>
- *
- * 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
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#ifdef __APPLE__
-#define __AVAILABILITYMACROS__
-#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
-#endif
-
-#include "inet.h" /* make it first to avoid macro redefinitions */
-#include <openssl/ssl.h> /* SSL_() */
-#include <openssl/err.h> /* ERR_() */
-#include <openssl/x509v3.h>
-#ifdef WIN32
-#include <openssl/rand.h> /* RAND_seed() */
-#endif
-#include "config.h"
-#include <time.h> /* asctime() */
-#include <string.h> /* strncpy() */
-#include "ssl.h" /* struct cert_info */
-
-#include <glib.h>
-#include <glib/gprintf.h>
-#include <gio/gio.h>
-#include "util.h"
-
-/* If openssl was built without ec */
-#ifndef SSL_OP_SINGLE_ECDH_USE
-#define SSL_OP_SINGLE_ECDH_USE 0
-#endif
-
-#ifndef SSL_OP_NO_COMPRESSION
-#define SSL_OP_NO_COMPRESSION 0
-#endif
-
-/* globals */
-static struct chiper_info chiper_info; /* static buffer for _SSL_get_cipher_info() */
-static char err_buf[256]; /* generic error buffer */
-
-
-/* +++++ Internal functions +++++ */
-
-static void
-__SSL_fill_err_buf (char *funcname)
-{
- int err;
- char buf[256];
-
-
- err = ERR_get_error ();
- ERR_error_string (err, buf);
- g_snprintf (err_buf, sizeof (err_buf), "%s: %s (%d)\n", funcname, buf, err);
-}
-
-
-static void
-__SSL_critical_error (char *funcname)
-{
- __SSL_fill_err_buf (funcname);
- fprintf (stderr, "%s\n", err_buf);
-
- exit (1);
-}
-
-/* +++++ SSL functions +++++ */
-
-SSL_CTX *
-_SSL_context_init (void (*info_cb_func))
-{
- SSL_CTX *ctx;
-
- SSLeay_add_ssl_algorithms ();
- SSL_load_error_strings ();
- ctx = SSL_CTX_new (SSLv23_client_method ());
-
- SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_BOTH);
- SSL_CTX_set_timeout (ctx, 300);
- SSL_CTX_set_options (ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3
- |SSL_OP_NO_COMPRESSION
- |SSL_OP_SINGLE_DH_USE|SSL_OP_SINGLE_ECDH_USE
- |SSL_OP_NO_TICKET
- |SSL_OP_CIPHER_SERVER_PREFERENCE);
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined (OPENSSL_NO_COMP) /* workaround for OpenSSL 0.9.8 */
- sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
-#endif
-
- /* used in SSL_connect(), SSL_accept() */
- SSL_CTX_set_info_callback (ctx, info_cb_func);
-
- return(ctx);
-}
-
-static void
-ASN1_TIME_snprintf (char *buf, int buf_len, ASN1_TIME * tm)
-{
- char *expires = NULL;
- BIO *inMem = BIO_new (BIO_s_mem ());
-
- ASN1_TIME_print (inMem, tm);
- BIO_get_mem_data (inMem, &expires);
- buf[0] = 0;
- if (expires != NULL)
- {
- /* expires is not \0 terminated */
- safe_strcpy (buf, expires, MIN(24, buf_len));
- }
- BIO_free (inMem);
-}
-
-
-static void
-broke_oneline (char *oneline, char *parray[])
-{
- char *pt, *ppt;
- int i;
-
-
- i = 0;
- ppt = pt = oneline + 1;
- while ((pt = strchr (pt, '/')))
- {
- *pt = 0;
- parray[i++] = ppt;
- ppt = ++pt;
- }
- parray[i++] = ppt;
- parray[i] = NULL;
-}
-
-
-/*
- FIXME: Master-Key, Extensions, CA bits
- (openssl x509 -text -in servcert.pem)
-*/
-int
-_SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl)
-{
- X509 *peer_cert;
- X509_PUBKEY *key;
- X509_ALGOR *algor = NULL;
- EVP_PKEY *peer_pkey;
- char notBefore[64];
- char notAfter[64];
- int alg;
- int sign_alg;
-
-
- if (!(peer_cert = SSL_get_peer_certificate (ssl)))
- return (1); /* FATAL? */
-
- X509_NAME_oneline (X509_get_subject_name (peer_cert), cert_info->subject,
- sizeof (cert_info->subject));
- X509_NAME_oneline (X509_get_issuer_name (peer_cert), cert_info->issuer,
- sizeof (cert_info->issuer));
- broke_oneline (cert_info->subject, cert_info->subject_word);
- broke_oneline (cert_info->issuer, cert_info->issuer_word);
-
- key = X509_get_X509_PUBKEY(peer_cert);
- if (!X509_PUBKEY_get0_param(NULL, NULL, 0, &algor, key))
- return 1;
-
- alg = OBJ_obj2nid (algor->algorithm);
-#ifndef HAVE_X509_GET_SIGNATURE_NID
- sign_alg = OBJ_obj2nid (peer_cert->sig_alg->algorithm);
-#else
- sign_alg = X509_get_signature_nid (peer_cert);
-#endif
- ASN1_TIME_snprintf (notBefore, sizeof (notBefore),
- X509_get_notBefore (peer_cert));
- ASN1_TIME_snprintf (notAfter, sizeof (notAfter),
- X509_get_notAfter (peer_cert));
-
- peer_pkey = X509_get_pubkey (peer_cert);
-
- safe_strcpy (cert_info->algorithm,
- (alg == NID_undef) ? "Unknown" : OBJ_nid2ln (alg),
- sizeof (cert_info->algorithm));
- cert_info->algorithm_bits = EVP_PKEY_bits (peer_pkey);
- safe_strcpy (cert_info->sign_algorithm,
- (sign_alg == NID_undef) ? "Unknown" : OBJ_nid2ln (sign_alg),
- sizeof (cert_info->sign_algorithm));
- /* EVP_PKEY_bits(ca_pkey)); */
- cert_info->sign_algorithm_bits = 0;
- safe_strcpy (cert_info->notbefore, notBefore, sizeof (cert_info->notbefore));
- safe_strcpy (cert_info->notafter, notAfter, sizeof (cert_info->notafter));
-
- EVP_PKEY_free (peer_pkey);
-
- /* SSL_SESSION_print_fp(stdout, SSL_get_session(ssl)); */
-/*
- if (ssl->session->sess_cert->peer_rsa_tmp) {
- tmp_pkey = EVP_PKEY_new();
- EVP_PKEY_assign_RSA(tmp_pkey, ssl->session->sess_cert->peer_rsa_tmp);
- cert_info->rsa_tmp_bits = EVP_PKEY_bits (tmp_pkey);
- EVP_PKEY_free(tmp_pkey);
- } else
- fprintf(stderr, "REMOTE SIDE DOESN'T PROVIDES ->peer_rsa_tmp\n");
-*/
- cert_info->rsa_tmp_bits = 0;
-
- X509_free (peer_cert);
-
- return (0);
-}
-
-
-struct chiper_info *
-_SSL_get_cipher_info (SSL * ssl)
-{
- const SSL_CIPHER *c;
-
-
- c = SSL_get_current_cipher (ssl);
- safe_strcpy (chiper_info.version, SSL_CIPHER_get_version (c),
- sizeof (chiper_info.version));
- safe_strcpy (chiper_info.chiper, SSL_CIPHER_get_name (c),
- sizeof (chiper_info.chiper));
- SSL_CIPHER_get_bits (c, &chiper_info.chiper_bits);
-
- return (&chiper_info);
-}
-
-
-int
-_SSL_send (SSL * ssl, char *buf, int len)
-{
- int num;
-
-
- num = SSL_write (ssl, buf, len);
-
- switch (SSL_get_error (ssl, num))
- {
- case SSL_ERROR_SSL: /* setup errno! */
- /* ??? */
- __SSL_fill_err_buf ("SSL_write");
- fprintf (stderr, "%s\n", err_buf);
- break;
- case SSL_ERROR_SYSCALL:
- /* ??? */
- perror ("SSL_write/write");
- break;
- case SSL_ERROR_ZERO_RETURN:
- /* fprintf(stderr, "SSL closed on write\n"); */
- break;
- }
-
- return (num);
-}
-
-
-int
-_SSL_recv (SSL * ssl, char *buf, int len)
-{
- int num;
-
-
- num = SSL_read (ssl, buf, len);
-
- switch (SSL_get_error (ssl, num))
- {
- case SSL_ERROR_SSL:
- /* ??? */
- __SSL_fill_err_buf ("SSL_read");
- fprintf (stderr, "%s\n", err_buf);
- break;
- case SSL_ERROR_SYSCALL:
- /* ??? */
- if (!would_block ())
- perror ("SSL_read/read");
- break;
- case SSL_ERROR_ZERO_RETURN:
- /* fprintf(stdeerr, "SSL closed on read\n"); */
- break;
- }
-
- return (num);
-}
-
-
-SSL *
-_SSL_socket (SSL_CTX *ctx, int sd)
-{
- SSL *ssl;
- const SSL_METHOD *method;
-
- if (!(ssl = SSL_new (ctx)))
- /* FATAL */
- __SSL_critical_error ("SSL_new");
-
- SSL_set_fd (ssl, sd);
-
-#ifndef HAVE_SSL_CTX_GET_SSL_METHOD
- method = ctx->method;
-#else
- method = SSL_CTX_get_ssl_method (ctx);
-#endif
- if (method == SSLv23_client_method())
- SSL_set_connect_state (ssl);
- else
- SSL_set_accept_state(ssl);
-
- return (ssl);
-}
-
-
-char *
-_SSL_set_verify (SSL_CTX *ctx, void *verify_callback, char *cacert)
-{
- if (!SSL_CTX_set_default_verify_paths (ctx))
- {
- __SSL_fill_err_buf ("SSL_CTX_set_default_verify_paths");
- return (err_buf);
- }
-/*
- if (cacert)
- {
- if (!SSL_CTX_load_verify_locations (ctx, cacert, NULL))
- {
- __SSL_fill_err_buf ("SSL_CTX_load_verify_locations");
- return (err_buf);
- }
- }
-*/
- SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, verify_callback);
-
- return (NULL);
-}
-
-
-void
-_SSL_close (SSL * ssl)
-{
- SSL_set_shutdown (ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
- SSL_free (ssl);
-#ifdef HAVE_ERR_REMOVE_THREAD_STATE
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L
- /* OpenSSL handles this itself in 1.1+ and this is a no-op */
- ERR_remove_thread_state (NULL);
-#endif
-#else
- ERR_remove_state (0);
-#endif
-}
-
-/* Hostname validation code based on OpenBSD's libtls. */
-
-static int
-_SSL_match_hostname (const char *cert_hostname, const char *hostname)
-{
- const char *cert_domain, *domain, *next_dot;
-
- if (g_ascii_strcasecmp (cert_hostname, hostname) == 0)
- return 0;
-
- /* Wildcard match? */
- if (cert_hostname[0] == '*')
- {
- /*
- * Valid wildcards:
- * - "*.domain.tld"
- * - "*.sub.domain.tld"
- * - etc.
- * Reject "*.tld".
- * No attempt to prevent the use of eg. "*.co.uk".
- */
- cert_domain = &cert_hostname[1];
- /* Disallow "*" */
- if (cert_domain[0] == '\0')
- return -1;
- /* Disallow "*foo" */
- if (cert_domain[0] != '.')
- return -1;
- /* Disallow "*.." */
- if (cert_domain[1] == '.')
- return -1;
- next_dot = strchr (&cert_domain[1], '.');
- /* Disallow "*.bar" */
- if (next_dot == NULL)
- return -1;
- /* Disallow "*.bar.." */
- if (next_dot[1] == '.')
- return -1;
-
- domain = strchr (hostname, '.');
-
- /* No wildcard match against a hostname with no domain part. */
- if (domain == NULL || strlen(domain) == 1)
- return -1;
-
- if (g_ascii_strcasecmp (cert_domain, domain) == 0)
- return 0;
- }
-
- return -1;
-}
-
-static int
-_SSL_check_subject_altname (X509 *cert, const char *host)
-{
- STACK_OF(GENERAL_NAME) *altname_stack = NULL;
- GInetAddress *addr;
- GSocketFamily family;
- int type = GEN_DNS;
- int count, i;
- int rv = -1;
-
- altname_stack = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
- if (altname_stack == NULL)
- return -1;
-
- addr = g_inet_address_new_from_string (host);
- if (addr != NULL)
- {
- family = g_inet_address_get_family (addr);
- if (family == G_SOCKET_FAMILY_IPV4 || family == G_SOCKET_FAMILY_IPV6)
- type = GEN_IPADD;
- }
-
- count = sk_GENERAL_NAME_num(altname_stack);
- for (i = 0; i < count; i++)
- {
- GENERAL_NAME *altname;
-
- altname = sk_GENERAL_NAME_value (altname_stack, i);
-
- if (altname->type != type)
- continue;
-
- if (type == GEN_DNS)
- {
- const unsigned char *data;
- int format;
-
- format = ASN1_STRING_type (altname->d.dNSName);
- if (format == V_ASN1_IA5STRING)
- {
-#ifdef HAVE_ASN1_STRING_GET0_DATA
- data = ASN1_STRING_get0_data (altname->d.dNSName);
-#else
- data = ASN1_STRING_data (altname->d.dNSName);
-#endif
-
- if (ASN1_STRING_length (altname->d.dNSName) != (int)strlen(data))
- {
- g_warning("NUL byte in subjectAltName, probably a malicious certificate.\n");
- rv = -2;
- break;
- }
-
- if (_SSL_match_hostname (data, host) == 0)
- {
- rv = 0;
- break;
- }
- }
- else
- g_warning ("unhandled subjectAltName dNSName encoding (%d)\n", format);
-
- }
- else if (type == GEN_IPADD)
- {
- const unsigned char *data;
- const guint8 *addr_bytes;
- int datalen, addr_len;
-
- datalen = ASN1_STRING_length (altname->d.iPAddress);
-#ifdef HAVE_ASN1_STRING_GET0_DATA
- data = ASN1_STRING_get0_data (altname->d.iPAddress);
-#else
- data = ASN1_STRING_data (altname->d.iPAddress);
-#endif
-
- addr_bytes = g_inet_address_to_bytes (addr);
- addr_len = (int)g_inet_address_get_native_size (addr);
-
- if (datalen == addr_len && memcmp (data, addr_bytes, addr_len) == 0)
- {
- rv = 0;
- break;
- }
- }
- }
-
- if (addr != NULL)
- g_object_unref (addr);
- sk_GENERAL_NAME_pop_free (altname_stack, GENERAL_NAME_free);
- return rv;
-}
-
-static int
-_SSL_check_common_name (X509 *cert, const char *host)
-{
- X509_NAME *name;
- char *common_name = NULL;
- int common_name_len;
- int rv = -1;
- GInetAddress *addr;
-
- name = X509_get_subject_name (cert);
- if (name == NULL)
- return -1;
-
- common_name_len = X509_NAME_get_text_by_NID (name, NID_commonName, NULL, 0);
- if (common_name_len < 0)
- return -1;
-
- common_name = g_malloc0 (common_name_len + 1);
-
- X509_NAME_get_text_by_NID (name, NID_commonName, common_name, common_name_len + 1);
-
- /* NUL bytes in CN? */
- if (common_name_len != (int)strlen(common_name))
- {
- g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n");
- rv = -2;
- goto out;
- }
-
- if ((addr = g_inet_address_new_from_string (host)) != NULL)
- {
- /*
- * We don't want to attempt wildcard matching against IP
- * addresses, so perform a simple comparison here.
- */
- if (g_strcmp0 (common_name, host) == 0)
- rv = 0;
- else
- rv = -1;
-
- g_object_unref (addr);
- }
- else if (_SSL_match_hostname (common_name, host) == 0)
- rv = 0;
-
-out:
- g_free(common_name);
- return rv;
-}
-
-int
-_SSL_check_hostname (X509 *cert, const char *host)
-{
- int rv;
-
- rv = _SSL_check_subject_altname (cert, host);
- if (rv == 0 || rv == -2)
- return rv;
-
- return _SSL_check_common_name (cert, host);
-}