-start nss
[oweals/gnunet.git] / src / gns / nss / nss_gns.c
1 /***
2     This file is part of nss-gns.
3
4     Parts taken from: nss.c in nss-mdns
5 ***/
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <assert.h>
15 #include <netdb.h>
16 #include <sys/socket.h>
17 #include <nss.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20
21 #elif defined(NSS_IPV4_ONLY)
22 #define _nss_mdns_gethostbyname2_r _nss_gns4_minimal_gethostbyname2_r
23 #define _nss_mdns_gethostbyname_r  _nss_gns4_minimal_gethostbyname_r
24 #define _nss_mdns_gethostbyaddr_r  _nss_gns4_minimal_gethostbyaddr_r
25 #elif defined(NSS_IPV6_ONLY)
26 #define _nss_mdns_gethostbyname2_r _nss_gns6_gethostbyname2_r
27 #define _nss_mdns_gethostbyname_r  _nss_gns6_gethostbyname_r
28 #define _nss_mdns_gethostbyaddr_r  _nss_gns6_gethostbyaddr_r
29 #else
30 #define _nss_mdns_gethostbyname2_r _nss_gns_gethostbyname2_r
31 #define _nss_mdns_gethostbyname_r  _nss_gns_gethostbyname_r
32 #define _nss_mdns_gethostbyaddr_r  _nss_gns_gethostbyaddr_r
33 #endif
34
35 /* Maximum number of entries to return */
36 #define MAX_ENTRIES 16
37
38 #define ALIGN(idx) do { \
39   if (idx % sizeof(void*)) \
40     idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \
41 } while(0)
42
43 struct userdata {
44     int count;
45     int data_len; /* only valid when doing reverse lookup */
46     union  {
47         ipv4_address_t ipv4[MAX_ENTRIES];
48         ipv6_address_t ipv6[MAX_ENTRIES];
49         char *name[MAX_ENTRIES];
50     } data;
51 };
52
53 #ifndef NSS_IPV6_ONLY
54 static void ipv4_callback(const ipv4_address_t *ipv4, void *userdata) {
55     struct userdata *u = userdata;
56     assert(ipv4 && userdata);
57
58     if (u->count >= MAX_ENTRIES)
59         return;
60
61     u->data.ipv4[u->count++] = *ipv4;
62     u->data_len += sizeof(ipv4_address_t);
63 }
64 #endif
65
66 #ifndef NSS_IPV4_ONLY
67 static void ipv6_callback(const ipv6_address_t *ipv6, void *userdata) {
68     struct userdata *u = userdata;
69     assert(ipv6 && userdata);
70
71     if (u->count >= MAX_ENTRIES)
72         return;
73
74     u->data.ipv6[u->count++] = *ipv6;
75     u->data_len += sizeof(ipv6_address_t);
76 }
77 #endif
78
79 static void name_callback(const char*name, void *userdata) {
80     struct userdata *u = userdata;
81     assert(name && userdata);
82
83     if (u->count >= MAX_ENTRIES)
84         return;
85
86     u->data.name[u->count++] = strdup(name);
87     u->data_len += strlen(name)+1;
88 }
89
90 static int ends_with(const char *name, const char* suffix) {
91     size_t ln, ls;
92     assert(name);
93     assert(suffix);
94
95     if ((ls = strlen(suffix)) > (ln = strlen(name)))
96         return 0;
97
98     return strcasecmp(name+ln-ls, suffix) == 0;
99 }
100
101 static int verify_name_allowed(const char *name) {
102     return ends_with(name, ".gnunet") || ends_with(name, ".zkey"); 
103 }
104
105 enum nss_status _nss_gns_gethostbyname2_r(
106     const char *name,
107     int af,
108     struct hostent * result,
109     char *buffer,
110     size_t buflen,
111     int *errnop,
112     int *h_errnop) {
113
114     struct userdata u;
115     enum nss_status status = NSS_STATUS_UNAVAIL;
116     int i;
117     size_t address_length, l, idx, astart;
118     void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata);
119     void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata);
120     int name_allowed;
121
122     if (af == AF_UNSPEC)
123 #ifdef NSS_IPV6_ONLY
124         af = AF_INET6;
125 #else
126         af = AF_INET;
127 #endif
128
129 #ifdef NSS_IPV4_ONLY
130     if (af != AF_INET) 
131 #elif NSS_IPV6_ONLY
132     if (af != AF_INET6)
133 #else        
134     if (af != AF_INET && af != AF_INET6)
135 #endif        
136     {    
137         *errnop = EINVAL;
138         *h_errnop = NO_RECOVERY;
139
140         goto finish;
141     }
142
143     address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t);
144     if (buflen <
145         sizeof(char*)+    /* alias names */
146         strlen(name)+1)  {   /* official name */
147         
148         *errnop = ERANGE;
149         *h_errnop = NO_RECOVERY;
150         status = NSS_STATUS_TRYAGAIN;
151         
152         goto finish;
153     }
154     
155     u.count = 0;
156     u.data_len = 0;
157
158 #ifdef NSS_IPV6_ONLY
159     ipv4_func = NULL;
160 #else
161     ipv4_func = af == AF_INET ? ipv4_callback : NULL;
162 #endif
163
164 #ifdef NSS_IPV4_ONLY
165     ipv6_func = NULL;
166 #else
167     ipv6_func = af == AF_INET6 ? ipv6_callback : NULL;
168 #endif
169
170 #ifdef ENABLE_GNS
171     name_allowed = verify_name_allowed(name);
172     
173     if (gns_works && name_allowed) {
174         int r;
175
176         if ((r = gns_resolve_name(af, name, data)) < 0)
177             gns_works = 0;
178         else if (r == 0) {
179             if (af == AF_INET && ipv4_func)
180                 ipv4_func((ipv4_address_t*) data, &u);
181             if (af == AF_INET6 && ipv6_func)
182                 ipv6_func((ipv6_address_t*)data, &u);
183         } else
184             status = NSS_STATUS_NOTFOUND;
185     }
186
187 #endif /* ENABLE_GNS */
188
189     if (u.count == 0) {
190         *errnop = ETIMEDOUT;
191         *h_errnop = HOST_NOT_FOUND;
192         goto finish;
193     }
194     
195     /* Alias names */
196     *((char**) buffer) = NULL;
197     result->h_aliases = (char**) buffer;
198     idx = sizeof(char*);
199     
200     /* Official name */
201     strcpy(buffer+idx, name); 
202     result->h_name = buffer+idx;
203     idx += strlen(name)+1;
204
205     ALIGN(idx);
206     
207     result->h_addrtype = af;
208     result->h_length = address_length;
209     
210     /* Check if there's enough space for the addresses */
211     if (buflen < idx+u.data_len+sizeof(char*)*(u.count+1)) {
212         *errnop = ERANGE;
213         *h_errnop = NO_RECOVERY;
214         status = NSS_STATUS_TRYAGAIN;
215         goto finish;
216     }
217
218     /* Addresses */
219     astart = idx;
220     l = u.count*address_length;
221     memcpy(buffer+astart, &u.data, l);
222     /* address_length is a multiple of 32bits, so idx is still aligned
223      * correctly */
224     idx += l;
225
226     /* Address array address_lenght is always a multiple of 32bits */
227     for (i = 0; i < u.count; i++)
228         ((char**) (buffer+idx))[i] = buffer+astart+address_length*i;
229     ((char**) (buffer+idx))[i] = NULL;
230     result->h_addr_list = (char**) (buffer+idx);
231
232     status = NSS_STATUS_SUCCESS;
233     
234 finish:
235     return status;
236 }
237
238 enum nss_status _nss_gns_gethostbyname_r (
239     const char *name,
240     struct hostent *result,
241     char *buffer,
242     size_t buflen,
243     int *errnop,
244     int *h_errnop) {
245
246     return _nss_gns_gethostbyname2_r(
247         name,
248         AF_UNSPEC,
249         result,
250         buffer,
251         buflen,
252         errnop,
253         h_errnop);
254 }
255
256 enum nss_status _nss_gns_gethostbyaddr_r(
257     const void* addr,
258     int len,
259     int af,
260     struct hostent *result,
261     char *buffer,
262     size_t buflen,
263     int *errnop,
264     int *h_errnop) {
265
266     /* we dont do this */
267     
268     struct userdata u;
269     enum nss_status status = NSS_STATUS_UNAVAIL;
270     int r;
271     size_t address_length, idx, astart;
272     
273     *errnop = EINVAL;
274     *h_errnop = NO_RECOVERY;
275
276     u.count = 0;
277     u.data_len = 0;
278
279     /* Check for address types */
280
281     *h_errnop = NO_RECOVERY;
282
283     status = NSS_STATUS_NOTFOUND;
284     return status;
285 }
286