From 3dbeb33115926a211d48a697c80e0ab0f1556bf4 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Fri, 8 Nov 2024 13:33:55 +1100 Subject: [PATCH] Support for building on Android Adds support for building using Swift Android SDK. --- CSystemLinux/include/CSystemLinux.h | 10 +- FlyingSocks/Sources/Mutex.swift | 4 +- FlyingSocks/Sources/Socket+Android.swift | 180 +++++++++++++++++++++ FlyingSocks/Sources/Socket.swift | 2 + FlyingSocks/Sources/SocketAddress.swift | 5 + FlyingSocks/Sources/SocketError.swift | 3 + FlyingSocks/Sources/SocketPool+Poll.swift | 2 + FlyingSocks/Sources/SocketPool+ePoll.swift | 2 +- Package.swift | 2 +- 9 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 FlyingSocks/Sources/Socket+Android.swift diff --git a/CSystemLinux/include/CSystemLinux.h b/CSystemLinux/include/CSystemLinux.h index 283a7bd7..79d47122 100644 --- a/CSystemLinux/include/CSystemLinux.h +++ b/CSystemLinux/include/CSystemLinux.h @@ -7,10 +7,14 @@ See https://swift.org/LICENSE.txt for license information */ -#ifdef __linux__ +#pragma once + +#ifdef __ANDROID__ +#include +#include +#endif +#ifdef __linux__ #include #include - #endif - diff --git a/FlyingSocks/Sources/Mutex.swift b/FlyingSocks/Sources/Mutex.swift index dc1c331c..9847c346 100644 --- a/FlyingSocks/Sources/Mutex.swift +++ b/FlyingSocks/Sources/Mutex.swift @@ -129,10 +129,12 @@ extension Mutex { } } -#elseif canImport(Glibc) || canImport(Musl) +#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) #if canImport(Musl) import Musl +#elseif canImport(Bionic) +import Android #else import Glibc #endif diff --git a/FlyingSocks/Sources/Socket+Android.swift b/FlyingSocks/Sources/Socket+Android.swift new file mode 100644 index 00000000..fb154a8e --- /dev/null +++ b/FlyingSocks/Sources/Socket+Android.swift @@ -0,0 +1,180 @@ +// +// Socket+Android.swift +// FlyingFox +// +// Created by Simon Whitty on 26/09/2024. +// Copyright © 2024 Simon Whitty. All rights reserved. +// +// Distributed under the permissive MIT license +// Get the latest version from here: +// +// https://github.com/swhitty/FlyingFox +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#if canImport(Android) +import Android + +// from linux/eventpoll.h, not imported by clang importer +let EPOLLET: UInt32 = 1 << 31; + +public extension Socket { + typealias FileDescriptorType = Int32 +} + +extension Socket.FileDescriptor { + static let invalid = Socket.FileDescriptor(rawValue: -1) +} + +extension Socket { + static let stream = Int32(SOCK_STREAM) + static let in_addr_any = Android.in_addr(s_addr: Android.in_addr_t(0)) + + static func makeAddressINET(port: UInt16) -> Android.sockaddr_in { + Android.sockaddr_in( + sin_family: sa_family_t(AF_INET), + sin_port: port.bigEndian, + sin_addr: in_addr_any, + __pad: (0, 0, 0, 0, 0, 0, 0, 0) + ) + } + + static func makeAddressINET6(port: UInt16) -> Android.sockaddr_in6 { + Android.sockaddr_in6( + sin6_family: sa_family_t(AF_INET6), + sin6_port: port.bigEndian, + sin6_flowinfo: 0, + sin6_addr: in6addr_any, + sin6_scope_id: 0 + ) + } + + static func makeAddressLoopback(port: UInt16) -> Android.sockaddr_in6 { + Android.sockaddr_in6( + sin6_family: sa_family_t(AF_INET6), + sin6_port: port.bigEndian, + sin6_flowinfo: 0, + sin6_addr: in6addr_loopback, + sin6_scope_id: 0 + ) + } + + static func makeAddressUnix(path: String) -> Android.sockaddr_un { + var addr = Android.sockaddr_un() + addr.sun_family = sa_family_t(AF_UNIX) + let pathCount = min(path.utf8.count, 104) + let len = UInt8(MemoryLayout.size + MemoryLayout.size + pathCount + 1) + _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in + path.withCString { + strncpy(ptr, $0, Int(len)) + } + } + return addr + } + + static func socket(_ domain: Int32, _ type: Int32, _ protocol: Int32) -> FileDescriptorType { + Android.socket(domain, type, `protocol`) + } + + static func socketpair(_ domain: Int32, _ type: Int32, _ protocol: Int32) -> (FileDescriptorType, FileDescriptorType) { + var sockets: [Int32] = [-1, -1] + _ = Android.socketpair(domain, type, `protocol`, &sockets) + return (sockets[0], sockets[1]) + } + + static func fcntl(_ fd: Int32, _ cmd: Int32) -> Int32 { + Android.fcntl(fd, cmd) + } + + static func fcntl(_ fd: Int32, _ cmd: Int32, _ value: Int32) -> Int32 { + Android.fcntl(fd, cmd, value) + } + + static func setsockopt(_ fd: Int32, _ level: Int32, _ name: Int32, + _ value: UnsafeRawPointer!, _ len: socklen_t) -> Int32 { + Android.setsockopt(fd, level, name, value, len) + } + + static func getsockopt(_ fd: Int32, _ level: Int32, _ name: Int32, + _ value: UnsafeMutableRawPointer!, _ len: UnsafeMutablePointer!) -> Int32 { + Android.getsockopt(fd, level, name, value, len) + } + + static func getpeername(_ fd: Int32, _ addr: UnsafeMutablePointer!, _ len: UnsafeMutablePointer!) -> Int32 { + Android.getpeername(fd, addr, len) + } + + static func getsockname(_ fd: Int32, _ addr: UnsafeMutablePointer!, _ len: UnsafeMutablePointer!) -> Int32 { + Android.getsockname(fd, addr, len) + } + + static func inet_ntop(_ domain: Int32, _ addr: UnsafeRawPointer!, + _ buffer: UnsafeMutablePointer!, _ addrLen: socklen_t) throws { + if Android.inet_ntop(domain, addr, buffer, addrLen) == nil { + throw SocketError.makeFailed("inet_ntop") + } + } + + static func inet_pton(_ domain: Int32, _ buffer: UnsafePointer!, _ addr: UnsafeMutableRawPointer!) -> Int32 { + Android.inet_pton(domain, buffer, addr) + } + + static func bind(_ fd: Int32, _ addr: UnsafePointer!, _ len: socklen_t) -> Int32 { + Android.bind(fd, addr, len) + } + + static func listen(_ fd: Int32, _ backlog: Int32) -> Int32 { + Android.listen(fd, backlog) + } + + static func accept(_ fd: Int32, _ addr: UnsafeMutablePointer!, _ len: UnsafeMutablePointer!) -> Int32 { + Android.accept(fd, addr, len) + } + + static func connect(_ fd: Int32, _ addr: UnsafePointer!, _ len: socklen_t) -> Int32 { + Android.connect(fd, addr, len) + } + + static func read(_ fd: Int32, _ buffer: UnsafeMutableRawPointer!, _ nbyte: Int) -> Int { + Android.read(fd, buffer, nbyte) + } + + static func write(_ fd: Int32, _ buffer: UnsafeRawPointer!, _ nbyte: Int) -> Int { + Android.send(fd, buffer, nbyte, Int32(MSG_NOSIGNAL)) + } + + static func close(_ fd: Int32) -> Int32 { + Android.close(fd) + } + + static func unlink(_ addr: UnsafePointer!) -> Int32 { + Android.unlink(addr) + } + + static func poll(_ fds: UnsafeMutablePointer!, _ nfds: UInt32, _ tmo_p: Int32) -> Int32 { + Android.poll(fds, nfds_t(nfds), tmo_p) + } + + static func pollfd(fd: FileDescriptorType, events: Int16, revents: Int16) -> Android.pollfd { + Android.pollfd(fd: fd, events: events, revents: revents) + } +} + +#endif diff --git a/FlyingSocks/Sources/Socket.swift b/FlyingSocks/Sources/Socket.swift index f4dc62c7..7de00526 100644 --- a/FlyingSocks/Sources/Socket.swift +++ b/FlyingSocks/Sources/Socket.swift @@ -31,6 +31,8 @@ #if canImport(WinSDK) import WinSDK.WinSock2 +#elseif canImport(Android) +@_exported import Android #endif import Foundation diff --git a/FlyingSocks/Sources/SocketAddress.swift b/FlyingSocks/Sources/SocketAddress.swift index 276c75ac..68839421 100644 --- a/FlyingSocks/Sources/SocketAddress.swift +++ b/FlyingSocks/Sources/SocketAddress.swift @@ -32,8 +32,13 @@ import Foundation #if canImport(WinSDK) import WinSDK.WinSock2 +#elseif canImport(Android) +import Android #endif +#if canImport(CSystemLinux) +import CSystemLinux +#endif public protocol SocketAddress: Sendable { static var family: sa_family_t { get } diff --git a/FlyingSocks/Sources/SocketError.swift b/FlyingSocks/Sources/SocketError.swift index 0caf645e..da1e2234 100644 --- a/FlyingSocks/Sources/SocketError.swift +++ b/FlyingSocks/Sources/SocketError.swift @@ -30,6 +30,9 @@ // import Foundation +#if canImport(Android) +import Android +#endif public enum SocketError: LocalizedError, Equatable { case failed(type: String, errno: Int32, message: String) diff --git a/FlyingSocks/Sources/SocketPool+Poll.swift b/FlyingSocks/Sources/SocketPool+Poll.swift index b5345b1d..3ba3de79 100644 --- a/FlyingSocks/Sources/SocketPool+Poll.swift +++ b/FlyingSocks/Sources/SocketPool+Poll.swift @@ -31,6 +31,8 @@ #if canImport(WinSDK) import WinSDK.WinSock2 +#elseif canImport(Android) +import Android #endif import Foundation diff --git a/FlyingSocks/Sources/SocketPool+ePoll.swift b/FlyingSocks/Sources/SocketPool+ePoll.swift index 2f1ed9c0..a104e019 100644 --- a/FlyingSocks/Sources/SocketPool+ePoll.swift +++ b/FlyingSocks/Sources/SocketPool+ePoll.swift @@ -219,7 +219,7 @@ extension EventNotification { private struct EPOLLEvents: OptionSet, Hashable { var rawValue: UInt32 -#if canImport(Musl) +#if canImport(Musl) || canImport(Android) static let read = EPOLLEvents(rawValue: UInt32(EPOLLIN)) static let write = EPOLLEvents(rawValue: UInt32(EPOLLOUT)) static let hup = EPOLLEvents(rawValue: UInt32(EPOLLHUP)) diff --git a/Package.swift b/Package.swift index bf8c359b..9f2db145 100644 --- a/Package.swift +++ b/Package.swift @@ -26,7 +26,7 @@ let package = Package( ), .target( name: "FlyingSocks", - dependencies: [.target(name: "CSystemLinux", condition: .when(platforms: [.linux]))], + dependencies: [.target(name: "CSystemLinux", condition: .when(platforms: [.linux, .android]))], path: "FlyingSocks/Sources", swiftSettings: .upcomingFeatures ),