Skip to content

Commit

Permalink
Networking rewrite (#161)
Browse files Browse the repository at this point in the history
* msquic refactor

* comment

* refactor to use handler

* avoid use CoreFoundation api

* fix submodule

* fix

* fix
  • Loading branch information
xlc authored Oct 14, 2024
1 parent dc5f5ac commit 7f8da02
Show file tree
Hide file tree
Showing 196 changed files with 44,816 additions and 1,784 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ private struct BlockchainStorage: Sendable {
var finalizedHead: Data32
}

public final class BlockchainDataProvider {
public final class BlockchainDataProvider: Sendable {
private let storage: ThreadSafeContainer<BlockchainStorage>
private let dataProvider: BlockchainDataProviderProtocol

Expand Down
2 changes: 1 addition & 1 deletion Boka/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ let package = Package(
dependencies: [
.package(path: "../Node"),
.package(path: "../TracingUtils"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.4.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.4.0"),
.package(url: "https://github.com/slashmo/swift-otel.git", from: "0.9.0"),
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.6.0"),
.package(url: "https://github.com/vapor/console-kit.git", from: "4.14.3"),
Expand Down
44 changes: 33 additions & 11 deletions Networking/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "Networking",
platforms: [
.macOS(.v14),
.macOS(.v15),
],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
Expand All @@ -18,26 +18,37 @@ let package = Package(
dependencies: [
.package(path: "../Utils"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.5.0"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "Networking",

dependencies: [
"MsQuicSwift",
.product(name: "Logging", package: "swift-log"),
.product(name: "X509", package: "swift-certificates"),
]
),
.target(
name: "CHelpers",
dependencies: [
"openssl",
],
sources: ["helpers.h", "helpers.c"],
publicHeadersPath: ".",
cSettings: [
.headerSearchPath("../include"),
]
),
.target(
name: "MsQuicSwift",
dependencies: [
"msquic",
"Utils",
.product(name: "NIO", package: "swift-nio"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
"CHelpers",
.product(name: "Logging", package: "swift-log"),
],
resources: [
.process("assets"),
],
linkerSettings: [
.unsafeFlags(["-L../.lib"]),
]
Expand All @@ -46,13 +57,24 @@ let package = Package(
name: "msquic",
path: "Sources"
),
.systemLibrary(
name: "openssl",
path: "Sources"
),
.testTarget(
name: "NetworkingTests",
dependencies: [
"Networking",
.product(name: "Testing", package: "swift-testing"),
]
),
.testTarget(
name: "MsQuicSwiftTests",
dependencies: [
"MsQuicSwift",
.product(name: "Testing", package: "swift-testing"),
]
),
],
swiftLanguageModes: [.version("6")]
)
113 changes: 113 additions & 0 deletions Networking/Sources/CHelpers/helpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/ssl3.h>

#include "helpers.h"

int generate_self_signed_cert_and_pkcs12(
const unsigned char *private_key_buf,
size_t private_key_len,
const char *alt_name, // null terminated string
unsigned char **pkcs12_data,
int *pkcs12_len
) {
X509 *cert = NULL;
PKCS12 *p12 = NULL;
EVP_PKEY *ed25519_key = NULL;
unsigned char *temp_buf = NULL;
int ret = 1;

// Create EVP_PKEY from the provided Ed25519 private key buffer
ed25519_key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, private_key_buf, private_key_len);
if (!ed25519_key) {
goto cleanup;
}

// Create a new X509 certificate
cert = X509_new();
if (!cert) {
goto cleanup;
}

// Set version to X509v3
X509_set_version(cert, 2);

// Set serial number (you might want to generate this randomly)
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);

// // Set validity period (1 year)
// X509_gmtime_adj(X509_get_notBefore(cert), 0);
// X509_gmtime_adj(X509_get_notAfter(cert), 31536000L);

// Set subject and issuer (self-signed, so they're the same)
X509_NAME *name = X509_get_subject_name(cert);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"Self-Signed Cert", -1, -1, 0);
X509_set_issuer_name(cert, name);

// Set public key
X509_set_pubkey(cert, ed25519_key);

// Add Subject Alternative Name extension
GENERAL_NAMES *alt_names = GENERAL_NAMES_new();
GENERAL_NAME *gen_name = GENERAL_NAME_new();
ASN1_IA5STRING *ia5 = ASN1_IA5STRING_new();
ASN1_STRING_set(ia5, alt_name, -1);
GENERAL_NAME_set0_value(gen_name, GEN_DNS, ia5);
sk_GENERAL_NAME_push(alt_names, gen_name);
X509_add1_ext_i2d(cert, NID_subject_alt_name, alt_names, 0, 0);
GENERAL_NAMES_free(alt_names);

// Self-sign the certificate
if (!X509_sign(cert, ed25519_key, NULL)) {
goto cleanup;
}

// Create PKCS12 structure
p12 = PKCS12_create(NULL, "My Certificate", ed25519_key, cert, NULL, 0, 0, 0, 0, 0);
if (!p12) {
goto cleanup;
}

// Get the size of the DER-encoded PKCS12 structure
*pkcs12_len = i2d_PKCS12(p12, NULL);
if (*pkcs12_len <= 0) {
goto cleanup;
}

// Allocate memory for the DER-encoded PKCS12 structure
*pkcs12_data = malloc(*pkcs12_len);
if (!*pkcs12_data) {
goto cleanup;
}

// Reset the pointer to the start of the buffer
temp_buf = *pkcs12_data;

// Actually encode the PKCS12 structure
*pkcs12_len = i2d_PKCS12(p12, &temp_buf);
if (*pkcs12_len <= 0) {
ret = 6;
free(*pkcs12_data);
*pkcs12_data = NULL;
*pkcs12_len = 0;
goto cleanup;
}

ret = 0;

cleanup:
if (ret != 0) {
ret = ERR_get_error();

}
X509_free(cert);
PKCS12_free(p12);
EVP_PKEY_free(ed25519_key);
return ret;
}

char *get_error_string(int error) {
return ERR_error_string(error, NULL);
}
16 changes: 16 additions & 0 deletions Networking/Sources/CHelpers/helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <stddef.h>
#include <arpa/inet.h>

int generate_self_signed_cert_and_pkcs12(
const unsigned char *private_key_buf,
size_t private_key_len,
const char *alt_name, // null terminated string
unsigned char **pkcs12_data,
int *pkcs12_len
);

char *get_error_string(int error);

static inline uint16_t helper_ntohs(in_port_t netport) {
return ntohs(netport);
}
77 changes: 77 additions & 0 deletions Networking/Sources/MsQuicSwift/NetAddr.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import CHelpers
import Foundation
import msquic
#if canImport(Glibc)
import Glibc
#elseif canImport(Darwin)
import Darwin
#endif

public struct NetAddr: Hashable, Sendable {
var ipAddress: String
var port: UInt16
var ipv4: Bool

public init(ipAddress: String, port: UInt16, ipv4: Bool = false) {
self.ipAddress = ipAddress
self.port = port
self.ipv4 = ipv4
}

public init(quicAddr: QUIC_ADDR) {
let (host, port, ipv4) = parseQuicAddr(quicAddr) ?? ("::dead:beef", 0, false)
ipAddress = host
self.port = port
self.ipv4 = ipv4
}

func toQuicAddr() -> QUIC_ADDR? {
var addr = QUIC_ADDR()
let cstring = ipAddress.cString(using: .utf8)
guard cstring != nil else {
return nil
}
let success = QuicAddrFromString(cstring!, port, &addr)
guard success == 1 else {
return nil
}
return addr
}
}

extension NetAddr: CustomStringConvertible {
public var description: String {
if ipv4 {
"\(ipAddress):\(port)"
} else {
"[\(ipAddress)]:\(port)"
}
}
}

func parseQuicAddr(_ addr: QUIC_ADDR) -> (String, UInt16, Bool)? {
let ipv6 = addr.Ip.sa_family == QUIC_ADDRESS_FAMILY(QUIC_ADDRESS_FAMILY_INET6)
let port = if ipv6 {
helper_ntohs(addr.Ipv6.sin6_port)
} else {
helper_ntohs(addr.Ipv4.sin_port)
}
var addr = addr
if ipv6 {
addr.Ipv6.sin6_port = 0
} else {
addr.Ipv4.sin_port = 0
}
var buffer = QUIC_ADDR_STR()
let success = QuicAddrToString(&addr, &buffer)
guard success == 1 else {
return nil
}
let ipaddress = withUnsafePointer(to: buffer.Address) { ptr in
ptr.withMemoryRebound(to: CChar.self, capacity: Int(64)) { ptr in
String(cString: ptr, encoding: .utf8)!
}
}

return (ipaddress, port, ipv6)
}
44 changes: 44 additions & 0 deletions Networking/Sources/MsQuicSwift/QuicAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import msquic
import Utils

public final class QuicAPI: Sendable {
private let api: SendablePointer<QUIC_API_TABLE>

public init() throws(QuicError) {
var ptr: UnsafeRawPointer?
try MsQuicOpenVersion(2, &ptr).requireSucceeded("MsQuicOpenVersion")

api = ptr!.assumingMemoryBound(
to: QUIC_API_TABLE.self
).asSendable
}

deinit {
MsQuicClose(api.value)
}

func call(
_ message: String,
fn: (UnsafePointer<QUIC_API_TABLE>) throws(QuicError) -> UInt32
) throws(QuicError) {
try fn(api.value).requireSucceeded(message)
}

func call(
fn: (UnsafePointer<QUIC_API_TABLE>) -> Void
) {
fn(api.value)
}

public static let shared = try! QuicAPI()
}

extension UInt32 {
private var asQuicStatus: QuicStatus {
QuicStatus(rawValue: self)
}

fileprivate func requireSucceeded(_ message: String) throws(QuicError) {
try asQuicStatus.requireSucceeded(message)
}
}
Loading

0 comments on commit 7f8da02

Please sign in to comment.