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.

Mar.01,2021

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
Menu