/[svn]/hopm/trunk/src/irc.c
ViewVC logotype

Contents of /hopm/trunk/src/irc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9920 - (show annotations)
Wed Feb 3 16:43:47 2021 UTC (21 months, 3 weeks ago) by michael
File MIME type: text/x-chdr
File size: 23914 byte(s)
- Add `irc::tls_disable_certificate_verification` configuration option as requested in github issue #43

1 /*
2 * Copyright (c) 2002-2003 Erik Fears
3 * Copyright (c) 2014-2021 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
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18 * USA
19 */
20
21 #include "setup.h"
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <poll.h>
33 #include <time.h>
34 #include <errno.h>
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"
46 #include "log.h"
47 #include "opercmd.h"
48 #include "scan.h"
49 #include "dnsbl.h"
50 #include "stats.h"
51 #include "options.h"
52 #include "match.h"
53 #include "compat.h"
54 #include "negcache.h"
55 #include "memory.h"
56 #include "main.h"
57 #include "opm_gettime.h"
58
59
60 /*
61 * Certain variables we don't want to allocate memory for over and over
62 * again so global scope is given.
63 */
64 static char IRC_RAW[MSGLENMAX]; /* Buffer to read data into */
65 static unsigned int IRC_RAW_LEN; /* Position of IRC_RAW */
66 static int IRC_FD = -1; /* File descriptor for IRC client */
67
68 static struct sockaddr_storage IRC_SVR; /* Sock Address Struct for IRC server */
69 static socklen_t IRC_SVR_LEN;
70 static char IRC_SVR_STR[INET6_ADDRSTRLEN];
71 static time_t IRC_LAST; /* Last full line of data from irc server */
72 static time_t IRC_LASTRECONNECT; /* Time of last reconnection */
73
74 #ifdef HAVE_LIBCRYPTO
75 static SSL_CTX *ssl_ctx;
76 static SSL *ssl_handle;
77 #endif
78
79
80 /* get_channel
81 *
82 * Check if a channel is defined in our conf. If so return
83 * a pointer to it.
84 *
85 * Parameters:
86 * channel: channel to search conf for
87 *
88 * Return: Pointer to ChannelConf containing the channel
89 */
90 static const struct ChannelConf *
91 get_channel(const char *name)
92 {
93 node_t *node;
94
95 LIST_FOREACH(node, IRCItem.channels.head)
96 {
97 struct ChannelConf *item = node->data;
98
99 if (strcasecmp(item->name, name) == 0)
100 return item;
101 }
102
103 return NULL;
104 }
105
106 /* m_perform
107 *
108 * actions to perform on IRC connection
109 *
110 * Parameters:
111 * parv[0] = source
112 * parv[1] = PING
113 * parv[2] = PING TS/Package
114 *
115 * source_p: UserInfo struct of the source user, or NULL if
116 * the source (parv[0]) is a server.
117 */
118 static void
119 m_perform(char *parv[], unsigned int parc, const char *msg, const char *source_p)
120 {
121 node_t *node;
122
123 log_printf("IRC -> Connected to %s[%s]:%i", IRC_SVR_STR, IRCItem.server, IRCItem.port);
124
125 /* Identify to nickserv if needed */
126 if (!EmptyString(IRCItem.nickserv))
127 irc_send("%s", IRCItem.nickserv);
128
129 /* Oper */
130 if (!EmptyString(IRCItem.oper))
131 irc_send("OPER %s", IRCItem.oper);
132
133 /* Set modes */
134 if (!EmptyString(IRCItem.mode))
135 irc_send("MODE %s %s", IRCItem.nick, IRCItem.mode);
136
137 /* Set Away */
138 if (!EmptyString(IRCItem.away))
139 irc_send("AWAY :%s", IRCItem.away);
140
141 /* Perform */
142 LIST_FOREACH(node, IRCItem.performs.head)
143 irc_send("%s", node->data);
144
145 /* Join all listed channels. */
146 LIST_FOREACH(node, IRCItem.channels.head)
147 {
148 const struct ChannelConf *channel = node->data;
149
150 if (EmptyString(channel->name))
151 continue;
152
153 if (!EmptyString(channel->key))
154 irc_send("JOIN %s %s", channel->name, channel->key);
155 else
156 irc_send("JOIN %s", channel->name);
157 }
158 }
159
160 /* m_ping
161 *
162 * parv[0] = source
163 * parv[1] = PING
164 * parv[2] = PING TS/Package
165 *
166 * source_p: UserInfo struct of the source user, or NULL if
167 * the source (parv[0]) is a server.
168 */
169 static void
170 m_ping(char *parv[], unsigned int parc, const char *msg, const char *source_p)
171 {
172 if (parc < 3)
173 return;
174
175 if (OPT_DEBUG >= 2)
176 log_printf("IRC -> PING? PONG!");
177
178 irc_send("PONG %s", parv[2]);
179 }
180
181 /* m_invite
182 *
183 * parv[0] = source
184 * parv[1] = INVITE
185 * parv[2] = target
186 * parv[3] = channel
187 *
188 * source_p: UserInfo struct of the source user, or NULL if
189 * the source (parv[0]) is a server.
190 */
191 static void
192 m_invite(char *parv[], unsigned int parc, const char *msg, const char *source_p)
193 {
194 if (parc < 4)
195 return;
196
197 log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
198
199 const struct ChannelConf *channel = get_channel(parv[3]);
200 if (channel == NULL)
201 return;
202
203 irc_send("JOIN %s %s", channel->name, channel->key);
204 }
205
206 /* m_ctcp
207 * parv[0] = source
208 * parv[1] = PRIVMSG
209 * parv[2] = target (channel or user)
210 * parv[3] = message
211 *
212 * source_p: UserInfo struct of the source user, or NULL if
213 * the source (parv[0]) is a server.
214 */
215 static void
216 m_ctcp(char *parv[], unsigned int parc, const char *msg, const char *source_p)
217 {
218 if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
219 irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s\001",
220 source_p, VERSION);
221 }
222
223 /* m_privmsg
224 *
225 * parv[0] = source
226 * parv[1] = PRIVMSG
227 * parv[2] = target (channel or user)
228 * parv[3] = message
229 *
230 * source_p: UserInfo struct of the source user, or NULL if
231 * the source (parv[0]) is a server.
232 */
233 static void
234 m_privmsg(char *parv[], unsigned int parc, const char *msg, const char *source_p)
235 {
236 if (source_p == NULL)
237 return;
238
239 if (parc < 4)
240 return;
241
242 /* CTCP */
243 if (*parv[3] == '\001')
244 {
245 m_ctcp(parv, parc, msg, source_p);
246 return;
247 }
248
249 /* Only interested in privmsg to channels */
250 if (*parv[2] != '#' && *parv[2] != '&')
251 return;
252
253 /* Get a target */
254 const struct ChannelConf *channel = get_channel(parv[2]);
255 if (channel == NULL)
256 return;
257
258 int hit = strncasecmp(parv[3], "!all ", 5) == 0;
259 if (hit == 0)
260 {
261 size_t nick_len = strlen(IRCItem.nick);
262
263 if (strncasecmp(parv[3], IRCItem.nick, nick_len) == 0)
264 hit = *(parv[3] + nick_len) == ' ' ||
265 *(parv[3] + nick_len) == ',' ||
266 *(parv[3] + nick_len) == ':';
267 }
268
269 if (hit)
270 /* XXX command_parse will alter parv[3]. */
271 command_parse(parv[3], channel->name, source_p);
272 }
273
274 /* m_notice
275 *
276 * parv[0] = source
277 * parv[1] = NOTICE
278 * parv[2] = target
279 * parv[3] = message
280 *
281 *
282 * source_p: UserInfo struct of the source user, or NULL if
283 * the source (parv[0]) is a server.
284 */
285 static void
286 m_notice(char *parv[], unsigned int parc, const char *msg, const char *source_p)
287 {
288 static regex_t *preg = NULL;
289 regmatch_t pmatch[5];
290 int errnum;
291 const char *user[4];
292 const node_t *node;
293
294 /* Not interested in notices from users */
295 if (source_p)
296 return;
297
298 if (parc < 4)
299 return;
300
301 /* Compile the regular expression if it has not been already */
302 if (preg == NULL)
303 {
304 preg = xcalloc(sizeof(*preg));
305
306 if ((errnum = regcomp(preg, IRCItem.connregex, REG_ICASE | REG_EXTENDED)))
307 {
308 char errmsg[256];
309
310 regerror(errnum, preg, errmsg, sizeof(errmsg));
311 log_printf("IRC REGEX -> Error when compiling regular expression: %s", errmsg);
312
313 xfree(preg);
314 preg = NULL;
315 return;
316 }
317 }
318
319 /* Match the expression against the possible connection notice */
320 if (regexec(preg, parv[3], 5, pmatch, 0))
321 return;
322
323 if (OPT_DEBUG > 0)
324 log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
325
326 if (pmatch[4].rm_so == -1)
327 {
328 log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
329 return;
330 }
331
332 /*
333 * Offsets for data in the connection notice:
334 *
335 * NICKNAME: pmatch[1].rm_so TO pmatch[1].rm_eo
336 * USERNAME: pmatch[2].rm_so TO pmatch[2].rm_eo
337 * HOSTNAME: pmatch[3].rm_so TO pmatch[3].rm_eo
338 * IP : pmatch[4].rm_so TO pmatch[4].rm_eo
339 */
340 for (unsigned int i = 0; i < 4; ++i)
341 {
342 user[i] = (parv[3] + pmatch[i + 1].rm_so);
343 *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
344 }
345
346 if (OPT_DEBUG > 0)
347 log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
348 user[0], user[1], user[2], user[3]);
349
350 LIST_FOREACH(node, IRCItem.notices.head)
351 irc_send("NOTICE %s :%s", user[0], node->data);
352
353 /* Pass this information off to scan.c */
354 scan_connect(user, msg);
355
356 /* Record the connect for stats purposes */
357 stats_connect();
358 }
359
360 /* m_userhost
361 *
362 * parv[0] = source
363 * parv[1] = USERHOST
364 * parv[2] = target (hopm)
365 * parv[3] = :nick=(flags)user@host
366 *
367 *
368 * source_p: UserInfo struct of the source user, or NULL if
369 * the source (parv[0]) is a server.
370 */
371 static void
372 m_userhost(char *parv[], unsigned int parc, const char *msg, const char *source_p)
373 {
374 if (parc < 4)
375 return;
376
377 command_userhost(parv[3]);
378 }
379
380 /* m_cannot_join
381 *
382 * parv[0] = source
383 * parv[1] = numeric
384 * parv[2] = target (hopm)
385 * parv[3] = channel
386 * parv[4] = error text
387 */
388 static void
389 m_cannot_join(char *parv[], unsigned int parc, const char *msg, const char *source_p)
390 {
391 if (parc < 5)
392 return;
393
394 /* Is it one of our channels? */
395 const struct ChannelConf *channel = get_channel(parv[3]);
396 if (channel == NULL)
397 return;
398
399 if (EmptyString(channel->invite))
400 return;
401
402 irc_send("%s", channel->invite);
403 }
404
405 /* m_kill
406 *
407 * parv[0] = source
408 * parv[1] = numeric
409 * parv[2] = target (hopm)
410 * parv[3] = channel
411 * parv[4] = error text
412 */
413 static void
414 m_kill(char *parv[], unsigned int parc, const char *msg, const char *source_p)
415 {
416 /* Restart hopm to rehash */
417 main_restart();
418 }
419
420 /* userinfo_create
421 *
422 * Parse a nick!user@host into a UserInfo struct
423 * and return a pointer to the new struct.
424 *
425 * Parameters:
426 * source: nick!user@host to parse
427 *
428 * Return:
429 * pointer to new UserInfo struct, or NULL if parsing failed
430 */
431 static const char *
432 userinfo_create(const char *source)
433 {
434 static char name[MSGLENMAX];
435 unsigned int has_user = 0;
436 unsigned int has_host = 0;
437
438 strlcpy(name, source, sizeof(name));
439
440 for (char *p = name; *p; ++p)
441 {
442 if (*p == '!')
443 {
444 *p = '\0';
445 ++has_user;
446 continue;
447 }
448
449 if (*p == '@' && has_user)
450 {
451 ++has_host;
452 continue;
453 }
454 }
455
456 if (has_user == 1 && has_host == 1)
457 return name;
458 return NULL;
459 };
460
461 /* irc_init
462 *
463 * Resolve IRC host and perform other initialization.
464 *
465 * Parameters:
466 * None
467 *
468 * Return:
469 * None
470 */
471 static void
472 irc_init(void)
473 {
474 int n;
475 const void *address;
476 struct addrinfo hints, *res = NULL;
477
478 assert(IRC_FD == -1);
479
480 memset(&IRC_SVR, 0, sizeof(IRC_SVR));
481
482 if (!EmptyString(IRCItem.bind))
483 {
484 memset(&hints, 0, sizeof(hints));
485
486 hints.ai_family = AF_UNSPEC;
487 hints.ai_socktype = SOCK_STREAM;
488 hints.ai_flags = AI_NUMERICHOST;
489
490 n = getaddrinfo(IRCItem.bind, NULL, &hints, &res);
491 if (n)
492 {
493 log_printf("IRC -> getaddrinfo() error: %s", gai_strerror(n));
494 exit(EXIT_FAILURE);
495 }
496 }
497
498 /* Resolve IRC host. */
499 if ((res == NULL || res->ai_family == AF_INET6) && (address = firedns_resolveip6(IRCItem.server)))
500 {
501 struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
502
503 IRC_SVR_LEN = sizeof(*in);
504 IRC_SVR.ss_family = AF_INET6;
505 in->sin6_port = htons(IRCItem.port);
506 memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
507 }
508 else if ((res == NULL || res->ai_family == AF_INET) && (address = firedns_resolveip4(IRCItem.server)))
509 {
510 struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
511
512 IRC_SVR_LEN = sizeof(*in);
513 IRC_SVR.ss_family = AF_INET;
514 in->sin_port = htons(IRCItem.port);
515 memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
516 }
517 else
518 {
519 log_printf("IRC -> Error resolving host '%s': %s", IRCItem.server,
520 firedns_strerror(firedns_errno));
521 exit(EXIT_FAILURE);
522 }
523
524 n = getnameinfo((const struct sockaddr *)&IRC_SVR, IRC_SVR_LEN, IRC_SVR_STR,
525 sizeof(IRC_SVR_STR), NULL, 0, NI_NUMERICHOST);
526 if (n)
527 {
528 log_printf("IRC -> getnameinfo() error: %s", gai_strerror(n));
529 exit(EXIT_FAILURE);
530 }
531
532 /* Request file desc for IRC client socket */
533 IRC_FD = socket(IRC_SVR.ss_family, SOCK_STREAM, 0);
534
535 if (IRC_FD == -1)
536 {
537 log_printf("IRC -> socket(): error creating socket: %s", strerror(errno));
538 exit(EXIT_FAILURE);
539 }
540
541 /* Bind */
542 if (res)
543 {
544 if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
545 {
546 log_printf("IRC -> error binding to %s: %s", IRCItem.bind, strerror(errno));
547 exit(EXIT_FAILURE);
548 }
549
550 freeaddrinfo(res);
551 }
552
553 if (IRCItem.tls)
554 {
555 #ifdef HAVE_LIBCRYPTO
556 /* Initialize SSL */
557 static int tls_init = 0;
558 if (tls_init == 0)
559 {
560 tls_init = 1;
561
562 ssl_ctx = SSL_CTX_new(TLS_client_method());
563 if (ssl_ctx == NULL)
564 {
565 log_printf("IRC -> unable to create SSL context");
566 exit(EXIT_FAILURE);
567 }
568
569 if (!EmptyString(IRCItem.rsa_private_key_file) &&
570 !EmptyString(IRCItem.tls_certificate_file))
571 {
572 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, IRCItem.tls_certificate_file) != 1)
573 {
574 log_printf("IRC -> couldn't load client certificate from %s: %s",
575 IRCItem.tls_certificate_file, ERR_error_string(ERR_get_error(), NULL));
576 exit(EXIT_FAILURE);
577 }
578
579 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, IRCItem.rsa_private_key_file, SSL_FILETYPE_PEM) != 1)
580 {
581 log_printf("IRC -> couldn't load private key from %s: %s",
582 IRCItem.rsa_private_key_file, ERR_error_string(ERR_get_error(), NULL));
583 exit(EXIT_FAILURE);
584 }
585
586 if (SSL_CTX_check_private_key(ssl_ctx) != 1)
587 {
588 log_printf("IRC -> Private key does not match the client certificate: %s",
589 ERR_error_string(ERR_get_error(), NULL));
590 exit(EXIT_FAILURE);
591 }
592 }
593
594 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
595 SSL_CTX_set_default_verify_paths(ssl_ctx);
596 }
597
598 ssl_handle = SSL_new(ssl_ctx);
599 SSL_set_fd(ssl_handle, IRC_FD);
600
601 if (IRCItem.tls_hostname_verification)
602 {
603 #ifndef LIBRESSL_VERSION_NUMBER
604 SSL_set_hostflags(ssl_handle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
605
606 if (SSL_set1_host(ssl_handle, IRCItem.server) == 0)
607 #else
608 X509_VERIFY_PARAM *param = SSL_get0_param(ssl_handle);
609 X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
610
611 if (X509_VERIFY_PARAM_set1_host(param, IRCItem.server, 0) == 0)
612 #endif
613 {
614 log_printf("IRC -> unable to set expected DNS hostname");
615 /* OpenSSL is unable to verify the server hostname at this point, so we exit. */
616 exit(EXIT_FAILURE);
617 }
618 }
619
620 SSL_set_verify(ssl_handle,
621 IRCItem.tls_disable_certificate_verification ? SSL_VERIFY_NONE : SSL_VERIFY_PEER, NULL);
622 #else
623 log_printf("IRC -> HOPM is not compiled with OpenSSL support");
624 exit(EXIT_FAILURE);
625 #endif
626 }
627 }
628
629 /* irc_close
630 *
631 * Close connection to IRC server.
632 *
633 * Parameters: NONE
634 *
635 * Return: NONE
636 */
637 static void
638 irc_close(void)
639 {
640 if (IRC_FD > -1)
641 {
642 #ifdef HAVE_LIBCRYPTO
643 if (ssl_handle)
644 {
645 SSL_shutdown(ssl_handle);
646 SSL_free(ssl_handle);
647 ssl_handle = NULL;
648 }
649 #endif
650
651 close(IRC_FD);
652 IRC_FD = -1; /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
653 }
654
655 log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
656 }
657
658 /* irc_connect
659 *
660 * Connect to IRC server.
661 * XXX: FD allocation done here
662 *
663 * Parameters: NONE
664 * Return: NONE
665 */
666 static void
667 irc_connect(void)
668 {
669 /* Only try to reconnect every IRCItem.reconnectinterval seconds */
670 if ((opm_gettime() - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
671 {
672 /* Sleep to avoid excessive CPU */
673 sleep(1);
674 return;
675 }
676
677 IRC_LASTRECONNECT =
678 IRC_LAST = opm_gettime();
679
680 irc_init();
681
682 log_printf("IRC -> Attempting to connect to %s[%s]:%i", IRC_SVR_STR, IRCItem.server, IRCItem.port);
683
684 /* Connect to IRC server as client. */
685 if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, IRC_SVR_LEN) == -1)
686 {
687 log_printf("IRC -> connect(): error connecting to %s: %s",
688 IRCItem.server, strerror(errno));
689
690 /* Close socket and try to connect again */
691 irc_close();
692 return;
693 }
694
695 #ifdef HAVE_LIBCRYPTO
696 if (ssl_handle)
697 {
698 int ret = SSL_connect(ssl_handle);
699 if (ret != 1)
700 {
701 const char *error = ERR_error_string(ERR_get_error(), NULL);
702 log_printf("IRC -> connect(): error performing TLS handshake with %s: %s",
703 IRCItem.server, error);
704
705 /* Close socket and try to connect again */
706 irc_close();
707 return;
708 }
709 }
710 #endif
711
712 irc_send("NICK %s", IRCItem.nick);
713
714 if (!EmptyString(IRCItem.password))
715 irc_send("PASS %s", IRCItem.password);
716
717 irc_send("USER %s %s %s :%s",
718 IRCItem.username,
719 IRCItem.username,
720 IRCItem.username,
721 IRCItem.realname);
722 IRC_LAST = opm_gettime();
723 }
724
725 /* irc_parse
726 *
727 * irc_parse is called by irc_read when a full line of data
728 * is ready to be parsed.
729 *
730 * Parameters: NONE
731 * Return: NONE
732 */
733 static void
734 irc_parse(void)
735 {
736 char *pos;
737
738 /*
739 * parv stores the parsed token, parc is the count of the parsed
740 * tokens
741 *
742 * parv[0] is ALWAYS the source, and is the server name of the
743 * source did not exist
744 */
745 char *parv[17];
746 static const unsigned int parv_size = sizeof(parv) / sizeof(parv[0]);
747 unsigned int parc = 1;
748 char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
749
750 struct CommandHash
751 {
752 const char *command;
753 void (*handler)(char *[], unsigned int, const char *, const char *);
754 };
755
756 /*
757 * Table should be ordered with most occuring (or priority)
758 * commands at the top of the list.
759 */
760 static const struct CommandHash COMMAND_TABLE[] =
761 {
762 { .command = "NOTICE", .handler = m_notice },
763 { .command = "PRIVMSG", .handler = m_privmsg },
764 { .command = "PING", .handler = m_ping },
765 { .command = "INVITE", .handler = m_invite },
766 { .command = "001", .handler = m_perform },
767 { .command = "302", .handler = m_userhost },
768 { .command = "471", .handler = m_cannot_join },
769 { .command = "473", .handler = m_cannot_join },
770 { .command = "474", .handler = m_cannot_join },
771 { .command = "475", .handler = m_cannot_join },
772 { .command = "KILL", .handler = m_kill },
773 { .command = NULL }
774 };
775
776 if (IRC_RAW_LEN == 0)
777 return;
778
779 if (OPT_DEBUG >= 2)
780 log_printf("IRC READ -> %s", IRC_RAW);
781
782 IRC_LAST = opm_gettime();
783
784 /* Store a copy of IRC_RAW for the handlers (for functions that need PROOF) */
785 strlcpy(msg, IRC_RAW, sizeof(msg));
786
787 /* parv[0] is always the source */
788 if (IRC_RAW[0] == ':')
789 parv[0] = IRC_RAW + 1;
790 else
791 {
792 parv[0] = IRCItem.server;
793 parv[parc++] = IRC_RAW;
794 }
795
796 pos = IRC_RAW;
797
798 while ((pos = strchr(pos, ' ')) && parc < parv_size)
799 {
800 /* Avoid excessive spaces and end of IRC_RAW */
801 if (*(pos + 1) == ' ' || *(pos + 1) == '\0')
802 {
803 pos++;
804 continue;
805 }
806
807 /* Anything after a : is considered the final string of the message */
808 if (*(pos + 1) == ':')
809 {
810 parv[parc++] = pos + 2;
811 *pos = '\0';
812 break;
813 }
814
815 /*
816 * Set the next parv at this position and replace the space with a
817 * \0 for the previous parv
818 */
819 parv[parc++] = pos + 1;
820 *pos = '\0';
821 pos++;
822 }
823
824 /*
825 * Determine which command this is from the command table
826 * and let the handler for that command take control
827 */
828 for (const struct CommandHash *cmd = COMMAND_TABLE; cmd->command; ++cmd)
829 {
830 if (strcasecmp(cmd->command, parv[1]) == 0)
831 {
832 cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
833 break;
834 }
835 }
836 }
837
838 /* irc_read
839 *
840 * irc_read is called by irc_cycle when new data is ready to be
841 * read from the irc server.
842 *
843 * Parameters: NONE
844 * Return: NONE
845 */
846 static void
847 irc_read(void)
848 {
849 ssize_t len;
850 char c;
851
852 while (1)
853 {
854 #ifdef HAVE_LIBCRYPTO
855 if (ssl_handle)
856 len = SSL_read(ssl_handle, &c, 1);
857 else
858 #endif
859 len = recv(IRC_FD, &c, 1, 0);
860
861 if (len <= 0)
862 break;
863
864 if (c == '\r')
865 continue;
866
867 if (c == '\n')
868 {
869 /* Null string. */
870 IRC_RAW[IRC_RAW_LEN] = '\0';
871
872 /* Parse line. */
873 irc_parse();
874
875 /* Reset counter. */
876 IRC_RAW_LEN = 0;
877 return;
878 }
879
880 if (c != '\0')
881 IRC_RAW[IRC_RAW_LEN++] = c;
882 }
883
884 if ((len <= 0) && (errno != EAGAIN))
885 {
886 if (errno != EINTR)
887 log_printf("IRC -> Error reading data from server: %s", strerror(errno));
888
889 irc_close();
890 IRC_RAW_LEN = 0;
891 }
892 }
893
894 /* irc_cycle
895 *
896 * Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
897 *
898 * Parameters:
899 * None
900 *
901 * Return:
902 * None
903 */
904 void
905 irc_cycle(void)
906 {
907 static struct pollfd pfd;
908
909 if (IRC_FD == -1)
910 {
911 /* Initialize negative cache. */
912 if (OptionsItem.negcache)
913 negcache_init();
914
915 /* Connect to remote host. */
916 irc_connect();
917 return; /* In case connect() immediately failed */
918 }
919
920 pfd.fd = IRC_FD;
921 pfd.events = POLLIN;
922
923 /* Block .050 seconds to avoid excessive CPU use on poll(). */
924 switch (poll(&pfd, 1, 50))
925 {
926 case 0:
927 case -1:
928 break;
929 default:
930 /* Check if IRC data is available. */
931 if (pfd.revents & POLLIN)
932 irc_read();
933 else if (pfd.revents & (POLLERR | POLLHUP))
934 irc_close();
935
936 break;
937 }
938 }
939
940 /* irc_send
941 *
942 * Send data to remote IRC host.
943 *
944 * Parameters:
945 * data: Format of data to send
946 * ...: varargs to format with
947 *
948 * Return: NONE
949 */
950 void
951 irc_send(const char *data, ...)
952 {
953 va_list arglist;
954 char buf[MSGLENMAX];
955
956 va_start(arglist, data);
957 ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
958 va_end(arglist);
959
960 if (OPT_DEBUG >= 2)
961 log_printf("IRC SEND -> %s", buf);
962
963 if (len > 510)
964 len = 510;
965
966 buf[len++] = '\r';
967 buf[len++] = '\n';
968
969 ssize_t sent;
970 #ifdef HAVE_LIBCRYPTO
971 if (ssl_handle)
972 sent = SSL_write(ssl_handle, buf, len);
973 else
974 #endif
975 sent = send(IRC_FD, buf, len, 0);
976
977 if (sent != len)
978 {
979 /* Return of -1 indicates error sending data; we reconnect. */
980 log_printf("IRC -> Error sending data to server: %s", strerror(errno));
981 irc_close();
982 }
983 }
984
985 /* irc_send_channels
986 *
987 * Send privmsg to all channels.
988 *
989 * Parameters:
990 * data: Format of data to send
991 * ...: varargs to format with
992 *
993 * Return: NONE
994 */
995 void
996 irc_send_channels(const char *data, ...)
997 {
998 const node_t *node;
999 va_list arglist;
1000 char buf[MSGLENMAX];
1001
1002 va_start(arglist, data);
1003 vsnprintf(buf, sizeof(buf), data, arglist);
1004 va_end(arglist);
1005
1006 LIST_FOREACH(node, IRCItem.channels.head)
1007 {
1008 const struct ChannelConf *chan = node->data;
1009
1010 irc_send("PRIVMSG %s :%s", chan->name, buf);
1011 }
1012 }
1013
1014 /* irc_timer
1015 *
1016 * Functions to be performed every ~seconds.
1017 *
1018 * Parameters: NONE
1019 * Return: NONE
1020 */
1021 void
1022 irc_timer(void)
1023 {
1024 time_t delta = opm_gettime() - IRC_LAST;
1025
1026 /* No data in IRCItem.readtimeout seconds */
1027 if (delta >= IRCItem.readtimeout)
1028 {
1029 log_printf("IRC -> Timeout awaiting data from server.");
1030 irc_close();
1031
1032 /* Make sure we don't do this again for a while */
1033 IRC_LAST = opm_gettime();
1034 }
1035 else if (delta >= IRCItem.readtimeout / 2)
1036 {
1037 /*
1038 * Generate some data so high ping times don't cause uneeded
1039 * reconnections
1040 */
1041 irc_send("PING :HOPM");
1042 }
1043 }

Properties

Name Value
svn:eol-style native
svn:keywords Id

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28