]>
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 /** 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.
87 ircd_aton_ip4(const char *input
, unsigned int *output
, unsigned char *pbits
)
89 unsigned int dots
= 0, pos
= 0, part
= 0, ip
= 0, bits
;
91 /* Intentionally no support for bizarre IPv4 formats (plain
92 * integers, octal or hex components) -- only vanilla dotted
98 while (1) switch (input
[pos
]) {
103 ip
|= part
<< (24 - 8 * dots
);
111 if (input
[++pos
] == '.')
113 ip
|= part
<< (32 - 8 * dots
);
115 if (input
[pos
] == '*') {
116 while (input
[++pos
] == '*' || input
[pos
] == '.') ;
117 if (input
[pos
] != '\0')
126 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
128 for (bits
= 0; IsDigit(input
[++pos
]); )
129 bits
= bits
* 10 + input
[pos
] - '0';
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';
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.
152 ipmask_parse(const char *input
, struct irc_in_addr
*ip
, unsigned char *pbits
)
159 memset(ip
, 0, sizeof(*ip
));
160 colon
= strchr(input
, ':');
161 dot
= strchr(input
, '.');
163 if (colon
&& (!dot
|| (dot
> colon
))) {
164 unsigned int part
= 0, pos
= 0, ii
= 0, colon
= 8;
165 const char *part_start
= NULL
;
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.
173 if (input
[pos
] == ':') {
174 if ((input
[pos
+1] != ':') || (input
[pos
+2] == ':'))
178 part_start
= input
+ pos
;
180 while (ii
< 8) switch (input
[pos
]) {
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';
186 part
= (part
<< 4) | chval
;
191 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
192 chval
= input
[pos
] - 'A' + 10;
194 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
195 chval
= input
[pos
] - 'a' + 10;
198 part_start
= input
+ ++pos
;
199 if (input
[pos
] == '.')
201 ip
->in6_16
[ii
++] = htons(part
);
203 if (input
[pos
] == ':') {
213 len
= ircd_aton_ip4(part_start
, &ip4
, pbits
);
214 if (!len
|| (ii
> 6))
216 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) >> 16);
217 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) & 65535);
220 pos
= part_start
+ len
- input
;
224 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
226 ip
->in6_16
[ii
++] = htons(part
);
227 for (part
= 0; IsDigit(input
[++pos
]); )
228 part
= part
* 10 + input
[pos
] - '0';
234 while (input
[++pos
] == '*' || input
[pos
] == ':') ;
235 if (input
[pos
] != '\0' || colon
< 8)
241 ip
->in6_16
[ii
++] = htons(part
);
242 if (colon
== 8 && ii
< 8)
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;
260 } else if (dot
|| strchr(input
, '/')) {
262 int len
= ircd_aton_ip4(input
, &addr
, pbits
);
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);
271 } else if (input
[0] == '*') {
272 unsigned int pos
= 0;
273 while (input
[++pos
] == '*') ;
274 if (input
[pos
] != '\0')
279 } else return 0; /* parse failed */
282 /* from numnicks.c */
285 * Converts a numeric to the corresponding character.
286 * The following characters are currently known to be forbidden:
288 * '\\0' : Because we use '\\0' as end of line.
290 * ' ' : Because parse_*() uses this as parameter separator.
292 * ':' : Because parse_server() uses this to detect if a prefix is a
295 * '+' : Because m_nick() uses this to determine if parv[6] is a
298 * '&', '#', '$', '@' and '%' :
299 * Because m_message() matches these characters to detect special cases.
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','[',']'
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,
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
329 /** Number of bits encoded in one numnick character. */
331 /** Bitmask to select value of next numnick character. */
332 #define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
333 /** Number of servers representable in a numnick. */
337 /** Convert a string to its value as a numnick.
338 * @param[in] s Numnick string to decode.
339 * @return %Numeric nickname value.
341 unsigned int base64toint(const char* s
)
343 unsigned int i
= convert2n
[(unsigned char) *s
++];
346 i
+= convert2n
[(unsigned char) *s
++];
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.
356 const char* inttobase64(char* buf
, unsigned int v
, unsigned int count
)
360 buf
[--count
] = convert2y
[(v
& NUMNICKMASK
)];
366 /** Number of bits encoded in one numnick character. */
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
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
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.
382 const char* iptobase64(char* buf
, const struct irc_in_addr
* addr
, unsigned int count
, int v6_ok
)
384 if (irc_in_addr_is_ipv4(addr
)) {
386 inttobase64(buf
, (ntohs(addr
->in6_16
[6]) << 16) | ntohs(addr
->in6_16
[7]), 6);
389 if (addr
->in6_16
[0] == htons(0x2002))
390 inttobase64(buf
, (ntohs(addr
->in6_16
[1]) << 16) | ntohs(addr
->in6_16
[2]), 6);
392 strcpy(buf
, "AAAAAA");
394 unsigned int max_start
, max_zeros
, curr_zeros
, zero
, ii
;
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);
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
])
407 else if (curr_zeros
> max_zeros
) {
408 max_start
= ii
- curr_zeros
;
409 max_zeros
= curr_zeros
;
413 if (curr_zeros
> max_zeros
) {
414 max_start
= ii
- curr_zeros
;
415 max_zeros
= curr_zeros
;
418 /* Print the rest of the address */
419 for (ii
= zero
; ii
< 8; ) {
420 if ((ii
== max_start
) && max_zeros
) {
424 inttobase64(output
, ntohs(addr
->in6_16
[ii
]), 3);
434 /** Decode an IP address from base64.
435 * @param[in] input Input buffer to decode.
436 * @param[out] addr IP address structure to populate.
438 void base64toip(const char* input
, struct irc_in_addr
* addr
)
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. */
445 addr
->in6_16
[5] = htons(65535);
446 addr
->in6_16
[6] = htons(in
>> 16);
447 addr
->in6_16
[7] = htons(in
& 65535);
450 unsigned int pos
= 0;
454 for (left
= (25 - strlen(input
)) / 3 - pos
; left
; left
--)
455 addr
->in6_16
[pos
++] = 0;
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
);