diff options
Diffstat (limited to 'components/lwp/syscall/syscall_netdb.c')
| -rw-r--r-- | components/lwp/syscall/syscall_netdb.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/components/lwp/syscall/syscall_netdb.c b/components/lwp/syscall/syscall_netdb.c new file mode 100644 index 0000000000..6c1dfde6e7 --- /dev/null +++ b/components/lwp/syscall/syscall_netdb.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +#if defined(RT_USING_SAL) && defined(SAL_USING_POSIX) +struct musl_addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + + struct musl_sockaddr *ai_addr; + char *ai_canonname; + + struct musl_addrinfo *ai_next; +}; + +/** + * @brief Resolves network addresses and service names into a list of address structures. + * + * This function provides a mechanism for resolving a host name and service name (or port number) + * into a list of address structures suitable for use with socket-based communication. The function + * can handle both IPv4 and IPv6 addresses and provides a flexible way to specify different types + * of address and service resolution options. + * + * @param[in] nodename The host name to be resolved. This can be a DNS name or an IP address in string format. + * If `nodename` is `NULL`, the function will resolve the local host. + * @param[in] servname The service name (e.g., "http") or port number to be resolved. If `servname` is `NULL`, + * the function will resolve the port number or address without any service association. + * @param[in] hints A pointer to a `struct musl_addrinfo` that provides hints for the address resolution. + * It can be used to specify criteria such as the desired address family (IPv4 or IPv6), + * socket type, protocol, and flags for resolution. + * @param[out] res A pointer to a `struct musl_addrinfo` that will be filled with the resolved address + * information. The resulting linked list will contain one or more `struct musl_sockaddr` + * structures, each representing a different address that can be used for communication. + * + * @return sysret_t Returns `0` on success, indicating that the address resolution was successful. + * On failure, returns a negative error code. + * + * @note The `res` parameter points to a linked list of resolved address structures. Each node in the list + * contains a different resolved address and can be used for socket connection purposes. + * It is important to free the memory allocated for the list after it is no longer needed, using `freeaddrinfo()`. + * + * @warning The `hints` structure allows you to specify various options for the resolution. However, incorrect + * hints may lead to unexpected or incorrect results. For example, if you request IPv6 addresses but + * the host only supports IPv4, the function may not return the expected results. + * + * @see freeaddrinfo(), sys_socket(), sys_connect(), sys_gethostbyname(), sys_gethostbyaddr() + */ +sysret_t sys_getaddrinfo(const char *nodename, + const char *servname, + const struct musl_addrinfo *hints, + struct musl_addrinfo *res) +{ + int ret = -1; + struct addrinfo *k_res = NULL; + char *k_nodename = NULL; + char *k_servname = NULL; + struct addrinfo *k_hints = NULL; +#ifdef ARCH_MM_MMU + int len = 0; +#endif + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)res, sizeof(*res))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + if (nodename) + { +#ifdef ARCH_MM_MMU + len = lwp_user_strlen(nodename); + if (len <= 0) + { + SET_ERRNO(EFAULT); + goto exit; + } + + k_nodename = (char *)kmem_get(len + 1); + if (!k_nodename) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + if (lwp_get_from_user(k_nodename, (void *)nodename, len + 1) != len + 1) + { + SET_ERRNO(EFAULT); + goto exit; + } +#else + k_nodename = rt_strdup(nodename); + if (!k_nodename) + { + SET_ERRNO(ENOMEM); + goto exit; + } +#endif + } + if (servname) + { +#ifdef ARCH_MM_MMU + len = lwp_user_strlen(servname); + if (len <= 0) + { + SET_ERRNO(EFAULT); + goto exit; + } + + k_servname = (char *)kmem_get(len + 1); + if (!k_servname) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + if (lwp_get_from_user(k_servname, (void *)servname, len + 1) < 0) + { + SET_ERRNO(EFAULT); + goto exit; + } +#else + k_servname = rt_strdup(servname); + if (!k_servname) + { + SET_ERRNO(ENOMEM); + goto exit; + } +#endif + } + + if (hints) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)hints, sizeof(*hints))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + k_hints = (struct addrinfo *)rt_malloc(sizeof *hints); + if (!k_hints) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + rt_memset(k_hints, 0x0, sizeof(struct addrinfo)); + k_hints->ai_flags = hints->ai_flags; + k_hints->ai_family = hints->ai_family; + k_hints->ai_socktype = hints->ai_socktype; + k_hints->ai_protocol = hints->ai_protocol; + k_hints->ai_addrlen = hints->ai_addrlen; + } + + ret = sal_getaddrinfo(k_nodename, k_servname, k_hints, &k_res); + if (ret == 0) + { + /* set sockaddr */ + sockaddr_tomusl(k_res->ai_addr, res->ai_addr); + res->ai_addrlen = k_res->ai_addrlen; + + /* set up addrinfo */ + res->ai_family = k_res->ai_family; + res->ai_flags = k_res->ai_flags; + res->ai_next = NULL; + + if (hints != NULL) + { + /* copy socktype & protocol from hints if specified */ + res->ai_socktype = hints->ai_socktype; + res->ai_protocol = hints->ai_protocol; + } + + sal_freeaddrinfo(k_res); + k_res = NULL; + } + +exit: + if (ret < 0) + { + ret = GET_ERRNO(); + } +#ifdef ARCH_MM_MMU + if (k_nodename) + { + kmem_put(k_nodename); + } +#else + if (k_nodename) + { + rt_free(k_nodename); + } +#endif +#ifdef ARCH_MM_MMU + if (k_servname) + { + kmem_put(k_servname); + } +#else + if (k_servname) + { + rt_free(k_servname); + } +#endif + if (k_hints) + { + rt_free(k_hints); + } + + return ret; +} + +#define HOSTENT_BUFSZ 512 + +/** + * @brief Resolves a host name to an address, with support for specifying the address family. + * + * This function performs a lookup of the specified host name, and resolves it to an address, + * while allowing the caller to specify the desired address family (e.g., IPv4 or IPv6). It is + * a reentrant version of `gethostbyname2`, meaning it is safe for use in multi-threaded applications. + * The results are returned in a user-provided buffer to avoid memory allocation overhead. + * + * @param[in] name The host name to be resolved. This can be a DNS name or an IP address in string format. + * @param[in] af The address family to use for the resolution. Common values are: + * - `AF_INET` for IPv4 addresses. + * - `AF_INET6` for IPv6 addresses. + * @param[out] ret A pointer to a `struct hostent` where the resolved host information will be stored. + * This includes the host name, alias names, address type, and the address itself. + * @param[in] buf A buffer to store additional information required for the `struct hostent` structure. + * This is needed to ensure the reentrant behavior and avoid memory allocation. + * @param[in] buflen The size of the buffer provided. + * @param[out] result A pointer to a `struct hostent*` that will point to the resolved host entry. + * This will be set to the value of `ret` upon success. + * @param[out] err A pointer to an integer where error codes will be stored. If the function fails, + * `err` will contain a non-zero value corresponding to the error. + * + * @return sysret_t Returns `0` on success, indicating the resolution was successful. + * On failure, returns a negative error code that indicates the failure reason. + * + * @note This function is reentrant and thread-safe, meaning it does not use static memory or global state. + * It relies on the buffers provided by the caller to store the resolved data. + * + * @see gethostbyname2(), sys_gethostbyname(), sys_socket(), sys_connect(), sys_getaddrinfo() + */ +sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *err) +{ + int ret_val = -1; + int sal_ret = -1, sal_err = -1; + struct hostent sal_he, sal_tmp; + struct hostent *sal_result = NULL; + char *sal_buf = NULL; + char *k_name = NULL; + int len = 0; + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)err, sizeof(*err))) + { + SET_ERRNO(EFAULT); + goto __exit; + } + + if (!lwp_user_accessable((void *)result, sizeof(*result)) || !lwp_user_accessable((void *)ret, sizeof(*ret)) || !lwp_user_accessable((void *)buf, buflen)) + { + /* not all arguments given */ + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } + + len = lwp_user_strlen(name); + if (len <= 0) + { + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } + + k_name = (char *)kmem_get(len + 1); + if (!k_name) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + if (lwp_get_from_user(k_name, (void *)name, len + 1) < 0) + { + SET_ERRNO(EFAULT); + goto __exit; + } +#else + k_name = rt_strdup(name); + if (k_name == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } +#endif + + *result = ret; + sal_buf = (char *)malloc(HOSTENT_BUFSZ); + if (sal_buf == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + /* get host by name in SAL */ + sal_ret = sal_gethostbyname_r(k_name, &sal_he, sal_buf, HOSTENT_BUFSZ, &sal_result, &sal_err); + if (sal_ret == 0) + { + int index = 0, cnt = 0; + char *ptr = buf; + + /* get counter */ + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + index++; + } + cnt = index + 1; + +#ifdef ARCH_MM_MMU + /* update user space hostent */ + lwp_put_to_user(buf, k_name, buflen - (ptr - buf)); + lwp_memcpy(&sal_tmp, &sal_he, sizeof(sal_he)); + sal_tmp.h_name = ptr; + ptr += rt_strlen(k_name); + + sal_tmp.h_addr_list = (char **)ptr; + ptr += cnt * sizeof(char *); + + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + sal_tmp.h_addr_list[index] = ptr; + lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); + + ptr += sal_he.h_length; + index++; + } + sal_tmp.h_addr_list[index] = NULL; + lwp_put_to_user(ret, &sal_tmp, sizeof(sal_tmp)); +#else + /* update user space hostent */ + ret->h_addrtype = sal_he.h_addrtype; + ret->h_length = sal_he.h_length; + + rt_strncpy(ptr, k_name, buflen - (ptr - buf)); + ret->h_name = ptr; + ptr += strlen(k_name); + + ret->h_addr_list = (char **)ptr; + ptr += cnt * sizeof(char *); + + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + ret->h_addr_list[index] = ptr; + lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); + + ptr += sal_he.h_length; + index++; + } + ret->h_addr_list[index] = NULL; +#endif + ret_val = 0; + } + else + { + SET_ERRNO(EINVAL); + } + +__exit: + if (ret_val < 0) + { + ret_val = GET_ERRNO(); + } + + /* release buffer */ + if (sal_buf) + { + free(sal_buf); + } +#ifdef ARCH_MM_MMU + if (k_name) + { + kmem_put(k_name); + } +#else + if (k_name) + { + free(k_name); + } +#endif + + return ret_val; +} +#endif |
