]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/numnicks.c
2 * IRC - Internet Relay Chat, ircd/channel.c
3 * Copyright (C) 1996 Carlo Wood (I wish this was C++ - this sucks :/)
5 * 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, or (at your option)
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.
15 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * @brief Implementation of numeric nickname operations.
21 * @version $Id: numnicks.c,v 1.30 2005/05/08 00:56:05 entrope Exp $
28 #include "ircd_alloc.h"
30 #include "ircd_string.h"
37 /* #include <assert.h> -- Now using assert in ircd_log.h */
43 /** @page numnicks Numeric Nicks
44 * %Numeric nicks (numnicks) are new as of version ircu2.10.00beta1.
46 * The idea is as follows:
47 * In most messages (for protocol 10+) the original nick will be
48 * replaced by a 5 character string: YYXXX
49 * Where 'YY' represents the server, and 'XXX' the nick on that server.
51 * 'YYXXX' should not interfere with the input parser, and therefore is
52 * not allowed to contain spaces or a ':'.
53 * Also, 'YY' can't start with a '+' because of m_server().
55 * We keep the characters printable for debugging reasons too.
57 * The 'XXX' value can be larger then the maximum number of clients
58 * per server, we use a mask (Server::nn_mask) to get the real
59 * client numeric. The overhead is used to have some redundancy so
60 * just-disconnected-client aren't confused with just-connected ones.
64 /* These must be the same on ALL servers ! Do not change ! */
66 /** Number of bits encoded in one numnick character. */
68 /** Bitmask to select value of next numnick character. */
69 #define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
70 /** Number of servers representable in a numnick. */
71 #define NN_MAX_SERVER 4096 /* (NUMNICKBASE * NUMNICKBASE) */
72 /** Number of clients representable in a numnick. */
73 #define NN_MAX_CLIENT 262144 /* NUMNICKBASE ^ 3 */
76 * The internal counter for the 'XX' of local clients
78 /** Maximum used server numnick, plus one. */
79 static unsigned int lastNNServer
= 0;
80 /** Array of servers indexed by numnick. */
81 static struct Client
* server_list
[NN_MAX_SERVER
];
86 * Converts a numeric to the corresponding character.
87 * The following characters are currently known to be forbidden:
89 * '\\0' : Because we use '\\0' as end of line.
91 * ' ' : Because parse_*() uses this as parameter separator.
93 * ':' : Because parse_server() uses this to detect if a prefix is a
96 * '+' : Because m_nick() uses this to determine if parv[6] is a
99 * '&', '#', '$', '@' and '%' :
100 * Because m_message() matches these characters to detect special cases.
102 static const char convert2y
[] = {
103 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
104 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
105 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
106 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
109 /** Converts a character to its (base64) numnick value. */
110 static const unsigned int convert2n
[] = {
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
115 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
116 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
117 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
118 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
132 /** Convert a string to its value as a numnick.
133 * @param[in] s Numnick string to decode.
134 * @return %Numeric nickname value.
136 unsigned int base64toint(const char* s
)
138 unsigned int i
= convert2n
[(unsigned char) *s
++];
141 i
+= convert2n
[(unsigned char) *s
++];
146 /** Encode a number as a numnick.
147 * @param[out] buf Output buffer.
148 * @param[in] v Value to encode.
149 * @param[in] count Number of numnick digits to write to \a buf.
151 const char* inttobase64(char* buf
, unsigned int v
, unsigned int count
)
155 buf
[--count
] = convert2y
[(v
& NUMNICKMASK
)];
161 /** Look up a server by numnick string.
162 * See @ref numnicks for more details.
163 * @param[in] numeric %Numeric nickname of server (may contain trailing junk).
164 * @return %Server with that numnick (or NULL).
166 static struct Client
* FindXNServer(const char* numeric
)
172 Debug((DEBUG_DEBUG
, "FindXNServer: %s(%d)", buf
, base64toint(buf
)));
173 return server_list
[base64toint(buf
)];
176 /** Look up a server by numnick string.
177 * See @ref numnicks for more details.
178 * @param[in] numeric %Numeric nickname of server.
179 * @return %Server with that numnick (or NULL).
181 struct Client
* FindNServer(const char* numeric
)
183 unsigned int len
= strlen(numeric
);
186 Debug((DEBUG_DEBUG
, "FindNServer: %s(%d)", numeric
, base64toint(numeric
)));
187 return server_list
[base64toint(numeric
)];
190 Debug((DEBUG_DEBUG
, "FindNServer: %c(%d)", *numeric
,
191 convert2n
[(unsigned char) *numeric
]));
192 return server_list
[convert2n
[(unsigned char) *numeric
]];
194 return FindXNServer(numeric
);
197 /** Look up a user by numnick string.
198 * See @ref numnicks for more details.
199 * @param[in] yxx %Numeric nickname of user.
200 * @return %User with that numnick (or NULL).
202 struct Client
* findNUser(const char* yxx
)
204 struct Client
* server
= 0;
205 if (5 == strlen(yxx
)) {
206 if (0 != (server
= FindXNServer(yxx
))) {
207 Debug((DEBUG_DEBUG
, "findNUser: %s(%d)", yxx
,
208 base64toint(yxx
+ 2) & cli_serv(server
)->nn_mask
));
209 return cli_serv(server
)->client_list
[base64toint(yxx
+ 2) & cli_serv(server
)->nn_mask
];
212 else if (0 != (server
= FindNServer(yxx
))) {
213 Debug((DEBUG_DEBUG
, "findNUser: %s(%d)",
214 yxx
, base64toint(yxx
+ 1) & cli_serv(server
)->nn_mask
));
215 return cli_serv(server
)->client_list
[base64toint(yxx
+ 1) & cli_serv(server
)->nn_mask
];
220 /** Remove a client from a server's user array.
221 * @param[in] server %Server that owns the user to remove.
222 * @param[in] yxx Numnick of client to remove.
224 void RemoveYXXClient(struct Client
* server
, const char* yxx
)
229 Debug((DEBUG_DEBUG
, "RemoveYXXClient: %s(%d)", yxx
,
230 base64toint(yxx
) & cli_serv(server
)->nn_mask
));
231 cli_serv(server
)->client_list
[base64toint(yxx
) & cli_serv(server
)->nn_mask
] = 0;
235 /** Set a server's numeric nick.
236 * @param[in] cptr %Client that announced the server (ignored).
237 * @param[in,out] server %Server that is being assigned a numnick.
238 * @param[in] yxx %Numeric nickname for server.
240 void SetServerYXX(struct Client
* cptr
, struct Client
* server
, const char* yxx
)
243 if (5 == strlen(yxx
)) {
244 ircd_strncpy(cli_yxx(server
), yxx
, 2);
245 ircd_strncpy(cli_serv(server
)->nn_capacity
, yxx
+ 2, 3);
248 (cli_yxx(server
))[0] = yxx
[0];
249 cli_serv(server
)->nn_capacity
[0] = yxx
[1];
250 cli_serv(server
)->nn_capacity
[1] = yxx
[2];
252 cli_serv(server
)->nn_mask
= base64toint(cli_serv(server
)->nn_capacity
);
254 index
= base64toint(cli_yxx(server
));
255 if (index
>= lastNNServer
)
256 lastNNServer
= index
+ 1;
257 server_list
[index
] = server
;
259 /* Note, exit_one_client uses the fact that `client_list' != NULL to
260 * determine that SetServerYXX has been called - and then calls
261 * ClearServerYXX. However, freeing the allocation happens in free_client() */
262 cli_serv(server
)->client_list
=
263 (struct Client
**) MyCalloc(cli_serv(server
)->nn_mask
+ 1, sizeof(struct Client
*));
266 /** Set a server's capacity.
267 * @param[in] c %Server whose capacity is being set.
268 * @param[in] capacity Maximum number of clients the server supports.
270 void SetYXXCapacity(struct Client
* c
, unsigned int capacity
)
272 unsigned int max_clients
= 16;
274 * Calculate mask to be used for the maximum number of clients
276 while (max_clients
< capacity
)
281 if (max_clients
> NN_MAX_CLIENT
) {
282 fprintf(stderr
, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
283 "too large ! Please decrease this value.\n",
284 max_clients
- NN_MAX_CLIENT
);
288 inttobase64(cli_serv(c
)->nn_capacity
, max_clients
, 3);
289 cli_serv(c
)->nn_mask
= max_clients
; /* Our Numeric Nick mask */
290 cli_serv(c
)->client_list
= (struct Client
**) MyCalloc(max_clients
+ 1,
291 sizeof(struct Client
*));
292 server_list
[base64toint(cli_yxx(c
))] = c
;
295 /** Set a server's numeric nick.
296 * See @ref numnicks for more details.
297 * @param[in] c %Server that is being assigned a numnick.
298 * @param[in] numeric Numnick value for server.
300 void SetYXXServerName(struct Client
* c
, unsigned int numeric
)
303 assert(numeric
< NN_MAX_SERVER
);
305 inttobase64(cli_yxx(c
), numeric
, 2);
306 if (numeric
>= lastNNServer
)
307 lastNNServer
= numeric
+ 1;
308 server_list
[numeric
] = c
;
311 /** Unassign a server's numnick.
312 * @param[in] server %Server that should be removed from the numnick table.
314 void ClearServerYXX(const struct Client
*server
)
316 unsigned int index
= base64toint(cli_yxx(server
));
317 if (server_list
[index
] == server
) /* Sanity check */
318 server_list
[index
] = 0;
321 /** Register numeric of new (remote) client.
322 * See @ref numnicks for more details.
323 * Add it to the appropriate client_list.
324 * @param[in] acptr %User being registered.
325 * @param[in] yxx User's numnick.
327 void SetRemoteNumNick(struct Client
* acptr
, const char *yxx
)
329 struct Client
** acptrp
;
330 struct Client
* server
= cli_user(acptr
)->server
;
332 if (5 == strlen(yxx
)) {
333 strcpy(cli_yxx(acptr
), yxx
+ 2);
336 (cli_yxx(acptr
))[0] = *++yxx
;
337 (cli_yxx(acptr
))[1] = *++yxx
;
338 (cli_yxx(acptr
))[2] = 0;
340 Debug((DEBUG_DEBUG
, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr
),
341 base64toint(cli_yxx(acptr
)) & cli_serv(server
)->nn_mask
));
343 acptrp
= &(cli_serv(server
))->client_list
[base64toint(cli_yxx(acptr
)) & cli_serv(server
)->nn_mask
];
346 * this exits the old client in the array, not the client
349 exit_client(cli_from(acptr
), *acptrp
, server
, "Numeric nick collision (Ghost)");
355 /** Register numeric of new (local) client.
356 * See @ref numnicks for more details.
357 * Assign a numnick and add it to our client_list.
358 * @param[in] cptr %User being registered.
360 int SetLocalNumNick(struct Client
*cptr
)
362 static unsigned int last_nn
= 0;
363 struct Client
** client_list
= cli_serv(&me
)->client_list
;
364 unsigned int mask
= cli_serv(&me
)->nn_mask
;
365 unsigned int count
= 0;
367 assert(cli_user(cptr
)->server
== &me
);
369 while (client_list
[last_nn
& mask
]) {
370 if (++count
== NN_MAX_CLIENT
) {
371 assert(count
< NN_MAX_CLIENT
);
374 if (++last_nn
== NN_MAX_CLIENT
)
377 client_list
[last_nn
& mask
] = cptr
; /* Reserve the numeric ! */
379 inttobase64(cli_yxx(cptr
), last_nn
, 3);
380 if (++last_nn
== NN_MAX_CLIENT
)
385 /** Mark servers whose name matches the given (compiled) mask by
386 * setting their FLAG_MAP flag.
387 * @param[in] cmask Compiled mask for server names.
388 * @param[in] minlen Minimum match length for \a cmask.
389 * @return Number of servers marked.
391 int markMatchexServer(const char *cmask
, int minlen
)
395 struct Client
*acptr
;
397 for (i
= 0; i
< lastNNServer
; i
++) {
398 if ((acptr
= server_list
[i
]))
400 if (matchexec(cli_name(acptr
), cmask
, minlen
))
401 ClrFlag(acptr
, FLAG_MAP
);
404 SetFlag(acptr
, FLAG_MAP
);
412 /** Find first server whose name matches the given mask.
413 * @param[in,out] mask %Server name mask (collapse()d in-place).
414 * @return Matching server with lowest numnick value (or NULL).
416 struct Client
* find_match_server(char *mask
)
418 struct Client
*acptr
;
421 if (!(BadPtr(mask
))) {
423 for (i
= 0; i
< lastNNServer
; i
++) {
424 if ((acptr
= server_list
[i
]) && (!match(mask
, cli_name(acptr
))))
431 /** Encode an IP address in the base64 used by numnicks.
432 * For IPv4 addresses (including IPv4-mapped and IPv4-compatible IPv6
433 * addresses), the 32-bit host address is encoded directly as six
436 * For IPv6 addresses, each 16-bit address segment is encoded as three
437 * characters, but the longest run of zero segments is encoded using an
439 * @param[out] buf Output buffer to write to.
440 * @param[in] addr IP address to encode.
441 * @param[in] count Number of bytes writable to \a buf.
442 * @param[in] v6_ok If non-zero, peer understands base-64 encoded IPv6 addresses.
444 const char* iptobase64(char* buf
, const struct irc_in_addr
* addr
, unsigned int count
, int v6_ok
)
446 if (irc_in_addr_is_ipv4(addr
)) {
448 inttobase64(buf
, (ntohs(addr
->in6_16
[6]) << 16) | ntohs(addr
->in6_16
[7]), 6);
451 if (addr
->in6_16
[0] == htons(0x2002))
452 inttobase64(buf
, (ntohs(addr
->in6_16
[1]) << 16) | ntohs(addr
->in6_16
[2]), 6);
454 strcpy(buf
, "AAAAAA");
456 unsigned int max_start
, max_zeros
, curr_zeros
, zero
, ii
;
460 /* Can start by printing out the leading non-zero parts. */
461 for (ii
= 0; (addr
->in6_16
[ii
]) && (ii
< 8); ++ii
) {
462 inttobase64(output
, ntohs(addr
->in6_16
[ii
]), 3);
465 /* Find the longest run of zeros. */
466 for (max_start
= zero
= ii
, max_zeros
= curr_zeros
= 0; ii
< 8; ++ii
) {
467 if (!addr
->in6_16
[ii
])
469 else if (curr_zeros
> max_zeros
) {
470 max_start
= ii
- curr_zeros
;
471 max_zeros
= curr_zeros
;
475 if (curr_zeros
> max_zeros
) {
476 max_start
= ii
- curr_zeros
;
477 max_zeros
= curr_zeros
;
480 /* Print the rest of the address */
481 for (ii
= zero
; ii
< 8; ) {
482 if ((ii
== max_start
) && max_zeros
) {
486 inttobase64(output
, ntohs(addr
->in6_16
[ii
]), 3);
496 /** Decode an IP address from base64.
497 * @param[in] input Input buffer to decode.
498 * @param[out] addr IP address structure to populate.
500 void base64toip(const char* input
, struct irc_in_addr
* addr
)
502 memset(addr
, 0, sizeof(*addr
));
503 if (strlen(input
) == 6) {
504 unsigned int in
= base64toint(input
);
505 /* An all-zero address should stay that way. */
507 addr
->in6_16
[5] = htons(65535);
508 addr
->in6_16
[6] = htons(in
>> 16);
509 addr
->in6_16
[7] = htons(in
& 65535);
512 unsigned int pos
= 0;
516 for (left
= (25 - strlen(input
)) / 3 - pos
; left
; left
--)
517 addr
->in6_16
[pos
++] = 0;
520 unsigned short accum
= convert2n
[(unsigned char)*input
++];
521 accum
= (accum
<< NUMNICKLOG
) | convert2n
[(unsigned char)*input
++];
522 accum
= (accum
<< NUMNICKLOG
) | convert2n
[(unsigned char)*input
++];
523 addr
->in6_16
[pos
++] = ntohs(accum
);