blob: 29a1297fe00de164c0cea03611be29ee6a107aaa [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
#if os(Android)
// Android Glibc differs a little with respect to the Linux Glibc.
// IFF_LOOPBACK is part of the enumeration net_device_flags, which needs to
// convert to UInt32.
private extension UInt32 {
init(_ value: net_device_flags) {
self.init(value.rawValue)
}
}
// getnameinfo uses size_t for its 4th and 6th arguments.
private func getnameinfo(_ addr: UnsafePointer<sockaddr>?, _ addrlen: socklen_t, _ host: UnsafeMutablePointer<Int8>?, _ hostlen: socklen_t, _ serv: UnsafeMutablePointer<Int8>?, _ servlen: socklen_t, _ flags: Int32) -> Int32 {
return Glibc.getnameinfo(addr, addrlen, host, Int(hostlen), serv, Int(servlen), flags)
}
#endif
open class Host: NSObject {
enum ResolveType {
case name
case address
case current
}
internal var _info: String?
internal var _type: ResolveType
internal var _resolved = false
internal var _names = [String]()
internal var _addresses = [String]()
static internal let _current = Host(currentHostName(), .current)
internal init(_ info: String?, _ type: ResolveType) {
_info = info
_type = type
}
static internal func currentHostName() -> String {
#if os(Windows)
var dwLength: DWORD = 0
GetComputerNameExA(ComputerNameDnsHostname, nil, &dwLength)
guard dwLength > 0 else { return "localhost" }
guard let hostname: UnsafeMutablePointer<Int8> =
UnsafeMutableBufferPointer<Int8>
.allocate(capacity: Int(dwLength + 1))
.baseAddress else {
return "localhost"
}
defer { hostname.deallocate() }
guard GetComputerNameExA(ComputerNameDnsHostname, hostname, &dwLength) else {
return "localhost"
}
return String(cString: hostname)
#else
let hname = UnsafeMutablePointer<Int8>.allocate(capacity: Int(NI_MAXHOST))
defer {
hname.deallocate()
}
let r = gethostname(hname, Int(NI_MAXHOST))
if r < 0 || hname[0] == 0 {
return "localhost"
}
return String(cString: hname)
#endif
}
open class func current() -> Host {
return _current
}
public convenience init(name: String?) {
self.init(name, .name)
}
public convenience init(address: String) {
self.init(address, .address)
}
open func isEqual(to aHost: Host) -> Bool {
if self === aHost { return true }
return addresses.firstIndex { aHost.addresses.contains($0) } != nil
}
internal func _resolveCurrent() {
#if os(Windows)
var szAddress: [WCHAR] =
Array<WCHAR>(repeating: 0, count: Int(NI_MAXHOST))
var ulSize: ULONG = 0
var ulResult: ULONG =
GetAdaptersAddresses(ULONG(AF_UNSPEC), 0, nil, nil, &ulSize)
var arAdapters: UnsafeMutableRawPointer =
UnsafeMutableRawPointer.allocate(byteCount: Int(ulSize),
alignment: 1)
defer { arAdapters.deallocate() }
ulResult = GetAdaptersAddresses(ULONG(AF_UNSPEC), 0, nil,
arAdapters.assumingMemoryBound(to: IP_ADAPTER_ADDRESSES.self),
&ulSize)
guard ulResult == ERROR_SUCCESS else { return }
var pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>? =
arAdapters.assumingMemoryBound(to: IP_ADAPTER_ADDRESSES.self)
while pAdapter != nil {
// print("Adapter: \(String(cString: pAdapter!.pointee.AdapterName))")
var arAddresses: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS> =
pAdapter!.pointee.FirstUnicastAddress
var pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>? =
arAddresses
while pAddress != nil {
switch pAddress!.pointee.Address.lpSockaddr.pointee.sa_family {
case ADDRESS_FAMILY(AF_INET), ADDRESS_FAMILY(AF_INET6):
if GetNameInfoW(pAddress!.pointee.Address.lpSockaddr,
pAddress!.pointee.Address.iSockaddrLength,
&szAddress, DWORD(szAddress.capacity), nil, 0,
NI_NUMERICHOST) == 0 {
// print("\tIP Address: \(String(decodingCString: &szAddress, as: UTF16.self))")
_addresses.append(String(decodingCString: &szAddress,
as: UTF16.self))
}
default: break
}
pAddress = pAddress!.pointee.Next
}
pAdapter = pAdapter!.pointee.Next
}
_resolved = true
#else
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) != 0 {
return
}
var ifa: UnsafeMutablePointer<ifaddrs>? = ifaddr
let address = UnsafeMutablePointer<Int8>.allocate(capacity: Int(NI_MAXHOST))
defer {
freeifaddrs(ifaddr)
address.deallocate()
}
while let ifaValue = ifa?.pointee {
if let ifa_addr = ifaValue.ifa_addr, ifaValue.ifa_flags & UInt32(IFF_LOOPBACK) == 0 {
let family = ifa_addr.pointee.sa_family
if family == sa_family_t(AF_INET) || family == sa_family_t(AF_INET6) {
let sa_len: socklen_t = socklen_t((family == sa_family_t(AF_INET6)) ? MemoryLayout<sockaddr_in6>.size : MemoryLayout<sockaddr_in>.size)
if getnameinfo(ifa_addr, sa_len, address, socklen_t(NI_MAXHOST), nil, 0, NI_NUMERICHOST) == 0 {
_addresses.append(String(cString: address))
}
}
}
ifa = ifaValue.ifa_next
}
_resolved = true
#endif
}
internal func _resolve() {
guard _resolved == false else { return }
#if os(Windows)
if let info = _info {
if _type == .current { return _resolveCurrent() }
var hints: ADDRINFOW = ADDRINFOW()
memset(&hints, 0, MemoryLayout<ADDRINFOW>.size)
switch (_type) {
case .name:
hints.ai_flags = AI_PASSIVE | AI_CANONNAME
case .address:
hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST
case .current:
break
}
hints.ai_family = AF_UNSPEC
hints.ai_socktype = SOCK_STREAM
hints.ai_protocol = IPPROTO_TCP.rawValue
var aiResult: UnsafeMutablePointer<ADDRINFOW>?
var bSucceeded: Bool = false
info.withCString(encodedAs: UTF16.self) {
if GetAddrInfoW($0, nil, &hints, &aiResult) == 0 {
bSucceeded = true
}
}
guard bSucceeded == true else { return }
defer { FreeAddrInfoW(aiResult) }
var wszHostName: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(NI_MAXHOST))
while aiResult != nil {
let aiInfo: ADDRINFOW = aiResult!.pointee
var sa_len: socklen_t = 0
switch aiInfo.ai_family {
case AF_INET:
sa_len = socklen_t(MemoryLayout<sockaddr_in>.size)
case AF_INET6:
sa_len = socklen_t(MemoryLayout<sockaddr_in6>.size)
default:
aiResult = aiInfo.ai_next
continue
}
let lookup = { (content: inout [String], flags: Int32) in
if GetNameInfoW(aiInfo.ai_addr, sa_len, &wszHostName,
DWORD(NI_MAXHOST), nil, 0, flags) == 0 {
content.append(String(decodingCString: &wszHostName,
as: UTF16.self))
}
}
lookup(&_addresses, NI_NUMERICHOST)
lookup(&_names, NI_NAMEREQD)
lookup(&_names, NI_NOFQDN | NI_NAMEREQD)
aiResult = aiInfo.ai_next
}
_resolved = true
}
#else
if let info = _info {
var flags: Int32 = 0
switch (_type) {
case .name:
flags = AI_PASSIVE | AI_CANONNAME
case .address:
flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST
case .current:
_resolveCurrent()
return
}
var hints = addrinfo()
hints.ai_family = PF_UNSPEC
#if os(macOS) || os(iOS) || os(Android)
hints.ai_socktype = SOCK_STREAM
#else
hints.ai_socktype = Int32(SOCK_STREAM.rawValue)
#endif
hints.ai_flags = flags
var res0: UnsafeMutablePointer<addrinfo>? = nil
let r = getaddrinfo(info, nil, &hints, &res0)
defer {
freeaddrinfo(res0)
}
if r != 0 {
return
}
var res: UnsafeMutablePointer<addrinfo>? = res0
let host = UnsafeMutablePointer<Int8>.allocate(capacity: Int(NI_MAXHOST))
defer {
host.deallocate()
}
while res != nil {
let info = res!.pointee
let family = info.ai_family
if family != AF_INET && family != AF_INET6 {
res = info.ai_next
continue
}
let sa_len: socklen_t = socklen_t((family == AF_INET6) ? MemoryLayout<sockaddr_in6>.size : MemoryLayout<sockaddr_in>.size)
let lookupInfo = { (content: inout [String], flags: Int32) in
if getnameinfo(info.ai_addr, sa_len, host, socklen_t(NI_MAXHOST), nil, 0, flags) == 0 {
content.append(String(cString: host))
}
}
lookupInfo(&_addresses, NI_NUMERICHOST)
lookupInfo(&_names, NI_NAMEREQD)
lookupInfo(&_names, NI_NOFQDN|NI_NAMEREQD)
res = info.ai_next
}
_resolved = true
}
#endif
}
open var name: String? {
return names.first
}
open var names: [String] {
_resolve()
return _names
}
open var address: String? {
return addresses.first
}
open var addresses: [String] {
_resolve()
return _addresses
}
open var localizedName: String? {
return nil
}
}