blob: 35098a8c3d3a57d19756666508aa733000a24f12 [file] [log] [blame]
// Copyright 2026 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/async/address_impl.h"
#include "iree/async/socket.h"
#include "iree/base/api.h"
#if defined(IREE_PLATFORM_GENERIC) || defined(IREE_PLATFORM_EMSCRIPTEN)
#include <stddef.h>
#include <string.h>
//===----------------------------------------------------------------------===//
// Self-contained sockaddr definitions (no platform socket headers needed).
// Layout matches Linux for consistency. These are only used as the internal
// storage format within iree_async_address_t on generic platforms.
//===----------------------------------------------------------------------===//
#define IREE_AF_INET 2
#define IREE_AF_INET6 10
typedef struct {
uint16_t sin_family;
uint16_t sin_port;
uint8_t sin_addr[4];
uint8_t sin_zero[8];
} iree_sockaddr_in_t;
typedef struct {
uint16_t sin6_family;
uint16_t sin6_port;
uint32_t sin6_flowinfo;
uint8_t sin6_addr[16];
uint32_t sin6_scope_id;
} iree_sockaddr_in6_t;
//===----------------------------------------------------------------------===//
// iree_async_address_from_ipv4
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t iree_async_address_from_ipv4(
iree_string_view_t host, uint16_t port, iree_async_address_t* out_address) {
memset(out_address, 0, sizeof(*out_address));
iree_sockaddr_in_t* addr = (iree_sockaddr_in_t*)out_address->storage;
addr->sin_family = IREE_AF_INET;
addr->sin_port = iree_async_htons(port);
if (iree_string_view_is_empty(host)) {
// INADDR_ANY (all zeros, already set by memset).
} else {
IREE_RETURN_IF_ERROR(iree_async_address_parse_ipv4(host, addr->sin_addr));
}
out_address->length = sizeof(iree_sockaddr_in_t);
return iree_ok_status();
}
//===----------------------------------------------------------------------===//
// iree_async_address_from_ipv6
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t iree_async_address_from_ipv6(
iree_string_view_t host, uint16_t port, iree_async_address_t* out_address) {
memset(out_address, 0, sizeof(*out_address));
iree_sockaddr_in6_t* addr = (iree_sockaddr_in6_t*)out_address->storage;
addr->sin6_family = IREE_AF_INET6;
addr->sin6_port = iree_async_htons(port);
if (iree_string_view_is_empty(host)) {
// in6addr_any (all zeros, already set by memset).
} else {
// Split zone suffix (%<zone>) before parsing the address.
iree_string_view_t zone = iree_string_view_empty();
iree_host_size_t percent_index = iree_string_view_find_char(host, '%', 0);
if (percent_index != IREE_STRING_VIEW_NPOS) {
zone =
iree_string_view_substr(host, percent_index + 1, IREE_HOST_SIZE_MAX);
host = iree_string_view_substr(host, 0, percent_index);
}
IREE_RETURN_IF_ERROR(iree_async_address_parse_ipv6(host, addr->sin6_addr));
// Resolve zone to scope_id (numeric only on generic platforms).
if (!iree_string_view_is_empty(zone)) {
uint32_t scope_id = 0;
if (!iree_string_view_atoi_uint32(zone, &scope_id)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"non-numeric IPv6 zone IDs not supported on "
"this platform (use numeric interface index)");
}
addr->sin6_scope_id = scope_id;
} else if (percent_index != IREE_STRING_VIEW_NPOS) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"empty IPv6 zone ID after '%%'");
}
}
out_address->length = sizeof(iree_sockaddr_in6_t);
return iree_ok_status();
}
//===----------------------------------------------------------------------===//
// iree_async_address_from_unix
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t iree_async_address_from_unix(
iree_string_view_t path, iree_async_address_t* out_address) {
(void)path;
(void)out_address;
return iree_make_status(IREE_STATUS_UNAVAILABLE,
"Unix domain sockets not supported on this platform");
}
//===----------------------------------------------------------------------===//
// iree_async_address_format
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t iree_async_address_format(
const iree_async_address_t* address, iree_host_size_t buffer_capacity,
char* buffer, iree_string_view_t* out_value) {
if (address->length == 0) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, "empty address");
}
// Read family from first 2 bytes (uint16_t at offset 0 in both
// iree_sockaddr_in_t and iree_sockaddr_in6_t).
uint16_t family;
memcpy(&family, address->storage, sizeof(family));
char temp[IREE_ASYNC_ADDRESS_MAX_FORMAT_LENGTH];
iree_host_size_t length = 0;
switch (family) {
case IREE_AF_INET: {
const iree_sockaddr_in_t* addr =
(const iree_sockaddr_in_t*)address->storage;
uint16_t port = iree_async_ntohs(addr->sin_port);
length = iree_async_address_format_ipv4(addr->sin_addr, port, temp);
break;
}
case IREE_AF_INET6: {
const iree_sockaddr_in6_t* addr =
(const iree_sockaddr_in6_t*)address->storage;
uint16_t port = iree_async_ntohs(addr->sin6_port);
length = iree_async_address_format_ipv6(addr->sin6_addr, port,
addr->sin6_scope_id, temp);
break;
}
default:
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"unknown address family %d", (int)family);
}
if (length > buffer_capacity) {
return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
"address format requires %" PRIhsz
" bytes but buffer capacity is %" PRIhsz,
length, buffer_capacity);
}
memcpy(buffer, temp, length);
if (out_value) {
out_value->data = buffer;
out_value->size = length;
}
return iree_ok_status();
}
#endif // IREE_PLATFORM_GENERIC || IREE_PLATFORM_EMSCRIPTEN