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