bug fixes (from code coverage tests)
[srvx.git] / src / proto-common.c
1 /* proto-common.c - common IRC protocol parsing/sending support
2  * Copyright 2000-2004 srvx Development Team
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.  Important limitations are
8  * listed in the COPYING file that accompanies this software.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, email srvx-maintainers@srvx.net.
17  */
18
19 #include "conf.h"
20 #include "gline.h"
21 #include "ioset.h"
22 #include "log.h"
23 #include "nickserv.h"
24 #include "timeq.h"
25 #ifdef HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
27 #endif
28 #ifdef HAVE_NETINET_IN_H
29 #include <netinet/in.h>
30 #endif
31 #ifdef HAVE_ARPA_INET_H
32 #include <arpa/inet.h>
33 #endif
34
35 unsigned int lines_processed;
36 FILE *replay_file;
37 struct io_fd *socket_io_fd;
38 int force_n2k;
39 const char *hidden_host_suffix;
40
41 static char replay_line[MAXLEN+80];
42 static int ping_freq;
43 static int ping_timeout;
44 static int replay_connected;
45 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
46 static struct userList dead_users;
47
48 extern struct cManagerNode cManager;
49 extern unsigned long burst_length;
50 extern struct cManagerNode cManager;
51 extern struct policer_params *oper_policer_params, *luser_policer_params;
52 extern server_link_func_t *slf_list;
53 extern unsigned int slf_size, slf_used;
54 extern new_user_func_t *nuf_list;
55 extern unsigned int nuf_size, nuf_used;
56 extern del_user_func_t *duf_list;
57 extern unsigned int duf_size, duf_used;
58 extern time_t boot_time;
59
60 void received_ping(void);
61
62 static int replay_read(void);
63 static dict_t irc_func_dict;
64
65 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
66 typedef void (*foreach_nonchan) (char *name, void *data);
67 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
68 typedef void (*foreach_nonuser) (char *name, void *data);
69 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
70
71 static void
72 uplink_readable(struct io_fd *fd) {
73     static char buffer[MAXLEN];
74     char *eol;
75     int pos;
76
77     pos = ioset_line_read(fd, buffer, sizeof(buffer));
78     if (pos <= 0) {
79         close_socket();
80         return;
81     }
82     if ((eol = strpbrk(buffer, "\r\n"))) *eol = 0;
83     log_replay(MAIN_LOG, false, buffer);
84     if (cManager.uplink->state != DISCONNECTED)
85         parse_line(buffer, 0);
86     lines_processed++;
87 }
88
89 void
90 socket_destroyed(struct io_fd *fd)
91 {
92     if (fd && fd->eof)
93         log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
94     socket_io_fd = NULL;
95     cManager.uplink->state = DISCONNECTED;
96     if (self->uplink) DelServer(self->uplink, 0, NULL);
97 }
98
99 void replay_event_loop(void)
100 {
101     while (!quit_services) {
102         if (!replay_connected) {
103             /* this time fudging is to get some of the logging right */
104             self->link = self->boot = now;
105             cManager.uplink->state = AUTHENTICATING;
106             irc_introduce(cManager.uplink->password);
107             replay_connected = 1;
108         } else if (!replay_read()) {
109             log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
110             close_socket();
111         }
112         timeq_run();
113     }
114 }
115
116 int
117 create_socket_client(struct uplinkNode *target)
118 {
119     int port = target->port;
120     const char *addr = target->host;
121
122     if (replay_file)
123         return feof(replay_file) ? 0 : 1;
124
125     if (socket_io_fd) {
126         /* Leave the existing socket open, say we failed. */
127         log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
128         return 0;
129     }
130
131     log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
132
133     socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
134     if (!socket_io_fd) {
135         log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
136         target->state = DISCONNECTED;
137         target->tries++;
138         return 0;
139     }
140     socket_io_fd->readable_cb = uplink_readable;
141     socket_io_fd->destroy_cb = socket_destroyed;
142     socket_io_fd->line_reads = 1;
143     socket_io_fd->wants_reads = 1;
144     log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
145     cManager.uplink = target;
146     target->state = AUTHENTICATING;
147     target->tries = 0;
148     return 1;
149 }
150
151 void
152 replay_read_line(void)
153 {
154     struct tm timestamp;
155     time_t new_time;
156
157     if (replay_line[0]) return;
158   read_line:
159     if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
160         if (feof(replay_file)) {
161             quit_services = 1;
162             memset(replay_line, 0, sizeof(replay_line));
163             return;
164         }
165     }
166     if ((replay_line[0] != '[')
167         || (replay_line[3] != ':')
168         || (replay_line[6] != ':')
169         || (replay_line[9] != ' ')
170         || (replay_line[12] != '/')
171         || (replay_line[15] != '/')
172         || (replay_line[20] != ']')
173         || (replay_line[21] != ' ')) {
174         log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
175         goto read_line;
176     }
177     timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
178     timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
179     timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
180     timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
181     timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
182     timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
183     timestamp.tm_isdst = 0;
184     new_time = mktime(&timestamp);
185     if (new_time == -1) {
186         log_module(MAIN_LOG, LOG_ERROR, "Unable to parse time struct tm_sec=%d tm_min=%d tm_hour=%d tm_mday=%d tm_mon=%d tm_year=%d", timestamp.tm_sec, timestamp.tm_min, timestamp.tm_hour, timestamp.tm_mday, timestamp.tm_mon, timestamp.tm_year);
187     } else {
188         now = new_time;
189     }
190
191     if (strncmp(replay_line+22, "(info) ", 7))
192         goto read_line;
193     return;
194 }
195
196 static int
197 replay_read(void)
198 {
199     size_t len;
200     char read_line[MAXLEN];
201     while (1) {
202         replay_read_line();
203         /* if it's a sent line, break out to handle it */
204         if (!strncmp(replay_line+29, "   ", 3))
205             break;
206         if (!strncmp(replay_line+29, "W: ", 3)) {
207             log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
208             replay_line[0] = 0;
209         } else {
210             return 0;
211         }
212     }
213     log_replay(MAIN_LOG, false, replay_line+32);
214     safestrncpy(read_line, replay_line+32, sizeof(read_line));
215     len = strlen(read_line);
216     if (read_line[len-1] == '\n')
217         read_line[--len] = 0;
218     replay_line[0] = 0;
219     parse_line(read_line, 0);
220     lines_processed++;
221     return 1;
222 }
223
224 static void
225 replay_write(char *text)
226 {
227     replay_read_line();
228     if (strncmp(replay_line+29, "W: ", 3)) {
229         log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
230         return;
231     } else {
232         if (strcmp(replay_line+32, text)) {
233             log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
234         } else {
235             log_replay(MAIN_LOG, true, text);
236         }
237         replay_line[0] = 0;
238     }
239 }
240
241 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
242
243 void
244 putsock(const char *text, ...)
245 {
246     va_list arg_list;
247     char buffer[MAXLEN];
248     int pos;
249
250     if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
251     buffer[0] = '\0';
252     va_start(arg_list, text);
253     pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
254     va_end(arg_list);
255     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
256     buffer[pos] = 0;
257     if (!replay_file) {
258         log_replay(MAIN_LOG, true, buffer);
259         buffer[pos++] = '\n';
260         buffer[pos] = 0;
261         ioset_write(socket_io_fd, buffer, pos);
262     } else {
263         replay_write(buffer);
264     }
265 }
266
267 void
268 close_socket(void)
269 {
270     if (replay_file) {
271         replay_connected = 0;
272         socket_destroyed(socket_io_fd);
273     } else {
274         ioset_close(socket_io_fd->fd, 1);
275     }
276 }
277
278 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
279 typedef CMD_FUNC(cmd_func_t);
280
281 static void timed_ping_timeout(void *data);
282
283 /* Ping state is kept in the timeq (only one of these two can be in
284  * the queue at any given time). */
285 void
286 timed_send_ping(UNUSED_ARG(void *data))
287 {
288     irc_ping(self->name);
289     timeq_add(now + ping_timeout, timed_ping_timeout, 0);
290 }
291
292 static void
293 timed_ping_timeout(UNUSED_ARG(void *data))
294 {
295     /* Uplink "health" tracking could be accomplished by counting the
296        number of ping timeouts that happen for each uplink. After the
297        timeouts per time period exceeds some amount, the uplink could
298        be marked as unavalable.*/
299     irc_squit(self, "Ping timeout.", NULL);
300 }
301
302 static CMD_FUNC(cmd_pass)
303 {
304     const char *true_pass;
305
306     if (argc < 2)
307         return 0;
308     true_pass = cManager.uplink->their_password;
309     if (true_pass && strcmp(true_pass, argv[1])) {
310         /* It might be good to mark the uplink as unavailable when
311            this happens, though there should be a way of resetting
312            the flag. */
313         irc_squit(self, "Incorrect password received.", NULL);
314         return 1;
315     }
316
317     cManager.uplink->state = BURSTING;
318     return 1;
319 }
320
321 static CMD_FUNC(cmd_dummy)
322 {
323     /* we don't care about these messages */
324     return 1;
325 }
326
327 static CMD_FUNC(cmd_error)
328 {
329     if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
330     log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
331
332     if (cManager.uplink->state != CONNECTED) {
333         /* Error messages while connected should be fine. */
334         cManager.uplink->flags |= UPLINK_UNAVAILABLE;
335         log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
336     }
337
338     close_socket();
339     return 1;
340 }
341
342 static CMD_FUNC(cmd_stats)
343 {
344     struct userNode *un;
345
346     if (argc < 2)
347         return 0;
348     if (!(un = GetUserH(origin)))
349         return 0;
350     switch (argv[1][0]) {
351     case 'u': {
352         unsigned int uptime = now - boot_time;
353         irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
354                     uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
355         irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
356                     self->max_clients+1, self->max_clients);
357         break;
358     }
359     default: /* unrecognized/unhandled stats output */ break;
360     }
361     irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
362     return 1;
363 }
364
365 static CMD_FUNC(cmd_whois)
366 {
367     struct userNode *from;
368     struct userNode *who;
369
370     if (argc < 3)
371         return 0;
372     if (!(from = GetUserH(origin))) {
373         log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
374         return 0;
375     }
376     if(!(who = GetUserH(argv[2]))) {
377         irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
378         return 1;
379     }
380     if (IsHiddenHost(who) && !IsOper(from)) {
381         /* Just stay quiet. */
382         return 1;
383     }
384     irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
385     irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
386     if (IsOper(who)) {
387         irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
388     }
389     irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
390     return 1;
391 }
392
393 static CMD_FUNC(cmd_version)
394 {
395     struct userNode *user;
396     if (!(user = GetUserH(origin))) {
397         log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
398         return 0;
399     }
400     irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
401     return 1;
402 }
403
404 static CMD_FUNC(cmd_admin)
405 {
406     struct userNode *user;
407     struct string_list *slist;
408
409     if (!(user = GetUserH(origin))) {
410         log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
411         return 0;
412     }
413     if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
414         unsigned int i;
415
416         irc_numeric(user, 256, ":Administrative info about %s", self->name);
417         for (i = 0; i < slist->used && i < 3; i++)
418             irc_numeric(user, 257 + i, ":%s", slist->list[i]);
419     } else {
420         irc_numeric(user, 423, ":No administrative info available");
421     }
422     return 1;
423 }
424
425 static void
426 recalc_bursts(struct server *eob_server)
427 {
428     unsigned int nn;
429     eob_server->burst = eob_server->self_burst;
430     if (eob_server->uplink != self)
431         eob_server->burst = eob_server->burst || eob_server->uplink->burst;
432     for (nn=0; nn < eob_server->children.used; nn++)
433         recalc_bursts(eob_server->children.list[nn]);
434 }
435
436 static struct chanmsg_func {
437     chanmsg_func_t func;
438     struct userNode *service;
439 } chanmsg_funcs[256]; /* indexed by trigger character */
440
441 struct privmsg_desc {
442     struct userNode *user;
443     char *text;
444     unsigned int is_notice : 1;
445     unsigned int is_qualified : 1;
446 };
447
448 static void
449 privmsg_chan_helper(struct chanNode *cn, void *data)
450 {
451     struct privmsg_desc *pd = data;
452     struct modeNode *mn;
453     struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
454
455     /* Don't complain if it can't find the modeNode because the channel might
456      * be -n */
457     if ((mn = GetUserMode(cn, pd->user)))
458         mn->idle_since = now;
459
460     /* Never send a NOTICE to a channel to one of the services */
461     if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
462         cf->func(pd->user, cn, pd->text+1, cf->service);
463 }
464
465 static void
466 privmsg_invalid(char *name, void *data)
467 {
468     struct privmsg_desc *pd = data;
469
470     if (*name == '$')
471         return;
472     irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
473 }
474
475 static void
476 part_helper(struct chanNode *cn, void *data)
477 {
478     DelChannelUser(data, cn, false, 0);
479 }
480
481 void
482 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
483 {
484     if (chanmsg_funcs[prefix].func)
485         log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
486     chanmsg_funcs[prefix].func = handler;
487     chanmsg_funcs[prefix].service = service;
488 }
489
490 struct userNode *
491 get_chanmsg_bot(unsigned char prefix)
492 {
493     return chanmsg_funcs[prefix].service;
494 }
495
496 static mode_change_func_t *mcf_list;
497 static unsigned int mcf_size = 0, mcf_used = 0;
498
499 void
500 reg_mode_change_func(mode_change_func_t handler)
501 {
502     if (mcf_used == mcf_size) {
503         if (mcf_size) {
504             mcf_size <<= 1;
505             mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
506         } else {
507             mcf_size = 8;
508             mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
509         }
510     }
511     mcf_list[mcf_used++] = handler;
512 }
513
514 struct mod_chanmode *
515 mod_chanmode_alloc(unsigned int argc)
516 {
517     struct mod_chanmode *res;
518     if (argc > 1)
519         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
520     else
521         res = calloc(1, sizeof(*res));
522     if (res)
523         res->argc = argc;
524     return res;
525 }
526
527 struct mod_chanmode *
528 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
529 {
530     struct mod_chanmode *res;
531     res = mod_chanmode_alloc(orig->argc + extra);
532     if (res) {
533         res->modes_set = orig->modes_set;
534         res->modes_clear = orig->modes_clear;
535         res->argc = orig->argc;
536         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
537     }
538     return res;
539 }
540
541 void
542 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
543 {
544     struct banNode *bn;
545     unsigned int ii, jj;
546
547     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
548     if (change->modes_set & MODE_LIMIT)
549         channel->limit = change->new_limit;
550     if (change->modes_set & MODE_KEY)
551         strcpy(channel->key, change->new_key);
552     for (ii = 0; ii < change->argc; ++ii) {
553         switch (change->args[ii].mode) {
554         case MODE_BAN:
555             /* If any existing ban is a subset of the new ban,
556              * silently remove it.  The new ban is not allowed
557              * to be more specific than an existing ban.
558              */
559             for (jj=0; jj<channel->banlist.used; ++jj) {
560                 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
561                     banList_remove(&channel->banlist, channel->banlist.list[jj]);
562                     free(channel->banlist.list[jj]);
563                     jj--;
564                 }
565             }
566             bn = calloc(1, sizeof(*bn));
567             safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
568             safestrncpy(bn->who, who->nick, sizeof(bn->who));
569             bn->set = now;
570             banList_append(&channel->banlist, bn);
571             break;
572         case MODE_REMOVE|MODE_BAN:
573             for (jj=0; jj<channel->banlist.used; ++jj) {
574                 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
575                     continue;
576                 free(channel->banlist.list[jj]);
577                 banList_remove(&channel->banlist, channel->banlist.list[jj]);
578                 break;
579             }
580             break;
581         case MODE_CHANOP:
582         case MODE_VOICE:
583         case MODE_VOICE|MODE_CHANOP:
584         case MODE_REMOVE|MODE_CHANOP:
585         case MODE_REMOVE|MODE_VOICE:
586         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
587             if (change->args[ii].mode & MODE_REMOVE)
588                 change->args[ii].member->modes &= ~change->args[ii].mode;
589             else
590                 change->args[ii].member->modes |= change->args[ii].mode;
591             break;
592         default:
593             assert(0 && "Invalid mode argument");
594             continue;
595         }
596     }
597 }
598
599 void
600 mod_chanmode_free(struct mod_chanmode *change)
601 {
602     free(change);
603 }
604
605 int
606 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
607 {
608     struct mod_chanmode *change;
609     unsigned int ii;
610
611     if (!modes || !modes[0])
612         return 0;
613     if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
614         return 0;
615     if (flags & MC_ANNOUNCE)
616         mod_chanmode_announce(who, channel, change);
617     else
618         mod_chanmode_apply(who, channel, change);
619     if (flags & MC_NOTIFY)
620         for (ii = 0; ii < mcf_used; ++ii)
621             mcf_list[ii](channel, who, change);
622     mod_chanmode_free(change);
623     return 1;
624 }
625
626 int
627 irc_make_chanmode(struct chanNode *chan, char *out) {
628     struct mod_chanmode change;
629     change.modes_set = chan->modes;
630     change.modes_clear = change.argc = 0;
631     change.new_limit = chan->limit;
632     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
633     return strlen(mod_chanmode_format(&change, out));
634 }
635
636 char *
637 generate_hostmask(struct userNode *user, int options)
638 {
639     char *nickname, *ident, *hostname;
640     char *mask;
641     int len, ii;
642
643     /* figure out string parts */
644     if (options & GENMASK_OMITNICK)
645         nickname = NULL;
646     else if (options & GENMASK_USENICK)
647         nickname = user->nick;
648     else
649         nickname = "*";
650     if (options & GENMASK_STRICT_IDENT)
651         ident = user->ident;
652     else if (options & GENMASK_ANY_IDENT)
653         ident = "*";
654     else {
655         ident = alloca(strlen(user->ident)+2);
656         ident[0] = '*';
657         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
658     }
659     hostname = user->hostname;
660     if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
661         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
662         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
663     } else if (options & GENMASK_STRICT_HOST) {
664         if (options & GENMASK_BYIP)
665             hostname = inet_ntoa(user->ip);
666     } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
667         /* Should generate an IP-based hostmask.  By popular acclaim, a /16
668          * hostmask is used by default. */
669         unsigned masked_ip, mask, masklen;
670         masklen = 16;
671         mask = ~0 << masklen;
672         masked_ip = ntohl(user->ip.s_addr) & mask;
673         hostname = alloca(32);
674         if (options & GENMASK_SRVXMASK) {
675             sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
676         } else {
677             int ofs = 0;
678             for (ii=0; ii<4; ii++) {
679                 if (masklen) {
680                     ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
681                     masklen -= 8;
682                     masked_ip <<= 8;
683                 } else {
684                     ofs += sprintf(hostname+ofs, "*.");
685                 }
686             }
687             /* Truncate the last . */
688             hostname[ofs-1] = 0;
689         }
690     } else {
691         int cnt;
692         /* This heuristic could be made smarter.  Is it worth the effort? */
693         for (ii=cnt=0; hostname[ii]; ii++)
694             if (hostname[ii] == '.')
695                 cnt++;
696         if (cnt == 1) {
697             /* only a two-level domain name; leave hostname */
698         } else if (cnt == 2) {
699             for (ii=0; user->hostname[ii] != '.'; ii++) ;
700             /* Add 3 to account for the *. and \0. */
701             hostname = alloca(strlen(user->hostname+ii)+3);
702             sprintf(hostname, "*.%s", user->hostname+ii+1);
703         } else {
704             for (cnt=3, ii--; cnt; ii--)
705                 if (user->hostname[ii] == '.')
706                     cnt--;
707             /* The loop above will overshoot the dot one character;
708                we skip forward two (the one character and the dot)
709                when printing, so we only add one for the \0. */
710             hostname = alloca(strlen(user->hostname+ii)+1);
711             sprintf(hostname, "*.%s", user->hostname+ii+2);
712         }
713     }
714     /* Emit hostmask */
715     len = strlen(ident) + strlen(hostname) + 2;
716     if (nickname) {
717         len += strlen(nickname) + 1;
718         mask = malloc(len);
719         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
720     } else {
721         mask = malloc(len);
722         sprintf(mask, "%s@%s", ident, hostname);
723     }
724     return mask;
725 }
726
727 int
728 IsChannelName(const char *name) {
729     unsigned int ii;
730
731     if (*name !='#')
732         return 0;
733     for (ii=1; name[ii]; ++ii) {
734         if ((name[ii] > 0) && (name[ii] <= 32))
735             return 0;
736         if (name[ii] == ',')
737             return 0;
738         if (name[ii] == '\xa0')
739             return 0;
740     }
741     return 1;
742 }