ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 9488
Committed: Sat Jul 4 19:30:52 2020 UTC (6 years ago) by michael
Content type: text/x-csrc
File size: 22104 byte(s)
Log Message:
- irc.c:irc_init(): we only do TLSv1.2 and higher

File Contents

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

Properties

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