]> jfr.im git - irc/quakenet/newserv.git/blame - lib/irc_ipv6.c
sstring: Empty input string should not return NULL.
[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>
edd0553f 6#include <arpa/inet.h>
526e7c1d
P
7#include <netdb.h>
8#include "irc_ipv6.h"
830f4525 9#include <stdio.h>
526e7c1d 10
b98ba21f 11#warning This source file is probably GPLed, it needs relicensing.
df3bf970 12
526e7c1d
P
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 */
18const 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 */
29const 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)) {
526e7c1d
P
35 unsigned char *pch;
36
37 pch = (unsigned char*)&in->in6_16[6];
830f4525 38 sprintf(buf,"%d.%d.%d.%d",pch[0],pch[1],pch[2],pch[3]);
526e7c1d
P
39 return buf;
40 } else {
526e7c1d
P
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]);
830f4525 68 pos+=sprintf(buf+pos,"%x",part);
526e7c1d
P
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 */
86static unsigned int
87ircd_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 '.':
9b6ac914
P
109 if (++dots > 3)
110 return 0;
526e7c1d
P
111 if (input[++pos] == '.')
112 return 0;
9b6ac914 113 ip |= part << (32 - 8 * dots);
526e7c1d
P
114 part = 0;
115 if (input[pos] == '*') {
054effe6 116 while (input[++pos] == '*' || input[pos] == '.') ;
526e7c1d
P
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 */
151int
152ipmask_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 '*':
054effe6 234 while (input[++pos] == '*' || input[pos] == ':') ;
526e7c1d
P
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 }
37241385
P
250 if (input[pos] != '\0')
251 return 0;
526e7c1d
P
252 finish:
253 if (colon < 8) {
254 unsigned int jj;
255 /* Shift stuff after "::" up and fill middle with zeros. */
256 for (jj = 0; jj < ii - colon; jj++)
257 ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1];
258 for (jj = 0; jj < 8 - ii; jj++)
259 ip->in6_16[colon + jj] = 0;
260 }
261 return pos;
262 } else if (dot || strchr(input, '/')) {
263 unsigned int addr;
264 int len = ircd_aton_ip4(input, &addr, pbits);
265 if (len) {
266 ip->in6_16[5] = htons(65535);
267 ip->in6_16[6] = htons(ntohl(addr) >> 16);
268 ip->in6_16[7] = htons(ntohl(addr) & 65535);
269 if (pbits)
270 *pbits += 96;
271 }
272 return len;
273 } else if (input[0] == '*') {
274 unsigned int pos = 0;
275 while (input[++pos] == '*') ;
276 if (input[pos] != '\0')
277 return 0;
278 if (pbits)
279 *pbits = 0;
280 return pos;
281 } else return 0; /* parse failed */
282}
283
284/* from numnicks.c */
285
286/**
287 * Converts a numeric to the corresponding character.
288 * The following characters are currently known to be forbidden:
289 *
290 * '\\0' : Because we use '\\0' as end of line.
291 *
292 * ' ' : Because parse_*() uses this as parameter separator.
293 *
294 * ':' : Because parse_server() uses this to detect if a prefix is a
295 * numeric or a name.
296 *
297 * '+' : Because m_nick() uses this to determine if parv[6] is a
298 * umode or not.
299 *
300 * '&', '#', '$', '@' and '%' :
301 * Because m_message() matches these characters to detect special cases.
302 */
303static const char convert2y[] = {
304 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
305 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
306 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
307 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
308};
309
310/** Converts a character to its (base64) numnick value. */
311static const unsigned int convert2n[] = {
312 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
314 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
315 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
316 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
317 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
318 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
319 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
320
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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
328 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
329};
330
331/** Number of bits encoded in one numnick character. */
332#define NUMNICKLOG 6
333/** Bitmask to select value of next numnick character. */
334#define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
335/** Number of servers representable in a numnick. */
336
337/* *INDENT-ON* */
338
339/** Convert a string to its value as a numnick.
340 * @param[in] s Numnick string to decode.
341 * @return %Numeric nickname value.
342 */
343unsigned int base64toint(const char* s)
344{
345 unsigned int i = convert2n[(unsigned char) *s++];
346 while (*s) {
347 i <<= NUMNICKLOG;
348 i += convert2n[(unsigned char) *s++];
349 }
350 return i;
351}
352
353/** Encode a number as a numnick.
354 * @param[out] buf Output buffer.
355 * @param[in] v Value to encode.
356 * @param[in] count Number of numnick digits to write to \a buf.
357 */
358const char* inttobase64(char* buf, unsigned int v, unsigned int count)
359{
360 buf[count] = '\0';
361 while (count > 0) {
362 buf[--count] = convert2y[(v & NUMNICKMASK)];
363 v >>= NUMNICKLOG;
364 }
365 return buf;
366}
367
368/** Number of bits encoded in one numnick character. */
369#define NUMNICKLOG 6
370
371/** Encode an IP address in the base64 used by numnicks.
372 * For IPv4 addresses (including IPv4-mapped and IPv4-compatible IPv6
373 * addresses), the 32-bit host address is encoded directly as six
374 * characters.
375 *
376 * For IPv6 addresses, each 16-bit address segment is encoded as three
377 * characters, but the longest run of zero segments is encoded using an
378 * underscore.
379 * @param[out] buf Output buffer to write to.
380 * @param[in] addr IP address to encode.
381 * @param[in] count Number of bytes writable to \a buf.
382 * @param[in] v6_ok If non-zero, peer understands base-64 encoded IPv6 addresses.
383 */
384const char* iptobase64(char* buf, const struct irc_in_addr* addr, unsigned int count, int v6_ok)
385{
386 if (irc_in_addr_is_ipv4(addr)) {
387 assert(count >= 6);
388 inttobase64(buf, (ntohs(addr->in6_16[6]) << 16) | ntohs(addr->in6_16[7]), 6);
389 } else if (!v6_ok) {
390 assert(count >= 6);
391 if (addr->in6_16[0] == htons(0x2002))
392 inttobase64(buf, (ntohs(addr->in6_16[1]) << 16) | ntohs(addr->in6_16[2]), 6);
393 else
394 strcpy(buf, "AAAAAA");
395 } else {
396 unsigned int max_start, max_zeros, curr_zeros, zero, ii;
397 char *output = buf;
398
399 assert(count >= 25);
400 /* Can start by printing out the leading non-zero parts. */
401 for (ii = 0; (addr->in6_16[ii]) && (ii < 8); ++ii) {
402 inttobase64(output, ntohs(addr->in6_16[ii]), 3);
403 output += 3;
404 }
405 /* Find the longest run of zeros. */
406 for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
407 if (!addr->in6_16[ii])
408 curr_zeros++;
409 else if (curr_zeros > max_zeros) {
410 max_start = ii - curr_zeros;
411 max_zeros = curr_zeros;
412 curr_zeros = 0;
413 }
414 }
415 if (curr_zeros > max_zeros) {
416 max_start = ii - curr_zeros;
417 max_zeros = curr_zeros;
526e7c1d
P
418 }
419 /* Print the rest of the address */
420 for (ii = zero; ii < 8; ) {
421 if ((ii == max_start) && max_zeros) {
422 *output++ = '_';
423 ii += max_zeros;
424 } else {
425 inttobase64(output, ntohs(addr->in6_16[ii]), 3);
426 output += 3;
427 ii++;
428 }
429 }
430 *output = '\0';
431 }
432 return buf;
433}
434
435/** Decode an IP address from base64.
436 * @param[in] input Input buffer to decode.
437 * @param[out] addr IP address structure to populate.
438 */
439void base64toip(const char* input, struct irc_in_addr* addr)
440{
441 memset(addr, 0, sizeof(*addr));
442 if (strlen(input) == 6) {
443 unsigned int in = base64toint(input);
444 /* An all-zero address should stay that way. */
445 if (in) {
446 addr->in6_16[5] = htons(65535);
447 addr->in6_16[6] = htons(in >> 16);
448 addr->in6_16[7] = htons(in & 65535);
449 }
450 } else {
451 unsigned int pos = 0;
452 do {
453 if (*input == '_') {
454 unsigned int left;
455 for (left = (25 - strlen(input)) / 3 - pos; left; left--)
456 addr->in6_16[pos++] = 0;
457 input++;
458 } else {
459 unsigned short accum = convert2n[(unsigned char)*input++];
460 accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
461 accum = (accum << NUMNICKLOG) | convert2n[(unsigned char)*input++];
462 addr->in6_16[pos++] = ntohs(accum);
463 }
464 } while (pos < 8);
465 }
466}
467
873f4217 468/** Test whether an address matches the most significant bits of a mask.
469 * @param[in] addr Address to test.
470 * @param[in] mask Address to test against.
471 * @param[in] bits Number of bits to test.
472 * @return 0 on mismatch, 1 if bits < 128 and all bits match; -1 if
473 * bits == 128 and all bits match.
474 */
475int ipmask_check(const struct irc_in_addr *addr, const struct irc_in_addr *mask, unsigned char bits)
476{
477 int k;
478
479 for (k = 0; k < 8; k++) {
480 if (bits < 16)
481 return !(htons(addr->in6_16[k] ^ mask->in6_16[k]) >> (16-bits));
482 if (addr->in6_16[k] != mask->in6_16[k])
483 return 0;
484 if (!(bits -= 16))
485 return 1;
486 }
487 return -1;
488}