]>
jfr.im git - irc/quakenet/newserv.git/blob - lib/irc_ipv6.c
1 /* IPv6 helper functions */
11 #warning This source file is probably GPLed, it needs relicensing.
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.
18 const char* ircd_ntoa(const struct irc_in_addr
* in
)
20 static char buf
[SOCKIPLEN
];
21 return ircd_ntoa_r(buf
, in
);
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.
29 const char* ircd_ntoa_r(char* buf
, const struct irc_in_addr
* in
)
34 if (irc_in_addr_is_ipv4(in
)) {
37 pch
= (unsigned char*)&in
->in6_16
[6];
38 sprintf(buf
,"%d.%d.%d.%d",pch
[0],pch
[1],pch
[2],pch
[3]);
41 unsigned int pos
, part
, max_start
, max_zeros
, curr_zeros
, ii
;
43 /* Find longest run of zeros. */
44 for (max_start
= ii
= 1, max_zeros
= curr_zeros
= 0; ii
< 8; ++ii
) {
47 else if (curr_zeros
> max_zeros
) {
48 max_start
= ii
- curr_zeros
;
49 max_zeros
= curr_zeros
;
53 if (curr_zeros
> max_zeros
) {
54 max_start
= ii
- curr_zeros
;
55 max_zeros
= curr_zeros
;
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
)) {
67 part
= ntohs(in
->in6_16
[ii
]);
68 pos
+=sprintf(buf
+pos
,"%x",part
);
74 /* Nul terminate and return number of characters used. */
80 /** Convert a CIDR mask to printable ASCII form.
81 * This is generally deprecated in favor of ircd_ntoa_masked_r().
82 * @param[in] in Address to convert.
83 * @param[in] bits Mask bits.
84 * @return Pointer to a static buffer containing the readable form.
86 const char* ircd_ntoa_masked(const struct irc_in_addr
* in
, unsigned char bits
)
88 static char buf
[CIDRLEN
];
89 return ircd_ntoa_masked_r(buf
, in
, bits
);
92 /** Convert a CIDR mask to printable ASCII form.
93 * @param[out] buf Output buffer to write to.
94 * @param[in] in Address to format.
95 * @param[in] bits Mask bits.
96 * @return Pointer to the output buffer \a buf.
98 const char* ircd_ntoa_masked_r(char* buf
, const struct irc_in_addr
* in
, unsigned char bits
)
100 char inname
[SOCKIPLEN
];
101 struct irc_in_addr intemp
;
105 int curbits
= bits
- i
* 16;
112 uint16_t mask
= 0xffff & ~((1 << (16 - curbits
)) - 1);
113 intemp
.in6_16
[i
] = htons(ntohs(in
->in6_16
[i
]) & mask
);
116 ircd_ntoa_r(inname
, &intemp
);
117 sprintf(buf
, "%s/%u", inname
, irc_bitlen(in
, bits
));
122 /** Attempt to parse an IPv4 address into a network-endian form.
123 * @param[in] input Input string.
124 * @param[out] output Network-endian representation of the address.
125 * @param[out] pbits Number of bits found in pbits.
126 * @return Number of characters used from \a input, or 0 if the parse failed.
129 ircd_aton_ip4(const char *input
, unsigned int *output
, unsigned char *pbits
)
131 unsigned int dots
= 0, pos
= 0, part
= 0, ip
= 0, bits
;
133 /* Intentionally no support for bizarre IPv4 formats (plain
134 * integers, octal or hex components) -- only vanilla dotted
140 while (1) switch (input
[pos
]) {
145 ip
|= part
<< (24 - 8 * dots
);
153 if (input
[++pos
] == '.')
155 ip
|= part
<< (32 - 8 * dots
);
157 if (input
[pos
] == '*') {
158 while (input
[++pos
] == '*' || input
[pos
] == '.') ;
159 if (input
[pos
] != '\0')
168 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
170 for (bits
= 0; IsDigit(input
[++pos
]); )
171 bits
= bits
* 10 + input
[pos
] - '0';
175 case '0': case '1': case '2': case '3': case '4':
176 case '5': case '6': case '7': case '8': case '9':
177 part
= part
* 10 + input
[pos
++] - '0';
186 /** Parse a numeric IPv4 or IPv6 address into an irc_in_addr.
187 * @param[in] input Input buffer.
188 * @param[out] ip Receives parsed IP address.
189 * @param[out] pbits If non-NULL, receives number of bits specified in address mask.
190 * @return Number of characters used from \a input, or 0 if the
191 * address was unparseable or malformed.
194 ipmask_parse(const char *input
, struct irc_in_addr
*ip
, unsigned char *pbits
)
201 memset(ip
, 0, sizeof(*ip
));
202 colon
= strchr(input
, ':');
203 dot
= strchr(input
, '.');
205 if (colon
&& (!dot
|| (dot
> colon
))) {
206 unsigned int part
= 0, pos
= 0, ii
= 0, colon
= 8;
207 const char *part_start
= NULL
;
209 /* Parse IPv6, possibly like ::127.0.0.1.
210 * This is pretty straightforward; the only trick is borrowed
211 * from Paul Vixie (BIND): when it sees a "::" continue as if
212 * it were a single ":", but note where it happened, and fill
213 * with zeros afterward.
215 if (input
[pos
] == ':') {
216 if ((input
[pos
+1] != ':') || (input
[pos
+2] == ':'))
220 part_start
= input
+ pos
;
222 while (ii
< 8) switch (input
[pos
]) {
224 case '0': case '1': case '2': case '3': case '4':
225 case '5': case '6': case '7': case '8': case '9':
226 chval
= input
[pos
] - '0';
228 part
= (part
<< 4) | chval
;
233 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
234 chval
= input
[pos
] - 'A' + 10;
236 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
237 chval
= input
[pos
] - 'a' + 10;
240 part_start
= input
+ ++pos
;
241 if (input
[pos
] == '.')
243 ip
->in6_16
[ii
++] = htons(part
);
245 if (input
[pos
] == ':') {
255 len
= ircd_aton_ip4(part_start
, &ip4
, pbits
);
256 if (!len
|| (ii
> 6))
258 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) >> 16);
259 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) & 65535);
262 pos
= part_start
+ len
- input
;
266 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
268 ip
->in6_16
[ii
++] = htons(part
);
269 for (part
= 0; IsDigit(input
[++pos
]); )
270 part
= part
* 10 + input
[pos
] - '0';
276 while (input
[++pos
] == '*' || input
[pos
] == ':') ;
277 if (input
[pos
] != '\0' || colon
< 8)
283 ip
->in6_16
[ii
++] = htons(part
);
284 if (colon
== 8 && ii
< 8)
292 if (input
[pos
] != '\0')
297 /* Shift stuff after "::" up and fill middle with zeros. */
298 for (jj
= 0; jj
< ii
- colon
; jj
++)
299 ip
->in6_16
[7 - jj
] = ip
->in6_16
[ii
- jj
- 1];
300 for (jj
= 0; jj
< 8 - ii
; jj
++)
301 ip
->in6_16
[colon
+ jj
] = 0;
304 } else if (dot
|| strchr(input
, '/')) {
306 int len
= ircd_aton_ip4(input
, &addr
, pbits
);
308 ip
->in6_16
[5] = htons(65535);
309 ip
->in6_16
[6] = htons(ntohl(addr
) >> 16);
310 ip
->in6_16
[7] = htons(ntohl(addr
) & 65535);
315 } else if (input
[0] == '*') {
316 unsigned int pos
= 0;
317 while (input
[++pos
] == '*') ;
318 if (input
[pos
] != '\0')
323 } else return 0; /* parse failed */
326 /* from numnicks.c */
329 * Converts a numeric to the corresponding character.
330 * The following characters are currently known to be forbidden:
332 * '\\0' : Because we use '\\0' as end of line.
334 * ' ' : Because parse_*() uses this as parameter separator.
336 * ':' : Because parse_server() uses this to detect if a prefix is a
339 * '+' : Because m_nick() uses this to determine if parv[6] is a
342 * '&', '#', '$', '@' and '%' :
343 * Because m_message() matches these characters to detect special cases.
345 static const char convert2y
[] = {
346 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
347 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
348 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
349 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
352 /** Converts a character to its (base64) numnick value. */
353 static const unsigned int convert2n
[] = {
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
358 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
359 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
360 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
361 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
373 /** Number of bits encoded in one numnick character. */
375 /** Bitmask to select value of next numnick character. */
376 #define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
377 /** Number of servers representable in a numnick. */
381 /** Convert a string to its value as a numnick.
382 * @param[in] s Numnick string to decode.
383 * @return %Numeric nickname value.
385 unsigned int base64toint(const char* s
)
387 unsigned int i
= convert2n
[(unsigned char) *s
++];
390 i
+= convert2n
[(unsigned char) *s
++];
395 /** Encode a number as a numnick.
396 * @param[out] buf Output buffer.
397 * @param[in] v Value to encode.
398 * @param[in] count Number of numnick digits to write to \a buf.
400 const char* inttobase64(char* buf
, unsigned int v
, unsigned int count
)
404 buf
[--count
] = convert2y
[(v
& NUMNICKMASK
)];
410 /** Number of bits encoded in one numnick character. */
413 /** Encode an IP address in the base64 used by numnicks.
414 * For IPv4 addresses (including IPv4-mapped and IPv4-compatible IPv6
415 * addresses), the 32-bit host address is encoded directly as six
418 * For IPv6 addresses, each 16-bit address segment is encoded as three
419 * characters, but the longest run of zero segments is encoded using an
421 * @param[out] buf Output buffer to write to.
422 * @param[in] addr IP address to encode.
423 * @param[in] count Number of bytes writable to \a buf.
424 * @param[in] v6_ok If non-zero, peer understands base-64 encoded IPv6 addresses.
426 const char* iptobase64(char* buf
, const struct irc_in_addr
* addr
, unsigned int count
, int v6_ok
)
428 if (irc_in_addr_is_ipv4(addr
)) {
430 inttobase64(buf
, (ntohs(addr
->in6_16
[6]) << 16) | ntohs(addr
->in6_16
[7]), 6);
433 if (addr
->in6_16
[0] == htons(0x2002))
434 inttobase64(buf
, (ntohs(addr
->in6_16
[1]) << 16) | ntohs(addr
->in6_16
[2]), 6);
436 strcpy(buf
, "AAAAAA");
438 unsigned int max_start
, max_zeros
, curr_zeros
, zero
, ii
;
442 /* Can start by printing out the leading non-zero parts. */
443 for (ii
= 0; (addr
->in6_16
[ii
]) && (ii
< 8); ++ii
) {
444 inttobase64(output
, ntohs(addr
->in6_16
[ii
]), 3);
447 /* Find the longest run of zeros. */
448 for (max_start
= zero
= ii
, max_zeros
= curr_zeros
= 0; ii
< 8; ++ii
) {
449 if (!addr
->in6_16
[ii
])
451 else if (curr_zeros
> max_zeros
) {
452 max_start
= ii
- curr_zeros
;
453 max_zeros
= curr_zeros
;
457 if (curr_zeros
> max_zeros
) {
458 max_start
= ii
- curr_zeros
;
459 max_zeros
= curr_zeros
;
461 /* Print the rest of the address */
462 for (ii
= zero
; ii
< 8; ) {
463 if ((ii
== max_start
) && max_zeros
) {
467 inttobase64(output
, ntohs(addr
->in6_16
[ii
]), 3);
477 /** Decode an IP address from base64.
478 * @param[in] input Input buffer to decode.
479 * @param[out] addr IP address structure to populate.
481 void base64toip(const char* input
, struct irc_in_addr
* addr
)
483 memset(addr
, 0, sizeof(*addr
));
484 if (strlen(input
) == 6) {
485 unsigned int in
= base64toint(input
);
486 /* An all-zero address should stay that way. */
488 addr
->in6_16
[5] = htons(65535);
489 addr
->in6_16
[6] = htons(in
>> 16);
490 addr
->in6_16
[7] = htons(in
& 65535);
493 unsigned int pos
= 0;
497 for (left
= (25 - strlen(input
)) / 3 - pos
; left
; left
--)
498 addr
->in6_16
[pos
++] = 0;
501 unsigned short accum
= convert2n
[(unsigned char)*input
++];
502 accum
= (accum
<< NUMNICKLOG
) | convert2n
[(unsigned char)*input
++];
503 accum
= (accum
<< NUMNICKLOG
) | convert2n
[(unsigned char)*input
++];
504 addr
->in6_16
[pos
++] = ntohs(accum
);
510 /** Test whether an address matches the most significant bits of a mask.
511 * @param[in] addr Address to test.
512 * @param[in] mask Address to test against.
513 * @param[in] bits Number of bits to test.
514 * @return 0 on mismatch, 1 if bits < 128 and all bits match; -1 if
515 * bits == 128 and all bits match.
517 int ipmask_check(const struct irc_in_addr
*addr
, const struct irc_in_addr
*mask
, unsigned char bits
)
521 for (k
= 0; k
< 8; k
++) {
523 return !(htons(addr
->in6_16
[k
] ^ mask
->in6_16
[k
]) >> (16-bits
));
524 if (addr
->in6_16
[k
] != mask
->in6_16
[k
])
532 /** Convert IP addresses to canonical form for comparison. 6to4 and Teredo addresses
533 * are converted to IPv4 addresses. All other addresses are left alone.
534 * @param[out] out Receives canonical format for address.
535 * @param[in] in IP address to canonicalize.
537 void ip_canonicalize_tunnel(struct irc_in_addr
*out
, const struct irc_in_addr
*in
)
539 if (in
->in6_16
[0] == htons(0x2002)) { /* 6to4 */
540 out
->in6_16
[0] = out
->in6_16
[1] = out
->in6_16
[2] = 0;
541 out
->in6_16
[3] = out
->in6_16
[4] = 0;
542 out
->in6_16
[5] = 0xffff;
543 out
->in6_16
[6] = in
->in6_16
[1];
544 out
->in6_16
[7] = in
->in6_16
[2];
545 } else if(in
->in6_16
[0] == htons(0x2001) && in
->in6_16
[1] == 0) { /* Teredo */
546 out
->in6_16
[0] = out
->in6_16
[1] = out
->in6_16
[2] = 0;
547 out
->in6_16
[3] = out
->in6_16
[4] = 0;
548 out
->in6_16
[5] = 0xffff;
549 out
->in6_16
[6] = ~(in
->in6_16
[6]);
550 out
->in6_16
[7] = ~(in
->in6_16
[7]);
552 memcpy(out
, in
, sizeof(*out
));