ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
(Generate patch)

Comparing hopm/trunk/src/irc.c (file contents):
Revision 8184 by michael, Thu Apr 13 20:00:32 2017 UTC vs.
Revision 9483 by michael, Sat Jul 4 19:12:39 2020 UTC

# Line 1 | Line 1
1   /*
2   *  Copyright (c) 2002-2003 Erik Fears
3 < *  Copyright (c) 2014-2017 ircd-hybrid development team
3 > *  Copyright (c) 2014-2020 ircd-hybrid development team
4   *
5   *  This program is free software; you can redistribute it and/or modify
6   *  it under the terms of the GNU General Public License as published by
# Line 35 | Line 35
35   #include <stdarg.h>
36   #include <regex.h>
37   #include <assert.h>
38 + #ifdef HAVE_LIBCRYPTO
39 + #include <openssl/ssl.h>
40 + #include <openssl/x509v3.h>
41 + #include <openssl/err.h>
42 + #endif
43  
44   #include "config.h"
45   #include "irc.h"
# Line 65 | Line 70 | static socklen_t svr_addrlen;
70   static time_t IRC_LAST;                  /* Last full line of data from irc server */
71   static time_t IRC_LASTRECONNECT;         /* Time of last reconnection              */
72  
73 + #ifdef HAVE_LIBCRYPTO
74 + static SSL_CTX *ssl_ctx;
75 + static SSL *ssl_handle;
76 + #endif
77 +
78  
79   /* get_channel
80   *
# Line 81 | Line 91 | get_channel(const char *name)
91   {
92    node_t *node;
93  
94 <  LIST_FOREACH(node, IRCItem->channels->head)
94 >  LIST_FOREACH(node, IRCItem.channels.head)
95    {
96      struct ChannelConf *item = node->data;
97  
# Line 109 | Line 119 | m_perform(char *parv[], unsigned int par
119   {
120    node_t *node;
121  
122 <  log_printf("IRC -> Connected to %s/%d", IRCItem->server, IRCItem->port);
122 >  log_printf("IRC -> Connected to %s/%d", IRCItem.server, IRCItem.port);
123  
124    /* Identify to nickserv if needed */
125 <  if (!EmptyString(IRCItem->nickserv))
126 <    irc_send("%s", IRCItem->nickserv);
125 >  if (!EmptyString(IRCItem.nickserv))
126 >    irc_send("%s", IRCItem.nickserv);
127  
128    /* Oper */
129 <  irc_send("OPER %s", IRCItem->oper);
129 >  irc_send("OPER %s", IRCItem.oper);
130  
131    /* Set modes */
132 <  irc_send("MODE %s %s", IRCItem->nick, IRCItem->mode);
132 >  irc_send("MODE %s %s", IRCItem.nick, IRCItem.mode);
133  
134    /* Set Away */
135 <  if (!EmptyString(IRCItem->away))
136 <    irc_send("AWAY :%s", IRCItem->away);
135 >  if (!EmptyString(IRCItem.away))
136 >    irc_send("AWAY :%s", IRCItem.away);
137  
138    /* Perform */
139 <  LIST_FOREACH(node, IRCItem->performs->head)
139 >  LIST_FOREACH(node, IRCItem.performs.head)
140      irc_send("%s", node->data);
141  
142    /* Join all listed channels. */
143 <  LIST_FOREACH(node, IRCItem->channels->head)
143 >  LIST_FOREACH(node, IRCItem.channels.head)
144    {
145      const struct ChannelConf *channel = node->data;
146  
# Line 178 | Line 188 | m_ping(char *parv[], unsigned int parc,
188   static void
189   m_invite(char *parv[], unsigned int parc, const char *msg, const char *source_p)
190   {
181  const struct ChannelConf *channel = NULL;
182
191    if (parc < 4)
192      return;
193  
194    log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
195  
196 <  if ((channel = get_channel(parv[3])) == NULL)
196 >  const struct ChannelConf *channel = get_channel(parv[3]);
197 >  if (channel == NULL)
198      return;
199  
200    irc_send("JOIN %s %s", channel->name, channel->key);
# Line 221 | Line 230 | m_ctcp(char *parv[], unsigned int parc,
230   static void
231   m_privmsg(char *parv[], unsigned int parc, const char *msg, const char *source_p)
232   {
224  const struct ChannelConf *channel = NULL;
225
233    if (source_p == NULL)
234      return;
235  
# Line 241 | Line 248 | m_privmsg(char *parv[], unsigned int par
248      return;
249  
250    /* Get a target */
251 <  if ((channel = get_channel(parv[2])) == NULL)
251 >  const struct ChannelConf *channel = get_channel(parv[2]);
252 >  if (channel == NULL)
253      return;
254  
255    int hit = strncasecmp(parv[3], "!all ", 5) == 0;
256    if (hit == 0)
257    {
258 <    size_t nick_len = strlen(IRCItem->nick);
258 >    size_t nick_len = strlen(IRCItem.nick);
259  
260 <    if (strncasecmp(parv[3], IRCItem->nick, nick_len) == 0)
261 <      hit = *(parv[3] + nick_len) == ' ';
260 >    if (strncasecmp(parv[3], IRCItem.nick, nick_len) == 0)
261 >      hit = *(parv[3] + nick_len) == ' ' ||
262 >            *(parv[3] + nick_len) == ',' ||
263 >            *(parv[3] + nick_len) == ':';
264    }
265  
266    if (hit)
267      /* XXX command_parse will alter parv[3]. */
268 <    command_parse(parv[3], channel, source_p);
268 >    command_parse(parv[3], channel->name, source_p);
269   }
270  
271   /* m_notice
# Line 290 | Line 300 | m_notice(char *parv[], unsigned int parc
300    {
301      preg = xcalloc(sizeof(*preg));
302  
303 <    if ((errnum = regcomp(preg, IRCItem->connregex, REG_ICASE | REG_EXTENDED)))
303 >    if ((errnum = regcomp(preg, IRCItem.connregex, REG_ICASE | REG_EXTENDED)))
304      {
305        char errmsg[256];
306  
# Line 334 | Line 344 | m_notice(char *parv[], unsigned int parc
344      log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
345                 user[0], user[1], user[2], user[3]);
346  
347 <  LIST_FOREACH(node, IRCItem->notices->head)
347 >  LIST_FOREACH(node, IRCItem.notices.head)
348      irc_send("NOTICE %s :%s", user[0], node->data);
349  
350    /* Pass this information off to scan.c */
# Line 375 | Line 385 | m_userhost(char *parv[], unsigned int pa
385   static void
386   m_cannot_join(char *parv[], unsigned int parc, const char *msg, const char *source_p)
387   {
378  const struct ChannelConf *channel = NULL;
379
388    if (parc < 5)
389      return;
390  
391    /* Is it one of our channels? */
392 <  if ((channel = get_channel(parv[3])) == NULL)
392 >  const struct ChannelConf *channel = get_channel(parv[3]);
393 >  if (channel == NULL)
394      return;
395  
396    if (EmptyString(channel->invite))
# Line 459 | Line 468 | userinfo_create(const char *source)
468   static void
469   irc_init(void)
470   {
471 <  const void *address = NULL;
471 >  const void *address;
472  
473    assert(IRC_FD == -1);
474  
475    memset(&IRC_SVR, 0, sizeof(IRC_SVR));
476  
477    /* Resolve IRC host. */
478 <  if ((address = firedns_resolveip6(IRCItem->server)))
478 >  if ((address = firedns_resolveip6(IRCItem.server)))
479    {
480      struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
481  
482      svr_addrlen = sizeof(*in);
483      IRC_SVR.ss_family = AF_INET6;
484 <    in->sin6_port = htons(IRCItem->port);
484 >    in->sin6_port = htons(IRCItem.port);
485      memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
486    }
487 <  else if ((address = firedns_resolveip4(IRCItem->server)))
487 >  else if ((address = firedns_resolveip4(IRCItem.server)))
488    {
489      struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
490  
491      svr_addrlen = sizeof(*in);
492      IRC_SVR.ss_family = AF_INET;
493 <    in->sin_port = htons(IRCItem->port);
493 >    in->sin_port = htons(IRCItem.port);
494      memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
495    }
496    else
497    {
498 <    log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem->server,
498 >    log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem.server,
499                 firedns_strerror(firedns_errno));
500      exit(EXIT_FAILURE);
501    }
# Line 501 | Line 510 | irc_init(void)
510    }
511  
512    /* Bind */
513 <  if (!EmptyString(IRCItem->vhost))
513 >  if (!EmptyString(IRCItem.vhost))
514    {
515      struct addrinfo hints, *res;
507    int n;
516  
517      memset(&hints, 0, sizeof(hints));
518  
# Line 512 | Line 520 | irc_init(void)
520      hints.ai_socktype = SOCK_STREAM;
521      hints.ai_flags = AI_NUMERICHOST;
522  
523 <    if ((n = getaddrinfo(IRCItem->vhost, NULL, &hints, &res)))
523 >    int n = getaddrinfo(IRCItem.vhost, NULL, &hints, &res);
524 >    if (n)
525      {
526 <      log_printf("IRC -> error binding to %s: %s", IRCItem->vhost, gai_strerror(n));
526 >      log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, gai_strerror(n));
527        exit(EXIT_FAILURE);
528      }
529      else if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
530      {
531 <      log_printf("IRC -> error binding to %s: %s", IRCItem->vhost, strerror(errno));
531 >      log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, strerror(errno));
532        exit(EXIT_FAILURE);
533      }
534  
535      freeaddrinfo(res);
536    }
537 +
538 +  if (IRCItem.tls)
539 +  {
540 + #ifdef HAVE_LIBCRYPTO
541 +    /* Initialize SSL */
542 +    static int tls_init = 0;
543 +    if (tls_init == 0)
544 +    {
545 +      tls_init = 1;
546 +
547 +      ssl_ctx = SSL_CTX_new(SSLv23_client_method());
548 +      if (ssl_ctx == NULL)
549 +      {
550 +        log_printf("IRC -> unable to create SSL context");
551 +        exit(EXIT_FAILURE);
552 +      }
553 +
554 +      SSL_CTX_set_default_verify_paths(ssl_ctx);
555 +    }
556 +
557 +    ssl_handle = SSL_new(ssl_ctx);
558 +    SSL_set_fd(ssl_handle, IRC_FD);
559 +    SSL_set_hostflags(ssl_handle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
560 +
561 +    if (!SSL_set1_host(ssl_handle, IRCItem.server))
562 +    {
563 +      log_printf("IRC -> unable to set expected DNS hostname");
564 +      /* OpenSSL is unable to verify the server hostname at this point, so we exit. */
565 +      exit(EXIT_FAILURE);
566 +    }
567 +
568 +    SSL_set_verify(ssl_handle, SSL_VERIFY_PEER, NULL);
569 + #else
570 +    log_printf("IRC -> HOPM is not compiled with OpenSSL support");
571 +    exit(EXIT_FAILURE);
572 + #endif
573 +  }
574   }
575  
576   /* irc_reconnect
# Line 542 | Line 588 | irc_reconnect(void)
588  
589    time(&present);
590  
591 <  /* Only try to reconnect every IRCItem->reconnectinterval seconds */
592 <  if ((present - IRC_LASTRECONNECT) < IRCItem->reconnectinterval)
591 >  /* Only try to reconnect every IRCItem.reconnectinterval seconds */
592 >  if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
593    {
594      /* Sleep to avoid excessive CPU */
595      sleep(1);
# Line 554 | Line 600 | irc_reconnect(void)
600  
601    if (IRC_FD > -1)
602    {
603 + #ifdef HAVE_LIBCRYPTO
604 +    if (ssl_handle)
605 +    {
606 +      SSL_shutdown(ssl_handle);
607 +      SSL_free(ssl_handle);
608 +      ssl_handle = NULL;
609 +    }
610 + #endif
611 +
612      close(IRC_FD);
613      IRC_FD = -1;  /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
614    }
615  
616 <  log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem->server);
616 >  log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
617   }
618  
619   /* irc_connect
# Line 576 | Line 631 | irc_connect(void)
631    if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, svr_addrlen) == -1)
632    {
633      log_printf("IRC -> connect(): error connecting to %s: %s",
634 <               IRCItem->server, strerror(errno));
634 >               IRCItem.server, strerror(errno));
635  
636      if (errno == EISCONN /* Already connected */ || errno == EALREADY /* Previous attempt not complete */)
637        return;
# Line 586 | Line 641 | irc_connect(void)
641      return;
642    }
643  
644 <  irc_send("NICK %s", IRCItem->nick);
644 > #ifdef HAVE_LIBCRYPTO
645 >  if (ssl_handle)
646 >  {
647 >    int ret = SSL_connect(ssl_handle);
648 >    if (ret != 1)
649 >    {
650 >      const char *error = ERR_error_string(ERR_get_error(), NULL);
651 >      log_printf("IRC -> connect(): error performing TLS handshake with %s: %s",
652 >                 IRCItem.server, error);
653 >
654 >      /* Try to connect again */
655 >      irc_reconnect();
656 >      return;
657 >    }
658 >  }
659 > #endif
660 >
661 >  irc_send("NICK %s", IRCItem.nick);
662  
663 <  if (!EmptyString(IRCItem->password))
664 <    irc_send("PASS %s", IRCItem->password);
663 >  if (!EmptyString(IRCItem.password))
664 >    irc_send("PASS %s", IRCItem.password);
665  
666    irc_send("USER %s %s %s :%s",
667 <           IRCItem->username,
668 <           IRCItem->username,
669 <           IRCItem->username,
670 <           IRCItem->realname);
667 >           IRCItem.username,
668 >           IRCItem.username,
669 >           IRCItem.username,
670 >           IRCItem.realname);
671    time(&IRC_LAST);
672   }
673  
# Line 620 | Line 692 | irc_parse(void)
692     * source did not exist
693     */
694    char        *parv[17];
695 +  static const unsigned int parv_size = sizeof(parv) / sizeof(parv[0]);
696    unsigned int parc = 1;
697    char         msg[MSGLENMAX];  /* Temporarily stores IRC msg to pass to handlers */
698  
# Line 665 | Line 738 | irc_parse(void)
738      parv[0] = IRC_RAW + 1;
739    else
740    {
741 <    parv[0] = IRCItem->server;
741 >    parv[0] = IRCItem.server;
742      parv[parc++] = IRC_RAW;
743    }
744  
745    pos = IRC_RAW;
746  
747 <  while ((pos = strchr(pos, ' ')) && parc <= 17)
747 >  while ((pos = strchr(pos, ' ')) && parc < parv_size)
748    {
749      /* Avoid excessive spaces and end of IRC_RAW */
750      if (*(pos + 1) == ' ' || *(pos + 1) == '\0')
# Line 725 | Line 798 | irc_read(void)
798    ssize_t len;
799    char c;
800  
801 <  while ((len = read(IRC_FD, &c, 1)) > 0)
801 >  while (1)
802    {
803 + #ifdef HAVE_LIBCRYPTO
804 +    if (ssl_handle)
805 +      len = SSL_read(ssl_handle, &c, 1);
806 +    else
807 + #endif
808 +      len = recv(IRC_FD, &c, 1, 0);
809 +
810 +    if (len <= 0)
811 +        break;
812      if (c == '\r')
813        continue;
814  
# Line 776 | Line 858 | irc_cycle(void)
858    if (IRC_FD == -1)
859    {
860      /* Initialize negative cache. */
861 <    if (OptionsItem->negcache)
861 >    if (OptionsItem.negcache)
862        negcache_init();
863  
864      /* Resolve remote host. */
# Line 822 | Line 904 | irc_send(const char *data, ...)
904   {
905    va_list arglist;
906    char buf[MSGLENMAX];
825  size_t len = 0;
907  
908    va_start(arglist, data);
909 <  len = vsnprintf(buf, sizeof(buf), data, arglist);
909 >  ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
910    va_end(arglist);
911  
912    if (OPT_DEBUG >= 2)
# Line 837 | Line 918 | irc_send(const char *data, ...)
918    buf[len++] = '\r';
919    buf[len++] = '\n';
920  
921 <  if (send(IRC_FD, buf, len, 0) == -1)
921 >  ssize_t sent;
922 > #ifdef HAVE_LIBCRYPTO
923 >  if (ssl_handle)
924 >    sent = SSL_write(ssl_handle, buf, len);
925 >  else
926 > #endif
927 >    sent = send(IRC_FD, buf, len, 0);
928 >
929 >  if (sent != len)
930    {
931      /* Return of -1 indicates error sending data; we reconnect. */
932      log_printf("IRC -> Error sending data to server: %s", strerror(errno));
# Line 866 | Line 955 | irc_send_channels(const char *data, ...)
955    vsnprintf(buf, sizeof(buf), data, arglist);
956    va_end(arglist);
957  
958 <  LIST_FOREACH(node, IRCItem->channels->head)
958 >  LIST_FOREACH(node, IRCItem.channels.head)
959    {
960      const struct ChannelConf *chan = node->data;
961  
# Line 890 | Line 979 | irc_timer(void)
979  
980    delta = present - IRC_LAST;
981  
982 <  /* No data in IRCItem->readtimeout seconds */
983 <  if (delta >= IRCItem->readtimeout)
982 >  /* No data in IRCItem.readtimeout seconds */
983 >  if (delta >= IRCItem.readtimeout)
984    {
985      log_printf("IRC -> Timeout awaiting data from server.");
986      irc_reconnect();
# Line 899 | Line 988 | irc_timer(void)
988      /* Make sure we don't do this again for a while */
989      time(&IRC_LAST);
990    }
991 <  else if (delta >= IRCItem->readtimeout / 2)
991 >  else if (delta >= IRCItem.readtimeout / 2)
992    {
993      /*
994       * Generate some data so high ping times don't cause uneeded

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)