获取网络(不是设备)的IP地址iOS Swift

时间:2018-11-06 15:41:56

标签: ios swift swift4.2

我已经编写了一些代码来获取Wi-Fi当前的SSID和设备的当前IP,但是我需要获取网络IP地址,因此,如果我的设备的IP为192.168.4.3,我知道它已连接至192.168.4.1网络,但是我不需要在代码中做这个假设...

获取SSID:

private func fetchSSIDInfo() -> String? {
    var ssid: String?

    if let interfaces = CNCopySupportedInterfaces() as NSArray? {

        for interface in interfaces {


            if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {

                ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
                break

            }

        }

    }

    return ssid

} 

获取设备的IP

static func getIPAddress()->String?{

    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else { return nil }
    guard let firstAddr = ifaddr else { return nil }

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" {

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)

            }

        }

    }

    freeifaddrs(ifaddr)

    return address

}

??网络IP地址?

**编辑** 如果我调用ifa_netmask,它将返回192.168.4.2/24 IP的255.255.255.0的网络掩码。

1 个答案:

答案 0 :(得分:1)

似乎您在寻找外部IP,而本地IP为192.168.4.1。没有某些外部服务的帮助,这是不可能的...

更新 有关详情,请参见rfc1533 rfc2131,请在Playground中尝试:-)

import Foundation

func pack<T:FixedWidthInteger>(_ fi: T)->Data {
    var nfi = fi
    if 1 == 1.littleEndian {
        nfi = fi.bigEndian
    }
    return withUnsafeBytes(of: nfi) { un -> Data in
        var data = Data()
        un.forEach({ (byte) in
            data.append(byte)
        })
        return data
    }
}

enum StringPack {
    case ipv4, ipv6, mac
}

func pack(_ txt: String, type: StringPack)->Data {
    var data = Data()
    switch type {
    case .ipv4:
        txt.split(separator: ".", omittingEmptySubsequences: false).forEach { s in
            data.append(UInt8(s) ?? 0)
        }
        while data.count < 4 { // padding with 0
            data.append(0)
        }
    case .ipv6:
        txt.split(separator: ":", omittingEmptySubsequences: false).forEach { (s) in
            data.append(pack(UInt16(s, radix: 16) ?? 0))
        }
        while data.count < 8 { // padding with 0
            data.append(0)
        }
    case .mac:
        txt.split(separator: ":", omittingEmptySubsequences: false).forEach { s in
            s
            UInt8(s, radix:16)
            data.append(UInt8(s, radix:16) ?? 0)
        }
        while data.count < 16 { // padding with 0
            data.append(0)
        }
    }
    return data
}

func dhcp_packet(
    // all parameters have default value (https://www.ietf.org/rfc/rfc2131.txt) for client using wifi interface
    op: UInt8 = 1, htype: UInt8 = 1, hlen: UInt8 = 6, hops: UInt8 = 0,
    xid: UInt32 = UInt32.random(in: UInt32.min...UInt32.max),
    secs: UInt16 = 0, flags: UInt16 = 0,
    ciaddr: String = "...",
    yiaddr: String = "...",
    siaddr: String = "...",
    giaddr: String = "...",
    chaddr: String = ":::::",
    sname: String = "",
    file: String = ""
    )->Data {
    // data represents dhcp_packet
    var data = Data()
    // sd tuple represents predefined fixed size data
    var sd:(Data, Int)

    data.append(op)
    data.append(htype)
    data.append(hlen)
    data.append(hops)
    data.append(pack(xid))
    data.append(pack(secs))
    data.append(pack(flags))
    data.append(pack(ciaddr, type: .ipv4))
    data.append(pack(yiaddr, type: .ipv4))
    data.append(pack(siaddr, type: .ipv4))
    data.append(pack(giaddr, type: .ipv4))
    data.append(pack(chaddr, type: .mac))
    sd = (Data(count: 64), min(sname.utf8.count, 64))
    sd.0.replaceSubrange(0..<sd.1, with: Data(sname.utf8)[0..<sd.1])
    data.append(sd.0)
    sd = (Data(count: 128), min(file.utf8.count, 128))
    sd.0.replaceSubrange(0..<sd.1, with: Data(sname.utf8)[0..<sd.1])
    data.append(sd.0)

    return data
}


import Darwin

func getWiFiAddress() -> (ip4: String, mac: String, addr: sockaddr_in) {

    var address : String = "..."
    var mac: String = ":::::"
    var success: Bool
    var addr_in = sockaddr_in()
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    success = getifaddrs(&ifaddr) == 0
    assert(success)
    assert(ifaddr != nil)
    let firstAddr = ifaddr!

    for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
        let interface = ifptr.pointee
        let addrFamily = interface.ifa_addr.pointee.sa_family
        let name = String(cString: interface.ifa_name)
        if  name == "en0" {
            if addrFamily == UInt8(AF_INET) {
                var addr = interface.ifa_addr.pointee
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                success = getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST) == 0
                assert(success)
                addr_in = withUnsafePointer(to: &addr) {
                    $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
                        $0.pointee
                    }
                }
                address = String(cString: hostname)
            }
            if addrFamily == UInt8(AF_LINK) {
                interface.ifa_addr.withMemoryRebound(to: sockaddr_dl.self, capacity: 1) { (sdl) -> Void in
                    var hw = sdl.pointee.sdl_data
                    withUnsafeBytes(of: &hw, { (p) -> Void in
                        mac = p[Int(sdl.pointee.sdl_nlen)..<Int(sdl.pointee.sdl_alen + sdl.pointee.sdl_nlen)].map({ (u) -> String in
                            var s = String(u, radix:16)
                            if s.count < 2 {
                                s.append("0")
                                s = String(s.reversed())
                            }
                            return s
                        }).joined(separator: ":")
                    })
                }
            }
        }
    }
    freeifaddrs(ifaddr)


    return (address, mac, addr_in)
}

