16 March 2015

IMG-THUMBNAIL

用Golang实现Fing的功能。

之前用了一个牛逼的App,叫做Fing,它可以获取到本地局域网里面所有设备的IP、MAC地址和设备厂商。一直觉得很牛逼,今天好好想了想,发现也没那么多神秘。

穷举局域网里的IP

局域网IP一般有19210两种形式。一般来说,10开始的局域网高端一些,能容纳的设备比较多。

穷举所有IP,有一个方式是通过trancert命令,记录访问某个网站经过的路径,那么第一条路径就是访问路由器,得到路由器IP之后,按最后一部分进行穷举。

但是这个方法有点麻烦,简化一点的,就是获取当前设备在局域网里面的IP,以此IP进行穷举。通过Golang获取IP的方法可以参考[1]。获取当前设备IP的方式可以参考:

func ExternalIP() (string, string, error) {
	ifaces, err := net.Interfaces()
	if err != nil {
		return "", "", err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue // not an ipv4 address
			}
			return ip.String(), iface.HardwareAddr.String(), nil
		}
	}
	return "", "", errors.New("are you connected to the network?")
}

Ping穷举的IP,获取MAC地址。

Ping穷举的IP地址,得到在线设备的IP,然后通过arp协议得到设备的MAC地址。arp协议就是用来将IP转换成MAC的协议。这两步可以用在GitHub开源的github.com/j-keck/arping包辅助实现,这个包的文档可以参考[2]。简单的使用如下:

func Mac(ip string) (net.HardwareAddr, time.Duration, error) {
	dstIP := net.ParseIP(ip)
	return arping.Ping(dstIP)
}

获取设备的厂商,也就是俗称的Vendor

这步逼格最高。我一直以为设备的Vendor,就像HTTP协议里面的UserAgent,可以通过这个东西来获取。所以有个协议可以用来得到这些东西。但是没找到。后来发现百度知道(没想到这个东西还是有点用的)里面有位大哥写了。

MAC地址是国际上有个机构管理(好像是IEEE)的,它能保证每块网卡有不同的MAC地址。而它在分配这些地址的时候,也不是随机分配的。MAC地址有6个字节,简单的可以把前三个字节用来表示厂商编号,后3个字节用来区分厂商的网卡编号。那么,我们就能通过前3个字节来得到设备的Vendor

在IEEE的网站上,提供了查询方式,我把那个接口提取了出来,是向http://standards.ieee.org/cgi-bin/ouisearch发送一个POSTFORM请求。还要将得到的HTML页面进行解析,就得到了Vendor

func Vendor(mac string) (string, error) {
	macs := strings.Split(mac, ":")
	if len(macs) != 6 {
		return "", fmt.Errorf("MAC Error: %s", mac)
	}
	mac = strings.Join(macs[0:3], "-")
	form := url.Values{}
	form.Add("x", mac)
	form.Add("submit2", "Search!")
	res, err := goreq.Request{
		Method:      "POST",
		Uri:         "http://standards.ieee.org/cgi-bin/ouisearch",
		ContentType: "application/x-www-form-urlencoded",
		UserAgent:   "Cyeam",
		ShowDebug:   true,
		Body:        form.Encode(),
	}.Do()
	if err != nil {
		return "", err
	}
	body, err := res.Body.ToString()
	if err != nil {
		return "", err
	}
	vendor := body[strings.Index(body, strings.ToUpper(mac))+len(mac):]
	vendor = strings.TrimLeft(vendor, "</b>   (hex)")
	vendor = strings.TrimSpace(vendor)
	return strings.Split(vendor, "\n")[0], nil
}

HTTP请求通过goreq发送,FORM请求application/x-www-form-urlencoded格式,还需要用的net/url包。

结果

最后放上运行结果:

IMG-THUMBNAIL

第一个是我当前运行的设备,网卡是Intel的,剩下的是路由器和手机。

我将上述完整的功能进行封装,本文所涉及到的完整源码请参考


参考文献
  1. How do I get the local IP address in Go? - Stackoverflow
  2. package arping - GoDoc
  3. go如何读取MAC地址或硬盘ID - Golang中国
  4. 如何通过MAC地址查询网络设备的厂家和型号 - 百度知道

原文链接:探测局域网里面的设备,转载请注明来源!

EOF