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