func sendBroadcast(data: Data, toPort: UInt16, waitForReplayOn: sockaddr_in)->Data {

    var wifiInterface: UInt32
    var fd: Int32
    var success: Bool
    var destAddr = sockaddr_in()
    var response = Data()

    wifiInterface = if_nametoindex("en0")
    assert(wifiInterface != 0)

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    assert(fd >= 0)

    var kOne = 1
    success = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
    assert(success)

    success = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
    assert(success)

    success = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
    assert(success)

    var wait = timeval(tv_sec: 0, tv_usec: 64000)
    success = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &wait, socklen_t(MemoryLayout.size(ofValue: wait))) == 0
    assert(success)

    success = setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &wifiInterface, socklen_t(MemoryLayout.size(ofValue: wifiInterface))) == 0
    assert(success)

    var addr_in = waitForReplayOn
    success = bindresvport(fd, &addr_in) == 0
    assert(success)

    destAddr.sin_family = sa_family_t(AF_INET)
    destAddr.sin_len = __uint8_t(MemoryLayout.size(ofValue: destAddr))
    destAddr.sin_addr.s_addr = INADDR_BROADCAST
    destAddr.sin_port = in_port_t(toPort.bigEndian)

    let bytesSent = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Int in
        let destAddrSize = socklen_t(MemoryLayout.size(ofValue: destAddr))
        return withUnsafePointer(to: &destAddr) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                sendto(fd, bytes, data.count, 0, $0, destAddrSize)
            }
        }
    }

    if (bytesSent >= 0) {
        print("DHCP packet with \(bytesSent) bytes broadcasted to UDP port \(toPort)")
        var receiveBuffer = [UInt8](repeating: 0, count: 1024)
        let bytes = recv(fd, &receiveBuffer, receiveBuffer.count, 0)
        response.append(contentsOf: receiveBuffer[0..<bytes])
    } else {
        print("error", errno)
    }

    success = close(fd) == 0
    assert(success)
    return response
}


var en0info = getWiFiAddress()
var packet = dhcp_packet(/*ciaddr: en0info.ip4,*/ chaddr: en0info.mac)

let dhcp_MAGIC_COOKIE: [UInt8] = [0x63, 0x82, 0x53, 0x63]
// DHCP_OPTIONS  [code, length, value]
let dhcp_DHCPINFORM : [UInt8] = [53, 1, 8]
// we request router(s) address (it is standart report, but ... :-)
// see https://www.ietf.org/rfc/rfc1533.txt
let dhcp_PARAMETER_REQUEST_LIST: [UInt8] = [55, 1, 3]
let dhcp_OPTIONS_END: UInt8 = 0xFF

packet.append(contentsOf: dhcp_MAGIC_COOKIE)
packet.append(contentsOf: dhcp_DHCPINFORM)
packet.append(contentsOf: dhcp_PARAMETER_REQUEST_LIST)
packet.append(dhcp_OPTIONS_END)

en0info.addr.sin_len = __uint8_t(MemoryLayout.size(ofValue: sockaddr_in()))
en0info.addr.sin_port = in_port_t(UInt16(68).bigEndian)
en0info.addr.sin_addr.s_addr = INADDR_ANY

var success = false
var attempt = 5
var response = Data()

repeat {
    response = sendBroadcast(data: packet, toPort: 67, waitForReplayOn: en0info.addr)
    // if succes is false, response is not for us, or invalid
    success = response[1..<240] == packet[1..<240]
    attempt -= 1
} while success == false && attempt > 0

if success == true {

    success = false
    var index = 240
    let maxIndex = response.count
    var option = (code: UInt8, length: UInt8, value: [UInt8])(0,0,[])
    var options = [UInt8: [UInt8]]()

    repeat {
        option.code = response[index]
        index += 1
        if option.code == 0 {
            continue
        }
        if option.code == 255 {
            success = true
            break
        }
        option.length = response[index]
        index += 1
        let nexti = index + Int(option.length)
        if nexti <= maxIndex {
            option.value = Array(response[index..<nexti])
            options[option.code] = option.value
        }
        index = nexti
    } while index < maxIndex

    print(options, success ? "OK" : "incoplete")
} else {
    print("DHCPINFORM failed")
}

在我的环境中它会打印

DHCP packet with 244 bytes broadcasted to UDP port 67
[3: [192, 168, 8, 1], 6: [192, 168, 8, 1, 192, 168, 8, 1], 53: [5], 54: [192, 168, 8, 1], 1: [255, 255, 255, 0]]

其中:

选项3代表路由器列表

选项6代表DNS服务器列表

选项53表示DHCP消息类型DHCPACK

选项54表示DHCP服务器标识符(此特定响应来自何处)

选项1代表子网掩码