3bb45a1533715a65faf2b998e00fde85a19aa47f
[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     nss-mdns is free software; you can redistribute it and/or modify
7     it under the terms of the GNU Lesser General Public License as published
8     by the Free Software Foundation; either version 2 of the License,
9     or (at your option) any later version.
10  
11     nss-mdns is distributed in the hope that it will be useful, but1
12     WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     General Public License for more details.
15  
16     You should have received a copy of the GNU Lesser General Public License
17     along with nss-mdns; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19     USA.
20 ***/
21
22 #include <gnunet_config.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <netdb.h>
28 #include <sys/socket.h>
29 #include <nss.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "nss_gns_query.h"
34
35 #include <arpa/inet.h>
36
37 /** macro to align idx to 32bit boundary */
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
44 /**
45  * function to check if name ends with a specific suffix
46  *
47  * @param name the name to check
48  * @param suffix the suffix to check for
49  * @return 1 if true
50  */
51 static int ends_with(const char *name, const char* suffix) {
52     size_t ln, ls;
53     assert(name);
54     assert(suffix);
55
56     if ((ls = strlen(suffix)) > (ln = strlen(name)))
57         return 0;
58
59     return strcasecmp(name+ln-ls, suffix) == 0;
60 }
61
62
63 /**
64  * Check if name is inside .gnunet or .zkey TLD
65  *
66  * @param name name to check
67  * @return 1 if true
68  */
69 static int verify_name_allowed(const char *name) {
70     return ends_with(name, ".gnunet") || ends_with(name, ".zkey"); 
71 }
72
73 /**
74  * The gethostbyname hook executed by nsswitch
75  *
76  * @param name the name to resolve
77  * @param af the address family to resolve
78  * @param result the result hostent
79  * @param buffer the result buffer
80  * @param buflen length of the buffer
81  * @param errnop idk
82  * @param h_errnop idk
83  * @return a nss_status code
84  */
85 enum nss_status _nss_gns_gethostbyname2_r(
86     const char *name,
87     int af,
88     struct hostent * result,
89     char *buffer,
90     size_t buflen,
91     int *errnop,
92     int *h_errnop) {
93
94     struct userdata u;
95     enum nss_status status = NSS_STATUS_UNAVAIL;
96     int i;
97     size_t address_length, l, idx, astart;
98     int name_allowed;
99     
100     if (af == AF_UNSPEC)
101 #ifdef NSS_IPV6_ONLY
102         af = AF_INET6;
103 #else
104         af = AF_INET;
105 #endif
106
107 #ifdef NSS_IPV4_ONLY
108     if (af != AF_INET) 
109 #elif NSS_IPV6_ONLY
110     if (af != AF_INET6)
111 #else        
112     if (af != AF_INET && af != AF_INET6)
113 #endif        
114     {    
115         *errnop = EINVAL;
116         *h_errnop = NO_RECOVERY;
117
118         goto finish;
119     }
120
121     address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t);
122     if (buflen <
123         sizeof(char*)+    /* alias names */
124         strlen(name)+1)  {   /* official name */
125         
126         *errnop = ERANGE;
127         *h_errnop = NO_RECOVERY;
128         status = NSS_STATUS_TRYAGAIN;
129         
130         goto finish;
131     }
132     
133     u.count = 0;
134     u.data_len = 0;
135
136     name_allowed = verify_name_allowed(name);
137     
138     if (name_allowed) {
139
140         if (!gns_resolve_name(af, name, &u) == 0)
141         {
142           status = NSS_STATUS_NOTFOUND;
143         }
144     }
145
146     if (u.count == 0) {
147         *errnop = ETIMEDOUT;
148         *h_errnop = HOST_NOT_FOUND;
149         printf("not found\n");
150         goto finish;
151     }
152
153         
154     /* Alias names */
155     *((char**) buffer) = NULL;
156     result->h_aliases = (char**) buffer;
157     idx = sizeof(char*);
158     
159     /* Official name */
160     strcpy(buffer+idx, name); 
161     result->h_name = buffer+idx;
162     idx += strlen(name)+1;
163
164     ALIGN(idx);
165     
166     result->h_addrtype = af;
167     result->h_length = address_length;
168     
169     /* Check if there's enough space for the addresses */
170     if (buflen < idx+u.data_len+sizeof(char*)*(u.count+1)) {
171         *errnop = ERANGE;
172         *h_errnop = NO_RECOVERY;
173         status = NSS_STATUS_TRYAGAIN;
174         goto finish;
175     }
176
177     /* Addresses */
178     astart = idx;
179     l = u.count*address_length;
180     memcpy(buffer+astart, &u.data, l);
181     /* address_length is a multiple of 32bits, so idx is still aligned
182      * correctly */
183     idx += l;
184
185     /* Address array address_lenght is always a multiple of 32bits */
186     for (i = 0; i < u.count; i++)
187         ((char**) (buffer+idx))[i] = buffer+astart+address_length*i;
188     ((char**) (buffer+idx))[i] = NULL;
189     result->h_addr_list = (char**) (buffer+idx);
190
191     status = NSS_STATUS_SUCCESS;
192     
193 finish:
194     return status;
195 }
196
197 /**
198  * The gethostbyname hook executed by nsswitch
199  *
200  * @param name the name to resolve
201  * @param result the result hostent
202  * @param buffer the result buffer
203  * @param buflen length of the buffer
204  * @param errnop idk
205  * @param h_errnop idk
206  * @return a nss_status code
207  */
208 enum nss_status _nss_gns_gethostbyname_r (
209     const char *name,
210     struct hostent *result,
211     char *buffer,
212     size_t buflen,
213     int *errnop,
214     int *h_errnop) {
215
216     return _nss_gns_gethostbyname2_r(
217         name,
218         AF_UNSPEC,
219         result,
220         buffer,
221         buflen,
222         errnop,
223         h_errnop);
224 }
225
226 /**
227  * The gethostbyaddr hook executed by nsswitch
228  * We can't do this so we always return NSS_STATUS_UNAVAIL
229  *
230  * @param addr the address to resolve
231  * @param len the length of the address
232  * @param af the address family of the address
233  * @param result the result hostent
234  * @param buffer the result buffer
235  * @param buflen length of the buffer
236  * @param errnop idk
237  * @param h_errnop idk
238  * @return NSS_STATUS_UNAVAIL
239  */
240 enum nss_status _nss_gns_gethostbyaddr_r(
241     const void* addr,
242     int len,
243     int af,
244     struct hostent *result,
245     char *buffer,
246     size_t buflen,
247     int *errnop,
248     int *h_errnop) {
249
250     /* we dont do this */
251     
252     enum nss_status status = NSS_STATUS_UNAVAIL;
253     
254     *errnop = EINVAL;
255     *h_errnop = NO_RECOVERY;
256
257     /* Check for address types */
258
259     *h_errnop = NO_RECOVERY;
260
261     status = NSS_STATUS_NOTFOUND;
262     return status;
263 }
264