Merge branch 'credentials' of git+ssh://gnunet.org/gnunet into credentials
[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 General Public License as published
8     by the Free Software Foundation; either version 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
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 .gnu 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, ".gnu") || 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           goto finish;
144         }
145     }
146     else
147     {
148       status = NSS_STATUS_UNAVAIL;
149       goto finish;
150     }
151
152     if (u.count == 0) {
153         *errnop = ETIMEDOUT;
154         *h_errnop = HOST_NOT_FOUND;
155         status = NSS_STATUS_NOTFOUND;
156         goto finish;
157     }
158
159
160     /* Alias names */
161     *((char**) buffer) = NULL;
162     result->h_aliases = (char**) buffer;
163     idx = sizeof(char*);
164
165     /* Official name */
166     strcpy(buffer+idx, name);
167     result->h_name = buffer+idx;
168     idx += strlen(name)+1;
169
170     ALIGN(idx);
171
172     result->h_addrtype = af;
173     result->h_length = address_length;
174
175     /* Check if there's enough space for the addresses */
176     if (buflen < idx+u.data_len+sizeof(char*)*(u.count+1)) {
177         *errnop = ERANGE;
178         *h_errnop = NO_RECOVERY;
179         status = NSS_STATUS_TRYAGAIN;
180         goto finish;
181     }
182
183     /* Addresses */
184     astart = idx;
185     l = u.count*address_length;
186     if (0 != l)
187       memcpy(buffer+astart, &u.data, l);
188     /* address_length is a multiple of 32bits, so idx is still aligned
189      * correctly */
190     idx += l;
191
192     /* Address array address_lenght is always a multiple of 32bits */
193     for (i = 0; i < u.count; i++)
194         ((char**) (buffer+idx))[i] = buffer+astart+address_length*i;
195     ((char**) (buffer+idx))[i] = NULL;
196     result->h_addr_list = (char**) (buffer+idx);
197
198     status = NSS_STATUS_SUCCESS;
199
200 finish:
201     return status;
202 }
203
204 /**
205  * The gethostbyname hook executed by nsswitch
206  *
207  * @param name the name to resolve
208  * @param result the result hostent
209  * @param buffer the result buffer
210  * @param buflen length of the buffer
211  * @param errnop idk
212  * @param h_errnop idk
213  * @return a nss_status code
214  */
215 enum nss_status _nss_gns_gethostbyname_r (
216     const char *name,
217     struct hostent *result,
218     char *buffer,
219     size_t buflen,
220     int *errnop,
221     int *h_errnop) {
222
223     return _nss_gns_gethostbyname2_r(
224         name,
225         AF_UNSPEC,
226         result,
227         buffer,
228         buflen,
229         errnop,
230         h_errnop);
231 }
232
233 /**
234  * The gethostbyaddr hook executed by nsswitch
235  * We can't do this so we always return NSS_STATUS_UNAVAIL
236  *
237  * @param addr the address to resolve
238  * @param len the length of the address
239  * @param af the address family of the address
240  * @param result the result hostent
241  * @param buffer the result buffer
242  * @param buflen length of the buffer
243  * @param errnop idk
244  * @param h_errnop idk
245  * @return NSS_STATUS_UNAVAIL
246  */
247 enum nss_status _nss_gns_gethostbyaddr_r(
248     const void* addr,
249     int len,
250     int af,
251     struct hostent *result,
252     char *buffer,
253     size_t buflen,
254     int *errnop,
255     int *h_errnop) {
256   
257     *errnop = EINVAL;
258     *h_errnop = NO_RECOVERY;
259     //NOTE we allow to leak this into DNS so no NOTFOUND
260     return NSS_STATUS_UNAVAIL;
261 }
262