How does a go TCP connect to a http service to get a response?
goal: golang service, prepare to get banner information of ip:port.
if ip:port is http, you can get http response, if it is ssh service, you can get ssh service output information. Such as version information.
pre-condition: the corresponding tcpAddr can be accessed (the http service outputs helloworld)
question: via code
conn, err := net.DialTimeout("tcp", tcpAddr.String(),timeout)
if err != nil {
return false
}
reply := make([]byte, 4096)
conn.Read(reply)
conn.SetReadDeadline(time.Now().Add(time.Second))
byte, err := ioutil.ReadAll(conn)
fmt.Println("srv reply:" + string(byte))
defer conn.Close()
found thread blocking on read.
how can I avoid this blocking and get the corresponding endpoint banner information?
append: if SetReadDeadline, first, it will unblock after timeout. But the reply obtained is empty.
tcp is at the transport layer, and http is at the application layer
Can
be used directly in this way? This is questionable.
because I have never used it in this way, basically the server uses the http protocol to dock when the server is http,client. It's the same with tcp.
use select, multiplexing, there is no so-called blocking problem.
suppose you want to make a service scanner for SSH and HTTP, then you may not find the difference between SSH and HTTP services for clients.
for SSH services, after the client completes the TCP connection, it will receive banner information from the server, such as this
.
SSH-2.0-xxxxxx\r\n...
but the HTTP server is different. After the client completes the TCP connection, it must send a request, such as
HEAD / HTTP/1.0\r\n\r\n
the server will return the content, such as
HTTP/1.0 404 NotFound\r\n
...
here is the reference code
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 <- fmt.Sprintf("SSH: %s", r)
}
g.Done()
}()
go func() {
if r, e := assume_http(address); e == nil {
result <- fmt.Sprintf("HTTP: %s", r)
}
g.Done()
}()
go func() {
g.Wait()
done <- 1
}()
select {
case <-done:
fmt.Printf("-sharp %s\n", address)
case r := <-result:
fmt.Printf("-sharp %s\n%s", address, r)
}
}
func main() {
check_address("github.com:80")
check_address("github.com:22")
}
run results
-sharp github.com:80
HTTP: HTTP/1.1 301 Moved Permanently
Content-length: 0
Location: https:///
Connection: close
-sharp github.com:22
SSH: SSH-2.0-libssh_0.7.0