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:
Jeroen Koekkoek 2019-01-02 16:55:07 +01:00
parent 5835a85dc2
commit 3c0b86df9c
16 changed files with 680 additions and 184 deletions

View file

@ -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);
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_sockaddrStringToAddress (str, (os_sockaddr *) &tmpaddr, ipv4))
return AFSR_INVALID;
if ((ipv4 && tmpaddr.ss_family != AF_INET) || (!ipv4 && tmpaddr.ss_family != AF_INET6))
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);

View file

@ -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);

View file

@ -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

View file

@ -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
*

View file

@ -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.
*
* @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
*/
_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);
* Lookup addresses for given host name.
*
* @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.
*/
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

View file

@ -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

View file

@ -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
View 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;
}

View file

@ -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)

View file

@ -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);
} else {
fmt = "getaddrinfo(\"%s\") did not return any results";
DDS_TRACE(fmt, addressString);
switch (af) {
case AF_INET:
{
struct in_addr buf;
if (inet_pton(af, str, &buf) != 1) {
err = EINVAL;
} else {
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;
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 */
errs[0] = os_getErrno();
cnt = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)str,
(DWORD)len,
NULL);
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,
errnum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)buf,
(DWORD)buflen,
NULL);
errs[1] = os_getErrno();
if (cnt == 0) {
if (errs[1] == ERROR_MORE_DATA) {
res = ERANGE;
} else {
res = EINVAL;
errs[1] = os_getErrno();
if (cnt == 0) {
if (errs[1] == ERROR_MORE_DATA) {
err = ERANGE;
} else {
err = EINVAL;
}
}
/* os_strerror_r should not modify errno itself. */
if (errs[0] != errs[1]) {
os_setErrno(errs[0]);
}
buf[buflen - 1] = '\0'; /* Always null-terminate, just to be safe. */
}
/* 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 */
return res;
return err;
}

View file

@ -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)

View file

@ -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
View 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 */