f84c335f3a98464d8b0ebe8c4ab5b8436ac50371
[ircu2.10.12-pk.git] / ircd / numnicks.c
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$
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_string.h"
30 #include "match.h"
31 #include "s_bsd.h"
32 #include "s_debug.h"
33 #include "s_misc.h"
34 #include "struct.h"
35
36 #include <assert.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41
42 /** @page numnicks Numeric Nicks
43  * %Numeric nicks (numnicks) are new as of version ircu2.10.00beta1.
44  *
45  * The idea is as follows:
46  * In most messages (for protocol 10+) the original nick will be
47  * replaced by a 5 character string: YYXXX
48  * Where 'YY' represents the server, and 'XXX' the nick on that server.
49  *
50  * 'YYXXX' should not interfer with the input parser, and therefore is
51  * not allowed to contain spaces or a ':'.
52  * Also, 'YY' can't start with a '+' because of m_server().
53  *
54  * We keep the characters printable for debugging reasons too.
55  *
56  * The 'XXX' value can be larger then the maximum number of clients
57  * per server, we use a mask (Server::nn_mask) to get the real
58  * client numeric. The overhead is used to have some redundancy so
59  * just-disconnected-client aren't confused with just-connected ones.
60  */
61
62
63 /* These must be the same on ALL servers ! Do not change ! */
64
65 /** Number of bits encoded in one numnick character. */
66 #define NUMNICKLOG 6
67 /** Bitmask to select value of next numnick character. */
68 #define NUMNICKMASK 63          /* (NUMNICKBASE-1) */
69 /** Number of servers representable in a numnick. */
70 #define NN_MAX_SERVER 4096      /* (NUMNICKBASE * NUMNICKBASE) */
71 /** Number of clients representable in a numnick. */
72 #define NN_MAX_CLIENT 262144    /* NUMNICKBASE ^ 3 */
73
74 /*
75  * The internal counter for the 'XX' of local clients
76  */
77 /** Maximum used server numnick, plus one. */
78 static unsigned int lastNNServer = 0;
79 /** Array of servers indexed by numnick. */
80 static struct Client* server_list[NN_MAX_SERVER];
81
82 /* *INDENT-OFF* */
83
84 /**
85  * Converts a numeric to the corresponding character.
86  * The following characters are currently known to be forbidden:
87  *
88  * '\\0' : Because we use '\\0' as end of line.
89  *
90  * ' '  : Because parse_*() uses this as parameter seperator.
91  *
92  * ':'  : Because parse_server() uses this to detect if a prefix is a
93  *        numeric or a name.
94  *
95  * '+'  : Because m_nick() uses this to determine if parv[6] is a
96  *        umode or not.
97  *
98  * '&', '#', '$', '@' and '%' :
99  *        Because m_message() matches these characters to detect special cases.
100  */
101 static const char convert2y[] = {
102   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
103   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
104   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
105   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
106 };
107
108 /** Converts a character to its (base64) numnick value. */
109 static const unsigned int convert2n[] = {
110    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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   52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
114    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
115   15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
116    0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
117   41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
118
119    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 };
128
129 /* *INDENT-ON* */
130
131 /** Convert a string to its value as a numnick.
132  * @param[in] s Numnick string to decode.
133  * @return %Numeric nickname value.
134  */
135 unsigned int base64toint(const char* s)
136 {
137   unsigned int i = convert2n[(unsigned char) *s++];
138   while (*s) {
139     i <<= NUMNICKLOG;
140     i += convert2n[(unsigned char) *s++];
141   }
142   return i;
143 }
144
145 /** Encode a number as a numnick.
146  * @param[out] buf Output buffer.
147  * @param[in] v Value to encode.
148  * @param[in] count Number of numnick digits to write to \a buf.
149  */
150 const char* inttobase64(char* buf, unsigned int v, unsigned int count)
151 {
152   buf[count] = '\0';
153   while (count > 0) {
154     buf[--count] = convert2y[(v & NUMNICKMASK)];
155     v >>= NUMNICKLOG;
156   }
157   return buf;
158 }
159
160 /** Look up a server by numnick string.
161  * See @ref numnicks for more details.
162  * @param[in] numeric %Numeric nickname of server (may contain trailing junk).
163  * @return %Server with that numnick (or NULL).
164  */
165 static struct Client* FindXNServer(const char* numeric)
166 {
167   char buf[3];
168   buf[0] = *numeric++;
169   buf[1] = *numeric;
170   buf[2] = '\0';
171   Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
172   return server_list[base64toint(buf)];
173 }
174
175 /** Look up a server by numnick string.
176  * See @ref numnicks for more details.
177  * @param[in] numeric %Numeric nickname of server.
178  * @return %Server with that numnick (or NULL).
179  */
180 struct Client* FindNServer(const char* numeric)
181 {
182   unsigned int len = strlen(numeric);
183
184   if (len < 3) {
185     Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
186     return server_list[base64toint(numeric)];
187   }
188   else if (len == 3) {
189     Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric, 
190            convert2n[(unsigned char) *numeric]));
191     return server_list[convert2n[(unsigned char) *numeric]];
192   }
193   return FindXNServer(numeric);
194 }
195
196 /** Look up a user by numnick string.
197  * See @ref numnicks for more details.
198  * @param[in] yxx %Numeric nickname of user.
199  * @return %User with that numnick (or NULL).
200  */
201 struct Client* findNUser(const char* yxx)
202 {
203   struct Client* server = 0;
204   if (5 == strlen(yxx)) {
205     if (0 != (server = FindXNServer(yxx))) {
206       Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, 
207              base64toint(yxx + 2) & cli_serv(server)->nn_mask));
208       return cli_serv(server)->client_list[base64toint(yxx + 2) & cli_serv(server)->nn_mask];
209     }
210   }
211   else if (0 != (server = FindNServer(yxx))) {
212     Debug((DEBUG_DEBUG, "findNUser: %s(%d)",
213            yxx, base64toint(yxx + 1) & cli_serv(server)->nn_mask));
214     return cli_serv(server)->client_list[base64toint(yxx + 1) & cli_serv(server)->nn_mask];
215   }
216   return 0;
217 }
218
219 /** Remove a client from a server's user array.
220  * @param[in] server %Server that owns the user to remove.
221  * @param[in] yxx Numnick of client to remove.
222  */
223 void RemoveYXXClient(struct Client* server, const char* yxx)
224 {
225   assert(0 != server);
226   assert(0 != yxx);
227   if (*yxx) {
228     Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx,
229            base64toint(yxx) & cli_serv(server)->nn_mask));
230     cli_serv(server)->client_list[base64toint(yxx) & cli_serv(server)->nn_mask] = 0;
231   }
232 }
233
234 /** Set a server's numeric nick.
235  * @param[in] cptr %Client that announced the server (ignored).
236  * @param[in,out] server %Server that is being assigned a numnick.
237  * @param[in] yxx %Numeric nickname for server.
238  */
239 void SetServerYXX(struct Client* cptr, struct Client* server, const char* yxx)
240 {
241   unsigned int index;
242   if (5 == strlen(yxx)) {
243     ircd_strncpy(cli_yxx(server), yxx, 2);
244     ircd_strncpy(cli_serv(server)->nn_capacity, yxx + 2, 3);
245   }
246   else {
247     (cli_yxx(server))[0]               = yxx[0];
248     cli_serv(server)->nn_capacity[0] = yxx[1];
249     cli_serv(server)->nn_capacity[1] = yxx[2];
250   }
251   cli_serv(server)->nn_mask = base64toint(cli_serv(server)->nn_capacity);
252
253   index = base64toint(cli_yxx(server));
254   if (index >= lastNNServer)
255     lastNNServer = index + 1;
256   server_list[index] = server;
257
258   /* Note, exit_one_client uses the fact that `client_list' != NULL to
259    * determine that SetServerYXX has been called - and then calls
260    * ClearServerYXX. However, freeing the allocation happens in free_client() */
261   cli_serv(server)->client_list =
262       (struct Client**) MyCalloc(cli_serv(server)->nn_mask + 1, sizeof(struct Client*));
263 }
264
265 /** Set a server's capacity.
266  * @param[in] c %Server whose capacity is being set.
267  * @param[in] capacity Maximum number of clients the server supports.
268  */
269 void SetYXXCapacity(struct Client* c, unsigned int capacity)
270 {
271   unsigned int max_clients = 16;
272   /*
273    * Calculate mask to be used for the maximum number of clients
274    */
275   while (max_clients < capacity)
276     max_clients <<= 1;
277   /*
278    * Sanity checks
279    */
280   if (max_clients > NN_MAX_CLIENT) {
281     fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
282             "too large ! Please decrease this value.\n",
283              max_clients - NN_MAX_CLIENT);
284     exit(-1);
285   }
286   --max_clients;
287   inttobase64(cli_serv(c)->nn_capacity, max_clients, 3);
288   cli_serv(c)->nn_mask = max_clients;       /* Our Numeric Nick mask */
289   cli_serv(c)->client_list = (struct Client**) MyCalloc(max_clients + 1,
290                                                      sizeof(struct Client*));
291   server_list[base64toint(cli_yxx(c))] = c;
292 }
293
294 /** Set a server's numeric nick.
295  * See @ref numnicks for more details.
296  * @param[in] c %Server that is being assigned a numnick.
297  * @param[in] numeric Numnick value for server.
298  */
299 void SetYXXServerName(struct Client* c, unsigned int numeric)
300 {
301   assert(0 != c);
302   assert(numeric < NN_MAX_SERVER);
303
304   inttobase64(cli_yxx(c), numeric, 2);
305   if (numeric >= lastNNServer)
306     lastNNServer = numeric + 1;
307   server_list[numeric] = c;
308 }
309
310 /** Unassign a server's numnick.
311  * @param[in] server %Server that should be removed from the numnick table.
312  */
313 void ClearServerYXX(const struct Client *server)
314 {
315   unsigned int index = base64toint(cli_yxx(server));
316   if (server_list[index] == server)     /* Sanity check */
317     server_list[index] = 0;
318 }
319
320 /** Register numeric of new (remote) client.
321  * See @ref numnicks for more details.
322  * Add it to the appropriate client_list.
323  * @param[in] acptr %User being registered.
324  * @param[in] yxx User's numnick.
325  */
326 void SetRemoteNumNick(struct Client* acptr, const char *yxx)
327 {
328   struct Client** acptrp;
329   struct Client*  server = cli_user(acptr)->server;
330
331   if (5 == strlen(yxx)) {
332     strcpy(cli_yxx(acptr), yxx + 2);
333   }
334   else {
335     (cli_yxx(acptr))[0] = *++yxx;
336     (cli_yxx(acptr))[1] = *++yxx;
337     (cli_yxx(acptr))[2] = 0;
338   }
339   Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr),
340          base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask));
341
342   acptrp = &(cli_serv(server))->client_list[base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask];
343   if (*acptrp) {
344     /*
345      * this exits the old client in the array, not the client
346      * that is being set
347      */
348     exit_client(cli_from(acptr), *acptrp, server, "Numeric nick collision (Ghost)");
349   }
350   *acptrp = acptr;
351 }
352
353
354 /** Register numeric of new (local) client.
355  * See @ref numnicks for more details.
356  * Assign a numnick and add it to our client_list.
357  * @param[in] cptr %User being registered.
358  */
359 int SetLocalNumNick(struct Client *cptr)
360 {
361   static unsigned int last_nn     = 0;
362   struct Client**     client_list = cli_serv(&me)->client_list;
363   unsigned int        mask        = cli_serv(&me)->nn_mask;
364   unsigned int        count       = 0;
365
366   assert(cli_user(cptr)->server == &me);
367
368   while (client_list[last_nn & mask]) {
369     if (++count == NN_MAX_CLIENT) {
370       assert(count < NN_MAX_CLIENT);
371       return 0;
372     }
373     if (++last_nn == NN_MAX_CLIENT)
374       last_nn = 0;
375   }
376   client_list[last_nn & mask] = cptr;  /* Reserve the numeric ! */
377
378   inttobase64(cli_yxx(cptr), last_nn, 3);
379   if (++last_nn == NN_MAX_CLIENT)
380     last_nn = 0;
381   return 1;
382 }
383
384 /** Mark servers whose name matches the given (compiled) mask by
385  * setting their FLAG_MAP flag.
386  * @param[in] cmask Compiled mask for server names.
387  * @param[in] minlen Minimum match length for \a cmask.
388  * @return Number of servers marked.
389  */
390 int markMatchexServer(const char *cmask, int minlen)
391 {
392   int cnt = 0;
393   int i;
394   struct Client *acptr;
395
396   for (i = 0; i < lastNNServer; i++) {
397     if ((acptr = server_list[i]))
398     {
399       if (matchexec(cli_name(acptr), cmask, minlen))
400         ClrFlag(acptr, FLAG_MAP);
401       else
402       {
403         SetFlag(acptr, FLAG_MAP);
404         cnt++;
405       }
406     }
407   }
408   return cnt;
409 }
410
411 /** Find first server whose name matches the given mask.
412  * @param[in,out] mask %Server name mask (collapse()d in-place).
413  * @return Matching server with lowest numnick value (or NULL).
414  */
415 struct Client* find_match_server(char *mask)
416 {
417   struct Client *acptr;
418   int i;
419
420   if (!(BadPtr(mask))) {
421     collapse(mask);
422     for (i = 0; i < lastNNServer; i++) {
423       if ((acptr = server_list[i]) && (!match(mask, cli_name(acptr))))
424         return acptr;
425     }
426   }
427   return 0;
428 }
429
430 /** Encode an IP address in the base64 used by numnicks.
431  * For IPv4 addresses (including IPv4-mapped and IPv4-compatible IPv6
432  * addresses), the 32-bit host address is encoded directly as six
433  * characters.
434  *
435  * For IPv6 addresses, each 16-bit address segment is encoded as three
436  * characters, but the longest run of zero segments is encoded using an
437  * underscore.
438  * @param[out] buf Output buffer to write to.
439  * @param[in] addr IP address to encode.
440  * @param[in] count Number of bytes writable to \a buf.
441  */
442 const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int count)
443 {
444   if (irc_in_addr_is_ipv4(addr)) {
445     assert(count >= 6);
446     inttobase64(buf, (htons(addr->in6_16[6]) << 16) | htons(addr->in6_16[7]), 6);
447   } else {
448     unsigned int max_start, max_zeros, curr_zeros, zero, ii;
449     char *output = buf;
450
451     assert(count >= 25);
452     /* Can start by printing out the leading non-zero parts. */
453     for (ii = 0; (addr->in6_16[ii]) && (ii < 8); ++ii) {
454       inttobase64(output, ntohs(addr->in6_16[ii]), 3);
455       output += 3;
456     }
457     /* Find the longest run of zeros. */
458     for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
459       if (!addr->in6_16[ii])
460         curr_zeros++;
461       else if (curr_zeros > max_zeros) {
462         max_start = ii - curr_zeros;
463         max_zeros = curr_zeros;
464         curr_zeros = 0;
465       }
466     }
467     if (curr_zeros > max_zeros) {
468       max_start = ii - curr_zeros;
469       max_zeros = curr_zeros;
470       curr_zeros = 0;
471     }
472     /* Print the rest of the address */
473     for (ii = zero; ii < 8; ) {
474       if ((ii == max_start) && max_zeros) {
475         *output++ = '_';
476         ii += max_zeros;
477       } else {
478         inttobase64(output, ntohs(addr->in6_16[ii]), 3);
479         output += 3;
480         ii++;
481       }
482     }
483     *output = '\0';
484   }
485   return buf;
486 }
487
488 /** Decode an IP address from base64.
489  * @param[in] input Input buffer to decode.
490  * @param[out] addr IP address structure to populate.
491  */
492 void base64toip(const char* input, struct irc_in_addr* addr)
493 {
494   memset(addr, 0, sizeof(*addr));
495   if (strlen(input) == 6) {
496     unsigned int in = base64toint(input);
497     addr->in6_16[6] = htons(in >> 16);
498     addr->in6_16[7] = htons(in & 65535);
499   } else {
500     unsigned int pos = 0;
501     do {
502       if (*input == '_') {
503         unsigned int left;
504         for (left = (25 - strlen(input)) / 3; left; left--)
505           addr->in6_16[pos++] = 0;
506         input++;
507       } else {
508         unsigned short accum = convert2n[(unsigned char)*input++];
509         accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
510         accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
511         addr->in6_16[pos++] = ntohs(accum);
512         input += 3;
513       }
514     } while (pos < 8);
515   }
516 }