ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 9727
Committed: Mon Nov 16 10:29:12 2020 UTC (5 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 23591 byte(s)
Log Message:
- The 'vhost' configuration directive found in the irc {} and scanner {} blocks has been renamed to 'bind'

File Contents

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

Properties

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