aboutsummaryrefslogtreecommitdiffstats
path: root/src/irc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc.c')
-rw-r--r--src/irc.c158
1 files changed, 126 insertions, 32 deletions
diff --git a/src/irc.c b/src/irc.c
index 77f48b4..cb34440 100644
--- a/src/irc.c
+++ b/src/irc.c
@@ -35,6 +35,11 @@
#include <stdarg.h>
#include <regex.h>
#include <assert.h>
+#ifdef HAVE_LIBCRYPTO
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#endif
#include "config.h"
#include "irc.h"
@@ -65,6 +70,11 @@ static socklen_t svr_addrlen;
static time_t IRC_LAST; /* Last full line of data from irc server */
static time_t IRC_LASTRECONNECT; /* Time of last reconnection */
+#ifdef HAVE_LIBCRYPTO
+static SSL_CTX *ssl_ctx;
+static SSL *ssl_handle;
+#endif
+
/* get_channel
*
@@ -524,9 +534,51 @@ irc_init(void)
freeaddrinfo(res);
}
+
+ if (IRCItem.tls)
+ {
+#ifdef HAVE_LIBCRYPTO
+ /* Initialize SSL */
+ static int tls_init = 0;
+ if (tls_init == 0)
+ {
+ tls_init = 1;
+
+ ssl_ctx = SSL_CTX_new(TLS_client_method());
+ if (ssl_ctx == NULL)
+ {
+ log_printf("IRC -> unable to create SSL context");
+ exit(EXIT_FAILURE);
+ }
+
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+
+ ssl_handle = SSL_new(ssl_ctx);
+ SSL_set_fd(ssl_handle, IRC_FD);
+
+ if (IRCItem.tls_hostname_verification)
+ {
+ SSL_set_hostflags(ssl_handle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+
+ if (SSL_set1_host(ssl_handle, IRCItem.server) == 0)
+ {
+ log_printf("IRC -> unable to set expected DNS hostname");
+ /* OpenSSL is unable to verify the server hostname at this point, so we exit. */
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ SSL_set_verify(ssl_handle, SSL_VERIFY_PEER, NULL);
+#else
+ log_printf("IRC -> HOPM is not compiled with OpenSSL support");
+ exit(EXIT_FAILURE);
+#endif
+ }
}
-/* irc_reconnect
+/* irc_close
*
* Close connection to IRC server.
*
@@ -535,24 +587,19 @@ irc_init(void)
* Return: NONE
*/
static void
-irc_reconnect(void)
+irc_close(void)
{
- time_t present;
-
- time(&present);
-
- /* Only try to reconnect every IRCItem.reconnectinterval seconds */
- if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
- {
- /* Sleep to avoid excessive CPU */
- sleep(1);
- return;
- }
-
- time(&IRC_LASTRECONNECT);
-
if (IRC_FD > -1)
{
+#ifdef HAVE_LIBCRYPTO
+ if (ssl_handle)
+ {
+ SSL_shutdown(ssl_handle);
+ SSL_free(ssl_handle);
+ ssl_handle = NULL;
+ }
+#endif
+
close(IRC_FD);
IRC_FD = -1; /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
}
@@ -571,20 +618,51 @@ irc_reconnect(void)
static void
irc_connect(void)
{
+ time_t present;
+
+ time(&present);
+
+ /* Only try to reconnect every IRCItem.reconnectinterval seconds */
+ if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
+ {
+ /* Sleep to avoid excessive CPU */
+ sleep(1);
+ return;
+ }
+
+ time(&IRC_LASTRECONNECT);
+ time(&IRC_LAST);
+
+ irc_init();
+
/* Connect to IRC server as client. */
if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, svr_addrlen) == -1)
{
log_printf("IRC -> connect(): error connecting to %s: %s",
IRCItem.server, strerror(errno));
- if (errno == EISCONN /* Already connected */ || errno == EALREADY /* Previous attempt not complete */)
- return;
-
- /* Try to connect again */
- irc_reconnect();
+ /* Close socket and try to connect again */
+ irc_close();
return;
}
+#ifdef HAVE_LIBCRYPTO
+ if (ssl_handle)
+ {
+ int ret = SSL_connect(ssl_handle);
+ if (ret != 1)
+ {
+ const char *error = ERR_error_string(ERR_get_error(), NULL);
+ log_printf("IRC -> connect(): error performing TLS handshake with %s: %s",
+ IRCItem.server, error);
+
+ /* Close socket and try to connect again */
+ irc_close();
+ return;
+ }
+ }
+#endif
+
irc_send("NICK %s", IRCItem.nick);
if (!EmptyString(IRCItem.password))
@@ -619,6 +697,7 @@ irc_parse(void)
* source did not exist
*/
char *parv[17];
+ static const unsigned int parv_size = sizeof(parv) / sizeof(parv[0]);
unsigned int parc = 1;
char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
@@ -670,7 +749,7 @@ irc_parse(void)
pos = IRC_RAW;
- while ((pos = strchr(pos, ' ')) && parc <= 17)
+ while ((pos = strchr(pos, ' ')) && parc < parv_size)
{
/* Avoid excessive spaces and end of IRC_RAW */
if (*(pos + 1) == ' ' || *(pos + 1) == '\0')
@@ -724,8 +803,18 @@ irc_read(void)
ssize_t len;
char c;
- while ((len = read(IRC_FD, &c, 1)) > 0)
+ while (1)
{
+#ifdef HAVE_LIBCRYPTO
+ if (ssl_handle)
+ len = SSL_read(ssl_handle, &c, 1);
+ else
+#endif
+ len = recv(IRC_FD, &c, 1, 0);
+
+ if (len <= 0)
+ break;
+
if (c == '\r')
continue;
@@ -751,7 +840,7 @@ irc_read(void)
if (errno != EINTR)
log_printf("IRC -> Error reading data from server: %s", strerror(errno));
- irc_reconnect();
+ irc_close();
IRC_RAW_LEN = 0;
return;
}
@@ -778,9 +867,6 @@ irc_cycle(void)
if (OptionsItem.negcache)
negcache_init();
- /* Resolve remote host. */
- irc_init();
-
/* Connect to remote host. */
irc_connect();
return; /* In case connect() immediately failed */
@@ -800,7 +886,7 @@ irc_cycle(void)
if (pfd.revents & POLLIN)
irc_read();
else if (pfd.revents & (POLLERR | POLLHUP))
- irc_reconnect();
+ irc_close();
break;
}
@@ -823,7 +909,7 @@ irc_send(const char *data, ...)
char buf[MSGLENMAX];
va_start(arglist, data);
- size_t len = vsnprintf(buf, sizeof(buf), data, arglist);
+ ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
va_end(arglist);
if (OPT_DEBUG >= 2)
@@ -835,11 +921,19 @@ irc_send(const char *data, ...)
buf[len++] = '\r';
buf[len++] = '\n';
- if (send(IRC_FD, buf, len, 0) == -1)
+ ssize_t sent;
+#ifdef HAVE_LIBCRYPTO
+ if (ssl_handle)
+ sent = SSL_write(ssl_handle, buf, len);
+ else
+#endif
+ sent = send(IRC_FD, buf, len, 0);
+
+ if (sent != len)
{
/* Return of -1 indicates error sending data; we reconnect. */
log_printf("IRC -> Error sending data to server: %s", strerror(errno));
- irc_reconnect();
+ irc_close();
}
}
@@ -892,7 +986,7 @@ irc_timer(void)
if (delta >= IRCItem.readtimeout)
{
log_printf("IRC -> Timeout awaiting data from server.");
- irc_reconnect();
+ irc_close();
/* Make sure we don't do this again for a while */
time(&IRC_LAST);