]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/numnicks.c
Should be unsigned long for A
[irc/quakenet/snircd.git] / ircd / numnicks.c
CommitLineData
189935b1 1/*
2 * IRC - Internet Relay Chat, ircd/channel.c
3 * Copyright (C) 1996 Carlo Wood (I wish this was C++ - this sucks :/)
4 *
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)
8 * any later version.
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, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19/** @file
20 * @brief Implementation of numeric nickname operations.
21 * @version $Id: numnicks.c,v 1.30 2005/05/08 00:56:05 entrope Exp $
22 */
23#include "config.h"
24
25#include "numnicks.h"
26#include "client.h"
27#include "ircd.h"
28#include "ircd_alloc.h"
29#include "ircd_log.h"
30#include "ircd_string.h"
31#include "match.h"
32#include "s_bsd.h"
33#include "s_debug.h"
34#include "s_misc.h"
35#include "struct.h"
36
37/* #include <assert.h> -- Now using assert in ircd_log.h */
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41
42
43/** @page numnicks Numeric Nicks
44 * %Numeric nicks (numnicks) are new as of version ircu2.10.00beta1.
45 *
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.
50 *
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().
54 *
55 * We keep the characters printable for debugging reasons too.
56 *
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.
61 */
62
63
64/* These must be the same on ALL servers ! Do not change ! */
65
66/** Number of bits encoded in one numnick character. */
67#define NUMNICKLOG 6
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 */
74
75/*
76 * The internal counter for the 'XX' of local clients
77 */
78/** Maximum used server numnick, plus one. */
79static unsigned int lastNNServer = 0;
80/** Array of servers indexed by numnick. */
81static struct Client* server_list[NN_MAX_SERVER];
82
83/* *INDENT-OFF* */
84
85/**
86 * Converts a numeric to the corresponding character.
87 * The following characters are currently known to be forbidden:
88 *
89 * '\\0' : Because we use '\\0' as end of line.
90 *
91 * ' ' : Because parse_*() uses this as parameter separator.
92 *
93 * ':' : Because parse_server() uses this to detect if a prefix is a
94 * numeric or a name.
95 *
96 * '+' : Because m_nick() uses this to determine if parv[6] is a
97 * umode or not.
98 *
99 * '&', '#', '$', '@' and '%' :
100 * Because m_message() matches these characters to detect special cases.
101 */
102static 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','[',']'
107};
108
109/** Converts a character to its (base64) numnick value. */
110static 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,
119
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
128};
129
130/* *INDENT-ON* */
131
132/** Convert a string to its value as a numnick.
133 * @param[in] s Numnick string to decode.
134 * @return %Numeric nickname value.
135 */
136unsigned int base64toint(const char* s)
137{
138 unsigned int i = convert2n[(unsigned char) *s++];
139 while (*s) {
140 i <<= NUMNICKLOG;
141 i += convert2n[(unsigned char) *s++];
142 }
143 return i;
144}
145
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.
150 */
151const char* inttobase64(char* buf, unsigned int v, unsigned int count)
152{
153 buf[count] = '\0';
154 while (count > 0) {
155 buf[--count] = convert2y[(v & NUMNICKMASK)];
156 v >>= NUMNICKLOG;
157 }
158 return buf;
159}
160
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).
165 */
166static struct Client* FindXNServer(const char* numeric)
167{
168 char buf[3];
169 buf[0] = *numeric++;
170 buf[1] = *numeric;
171 buf[2] = '\0';
172 Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
173 return server_list[base64toint(buf)];
174}
175
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).
180 */
181struct Client* FindNServer(const char* numeric)
182{
183 unsigned int len = strlen(numeric);
184
185 if (len < 3) {
186 Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
187 return server_list[base64toint(numeric)];
188 }
189 else if (len == 3) {
190 Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric,
191 convert2n[(unsigned char) *numeric]));
192 return server_list[convert2n[(unsigned char) *numeric]];
193 }
194 return FindXNServer(numeric);
195}
196
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).
201 */
202struct Client* findNUser(const char* yxx)
203{
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];
210 }
211 }
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];
216 }
217 return 0;
218}
219
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.
223 */
224void RemoveYXXClient(struct Client* server, const char* yxx)
225{
226 assert(0 != server);
227 assert(0 != yxx);
228 if (*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;
232 }
233}
234
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.
239 */
240void SetServerYXX(struct Client* cptr, struct Client* server, const char* yxx)
241{
242 unsigned int index;
243 if (5 == strlen(yxx)) {
244 ircd_strncpy(cli_yxx(server), yxx, 2);
245 ircd_strncpy(cli_serv(server)->nn_capacity, yxx + 2, 3);
246 }
247 else {
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];
251 }
252 cli_serv(server)->nn_mask = base64toint(cli_serv(server)->nn_capacity);
253
254 index = base64toint(cli_yxx(server));
255 if (index >= lastNNServer)
256 lastNNServer = index + 1;
257 server_list[index] = server;
258
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*));
264}
265
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.
269 */
270void SetYXXCapacity(struct Client* c, unsigned int capacity)
271{
272 unsigned int max_clients = 16;
273 /*
274 * Calculate mask to be used for the maximum number of clients
275 */
276 while (max_clients < capacity)
277 max_clients <<= 1;
278 /*
279 * Sanity checks
280 */
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);
285 exit(-1);
286 }
287 --max_clients;
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;
293}
294
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.
299 */
300void SetYXXServerName(struct Client* c, unsigned int numeric)
301{
302 assert(0 != c);
303 assert(numeric < NN_MAX_SERVER);
304
305 inttobase64(cli_yxx(c), numeric, 2);
306 if (numeric >= lastNNServer)
307 lastNNServer = numeric + 1;
308 server_list[numeric] = c;
309}
310
311/** Unassign a server's numnick.
312 * @param[in] server %Server that should be removed from the numnick table.
313 */
314void ClearServerYXX(const struct Client *server)
315{
316 unsigned int index = base64toint(cli_yxx(server));
317 if (server_list[index] == server) /* Sanity check */
318 server_list[index] = 0;
319}
320
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.
326 */
327void SetRemoteNumNick(struct Client* acptr, const char *yxx)
328{
329 struct Client** acptrp;
330 struct Client* server = cli_user(acptr)->server;
331
332 if (5 == strlen(yxx)) {
333 strcpy(cli_yxx(acptr), yxx + 2);
334 }
335 else {
336 (cli_yxx(acptr))[0] = *++yxx;
337 (cli_yxx(acptr))[1] = *++yxx;
338 (cli_yxx(acptr))[2] = 0;
339 }
340 Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr),
341 base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask));
342
343 acptrp = &(cli_serv(server))->client_list[base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask];
344 if (*acptrp) {
345 /*
346 * this exits the old client in the array, not the client
347 * that is being set
348 */
349 exit_client(cli_from(acptr), *acptrp, server, "Numeric nick collision (Ghost)");
350 }
351 *acptrp = acptr;
352}
353
354
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.
359 */
360int SetLocalNumNick(struct Client *cptr)
361{
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;
366
367 assert(cli_user(cptr)->server == &me);
368
369 while (client_list[last_nn & mask]) {
370 if (++count == NN_MAX_CLIENT) {
371 assert(count < NN_MAX_CLIENT);
372 return 0;
373 }
374 if (++last_nn == NN_MAX_CLIENT)
375 last_nn = 0;
376 }
377 client_list[last_nn & mask] = cptr; /* Reserve the numeric ! */
378
379 inttobase64(cli_yxx(cptr), last_nn, 3);
380 if (++last_nn == NN_MAX_CLIENT)
381 last_nn = 0;
382 return 1;
383}
384
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.
390 */
391int markMatchexServer(const char *cmask, int minlen)
392{
393 int cnt = 0;
394 int i;
395 struct Client *acptr;
396
397 for (i = 0; i < lastNNServer; i++) {
398 if ((acptr = server_list[i]))
399 {
400 if (matchexec(cli_name(acptr), cmask, minlen))
401 ClrFlag(acptr, FLAG_MAP);
402 else
403 {
404 SetFlag(acptr, FLAG_MAP);
405 cnt++;
406 }
407 }
408 }
409 return cnt;
410}
411
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).
415 */
416struct Client* find_match_server(char *mask)
417{
418 struct Client *acptr;
419 int i;
420
421 if (!(BadPtr(mask))) {
422 collapse(mask);
423 for (i = 0; i < lastNNServer; i++) {
424 if ((acptr = server_list[i]) && (!match(mask, cli_name(acptr))))
425 return acptr;
426 }
427 }
428 return 0;
429}
430
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
434 * characters.
435 *
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
438 * underscore.
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.
443 */
444const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int count, int v6_ok)
445{
446 if (irc_in_addr_is_ipv4(addr)) {
447 assert(count >= 6);
448 inttobase64(buf, (ntohs(addr->in6_16[6]) << 16) | ntohs(addr->in6_16[7]), 6);
449 } else if (!v6_ok) {
450 assert(count >= 6);
451 if (addr->in6_16[0] == htons(0x2002))
452 inttobase64(buf, (ntohs(addr->in6_16[1]) << 16) | ntohs(addr->in6_16[2]), 6);
453 else
454 strcpy(buf, "AAAAAA");
455 } else {
456 unsigned int max_start, max_zeros, curr_zeros, zero, ii;
457 char *output = buf;
458
459 assert(count >= 25);
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);
463 output += 3;
464 }
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])
468 curr_zeros++;
469 else if (curr_zeros > max_zeros) {
470 max_start = ii - curr_zeros;
471 max_zeros = curr_zeros;
472 curr_zeros = 0;
473 }
474 }
475 if (curr_zeros > max_zeros) {
476 max_start = ii - curr_zeros;
477 max_zeros = curr_zeros;
478 curr_zeros = 0;
479 }
480 /* Print the rest of the address */
481 for (ii = zero; ii < 8; ) {
482 if ((ii == max_start) && max_zeros) {
483 *output++ = '_';
484 ii += max_zeros;
485 } else {
486 inttobase64(output, ntohs(addr->in6_16[ii]), 3);
487 output += 3;
488 ii++;
489 }
490 }
491 *output = '\0';
492 }
493 return buf;
494}
495
496/** Decode an IP address from base64.
497 * @param[in] input Input buffer to decode.
498 * @param[out] addr IP address structure to populate.
499 */
500void base64toip(const char* input, struct irc_in_addr* addr)
501{
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. */
506 if (in) {
507 addr->in6_16[5] = htons(65535);
508 addr->in6_16[6] = htons(in >> 16);
509 addr->in6_16[7] = htons(in & 65535);
510 }
511 } else {
512 unsigned int pos = 0;
513 do {
514 if (*input == '_') {
515 unsigned int left;
516 for (left = (25 - strlen(input)) / 3 - pos; left; left--)
517 addr->in6_16[pos++] = 0;
518 input++;
519 } else {
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);
524 }
525 } while (pos < 8);
526 }
527}