ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/branches/1.1.x/src/irc.c
Revision: 9726
Committed: Mon Nov 16 10:28:41 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

# User Rev Content
1 michael 5052 /*
2 michael 5351 * Copyright (c) 2002-2003 Erik Fears
3 michael 9103 * 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 9477 #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    
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 michael 5695 static char IRC_RAW[MSGLENMAX]; /* Buffer to read data into */
64     static unsigned int IRC_RAW_LEN; /* Position of IRC_RAW */
65 michael 6215 static int IRC_FD = -1; /* File descriptor for IRC client */
66 michael 5052
67 michael 5695 static struct sockaddr_storage IRC_SVR; /* Sock Address Struct for IRC server */
68 michael 9514 static socklen_t IRC_SVR_LEN;
69     static char IRC_SVR_STR[INET6_ADDRSTRLEN];
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 9477 #ifdef HAVE_LIBCRYPTO
74     static SSL_CTX *ssl_ctx;
75     static SSL *ssl_handle;
76     #endif
77 michael 5052
78 michael 9477
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 8138 get_channel(const char *name)
91 michael 5052 {
92 michael 5114 node_t *node;
93 michael 5052
94 michael 8580 LIST_FOREACH(node, IRCItem.channels.head)
95 michael 5114 {
96 michael 5116 struct ChannelConf *item = node->data;
97 michael 5052
98 michael 8138 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 9516 log_printf("IRC -> Connected to %s[%s]:%i", IRC_SVR_STR, IRCItem.server, IRCItem.port);
123 michael 5052
124 michael 5114 /* Identify to nickserv if needed */
125 michael 8580 if (!EmptyString(IRCItem.nickserv))
126     irc_send("%s", IRCItem.nickserv);
127 michael 5052
128 michael 5114 /* Oper */
129 michael 9516 if (!EmptyString(IRCItem.oper))
130     irc_send("OPER %s", IRCItem.oper);
131 michael 5052
132 michael 5114 /* Set modes */
133 michael 9516 if (!EmptyString(IRCItem.mode))
134     irc_send("MODE %s %s", IRCItem.nick, IRCItem.mode);
135 michael 5052
136 michael 5114 /* Set Away */
137 michael 8580 if (!EmptyString(IRCItem.away))
138     irc_send("AWAY :%s", IRCItem.away);
139 michael 5052
140 michael 5114 /* Perform */
141 michael 8580 LIST_FOREACH(node, IRCItem.performs.head)
142 michael 5114 irc_send("%s", node->data);
143 michael 5052
144 michael 5114 /* Join all listed channels. */
145 michael 8580 LIST_FOREACH(node, IRCItem.channels.head)
146 michael 5114 {
147 michael 5279 const struct ChannelConf *channel = node->data;
148 michael 5052
149 michael 5114 if (EmptyString(channel->name))
150     continue;
151 michael 5052
152 michael 5114 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 michael 5052 }
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 michael 5114 static void
169 michael 6198 m_ping(char *parv[], unsigned int parc, const char *msg, const char *source_p)
170 michael 5052 {
171 michael 5114 if (parc < 3)
172     return;
173 michael 5052
174 michael 5114 if (OPT_DEBUG >= 2)
175     log_printf("IRC -> PING? PONG!");
176 michael 5052
177 michael 5114 irc_send("PONG %s", parv[2]);
178 michael 5052 }
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 michael 5114 static void
191 michael 6198 m_invite(char *parv[], unsigned int parc, const char *msg, const char *source_p)
192 michael 5052 {
193 michael 5114 if (parc < 4)
194     return;
195 michael 5052
196 michael 5114 log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
197 michael 5052
198 michael 9329 const struct ChannelConf *channel = get_channel(parv[3]);
199     if (channel == NULL)
200 michael 5114 return;
201    
202     irc_send("JOIN %s %s", channel->name, channel->key);
203 michael 5052 }
204    
205 michael 6167 /* 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 michael 6198 m_ctcp(char *parv[], unsigned int parc, const char *msg, const char *source_p)
216 michael 6167 {
217     if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
218 michael 9669 irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s\001",
219     source_p, VERSION);
220 michael 6167 }
221    
222 michael 5052 /* 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 michael 5114 static void
233 michael 6198 m_privmsg(char *parv[], unsigned int parc, const char *msg, const char *source_p)
234 michael 5052 {
235 michael 5114 if (source_p == NULL)
236     return;
237 michael 5052
238 michael 5114 if (parc < 4)
239     return;
240 michael 5052
241 michael 5114 /* CTCP */
242 michael 6011 if (*parv[3] == '\001')
243 michael 6005 {
244 michael 5114 m_ctcp(parv, parc, msg, source_p);
245 michael 6005 return;
246     }
247 michael 5052
248 michael 5114 /* Only interested in privmsg to channels */
249 michael 6011 if (*parv[2] != '#' && *parv[2] != '&')
250 michael 5114 return;
251 michael 5052
252 michael 5114 /* Get a target */
253 michael 9329 const struct ChannelConf *channel = get_channel(parv[2]);
254     if (channel == NULL)
255 michael 5114 return;
256 michael 5052
257 michael 8125 int hit = strncasecmp(parv[3], "!all ", 5) == 0;
258     if (hit == 0)
259     {
260 michael 8580 size_t nick_len = strlen(IRCItem.nick);
261 michael 5052
262 michael 8580 if (strncasecmp(parv[3], IRCItem.nick, nick_len) == 0)
263 michael 8225 hit = *(parv[3] + nick_len) == ' ' ||
264     *(parv[3] + nick_len) == ',' ||
265     *(parv[3] + nick_len) == ':';
266 michael 8125 }
267 michael 5052
268 michael 8125 if (hit)
269 michael 5114 /* XXX command_parse will alter parv[3]. */
270 michael 8194 command_parse(parv[3], channel->name, source_p);
271 michael 5114 }
272 michael 5052
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 michael 5114 static void
285 michael 6198 m_notice(char *parv[], unsigned int parc, const char *msg, const char *source_p)
286 michael 5052 {
287 michael 5114 static regex_t *preg = NULL;
288     regmatch_t pmatch[5];
289 michael 5279 int errnum;
290 michael 5338 const char *user[4];
291 michael 5405 const node_t *node;
292 michael 5052
293 michael 5114 /* Not interested in notices from users */
294     if (source_p)
295     return;
296 michael 5052
297 michael 5322 if (parc < 4)
298     return;
299    
300 michael 5114 /* Compile the regular expression if it has not been already */
301     if (preg == NULL)
302     {
303 michael 6149 preg = xcalloc(sizeof(*preg));
304 michael 5052
305 michael 8580 if ((errnum = regcomp(preg, IRCItem.connregex, REG_ICASE | REG_EXTENDED)))
306 michael 5114 {
307 michael 5290 char errmsg[256];
308    
309     regerror(errnum, preg, errmsg, sizeof(errmsg));
310 michael 6014 log_printf("IRC REGEX -> Error when compiling regular expression: %s", errmsg);
311 michael 5052
312 michael 5426 xfree(preg);
313 michael 5114 preg = NULL;
314 michael 5052 return;
315 michael 5114 }
316     }
317 michael 5052
318 michael 5114 /* Match the expression against the possible connection notice */
319     if (regexec(preg, parv[3], 5, pmatch, 0))
320     return;
321 michael 5052
322 michael 5114 if (OPT_DEBUG > 0)
323     log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
324 michael 5052
325 michael 5114 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 michael 5052
331 michael 5114 /*
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 michael 5279 for (unsigned int i = 0; i < 4; ++i)
340 michael 5114 {
341     user[i] = (parv[3] + pmatch[i + 1].rm_so);
342     *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
343     }
344 michael 5052
345 michael 5114 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 michael 5052
349 michael 8580 LIST_FOREACH(node, IRCItem.notices.head)
350 michael 5405 irc_send("NOTICE %s :%s", user[0], node->data);
351    
352 michael 5114 /* Pass this information off to scan.c */
353     scan_connect(user, msg);
354 michael 5052
355 michael 5114 /* Record the connect for stats purposes */
356     stats_connect();
357 michael 5052 }
358    
359     /* m_userhost
360     *
361     * parv[0] = source
362     * parv[1] = USERHOST
363 michael 5069 * parv[2] = target (hopm)
364 michael 5052 * 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 michael 5114 static void
371 michael 6198 m_userhost(char *parv[], unsigned int parc, const char *msg, const char *source_p)
372 michael 5052 {
373 michael 5114 if (parc < 4)
374     return;
375 michael 5052
376 michael 5114 command_userhost(parv[3]);
377 michael 5052 }
378    
379     /* m_cannot_join
380     *
381     * parv[0] = source
382     * parv[1] = numeric
383 michael 5069 * parv[2] = target (hopm)
384 michael 5052 * parv[3] = channel
385     * parv[4] = error text
386     */
387 michael 5114 static void
388 michael 6198 m_cannot_join(char *parv[], unsigned int parc, const char *msg, const char *source_p)
389 michael 5052 {
390 michael 5114 if (parc < 5)
391     return;
392 michael 5052
393 michael 5114 /* Is it one of our channels? */
394 michael 9329 const struct ChannelConf *channel = get_channel(parv[3]);
395     if (channel == NULL)
396 michael 5114 return;
397 michael 5052
398 michael 5114 if (EmptyString(channel->invite))
399     return;
400 michael 5052
401 michael 5114 irc_send("%s", channel->invite);
402 michael 5052 }
403    
404     /* m_kill
405     *
406     * parv[0] = source
407     * parv[1] = numeric
408 michael 5069 * parv[2] = target (hopm)
409 michael 5052 * parv[3] = channel
410     * parv[4] = error text
411     */
412 michael 5114 static void
413 michael 6198 m_kill(char *parv[], unsigned int parc, const char *msg, const char *source_p)
414 michael 5052 {
415 michael 5084 /* Restart hopm to rehash */
416     main_restart();
417 michael 5052 }
418 michael 6167
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 michael 6198 static const char *
431 michael 6167 userinfo_create(const char *source)
432     {
433 michael 6198 static char name[MSGLENMAX];
434     unsigned int has_user = 0;
435     unsigned int has_host = 0;
436 michael 6167
437 michael 6198 strlcpy(name, source, sizeof(name));
438 michael 6167
439 michael 6198 for (char *p = name; *p; ++p)
440 michael 6167 {
441     if (*p == '!')
442     {
443     *p = '\0';
444 michael 6198 ++has_user;
445 michael 6167 continue;
446     }
447    
448 michael 6198 if (*p == '@' && has_user)
449 michael 6167 {
450 michael 6198 ++has_host;
451 michael 6167 continue;
452     }
453     }
454    
455 michael 6198 if (has_user == 1 && has_host == 1)
456     return name;
457     return NULL;
458 michael 6167 };
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 michael 9514 int n;
474 michael 9329 const void *address;
475 michael 9518 struct addrinfo hints, *res = NULL;
476 michael 6167
477 michael 6215 assert(IRC_FD == -1);
478 michael 6167
479     memset(&IRC_SVR, 0, sizeof(IRC_SVR));
480    
481 michael 9726 if (!EmptyString(IRCItem.bind))
482 michael 9518 {
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 michael 9726 n = getaddrinfo(IRCItem.bind, NULL, &hints, &res);
490 michael 9518 if (n)
491     {
492     log_printf("IRC -> getaddrinfo() error: %s", gai_strerror(n));
493     exit(EXIT_FAILURE);
494     }
495     }
496    
497 michael 6167 /* Resolve IRC host. */
498 michael 9518 if ((res == NULL || res->ai_family == AF_INET6) && (address = firedns_resolveip6(IRCItem.server)))
499 michael 6167 {
500     struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
501    
502 michael 9514 IRC_SVR_LEN = sizeof(*in);
503 michael 6167 IRC_SVR.ss_family = AF_INET6;
504 michael 8580 in->sin6_port = htons(IRCItem.port);
505 michael 6167 memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
506     }
507 michael 9518 else if ((res == NULL || res->ai_family == AF_INET) && (address = firedns_resolveip4(IRCItem.server)))
508 michael 6167 {
509     struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
510    
511 michael 9514 IRC_SVR_LEN = sizeof(*in);
512 michael 6167 IRC_SVR.ss_family = AF_INET;
513 michael 8580 in->sin_port = htons(IRCItem.port);
514 michael 6167 memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
515     }
516     else
517     {
518 michael 9518 log_printf("IRC -> Error resolving host '%s': %s", IRCItem.server,
519 michael 6167 firedns_strerror(firedns_errno));
520     exit(EXIT_FAILURE);
521     }
522    
523 michael 9514 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 michael 6167 /* 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 michael 9518 if (res)
542 michael 6167 {
543 michael 9518 if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
544 michael 6167 {
545 michael 9726 log_printf("IRC -> error binding to %s: %s", IRCItem.bind, strerror(errno));
546 michael 6167 exit(EXIT_FAILURE);
547     }
548    
549     freeaddrinfo(res);
550     }
551 michael 9477
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 michael 9487 ssl_ctx = SSL_CTX_new(TLS_client_method());
562 michael 9477 if (ssl_ctx == NULL)
563     {
564     log_printf("IRC -> unable to create SSL context");
565     exit(EXIT_FAILURE);
566     }
567    
568 michael 9524 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 michael 9487 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
594 michael 9477 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 michael 9500 if (IRCItem.tls_hostname_verification)
601 michael 9477 {
602 michael 9500 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 michael 9477 }
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 michael 6167 }
619    
620 michael 9507 /* irc_close
621 michael 6167 *
622     * Close connection to IRC server.
623     *
624     * Parameters: NONE
625     *
626     * Return: NONE
627     */
628     static void
629 michael 9507 irc_close(void)
630 michael 6167 {
631 michael 6215 if (IRC_FD > -1)
632     {
633 michael 9477 #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 michael 6167 close(IRC_FD);
643 michael 6215 IRC_FD = -1; /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
644     }
645 michael 6167
646 michael 8580 log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
647 michael 6167 }
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 michael 9507 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 michael 9494 return;
670 michael 9507 }
671 michael 9494
672 michael 9507 time(&IRC_LASTRECONNECT);
673     time(&IRC_LAST);
674 michael 9494
675 michael 9507 irc_init();
676    
677 michael 9514 log_printf("IRC -> Attempting to connect to %s[%s]:%i", IRC_SVR_STR, IRCItem.server, IRCItem.port);
678    
679 michael 6167 /* Connect to IRC server as client. */
680 michael 9514 if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, IRC_SVR_LEN) == -1)
681 michael 6167 {
682     log_printf("IRC -> connect(): error connecting to %s: %s",
683 michael 8580 IRCItem.server, strerror(errno));
684 michael 6167
685 michael 9507 /* Close socket and try to connect again */
686     irc_close();
687 michael 6167 return;
688     }
689    
690 michael 9477 #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 michael 9507 /* Close socket and try to connect again */
701     irc_close();
702 michael 9477 return;
703     }
704     }
705     #endif
706    
707 michael 8580 irc_send("NICK %s", IRCItem.nick);
708 michael 6167
709 michael 8580 if (!EmptyString(IRCItem.password))
710     irc_send("PASS %s", IRCItem.password);
711 michael 6167
712     irc_send("USER %s %s %s :%s",
713 michael 8580 IRCItem.username,
714     IRCItem.username,
715     IRCItem.username,
716     IRCItem.realname);
717 michael 6167 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 michael 9484 static const unsigned int parv_size = sizeof(parv) / sizeof(parv[0]);
742 michael 6167 unsigned int parc = 1;
743     char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
744    
745 michael 8177 struct CommandHash
746     {
747     const char *command;
748     void (*handler)(char *[], unsigned int, const char *, const char *);
749     };
750    
751 michael 6167 /*
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 michael 8156 { .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 michael 6167 };
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 michael 8580 parv[0] = IRCItem.server;
788 michael 6167 parv[parc++] = IRC_RAW;
789     }
790    
791     pos = IRC_RAW;
792    
793 michael 9484 while ((pos = strchr(pos, ' ')) && parc < parv_size)
794 michael 6167 {
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 michael 6198 cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
828 michael 6167 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 michael 6265 ssize_t len;
845 michael 6167 char c;
846    
847 michael 9477 while (1)
848 michael 6167 {
849 michael 9477 #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 michael 9492 break;
858    
859 michael 6167 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 michael 9522 return;
873 michael 6167 }
874    
875     if (c != '\0')
876     IRC_RAW[IRC_RAW_LEN++] = c;
877     }
878    
879     if ((len <= 0) && (errno != EAGAIN))
880     {
881 michael 6271 if (errno != EINTR)
882     log_printf("IRC -> Error reading data from server: %s", strerror(errno));
883    
884 michael 9507 irc_close();
885 michael 6167 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 michael 6215 if (IRC_FD == -1)
905 michael 6167 {
906 michael 8138 /* Initialize negative cache. */
907 michael 8580 if (OptionsItem.negcache)
908 michael 7880 negcache_init();
909 michael 6167
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 michael 9507 irc_close();
930 michael 6167
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 michael 9471 ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
953 michael 6167 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 michael 9477 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 michael 6167 {
974     /* Return of -1 indicates error sending data; we reconnect. */
975     log_printf("IRC -> Error sending data to server: %s", strerror(errno));
976 michael 9507 irc_close();
977 michael 6167 }
978     }
979    
980 michael 6563 /* irc_send_channels
981 michael 6167 *
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 michael 8580 LIST_FOREACH(node, IRCItem.channels.head)
1002 michael 6167 {
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 michael 8580 /* No data in IRCItem.readtimeout seconds */
1026     if (delta >= IRCItem.readtimeout)
1027 michael 6167 {
1028     log_printf("IRC -> Timeout awaiting data from server.");
1029 michael 9507 irc_close();
1030 michael 6167
1031     /* Make sure we don't do this again for a while */
1032     time(&IRC_LAST);
1033     }
1034 michael 8580 else if (delta >= IRCItem.readtimeout / 2)
1035 michael 6167 {
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