mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
156 lines
3.3 KiB
Go
156 lines
3.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package ssh
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"golang.org/x/net/proxy"
|
|
)
|
|
|
|
// Dialer implements for SSH over HTTP Proxy.
|
|
type proxyDialer struct {
|
|
proxy proxyInfo
|
|
// forwarding Dialer
|
|
forward proxy.Dialer
|
|
}
|
|
|
|
type proxyInfo struct {
|
|
// HTTP Proxy host or host:port
|
|
host string
|
|
// HTTP Proxy scheme
|
|
scheme string
|
|
// An immutable encapsulation of username and password details for a URL
|
|
userInfo *url.Userinfo
|
|
}
|
|
|
|
func newProxyInfo(host, scheme, username, password string) *proxyInfo {
|
|
p := &proxyInfo{
|
|
host: host,
|
|
scheme: scheme,
|
|
}
|
|
|
|
p.userInfo = url.UserPassword(username, password)
|
|
|
|
if p.scheme == "" {
|
|
p.scheme = "http"
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
func (p *proxyInfo) url() *url.URL {
|
|
return &url.URL{
|
|
Scheme: p.scheme,
|
|
User: p.userInfo,
|
|
Host: p.host,
|
|
}
|
|
}
|
|
|
|
func (p *proxyDialer) Dial(network, addr string) (net.Conn, error) {
|
|
// Dial the proxy host
|
|
c, err := p.forward.Dial(network, p.proxy.host)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = c.SetDeadline(time.Now().Add(15 * time.Second))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Generate request URL to host accessed through the proxy
|
|
reqUrl := &url.URL{
|
|
Scheme: "",
|
|
Host: addr,
|
|
}
|
|
|
|
// Create a request object using the CONNECT method to instruct the proxy server to tunnel a protocol other than HTTP.
|
|
req, err := http.NewRequest("CONNECT", reqUrl.String(), nil)
|
|
if err != nil {
|
|
c.Close()
|
|
return nil, err
|
|
}
|
|
|
|
// If http proxy requires authentication, configure settings for basic authentication.
|
|
if p.proxy.userInfo.String() != "" {
|
|
username := p.proxy.userInfo.Username()
|
|
password, _ := p.proxy.userInfo.Password()
|
|
req.SetBasicAuth(username, password)
|
|
req.Header.Add("Proxy-Authorization", req.Header.Get("Authorization"))
|
|
}
|
|
|
|
// Do not close the connection after sending this request and reading its response.
|
|
req.Close = false
|
|
|
|
// Writes the request in the form expected by an HTTP proxy.
|
|
err = req.Write(c)
|
|
if err != nil {
|
|
c.Close()
|
|
return nil, err
|
|
}
|
|
|
|
res, err := http.ReadResponse(bufio.NewReader(c), req)
|
|
|
|
if err != nil {
|
|
res.Body.Close()
|
|
c.Close()
|
|
return nil, err
|
|
}
|
|
|
|
res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
c.Close()
|
|
return nil, fmt.Errorf("Connection Error: StatusCode: %d", res.StatusCode)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// NewHttpProxyDialer generate Http Proxy Dialer
|
|
func newHttpProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
|
|
var proxyUserName, proxyPassword string
|
|
if u.User != nil {
|
|
proxyUserName = u.User.Username()
|
|
proxyPassword, _ = u.User.Password()
|
|
}
|
|
|
|
pd := &proxyDialer{
|
|
proxy: *newProxyInfo(u.Host, u.Scheme, proxyUserName, proxyPassword),
|
|
forward: forward,
|
|
}
|
|
|
|
return pd, nil
|
|
}
|
|
|
|
// RegisterDialerType register schemes used by `proxy.FromURL`
|
|
func RegisterDialerType() {
|
|
proxy.RegisterDialerType("http", newHttpProxyDialer)
|
|
proxy.RegisterDialerType("https", newHttpProxyDialer)
|
|
}
|
|
|
|
// NewHttpProxyConn create a connection to connect through the proxy server.
|
|
func newHttpProxyConn(p *proxyInfo, targetAddr string) (net.Conn, error) {
|
|
pd, err := proxy.FromURL(p.url(), proxy.Direct)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
proxyConn, err := pd.Dial("tcp", targetAddr)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return proxyConn, err
|
|
}
|