Merge pull request #1842 from HaishinKit/feature/ipv6-srt

Added IPv6 support in the SRT protocol.
This commit is contained in:
shogo4405
2026-03-28 17:41:43 +09:00
committed by GitHub
4 changed files with 91 additions and 50 deletions
@@ -1,30 +0,0 @@
import Foundation
extension sockaddr_in {
var size: Int {
return MemoryLayout.size(ofValue: self)
}
init?(_ host: String, port: Int) {
self.init()
self.sin_family = sa_family_t(AF_INET)
self.sin_port = CFSwapInt16BigToHost(UInt16(port))
if inet_pton(AF_INET, host, &sin_addr) == 1 {
return
}
guard let hostent = gethostbyname(host), hostent.pointee.h_addrtype == AF_INET else {
return nil
}
if let h_addr_list = hostent.pointee.h_addr_list[0] {
self.sin_addr = UnsafeRawPointer(h_addr_list).assumingMemoryBound(to: in_addr.self).pointee
} else {
return nil
}
}
mutating func makeSockaddr() -> sockaddr {
var address = sockaddr()
memcpy(&address, &self, size)
return address
}
}
+30 -15
View File
@@ -8,6 +8,7 @@ final actor SRTSocket {
enum Error: Swift.Error { enum Error: Swift.Error {
case notConnected case notConnected
case invalidArgument(_ message: String)
case rejected(_ reason: SRTRejectReason) case rejected(_ reason: SRTRejectReason)
case illegalState(_ message: String) case illegalState(_ message: String)
} }
@@ -128,28 +129,42 @@ final actor SRTSocket {
let status: Int32 = try { let status: Int32 = try {
switch url.mode { switch url.mode {
case .caller: case .caller:
guard var remote = url.remote else { guard let remote = url.remote else {
return SRT_ERROR throw Error.invalidArgument("missing remote url")
}
return try remote.resolve(AI_ADDRCONFIG) { name, length in
srt_connect(socket, name, length)
} }
var remoteaddr = remote.makeSockaddr()
return srt_connect(socket, &remoteaddr, Int32(remote.size))
case .listener: case .listener:
guard var local = url.local else { guard let local = url.local else {
return SRT_ERROR throw Error.invalidArgument("missing local url")
} }
var localaddr = local.makeSockaddr() let _: Int32 = try local.resolve(AI_PASSIVE) { name, length in
let status = srt_bind(socket, &localaddr, Int32(local.size)) let status = srt_bind(socket, name, length)
guard status != SRT_ERROR else { if status == SRT_ERROR {
throw makeSocketError() return nil
} else {
return status
}
} }
return srt_listen(socket, 1) return srt_listen(socket, 1)
case .rendezvous: case .rendezvous:
guard var remote = url.remote, var local = url.local else { guard let remote = url.remote else {
return SRT_ERROR throw Error.invalidArgument("missing remote url")
}
guard let local = url.local else {
throw Error.invalidArgument("missing local url")
}
return try remote.resolve(AI_PASSIVE | AI_ADDRCONFIG) { remotename, remotelen in
return try local.resolve(AI_PASSIVE | AI_ADDRCONFIG) { localname, locallen in
let status = srt_rendezvous(socket, localname, locallen, remotename, remotelen)
if status == SRT_ERROR {
return nil
} else {
return status
}
}
} }
var remoteaddr = remote.makeSockaddr()
var localaddr = local.makeSockaddr()
return srt_rendezvous(socket, &remoteaddr, Int32(remote.size), &localaddr, Int32(local.size))
} }
}() }()
guard status != SRT_ERROR else { guard status != SRT_ERROR else {
+5 -5
View File
@@ -27,20 +27,20 @@ struct SRTSocketURL {
let mode: SRTMode let mode: SRTMode
let options: [SRTSocketOption] let options: [SRTSocketOption]
var remote: sockaddr_in? { var remote: AddrInfo? {
guard let host = url.host else { guard let host = url.host else {
return nil return nil
} }
return .init(host, port: url.port ?? Self.defaultPort) return AddrInfo(host: host, port: url.port ?? Self.defaultPort)
} }
var local: sockaddr_in? { var local: AddrInfo? {
let queryItems = Self.getQueryItems(url) let queryItems = Self.getQueryItems(url)
let adapter = queryItems["adapter"] ?? "0.0.0.0" let adapter = queryItems["adapter"] ?? "0.0.0.0"
if let port = queryItems["port"] { if let port = queryItems["port"] {
return .init(adapter, port: Int(port) ?? url.port ?? Self.defaultPort) return AddrInfo(host: adapter, port: Int(port) ?? url.port ?? Self.defaultPort)
} }
return .init(adapter, port: url.port ?? Self.defaultPort) return AddrInfo(host: adapter, port: url.port ?? Self.defaultPort)
} }
init?(_ url: URL?) { init?(_ url: URL?) {
+56
View File
@@ -0,0 +1,56 @@
import Foundation
import libsrt
struct AddrInfo {
enum Error: Swift.Error {
case failedToGetaddrinfo(_ code: Int)
case failedToResolve
}
let host: String
let port: Int
@discardableResult
func resolve<R>(_ flags: Int32, lambda: (UnsafePointer<sockaddr>, Int32) throws -> R?) throws -> R {
var hints = addrinfo(
ai_flags: flags,
ai_family: AF_UNSPEC,
ai_socktype: SOCK_DGRAM,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil
)
var result: UnsafeMutablePointer<addrinfo>?
let rv = getaddrinfo(host, String(port), &hints, &result)
guard rv == 0 else {
throw Error.failedToGetaddrinfo(Int(rv))
}
defer {
freeaddrinfo(result)
}
var addr = sockaddr_storage()
var rp = result
while rp != nil {
if let ai = rp?.pointee {
memcpy(&addr, ai.ai_addr, Int(ai.ai_addrlen))
let result = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
do {
return try lambda($0, Int32(ai.ai_addrlen))
} catch {
print("AddrInfo.resolve: lambda threw error for address \(host):\(port): \(error)")
return nil
}
}
}
if let result {
return result
}
}
rp = rp?.pointee.ai_next
}
throw Error.failedToResolve
}
}