chiark / gitweb /
Support transport over IPv6.
[adns.git] / 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  * IPv4
42  */
43
44 #define SIN(sa) ((struct sockaddr_in *)(sa))
45 #define CSIN(sa) ((const struct sockaddr_in *)(sa))
46
47 static void *inet_sockaddr_to_inaddr(struct sockaddr *sa)
48   { return &SIN(sa)->sin_addr; }
49
50 static int inet_sockaddr_equalp(const struct sockaddr *sa,
51                                 const struct sockaddr *sb)
52 {
53   const struct sockaddr_in *sina = CSIN(sa), *sinb = CSIN(sb);
54   return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr &&
55           sina->sin_port == sinb->sin_port);
56 }
57
58 static void inet_prefix_mask(int len, union gen_addr *mask)
59   { mask->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len)); }
60
61 static int inet_guess_len(const union gen_addr *addr)
62 {
63   unsigned a = (ntohl(addr->v4.s_addr) >> 24) & 0xff;
64
65   if (a < 128) return 8;
66   else if (a < 192) return 16;
67   else if (a < 224) return 24;
68   else return -1;
69 }
70
71 static int inet_matchp(const union gen_addr *addr,
72                        const union gen_addr *base,
73                        const union gen_addr *mask)
74   { return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr; }
75
76 static int inet_rev_parsecomp(const char *p, size_t n)
77 {
78   int i = 0;
79   if (n > 3) return -1;
80
81   while (n--) {
82     if ('0' <= *p && *p <= '9') i = 10*i + *p++ - '0';
83     else return -1;
84   }
85   return i;
86 }
87
88 static void inet_rev_mkaddr(union gen_addr *addr, const byte *ipv)
89 {
90   addr->v4.s_addr = htonl((ipv[3]<<24) | (ipv[2]<<16) |
91                           (ipv[1]<<8) | (ipv[0]));
92 }
93
94 static char *inet_rev_mkname(const struct sockaddr *sa, char *buf)
95 {
96   unsigned long a = ntohl(CSIN(sa)->sin_addr.s_addr);
97   int i;
98
99   for (i = 0; i < 4; i++) {
100     if (i) *buf++ = '.';
101     buf += sprintf(buf, "%d", (int)(a & 0xff));
102     a >>= 8;
103   }
104   return buf;
105 }
106
107 const afinfo adns__inet_afinfo = {
108   AF_INET, 32, '.', 4, 3, adns_r_a,
109   inet_sockaddr_to_inaddr, inet_sockaddr_equalp,
110   inet_prefix_mask, inet_guess_len, inet_matchp,
111   inet_rev_parsecomp, inet_rev_mkaddr, inet_rev_mkname
112 };
113
114 /*
115  * IPv6
116  */
117
118 #define SIN6(sa) ((struct sockaddr_in6 *)(sa))
119 #define CSIN6(sa) ((const struct sockaddr_in6 *)(sa))
120
121 static void *inet6_sockaddr_to_inaddr(struct sockaddr *sa)
122   { return &SIN6(sa)->sin6_addr; }
123
124 static int inet6_sockaddr_equalp(const struct sockaddr *sa,
125                                  const struct sockaddr *sb)
126 {
127   const struct sockaddr_in6 *sin6a = CSIN6(sa), *sin6b = CSIN6(sb);
128   return (memcmp(sin6a->sin6_addr.s6_addr,
129                  sin6b->sin6_addr.s6_addr,
130                  sizeof(sin6a->sin6_addr.s6_addr)) == 0 &&
131           sin6a->sin6_port == sin6b->sin6_port &&
132           sin6a->sin6_scope_id == sin6b->sin6_scope_id);
133 }
134
135 static void inet6_prefix_mask(int len, union gen_addr *mask)
136 {
137   int i = len/8, j = len%8;
138   unsigned char *m = mask->v6.s6_addr;
139
140   assert(len < 128);
141   memset(m, 0xff, i);
142   if (j) m[i++] = (0xff << (8-j)) & 0xff;
143   memset(m + i, 0, 16-i);
144 }
145
146 static int inet6_guess_len(const union gen_addr *addr)
147   { return 64; }
148
149 static int inet6_matchp(const union gen_addr *addr,
150                         const union gen_addr *base,
151                         const union gen_addr *mask)
152 {
153   int i;
154   const char *a = addr->v6.s6_addr;
155   const char *b = base->v6.s6_addr;
156   const char *m = mask->v6.s6_addr;
157
158   for (i = 0; i < 16; i++)
159     if ((a[i] & m[i]) != b[i]) return 0;
160   return 1;
161 }
162
163 static int inet6_rev_parsecomp(const char *p, size_t n)
164 {
165   if (n != 1) return -1;
166   else if ('0' <= *p && *p <= '9') return *p - '0';
167   else if ('a' <= *p && *p <= 'f') return *p - 'a' + 10;
168   else if ('A' <= *p && *p <= 'F') return *p - 'a' + 10;
169   else return -1;
170 }
171
172 static void inet6_rev_mkaddr(union gen_addr *addr, const byte *ipv)
173 {
174   unsigned char *a = addr->v6.s6_addr;
175   int i;
176
177   for (i = 0; i < 16; i++)
178     a[i] = (ipv[31-2*i] << 4) | (ipv[30-2*i] << 0);
179 }
180
181 static char *inet6_rev_mkname(const struct sockaddr *sa, char *buf)
182 {
183   const unsigned char *a = CSIN6(sa)->sin6_addr.s6_addr + 16;
184   unsigned c, y;
185   int i, j;
186
187   for (i = 0; i < 16; i++) {
188     c = *--a;
189     for (j = 0; j < 2; j++) {
190       if (i || j) *buf++ = '.';
191       y = c & 0xf;
192       if (y < 10) *buf++ = y + '0';
193       else *buf++ = y - 10 + 'a';
194       c >>= 4;
195     }
196   }
197   return buf;
198 }
199
200 const afinfo adns__inet6_afinfo = {
201   AF_INET6, 128, ':', 32, 1, adns_r_aaaa,
202   inet6_sockaddr_to_inaddr, inet6_sockaddr_equalp,
203   inet6_prefix_mask, inet6_guess_len, inet6_matchp,
204   inet6_rev_parsecomp, inet6_rev_mkaddr, inet6_rev_mkname
205 };