]> jfr.im git - irc/quakenet/newserv.git/blob - lib/irc_ipv6.c
Add jupe support
[irc/quakenet/newserv.git] / lib / irc_ipv6.c
1 /* IPv6 helper functions */
2
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6 #include <netdb.h>
7 #include "irc_ipv6.h"
8
9 /*
10 * this new faster inet_ntoa was ripped from:
11 * From: Thomas Helvey <tomh@inxpress.net>
12 */
13 /** Array of text strings for dotted quads. */
14 static const char* IpQuadTab[] =
15 {
16 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
17 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
18 "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
19 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
20 "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
21 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
22 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
23 "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
24 "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
25 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
26 "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
27 "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
28 "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
29 "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
30 "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
31 "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
32 "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
33 "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
34 "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
35 "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
36 "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
37 "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
38 "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
39 "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
40 "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
41 "250", "251", "252", "253", "254", "255"
42 };
43
44 /** Convert an IP address to printable ASCII form.
45 * This is generally deprecated in favor of ircd_ntoa_r().
46 * @param[in] in Address to convert.
47 * @return Pointer to a static buffer containing the readable form.
48 */
49 const char* ircd_ntoa(const struct irc_in_addr* in)
50 {
51 static char buf[SOCKIPLEN];
52 return ircd_ntoa_r(buf, in);
53 }
54
55 /** Convert an IP address to printable ASCII form.
56 * @param[out] buf Output buffer to write to.
57 * @param[in] in Address to format.
58 * @return Pointer to the output buffer \a buf.
59 */
60 const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
61 {
62 assert(buf != NULL);
63 assert(in != NULL);
64
65 if (irc_in_addr_is_ipv4(in)) {
66 unsigned int pos, len;
67 unsigned char *pch;
68
69 pch = (unsigned char*)&in->in6_16[6];
70 len = strlen(IpQuadTab[*pch]);
71 memcpy(buf, IpQuadTab[*pch++], len);
72 pos = len;
73 buf[pos++] = '.';
74 len = strlen(IpQuadTab[*pch]);
75 memcpy(buf+pos, IpQuadTab[*pch++], len);
76 pos += len;
77 buf[pos++] = '.';
78 len = strlen(IpQuadTab[*pch]);
79 memcpy(buf+pos, IpQuadTab[*pch++], len);
80 pos += len;
81 buf[pos++] = '.';
82 len = strlen(IpQuadTab[*pch]);
83 memcpy(buf+pos, IpQuadTab[*pch++], len);
84 buf[pos + len] = '\0';
85 return buf;
86 } else {
87 static const char hexdigits[] = "0123456789abcdef";
88 unsigned int pos, part, max_start, max_zeros, curr_zeros, ii;
89
90 /* Find longest run of zeros. */
91 for (max_start = ii = 1, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
92 if (!in->in6_16[ii])
93 curr_zeros++;
94 else if (curr_zeros > max_zeros) {
95 max_start = ii - curr_zeros;
96 max_zeros = curr_zeros;
97 curr_zeros = 0;
98 }
99 }
100 if (curr_zeros > max_zeros) {
101 max_start = ii - curr_zeros;
102 max_zeros = curr_zeros;
103 }
104
105 /* Print out address. */
106 /** Append \a CH to the output buffer. */
107 #define APPEND(CH) do { buf[pos++] = (CH); } while (0)
108 for (pos = ii = 0; (ii < 8); ++ii) {
109 if ((max_zeros > 0) && (ii == max_start)) {
110 APPEND(':');
111 ii += max_zeros - 1;
112 continue;
113 }
114 part = ntohs(in->in6_16[ii]);
115 if (part >= 0x1000)
116 APPEND(hexdigits[part >> 12]);
117 if (part >= 0x100)
118 APPEND(hexdigits[(part >> 8) & 15]);
119 if (part >= 0x10)
120 APPEND(hexdigits[(part >> 4) & 15]);
121 APPEND(hexdigits[part & 15]);
122 if (ii < 7)
123 APPEND(':');
124 }
125 #undef APPEND
126
127 /* Nul terminate and return number of characters used. */
128 buf[pos++] = '\0';
129 return buf;
130 }
131 }
132
133 /** Attempt to parse an IPv4 address into a network-endian form.
134 * @param[in] input Input string.
135 * @param[out] output Network-endian representation of the address.
136 * @param[out] pbits Number of bits found in pbits.
137 * @return Number of characters used from \a input, or 0 if the parse failed.
138 */
139 static unsigned int
140 ircd_aton_ip4(const char *input, unsigned int *output, unsigned char *pbits)
141 {
142 unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits;
143
144 /* Intentionally no support for bizarre IPv4 formats (plain
145 * integers, octal or hex components) -- only vanilla dotted
146 * decimal quads.
147 */
148 if (input[0] == '.')
149 return 0;
150 bits = 32;
151 while (1) switch (input[pos]) {
152 case '\0':
153 if (dots < 3)
154 return 0;
155 out:
156 ip |= part << (24 - 8 * dots);
157 *output = htonl(ip);
158 if (pbits)
159 *pbits = bits;
160 return pos;
161 case '.':
162 if (input[++pos] == '.')
163 return 0;
164 ip |= part << (24 - 8 * dots++);
165 part = 0;
166 if (input[pos] == '*') {
167 while (input[++pos] == '*') ;
168 if (input[pos] != '\0')
169 return 0;
170 if (pbits)
171 *pbits = dots * 8;
172 *output = htonl(ip);
173 return pos;
174 }
175 break;
176 case '/':
177 if (!pbits || !IsDigit(input[pos + 1]))
178 return 0;
179 for (bits = 0; IsDigit(input[++pos]); )
180 bits = bits * 10 + input[pos] - '0';
181 if (bits > 32)
182 return 0;
183 goto out;
184 case '0': case '1': case '2': case '3': case '4':
185 case '5': case '6': case '7': case '8': case '9':
186 part = part * 10 + input[pos++] - '0';
187 if (part > 255)
188 return 0;
189 break;
190 default:
191 return 0;
192 }
193 }
194
195 /** Parse a numeric IPv4 or IPv6 address into an irc_in_addr.
196 * @param[in] input Input buffer.
197 * @param[out] ip Receives parsed IP address.
198 * @param[out] pbits If non-NULL, receives number of bits specified in address mask.
199 * @return Number of characters used from \a input, or 0 if the
200 * address was unparseable or malformed.
201 */
202 int
203 ipmask_parse(const char *input, struct irc_in_addr *ip, unsigned char *pbits)
204 {
205 char *colon;
206 char *dot;
207
208 assert(ip);
209 assert(input);
210 memset(ip, 0, sizeof(*ip));
211 colon = strchr(input, ':');
212 dot = strchr(input, '.');
213
214 if (colon && (!dot || (dot > colon))) {
215 unsigned int part = 0, pos = 0, ii = 0, colon = 8;
216 const char *part_start = NULL;
217
218 /* Parse IPv6, possibly like ::127.0.0.1.
219 * This is pretty straightforward; the only trick is borrowed
220 * from Paul Vixie (BIND): when it sees a "::" continue as if
221 * it were a single ":", but note where it happened, and fill
222 * with zeros afterward.
223 */
224 if (input[pos] == ':') {
225 if ((input[pos+1] != ':') || (input[pos+2] == ':'))
226 return 0;
227 colon = 0;
228 pos += 2;
229 part_start = input + pos;
230 }
231 while (ii < 8) switch (input[pos]) {
232 unsigned char chval;
233 case '0': case '1': case '2': case '3': case '4':
234 case '5': case '6': case '7': case '8': case '9':
235 chval = input[pos] - '0';
236 use_chval:
237 part = (part << 4) | chval;
238 if (part > 0xffff)
239 return 0;
240 pos++;
241 break;
242 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
243 chval = input[pos] - 'A' + 10;
244 goto use_chval;
245 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
246 chval = input[pos] - 'a' + 10;
247 goto use_chval;
248 case ':':
249 part_start = input + ++pos;
250 if (input[pos] == '.')
251 return 0;
252 ip->in6_16[ii++] = htons(part);
253 part = 0;
254 if (input[pos] == ':') {
255 if (colon < 8)
256 return 0;
257 colon = ii;
258 pos++;
259 }
260 break;
261 case '.': {
262 uint32_t ip4;
263 unsigned int len;
264 len = ircd_aton_ip4(part_start, &ip4, pbits);
265 if (!len || (ii > 6))
266 return 0;
267 ip->in6_16[ii++] = htons(ntohl(ip4) >> 16);
268 ip->in6_16[ii++] = htons(ntohl(ip4) & 65535);
269 if (pbits)
270 *pbits += 96;
271 pos = part_start + len - input;
272 goto finish;
273 }
274 case '/':
275 if (!pbits || !IsDigit(input[pos + 1]))
276 return 0;
277 ip->in6_16[ii++] = htons(part);
278 for (part = 0; IsDigit(input[++pos]); )
279 part = part * 10 + input[pos] - '0';
280 if (part > 128)
281 return 0;
282 *pbits = part;
283 goto finish;
284 case '*':
285 while (input[++pos] == '*') ;
286 if (input[pos] != '\0' || colon < 8)
287 return 0;
288 if (pbits)
289 *pbits = ii * 16;
290 return pos;
291 case '\0':
292 ip->in6_16[ii++] = htons(part);
293 if (colon == 8 && ii < 8)
294 return 0;
295 if (pbits)
296 *pbits = 128;
297 goto finish;
298 default:
299 return 0;
300 }
301 finish:
302 if (colon < 8) {
303 unsigned int jj;
304 /* Shift stuff after "::" up and fill middle with zeros. */
305 for (jj = 0; jj < ii - colon; jj++)
306 ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1];
307 for (jj = 0; jj < 8 - ii; jj++)
308 ip->in6_16[colon + jj] = 0;
309 }
310 return pos;
311 } else if (dot || strchr(input, '/')) {
312 unsigned int addr;
313 int len = ircd_aton_ip4(input, &addr, pbits);
314 if (len) {
315 ip->in6_16[5] = htons(65535);
316 ip->in6_16[6] = htons(ntohl(addr) >> 16);
317 ip->in6_16[7] = htons(ntohl(addr) & 65535);
318 if (pbits)
319 *pbits += 96;
320 }
321 return len;
322 } else if (input[0] == '*') {
323 unsigned int pos = 0;
324 while (input[++pos] == '*') ;
325 if (input[pos] != '\0')
326 return 0;
327 if (pbits)
328 *pbits = 0;
329 return pos;
330 } else return 0; /* parse failed */
331 }
332
333 /* from numnicks.c */
334
335 /**
336 * Converts a numeric to the corresponding character.
337 * The following characters are currently known to be forbidden:
338 *
339 * '\\0' : Because we use '\\0' as end of line.
340 *
341 * ' ' : Because parse_*() uses this as parameter separator.
342 *
343 * ':' : Because parse_server() uses this to detect if a prefix is a
344 * numeric or a name.
345 *
346 * '+' : Because m_nick() uses this to determine if parv[6] is a
347 * umode or not.
348 *
349 * '&', '#', '$', '@' and '%' :
350 * Because m_message() matches these characters to detect special cases.
351 */
352 static const char convert2y[] = {
353 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
354 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
355 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
356 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
357 };
358
359 /** Converts a character to its (base64) numnick value. */
360 static const unsigned int convert2n[] = {
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
365 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
366 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
367 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
368 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
369
370 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
372 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
374 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
378 };
379
380 /** Number of bits encoded in one numnick character. */
381 #define NUMNICKLOG 6
382 /** Bitmask to select value of next numnick character. */
383 #define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
384 /** Number of servers representable in a numnick. */
385
386 /* *INDENT-ON* */
387
388 /** Convert a string to its value as a numnick.
389 * @param[in] s Numnick string to decode.
390 * @return %Numeric nickname value.
391 */
392 unsigned int base64toint(const char* s)
393 {
394 unsigned int i = convert2n[(unsigned char) *s++];
395 while (*s) {
396 i <<= NUMNICKLOG;
397 i += convert2n[(unsigned char) *s++];
398 }
399 return i;
400 }
401
402 /** Encode a number as a numnick.
403 * @param[out] buf Output buffer.
404 * @param[in] v Value to encode.
405 * @param[in] count Number of numnick digits to write to \a buf.
406 */
407 const char* inttobase64(char* buf, unsigned int v, unsigned int count)
408 {
409 buf[count] = '\0';
410 while (count > 0) {
411 buf[--count] = convert2y[(v & NUMNICKMASK)];
412 v >>= NUMNICKLOG;
413 }
414 return buf;
415 }
416
417 /** Number of bits encoded in one numnick character. */
418 #define NUMNICKLOG 6
419
420 /** Encode an IP address in the base64 used by numnicks.
421 * For IPv4 addresses (including IPv4-mapped and IPv4-compatible IPv6
422 * addresses), the 32-bit host address is encoded directly as six
423 * characters.
424 *
425 * For IPv6 addresses, each 16-bit address segment is encoded as three
426 * characters, but the longest run of zero segments is encoded using an
427 * underscore.
428 * @param[out] buf Output buffer to write to.
429 * @param[in] addr IP address to encode.
430 * @param[in] count Number of bytes writable to \a buf.
431 * @param[in] v6_ok If non-zero, peer understands base-64 encoded IPv6 addresses.
432 */
433 const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int count, int v6_ok)
434 {
435 if (irc_in_addr_is_ipv4(addr)) {
436 assert(count >= 6);
437 inttobase64(buf, (ntohs(addr->in6_16[6]) << 16) | ntohs(addr->in6_16[7]), 6);
438 } else if (!v6_ok) {
439 assert(count >= 6);
440 if (addr->in6_16[0] == htons(0x2002))
441 inttobase64(buf, (ntohs(addr->in6_16[1]) << 16) | ntohs(addr->in6_16[2]), 6);
442 else
443 strcpy(buf, "AAAAAA");
444 } else {
445 unsigned int max_start, max_zeros, curr_zeros, zero, ii;
446 char *output = buf;
447
448 assert(count >= 25);
449 /* Can start by printing out the leading non-zero parts. */
450 for (ii = 0; (addr->in6_16[ii]) && (ii < 8); ++ii) {
451 inttobase64(output, ntohs(addr->in6_16[ii]), 3);
452 output += 3;
453 }
454 /* Find the longest run of zeros. */
455 for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
456 if (!addr->in6_16[ii])
457 curr_zeros++;
458 else if (curr_zeros > max_zeros) {
459 max_start = ii - curr_zeros;
460 max_zeros = curr_zeros;
461 curr_zeros = 0;
462 }
463 }
464 if (curr_zeros > max_zeros) {
465 max_start = ii - curr_zeros;
466 max_zeros = curr_zeros;
467 curr_zeros = 0;
468 }
469 /* Print the rest of the address */
470 for (ii = zero; ii < 8; ) {
471 if ((ii == max_start) && max_zeros) {
472 *output++ = '_';
473 ii += max_zeros;
474 } else {
475 inttobase64(output, ntohs(addr->in6_16[ii]), 3);
476 output += 3;
477 ii++;
478 }
479 }
480 *output = '\0';
481 }
482 return buf;
483 }
484
485 /** Decode an IP address from base64.
486 * @param[in] input Input buffer to decode.
487 * @param[out] addr IP address structure to populate.
488 */
489 void base64toip(const char* input, struct irc_in_addr* addr)
490 {
491 memset(addr, 0, sizeof(*addr));
492 if (strlen(input) == 6) {
493 unsigned int in = base64toint(input);
494 /* An all-zero address should stay that way. */
495 if (in) {
496 addr->in6_16[5] = htons(65535);
497 addr->in6_16[6] = htons(in >> 16);
498 addr->in6_16[7] = htons(in & 65535);
499 }
500 } else {
501 unsigned int pos = 0;
502 do {
503 if (*input == '_') {
504 unsigned int left;
505 for (left = (25 - strlen(input)) / 3 - pos; left; left--)
506 addr->in6_16[pos++] = 0;
507 input++;
508 } else {
509 unsigned short accum = convert2n[(unsigned char)*input++];
510 accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
511 accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
512 addr->in6_16[pos++] = ntohs(accum);
513 }
514 } while (pos < 8);
515 }
516 }
517