1/**
2 * @file
3 * API functions for name resolving
4 *
5 */
6
7/*
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 *    this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 *    this list of conditions and the following disclaimer in the documentation
15 *    and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
22 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
28 * OF SUCH DAMAGE.
29 *
30 * This file is part of the lwIP TCP/IP stack.
31 *
32 * Author: Simon Goldschmidt
33 *
34 */
35
36#include "lwip/netdb.h"
37
38#if LWIP_DNS && LWIP_SOCKET
39
40#include "lwip/err.h"
41#include "lwip/mem.h"
42#include "lwip/ip_addr.h"
43#include "lwip/api.h"
44
45#include <string.h>
46#include <stdlib.h>
47
48/** helper struct for gethostbyname_r to access the char* buffer */
49struct gethostbyname_r_helper {
50    struct ip_addr *addrs;
51    struct ip_addr addr;
52    char *aliases;
53};
54
55/** h_errno is exported in netdb.h for access by applications. */
56#if LWIP_DNS_API_DECLARE_H_ERRNO
57int h_errno;
58#endif                          /* LWIP_DNS_API_DECLARE_H_ERRNO */
59
60/** define "hostent" variables storage: 0 if we use a static (but unprotected)
61 * set of variables for lwip_gethostbyname, 1 if we use a local storage */
62#ifndef LWIP_DNS_API_HOSTENT_STORAGE
63#define LWIP_DNS_API_HOSTENT_STORAGE 0
64#endif
65
66/** define "hostent" variables storage */
67#if LWIP_DNS_API_HOSTENT_STORAGE
68#define HOSTENT_STORAGE
69#else
70#define HOSTENT_STORAGE static
71#endif                          /* LWIP_DNS_API_STATIC_HOSTENT */
72
73/**
74 * Returns an entry containing addresses of address family AF_INET
75 * for the host with name name.
76 * Due to dns_gethostbyname limitations, only one address is returned.
77 *
78 * @param name the hostname to resolve
79 * @return an entry containing addresses of address family AF_INET
80 *         for the host with name name
81 */
82struct hostent *lwip_gethostbyname(const char *name)
83{
84    err_t err;
85    struct ip_addr addr;
86
87    /* buffer variables for lwip_gethostbyname() */
88    HOSTENT_STORAGE struct hostent s_hostent;
89    HOSTENT_STORAGE char *s_aliases;
90    HOSTENT_STORAGE struct ip_addr s_hostent_addr;
91    HOSTENT_STORAGE struct ip_addr *s_phostent_addr;
92
93    /* query host IP address */
94    err = netconn_gethostbyname(name, &addr);
95    if (err != ERR_OK) {
96        LWIP_DEBUGF(DNS_DEBUG,
97                    ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
98        h_errno = HOST_NOT_FOUND;
99        return NULL;
100    }
101
102    /* fill hostent */
103    s_hostent_addr = addr;
104    s_phostent_addr = &s_hostent_addr;
105    s_hostent.h_name = (char *) name;
106    s_hostent.h_aliases = &s_aliases;
107    s_hostent.h_addrtype = AF_INET;
108    s_hostent.h_length = sizeof(struct ip_addr);
109    s_hostent.h_addr_list = (char **) &s_phostent_addr;
110
111#if DNS_DEBUG
112    /* dump hostent */
113    LWIP_DEBUGF(DNS_DEBUG,
114                ("hostent.h_name           == %s\n", s_hostent.h_name));
115    LWIP_DEBUGF(DNS_DEBUG,
116                ("hostent.h_aliases        == %p\n", s_hostent.h_aliases));
117    if (s_hostent.h_aliases != NULL) {
118        u8_t idx;
119
120        for (idx = 0; s_hostent.h_aliases[idx]; idx++) {
121            LWIP_DEBUGF(DNS_DEBUG,
122                        ("hostent.h_aliases[%i]->   == %p\n", idx,
123                         s_hostent.h_aliases[idx]));
124            LWIP_DEBUGF(DNS_DEBUG,
125                        ("hostent.h_aliases[%i]->   == %s\n", idx,
126                         s_hostent.h_aliases[idx]));
127        }
128    }
129    LWIP_DEBUGF(DNS_DEBUG,
130                ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
131    LWIP_DEBUGF(DNS_DEBUG,
132                ("hostent.h_length         == %d\n", s_hostent.h_length));
133    LWIP_DEBUGF(DNS_DEBUG,
134                ("hostent.h_addr_list      == %p\n", s_hostent.h_addr_list));
135    if (s_hostent.h_addr_list != NULL) {
136        u8_t idx;
137
138        for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
139            LWIP_DEBUGF(DNS_DEBUG,
140                        ("hostent.h_addr_list[%i]   == %p\n", idx,
141                         s_hostent.h_addr_list[idx]));
142            LWIP_DEBUGF(DNS_DEBUG,
143                        ("hostent.h_addr_list[%i]-> == %s\n", idx,
144                         inet_ntoa(*
145                                   ((struct in_addr *) (s_hostent.
146                                                        h_addr_list[idx])))));
147        }
148    }
149#endif                          /* DNS_DEBUG */
150
151#if LWIP_DNS_API_HOSTENT_STORAGE
152    /* this function should return the "per-thread" hostent after copy from s_hostent */
153    return sys_thread_hostent(&s_hostent);
154#else
155    return &s_hostent;
156#endif                          /* LWIP_DNS_API_HOSTENT_STORAGE */
157}
158
159/**
160 * Thread-safe variant of lwip_gethostbyname: instead of using a static
161 * buffer, this function takes buffer and errno pointers as arguments
162 * and uses these for the result.
163 *
164 * @param name the hostname to resolve
165 * @param ret pre-allocated struct where to store the result
166 * @param buf pre-allocated buffer where to store additional data
167 * @param buflen the size of buf
168 * @param result pointer to a hostent pointer that is set to ret on success
169 *               and set to zero on error
170 * @param h_errnop pointer to an int where to store errors (instead of modifying
171 *                 the global h_errno)
172 * @return 0 on success, non-zero on error, additional error information
173 *         is stored in *h_errnop instead of h_errno to be thread-safe
174 */
175int
176lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
177                     size_t buflen, struct hostent **result, int *h_errnop)
178{
179    err_t err;
180    struct gethostbyname_r_helper *h;
181    char *hostname;
182    size_t namelen;
183    int lh_errno;
184
185    if (h_errnop == NULL) {
186        /* ensure h_errnop is never NULL */
187        h_errnop = &lh_errno;
188    }
189
190    if (result == NULL) {
191        /* not all arguments given */
192        *h_errnop = EINVAL;
193        return -1;
194    }
195    /* first thing to do: set *result to nothing */
196    *result = NULL;
197    if ((name == NULL) || (ret == NULL) || (buf == 0)) {
198        /* not all arguments given */
199        *h_errnop = EINVAL;
200        return -1;
201    }
202
203    namelen = strlen(name);
204    if (buflen <
205        (sizeof(struct gethostbyname_r_helper) + namelen + 1 +
206         (MEM_ALIGNMENT - 1))) {
207        /* buf can't hold the data needed + a copy of name */
208        *h_errnop = ERANGE;
209        return -1;
210    }
211
212    h = (struct gethostbyname_r_helper *) LWIP_MEM_ALIGN(buf);
213    hostname = ((char *) h) + sizeof(struct gethostbyname_r_helper);
214
215    /* query host IP address */
216    err = netconn_gethostbyname(name, &(h->addr));
217    if (err != ERR_OK) {
218        LWIP_DEBUGF(DNS_DEBUG,
219                    ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
220        *h_errnop = ENSRNOTFOUND;
221        return -1;
222    }
223
224    /* copy the hostname into buf */
225    MEMCPY(hostname, name, namelen);
226    hostname[namelen] = 0;
227
228    /* fill hostent */
229    h->addrs = &(h->addr);
230    h->aliases = NULL;
231    ret->h_name = (char *) hostname;
232    ret->h_aliases = &(h->aliases);
233    ret->h_addrtype = AF_INET;
234    ret->h_length = sizeof(struct ip_addr);
235    ret->h_addr_list = (char **) &(h->addrs);
236
237    /* set result != NULL */
238    *result = ret;
239
240    /* return success */
241    return 0;
242}
243
244/**
245 * Frees one or more addrinfo structures returned by getaddrinfo(), along with
246 * any additional storage associated with those structures. If the ai_next field
247 * of the structure is not null, the entire list of structures is freed.
248 *
249 * @param ai struct addrinfo to free
250 */
251void lwip_freeaddrinfo(struct addrinfo *ai)
252{
253    struct addrinfo *next;
254
255    while (ai != NULL) {
256        if (ai->ai_addr != NULL) {
257            mem_free(ai->ai_addr);
258        }
259        if (ai->ai_canonname != NULL) {
260            mem_free(ai->ai_canonname);
261        }
262        next = ai->ai_next;
263        mem_free(ai);
264        ai = next;
265    }
266}
267
268/**
269 * Translates the name of a service location (for example, a host name) and/or
270 * a service name and returns a set of socket addresses and associated
271 * information to be used in creating a socket with which to address the
272 * specified service.
273 * Memory for the result is allocated internally and must be freed by calling
274 * lwip_freeaddrinfo()!
275 *
276 * Due to a limitation in dns_gethostbyname, only the first address of a
277 * host is returned.
278 * Also, service names are not supported (only port numbers)!
279 *
280 * @param nodename descriptive name or address string of the host
281 *                 (may be NULL -> local address)
282 * @param servname port number as string of NULL
283 * @param hints structure containing input values that set socktype and protocol
284 * @param res pointer to a pointer where to store the result (set to NULL on failure)
285 * @return 0 on success, non-zero on failure
286 */
287int
288lwip_getaddrinfo(const char *nodename, const char *servname,
289                 const struct addrinfo *hints, struct addrinfo **res)
290{
291    err_t err;
292    struct ip_addr addr;
293    u32_t ip = 0;
294    struct addrinfo *ai;
295    struct sockaddr_in *sa = NULL;
296    int port_nr = 0;
297
298    if (res == NULL) {
299        return EAI_FAIL;
300    }
301    *res = NULL;
302    if ((nodename == NULL) && (servname == NULL)) {
303        return EAI_NONAME;
304    }
305
306    if (servname != NULL) {
307        /* service name specified: convert to port number
308         * @todo?: currently, only ASCII integers (port numbers) are supported! */
309        port_nr = atoi(servname);
310        if ((port_nr <= 0) || (port_nr > 0xffff)) {
311            return EAI_SERVICE;
312        }
313    }
314
315    if (nodename != NULL) {
316        /* service location specified, check if it's an IP */
317        if ((ip = inet_addr(nodename)) != INADDR_NONE) {
318            addr.addr = ip;
319        } else { /* try to resolve */
320            err = netconn_gethostbyname(nodename, &addr);
321            if (err != ERR_OK) {
322                return EAI_FAIL;
323            }
324        }
325    } else {
326        /* service location specified, use loopback address */
327        addr.addr = INADDR_LOOPBACK;
328    }
329
330    ai = mem_malloc(sizeof(struct addrinfo));
331    if (ai == NULL) {
332        goto memerr;
333    }
334    memset(ai, 0, sizeof(struct addrinfo));
335    sa = mem_malloc(sizeof(struct sockaddr_in));
336    if (sa == NULL) {
337        goto memerr;
338    }
339    memset(sa, 0, sizeof(struct sockaddr_in));
340    /* set up sockaddr */
341    sa->sin_addr.s_addr = addr.addr;
342    sa->sin_family = AF_INET;
343    sa->sin_len = sizeof(struct sockaddr_in);
344    sa->sin_port = htons(port_nr);
345
346    /* set up addrinfo */
347    ai->ai_family = AF_INET;
348    if (hints != NULL) {
349        /* copy socktype & protocol from hints if specified */
350        ai->ai_socktype = hints->ai_socktype;
351        ai->ai_protocol = hints->ai_protocol;
352    }
353    if (nodename != NULL) {
354        /* copy nodename to canonname if specified */
355        size_t namelen = strlen(nodename);
356
357        LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t) - 1);
358        ai->ai_canonname = mem_malloc((mem_size_t) (namelen + 1));
359        if (ai->ai_canonname == NULL) {
360            goto memerr;
361        }
362        MEMCPY(ai->ai_canonname, nodename, namelen);
363        ai->ai_canonname[namelen] = 0;
364    }
365    ai->ai_addrlen = sizeof(struct sockaddr_in);
366    ai->ai_addr = (struct sockaddr *) sa;
367
368    *res = ai;
369
370    return 0;
371  memerr:
372    if (ai != NULL) {
373        mem_free(ai);
374    }
375    if (sa != NULL) {
376        mem_free(sa);
377    }
378    return EAI_MEMORY;
379}
380
381#endif                          /* LWIP_DNS && LWIP_SOCKET */
382