blob: d13c1addbd2c85ccd5e975a067c55f76ca86fda8 [file] [edit]
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ssh
import (
"bufio"
"net"
"net/http"
"strings"
"testing"
"golang.org/x/net/proxy"
)
func TestProxyDialer_Dial_connectResponseError(t *testing.T) {
// A proxy that accepts the CONNECT request and then closes without
// answering must surface a dial error, not crash the process
addr := fakeProxy(t, func(net.Conn) {})
pd := &proxyDialer{
proxy: *newProxyInfo(addr, "http", "", ""),
forward: proxy.Direct,
}
conn, err := pd.Dial("tcp", "192.0.2.1:22")
if err == nil {
conn.Close()
t.Fatal("expected an error, got nil")
}
}
func TestProxyDialer_Dial_success(t *testing.T) {
// A proxy that answers the CONNECT request with 200 must yield a connection
addr := fakeProxy(t, func(c net.Conn) {
_, _ = c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
})
pd := &proxyDialer{
proxy: *newProxyInfo(addr, "http", "", ""),
forward: proxy.Direct,
}
conn, err := pd.Dial("tcp", "192.0.2.1:22")
if err != nil {
t.Fatalf("Dial returned error: %v, want nil", err)
}
defer conn.Close()
}
func TestProxyDialer_Dial_nonOKStatus(t *testing.T) {
// A proxy that rejects the CONNECT request must surface its status code as a dial error
addr := fakeProxy(t, func(c net.Conn) {
_, _ = c.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nContent-Length: 0\r\n\r\n"))
})
pd := &proxyDialer{
proxy: *newProxyInfo(addr, "http", "", ""),
forward: proxy.Direct,
}
conn, err := pd.Dial("tcp", "192.0.2.1:22")
if err == nil {
conn.Close()
t.Fatal("expected an error, got nil")
}
if !strings.Contains(err.Error(), "407") {
t.Fatalf("error = %v, want it to mention 407", err)
}
}
// fakeProxy starts an HTTP proxy on the loopback interface and returns its address.
// It accepts one connection, reads the CONNECT request, calls respond and then
// closes: a respond that writes nothing models a proxy that hangs up without answering
func fakeProxy(t *testing.T, respond func(net.Conn)) string {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
defer close(done)
conn, err := ln.Accept()
if err != nil {
return
}
defer conn.Close()
_, _ = http.ReadRequest(bufio.NewReader(conn))
respond(conn)
}()
t.Cleanup(func() {
ln.Close()
<-done
})
return ln.Addr().String()
}