Add os_gethostbyname and tidy socket address to and from text form functions
Signed-off-by: Jeroen Koekkoek <jeroen@koekkoek.nl>
This commit is contained in:
parent
5835a85dc2
commit
3c0b86df9c
16 changed files with 680 additions and 184 deletions
|
@ -74,15 +74,39 @@ enum ddsi_nearby_address_result ddsi_ipaddr_is_nearby_address (ddsi_tran_factory
|
|||
|
||||
enum ddsi_locator_from_string_result ddsi_ipaddr_from_string (ddsi_tran_factory_t tran, nn_locator_t *loc, const char *str, int32_t kind)
|
||||
{
|
||||
int af = AF_INET;
|
||||
os_sockaddr_storage tmpaddr;
|
||||
int ipv4 = (kind == NN_LOCATOR_KIND_UDPv4 || kind == NN_LOCATOR_KIND_TCPv4);
|
||||
assert (kind == NN_LOCATOR_KIND_UDPv4 || kind == NN_LOCATOR_KIND_TCPv4 ||
|
||||
kind == NN_LOCATOR_KIND_TCPv6 || kind == NN_LOCATOR_KIND_UDPv6);
|
||||
(void)tran;
|
||||
if (!os_sockaddrStringToAddress (str, (os_sockaddr *) &tmpaddr, ipv4))
|
||||
return AFSR_INVALID;
|
||||
if ((ipv4 && tmpaddr.ss_family != AF_INET) || (!ipv4 && tmpaddr.ss_family != AF_INET6))
|
||||
|
||||
switch (kind) {
|
||||
case NN_LOCATOR_KIND_UDPv4:
|
||||
case NN_LOCATOR_KIND_TCPv4:
|
||||
break;
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
case NN_LOCATOR_KIND_UDPv6:
|
||||
case NN_LOCATOR_KIND_TCPv6:
|
||||
af = AF_INET6;
|
||||
break;
|
||||
#endif /* OS_SOCKET_HAS_IPV6 */
|
||||
default:
|
||||
return AFSR_MISMATCH;
|
||||
}
|
||||
|
||||
(void)tran;
|
||||
if (os_sockaddrfromstr(af, str, (os_sockaddr *) &tmpaddr) != 0) {
|
||||
#if OS_SOCKET_HAS_DNS
|
||||
/* Not a valid IP address. User may have specified a hostname instead. */
|
||||
os_hostent_t *hent = NULL;
|
||||
if (os_gethostbyname(str, af, &hent) != 0) {
|
||||
return AFSR_UNKNOWN;
|
||||
}
|
||||
memcpy(&tmpaddr, &hent->addrs[0], sizeof(hent->addrs[0]));
|
||||
#else
|
||||
return AFSR_INVALID;
|
||||
#endif /* OS_SOCKET_HAS_DNS */
|
||||
}
|
||||
if (tmpaddr.ss_family != af) {
|
||||
return AFSR_MISMATCH;
|
||||
}
|
||||
ddsi_ipaddr_to_loc (loc, (os_sockaddr *)&tmpaddr, kind);
|
||||
/* This is just an address, so there is no valid value for port, other than INVALID.
|
||||
Without a guarantee that tmpaddr has port 0, best is to set it explicitly here */
|
||||
|
@ -100,7 +124,7 @@ char *ddsi_ipaddr_to_string (ddsi_tran_factory_t tran, char *dst, size_t sizeof_
|
|||
switch (src.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
os_sockaddrAddressToString ((const os_sockaddr *) &src, dst, sizeof_dst);
|
||||
os_sockaddrtostr ((const os_sockaddr *) &src, dst, sizeof_dst);
|
||||
if (with_port) {
|
||||
pos = strlen (dst);
|
||||
assert(pos <= sizeof_dst);
|
||||
|
@ -110,7 +134,7 @@ char *ddsi_ipaddr_to_string (ddsi_tran_factory_t tran, char *dst, size_t sizeof_
|
|||
#if OS_SOCKET_HAS_IPV6
|
||||
case AF_INET6:
|
||||
dst[0] = '[';
|
||||
os_sockaddrAddressToString ((const os_sockaddr *) &src, dst + 1, sizeof_dst);
|
||||
os_sockaddrtostr ((const os_sockaddr *) &src, dst + 1, sizeof_dst);
|
||||
pos = strlen (dst);
|
||||
if (with_port) {
|
||||
assert(pos <= sizeof_dst);
|
||||
|
|
|
@ -484,7 +484,7 @@ static char *ddsi_udp_locator_to_string (ddsi_tran_factory_t tran, char *dst, si
|
|||
memset (&src, 0, sizeof (src));
|
||||
src.sin_family = AF_INET;
|
||||
memcpy (&src.sin_addr.s_addr, &mcgen.ipv4, 4);
|
||||
os_sockaddrAddressToString ((const os_sockaddr *) &src, dst, sizeof_dst);
|
||||
os_sockaddrtostr ((const os_sockaddr *) &src, dst, sizeof_dst);
|
||||
pos = strlen (dst);
|
||||
assert (pos <= sizeof_dst);
|
||||
cnt = snprintf (dst + pos, sizeof_dst - pos, ";%u;%u;%u", mcgen.base, mcgen.count, mcgen.idx);
|
||||
|
|
|
@ -25,7 +25,8 @@ set(sources
|
|||
src/os_time.c
|
||||
src/os_errno.c
|
||||
src/os_iter.c
|
||||
src/os_strlcpy.c)
|
||||
src/os_strlcpy.c
|
||||
src/os_dns.c)
|
||||
|
||||
string(TOLOWER ${CMAKE_SYSTEM_NAME} system_name)
|
||||
set(system_sources
|
||||
|
|
|
@ -22,7 +22,20 @@ extern "C" {
|
|||
#include <winerror.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h> /* Required on Windows platforms too */
|
||||
#include <errno.h> /* Required on Windows platforms too. */
|
||||
|
||||
|
||||
/* DNS runtime functions require custom error codes to be defined. For POSIX
|
||||
platforms a high enough 32-bit number (not too close to any system specified
|
||||
error number) should be safe. Microsoft Windows error codes, however, are
|
||||
encoded after the HRESULT (https://en.wikipedia.org/wiki/HRESULT) scheme. To
|
||||
avoid clashes the customer code bit (0x20000000) must be set. */
|
||||
#define OS_ERRBASE (0x20000000)
|
||||
|
||||
#define OS_HOST_NOT_FOUND (OS_ERRBASE + 1)
|
||||
#define OS_NO_DATA (OS_ERRBASE + 2)
|
||||
#define OS_NO_RECOVERY (OS_ERRBASE + 3)
|
||||
#define OS_TRY_AGAIN (OS_ERRBASE + 4)
|
||||
|
||||
/** \brief Get error code set by last operation that failed
|
||||
*
|
||||
|
@ -41,6 +54,28 @@ extern "C" {
|
|||
os_setErrno (
|
||||
int err);
|
||||
|
||||
/**
|
||||
* \brief Return string describing internal error number.
|
||||
*
|
||||
* @param[in] errnum
|
||||
* @param[out] buf
|
||||
* @param[in] buflen
|
||||
*
|
||||
* @returns 0 on success or a valid error number on failure.
|
||||
*
|
||||
* @retval 0
|
||||
* Success. The error message is copied to the buffer.
|
||||
* @retval EINVAL
|
||||
* Not a valid internal error code.
|
||||
* @retval ERANGE
|
||||
* Buffer is not large enough to store the error message.
|
||||
*/
|
||||
OSAPI_EXPORT int _Success_(return == 0)
|
||||
os_errstr(
|
||||
_In_ int errnum,
|
||||
_Out_writes_opt_z_(buflen) char *buf,
|
||||
_In_ size_t buflen);
|
||||
|
||||
/**
|
||||
* \brief Get description for specified error code
|
||||
*
|
||||
|
|
|
@ -48,7 +48,7 @@ extern "C" {
|
|||
typedef struct sockaddr os_sockaddr;
|
||||
typedef struct sockaddr_storage os_sockaddr_storage;
|
||||
|
||||
#if defined(OS_SOCKET_HAS_IPV6) && OS_SOCKET_HAS_IPV6 == 1
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
typedef struct ipv6_mreq os_ipv6_mreq;
|
||||
typedef struct in6_addr os_in6_addr;
|
||||
|
||||
|
@ -243,37 +243,83 @@ extern "C" {
|
|||
const os_sockaddr* thatSock,
|
||||
const os_sockaddr* mask);
|
||||
|
||||
/**
|
||||
* Convert a socket address to a string format presentation representation
|
||||
* @param sa The socket address struct.
|
||||
* @param buffer A character buffer to hold the string rep of the address.
|
||||
* @param buflen The (max) size of the buffer
|
||||
* @return Pointer to start of string
|
||||
*/
|
||||
OSAPI_EXPORT char*
|
||||
os_sockaddrAddressToString(const os_sockaddr* sa,
|
||||
char* buffer, size_t buflen);
|
||||
#ifdef OS_SOCKET_HAS_DNS
|
||||
|
||||
typedef struct {
|
||||
size_t naddrs;
|
||||
os_sockaddr_storage addrs[];
|
||||
} os_hostent_t;
|
||||
|
||||
/**
|
||||
* Convert the provided addressString into a os_sockaddr.
|
||||
* Lookup addresses for given host name.
|
||||
*
|
||||
* @param addressString The string representation of a network address.
|
||||
* @param addressOut A pointer to an os_sockaddr. Must be big enough for
|
||||
* the address type specified by the string. This implies it should
|
||||
* generally be the address of an os_sockaddr_storage for safety's sake.
|
||||
* @param isIPv4 If the addressString is a hostname specifies whether
|
||||
* and IPv4 address should be returned. If false an Ipv6 address will be
|
||||
* requested. If the address is in either valid decimal presentation format
|
||||
* param will be ignored.
|
||||
* @return true on successful conversion. false otherwise
|
||||
* @param[in] name Host name to resolve.
|
||||
* @param[in] af Address family, either AF_INET, AF_INET6 or AF_UNSPEC.
|
||||
* @param[out] hent Structure of type os_hostent_t.
|
||||
*
|
||||
* @returns 0 on success or valid error number on failure.
|
||||
*
|
||||
* @retval 0
|
||||
* Success. Host name successfully resolved to address(es).
|
||||
* @retval OS_HOST_NOT_FOUND
|
||||
* Host not found.
|
||||
* @retval OS_NO_DATA
|
||||
* Valid name, no data record of requested type.
|
||||
* @retval OS_NO_RECOVERY
|
||||
* Nonrecoverable error.
|
||||
* @retval OS_TRY_AGAIN
|
||||
* Nonauthoratitative host not found.
|
||||
*/
|
||||
_Success_(return) OSAPI_EXPORT bool
|
||||
os_sockaddrStringToAddress(
|
||||
_In_z_ const char *addressString,
|
||||
_When_(isIPv4, _Out_writes_bytes_(sizeof(os_sockaddr_in)))
|
||||
_When_(!isIPv4, _Out_writes_bytes_(sizeof(os_sockaddr_in6)))
|
||||
os_sockaddr *addressOut,
|
||||
_In_ bool isIPv4);
|
||||
OSAPI_EXPORT _Success_(return == 0) int
|
||||
os_gethostbyname(
|
||||
_In_z_ const char *name,
|
||||
_In_ int af,
|
||||
_Out_ os_hostent_t **hent);
|
||||
|
||||
#endif /* OS_SOCKET_HAS_DNS */
|
||||
|
||||
/**
|
||||
* Convert IPv4 and IPv6 addresses from text to socket address.
|
||||
*
|
||||
* @param af[in] Address family, either AF_INET or AF_INET6.
|
||||
* @param str[in] Network address in text form.
|
||||
* @param sa[out] Pointer to a sufficiently large enough socket address
|
||||
* structure. This implies it should generally be the
|
||||
* address to a structure of type struct sockaddr_storage.
|
||||
*
|
||||
* @return 0 on success or a valid error number on failure.
|
||||
*/
|
||||
OSAPI_EXPORT _Success_(return) int
|
||||
os_sockaddrfromstr(
|
||||
_In_ int af,
|
||||
_In_z_ const char *str,
|
||||
_When_(af == AF_INET, _Out_writes_bytes_(sizeof(os_sockaddr_in)))
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
_When_(af == AF_INET6, _Out_writes_bytes_(sizeof(os_sockaddr_in6)))
|
||||
#endif
|
||||
void *sa);
|
||||
|
||||
/**
|
||||
* Convert a socket address to text form.
|
||||
*
|
||||
* @param[in] sa Socket address structure.
|
||||
* @param[out] buf Buffer to which resulting string is copied.
|
||||
* @param[in] size Number of bytes available in the buffer.
|
||||
*
|
||||
* @returns 0 on success or a valid error number on failure.
|
||||
*
|
||||
* @retval 0
|
||||
* Success. Socket address structure converted to text form.
|
||||
* @retval EAFNOSUPPORT
|
||||
* Socket address structure of unsupported valid address family.
|
||||
* @retval ENOSPC
|
||||
* Text form would exceed the size specified by size.
|
||||
*/
|
||||
OSAPI_EXPORT _Success_(return == 0) int
|
||||
os_sockaddrtostr(
|
||||
_In_ const void *sa,
|
||||
_Out_writes_z_(size) char *buf,
|
||||
_In_ size_t size);
|
||||
|
||||
/* docced in implementation file */
|
||||
OSAPI_EXPORT bool
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sockio.h>
|
||||
|
@ -36,6 +37,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/* Keep defines before common header */
|
||||
#define OS_SOCKET_HAS_DNS 1
|
||||
#define OS_SOCKET_HAS_IPV6 1
|
||||
#define OS_SOCKET_HAS_SA_LEN 1
|
||||
#define OS_NO_SIOCGIFINDEX 1
|
||||
|
|
|
@ -25,6 +25,7 @@ extern "C" {
|
|||
|
||||
/* Keep defines before common header */
|
||||
#define OS_SOCKET_HAS_IPV6 1
|
||||
#define OS_SOCKET_HAS_DNS 1
|
||||
#define OS_SOCKET_HAS_SA_LEN 0
|
||||
#define OS_NO_SIOCGIFINDEX 1
|
||||
#define OS_NO_NETLINK 1
|
||||
|
|
146
src/os/src/os_dns.c
Normal file
146
src/os/src/os_dns.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "os/os.h"
|
||||
|
||||
int
|
||||
os_gethostbyname(
|
||||
const char *name,
|
||||
int af,
|
||||
os_hostent_t **hentp)
|
||||
{
|
||||
int err = 0;
|
||||
int gai_err = 0;
|
||||
struct addrinfo hints, *res = NULL;
|
||||
os_hostent_t *hent = NULL;
|
||||
|
||||
assert(name != NULL);
|
||||
assert(hentp != NULL);
|
||||
|
||||
switch (af) {
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
case AF_INET6:
|
||||
#endif
|
||||
case AF_INET:
|
||||
case AF_UNSPEC:
|
||||
break;
|
||||
default:
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
/* Windows returns all registered addresses on the local computer if the
|
||||
"nodename" parameter is an empty string. *NIX return HOST_NOT_FOUND.
|
||||
Deny empty hostnames to keep behavior across platforms consistent. */
|
||||
if (strlen(name) == 0) {
|
||||
return OS_HOST_NOT_FOUND;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
|
||||
gai_err = getaddrinfo(name, NULL, &hints, &res);
|
||||
/* gai_strerror cannot be used because Windows does not offer a thread-safe
|
||||
implementation and lwIP (there maybe others as well) does not offer an
|
||||
implementation at all.
|
||||
|
||||
NOTE: Error codes returned by getaddrinfo map directly onto Windows
|
||||
Socket error codes and WSAGetLastError can be used instead. */
|
||||
DDS_TRACE("getaddrinfo for %s returned %d\n", name, gai_err);
|
||||
switch (gai_err) {
|
||||
#if defined(EAI_AGAIN)
|
||||
case EAI_AGAIN:
|
||||
/* Name server returned a temporary failure indication. */
|
||||
err = OS_TRY_AGAIN;
|
||||
break;
|
||||
#endif
|
||||
case EAI_FAIL:
|
||||
/* Name server returned a permanent failure indication. */
|
||||
err = OS_NO_RECOVERY;
|
||||
break;
|
||||
/* Windows defines EAI_NODATA to EAI_NONAME. */
|
||||
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
|
||||
case EAI_NODATA:
|
||||
/* Host exists, but does not have any network addresses defined. */
|
||||
err = OS_NO_DATA;
|
||||
break;
|
||||
#endif
|
||||
#if defined(EAI_ADDRFAMILY)
|
||||
case EAI_ADDRFAMILY: /* Host has no addresses in requested family. */
|
||||
#endif
|
||||
#if defined(EAI_NOSECURENAME) /* Windows */
|
||||
case EAI_NOSECURENAME:
|
||||
#endif
|
||||
case EAI_NONAME:
|
||||
/* Host does not exist. */
|
||||
err = OS_HOST_NOT_FOUND;
|
||||
break;
|
||||
case EAI_MEMORY:
|
||||
/* Out of memory. */
|
||||
err = ENOMEM;
|
||||
break;
|
||||
#if defined(EAI_SYSTEM)
|
||||
case EAI_SYSTEM:
|
||||
/* Other system error. */
|
||||
err = errno;
|
||||
break;
|
||||
#endif
|
||||
case EAI_BADFLAGS: /* Invalid flags in hints.ai_flags. */
|
||||
case EAI_FAMILY: /* Address family not supported. */
|
||||
case EAI_SERVICE: /* Service not available for socket type. */
|
||||
case EAI_SOCKTYPE: /* Socket type not supported. */
|
||||
case 0:
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
size_t addrno, naddrs, size;
|
||||
|
||||
assert(gai_err == 0);
|
||||
assert(res != NULL);
|
||||
|
||||
naddrs = 0;
|
||||
for (ai = res; ai != NULL; ai = ai->ai_next) {
|
||||
naddrs++;
|
||||
}
|
||||
|
||||
size = sizeof(*hent) + (naddrs * sizeof(hent->addrs[0]));
|
||||
if ((hent = os_malloc_0_s(size)) != NULL) {
|
||||
hent->naddrs = naddrs;
|
||||
for (addrno = 0, ai = res;
|
||||
addrno < naddrs && ai != NULL;
|
||||
addrno++, ai = ai->ai_next)
|
||||
{
|
||||
memcpy(&hent->addrs[addrno], res->ai_addr, res->ai_addrlen);
|
||||
}
|
||||
} else {
|
||||
err = ENOMEM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DDS_FATAL("getaddrinfo returned unkown error %d\n", gai_err);
|
||||
}
|
||||
|
||||
if (res != NULL) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
*hentp = hent;
|
||||
} else {
|
||||
os_free(hent);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
|
@ -15,6 +15,40 @@
|
|||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct { int err; const char *str; } errstrs[] =
|
||||
{
|
||||
{ OS_HOST_NOT_FOUND, "Host not found" },
|
||||
{ OS_NO_DATA, "Valid name, no data record of requested type" },
|
||||
{ OS_NO_RECOVERY, "Nonrecoverable error" },
|
||||
{ OS_TRY_AGAIN, "Nonauthoritative host not found" },
|
||||
};
|
||||
|
||||
int os_errstr(_In_ int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int err = 0;
|
||||
const char *str = NULL;
|
||||
unsigned idx = (unsigned)(errnum - (OS_ERRBASE + 1));
|
||||
static const unsigned max = (unsigned)(sizeof(errstrs)/sizeof(errstrs[0]));
|
||||
|
||||
if (errnum > OS_ERRBASE && idx < max) {
|
||||
size_t len;
|
||||
assert(errstrs[idx].err == errnum);
|
||||
str = errstrs[idx].str;
|
||||
assert(str != NULL);
|
||||
len = strlen(str);
|
||||
if (len < buflen) {
|
||||
memcpy(buf, str, len);
|
||||
buf[len] = '\0';
|
||||
} else {
|
||||
err = ERANGE;
|
||||
}
|
||||
} else {
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#define MIN_BUFLEN (64)
|
||||
#define MAX_BUFLEN (1024)
|
||||
|
||||
|
|
|
@ -37,14 +37,6 @@ const os_in6_addr os_in6addr_loopback = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef OS_INET_NTOP
|
||||
#define OS_INET_NTOP inet_ntop
|
||||
#endif
|
||||
|
||||
#ifndef OS_INET_PTON
|
||||
#define OS_INET_PTON inet_pton
|
||||
#endif
|
||||
|
||||
const int afs[] = {
|
||||
#ifdef __linux
|
||||
AF_PACKET,
|
||||
|
@ -220,57 +212,99 @@ os_sockaddrSameSubnet(const os_sockaddr* thisSock,
|
|||
return result;
|
||||
}
|
||||
|
||||
#if WIN32
|
||||
/*
|
||||
* gai_strerror under Windows is not thread safe. See getaddrinfo on MSDN:
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520.aspx
|
||||
*
|
||||
* The error codes that getaddrinfo returns map directly onto WSA error codes.
|
||||
* os_strerror_r can therefore safely be used to retrieve their description.
|
||||
*/
|
||||
#define os_gai_strerror(errnum) os_strerror(errnum)
|
||||
#else
|
||||
#define os_gai_strerror(errnum) gai_strerror(errnum)
|
||||
#endif /* WIN32 */
|
||||
|
||||
_Success_(return) bool
|
||||
os_sockaddrStringToAddress(
|
||||
_In_z_ const char *addressString,
|
||||
_When_(isIPv4, _Out_writes_bytes_(sizeof(os_sockaddr_in)))
|
||||
_When_(!isIPv4, _Out_writes_bytes_(sizeof(os_sockaddr_in6)))
|
||||
os_sockaddr *addressOut,
|
||||
_In_ bool isIPv4)
|
||||
int _Success_(return == 0)
|
||||
os_sockaddrfromstr(
|
||||
_In_ int af,
|
||||
_In_z_ const char *str,
|
||||
_When_(af == AF_INET, _Out_writes_bytes(sizeof(os_sockaddr_in)))
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
_When_(af == AF_INET6, _Out_writes_bytes(sizeof(os_sockaddr_in6)))
|
||||
#endif /* OS_SOCKET_HAS_IPV6 */
|
||||
void *sa)
|
||||
{
|
||||
int ret;
|
||||
const char *fmt;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res = NULL;
|
||||
int err = 0;
|
||||
|
||||
assert(addressString != NULL);
|
||||
assert(addressOut != NULL);
|
||||
assert(str != NULL);
|
||||
assert(sa != NULL);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
#if (OS_SOCKET_HAS_IPV6 == 1)
|
||||
hints.ai_family = (isIPv4 ? AF_INET : AF_INET6);
|
||||
#else
|
||||
hints.ai_family = AF_INET;
|
||||
OS_UNUSED_ARG(isIPv4);
|
||||
#endif /* IPv6 */
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
ret = getaddrinfo(addressString, NULL, &hints, &res);
|
||||
if (ret != 0) {
|
||||
fmt = "getaddrinfo(\"%s\") failed: %s";
|
||||
DDS_TRACE(fmt, addressString, os_gai_strerror(ret));
|
||||
} else if (res != NULL) {
|
||||
memcpy(addressOut, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
{
|
||||
struct in_addr buf;
|
||||
if (inet_pton(af, str, &buf) != 1) {
|
||||
err = EINVAL;
|
||||
} else {
|
||||
fmt = "getaddrinfo(\"%s\") did not return any results";
|
||||
DDS_TRACE(fmt, addressString);
|
||||
memset(sa, 0, sizeof(os_sockaddr_in));
|
||||
((os_sockaddr_in *)sa)->sin_family = AF_INET;
|
||||
memcpy(&((os_sockaddr_in *)sa)->sin_addr, &buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
case AF_INET6:
|
||||
{
|
||||
struct in6_addr buf;
|
||||
if (inet_pton(af, str, &buf) != 1) {
|
||||
err = EINVAL;
|
||||
} else {
|
||||
memset(sa, 0, sizeof(os_sockaddr_in6));
|
||||
((os_sockaddr_in6 *)sa)->sin6_family = AF_INET6;
|
||||
memcpy(&((os_sockaddr_in6 *)sa)->sin6_addr, &buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* OS_SOCKET_HAS_IPV6 */
|
||||
default:
|
||||
err = EAFNOSUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret == 0 && res != NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
int _Success_(return == 0)
|
||||
os_sockaddrtostr(
|
||||
_In_ const void *sa,
|
||||
_Out_writes_z_(size) char *buf,
|
||||
_In_ size_t size)
|
||||
{
|
||||
int err = 0;
|
||||
const char *ptr;
|
||||
|
||||
assert(sa != NULL);
|
||||
assert(buf != NULL);
|
||||
|
||||
switch (((os_sockaddr *)sa)->sa_family) {
|
||||
case AF_INET:
|
||||
ptr = inet_ntop(
|
||||
AF_INET, &((os_sockaddr_in *)sa)->sin_addr, buf, (socklen_t)size);
|
||||
break;
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
case AF_INET6:
|
||||
ptr = inet_ntop(
|
||||
AF_INET6, &((os_sockaddr_in6 *)sa)->sin6_addr, buf, (socklen_t)size);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
if (ptr == NULL) {
|
||||
#if WIN32
|
||||
err = GetLastError();
|
||||
if (ERROR_INVALID_PARAMETER) {
|
||||
/* ERROR_INVALID_PARAMETER is returned if the buffer is a null
|
||||
pointer or the size of the buffer is not sufficiently large
|
||||
enough. *NIX platforms set errno to ENOSPC if the buffer is not
|
||||
large enough to store the IP address in text form. */
|
||||
err = ENOSPC;
|
||||
}
|
||||
#else
|
||||
err = errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -290,7 +324,7 @@ os_sockaddrIsLoopback(const os_sockaddr* thisSock)
|
|||
if (linkLocalLoopbackPtr == NULL)
|
||||
{
|
||||
/* Initialise once (where 'once' implies some small integer) */
|
||||
os_sockaddrStringToAddress("fe80::1", (os_sockaddr*) &linkLocalLoopback, false /* ! ipv4 */ );
|
||||
os_sockaddrfromstr(AF_INET6, "fe80::1", (os_sockaddr*) &linkLocalLoopback);
|
||||
linkLocalLoopbackPtr = (os_sockaddr*) &linkLocalLoopback;
|
||||
}
|
||||
|
||||
|
@ -328,28 +362,3 @@ os_sockaddrSetInAddrAny(
|
|||
((os_sockaddr_in*)sa)->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
os_sockaddrAddressToString(const os_sockaddr* sa,
|
||||
char* buffer, size_t buflen)
|
||||
{
|
||||
assert (buflen <= 0x7fffffff);
|
||||
|
||||
switch(sa->sa_family) {
|
||||
case AF_INET:
|
||||
OS_INET_NTOP(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
|
||||
buffer, (socklen_t) buflen);
|
||||
break;
|
||||
#if (OS_SOCKET_HAS_IPV6 == 1)
|
||||
case AF_INET6:
|
||||
OS_INET_NTOP(AF_INET6, &(((os_sockaddr_in6 *)sa)->sin6_addr),
|
||||
buffer, (socklen_t) buflen);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
(void) snprintf(buffer, buflen, "Unknown address family");
|
||||
break;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -33,16 +33,18 @@ os_setErrno (int err)
|
|||
}
|
||||
|
||||
int
|
||||
os_strerror_r (int err, char *str, size_t len)
|
||||
os_strerror_r (int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int res;
|
||||
int err;
|
||||
|
||||
assert(str != NULL);
|
||||
assert(len > 0);
|
||||
assert(buf != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
str[0] = '\0'; /* null-terminate in case nothing is written */
|
||||
res = strerror_r(err, str, len);
|
||||
str[len - 1] = '\0'; /* always null-terminate, just to be safe */
|
||||
if ((err = os_errstr(errnum, buf, buflen)) == EINVAL) {
|
||||
buf[0] = '\0'; /* null-terminate in case nothing is written */
|
||||
err = strerror_r(errnum, buf, buflen);
|
||||
buf[buflen - 1] = '\0'; /* always null-terminate, just to be safe */
|
||||
}
|
||||
|
||||
return res;
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -39,44 +39,49 @@ strerrorIf(
|
|||
details. NAME_MAX is defined in limits.h. */
|
||||
|
||||
int
|
||||
os_strerror_r(int err, char *str, size_t len)
|
||||
os_strerror_r(int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int res = EINVAL;
|
||||
int err;
|
||||
const char *str;
|
||||
|
||||
assert(str != NULL);
|
||||
assert(buf != NULL);
|
||||
|
||||
if (len < (NAME_MAX + 1)) {
|
||||
res = ERANGE;
|
||||
} else if (strerrorIf(err) != NULL) {
|
||||
(void)strerror_r(err, str);
|
||||
if ((err = os_errstr(errnum, buf, buflen)) == EINVAL) {
|
||||
err = 0;
|
||||
if (buflen < (NAME_MAX + 1)) {
|
||||
err = ERANGE;
|
||||
} else if (strerrorIf(errnum) != NULL) {
|
||||
(void)strerror_r(errnum, buf);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return err;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
os_strerror_r(int err, char *str, size_t len)
|
||||
os_strerror_r(int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int res = 0;
|
||||
int err;
|
||||
const char *str;
|
||||
|
||||
assert(str != NULL);
|
||||
assert(buf != NULL);
|
||||
|
||||
/* VxWorks's strerror_r always returns 0 (zero), so the only way to decide
|
||||
if the error was truncated is to check if the last position in the
|
||||
buffer is overwritten by strerror_r. */
|
||||
str[len - 1] = 'x';
|
||||
|
||||
(void)strerror_r (err, str, len);
|
||||
|
||||
if (str[len - 1] != 'x') {
|
||||
res = ERANGE;
|
||||
if ((err = os_errstr(errnum, buf, buflen)) == EINVAL) {
|
||||
/* VxWorks's strerror_r always returns 0 (zero), so the only way to
|
||||
decide if the error was truncated is to check if the last position
|
||||
in the buffer is overwritten by strerror_r. */
|
||||
err = 0;
|
||||
buf[buflen - 1] = 'x';
|
||||
(void)strerror_r(errnum, buf, buflen);
|
||||
if (buf[buflen - 1] != 'x') {
|
||||
err = ERANGE;
|
||||
}
|
||||
buf[buflen - 1] = '\0'; /* Always null terminate, just to be safe. */
|
||||
}
|
||||
|
||||
str[len - 1] = '\0'; /* always null terminate, just to be safe */
|
||||
|
||||
return res;
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* _WRS_KERNEL */
|
||||
|
|
|
@ -51,43 +51,46 @@ os_setErrno(int err)
|
|||
|
||||
int
|
||||
os_strerror_r(
|
||||
_In_ int err,
|
||||
_Out_writes_z_(len) char *str,
|
||||
_In_ size_t len)
|
||||
_In_ int errnum,
|
||||
_Out_writes_z_(buflen) char *buf,
|
||||
_In_ size_t buflen)
|
||||
{
|
||||
int res = 0, errs[2];
|
||||
int err = 0, errs[2];
|
||||
DWORD cnt;
|
||||
|
||||
assert(str != NULL);
|
||||
assert(len > 0);
|
||||
assert(buf != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
str[0] = '\0'; /* null-terminate in case nothing is written */
|
||||
if ((err = os_errstr(errnum, buf, buflen)) == EINVAL) {
|
||||
err = 0;
|
||||
buf[0] = '\0'; /* Null-terminate in case nothing is written. */
|
||||
errs[0] = os_getErrno();
|
||||
cnt = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
NULL,
|
||||
err,
|
||||
errnum,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)str,
|
||||
(DWORD)len,
|
||||
(LPTSTR)buf,
|
||||
(DWORD)buflen,
|
||||
NULL);
|
||||
|
||||
errs[1] = os_getErrno();
|
||||
if (cnt == 0) {
|
||||
if (errs[1] == ERROR_MORE_DATA) {
|
||||
res = ERANGE;
|
||||
err = ERANGE;
|
||||
} else {
|
||||
res = EINVAL;
|
||||
err = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* os_strerror_r should not modify errno itself */
|
||||
/* os_strerror_r should not modify errno itself. */
|
||||
if (errs[0] != errs[1]) {
|
||||
os_setErrno(errs[0]);
|
||||
}
|
||||
|
||||
str[len - 1] = '\0'; /* always null-terminate, just to be safe */
|
||||
buf[buflen - 1] = '\0'; /* Always null-terminate, just to be safe. */
|
||||
}
|
||||
|
||||
return res;
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ set(sources
|
|||
"thread_cleanup.c"
|
||||
"strcasecmp.c"
|
||||
"log.c"
|
||||
"strlcpy.c")
|
||||
"strlcpy.c"
|
||||
"socket.c")
|
||||
|
||||
add_cunit_executable(cunit_abstraction ${sources})
|
||||
target_link_libraries(cunit_abstraction OSAPI)
|
||||
|
|
|
@ -45,3 +45,23 @@ CU_Test(os_errno, get_and_set)
|
|||
|
||||
printf ("Ending tc_os_errno\n");
|
||||
}
|
||||
|
||||
CU_Test(os_errstr, no_space)
|
||||
{
|
||||
int err;
|
||||
char buf[1] = { 0 };
|
||||
err = os_errstr(OS_HOST_NOT_FOUND, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, ERANGE);
|
||||
}
|
||||
|
||||
/* os_errstr only provides string representations for internal error codes. */
|
||||
CU_Test(os_errstr, bad_errno)
|
||||
{
|
||||
int err;
|
||||
char buf[128];
|
||||
buf[0] = '\0';
|
||||
err = os_errstr(OS_ERRBASE, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, EINVAL);
|
||||
err = os_errstr(EINVAL, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, EINVAL);
|
||||
}
|
||||
|
|
167
src/os/tests/socket.c
Normal file
167
src/os/tests/socket.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CUnit/Theory.h"
|
||||
|
||||
#include "os/os.h"
|
||||
|
||||
OS_WARNING_MSVC_OFF(4305)
|
||||
#if OS_ENDIANNESS == OS_BIG_ENDIAN
|
||||
static const struct sockaddr_in ipv4_loopback =
|
||||
{ .sin_family = AF_INET, .sin_addr = { .s_addr = 0x7f000001 } };
|
||||
#else
|
||||
static const struct sockaddr_in ipv4_loopback =
|
||||
{ .sin_family = AF_INET, .sin_addr = { .s_addr = 0x0100007f } };
|
||||
#endif /* OS_ENDIANNESS */
|
||||
OS_WARNING_MSVC_ON(4305)
|
||||
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
static const struct sockaddr_in6 ipv6_loopback =
|
||||
{ .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_LOOPBACK_INIT };
|
||||
#endif
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
os_osInit();
|
||||
}
|
||||
|
||||
static void teardown(void)
|
||||
{
|
||||
os_osExit();
|
||||
}
|
||||
|
||||
CU_Test(os_sockaddrfromstr, bad_family)
|
||||
{
|
||||
int err;
|
||||
os_sockaddr_storage sa;
|
||||
err = os_sockaddrfromstr(AF_UNSPEC, "127.0.0.1", &sa);
|
||||
CU_ASSERT_EQUAL(err, EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
static void sockaddrfromstr_test(char *str, int af, int ret)
|
||||
{
|
||||
int err;
|
||||
os_sockaddr_storage ss;
|
||||
err = os_sockaddrfromstr(af, str, &ss);
|
||||
CU_ASSERT_EQUAL(err, ret);
|
||||
if (err == 0) {
|
||||
CU_ASSERT_EQUAL(ss.ss_family, af);
|
||||
}
|
||||
}
|
||||
|
||||
CU_TheoryDataPoints(os_sockaddrfromstr, ipv4) = {
|
||||
CU_DataPoints(char *, "127.0.0.1", "0.0.0.0", "nip"),
|
||||
CU_DataPoints(int, AF_INET, AF_INET, AF_INET),
|
||||
CU_DataPoints(int, 0, 0, EINVAL)
|
||||
};
|
||||
|
||||
CU_Theory((char *str, int af, int ret), os_sockaddrfromstr, ipv4, .init=setup, .fini=teardown)
|
||||
{
|
||||
sockaddrfromstr_test(str, af, ret);
|
||||
}
|
||||
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
CU_TheoryDataPoints(os_sockaddrfromstr, ipv6) = {
|
||||
CU_DataPoints(char *, "127.0.0.1", "::1", "::1", "::", "nip"),
|
||||
CU_DataPoints(int, AF_INET6, AF_INET6, AF_INET, AF_INET6, AF_INET6),
|
||||
CU_DataPoints(int, EINVAL, 0, EINVAL, 0, EINVAL)
|
||||
};
|
||||
|
||||
CU_Theory((char *str, int af, int ret), os_sockaddrfromstr, ipv6, .init=setup, .fini=teardown)
|
||||
{
|
||||
sockaddrfromstr_test(str, af, ret);
|
||||
}
|
||||
#endif /* OS_SOCKET_HAS_IPV6 */
|
||||
|
||||
CU_Test(os_sockaddrtostr, bad_sockaddr, .init=setup, .fini=teardown)
|
||||
{
|
||||
int err;
|
||||
char buf[128] = { 0 };
|
||||
os_sockaddr_in sa;
|
||||
memcpy(&sa, &ipv4_loopback, sizeof(ipv4_loopback));
|
||||
sa.sin_family = AF_UNSPEC;
|
||||
err = os_sockaddrtostr(&sa, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
CU_Test(os_sockaddrtostr, no_space, .init=setup, .fini=teardown)
|
||||
{
|
||||
int err;
|
||||
char buf[1] = { 0 };
|
||||
err = os_sockaddrtostr(&ipv4_loopback, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, ENOSPC);
|
||||
}
|
||||
|
||||
CU_Test(os_sockaddrtostr, ipv4)
|
||||
{
|
||||
int err;
|
||||
char buf[128] = { 0 };
|
||||
err = os_sockaddrtostr(&ipv4_loopback, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, 0);
|
||||
CU_ASSERT_STRING_EQUAL(buf, "127.0.0.1");
|
||||
}
|
||||
|
||||
CU_Test(os_sockaddrtostr, ipv6)
|
||||
{
|
||||
int err;
|
||||
char buf[128] = { 0 };
|
||||
err = os_sockaddrtostr(&ipv6_loopback, buf, sizeof(buf));
|
||||
CU_ASSERT_EQUAL(err, 0);
|
||||
CU_ASSERT_STRING_EQUAL(buf, "::1");
|
||||
}
|
||||
|
||||
#if OS_SOCKET_HAS_DNS
|
||||
static void gethostbyname_test(char *name, int af, int ret)
|
||||
{
|
||||
int err;
|
||||
os_hostent_t *hent = NULL;
|
||||
err = os_gethostbyname(name, af, &hent);
|
||||
CU_ASSERT_EQUAL(err, ret);
|
||||
if (err == 0) {
|
||||
CU_ASSERT_FATAL(hent->naddrs > 0);
|
||||
if (af != AF_UNSPEC) {
|
||||
CU_ASSERT_EQUAL(hent->addrs[0].ss_family, af);
|
||||
}
|
||||
}
|
||||
os_free(hent);
|
||||
}
|
||||
|
||||
CU_TheoryDataPoints(os_gethostbyname, ipv4) = {
|
||||
CU_DataPoints(char *, "", "_nah", "127.0.0.1", "127.0.0.1"),
|
||||
CU_DataPoints(int, AF_UNSPEC, AF_UNSPEC, AF_INET, AF_UNSPEC),
|
||||
CU_DataPoints(int, OS_HOST_NOT_FOUND, OS_HOST_NOT_FOUND, 0, 0)
|
||||
};
|
||||
|
||||
CU_Theory((char *name, int af, int ret), os_gethostbyname, ipv4, .init=setup, .fini=teardown)
|
||||
{
|
||||
gethostbyname_test(name, af, ret);
|
||||
}
|
||||
|
||||
#if OS_SOCKET_HAS_IPV6
|
||||
/* Lookup of IPv4 address and specifying AF_INET6 is not invalid as it may
|
||||
return an IPV4-mapped IPv6 address. */
|
||||
CU_TheoryDataPoints(os_gethostbyname, ipv6) = {
|
||||
CU_DataPoints(char *, "::1", "::1", "::1"),
|
||||
CU_DataPoints(int, AF_INET, AF_INET6, AF_UNSPEC),
|
||||
CU_DataPoints(int, OS_HOST_NOT_FOUND, 0, 0)
|
||||
};
|
||||
|
||||
CU_Theory((char *name, int af, int ret), os_gethostbyname, ipv6, .init=setup, .fini=teardown)
|
||||
{
|
||||
gethostbyname_test(name, af, ret);
|
||||
}
|
||||
#endif /* OS_SOCKET_HAS_IPV6 */
|
||||
#endif /* OS_SOCKET_HAS_DNS */
|
Loading…
Add table
Add a link
Reference in a new issue