220 lines
5.8 KiB
Swift
220 lines
5.8 KiB
Swift
//
|
|
// SocketUtils.swift
|
|
// BlueSocket
|
|
//
|
|
// Created by Bill Abt on 11/19/15.
|
|
// Copyright © 2016 IBM. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#if os(macOS) || os(iOS) || os(tvOS)
|
|
import Darwin
|
|
#elseif os(Linux)
|
|
import Glibc
|
|
#endif
|
|
|
|
import Foundation
|
|
|
|
//
|
|
// Great help with this from
|
|
// https://blog.obdev.at/representing-socket-addresses-in-swift-using-enums/
|
|
//
|
|
extension Socket.Address {
|
|
|
|
///
|
|
/// Call a low level socket function using the specified socket address pointer.
|
|
///
|
|
/// - Parameters:
|
|
/// - body: The closure containing the call to the low level function.
|
|
///
|
|
/// - Returns: The result of executing the closure.
|
|
///
|
|
func withSockAddrPointer<Result>(body: (UnsafePointer<sockaddr>, socklen_t) throws -> Result) rethrows -> Result {
|
|
|
|
///
|
|
/// Internal function to call do the cast and call to the closure.
|
|
///
|
|
/// - Parameter: Closure body.
|
|
///
|
|
/// - Returns: Result of executing the closure.
|
|
///
|
|
func castAndCall<T>(_ address: T, _ body: (UnsafePointer<sockaddr>, socklen_t) throws -> Result) rethrows -> Result {
|
|
var localAddress = address // We need a `var` here for the `&`.
|
|
return try withUnsafePointer(to: &localAddress) {
|
|
return try $0.withMemoryRebound(to: sockaddr.self, capacity: 1, {
|
|
return try body($0, socklen_t(MemoryLayout<T>.size))
|
|
})
|
|
}
|
|
}
|
|
|
|
switch self {
|
|
|
|
case .ipv4(let address):
|
|
return try castAndCall(address, body)
|
|
|
|
case .ipv6(let address):
|
|
return try castAndCall(address, body)
|
|
|
|
case .unix(let address):
|
|
return try castAndCall(address, body)
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Socket.Address {
|
|
|
|
///
|
|
/// Creates a Socket.Address
|
|
///
|
|
/// - Parameters:
|
|
/// - addressProvider: Tuple containing pointers to the sockaddr and its length.
|
|
///
|
|
/// - Returns: Newly initialized Socket.Address.
|
|
///
|
|
init?(addressProvider: (UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) throws -> Void) rethrows {
|
|
|
|
var addressStorage = sockaddr_storage()
|
|
var addressStorageLength = socklen_t(MemoryLayout.size(ofValue: addressStorage))
|
|
try withUnsafeMutablePointer(to: &addressStorage) {
|
|
try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { addressPointer in
|
|
try withUnsafeMutablePointer(to: &addressStorageLength) { addressLengthPointer in
|
|
try addressProvider(addressPointer, addressLengthPointer)
|
|
}
|
|
}
|
|
}
|
|
|
|
switch Int32(addressStorage.ss_family) {
|
|
case AF_INET:
|
|
self = withUnsafePointer(to: &addressStorage) {
|
|
return $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
|
|
return Socket.Address.ipv4($0.pointee)
|
|
}
|
|
}
|
|
case AF_INET6:
|
|
self = withUnsafePointer(to: &addressStorage) {
|
|
return $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
|
|
return Socket.Address.ipv6($0.pointee)
|
|
}
|
|
}
|
|
case AF_UNIX:
|
|
self = withUnsafePointer(to: &addressStorage) {
|
|
return $0.withMemoryRebound(to: sockaddr_un.self, capacity: 1) {
|
|
return Socket.Address.unix($0.pointee)
|
|
}
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
#if os(Linux)
|
|
|
|
/// Arm archictecture only allows for 16 fds.
|
|
#if arch(arm)
|
|
let __fd_set_count = 16
|
|
#else
|
|
let __fd_set_count = 32
|
|
#endif
|
|
|
|
extension fd_set {
|
|
|
|
@inline(__always)
|
|
mutating func withCArrayAccess<T>(block: (UnsafeMutablePointer<Int32>) throws -> T) rethrows -> T {
|
|
return try withUnsafeMutablePointer(to: &__fds_bits) {
|
|
try block(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self))
|
|
}
|
|
}
|
|
}
|
|
|
|
#else // not Linux on ARM
|
|
|
|
// __DARWIN_FD_SETSIZE is number of *bits*, so divide by number bits in each element to get element count
|
|
// at present this is 1024 / 32 == 32
|
|
let __fd_set_count = Int(__DARWIN_FD_SETSIZE) / 32
|
|
|
|
extension fd_set {
|
|
|
|
@inline(__always)
|
|
mutating func withCArrayAccess<T>(block: (UnsafeMutablePointer<Int32>) throws -> T) rethrows -> T {
|
|
return try withUnsafeMutablePointer(to: &fds_bits) {
|
|
try block(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self))
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
extension fd_set {
|
|
|
|
@inline(__always)
|
|
private static func address(for fd: Int32) -> (Int, Int32) {
|
|
var intOffset = Int(fd) / __fd_set_count
|
|
#if _endian(big)
|
|
if intOffset % 2 == 0 {
|
|
intOffset += 1
|
|
} else {
|
|
intOffset -= 1
|
|
}
|
|
#endif
|
|
let bitOffset = Int(fd) % __fd_set_count
|
|
let mask = Int32(bitPattern: UInt32(1 << bitOffset))
|
|
return (intOffset, mask)
|
|
}
|
|
|
|
///
|
|
/// Zero the fd_set
|
|
///
|
|
public mutating func zero() {
|
|
#if swift(>=4.1)
|
|
withCArrayAccess { $0.initialize(repeating: 0, count: __fd_set_count) }
|
|
#else
|
|
withCArrayAccess { $0.initialize(to: 0, count: __fd_set_count) }
|
|
#endif
|
|
}
|
|
|
|
///
|
|
/// Set an fd in an fd_set
|
|
///
|
|
/// - Parameter fd: The fd to add to the fd_set
|
|
///
|
|
public mutating func set(_ fd: Int32) {
|
|
let (index, mask) = fd_set.address(for: fd)
|
|
withCArrayAccess { $0[index] |= mask }
|
|
}
|
|
|
|
///
|
|
/// Clear an fd from an fd_set
|
|
///
|
|
/// - Parameter fd: The fd to clear from the fd_set
|
|
///
|
|
public mutating func clear(_ fd: Int32) {
|
|
let (index, mask) = fd_set.address(for: fd)
|
|
withCArrayAccess { $0[index] &= ~mask }
|
|
}
|
|
|
|
///
|
|
/// Check if an fd is present in an fd_set
|
|
///
|
|
/// - Parameter fd: The fd to check
|
|
///
|
|
/// - Returns: `True` if present, `false` otherwise.
|
|
///
|
|
public mutating func isSet(_ fd: Int32) -> Bool {
|
|
let (index, mask) = fd_set.address(for: fd)
|
|
return withCArrayAccess { $0[index] & mask != 0 }
|
|
}
|
|
}
|