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