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