obfssh_client: add proxy support

support proxy type http, https(http proxy over TLS), socks5
master
fangdingjun 8 years ago
parent 231f434e73
commit c2c1be3ac3

@ -59,6 +59,15 @@ type config struct {
LocalForwards stringSlice `yaml:"local_forward"` LocalForwards stringSlice `yaml:"local_forward"`
RemoteForwards stringSlice `yaml:"remote_forward"` RemoteForwards stringSlice `yaml:"remote_forward"`
DynamicForwards stringSlice `yaml:"dynamic_forward"` DynamicForwards stringSlice `yaml:"dynamic_forward"`
Proxy proxy
}
type proxy struct {
Scheme string
Host string
Port int
SNI string
Insecure bool
} }
// loadConfig load config from config file // loadConfig load config from config file

@ -14,6 +14,32 @@
# port: 2223 # port: 2223
# proxy
# the proxy server to connect to
# supported scheme: http, https, socks5
# when scheme is http will create a tcp connection to proxy server
# and send http CONNECT request to target server
# when scheme is https will create a TLS connection to proxy server
# and send http CONNECT request to target server
# when scheme is socks5 will handshake with proxy server with socks5 protocol
# and send a connect request to target server
#
# https proxy has an sni and insecure options
# sni is the server TLS SNI name
# insecure indicates verify server certificate or not
# proxy:
# scheme: socks5
# host: 127.0.0.1
# port: 9050
# proxy:
# scheme: https
# host: www.example.com
# port: 443
# sni: example.com
# insecure: false
# obfs_method # obfs_method
# #

@ -6,7 +6,8 @@ import (
) )
func TestConfig(t *testing.T) { func TestConfig(t *testing.T) {
c, err := loadConfig("config_example.yaml") var c config
err := loadConfig(&c, "config_example.yaml")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

@ -0,0 +1,138 @@
package main
import (
"bufio"
"crypto/tls"
"fmt"
"github.com/fangdingjun/obfssh"
socks "github.com/fangdingjun/socks-go"
"net"
"net/url"
"os"
"strconv"
"strings"
)
func updateProxyFromEnv(cfg *config) {
if cfg.Proxy.Scheme != "" && cfg.Proxy.Host != "" && cfg.Proxy.Port != 0 {
obfssh.Log(obfssh.DEBUG, "proxy already specified by config, not parse environment proxy")
return
}
proxyStr := os.Getenv("http_proxy")
if proxyStr == "" {
proxyStr = os.Getenv("https_proxy")
}
if proxyStr == "" {
return
}
u, err := url.Parse(proxyStr)
if err != nil {
obfssh.Log(obfssh.DEBUG, "parse proxy from environment failed: %s", err)
return
}
cfg.Proxy.Scheme = u.Scheme
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
cfg.Proxy.Host = host
cfg.Proxy.Port = 8080
} else {
cfg.Proxy.Host = host
p, err := strconv.ParseInt(port, 10, 32)
if err == nil {
cfg.Proxy.Port = int(p)
} else {
cfg.Proxy.Port = 8080
}
}
}
func httpProxyHandshake(c net.Conn, host string, port int) error {
fmt.Fprintf(c, "CONNECT %s:%d HTTP/1.1\r\n", host, port)
fmt.Fprintf(c, "Host: %s:%d\r\n", host, port)
fmt.Fprintf(c, "User-Agent: go/1.7\r\n")
fmt.Fprintf(c, "\r\n")
r := bufio.NewReader(c)
// read status line
statusLine, err := r.ReadString('\n')
if err != nil {
return err
}
if statusLine[0:4] != "HTTP" {
return err
}
status := strings.Fields(statusLine)[1]
statusCode, err := strconv.ParseInt(status, 10, 32)
if err != nil {
return err
}
if statusCode != 200 {
return fmt.Errorf("http status error %d", statusCode)
}
// read header
for {
h, err := r.ReadString('\n')
if err != nil {
return err
}
h1 := strings.Trim(h, " \r\n")
if h1 == "" {
break
}
}
return nil
}
func dialHTTPProxy(host string, port int, p proxy) (net.Conn, error) {
c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", p.Host, p.Port))
if err = httpProxyHandshake(c, host, port); err != nil {
c.Close()
return nil, err
}
return c, nil
}
func dialHTTPSProxy(host string, port int, p proxy) (net.Conn, error) {
hostname := p.Host
if p.SNI != "" {
hostname = p.SNI
}
tlsconfig := &tls.Config{
ServerName: hostname,
InsecureSkipVerify: p.Insecure,
}
c, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", p.Host, p.Port), tlsconfig)
if err := c.Handshake(); err != nil {
c.Close()
return nil, err
}
if err = httpProxyHandshake(c, host, port); err != nil {
c.Close()
return nil, err
}
return c, nil
}
func dialSocks5Proxy(host string, port int, p proxy) (net.Conn, error) {
c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", p.Host, p.Port))
if err != nil {
c.Close()
return nil, err
}
c1 := &socks.Client{Conn: c}
if err = c1.Connect(host, uint16(port)); err != nil {
c1.Close()
return nil, err
}
return c1, err
}

@ -156,9 +156,27 @@ func main() {
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
// parse environment proxy
updateProxyFromEnv(&cfg)
rhost := net.JoinHostPort(host, fmt.Sprintf("%d", cfg.Port)) rhost := net.JoinHostPort(host, fmt.Sprintf("%d", cfg.Port))
c, err := net.Dial("tcp", rhost) var c net.Conn
if cfg.Proxy.Scheme != "" && cfg.Proxy.Host != "" && cfg.Proxy.Port != 0 {
switch cfg.Proxy.Scheme {
case "http":
c, err = dialHTTPProxy(host, cfg.Port, cfg.Proxy)
case "https":
c, err = dialHTTPSProxy(host, cfg.Port, cfg.Proxy)
case "socks5":
c, err = dialSocks5Proxy(host, cfg.Port, cfg.Proxy)
default:
err = fmt.Errorf("unsupported scheme: %s", cfg.Proxy.Scheme)
}
} else {
c, err = net.Dial("tcp", rhost)
}
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

Loading…
Cancel
Save