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