diff options
Diffstat (limited to 'src/irc.c')
-rw-r--r-- | src/irc.c | 158 |
1 files changed, 126 insertions, 32 deletions
@@ -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); |