假定你要做一个 SSH 与 HTTP 的服务扫描器,那你可能没有发现 SSH 与 HTTP 服务对于客户端的区别。
对 SSH 服务来说,客户端完成 TCP 连接后,就会收到服务器发送的 banner 信息,类似这样的
SSH-2.0-xxxxxx\r\n...
但是 HTTP 服务器不一样,客户端完成 TCP 连接后,必须发送一个请求,如
HEAD / HTTP/1.0\r\n\r\n
服务器才会返回内容,如
HTTP/1.0 404 NotFound\r\n
...
下面是参考代码
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"sync"
"time"
)
// 假定是 SSH 服务。
// 返回 banner 第一行。
func assume_ssh(address string) (string, error) {
conn, err := net.DialTimeout("tcp", address, time.Second*10)
if err != nil {
return "", err
}
defer conn.Close()
tcpconn := conn.(*net.TCPConn)
// 设置读取的超时时间
tcpconn.SetReadDeadline(time.Now().Add(time.Second * 5))
reader := bufio.NewReader(conn)
return reader.ReadString('\n')
}
func split_http_head(data []byte, atEOF bool) (advance int, token []byte, err error) {
head_end := bytes.Index(data, []byte("\r\n\r\n"))
if head_end == -1 {
return 0, nil, nil
}
return head_end + 4, data[:head_end+4], nil
}
// 假定是 HTTP 服务。
// 返回 "/" HTTP 返回头。
func assume_http(address string) (string, error) {
conn, err := net.DialTimeout("tcp", address, time.Second*10)
if err != nil {
return "", err
}
defer conn.Close()
tcpconn := conn.(*net.TCPConn)
// 设置写的超时时间
tcpconn.SetWriteDeadline(time.Now().Add(time.Second * 5))
if _, err := conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n")); err != nil {
return "", err
}
// 设置读的超时时间
tcpconn.SetReadDeadline(time.Now().Add(time.Second * 5))
scanner := bufio.NewScanner(conn)
scanner.Split(split_http_head)
if scanner.Scan() {
return scanner.Text(), nil
}
err = scanner.Err()
if err == nil {
err = io.EOF
}
return "", err
}
func check_address(address string) {
result := make(chan string, 2)
done := make(chan int, 1)
var g sync.WaitGroup
g.Add(2)
go func() {
if r, e := assume_ssh(address); e == nil {
result
}
g.Done()
}()
go func() {
if r, e := assume_http(address); e == nil {
result
}
g.Done()
}()
go func() {
g.Wait()
done
}()
select {
case
fmt.Printf("# %s\n无结果", address)
case r :=
fmt.Printf("# %s\n%s", address, r)
}
}
func main() {
check_address("github.com:80")
check_address("github.com:22")
}
运行结果
# github.com:80
HTTP: HTTP/1.1 301 Moved Permanently
Content-length: 0
Location: https:///
Connection: close
# github.com:22
SSH: SSH-2.0-libssh_0.7.0