Skip to content

Commit

Permalink
Host Resovler Arch Update (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
waahm7 authored Feb 28, 2023
1 parent ccec2f7 commit dce7c17
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 39 deletions.
18 changes: 9 additions & 9 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,21 @@ awsCCalPlatformExcludes.append("source/darwin")

// swift never uses Microsoft Visual C++ compiler
awsCChecksumsExcludes.append("source/intel/visualc")
//TODO: enable hardware acceleration https://github.com/awslabs/aws-sdk-swift/issues/867
//#if arch(arm64)
// TODO: enable hardware acceleration https://github.com/awslabs/aws-sdk-swift/issues/867
// #if arch(arm64)
//// includes source/arm
//// TODO: look at the compiler flag in C
//awsCChecksumsExcludes.append("source/intel")
//awsCChecksumsExcludes.append("source/generic")
//#elseif arch(x86_64) || arch(i386)
// awsCChecksumsExcludes.append("source/intel")
// awsCChecksumsExcludes.append("source/generic")
// #elseif arch(x86_64) || arch(i386)
//// include src/intel/asm
//awsCChecksumsExcludes.append("source/arm")
//awsCChecksumsExcludes.append("source/generic")
//#else
// awsCChecksumsExcludes.append("source/arm")
// awsCChecksumsExcludes.append("source/generic")
// #else
// includes source/generic
awsCChecksumsExcludes.append("source/arm")
awsCChecksumsExcludes.append("source/intel")
//#endif
// #endif

let awsCSdkUtilsPlatformExcludes = ["CODE_OF_CONDUCT.md"] + excludesFromAll

Expand Down
10 changes: 9 additions & 1 deletion Source/AwsCommonRuntimeKit/crt/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ class Box<T> {
self.contents = contents
}

func passRetained() -> UnsafeMutableRawPointer {
Unmanaged<Box>.passRetained(self).toOpaque()
}

func passUnretained() -> UnsafeMutableRawPointer {
return Unmanaged<Box>.passUnretained(self).toOpaque()
Unmanaged<Box>.passUnretained(self).toOpaque()
}

func release() {
Unmanaged.passUnretained(self).release()
}
}

