ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 9501
Committed: Sun Jul 5 05:41:27 2020 UTC (6 years ago) by michael
Content type: text/x-csrc
File size: 22814 byte(s)
Log Message:
- Add tls_hostname_verification switch to allow the server identity check to be turned off if desired

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

Properties

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