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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9517 - (show annotations)
Sat Jul 11 20:28:46 2020 UTC (3 weeks, 4 days ago) by michael
File MIME type: text/x-chdr
File size: 22536 byte(s)
- Allow IRCItem.oper and IRCItem.mode to be empty

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 #include "serno.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(%s)\001",
220 source_p, VERSION, SERIALNUM);
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
477 assert(IRC_FD == -1);
478
479 memset(&IRC_SVR, 0, sizeof(IRC_SVR));
480
481 /* Resolve IRC host. */
482 if ((address = firedns_resolveip6(IRCItem.server)))
483 {
484 struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
485
486 IRC_SVR_LEN = sizeof(*in);
487 IRC_SVR.ss_family = AF_INET6;
488 in->sin6_port = htons(IRCItem.port);
489 memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
490 }
491 else if ((address = firedns_resolveip4(IRCItem.server)))
492 {
493 struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
494
495 IRC_SVR_LEN = sizeof(*in);
496 IRC_SVR.ss_family = AF_INET;
497 in->sin_port = htons(IRCItem.port);
498 memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
499 }
500 else
501 {
502 log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem.server,
503 firedns_strerror(firedns_errno));
504 exit(EXIT_FAILURE);
505 }
506
507 n = getnameinfo((const struct sockaddr *)&IRC_SVR, IRC_SVR_LEN, IRC_SVR_STR,
508 sizeof(IRC_SVR_STR), NULL, 0, NI_NUMERICHOST);
509 if (n)
510 {
511 log_printf("IRC -> getnameinfo() error: %s", gai_strerror(n));
512 exit(EXIT_FAILURE);
513 }
514
515 /* Request file desc for IRC client socket */
516 IRC_FD = socket(IRC_SVR.ss_family, SOCK_STREAM, 0);
517
518 if (IRC_FD == -1)
519 {
520 log_printf("IRC -> socket(): error creating socket: %s", strerror(errno));
521 exit(EXIT_FAILURE);
522 }
523
524 /* Bind */
525 if (!EmptyString(IRCItem.vhost))
526 {
527 struct addrinfo hints, *res;
528
529 memset(&hints, 0, sizeof(hints));
530
531 hints.ai_family = AF_UNSPEC;
532 hints.ai_socktype = SOCK_STREAM;
533 hints.ai_flags = AI_NUMERICHOST;
534
535 n = getaddrinfo(IRCItem.vhost, NULL, &hints, &res);
536 if (n)
537 {
538 log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, gai_strerror(n));
539 exit(EXIT_FAILURE);
540 }
541 else if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
542 {
543 log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, strerror(errno));
544 exit(EXIT_FAILURE);
545 }
546
547 freeaddrinfo(res);
548 }
549
550 if (IRCItem.tls)
551 {
552 #ifdef HAVE_LIBCRYPTO
553 /* Initialize SSL */
554 static int tls_init = 0;
555 if (tls_init == 0)
556 {
557 tls_init = 1;
558
559 ssl_ctx = SSL_CTX_new(TLS_client_method());
560 if (ssl_ctx == NULL)
561 {
562 log_printf("IRC -> unable to create SSL context");
563 exit(EXIT_FAILURE);
564 }
565
566 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
567 SSL_CTX_set_default_verify_paths(ssl_ctx);
568 }
569
570 ssl_handle = SSL_new(ssl_ctx);
571 SSL_set_fd(ssl_handle, IRC_FD);
572
573 if (IRCItem.tls_hostname_verification)
574 {
575 SSL_set_hostflags(ssl_handle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
576
577 if (SSL_set1_host(ssl_handle, IRCItem.server) == 0)
578 {
579 log_printf("IRC -> unable to set expected DNS hostname");
580 /* OpenSSL is unable to verify the server hostname at this point, so we exit. */
581 exit(EXIT_FAILURE);
582 }
583 }
584
585 SSL_set_verify(ssl_handle, SSL_VERIFY_PEER, NULL);
586 #else
587 log_printf("IRC -> HOPM is not compiled with OpenSSL support");
588 exit(EXIT_FAILURE);
589 #endif
590 }
591 }
592
593 /* irc_close
594 *
595 * Close connection to IRC server.
596 *
597 * Parameters: NONE
598 *
599 * Return: NONE
600 */
601 static void
602 irc_close(void)
603 {
604 if (IRC_FD > -1)
605 {
606 #ifdef HAVE_LIBCRYPTO
607 if (ssl_handle)
608 {
609 SSL_shutdown(ssl_handle);
610 SSL_free(ssl_handle);
611 ssl_handle = NULL;
612 }
613 #endif
614
615 close(IRC_FD);
616 IRC_FD = -1; /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
617 }
618
619 log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
620 }
621
622 /* irc_connect
623 *
624 * Connect to IRC server.
625 * XXX: FD allocation done here
626 *
627 * Parameters: NONE
628 * Return: NONE
629 */
630 static void
631 irc_connect(void)
632 {
633 time_t present;
634
635 time(&present);
636
637 /* Only try to reconnect every IRCItem.reconnectinterval seconds */
638 if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
639 {
640 /* Sleep to avoid excessive CPU */
641 sleep(1);
642 return;
643 }
644
645 time(&IRC_LASTRECONNECT);
646 time(&IRC_LAST);
647
648 irc_init();
649
650 log_printf("IRC -> Attempting to connect to %s[%s]:%i", IRC_SVR_STR, IRCItem.server, IRCItem.port);
651
652 /* Connect to IRC server as client. */
653 if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, IRC_SVR_LEN) == -1)
654 {
655 log_printf("IRC -> connect(): error connecting to %s: %s",
656 IRCItem.server, strerror(errno));
657
658 /* Close socket and try to connect again */
659 irc_close();
660 return;
661 }
662
663 #ifdef HAVE_LIBCRYPTO
664 if (ssl_handle)
665 {
666 int ret = SSL_connect(ssl_handle);
667 if (ret != 1)
668 {
669 const char *error = ERR_error_string(ERR_get_error(), NULL);
670 log_printf("IRC -> connect(): error performing TLS handshake with %s: %s",
671 IRCItem.server, error);
672
673 /* Close socket and try to connect again */
674 irc_close();
675 return;
676 }
677 }
678 #endif
679
680 irc_send("NICK %s", IRCItem.nick);
681
682 if (!EmptyString(IRCItem.password))
683 irc_send("PASS %s", IRCItem.password);
684
685 irc_send("USER %s %s %s :%s",
686 IRCItem.username,
687 IRCItem.username,
688 IRCItem.username,
689 IRCItem.realname);
690 time(&IRC_LAST);
691 }
692
693 /* irc_parse
694 *
695 * irc_parse is called by irc_read when a full line of data
696 * is ready to be parsed.
697 *
698 * Parameters: NONE
699 * Return: NONE
700 */
701 static void
702 irc_parse(void)
703 {
704 char *pos;
705
706 /*
707 * parv stores the parsed token, parc is the count of the parsed
708 * tokens
709 *
710 * parv[0] is ALWAYS the source, and is the server name of the
711 * source did not exist
712 */
713 char *parv[17];
714 static const unsigned int parv_size = sizeof(parv) / sizeof(parv[0]);
715 unsigned int parc = 1;
716 char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
717
718 struct CommandHash
719 {
720 const char *command;
721 void (*handler)(char *[], unsigned int, const char *, const char *);
722 };
723
724 /*
725 * Table should be ordered with most occuring (or priority)
726 * commands at the top of the list.
727 */
728 static const struct CommandHash COMMAND_TABLE[] =
729 {
730 { .command = "NOTICE", .handler = m_notice },
731 { .command = "PRIVMSG", .handler = m_privmsg },
732 { .command = "PING", .handler = m_ping },
733 { .command = "INVITE", .handler = m_invite },
734 { .command = "001", .handler = m_perform },
735 { .command = "302", .handler = m_userhost },
736 { .command = "471", .handler = m_cannot_join },
737 { .command = "473", .handler = m_cannot_join },
738 { .command = "474", .handler = m_cannot_join },
739 { .command = "475", .handler = m_cannot_join },
740 { .command = "KILL", .handler = m_kill },
741 { .command = NULL }
742 };
743
744 if (IRC_RAW_LEN == 0)
745 return;
746
747 if (OPT_DEBUG >= 2)
748 log_printf("IRC READ -> %s", IRC_RAW);
749
750 time(&IRC_LAST);
751
752 /* Store a copy of IRC_RAW for the handlers (for functions that need PROOF) */
753 strlcpy(msg, IRC_RAW, sizeof(msg));
754
755 /* parv[0] is always the source */
756 if (IRC_RAW[0] == ':')
757 parv[0] = IRC_RAW + 1;
758 else
759 {
760 parv[0] = IRCItem.server;
761 parv[parc++] = IRC_RAW;
762 }
763
764 pos = IRC_RAW;
765
766 while ((pos = strchr(pos, ' ')) && parc < parv_size)
767 {
768 /* Avoid excessive spaces and end of IRC_RAW */
769 if (*(pos + 1) == ' ' || *(pos + 1) == '\0')
770 {
771 pos++;
772 continue;
773 }
774
775 /* Anything after a : is considered the final string of the message */
776 if (*(pos + 1) == ':')
777 {
778 parv[parc++] = pos + 2;
779 *pos = '\0';
780 break;
781 }
782
783 /*
784 * Set the next parv at this position and replace the space with a
785 * \0 for the previous parv
786 */
787 parv[parc++] = pos + 1;
788 *pos = '\0';
789 pos++;
790 }
791
792 /*
793 * Determine which command this is from the command table
794 * and let the handler for that command take control
795 */
796 for (const struct CommandHash *cmd = COMMAND_TABLE; cmd->command; ++cmd)
797 {
798 if (strcasecmp(cmd->command, parv[1]) == 0)
799 {
800 cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
801 break;
802 }
803 }
804 }
805
806 /* irc_read
807 *
808 * irc_read is called by irc_cycle when new data is ready to be
809 * read from the irc server.
810 *
811 * Parameters: NONE
812 * Return: NONE
813 */
814 static void
815 irc_read(void)
816 {
817 ssize_t len;
818 char c;
819
820 while (1)
821 {
822 #ifdef HAVE_LIBCRYPTO
823 if (ssl_handle)
824 len = SSL_read(ssl_handle, &c, 1);
825 else
826 #endif
827 len = recv(IRC_FD, &c, 1, 0);
828
829 if (len <= 0)
830 break;
831
832 if (c == '\r')
833 continue;
834
835 if (c == '\n')
836 {
837 /* Null string. */
838 IRC_RAW[IRC_RAW_LEN] = '\0';
839
840 /* Parse line. */
841 irc_parse();
842
843 /* Reset counter. */
844 IRC_RAW_LEN = 0;
845 break;
846 }
847
848 if (c != '\0')
849 IRC_RAW[IRC_RAW_LEN++] = c;
850 }
851
852 if ((len <= 0) && (errno != EAGAIN))
853 {
854 if (errno != EINTR)
855 log_printf("IRC -> Error reading data from server: %s", strerror(errno));
856
857 irc_close();
858 IRC_RAW_LEN = 0;
859 return;
860 }
861 }
862
863 /* irc_cycle
864 *
865 * Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
866 *
867 * Parameters:
868 * None
869 *
870 * Return:
871 * None
872 */
873 void
874 irc_cycle(void)
875 {
876 static struct pollfd pfd;
877
878 if (IRC_FD == -1)
879 {
880 /* Initialize negative cache. */
881 if (OptionsItem.negcache)
882 negcache_init();
883
884 /* Connect to remote host. */
885 irc_connect();
886 return; /* In case connect() immediately failed */
887 }
888
889 pfd.fd = IRC_FD;
890 pfd.events = POLLIN;
891
892 /* Block .050 seconds to avoid excessive CPU use on poll(). */
893 switch (poll(&pfd, 1, 50))
894 {
895 case 0:
896 case -1:
897 break;
898 default:
899 /* Check if IRC data is available. */
900 if (pfd.revents & POLLIN)
901 irc_read();
902 else if (pfd.revents & (POLLERR | POLLHUP))
903 irc_close();
904
905 break;
906 }
907 }
908
909 /* irc_send
910 *
911 * Send data to remote IRC host.
912 *
913 * Parameters:
914 * data: Format of data to send
915 * ...: varargs to format with
916 *
917 * Return: NONE
918 */
919 void
920 irc_send(const char *data, ...)
921 {
922 va_list arglist;
923 char buf[MSGLENMAX];
924
925 va_start(arglist, data);
926 ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
927 va_end(arglist);
928
929 if (OPT_DEBUG >= 2)
930 log_printf("IRC SEND -> %s", buf);
931
932 if (len > 510)
933 len = 510;
934
935 buf[len++] = '\r';
936 buf[len++] = '\n';
937
938 ssize_t sent;
939 #ifdef HAVE_LIBCRYPTO
940 if (ssl_handle)
941 sent = SSL_write(ssl_handle, buf, len);
942 else
943 #endif
944 sent = send(IRC_FD, buf, len, 0);
945
946 if (sent != len)
947 {
948 /* Return of -1 indicates error sending data; we reconnect. */
949 log_printf("IRC -> Error sending data to server: %s", strerror(errno));
950 irc_close();
951 }
952 }
953
954 /* irc_send_channels
955 *
956 * Send privmsg to all channels.
957 *
958 * Parameters:
959 * data: Format of data to send
960 * ...: varargs to format with
961 *
962 * Return: NONE
963 */
964 void
965 irc_send_channels(const char *data, ...)
966 {
967 const node_t *node;
968 va_list arglist;
969 char buf[MSGLENMAX];
970
971 va_start(arglist, data);
972 vsnprintf(buf, sizeof(buf), data, arglist);
973 va_end(arglist);
974
975 LIST_FOREACH(node, IRCItem.channels.head)
976 {
977 const struct ChannelConf *chan = node->data;
978
979 irc_send("PRIVMSG %s :%s", chan->name, buf);
980 }
981 }
982
983 /* irc_timer
984 *
985 * Functions to be performed every ~seconds.
986 *
987 * Parameters: NONE
988 * Return: NONE
989 */
990 void
991 irc_timer(void)
992 {
993 time_t present, delta;
994
995 time(&present);
996
997 delta = present - IRC_LAST;
998
999 /* No data in IRCItem.readtimeout seconds */
1000 if (delta >= IRCItem.readtimeout)
1001 {
1002 log_printf("IRC -> Timeout awaiting data from server.");
1003 irc_close();
1004
1005 /* Make sure we don't do this again for a while */
1006 time(&IRC_LAST);
1007 }
1008 else if (delta >= IRCItem.readtimeout / 2)
1009 {
1010 /*
1011 * Generate some data so high ping times don't cause uneeded
1012 * reconnections
1013 */
1014 irc_send("PING :HOPM");
1015 }
1016 }

Properties

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

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