addrfam: mark unknown_af as not returning
[adns] / src / addrfam.c
CommitLineData
07554ccd
MW
1/*
2 * addrfam.c
3 * - address-family specific code
4 */
5/*
6 * This file is part of adns, which is
7 * Copyright (C) 1997-2000,2003,2006 Ian Jackson
8 * Copyright (C) 1999-2000,2003,2006 Tony Finch
9 * Copyright (C) 1991 Massachusetts Institute of Technology
10 * (See the file INSTALL for full details.)
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27#include <stdlib.h>
28#include <errno.h>
29#include <limits.h>
30#include <unistd.h>
5806b745
IJ
31#include <inttypes.h>
32#include <stddef.h>
33#include <stdbool.h>
07554ccd
MW
34
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <netinet/in.h>
38#include <arpa/inet.h>
5806b745 39#include <net/if.h>
07554ccd
MW
40
41#include "internal.h"
42
43/*
44 * General address-family operations.
45 */
46
47#define SIN(sa) ((struct sockaddr_in *)(sa))
48#define CSIN(sa) ((const struct sockaddr_in *)(sa))
49
50#define SIN6(sa) ((struct sockaddr_in6 *)(sa))
51#define CSIN6(sa) ((const struct sockaddr_in6 *)(sa))
52
53/* This gadget (thanks, Richard Kettlewell) makes sure that we handle the
54 * same set of address families in each switch. */
55#define AF_CASES(pre) \
56 case AF_INET: goto pre##_inet; \
57 case AF_INET6: goto pre##_inet6
58
6380363d 59static void unknown_af(int af) NONRETURNING;
07554ccd
MW
60static void unknown_af(int af) {
61 fprintf(stderr, "ADNS INTERNAL: unknown address family %d\n", af);
62 abort();
63}
64
65#define IN6_ADDR_EQUALP(a, b) \
66 (!memcmp((a).s6_addr, (b).s6_addr, sizeof((a).s6_addr)))
67
68int adns__genaddr_equal_p(int af, const union gen_addr *a,
69 int bf, const void *b) {
70 const union gen_addr *bb= b;
71 if (af != bf) return 0;
72 switch (af) {
73 AF_CASES(af);
74 af_inet: return a->v4.s_addr == bb->v4.s_addr;
75 af_inet6: return IN6_ADDR_EQUALP(a->v6, bb->v6);
76 default: unknown_af(af); return -1;
77 }
78}
79
80int adns__sockaddr_equal_p(const struct sockaddr *sa,
81 const struct sockaddr *sb) {
82 if (sa->sa_family != sb->sa_family) return 0;
83 switch (sa->sa_family) {
84 AF_CASES(af);
85 af_inet: {
86 const struct sockaddr_in *sina= CSIN(sa), *sinb= CSIN(sb);
87 return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr &&
88 sina->sin_port == sinb->sin_port);
89 }
90 af_inet6: {
91 /* Don't check the flowlabel. That's apparently useful for routing
92 * performance, but doesn't affect the address in any important
93 * respect. */
94 const struct sockaddr_in6 *sin6a= CSIN6(sa), *sin6b= CSIN6(sb);
95 return (IN6_ADDR_EQUALP(sin6a->sin6_addr, sin6b->sin6_addr) &&
96 sin6a->sin6_port == sin6b->sin6_port &&
97 sin6a->sin6_scope_id == sin6b->sin6_scope_id);
98 }
99 default:
100 unknown_af(sa->sa_family);
101 return -1;
102 }
103}
104
105int adns__addr_width(int af) {
106 switch (af) {
107 AF_CASES(af);
108 af_inet: return 32;
109 af_inet6: return 128;
110 default: unknown_af(af); return -1;
111 }
112}
113
114void adns__prefix_mask(int af, int len, union gen_addr *mask_r) {
115 switch (af) {
116 AF_CASES(af);
117 af_inet:
118 assert(len <= 32);
119 mask_r->v4.s_addr= htonl(!len ? 0 : 0xffffffff << (32-len));
120 break;
121 af_inet6: {
122 int i= len/8, j= len%8;
123 unsigned char *m= mask_r->v6.s6_addr;
124 assert(len <= 128);
125 memset(m, 0xff, i);
126 if (j) m[i++]= (0xff << (8-j)) & 0xff;
127 memset(m+i, 0, 16-i);
128 } break;
129 default:
130 unknown_af(af);
131 break;
132 }
133}
134
135int adns__guess_prefix_length(int af, const union gen_addr *addr) {
136 switch (af) {
137 AF_CASES(af);
138 af_inet: {
139 unsigned a= (ntohl(addr->v4.s_addr) >> 24) & 0xff;
140 if (a < 128) return 8;
141 else if (a < 192) return 16;
142 else if (a < 224) return 24;
143 else return -1;
144 } break;
145 af_inet6:
146 return 64;
147 default:
148 unknown_af(af);
149 return -1;
150 }
151}
152
153int adns__addr_match_p(int addraf, const union gen_addr *addr,
154 int netaf, const union gen_addr *base,
155 const union gen_addr *mask)
156{
157 if (addraf != netaf) return 0;
158 switch (addraf) {
159 AF_CASES(af);
160 af_inet:
161 return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr;
162 af_inet6: {
163 int i;
164 const char *a= addr->v6.s6_addr;
165 const char *b= base->v6.s6_addr;
166 const char *m= mask->v6.s6_addr;
167 for (i = 0; i < 16; i++)
168 if ((a[i] & m[i]) != b[i]) return 0;
169 return 1;
170 } break;
171 default:
172 unknown_af(addraf);
173 return -1;
174 }
175}
176
177void adns__sockaddr_extract(const struct sockaddr *sa,
178 union gen_addr *a_r, int *port_r) {
179 switch (sa->sa_family) {
180 AF_CASES(af);
181 af_inet: {
182 const struct sockaddr_in *sin = CSIN(sa);
183 if (port_r) *port_r= ntohs(sin->sin_port);
184 if (a_r) a_r->v4= sin->sin_addr;
185 break;
186 }
187 af_inet6: {
188 const struct sockaddr_in6 *sin6 = CSIN6(sa);
189 if (port_r) *port_r= ntohs(sin6->sin6_port);
190 if (a_r) a_r->v6= sin6->sin6_addr;
191 break;
192 }
193 default:
194 unknown_af(sa->sa_family);
195 }
196}
197
198void adns__sockaddr_inject(const union gen_addr *a, int port,
199 struct sockaddr *sa) {
200 switch (sa->sa_family) {
201 AF_CASES(af);
202 af_inet: {
203 struct sockaddr_in *sin = SIN(sa);
204 if (port != -1) sin->sin_port= htons(port);
205 if (a) sin->sin_addr= a->v4;
206 break;
207 }
208 af_inet6: {
209 struct sockaddr_in6 *sin6 = SIN6(sa);
210 if (port != -1) sin6->sin6_port= htons(port);
211 if (a) sin6->sin6_addr= a->v6;
212 break;
213 }
214 default:
215 unknown_af(sa->sa_family);
216 }
217}
5806b745
IJ
218
219/*
220 * addr2text and text2addr
221 */
222
223#define ADDRFAM_DEBUG
224#ifdef ADDRFAM_DEBUG
225static void af_debug_func(const char *fmt, ...) {
226 int esave= errno;
227 va_list al;
228 va_start(al,fmt);
229 vfprintf(stderr,fmt,al);
230 va_end(al);
231 errno= esave;
232}
233# define af_debug(fmt,...) \
234 (af_debug_func("%s: " fmt "\n", __func__, __VA_ARGS__))
235#else
236# define af_debug(fmt,...) ((void)("" fmt "", __VA_ARGS__))
237#endif
238
239static bool addrtext_our_errno(int e) {
240 return
241 e==EAFNOSUPPORT ||
242 e==EINVAL ||
243 e==ENOSPC ||
244 e==ENOSYS;
245}
246
247static bool addrtext_scope_use_ifname(const struct sockaddr *sa) {
248 const struct in6_addr *in6= &CSIN6(sa)->sin6_addr;
249 return
250 IN6_IS_ADDR_LINKLOCAL(in6) ||
251 IN6_IS_ADDR_MC_LINKLOCAL(in6);
252}
253
254int adns_text2addr(const char *text, uint16_t port, adns_queryflags flags,
255 struct sockaddr *sa, socklen_t *salen_io) {
256 int af;
257 char copybuf[INET6_ADDRSTRLEN];
258 const char *parse=text;
259 const char *scopestr=0;
260 socklen_t needlen;
261 void *dst;
262 uint16_t *portp;
263
264#define INVAL(how) do{ \
265 af_debug("invalid: %s: `%s'", how, text); \
266 return EINVAL; \
267}while(0)
268
269#define AFCORE(INETx,SINx,sinx) \
270 af= AF_##INETx; \
271 dst = &SINx(sa)->sinx##_addr; \
272 portp = &SINx(sa)->sinx##_port; \
273 needlen= sizeof(*SINx(sa));
274
275 if (!strchr(text, ':')) { /* INET */
276
277 AFCORE(INET,SIN,sin);
278
279 } else { /* INET6 */
280
281 AFCORE(INET6,SIN6,sin6);
282
283 const char *percent= strchr(text, '%');
284 if (percent) {
285 ptrdiff_t lhslen = percent - text;
286 if (lhslen >= INET6_ADDRSTRLEN) INVAL("scoped addr lhs too long");
287 memcpy(copybuf, text, lhslen);
288 copybuf[lhslen]= 0;
289
290 parse= copybuf;
291 scopestr= percent+1;
292
293 af_debug("will parse scoped addr `%s' %% `%s'", parse, scopestr);
294 }
295
296 }
297
298#undef AFCORE
299
300 if (scopestr && (flags & adns_qf_addrlit_scope_forbid))
301 INVAL("scoped addr but _scope_forbid");
302
303 if (*salen_io < needlen) {
304 *salen_io = needlen;
305 return ENOSPC;
306 }
307
308 memset(sa, 0, needlen);
309
310 sa->sa_family= af;
311 *portp = htons(port);
312
313 if (af == AF_INET && !(flags & adns_qf_addrlit_ipv4_quadonly)) {
314 /* we have to use inet_aton to deal with non-dotted-quad literals */
315 int r= inet_aton(parse,&SIN(sa)->sin_addr);
316 if (!r) INVAL("inet_aton rejected");
317 } else {
318 int r= inet_pton(af,parse,dst);
319 if (!r) INVAL("inet_pton rejected");
320 assert(r>0);
321 }
322
323 if (scopestr) {
324 errno=0;
325 char *ep;
326 unsigned long scope= strtoul(scopestr,&ep,10);
327 if (errno==ERANGE) INVAL("numeric scope id too large for unsigned long");
328 assert(!errno);
329 if (!*ep) {
330 if (scope > ~(uint32_t)0)
331 INVAL("numeric scope id too large for uint32_t");
332 } else { /* !!*ep */
333 if (flags & adns_qf_addrlit_scope_numeric)
334 INVAL("non-numeric scope but _scope_numeric");
335 if (!addrtext_scope_use_ifname(sa)) {
336 af_debug("cannot convert non-numeric scope"
337 " in non-link-local addr `%s'", text);
338 return ENOSYS;
339 }
340 errno= 0;
341 scope= if_nametoindex(scopestr);
342 if (!scope) {
343 /* RFC3493 says "No errors are defined". It's not clear
344 * whether that is supposed to mean if_nametoindex "can't
345 * fail" (other than by the supplied name not being that of an
346 * interface) which seems unrealistic, or that it conflates
347 * all its errors together by failing to set errno, or simply
348 * that they didn't bother to document the errors.
349 *
350 * glibc, FreeBSD and OpenBSD all set errno (to ENXIO when
351 * appropriate). See Debian bug #749349.
352 *
353 * We attempt to deal with this by clearing errno to start
354 * with, and then perhaps mapping the results. */
355 af_debug("if_nametoindex rejected scope name (errno=%s)",
356 strerror(errno));
357 if (errno==0) {
358 return ENXIO;
359 } else if (addrtext_our_errno(errno)) {
360 /* we use these for other purposes, urgh. */
361 perror("adns: adns_text2addr: if_nametoindex"
362 " failed with unexpected error");
363 return EIO;
364 } else {
365 return errno;
366 }
367 } else { /* ix>0 */
368 if (scope > ~(uint32_t)0) {
369 fprintf(stderr,"adns: adns_text2addr: if_nametoindex"
370 " returned an interface index >=2^32 which will not fit"
371 " in sockaddr_in6.sin6_scope_id");
372 return EIO;
373 }
374 }
375 } /* else; !!*ep */
376
377 SIN6(sa)->sin6_scope_id= scope;
378 } /* if (scopestr) */
379
380 *salen_io = needlen;
381 return 0;
382}
383
384int adns_addr2text(const struct sockaddr *sa, adns_queryflags flags,
385 char *buffer, int *buflen_io, int *port_r) {
386 const void *src;
387 int port;
388
389 if (*buflen_io < ADNS_ADDR2TEXT_BUFLEN) {
390 *buflen_io = ADNS_ADDR2TEXT_BUFLEN;
391 return ENOSPC;
392 }
393
394 switch (sa->sa_family) {
395 AF_CASES(af);
396 af_inet: src= &CSIN(sa)->sin_addr; port= CSIN(sa)->sin_port; break;
397 af_inet6: src= &CSIN6(sa)->sin6_addr; port= CSIN6(sa)->sin6_port; break;
398 default: return EAFNOSUPPORT;
399 }
400
401 const char *ok= inet_ntop(sa->sa_family, src, buffer, *buflen_io);
402 assert(ok);
403
404 if (sa->sa_family == AF_INET6) {
405 uint32_t scope = CSIN6(sa)->sin6_scope_id;
406 if (scope) {
407 if (flags & adns_qf_addrlit_scope_forbid)
408 return EINVAL;
409 int scopeoffset = strlen(buffer);
410 int remain = *buflen_io - scopeoffset;
411 char *scopeptr = buffer + scopeoffset;
412 assert(remain >= IF_NAMESIZE+1/*%*/);
413 *scopeptr++= '%'; remain--;
414 bool parsedname = 0;
c3a165f5
MW
415 af_debug("will print scoped addr `%.*s' %% %"PRIu32"",
416 scopeoffset,buffer, scope);
5806b745
IJ
417 if (scope <= UINT_MAX /* so we can pass it to if_indextoname */
418 && !(flags & adns_qf_addrlit_scope_numeric)
419 && addrtext_scope_use_ifname(sa)) {
420 parsedname = if_indextoname(scope, scopeptr);
421 if (!parsedname) {
422 af_debug("if_indextoname rejected scope (errno=%s)",
423 strerror(errno));
424 if (errno==ENXIO) {
425 /* fair enough, show it as a number then */
426 } else if (addrtext_our_errno(errno)) {
427 /* we use these for other purposes, urgh. */
428 perror("adns: adns_addr2text: if_indextoname"
429 " failed with unexpected error");
430 return EIO;
431 } else {
432 return errno;
433 }
434 }
435 }
436 if (!parsedname) {
437 int r = snprintf(scopeptr, remain,
438 "%"PRIu32"", scope);
439 assert(r < *buflen_io - scopeoffset);
440 }
441 af_debug("printed scoped addr `%s'", buffer);
442 }
443 }
444
445 if (port_r) *port_r= ntohs(port);
446 return 0;
447}
b0e8338a
MW
448
449char *adns__sockaddr_ntoa(const struct sockaddr *sa, char *buf) {
450 int err;
451 int len= ADNS_ADDR2TEXT_BUFLEN;
452
453 err= adns_addr2text(sa, 0, buf, &len, 0);
454 if (err == EIO)
455 err= adns_addr2text(sa, adns_qf_addrlit_scope_numeric, buf, &len, 0);
456 assert(!err);
457 return buf;
458}
51cb3de3
MW
459
460/*
461 * Reverse-domain parsing and construction.
462 */
463
464int adns__make_reverse_domain(const struct sockaddr *sa, const char *zone,
465 char **buf_io, size_t bufsz,
466 char **buf_free_r) {
467 size_t req;
468 char *p;
469 unsigned c, y;
470 unsigned long aa;
471 const unsigned char *ap;
472 int i, j;
473
474 switch (sa->sa_family) {
475 AF_CASES(af);
476 af_inet:
477 req= 4 * 4;
478 if (!zone) zone= "in-addr.arpa";
479 break;
480 af_inet6:
481 req = 2 * 32;
482 if (!zone) zone= "ip6.arpa";
483 break;
484 default:
485 return ENOSYS;
486 }
487
488 req += strlen(zone) + 1;
489 if (req <= bufsz)
490 p= *buf_io;
491 else {
492 p= malloc(req); if (!p) return errno;
493 *buf_free_r = p;
494 }
495
496 *buf_io= p;
497 switch (sa->sa_family) {
498 AF_CASES(bf);
499 bf_inet:
500 aa= ntohl(CSIN(sa)->sin_addr.s_addr);
501 for (i=0; i<4; i++) {
502 p += sprintf(p, "%d", (int)(aa & 0xff));
503 *p++= '.';
504 aa >>= 8;
505 }
506 break;
507 bf_inet6:
508 ap= CSIN6(sa)->sin6_addr.s6_addr + 16;
509 for (i=0; i<16; i++) {
510 c= *--ap;
511 for (j=0; j<2; j++) {
512 y= c & 0xf;
513 *p++= (y < 10) ? y + '0' : y - 10 + 'a';
514 c >>= 4;
515 *p++= '.';
516 }
517 }
518 break;
519 default:
520 unknown_af(sa->sa_family);
521 }
522
523 strcpy(p, zone);
524 return 0;
525}
526
527
528static int inet_rev_parsecomp(const char *p, size_t n) {
529 int i= 0;
530 if (n > 3) return -1;
531
532 while (n--) {
533 if ('0' <= *p && *p <= '9') i= 10*i + *p++ - '0';
534 else return -1;
535 }
536 return i;
537}
538
539static void inet_rev_mkaddr(union gen_addr *addr, const byte *ipv) {
540 addr->v4.s_addr= htonl((ipv[3]<<24) | (ipv[2]<<16) |
541 (ipv[1]<<8) | (ipv[0]));
542}
543
544static int inet6_rev_parsecomp(const char *p, size_t n) {
545 if (n != 1) return -1;
546 else if ('0' <= *p && *p <= '9') return *p - '0';
547 else if ('a' <= *p && *p <= 'f') return *p - 'a' + 10;
548 else if ('A' <= *p && *p <= 'F') return *p - 'a' + 10;
549 else return -1;
550}
551
552static void inet6_rev_mkaddr(union gen_addr *addr, const byte *ipv) {
553 unsigned char *a= addr->v6.s6_addr;
554 int i;
555
556 for (i=0; i<16; i++)
557 a[i]= (ipv[31-2*i] << 4) | (ipv[30-2*i] << 0);
558}
559
560static const struct revparse_domain {
561 int af; /* address family */
562 int nrevlab; /* n of reverse-address labels */
563 adns_rrtype rrtype; /* forward-lookup type */
564
565 int (*rev_parsecomp)(const char *p, size_t n);
566 /* parse a single component from a label; return the integer value, or -1
567 * if it was unintelligible.
568 */
569
570 void (*rev_mkaddr)(union gen_addr *addr, const byte *ipv);
571 /* write out the parsed address from a vector of parsed components */
572
573 const char *const tail[3]; /* tail label names */
574} revparse_domains[NREVDOMAINS] = {
575 { AF_INET, 4, adns_r_a, inet_rev_parsecomp, inet_rev_mkaddr,
576 { DNS_INADDR_ARPA, 0 } },
577 { AF_INET6, 32, adns_r_aaaa, inet6_rev_parsecomp, inet6_rev_mkaddr,
578 { DNS_IP6_ARPA, 0 } },
579};
580
581#define REVDOMAIN_MAP(rps, labnum) \
582 ((labnum) ? (rps)->map : (1 << NREVDOMAINS) - 1)
583
584int adns__revparse_label(struct revparse_state *rps, int labnum,
585 const char *label, int lablen) {
586 unsigned f= REVDOMAIN_MAP(rps, labnum);
587 const struct revparse_domain *rpd;
588 const char *tp;
589 unsigned d;
590 int i, ac;
591
592 for (rpd=revparse_domains, i=0, d=1; i<NREVDOMAINS; rpd++, i++, d <<= 1) {
593 if (!(f & d)) continue;
594 if (labnum >= rpd->nrevlab) {
595 tp = rpd->tail[labnum - rpd->nrevlab];
596 if (!tp || strncmp(label, tp, lablen) != 0 || tp[lablen])
597 goto mismatch;
598 } else {
599 ac= rpd->rev_parsecomp(label, lablen);
600 if (ac < 0) goto mismatch;
601 assert(labnum < sizeof(rps->ipv[i]));
602 rps->ipv[i][labnum]= ac;
603 }
604 continue;
605
606 mismatch:
607 f &= ~d;
608 if (!f) return -1;
609 }
610
611 rps->map= f;
612 return 0;
613}
614
615int adns__revparse_done(struct revparse_state *rps, int nlabels,
616 adns_rrtype *rrtype_r, struct af_addr *addr_r) {
617 unsigned f= REVDOMAIN_MAP(rps, nlabels);
618 const struct revparse_domain *rpd;
619 unsigned d;
620 int i, found= -1;
621
622 for (rpd=revparse_domains, i=0, d=1; i<NREVDOMAINS; rpd++, i++, d <<= 1) {
623 if (!(f & d)) continue;
624 if (nlabels >= rpd->nrevlab && !rpd->tail[nlabels - rpd->nrevlab])
625 { found = i; continue; }
626 f &= ~d;
627 if (!f) return -1;
628 }
629 assert(found >= 0); assert(f == (1 << found));
630
631 rpd= &revparse_domains[found];
632 *rrtype_r= rpd->rrtype;
633 addr_r->af= rpd->af;
634 rpd->rev_mkaddr(&addr_r->addr, rps->ipv[found]);
635 return 0;
636}