chiark / gitweb /
Do address-family dispatch with lots of switchy functions.
[adns] / src / addrfam.c
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>
31
32 #include <sys/types.h>
33 #include <netdb.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37
38 #include "internal.h"
39
40 /*
41  * General address-family operations.
42  */
43
44 #define SIN(sa) ((struct sockaddr_in *)(sa))
45 #define CSIN(sa) ((const struct sockaddr_in *)(sa))
46
47 #define SIN6(sa) ((struct sockaddr_in6 *)(sa))
48 #define CSIN6(sa) ((const struct sockaddr_in6 *)(sa))
49
50 /* This gadget (thanks, Richard Kettlewell) makes sure that we handle the
51  * same set of address families in each switch. */
52 #define AF_CASES(pre)                                                   \
53   case AF_INET: goto pre##_inet;                                        \
54   case AF_INET6: goto pre##_inet6
55
56 static void unknown_af(int af) {
57   fprintf(stderr, "ADNS INTERNAL: unknown address family %d\n", af);
58   abort();
59 }
60
61 int adns__af_supported_p(int af)
62 {
63   switch (af) {
64     AF_CASES(af);
65     af_inet: af_inet6: return 1;
66     default: return 0;
67   }
68 }
69
70 int adns__sockaddr_equal_p(const struct sockaddr *sa,
71                            const struct sockaddr *sb)
72 {
73   if (sa->sa_family != sb->sa_family) return 0;
74   switch (sa->sa_family) {
75     AF_CASES(af);
76     af_inet: {
77       const struct sockaddr_in *sina = CSIN(sa), *sinb = CSIN(sb);
78       return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr &&
79               sina->sin_port == sinb->sin_port);
80     }
81     af_inet6: {
82       /* Don't check the flowlabel.  That's apparently useful for routing
83        * performance, but doesn't affect the address in any important
84        * respect.
85        */
86       const struct sockaddr_in6 *sin6a = CSIN6(sa), *sin6b = CSIN6(sb);
87       return (memcmp(sin6a->sin6_addr.s6_addr,
88                      sin6b->sin6_addr.s6_addr,
89                      sizeof(sin6a->sin6_addr.s6_addr)) == 0 &&
90               sin6a->sin6_port == sin6b->sin6_port &&
91               sin6a->sin6_scope_id == sin6b->sin6_scope_id);
92     }
93     default:
94       unknown_af(sa->sa_family);
95       return -1;
96   }
97 }
98
99 int adns__gen_pton(const char *p, int *af_r, union gen_addr *addr_r)
100 {
101   static const int aflist[] = { AF_INET6, AF_INET };
102   int i, rc;
103
104   for (i = 0; i < sizeof(aflist)/sizeof(*aflist); i++) {
105     rc = inet_pton(aflist[i], p, addr_r);
106     assert(rc >= 0);
107     if (rc) { *af_r = aflist[i]; return 1; }
108   }
109   return 0;
110 }
111
112 int adns__addr_width(int af)
113 {
114   switch (af) {
115     AF_CASES(af);
116     af_inet: return 32;
117     af_inet6: return 128;
118     default: unknown_af(af); return -1;
119   }
120 }
121
122 void adns__prefix_mask(int af, int len, union gen_addr *mask_r)
123 {
124   switch (af) {
125     AF_CASES(af);
126     af_inet:
127       assert(len <= 32);
128       mask_r->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len));
129       break;
130     af_inet6: {
131       int i = len/8, j = len%8;
132       unsigned char *m = mask_r->v6.s6_addr;
133
134       assert(len <= 128);
135       memset(m, 0xff, i);
136       if (j) m[i++] = (0xff << (8-j)) & 0xff;
137       memset(m+i, 0, 16-i);
138     } break;
139     default:
140       unknown_af(af);
141       break;
142   }
143 }
144
145 int adns__guess_prefix_length(int af, const union gen_addr *addr)
146 {
147   switch (af) {
148     AF_CASES(af);
149     af_inet: {
150       unsigned a = (ntohl(addr->v4.s_addr) >> 24) & 0xff;
151
152       if (a < 128) return 8;
153       else if (a < 192) return 16;
154       else if (a < 224) return 24;
155       else return -1;
156     } break;
157     af_inet6:
158       return 64;
159     default:
160       unknown_af(af);
161       return -1;
162   }
163 }
164
165 int adns__addr_match_p(int addraf, const union gen_addr *addr,
166                        int netaf, const union gen_addr *base,
167                        const union gen_addr *mask)
168 {
169   if (addraf != netaf) return 0;
170   switch (addraf) {
171     AF_CASES(af);
172     af_inet:
173       return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr;
174     af_inet6: {
175       int i;
176       const char *a = addr->v6.s6_addr;
177       const char *b = base->v6.s6_addr;
178       const char *m = mask->v6.s6_addr;
179
180       for (i = 0; i < 16; i++)
181         if ((a[i] & m[i]) != b[i]) return 0;
182       return 1;
183     } break;
184     default:
185       unknown_af(addraf);
186       return -1;
187   }
188 }
189
190 const void *adns__sockaddr_to_inaddr(const struct sockaddr *sa)
191 {
192   switch (sa->sa_family) {
193     AF_CASES(af);
194     af_inet: return &CSIN(sa)->sin_addr;
195     af_inet6: return &CSIN6(sa)->sin6_addr;
196     default: unknown_af(sa->sa_family); return 0;
197   }
198 }
199
200 /*
201  * Reverse-domain parsing and construction.
202  */
203
204 int adns__make_reverse_domain(const struct sockaddr *sa,
205                               const char *zone,
206                               char **buf_io, size_t bufsz,
207                               char **buf_free_r)
208 {
209   size_t req;
210   char *p;
211   unsigned c, y;
212   unsigned long aa;
213   const unsigned char *ap;
214   int i, j;
215
216   switch (sa->sa_family) {
217     AF_CASES(af);
218     af_inet:
219       req = 4 * 4;
220       if (!zone) zone = "in-addr.arpa";
221       break;
222     af_inet6:
223       req = 2 * 32;
224       if (!zone) zone = "ip6.arpa";
225       break;
226     default:
227       return ENOSYS;
228   }
229
230   req += strlen(zone) + 1;
231   if (req <= bufsz)
232     p = *buf_io;
233   else {
234     p = malloc(req); if (!p) return errno;
235     *buf_free_r = p;
236   }
237
238   *buf_io = p;
239   switch (sa->sa_family) {
240     AF_CASES(bf);
241     bf_inet:
242       aa = ntohl(CSIN(sa)->sin_addr.s_addr);
243       for (i = 0; i < 4; i++) {
244         p += sprintf(p, "%d", (int)(aa & 0xff));
245         *p++ = '.';
246         aa >>= 8;
247       }
248       break;
249     bf_inet6:
250       ap = CSIN6(sa)->sin6_addr.s6_addr + 16;
251       for (i = 0; i < 16; i++) {
252         c = *--ap;
253         for (j = 0; j < 2; j++) {
254           y = c & 0xf;
255           if (y < 10) *p++ = y + '0';
256           else *p++ = y - 10 + 'a';
257           c >>= 4;
258           *p++ = '.';
259         }
260       }
261       break;
262     default:
263       unknown_af(sa->sa_family);
264   }
265
266   strcpy(p, zone);
267   return 0;
268 }
269
270
271 static int inet_rev_parsecomp(const char *p, size_t n)
272 {
273   int i = 0;
274   if (n > 3) return -1;
275
276   while (n--) {
277     if ('0' <= *p && *p <= '9') i = 10*i + *p++ - '0';
278     else return -1;
279   }
280   return i;
281 }
282
283 static void inet_rev_mkaddr(union gen_addr *addr, const byte *ipv)
284 {
285   addr->v4.s_addr = htonl((ipv[3]<<24) | (ipv[2]<<16) |
286                           (ipv[1]<<8) | (ipv[0]));
287 }
288
289 static int inet6_rev_parsecomp(const char *p, size_t n)
290 {
291   if (n != 1) return -1;
292   else if ('0' <= *p && *p <= '9') return *p - '0';
293   else if ('a' <= *p && *p <= 'f') return *p - 'a' + 10;
294   else if ('A' <= *p && *p <= 'F') return *p - 'a' + 10;
295   else return -1;
296 }
297
298 static void inet6_rev_mkaddr(union gen_addr *addr, const byte *ipv)
299 {
300   unsigned char *a = addr->v6.s6_addr;
301   int i;
302
303   for (i = 0; i < 16; i++)
304     a[i] = (ipv[31-2*i] << 4) | (ipv[30-2*i] << 0);
305 }
306
307 static const struct revparse_domain {
308   int af;                               /* address family */
309   int nrevlab;                          /* n of reverse-address labels */
310   adns_rrtype rrtype;                   /* forward-lookup type */
311
312   int (*rev_parsecomp)(const char *p, size_t n);
313   /* parse a single component from a label; return the integer value, or -1
314    * if it was unintelligible.
315    */
316
317   void (*rev_mkaddr)(union gen_addr *addr, const byte *ipv);
318   /* write out the parsed address from a vector of parsed components */
319
320   const char *const tail[3];            /* tail label names */
321 } revparse_domains[NREVDOMAINS] = {
322   { AF_INET, 4, adns_r_a, inet_rev_parsecomp, inet_rev_mkaddr,
323     { DNS_INADDR_ARPA, 0 } },
324   { AF_INET6, 32, adns_r_aaaa, inet6_rev_parsecomp, inet6_rev_mkaddr,
325     { DNS_IP6_ARPA, 0 } },
326 };
327
328 #define REVDOMAIN_MAP(rps, labnum) \
329   ((labnum) ? (rps)->map : (1 << NREVDOMAINS) - 1)
330
331 int adns__revparse_label(struct revparse_state *rps, int labnum,
332                          const char *label, int lablen)
333 {
334   unsigned f = REVDOMAIN_MAP(rps, labnum);
335   const struct revparse_domain *rpd;
336   const char *tp;
337   unsigned d;
338   int i, ac;
339
340   for (rpd=revparse_domains, i=0, d=1; i<NREVDOMAINS; rpd++, i++, d <<= 1) {
341     if (!(f & d)) continue;
342     if (labnum >= rpd->nrevlab) {
343       tp = rpd->tail[labnum - rpd->nrevlab];
344       if (!tp || strncmp(label, tp, lablen) != 0 || tp[lablen])
345         goto mismatch;
346     } else {
347       ac = rpd->rev_parsecomp(label, lablen);
348       if (ac < 0) goto mismatch;
349       assert(labnum < sizeof(rps->ipv[i]));
350       rps->ipv[i][labnum] = ac;
351     }
352     continue;
353
354   mismatch:
355     f &= ~d;
356     if (!f) return -1;
357   }
358
359   rps->map = f;
360   return 0;
361 }
362
363 int adns__revparse_done(struct revparse_state *rps, int nlabels,
364                         adns_rrtype *rrtype_r, struct af_addr *addr_r)
365 {
366   unsigned f = REVDOMAIN_MAP(rps, nlabels);
367   const struct revparse_domain *rpd;
368   unsigned d;
369   int i, found = -1;
370
371   for (rpd=revparse_domains, i=0, d=1; i<NREVDOMAINS; rpd++, i++, d <<= 1) {
372     if (!(f & d)) continue;
373     if (nlabels >= rpd->nrevlab && !rpd->tail[nlabels - rpd->nrevlab])
374       { found = i; continue; }
375     f &= ~d;
376     if (!f) return -1;
377   }
378   assert(found >= 0); assert(f == (1 << found));
379
380   rpd = &revparse_domains[found];
381   *rrtype_r = rpd->rrtype;
382   addr_r->af = rpd->af;
383   rpd->rev_mkaddr(&addr_r->addr, rps->ipv[found]);
384   return 0;
385 }