you are better than you think

udp端口探活

· by thur · Read in about 1 min · (212 Words)
udp udp port scanner port scanner

一 背景

商业客户反馈用categrafnet_response插件配置了udp探测, 遇到报错了,如图 error.png

udp是无连接的,无法用建立连接的形式判断端口。 插件最初的设计是需要配置udp的发送字符,并且配置期望返回的字符串,

[[instances]]
targets = [
      "127.0.0.1:161",
]

protocol = "udp"

## string sent to the server
  send = "hello"
## expected string in answer
  expect = "hello"

通过返回字符与期望字符是否相等,来判断端口是否连通。用户随即发了另一张图 ,用ncat 来探测端口是ok的 ncat.png

ncat 探测逻辑

先看下 ncat的udp探测逻辑

/*
 * udptest()
 * Do a few writes to see if the UDP port is there.
 * Fails once PF state table is full.
 */
int
udptest(int s)
{
	int i, t;

	if ((write(s, "X", 1) != 1) ||
	    ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED)))
		return -1;

	/* Give the remote host some time to reply. */
	for (i = 0, t = (timeout == -1) ? UDP_SCAN_TIMEOUT : (timeout / 1000);
	     i < t; i++) {
		sleep(1);
		if ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED))
			return -1;
	}
	return 1;
}

先理一下代码片段的探测逻辑,先向目标写入一个X,观察是否有ECONNREFUSED, 如果有,则表示端口没有打开; 如果没有ECONNREFUSED,则看一下timeout是否设置,没有设置,则for循环3次(UDP_SCAN_TIMEOUT),如果设置了timeout, 则for循环timeout的次数(以秒计)。再看下for循环里面,依然是每次写入一个 X ,观察是否有 ECONNREFUSED

简单来说,就是向探测目标发送一个X,观察是否有connection refused,没有的话表明目标端口是打开的(即使对端没有返回任何内容导致超时)。

实现

看完这个逻辑就简单了,我们可以用go照着实现。网络上的udp port scanner 除了发送X , 还有发送0的, 也有根据已知端口,按照协议发送数据的。简单和通用起见,还是按照ncat的逻辑来。

       msg := []byte("X")
        t := math.Max(float64(time.Duration(ins.ReadTimeout)/time.Second), 3)
		for i := 0; i < int(t); i++ {
			time.Sleep(1 * time.Second)
			_, err = conn.Write(msg)
			if err != nil && config.Config.DebugMode {
				log.Printf("E! write udp failed, address: %s, error: %s", address, err)
			}
			if err != nil && strings.Contains(err.Error(), "refused") {
				fields["result_code"] = ConnectionFailed
				return tags, fields, nil
			}
	}

完整PR见链接

Comments