1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
38 unsigned int lines_processed
;
40 struct io_fd
*socket_io_fd
;
42 const char *hidden_host_suffix
;
44 static char replay_line
[MAXLEN
+80];
46 static int ping_timeout
;
47 static int replay_connected
;
48 static unsigned int nicklen
= NICKLEN
; /* how long do we think servers allow nicks to be? */
49 static struct userList dead_users
;
51 extern struct cManagerNode cManager
;
52 extern unsigned long burst_length
;
53 extern struct cManagerNode cManager
;
54 extern struct policer_params
*oper_policer_params
, *luser_policer_params
;
55 extern server_link_func_t
*slf_list
;
56 extern unsigned int slf_size
, slf_used
;
57 extern new_user_func_t
*nuf_list
;
58 extern unsigned int nuf_size
, nuf_used
;
59 extern del_user_func_t
*duf_list
;
60 extern unsigned int duf_size
, duf_used
;
61 extern time_t boot_time
;
63 void received_ping(void);
65 static int replay_read(void);
66 static dict_t irc_func_dict
;
68 typedef void (*foreach_chanfunc
) (struct chanNode
*chan
, void *data
);
69 typedef void (*foreach_nonchan
) (char *name
, void *data
);
70 typedef void (*foreach_userfunc
) (struct userNode
*user
, void *data
);
71 typedef void (*foreach_nonuser
) (char *name
, void *data
);
72 static void parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
);
75 uplink_readable(struct io_fd
*fd
) {
76 static char buffer
[MAXLEN
];
80 pos
= ioset_line_read(fd
, buffer
, sizeof(buffer
));
85 if ((eol
= strpbrk(buffer
, "\r\n")))
87 log_replay(MAIN_LOG
, false, buffer
);
88 if (cManager
.uplink
->state
!= DISCONNECTED
)
89 parse_line(buffer
, 0);
94 socket_destroyed(struct io_fd
*fd
)
97 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
99 cManager
.uplink
->state
= DISCONNECTED
;
101 DelServer(self
->uplink
, 0, NULL
);
104 void replay_event_loop(void)
106 while (!quit_services
) {
107 if (!replay_connected
) {
108 /* this time fudging is to get some of the logging right */
109 self
->link
= self
->boot
= now
;
110 cManager
.uplink
->state
= AUTHENTICATING
;
111 irc_introduce(cManager
.uplink
->password
);
112 replay_connected
= 1;
113 } else if (!replay_read()) {
114 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
122 create_socket_client(struct uplinkNode
*target
)
124 int port
= target
->port
;
125 const char *addr
= target
->host
;
128 return feof(replay_file
) ? 0 : 1;
131 /* Leave the existing socket open, say we failed. */
132 log_module(MAIN_LOG
, LOG_ERROR
, "Refusing to create second connection to %s:%d.", addr
, port
);
136 log_module(MAIN_LOG
, LOG_INFO
, "Connecting to %s:%i...", addr
, port
);
138 socket_io_fd
= ioset_connect((struct sockaddr
*)cManager
.uplink
->bind_addr
, sizeof(struct sockaddr
), addr
, port
, 1, 0, NULL
);
140 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to uplink failed: %s (%d)", strerror(errno
), errno
);
141 target
->state
= DISCONNECTED
;
145 socket_io_fd
->readable_cb
= uplink_readable
;
146 socket_io_fd
->destroy_cb
= socket_destroyed
;
147 socket_io_fd
->line_reads
= 1;
148 socket_io_fd
->wants_reads
= 1;
149 log_module(MAIN_LOG
, LOG_INFO
, "Connection to server established.");
150 cManager
.uplink
= target
;
151 target
->state
= AUTHENTICATING
;
157 replay_read_line(void)
162 if (replay_line
[0]) return;
164 if (!fgets(replay_line
, sizeof(replay_line
), replay_file
)) {
165 if (feof(replay_file
)) {
167 memset(replay_line
, 0, sizeof(replay_line
));
171 if ((replay_line
[0] != '[')
172 || (replay_line
[3] != ':')
173 || (replay_line
[6] != ':')
174 || (replay_line
[9] != ' ')
175 || (replay_line
[12] != '/')
176 || (replay_line
[15] != '/')
177 || (replay_line
[20] != ']')
178 || (replay_line
[21] != ' ')) {
179 log_module(MAIN_LOG
, LOG_ERROR
, "Unrecognized timestamp in replay file: %s", replay_line
);
182 timestamp
.tm_hour
= strtoul(replay_line
+1, NULL
, 10);
183 timestamp
.tm_min
= strtoul(replay_line
+4, NULL
, 10);
184 timestamp
.tm_sec
= strtoul(replay_line
+7, NULL
, 10);
185 timestamp
.tm_mon
= strtoul(replay_line
+10, NULL
, 10) - 1;
186 timestamp
.tm_mday
= strtoul(replay_line
+13, NULL
, 10);
187 timestamp
.tm_year
= strtoul(replay_line
+16, NULL
, 10) - 1900;
188 timestamp
.tm_isdst
= 0;
189 new_time
= mktime(×tamp
);
190 if (new_time
== -1) {
191 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
);
196 if (strncmp(replay_line
+22, "(info) ", 7))
205 char read_line
[MAXLEN
];
208 /* if it's a sent line, break out to handle it */
209 if (!strncmp(replay_line
+29, " ", 3))
211 if (!strncmp(replay_line
+29, "W: ", 3)) {
212 log_module(MAIN_LOG
, LOG_ERROR
, "Expected response from services: %s", replay_line
+32);
218 log_replay(MAIN_LOG
, false, replay_line
+32);
219 safestrncpy(read_line
, replay_line
+32, sizeof(read_line
));
220 len
= strlen(read_line
);
221 if (read_line
[len
-1] == '\n')
222 read_line
[--len
] = 0;
224 parse_line(read_line
, 0);
230 replay_write(char *text
)
233 if (strncmp(replay_line
+29, "W: ", 3)) {
234 log_module(MAIN_LOG
, LOG_ERROR
, "Unexpected output during replay: %s", text
);
237 if (strcmp(replay_line
+32, text
)) {
238 log_module(MAIN_LOG
, LOG_ERROR
, "Incorrect output during replay:\nReceived: %sExpected: %s", text
, replay_line
+32);
240 log_replay(MAIN_LOG
, true, text
);
246 void putsock(const char *text
, ...) PRINTF_LIKE(1, 2);
249 putsock(const char *text
, ...)
255 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
257 va_start(arg_list
, text
);
258 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
260 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
263 log_replay(MAIN_LOG
, true, buffer
);
264 buffer
[pos
++] = '\n';
266 ioset_write(socket_io_fd
, buffer
, pos
);
268 replay_write(buffer
);
276 replay_connected
= 0;
277 socket_destroyed(socket_io_fd
);
279 ioset_close(socket_io_fd
->fd
, 1);
283 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
284 typedef CMD_FUNC(cmd_func_t
);
286 static void timed_ping_timeout(void *data
);
288 /* Ping state is kept in the timeq (only one of these two can be in
289 * the queue at any given time). */
291 timed_send_ping(UNUSED_ARG(void *data
))
293 irc_ping(self
->name
);
294 timeq_add(now
+ ping_timeout
, timed_ping_timeout
, 0);
298 timed_ping_timeout(UNUSED_ARG(void *data
))
300 /* Uplink "health" tracking could be accomplished by counting the
301 number of ping timeouts that happen for each uplink. After the
302 timeouts per time period exceeds some amount, the uplink could
303 be marked as unavalable.*/
304 irc_squit(self
, "Ping timeout.", NULL
);
307 static CMD_FUNC(cmd_pass
)
309 const char *true_pass
;
313 true_pass
= cManager
.uplink
->their_password
;
314 if (true_pass
&& strcmp(true_pass
, argv
[1])) {
315 /* It might be good to mark the uplink as unavailable when
316 this happens, though there should be a way of resetting
318 irc_squit(self
, "Incorrect password received.", NULL
);
322 cManager
.uplink
->state
= BURSTING
;
326 static CMD_FUNC(cmd_dummy
)
328 /* we don't care about these messages */
332 static CMD_FUNC(cmd_error
)
334 if (argv
[1]) log_module(MAIN_LOG
, LOG_ERROR
, "Error: %s", argv
[1]);
335 log_module(MAIN_LOG
, LOG_ERROR
, "Error received from uplink, squitting.");
337 if (cManager
.uplink
->state
!= CONNECTED
) {
338 /* Error messages while connected should be fine. */
339 cManager
.uplink
->flags
|= UPLINK_UNAVAILABLE
;
340 log_module(MAIN_LOG
, LOG_ERROR
, "Disabling uplink.");
347 static CMD_FUNC(cmd_stats
)
353 if (!(un
= GetUserH(origin
)))
355 switch (argv
[1][0]) {
357 unsigned int uptime
= now
- boot_time
;
358 irc_numeric(un
, RPL_STATSUPTIME
, ":Server Up %d days %d:%02d:%02d",
359 uptime
/(24*60*60), (uptime
/(60*60))%24
, (uptime
/60)%60
, uptime%60
);
360 irc_numeric(un
, RPL_MAXCONNECTIONS
, ":Highest connection count: %d (%d clients)",
361 self
->max_clients
+1, self
->max_clients
);
364 default: /* unrecognized/unhandled stats output */ break;
366 irc_numeric(un
, 219, "%s :End of /STATS report", argv
[1]);
370 static CMD_FUNC(cmd_version
)
372 struct userNode
*user
;
373 if (!(user
= GetUserH(origin
))) {
374 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find VERSION origin user %s", origin
);
377 irc_numeric(user
, 351, "%s %s %s", PACKAGE_TARNAME
, PACKAGE_VERSION
, self
->name
);
381 static CMD_FUNC(cmd_admin
)
383 struct userNode
*user
;
384 struct string_list
*slist
;
386 if (!(user
= GetUserH(origin
))) {
387 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find ADMIN origin user %s", origin
);
390 if ((slist
= conf_get_data("server/admin", RECDB_STRING_LIST
)) && slist
->used
) {
393 irc_numeric(user
, 256, ":Administrative info about %s", self
->name
);
394 for (i
= 0; i
< slist
->used
&& i
< 3; i
++)
395 irc_numeric(user
, 257 + i
, ":%s", slist
->list
[i
]);
397 irc_numeric(user
, 423, ":No administrative info available");
403 recalc_bursts(struct server
*eob_server
)
406 eob_server
->burst
= eob_server
->self_burst
;
407 if (eob_server
->uplink
!= self
)
408 eob_server
->burst
= eob_server
->burst
|| eob_server
->uplink
->burst
;
409 for (nn
=0; nn
< eob_server
->children
.used
; nn
++)
410 recalc_bursts(eob_server
->children
.list
[nn
]);
413 static struct chanmsg_func
{
415 struct userNode
*service
;
416 } chanmsg_funcs
[256]; /* indexed by trigger character */
418 static struct allchanmsg_func
{
420 struct userNode
*service
;
421 } allchanmsg_funcs
[ALLCHANMSG_FUNCS_MAX
];
423 struct privmsg_desc
{
424 struct userNode
*user
;
426 unsigned int is_notice
: 1;
427 unsigned int is_qualified
: 1;
431 privmsg_chan_helper(struct chanNode
*cn
, void *data
)
433 struct privmsg_desc
*pd
= data
;
435 struct chanmsg_func
*cf
= &chanmsg_funcs
[(unsigned char)pd
->text
[0]];
438 /* Don't complain if it can't find the modeNode because the channel might
440 if ((mn
= GetUserMode(cn
, pd
->user
)))
441 mn
->idle_since
= now
;
443 /* Never send a NOTICE to a channel to one of the services */
444 if (!pd
->is_notice
&& cf
->func
445 && ((cn
->modes
& MODE_REGISTERED
) || GetUserMode(cn
, cf
->service
)))
446 cf
->func(pd
->user
, cn
, pd
->text
+1, cf
->service
);
448 /* This catches *all* text sent to the channel that the services server sees */
449 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
450 cf
= (struct chanmsg_func
*)&allchanmsg_funcs
[x
];
452 break; /* end of list */
454 cf
->func(pd
->user
, cn
, pd
->text
, cf
->service
);
459 privmsg_invalid(char *name
, void *data
)
461 struct privmsg_desc
*pd
= data
;
465 irc_numeric(pd
->user
, ERR_NOSUCHNICK
, "%s@%s :No such nick", name
, self
->name
);
469 part_helper(struct chanNode
*cn
, void *data
)
471 DelChannelUser(data
, cn
, false, 0);
475 reg_chanmsg_func(unsigned char prefix
, struct userNode
*service
, chanmsg_func_t handler
)
477 if (chanmsg_funcs
[prefix
].func
)
478 log_module(MAIN_LOG
, LOG_WARNING
, "Re-registering new chanmsg handler for character `%c'.", prefix
);
479 chanmsg_funcs
[prefix
].func
= handler
;
480 chanmsg_funcs
[prefix
].service
= service
;
484 reg_allchanmsg_func(struct userNode
*service
, chanmsg_func_t handler
)
487 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
488 if (allchanmsg_funcs
[x
].func
)
490 allchanmsg_funcs
[x
].func
= handler
;
491 allchanmsg_funcs
[x
].service
= service
;
497 get_chanmsg_bot(unsigned char prefix
)
499 return chanmsg_funcs
[prefix
].service
;
502 static mode_change_func_t
*mcf_list
;
503 static unsigned int mcf_size
= 0, mcf_used
= 0;
506 reg_mode_change_func(mode_change_func_t handler
)
508 if (mcf_used
== mcf_size
) {
511 mcf_list
= realloc(mcf_list
, mcf_size
*sizeof(mode_change_func_t
));
514 mcf_list
= malloc(mcf_size
*sizeof(mode_change_func_t
));
517 mcf_list
[mcf_used
++] = handler
;
520 struct mod_chanmode
*
521 mod_chanmode_alloc(unsigned int argc
)
523 struct mod_chanmode
*res
;
525 res
= calloc(1, sizeof(*res
) + (argc
-1)*sizeof(res
->args
[0]));
527 res
= calloc(1, sizeof(*res
));
530 res
->alloc_argc
= argc
;
537 struct mod_chanmode
*
538 mod_chanmode_dup(struct mod_chanmode
*orig
, unsigned int extra
)
540 struct mod_chanmode
*res
;
541 res
= mod_chanmode_alloc(orig
->argc
+ extra
);
543 res
->modes_set
= orig
->modes_set
;
544 res
->modes_clear
= orig
->modes_clear
;
545 res
->new_limit
= orig
->new_limit
;
546 memcpy(res
->new_key
, orig
->new_key
, sizeof(res
->new_key
));
547 res
->argc
= orig
->argc
;
548 memcpy(res
->args
, orig
->args
, orig
->argc
*sizeof(orig
->args
[0]));
554 mod_chanmode_apply(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
557 struct exemptNode
*en
;
560 assert(change
->argc
<= change
->alloc_argc
);
561 channel
->modes
= (channel
->modes
& ~change
->modes_clear
) | change
->modes_set
;
562 if (change
->modes_set
& MODE_LIMIT
)
563 channel
->limit
= change
->new_limit
;
564 if (change
->modes_set
& MODE_KEY
)
565 strcpy(channel
->key
, change
->new_key
);
566 for (ii
= 0; ii
< change
->argc
; ++ii
) {
567 switch (change
->args
[ii
].mode
) {
569 /* If any existing ban is a subset of the new ban,
570 * silently remove it. The new ban is not allowed
571 * to be more specific than an existing ban.
573 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
574 bn
= channel
->banlist
.list
[jj
];
575 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, bn
->ban
)) {
576 banList_remove(&channel
->banlist
, bn
);
581 bn
= calloc(1, sizeof(*bn
));
582 safestrncpy(bn
->ban
, change
->args
[ii
].u
.hostmask
, sizeof(bn
->ban
));
584 safestrncpy(bn
->who
, who
->nick
, sizeof(bn
->who
));
586 safestrncpy(bn
->who
, "<unknown>", sizeof(bn
->who
));
588 banList_append(&channel
->banlist
, bn
);
590 case MODE_REMOVE
|MODE_BAN
:
591 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
592 bn
= channel
->banlist
.list
[jj
];
593 if (strcmp(bn
->ban
, change
->args
[ii
].u
.hostmask
))
596 banList_remove(&channel
->banlist
, bn
);
601 /* If any existing exempt is a subset of the new exempt,
602 * silently remove it. The new exempt is not allowed
603 * to be more specific than an existing exempt.
605 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
606 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, channel
->exemptlist
.list
[jj
]->exempt
)) {
607 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
608 free(channel
->exemptlist
.list
[jj
]);
612 en
= calloc(1, sizeof(*en
));
613 safestrncpy(en
->exempt
, change
->args
[ii
].u
.hostmask
, sizeof(en
->exempt
));
615 safestrncpy(en
->who
, who
->nick
, sizeof(en
->who
));
617 safestrncpy(en
->who
, "<unknown>", sizeof(en
->who
));
619 exemptList_append(&channel
->exemptlist
, en
);
621 case MODE_REMOVE
|MODE_EXEMPT
:
622 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
623 if (strcmp(channel
->exemptlist
.list
[jj
]->exempt
, change
->args
[ii
].u
.hostmask
))
625 free(channel
->exemptlist
.list
[jj
]);
626 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
630 /* XXX Hack: this is the stupedest use of switch iv ever seen.
631 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
632 * because switch does only full comparison. This needs redone as if/else.
637 case MODE_VOICE
|MODE_CHANOP
:
638 case MODE_VOICE
|MODE_HALFOP
:
639 case MODE_CHANOP
|MODE_HALFOP
:
640 case MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
641 case MODE_REMOVE
|MODE_CHANOP
:
642 case MODE_REMOVE
|MODE_HALFOP
:
643 case MODE_REMOVE
|MODE_VOICE
:
644 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
:
645 case MODE_REMOVE
|MODE_VOICE
|MODE_HALFOP
:
646 case MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
:
647 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
648 if (change
->args
[ii
].mode
& MODE_REMOVE
)
649 change
->args
[ii
].u
.member
->modes
&= ~change
->args
[ii
].mode
;
651 change
->args
[ii
].u
.member
->modes
|= change
->args
[ii
].mode
;
654 assert(0 && "Invalid mode argument");
661 mod_chanmode_free(struct mod_chanmode
*change
)
667 mod_chanmode(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
669 struct mod_chanmode
*change
;
672 if (!modes
|| !modes
[0])
674 if (!(change
= mod_chanmode_parse(channel
, modes
, argc
, flags
)))
676 if (flags
& MC_ANNOUNCE
)
677 mod_chanmode_announce(who
, channel
, change
);
679 mod_chanmode_apply(who
, channel
, change
);
680 if (flags
& MC_NOTIFY
)
681 for (ii
= 0; ii
< mcf_used
; ++ii
)
682 mcf_list
[ii
](channel
, who
, change
);
683 mod_chanmode_free(change
);
688 irc_make_chanmode(struct chanNode
*chan
, char *out
)
690 struct mod_chanmode change
;
691 mod_chanmode_init(&change
);
692 change
.modes_set
= chan
->modes
;
693 change
.new_limit
= chan
->limit
;
694 safestrncpy(change
.new_key
, chan
->key
, sizeof(change
.new_key
));
695 return strlen(mod_chanmode_format(&change
, out
));
699 generate_hostmask(struct userNode
*user
, int options
)
701 char *nickname
, *ident
, *hostname
;
705 /* figure out string parts */
706 if (options
& GENMASK_OMITNICK
)
708 else if (options
& GENMASK_USENICK
)
709 nickname
= user
->nick
;
712 if (options
& GENMASK_STRICT_IDENT
)
713 // sethost - reed/apples
714 if (IsSetHost(user
)) {
715 ident
= alloca(strcspn(user
->sethost
, "@")+2);
716 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
720 else if (options
& GENMASK_ANY_IDENT
)
723 // sethost - reed/apples
724 if (IsSetHost(user
)) {
725 ident
= alloca(strcspn(user
->sethost
, "@")+3);
727 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
729 ident
= alloca(strlen(user
->ident
)+2);
731 strcpy(ident
+1, user
->ident
+ ((*user
->ident
== '~')?1:0));
734 hostname
= user
->hostname
;
735 if (IsFakeHost(user
) && IsHiddenHost(user
) && !(options
& GENMASK_NO_HIDING
)) {
736 hostname
= user
->fakehost
;
737 } else if (IsHiddenHost(user
) && user
->handle_info
&& hidden_host_suffix
&& !(options
& GENMASK_NO_HIDING
)) {
738 hostname
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
739 sprintf(hostname
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
740 } else if (options
& GENMASK_STRICT_HOST
) {
741 if (options
& GENMASK_BYIP
)
742 hostname
= inet_ntoa(user
->ip
);
743 } else if ((options
& GENMASK_BYIP
) || !hostname
[strspn(hostname
, "0123456789.")]) {
744 /* Should generate an IP-based hostmask. By popular acclaim, a /16
745 * hostmask is used by default. */
746 unsigned masked_ip
, mask
, masklen
;
748 mask
= ~0 << masklen
;
749 masked_ip
= ntohl(user
->ip
.s_addr
) & mask
;
750 hostname
= alloca(32);
751 if (options
& GENMASK_X3MASK
) {
752 sprintf(hostname
, "%d.%d.%d.%d/%d", (masked_ip
>>24)&0xFF, (masked_ip
>>16)&0xFF, (masked_ip
>>8)&0xFF, masked_ip
&0xFF, masklen
);
755 for (ii
=0; ii
<4; ii
++) {
757 ofs
+= sprintf(hostname
+ofs
, "%d.", (masked_ip
>>24)&0xFF);
761 ofs
+= sprintf(hostname
+ofs
, "*.");
764 /* Truncate the last . */
769 /* This heuristic could be made smarter. Is it worth the effort? */
770 for (ii
=cnt
=0; hostname
[ii
]; ii
++)
771 if (hostname
[ii
] == '.')
774 /* only a two-level domain name; leave hostname */
775 } else if (cnt
== 2) {
776 for (ii
=0; user
->hostname
[ii
] != '.'; ii
++) ;
777 /* Add 3 to account for the *. and \0. */
778 hostname
= alloca(strlen(user
->hostname
+ii
)+3);
779 sprintf(hostname
, "*.%s", user
->hostname
+ii
+1);
781 for (cnt
=3, ii
--; cnt
; ii
--)
782 if (user
->hostname
[ii
] == '.')
784 /* The loop above will overshoot the dot one character;
785 we skip forward two (the one character and the dot)
786 when printing, so we only add one for the \0. */
787 hostname
= alloca(strlen(user
->hostname
+ii
)+1);
788 sprintf(hostname
, "*.%s", user
->hostname
+ii
+2);
791 // sethost - reed/apples
793 hostname
= strchr(user
->sethost
, '@') + 1;
796 len
= strlen(ident
) + strlen(hostname
) + 2;
798 len
+= strlen(nickname
) + 1;
800 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
803 sprintf(mask
, "%s@%s", ident
, hostname
);
809 IsChannelName(const char *name
) {
814 for (ii
=1; name
[ii
]; ++ii
) {
815 if ((name
[ii
] > 0) && (name
[ii
] <= 32))
819 if (name
[ii
] == '\xa0')