Expand Down
2 changes: 1 addition & 1 deletion Source/AwsCommonRuntimeKit/http/HTTPRequestOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct HTTPRequestOptions {
/// When using HTTP/2, set http2ManualDataWrites to true to specify that request body data will be provided over time.
/// The stream will only be polled for writing when data has been supplied via `HTTP2Stream.writeData`
public var http2ManualDataWrites: Bool = false

public init(request: HTTPRequestBase,
onInterimResponse: OnInterimResponse? = nil,
onResponse: @escaping OnResponse,
Expand Down
72 changes: 59 additions & 13 deletions Source/AwsCommonRuntimeKit/io/HostAddress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,68 @@

import AwsCIo

public struct HostAddress {
public var host: String
public var address: String
public var recordType: AddressRecordType
/// Represents a single HostAddress resolved by the Host Resolver
public struct HostAddress: CStruct {

/// Address type is ipv4 or ipv6
public let addressType: HostAddressType

/// Resolved numerical address represented as a String
public let address: String

/// Host name of the resolved address
public let hostName: String

/// Service record. Currently, unused because we use HTTP, but this may change as we add more protocols.
public let service: String? = nil

let allocator: Allocator
let expiry: UInt64
let useCount: Int
let connectionFailureCount: Int
let weight: UInt8

init(hostAddress: aws_host_address) {
self.host = String(awsString: hostAddress.host)!
self.address = String(awsString: hostAddress.address)!
self.recordType = AddressRecordType(rawValue: hostAddress.record_type)
hostName = String(awsString: hostAddress.host)!
address = String(awsString: hostAddress.address)!
addressType = HostAddressType(rawValue: hostAddress.record_type)
allocator = hostAddress.allocator
expiry = hostAddress.expiry
useCount = hostAddress.use_count
connectionFailureCount = hostAddress.connection_failure_count
weight = hostAddress.weight
}

typealias RawType = aws_host_address
func withCStruct<Result>(_ body: (aws_host_address) -> Result) -> Result {
let cAddress = AWSString(address, allocator: allocator)
let cHostName = AWSString(hostName, allocator: allocator)

var cHostAddress = aws_host_address()
cHostAddress.record_type = addressType.rawValue
cHostAddress.address = UnsafePointer(cAddress.rawValue)
cHostAddress.host = UnsafePointer(cHostName.rawValue)
cHostAddress.allocator = allocator.rawValue
cHostAddress.expiry = expiry
cHostAddress.use_count = useCount
cHostAddress.connection_failure_count = connectionFailureCount
cHostAddress.weight = weight
return body(cHostAddress)
}
}

/// Arguments for Host Resolver operations
public struct HostResolverArguments {

/// Host name to resolve
public var hostName: String

/// Service record. Currently unused because we use HTTP, but this may
/// change as we add more protocols.
public var service: String?

public init(host: String,
address: String,
recordType: AddressRecordType = .typeA) {
self.host = host
self.address = address
self.recordType = recordType
public init(hostName: String, service: String? = nil) {
self.hostName = hostName
self.service = service
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@

import AwsCIo

public enum AddressRecordType {
case typeA
case typeAAAA
/// Type of Host Address (ipv4 or ipv6)
public enum HostAddressType {
case A
case AAAA
}

extension AddressRecordType: RawRepresentable, CaseIterable {
extension HostAddressType: RawRepresentable, CaseIterable {

public init(rawValue: aws_address_record_type) {
let value = Self.allCases.first { $0.rawValue == rawValue }
self = value ?? .typeA
self = value ?? .A
}
public var rawValue: aws_address_record_type {
switch self {
case .typeA: return aws_address_record_type(rawValue: 0)
case .typeAAAA: return aws_address_record_type(rawValue: 1)
case .A: return aws_address_record_type(rawValue: 0)
case .AAAA: return aws_address_record_type(rawValue: 1)
}
}
}
89 changes: 85 additions & 4 deletions Source/AwsCommonRuntimeKit/io/HostResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,32 @@
import AwsCCommon
import AwsCIo

public class HostResolver {
/// All host resolver implementation must conform to this protocol.
public protocol HostResolverProtocol {
/// Resolves the address(es) for HostResolverArguments and returns a list of
/// addresses with (most likely) two addresses, one AAAA and one A.
/// - Parameter args: The host name to resolve address for.
/// - Returns: List of resolved host addresses.
/// - Throws: CommonRunTimeError.crtError
func resolveAddress(args: HostResolverArguments) async throws -> [HostAddress]
/// Reports a failure on an address so that the background cache can accommodate
/// the failure and likely not return the address until it recovers.
/// - Parameter address: The address to report the failure for.
func reportFailureOnAddress(address: HostAddress)
/// Empties the cache for an address.
/// - Parameter args: The host name to purge the cache.
func purgeCache(args: HostResolverArguments) async
/// Empties the cache for all addresses.
func purgeCache() async
}

/// CRT Host Resolver which performs async DNS lookups
public class HostResolver: HostResolverProtocol {
let rawValue: UnsafeMutablePointer<aws_host_resolver>
let maxTTL: Int
private let allocator: Allocator

/// Creates a host resolver with the default behavior.
public static func makeDefault(eventLoopGroup: EventLoopGroup,
maxHosts: Int = 16,
maxTTL: Int = 30,
Expand Down Expand Up @@ -47,7 +68,13 @@ public class HostResolver {
self.maxTTL = maxTTL
}

public func resolve(host: String) async throws -> [HostAddress] {
/// Resolves the address(es) for HostResolverArguments and returns a list of
/// addresses with (most likely) two addresses, one AAAA and one A.
/// - Parameter args: The host name to resolve address for.
/// - Returns: List of resolved host addresses.
/// - Throws: CommonRunTimeError.crtError
public func resolveAddress(args: HostResolverArguments) async throws -> [HostAddress] {
let host = args.hostName
return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation<[HostAddress], Error>) in
let continuationCore = ContinuationCore(continuation: continuation)
let hostStr = AWSString(host, allocator: allocator)
Expand All @@ -59,15 +86,62 @@ public class HostResolver {
hostResolutionConfigPointer,
continuationCore.passRetained()) != AWS_OP_SUCCESS {

// TODO: this is wrong. Sometimes it triggers the error callback and sometimes it doesn't.
// I have a fix in progress in aws-c-io
continuationCore.release()
continuation.resume(throwing: CommonRunTimeError.crtError(CRTError.makeFromLastError()))
}
}
})
}

/// Reports a failure on an address so that the background cache can accommodate
/// the failure and likely not return the address until it recovers.
/// Note: While the underlying C API may report an error, we ignore it because users don't care and
/// don't have a good way to deal with it.
/// - Parameter address: The address to report the failure for.
public func reportFailureOnAddress(address: HostAddress) {
address.withCPointer { cAddress in
_ = aws_host_resolver_record_connection_failure(rawValue, cAddress)
}
}

/// Purges the cache for a specific address
/// Note: While the underlying C API may report an error, we ignore it because users don't care and
/// don't have a good way to deal with it.
/// - Parameter args: The host name to purge the cache.
public func purgeCache(args: HostResolverArguments) async {
let host = AWSString(args.hostName, allocator: allocator)
return await withCheckedContinuation({ (continuation: CheckedContinuation<(), Never>) in
let continuationCore = Box(continuation)
var purgeCacheOptions = aws_host_resolver_purge_host_options()
purgeCacheOptions.host = UnsafePointer(host.rawValue)
purgeCacheOptions.on_host_purge_complete_callback = onPurgeCacheComplete
purgeCacheOptions.user_data = continuationCore.passRetained()
guard aws_host_resolver_purge_host_cache(rawValue, &purgeCacheOptions) == AWS_OP_SUCCESS else {
continuationCore.release()
continuation.resume()
return
}
})
}

/// Wipe out anything cached by resolver.
/// Note: While the underlying C API may report an error, we ignore it because users don't care and
/// don't have a good way to deal with it.
public func purgeCache() async {
await withCheckedContinuation({ (continuation: CheckedContinuation<(), Never>) in
let continuationCore = Box(continuation)
guard aws_host_resolver_purge_cache_with_callback(
rawValue,
onPurgeCacheComplete,
continuationCore.passRetained()) == AWS_OP_SUCCESS
else {
continuationCore.release()
continuation.resume()
return
}
})
}

func getHostResolutionConfig() -> aws_host_resolution_config {
var cHostResolutionConfig = aws_host_resolution_config()
cHostResolutionConfig.max_ttl = maxTTL
Expand All @@ -80,6 +154,13 @@ public class HostResolver {
}
}

private func onPurgeCacheComplete(_ userData: UnsafeMutableRawPointer!) {
let continuationCore = Unmanaged<Box<CheckedContinuation<(), Never>>>
.fromOpaque(userData)
.takeRetainedValue()
continuationCore.contents.resume()
}

private func onHostResolved(_ resolver: UnsafeMutablePointer<aws_host_resolver>?,
_ hostName: UnsafePointer<aws_string>?,
_ errorCode: Int32,
Expand Down
35 changes: 34 additions & 1 deletion Test/AwsCommonRuntimeKitTests/io/HostResolverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,45 @@ class HostResolverTests: XCBaseTestCase {
maxTTL: 5,
allocator: allocator)

let addresses = try await resolver.resolve(host: "localhost")
let addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssertNoThrow(addresses)
XCTAssertNotNil(addresses.count)
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
}

func testPurgeCache() async throws {
let elg = try EventLoopGroup(allocator: allocator)
let resolver = try HostResolver(eventLoopGroup: elg,
maxHosts: 8,
maxTTL: 5,
allocator: allocator)

var addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
await resolver.purgeCache(args: HostResolverArguments(hostName: "localHost"))
await resolver.purgeCache(args: HostResolverArguments(hostName: "localHost"))
addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
await resolver.purgeCache()
await resolver.purgeCache()
addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
}

func testReportConnectionOnFailure() async throws {
let elg = try EventLoopGroup(allocator: allocator)
let resolver = try HostResolver(eventLoopGroup: elg,
maxHosts: 8,
maxTTL: 5,
allocator: allocator)

var addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
resolver.reportFailureOnAddress(address: addresses[0])
addresses = try await resolver.resolveAddress(args: HostResolverArguments(hostName: "localhost"))
XCTAssert(addresses.count >= 1, "Address Count is (\(String(describing: addresses.count)))")
}

func testHotResolverShutdownCallback() async throws {
let shutdownWasCalled = XCTestExpectation(description: "Shutdown callback was called")
shutdownWasCalled.expectedFulfillmentCount = 2
Expand Down
2 changes: 1 addition & 1 deletion aws-common-runtime/s2n
Submodule s2n updated 53 files
+3 −0 .gitignore
+1 −2 README.md
+16 −7 api/s2n.h
+76 −0 api/unstable/fingerprint.h
+0 −1 bin/s2nc.c
+2 −0 bin/s2nd.c
+1 −1 bindings/rust/.clippy.toml
+1 −1 bindings/rust/rust-toolchain
+3 −3 bindings/rust/s2n-tls-sys/Cargo.toml
+5 −4 bindings/rust/s2n-tls-tokio/Cargo.toml
+4 −3 bindings/rust/s2n-tls/Cargo.toml
+0 −2 bindings/rust/s2n-tls/src/lib.rs
+2 −0 codebuild/bin/criterion_baseline.sh
+6 −6 codebuild/bin/criterion_delta.sh
+1 −1 codebuild/bin/s2n_set_build_preset.sh
+4 −2 codebuild/bin/test_install_shared_and_static.sh
+2 −14 codebuild/spec/buildspec_omnibus.yml
+1 −0 crypto/s2n_aead_cipher_aes_gcm.c
+1 −0 crypto/s2n_cipher.h
+44 −135 docs/USAGE-GUIDE.md
+110 −0 flake.lock
+42 −0 flake.nix
+8 −0 stuffer/s2n_stuffer.c
+1 −0 stuffer/s2n_stuffer.h
+6 −0 tests/integrationv2/common.py
+81 −9 tests/integrationv2/test_signature_algorithms.py
+97 −0 tests/unit/s2n_build_test.c
+17 −5 tests/unit/s2n_client_hello_test.c
+918 −0 tests/unit/s2n_fingerprint_ja3_test.c
+62 −12 tests/unit/s2n_init_test.c
+38 −0 tests/unit/s2n_ktls_mode_test.c
+42 −0 tests/unit/s2n_ktls_test.c
+1 −1 tests/unit/s2n_mem_usage_test.c
+14 −7 tests/unit/s2n_record_test.c
+4 −2 tests/unit/s2n_rfc5952_test.c
+2 −1 tests/unit/s2n_ssl_prf_test.c
+12 −6 tests/unit/s2n_stream_cipher_null_test.c
+2 −1 tests/unit/s2n_stuffer_base64_test.c
+4 −2 tests/unit/s2n_stuffer_hex_test.c
+41 −5 tests/unit/s2n_stuffer_test.c
+2 −1 tests/unit/s2n_tls13_record_aead_test.c
+4 −2 tests/unit/s2n_tls_hybrid_prf_test.c
+4 −2 tests/unit/s2n_wildcard_hostname_test.c
+16 −2 tls/s2n_client_hello.c
+10 −0 tls/s2n_client_hello.h
+1 −0 tls/s2n_config.c
+6 −2 tls/s2n_connection.h
+322 −0 tls/s2n_fingerprint.c
+4 −0 tls/s2n_handshake_io.c
+34 −0 tls/s2n_ktls.h
+28 −2 tls/s2n_record_read.c
+6 −0 utils/s2n_init.c
+21 −0 utils/s2n_random.c

0 comments on commit dce7c17

Please sign in to comment.