-avoid calling memcpy() with NULL argument, even if len is 0
[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     GNUNET_memcpy(buffer+astart, &u.data, l);
187     /* address_length is a multiple of 32bits, so idx is still aligned
188      * correctly */
189     idx += l;
190
191     /* Address array address_lenght is always a multiple of 32bits */
192     for (i = 0; i < u.count; i++)
193         ((char**) (buffer+idx))[i] = buffer+astart+address_length*i;
194     ((char**) (buffer+idx))[i] = NULL;
195     result->h_addr_list = (char**) (buffer+idx);
196
197     status = NSS_STATUS_SUCCESS;
198
199 finish:
200     return status;
201 }
202
203 /**
204  * The gethostbyname hook executed by nsswitch
205  *
206  * @param name the name to resolve
207  * @param result the result hostent
208  * @param buffer the result buffer
209  * @param buflen length of the buffer
210  * @param errnop idk
211  * @param h_errnop idk
212  * @return a nss_status code
213  */
214 enum nss_status _nss_gns_gethostbyname_r (
215     const char *name,
216     struct hostent *result,
217     char *buffer,
218     size_t buflen,
219     int *errnop,
220     int *h_errnop) {
221
222     return _nss_gns_gethostbyname2_r(
223         name,
224         AF_UNSPEC,
225         result,
226         buffer,
227         buflen,
228         errnop,
229         h_errnop);
230 }
231
232 /**
233  * The gethostbyaddr hook executed by nsswitch
234  * We can't do this so we always return NSS_STATUS_UNAVAIL
235  *
236  * @param addr the address to resolve
237  * @param len the length of the address
238  * @param af the address family of the address
239  * @param result the result hostent
240  * @param buffer the result buffer
241  * @param buflen length of the buffer
242  * @param errnop idk
243  * @param h_errnop idk
244  * @return NSS_STATUS_UNAVAIL
245  */
246 enum nss_status _nss_gns_gethostbyaddr_r(
247     const void* addr,
248     int len,
249     int af,
250     struct hostent *result,
251     char *buffer,
252     size_t buflen,
253     int *errnop,
254     int *h_errnop) {
255   
256     *errnop = EINVAL;
257     *h_errnop = NO_RECOVERY;
258     //NOTE we allow to leak this into DNS so no NOTFOUND
259     return NSS_STATUS_UNAVAIL;
260 }
261