Skip to content

Commit

Permalink
Add Windows support for getnameinfo and getaddrinfo
Browse files Browse the repository at this point in the history
getaddrinfo was partially supported already but did not correctly
handle non-ASCII hostnames

Signed-off-by: Dave Thaler <[email protected]>
  • Loading branch information
dthaler committed Apr 10, 2020
1 parent b52ed14 commit d99b5fe
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 75 deletions.
38 changes: 26 additions & 12 deletions common/oe_host_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ typedef struct _getaddrinfo_handle

OE_INLINE getaddrinfo_handle_t* _cast_getaddrinfo_handle(void* handle_);

/**
* _strcpy_to_utf8.
*
* This function copies a native (UTF-16LE on Windows) string into a UTF-8
* buffer. If the buffer is not large enough, the value returned will be larger
* than ai_canonname_buf_len.
*
* @param[out] ai_canonname_buf The buffer to fill in with a UTF-8 string
* @param[in] ai_canonname_buf_len The size in bytes of the buffer to fill in
* @param[in] ai_canonname The native string to copy from
*
* @return The size in bytes needed for the output buffer, or 0 on failure
*/
size_t _strcpy_to_utf8(
char* ai_canonname_buf,
size_t ai_canonname_buf_len,
void* ai_canonname);

int _getaddrinfo_read(
uint64_t handle_,
int* ai_flags,
Expand Down Expand Up @@ -105,32 +123,28 @@ int _getaddrinfo_read(
*ai_socktype = p->ai_socktype;
*ai_protocol = p->ai_protocol;
*ai_addrlen = (oe_socklen_t)p->ai_addrlen;

if (p->ai_canonname)
*ai_canonnamelen = strlen(p->ai_canonname) + 1;
else
*ai_canonnamelen = 0;
*ai_canonnamelen = 0;

if (*ai_addrlen > ai_addrlen_in)
{
*err_no = OE_ENAMETOOLONG;
goto done;
}

if (*ai_canonnamelen > ai_canonnamelen_in)
{
*err_no = OE_ENAMETOOLONG;
goto done;
}

if (ai_addr)
{
memcpy(ai_addr, p->ai_addr, *ai_addrlen);
}

if (ai_canonname && p->ai_canonname)
{
memcpy(ai_canonname, p->ai_canonname, *ai_canonnamelen);
*ai_canonnamelen = _strcpy_to_utf8(
ai_canonname, ai_canonnamelen_in, p->ai_canonname);
if (*ai_canonnamelen > ai_canonnamelen_in)
{
*err_no = OE_ENAMETOOLONG;
goto done;
}
}

handle->next = handle->next->ai_next;
Expand Down
15 changes: 15 additions & 0 deletions host/linux/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,21 @@ int oe_syscall_getaddrinfo_open_ocall(
return ret;
}

size_t _strcpy_to_utf8(
char* ai_canonname_buf,
size_t ai_canonname_buf_len,
void* ai_canonname)
{
const char* canonname = (const char*)ai_canonname;

size_t buf_needed = strlen(canonname) + 1;
if (buf_needed <= ai_canonname_buf_len)
{
memcpy(ai_canonname_buf, canonname, buf_needed);
}
return buf_needed;
}

int oe_syscall_getaddrinfo_read_ocall(
uint64_t handle_,
int* ai_flags,
Expand Down
139 changes: 104 additions & 35 deletions host/windows/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,17 +370,17 @@ inline char* _nul_to_dev_null(PCWSTR path)

inline void _canonicalize_path_separators(
char* path,
uint32_t path_length,
size_t path_length,
char separator)
{
for (uint32_t i = 0; i < path_length; i++)
for (size_t i = 0; i < path_length; i++)
{
if (path[i] == '\\' || path[i] == '/')
path[i] = separator;
}
}

inline void _windows_to_oe_syscall_volume_root(char* path, uint32_t path_length)
inline void _windows_to_oe_syscall_volume_root(char* path, size_t path_length)
{
/* convert "C:" to "/c" */
if (path && path_length >= 2 && isalpha(path[0]) && path[1] == ':')
Expand Down Expand Up @@ -470,6 +470,39 @@ errno_t _get_full_path_name(
return err;
}

/**
* _strcpy_to_utf8.
*
* This function copies a native (UTF-16LE on Windows) string into a UTF-8
* buffer. If the buffer is not large enough, the value returned will be larger
* than ai_canonname_buf_len.
*
* @param[out] ai_canonname_buf The buffer to fill in with a UTF-8 string,
* or NULL to just get the size needed.
* @param[in] ai_canonname_buf_len The size in bytes of the buffer to fill in
* @param[in] ai_canonname The native string to copy from
*
* @return The size in bytes needed for the output buffer, or 0 on failure
*/
size_t _strcpy_to_utf8(
char* ai_canonname_buf,
size_t ai_canonname_buf_len,
void* ai_canonname)
{
PWSTR canonname = (PWSTR)ai_canonname;
int buflen =
(ai_canonname_buf_len <= INT_MAX) ? (int)ai_canonname_buf_len : INT_MAX;

size_t buf_needed =
WideCharToMultiByte(CP_UTF8, 0, canonname, -1, NULL, 0, NULL, NULL);
if (buf_needed <= buflen)
{
WideCharToMultiByte(
CP_UTF8, 0, canonname, -1, ai_canonname_buf, buflen, NULL, NULL);
}
return buf_needed;
}

/* Converts a Windows path to a POSIX style used in enclaves by OE syscalls:
*
* <drive_letter>:\<item>\<item> -> /<drive_letter>/<item>/<item>
Expand All @@ -489,7 +522,7 @@ char* oe_win_path_to_posix(PCWSTR wpath)
{
PWSTR wenclave_path = NULL;
char* enclave_path = NULL;
uint32_t enclave_path_length = 0;
size_t enclave_path_length = 0;
errno_t err = 0;

if (!wpath || wcsnlen_s(wpath, MAX_PATH) == 0 ||
Expand All @@ -511,8 +544,7 @@ char* oe_win_path_to_posix(PCWSTR wpath)
}

// Convert UTF-16LE to UTF-8.
enclave_path_length =
WideCharToMultiByte(CP_UTF8, 0, wenclave_path, -1, NULL, 0, NULL, NULL);
enclave_path_length = _strcpy_to_utf8(NULL, 0, wenclave_path);
if (enclave_path_length == 0)
{
DWORD winerror = GetLastError();
Expand All @@ -526,15 +558,7 @@ char* oe_win_path_to_posix(PCWSTR wpath)
err = OE_ENOMEM;
goto done;
}
WideCharToMultiByte(
CP_UTF8,
0,
wenclave_path,
-1,
enclave_path,
enclave_path_length,
NULL,
NULL);
_strcpy_to_utf8(enclave_path, enclave_path_length, wenclave_path);

_canonicalize_path_separators(enclave_path, enclave_path_length, '/');

Expand Down Expand Up @@ -1567,18 +1591,11 @@ int oe_syscall_readdir_ocall(uint64_t dirp, struct oe_dirent* entry)
memset(entry->d_name, 0, OE_NAME_MAX + 1);

/* Convert from UTF-16LE to UTF-8. */
if (WideCharToMultiByte(
CP_UTF8,
0,
pdir->FindFileData.cFileName,
-1,
entry->d_name,
OE_NAME_MAX + 1,
NULL,
NULL) == 0)
if (_strcpy_to_utf8(
entry->d_name, OE_NAME_MAX + 1, pdir->FindFileData.cFileName) == 0)
{
DWORD winerr = GetLastError();
OE_TRACE_ERROR("WideCharToMultiByte failed with %#x\n", winerr);
OE_TRACE_ERROR("_strcpy_to_utf8 failed with %#x\n", winerr);
_set_errno(_winerr_to_errno(winerr));
goto done;
}
Expand Down Expand Up @@ -2609,8 +2626,44 @@ int oe_syscall_getaddrinfo_open_ocall(
goto done;
}

ret =
getaddrinfo(node, service, (const struct addrinfo*)hints, &handle->res);
// Convert the node name (if any) from UTF-8 to UTF-16LE.
PWSTR wnode = NULL;
WCHAR wnode_buf[NI_MAXHOST];
if (node != NULL)
{
if (MultiByteToWideChar(CP_UTF8, 0, node, -1, wnode_buf, NI_MAXHOST) ==
0)
{
ret = OE_EAI_FAIL;
goto done;
}
wnode = wnode_buf;
}

// Convert the service name (if any) from UTF-8 to UTF-16LE.
PWSTR wservice = NULL;
WCHAR wserv_buf[NI_MAXSERV];
if (service != NULL)
{
if (MultiByteToWideChar(
CP_UTF8, 0, service, -1, wserv_buf, NI_MAXSERV) == 0)
{
ret = OE_EAI_FAIL;
goto done;
}
wservice = wserv_buf;
}

// The addrinfo structure is the same between ADDRINFOA and ADDRINFOW
// except for the types of pointers. However, in the hints, the pointer
// fields must always be NULL anyway, so we don't need any conversion
// other than a simple cast.
ADDRINFOW* whints = (ADDRINFOW*)hints;

// Get the list of ADDRINFOW structs. Again a simple cast will do
// since we will deal with the type of pointer when reading the values
// out of the struct.
ret = GetAddrInfoW(wnode, wservice, whints, (ADDRINFOW**)&handle->res);
if (ret == 0)
{
handle->magic = GETADDRINFO_HANDLE_MAGIC;
Expand Down Expand Up @@ -2694,15 +2747,31 @@ int oe_syscall_getnameinfo_ocall(
oe_socklen_t servlen,
int flags)
{
OE_UNUSED(sa);
OE_UNUSED(salen);
OE_UNUSED(host);
OE_UNUSED(hostlen);
OE_UNUSED(serv);
OE_UNUSED(servlen);
OE_UNUSED(flags);
WCHAR whost[NI_MAXHOST];
WCHAR wserv[NI_MAXSERV];
errno = 0;

PANIC;
// Get the name in UTF-16LE format. We cannot use getnameinfo since it uses
// ANSI code pages and the current code page may not be able to represent
// the name.
int ret = GetNameInfoW(
(const struct sockaddr*)sa,
salen,
whost,
_countof(whost),
wserv,
_countof(wserv),
flags);
if (ret != 0)
return _wsaerr_to_eai(ret);

// Convert UTF-16LE to UTF-8.
if (_strcpy_to_utf8(host, hostlen, whost) == 0)
{
return EAI_FAIL;
}

return 0;
}

/*
Expand Down
2 changes: 1 addition & 1 deletion tests/syscall/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ add_subdirectory(cpio)
add_subdirectory(dup)
add_subdirectory(fs)
add_subdirectory(hostfs)
add_subdirectory(resolver)
add_subdirectory(socket)
if(UNIX)
add_subdirectory(datagram)
add_subdirectory(epoll)
add_subdirectory(ids)
add_subdirectory(poller)
add_subdirectory(resolver)
add_subdirectory(sendmsg)
add_subdirectory(socketpair)
endif()
4 changes: 2 additions & 2 deletions tests/syscall/resolver/enc/enc.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ static struct oe_addrinfo* _clone_addrinfo(const struct oe_addrinfo* ai)
return ret;
}

int ecall_getaddrinfo(struct addrinfo** res)
int ecall_getaddrinfo(struct oe_addrinfo** res)
{
struct oe_addrinfo* ai = NULL;
const char host[] = {"localhost"};
Expand All @@ -186,7 +186,7 @@ int ecall_getaddrinfo(struct addrinfo** res)

OE_TEST(oe_getaddrinfo(host, serv, &hints, (struct oe_addrinfo**)&ai) == 0);

if (res && !(*res = (struct addrinfo*)_clone_addrinfo(ai)))
if (res && !(*res = (struct oe_addrinfo*)_clone_addrinfo(ai)))
OE_TEST("_clone_addrinfo() failed" == NULL);

oe_freeaddrinfo(ai);
Expand Down
Loading

0 comments on commit d99b5fe

Please sign in to comment.