Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ let package = Package(
.define("_GNU_SOURCE"),
.define("_POSIX_C_SOURCE", to: "200112L"),
.define("_DARWIN_C_SOURCE"),
// Disable assembly on Windows as SPM doesn't support .S files on Windows
.define("OPENSSL_NO_ASM", .when(platforms: [.windows])),
]
),
.target(
Expand Down
33 changes: 33 additions & 0 deletions Sources/CNIOBoringSSL/include/CNIOBoringSSL_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,39 @@

#define BORINGSSL_PREFIX CNIOBoringSSL

// On Windows, prevent conflicts between winsock.h and winsock2.h
// Also prevent min/max macro conflicts with std::numeric_limits
#if defined(_WIN32) || defined(_WIN64)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_ // Prevent winsock.h from being included
#endif
#include <winsock2.h>
#include <ws2tcpip.h>

// Undefine Windows CryptoAPI macros that conflict with BoringSSL types
// wincrypt.h defines these as LPCSTR constants for CertGetNameString
#ifdef X509_NAME
#undef X509_NAME
#endif
#ifdef X509_EXTENSIONS
#undef X509_EXTENSIONS
#endif
#ifdef X509_CERT_PAIR
#undef X509_CERT_PAIR
#endif
#ifdef X509_NAME_VALUE
#undef X509_NAME_VALUE
#endif
#ifdef PKCS7_SIGNER_INFO
#undef PKCS7_SIGNER_INFO
#endif
#endif

// This file should be the first included by all BoringSSL headers.

Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOSSL/ByteBufferBIO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Musl
import Glibc
#elseif canImport(Bionic)
import Bionic
#elseif os(Windows)
import WinSDK
#else
#error("unsupported os")
#endif
Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOSSL/IdentityVerification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Musl
import Glibc
#elseif canImport(Android)
import Android
#elseif os(Windows)
import WinSDK
#else
#error("unsupported os")
#endif
Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOSSL/NIOSSLClientHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import Musl
import Glibc
#elseif canImport(Android)
import Android
#elseif os(Windows)
import WinSDK
#else
#error("unsupported os")
#endif
Expand Down
21 changes: 19 additions & 2 deletions Sources/NIOSSL/PosixPort.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import Musl
import Glibc
#elseif canImport(Android)
import Android
#elseif os(Windows)
import ucrt
import WinSDK
#else
#error("unsupported os")
#endif
Expand All @@ -41,12 +44,14 @@ internal typealias FILEPointer = UnsafeMutablePointer<FILE>
#endif

private let sysFopen = fopen
private let sysMlock = mlock
private let sysMunlock = munlock
private let sysFclose = fclose
private let sysStat = { @Sendable in stat($0, $1) }
#if !os(Windows)
private let sysMlock = mlock
private let sysMunlock = munlock
private let sysLstat = lstat
private let sysReadlink = readlink
#endif

// MARK:- Copied code from SwiftNIO
private func isUnacceptableErrno(_ code: CInt) -> Bool {
Expand All @@ -65,7 +70,11 @@ internal func wrapSyscall<T: FixedWidthInteger>(where function: String = #functi
while true {
let res = try body()
if res == -1 {
#if os(Windows)
let err = Int32(bitPattern: GetLastError())
#else
let err = errno
#endif
if err == EINTR {
continue
}
Expand All @@ -84,7 +93,11 @@ internal func wrapErrorIsNullReturnCall<T>(
) throws -> T {
while true {
guard let res = try body() else {
#if os(Windows)
let err = Int32(bitPattern: GetLastError())
#else
let err = errno
#endif
if err == EINTR {
continue
}
Expand Down Expand Up @@ -113,6 +126,7 @@ internal enum Posix {
}
}

#if !os(Windows)
@inline(never)
internal static func readlink(
path: UnsafePointer<Int8>,
Expand All @@ -123,6 +137,7 @@ internal enum Posix {
sysReadlink(path, buf, bufSize)
}
}
#endif

@inline(never)
@discardableResult
Expand All @@ -132,6 +147,7 @@ internal enum Posix {
}
}

#if !os(Windows)
@inline(never)
@discardableResult
internal static func lstat(path: UnsafePointer<Int8>, buf: UnsafeMutablePointer<stat>) throws -> Int32 {
Expand All @@ -155,4 +171,5 @@ internal enum Posix {
sysMunlock(addr, len)
}
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/NIOSSL/SSLCallbacks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Musl
import Glibc
#elseif canImport(Bionic)
import Bionic
#elseif os(Windows)
import WinSDK
#else
#error("unsupported os")
#endif
Expand Down
4 changes: 3 additions & 1 deletion Sources/NIOSSL/SSLCertificate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Musl
import Glibc
#elseif canImport(Bionic)
import Bionic
#elseif os(Windows)
import WinSDK
#else
#error("unsupported os")
#endif
Expand Down Expand Up @@ -427,7 +429,7 @@ extension NIOSSLCertificate {
var dataPtr: UnsafeMutablePointer<CChar>? = nil
let length = CNIOBoringSSL_BIO_get_mem_data(bio, &dataPtr)

guard let bytes = dataPtr.map({ UnsafeRawBufferPointer(start: $0, count: length) }) else {
guard let bytes = dataPtr.map({ UnsafeRawBufferPointer(start: $0, count: .init(length)) }) else {
fatalError("Failed to map bytes from a certificate")
}

Expand Down
33 changes: 31 additions & 2 deletions Sources/NIOSSL/SSLContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import Musl
import Glibc
#elseif canImport(Android)
import Android
#elseif os(Windows)
import ucrt
import WinSDK
#else
#error("unsupported os")
#endif
Expand All @@ -45,8 +48,8 @@ internal enum FileSystemObject {
return nil
}

#if os(Android) && arch(arm)
return (statObj.st_mode & UInt32(S_IFDIR)) != 0 ? .directory : .file
#if (os(Android) && arch(arm)) || os(Windows)
return (UInt32(statObj.st_mode) & UInt32(S_IFDIR)) != 0 ? .directory : .file
#else
return (statObj.st_mode & S_IFDIR) != 0 ? .directory : .file
#endif
Expand Down Expand Up @@ -788,13 +791,15 @@ extension NIOSSLContext {

// Check if the element is a symlink. If it is not, return false.
var buffer = stat()
#if !os(Windows) // Windows has no symlinks
let _ = try Posix.lstat(path: path, buf: &buffer)
// Check the mode to make sure this is a symlink
#if os(Android) && arch(arm)
if (buffer.st_mode & UInt32(S_IFMT)) != UInt32(S_IFLNK) { return false }
#else
if (buffer.st_mode & S_IFMT) != S_IFLNK { return false }
#endif
#endif

// Return true at this point because the file format is considered to be in rehash format and a symlink.
// Rehash format being "%08lx.%d" or HHHHHHHH.D
Expand Down Expand Up @@ -919,16 +924,35 @@ internal class DirectoryContents: Sequence, IteratorProtocol {
// Otherwise an OpaquePointer needs to be used to account for the non-defined type in glibc.
#if canImport(Darwin)
let dir: UnsafeMutablePointer<DIR>
#elseif os(Windows)
var fileData = WIN32_FIND_DATA()
var dir: HANDLE? = nil
#else
let dir: OpaquePointer
#endif

init(path: String) {
self.path = path
#if os(Windows)
self.dir = FindFirstFileA(path, &fileData)
#else
self.dir = opendir(path)!
#endif
}

func next() -> String? {
#if os(Windows)
if dir != INVALID_HANDLE_VALUE {
let name = withUnsafePointer(to: &fileData.cFileName) { ptr in
// Pointers to homogeneous tuples in Swift are always bound to both the tuple type and the element type,
// so the assumption below is safe.
let elementPointer = UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self)
return String(cString: elementPointer)
}
FindNextFileA(dir, &fileData)
return self.path + name
}
#else
if let dirent: UnsafeMutablePointer<dirent> = readdir(self.dir) {
let name = withUnsafePointer(to: &dirent.pointee.d_name) { (ptr) -> String in
// Pointers to homogeneous tuples in Swift are always bound to both the tuple type and the element type,
Expand All @@ -938,11 +962,16 @@ internal class DirectoryContents: Sequence, IteratorProtocol {
}
return self.path + name
}
#endif
return nil
}

deinit {
#if os(Windows)
FindClose(dir)
#else
closedir(dir)
#endif
}
}

Expand Down
10 changes: 9 additions & 1 deletion Sources/NIOSSL/SSLPKCS12Bundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ extension NIOSSLPKCS12Bundle {

var dataPtr: UnsafeMutablePointer<CChar>? = nil
let length = CNIOBoringSSL_BIO_get_mem_data(bio, &dataPtr)
guard let bytes = dataPtr.map({ UnsafeMutableRawBufferPointer(start: $0, count: length) }) else {
guard let bytes = dataPtr.map({ UnsafeMutableRawBufferPointer(start: $0, count: .init(length)) }) else {
fatalError("Failed to get bytes from private key")
}

Expand All @@ -269,11 +269,19 @@ extension Collection where Element == UInt8 {
bufferPtr.deallocate()
}

#if os(Windows)
// TODO: throw an error on failure
VirtualLock(bufferPtr.baseAddress!, UInt64(bufferPtr.count))
defer {
VirtualUnlock(bufferPtr.baseAddress!, UInt64(bufferPtr.count))
}
#else
try Posix.mlock(addr: bufferPtr.baseAddress!, len: bufferPtr.count)
defer {
// If munlock fails take out the process.
try! Posix.munlock(addr: bufferPtr.baseAddress!, len: bufferPtr.count)
}
#endif

let (_, nextIndex) = bufferPtr.initialize(from: self)
assert(nextIndex == (bufferPtr.endIndex - 1))
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOSSL/SSLPrivateKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ extension NIOSSLPrivateKey {
var dataPtr: UnsafeMutablePointer<CChar>? = nil
let length = CNIOBoringSSL_BIO_get_mem_data(bio, &dataPtr)

guard let bytes = dataPtr.map({ UnsafeRawBufferPointer(start: $0, count: length) }) else {
guard let bytes = dataPtr.map({ UnsafeRawBufferPointer(start: $0, count: .init(length)) }) else {
fatalError("Failed to map bytes from a private key")
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOSSL/SSLPublicKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ extension NIOSSLPublicKey {
var dataPtr: UnsafeMutablePointer<CChar>? = nil
let length = CNIOBoringSSL_BIO_get_mem_data(bio, &dataPtr)

guard let bytes = dataPtr.map({ UnsafeMutableRawBufferPointer(start: $0, count: length) }) else {
guard let bytes = dataPtr.map({ UnsafeMutableRawBufferPointer(start: $0, count: .init(length)) }) else {
fatalError("Failed to map bytes from a public key")
}

Expand Down
16 changes: 14 additions & 2 deletions Sources/NIOSSL/SubjectAlternativeName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Musl
import Glibc
#elseif canImport(Android)
import Android
#elseif os(Windows)
import ucrt
#else
#error("unsupported os")
#endif
Expand Down Expand Up @@ -208,7 +210,12 @@ extension _SubjectAlternativeName.IPAddress: CustomStringConvertible {
var address = address
var dest: [CChar] = Array(repeating: 0, count: Self.ipv4AddressLength)
dest.withUnsafeMutableBufferPointer { pointer in
let result = inet_ntop(AF_INET, &address, pointer.baseAddress!, socklen_t(pointer.count))
#if os(Windows)
let size = pointer.count
#else
let size = socklen_t(pointer.count)
#endif
let result = inet_ntop(AF_INET, &address, pointer.baseAddress!, size)
precondition(
result != nil,
"The IP address was invalid. This should never happen as we're within the IP address struct."
Expand All @@ -221,7 +228,12 @@ extension _SubjectAlternativeName.IPAddress: CustomStringConvertible {
var address = address
var dest: [CChar] = Array(repeating: 0, count: Self.ipv6AddressLength)
dest.withUnsafeMutableBufferPointer { pointer in
let result = inet_ntop(AF_INET6, &address, pointer.baseAddress!, socklen_t(pointer.count))
#if os(Windows)
let size = pointer.count
#else
let size = socklen_t(pointer.count)
#endif
let result = inet_ntop(AF_INET6, &address, pointer.baseAddress!, size)
precondition(
result != nil,
"The IP address was invalid. This should never happen as we're within the IP address struct."
Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOSSLHTTP1Client/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ tlsConfiguration.renegotiationSupport = .once
let sslContext = try! NIOSSLContext(configuration: tlsConfiguration)

let bootstrap = ClientBootstrap(group: eventLoopGroup)
#if !os(Windows)
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
#endif
.channelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
let openSslHandler = try NIOSSLClientHandler(context: sslContext, serverHostname: url.host)
Expand Down
4 changes: 4 additions & 0 deletions Sources/NIOTLSServer/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
#if !os(Windows)
.serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
#endif

// Set the handlers that are applied to the accepted channels.
.childChannelInitializer { channel in
Expand All @@ -53,8 +55,10 @@ let bootstrap = ServerBootstrap(group: group)
}

// Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels
#if !os(Windows)
.childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
.childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
#endif

defer {
try! group.syncShutdownGracefully()
Expand Down