obfs: added simple-obfs support. #79

This commit is contained in:
nadoo 2018-12-12 21:40:31 +08:00
parent da055c9078
commit 750862abdb
11 changed files with 497 additions and 9 deletions

View File

@ -28,6 +28,7 @@ Listen (local proxy server):
- UDP over TCP tunnel - UDP over TCP tunnel
- TLS, use it together with above proxy protocols(tcp) - TLS, use it together with above proxy protocols(tcp)
- Unix domain socket, use it together with above proxy protocols(tcp) - Unix domain socket, use it together with above proxy protocols(tcp)
- KCP protocol, use it together with above proxy protocols(tcp)
Forward (local proxy client/upstream proxy server): Forward (local proxy client/upstream proxy server):
@ -39,6 +40,8 @@ Forward (local proxy client/upstream proxy server):
- TLS, use it together with above proxy protocols(tcp) - TLS, use it together with above proxy protocols(tcp)
- Websocket, use it together with above proxy protocols(tcp) - Websocket, use it together with above proxy protocols(tcp)
- Unix domain socket, use it together with above proxy protocols(tcp) - Unix domain socket, use it together with above proxy protocols(tcp)
- KCP protocol, use it together with above proxy protocols(tcp)
- Simple-Obfs, use it together with above proxy protocols(tcp)
DNS Forwarding Server (udp2tcp): DNS Forwarding Server (udp2tcp):
@ -113,7 +116,7 @@ glider -config CONFIGPATH -listen :8080 -verbose
## Usage ## Usage
```bash ```bash
glider v0.6.9 usage: glider v0.6.10 usage:
-checkduration int -checkduration int
proxy check interval(seconds) (default 30) proxy check interval(seconds) (default 30)
-checkwebsite string -checkwebsite string
@ -166,10 +169,12 @@ Available Schemes:
udptun: udp tunnel udptun: udp tunnel
uottun: udp over tcp tunnel uottun: udp over tcp tunnel
unix: unix domain socket unix: unix domain socket
kcp: kcp protocol
simple-obfs: simple-obfs protocol
Available schemes for different modes: Available schemes for different modes:
listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp
forward: ss socks5 http ssr vmess tls ws unix forward: ss socks5 http ssr vmess tls ws unix kcp simple-bfs
SS scheme: SS scheme:
ss://method:pass@host:port ss://method:pass@host:port
@ -227,6 +232,18 @@ TLS and Websocket with a specified proxy protocol:
Unix domain socket scheme: Unix domain socket scheme:
unix://path unix://path
KCP scheme:
kcp://CRYPT:KEY@host:port[?dataShards=NUM&parityShards=NUM]
Available crypt types for KCP:
none, sm4, tea, xor, aes, aes-128, aes-192, blowfish, twofish, cast5, 3des, xtea, salsa20
Simple-Obfs scheme:
simple-obfs://host:port[?type=TYPE&host=HOST&uri=URI&ua=UA]
Available types for simple-obfs:
http, tls
DNS forwarding server: DNS forwarding server:
dns=:53 dns=:53
dnsserver=8.8.8.8:53 dnsserver=8.8.8.8:53

22
conf.go
View File

@ -122,11 +122,13 @@ func usage() {
fmt.Fprintf(os.Stderr, " udptun: udp tunnel\n") fmt.Fprintf(os.Stderr, " udptun: udp tunnel\n")
fmt.Fprintf(os.Stderr, " uottun: udp over tcp tunnel\n") fmt.Fprintf(os.Stderr, " uottun: udp over tcp tunnel\n")
fmt.Fprintf(os.Stderr, " unix: unix domain socket\n") fmt.Fprintf(os.Stderr, " unix: unix domain socket\n")
fmt.Fprintf(os.Stderr, " kcp: kcp protocol\n")
fmt.Fprintf(os.Stderr, " simple-obfs: simple-obfs protocol\n")
fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "Available schemes for different modes:\n") fmt.Fprintf(os.Stderr, "Available schemes for different modes:\n")
fmt.Fprintf(os.Stderr, " listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix\n") fmt.Fprintf(os.Stderr, " listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp\n")
fmt.Fprintf(os.Stderr, " forward: ss socks5 http ssr vmess tls ws unix\n") fmt.Fprintf(os.Stderr, " forward: ss socks5 http ssr vmess tls ws unix kcp simple-bfs\n")
fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "SS scheme:\n") fmt.Fprintf(os.Stderr, "SS scheme:\n")
@ -198,6 +200,22 @@ func usage() {
fmt.Fprintf(os.Stderr, " unix://path\n") fmt.Fprintf(os.Stderr, " unix://path\n")
fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "KCP scheme:\n")
fmt.Fprintf(os.Stderr, " kcp://CRYPT:KEY@host:port[?dataShards=NUM&parityShards=NUM]\n")
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "Available crypt types for KCP:\n")
fmt.Fprintf(os.Stderr, " none, sm4, tea, xor, aes, aes-128, aes-192, blowfish, twofish, cast5, 3des, xtea, salsa20\n")
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "Simple-Obfs scheme:\n")
fmt.Fprintf(os.Stderr, " simple-obfs://host:port[?type=TYPE&host=HOST&uri=URI&ua=UA]\n")
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "Available types for simple-obfs:\n")
fmt.Fprintf(os.Stderr, " http, tls\n")
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "DNS forwarding server:\n") fmt.Fprintf(os.Stderr, "DNS forwarding server:\n")
fmt.Fprintf(os.Stderr, " dns=:53\n") fmt.Fprintf(os.Stderr, " dns=:53\n")
fmt.Fprintf(os.Stderr, " dnsserver=8.8.8.8:53\n") fmt.Fprintf(os.Stderr, " dnsserver=8.8.8.8:53\n")

View File

@ -64,6 +64,9 @@ listen=socks5://:1080
# socks5 over unix domain socket # socks5 over unix domain socket
# listen=unix:///tmp/glider.socket,socks5:// # listen=unix:///tmp/glider.socket,socks5://
# socks5 over kcp
# listen=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,socks5://
# FORWARDERS # FORWARDERS
# ---------- # ----------
# Forwarders, we can setup multiple forwarders. # Forwarders, we can setup multiple forwarders.
@ -113,6 +116,12 @@ listen=socks5://:1080
# ss over tls # ss over tls
# forward=tls://1.1.1.1:443,ss://AEAD_CHACHA20_POLY1305:pass@ # forward=tls://1.1.1.1:443,ss://AEAD_CHACHA20_POLY1305:pass@
# ss over kcp
# forward=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,ss://AEAD_CHACHA20_POLY1305:pass@
# ss with simple-obfs
# forward=simple-obfs://1.1.1.1:443?type=tls&host=apple.com,ss://AEAD_CHACHA20_POLY1305:pass@
# socks5 over unix domain socket # socks5 over unix domain socket
# forward=unix:///tmp/glider.socket,socks5:// # forward=unix:///tmp/glider.socket,socks5://

View File

@ -16,6 +16,7 @@ import (
_ "github.com/nadoo/glider/proxy/http" _ "github.com/nadoo/glider/proxy/http"
_ "github.com/nadoo/glider/proxy/kcp" _ "github.com/nadoo/glider/proxy/kcp"
_ "github.com/nadoo/glider/proxy/mixed" _ "github.com/nadoo/glider/proxy/mixed"
_ "github.com/nadoo/glider/proxy/obfs"
_ "github.com/nadoo/glider/proxy/socks5" _ "github.com/nadoo/glider/proxy/socks5"
_ "github.com/nadoo/glider/proxy/ss" _ "github.com/nadoo/glider/proxy/ss"
_ "github.com/nadoo/glider/proxy/ssr" _ "github.com/nadoo/glider/proxy/ssr"

View File

@ -89,7 +89,7 @@ func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) {
f.IncFailures() f.IncFailures()
if f.Failures() >= f.MaxFailures() { if f.Failures() >= f.MaxFailures() {
f.Disable() f.Disable()
log.F("[forwarder] %s reaches maxfailures, set to DISABLED", f.addr) log.F("[forwarder] %s reaches maxfailures.", f.addr)
} }
} }

View File

@ -258,6 +258,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
_, code, _, ok := parseFirstLine(respTP) _, code, _, ok := parseFirstLine(respTP)
if ok && code == "200" { if ok && code == "200" {
// TODO: check here
respTP.ReadMIMEHeader() respTP.ReadMIMEHeader()
return rc, err return rc, err
} else if code == "407" { } else if code == "407" {

View File

@ -69,7 +69,7 @@ func NewKCP(s string, dialer proxy.Dialer) (*KCP, error) {
parityShards, err := strconv.ParseUint(pShards, 10, 32) parityShards, err := strconv.ParseUint(pShards, 10, 32)
if err != nil { if err != nil {
log.F("[kcp] parse dataShards err: %s", err) log.F("[kcp] parse parityShards err: %s", err)
return nil, err return nil, err
} }

78
proxy/obfs/http.go Normal file
View File

@ -0,0 +1,78 @@
package obfs
import (
"bufio"
"bytes"
"crypto/rand"
"encoding/base64"
"io"
"net"
)
// HTTPObfs struct
type HTTPObfs struct {
obfsHost string
obfsURI string
obfsUA string
}
// NewHTTPObfs returns a HTTPObfs object
func NewHTTPObfs(obfsHost, obfsURI, obfsUA string) *HTTPObfs {
return &HTTPObfs{obfsHost, obfsURI, obfsUA}
}
// HTTPObfsConn struct
type HTTPObfsConn struct {
*HTTPObfs
net.Conn
reader io.Reader
}
// NewConn returns a new obfs connection
func (p *HTTPObfs) NewConn(c net.Conn) (net.Conn, error) {
cc := &HTTPObfsConn{
Conn: c,
HTTPObfs: p,
}
// send http header to remote server
_, err := cc.writeHeader()
return cc, err
}
func (c *HTTPObfsConn) writeHeader() (int, error) {
buf := new(bytes.Buffer)
buf.Write([]byte("GET " + c.obfsURI + " HTTP/1.1\r\n"))
buf.Write([]byte("Host: " + c.obfsHost + "\r\n"))
buf.Write([]byte("User-Agent: " + c.obfsUA + "\r\n"))
buf.Write([]byte("Upgrade: websocket\r\n"))
buf.Write([]byte("Connection: Upgrade\r\n"))
p := make([]byte, 16)
rand.Read(p)
buf.Write([]byte("Sec-WebSocket-Key: " + base64.StdEncoding.EncodeToString(p) + "\r\n"))
buf.Write([]byte("\r\n"))
return c.Conn.Write(buf.Bytes())
}
func (c *HTTPObfsConn) Read(b []byte) (n int, err error) {
if c.reader == nil {
r := bufio.NewReader(c.Conn)
c.reader = r
for {
l, _, err := r.ReadLine()
if err != nil {
return 0, err
}
if len(l) == 0 {
break
}
}
}
return c.reader.Read(b)
}

109
proxy/obfs/obfs.go Normal file
View File

@ -0,0 +1,109 @@
// Package obfs implements simple-obfs of ss
package obfs
import (
"errors"
"net"
"net/url"
"github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/proxy"
)
// Obfs struct
type Obfs struct {
dialer proxy.Dialer
addr string
obfsType string
obfsHost string
obfsURI string
obfsUA string
obfsConn func(c net.Conn) (net.Conn, error)
}
func init() {
proxy.RegisterDialer("simple-obfs", NewObfsDialer)
}
// NewObfs returns a proxy struct
func NewObfs(s string, dialer proxy.Dialer) (*Obfs, error) {
u, err := url.Parse(s)
if err != nil {
log.F("parse url err: %s", err)
return nil, err
}
addr := u.Host
query := u.Query()
obfsType := query.Get("type")
if obfsType == "" {
obfsType = "http"
}
obfsHost := query.Get("host")
if obfsHost == "" {
return nil, errors.New("[obfs] host cannot be null")
}
obfsURI := query.Get("uri")
if obfsURI == "" {
obfsURI = "/"
}
obfsUA := query.Get("ua")
if obfsUA == "" {
obfsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
}
p := &Obfs{
dialer: dialer,
addr: addr,
obfsType: obfsType,
obfsHost: obfsHost,
obfsURI: obfsURI,
obfsUA: obfsUA,
}
switch obfsType {
case "http":
httpObfs := NewHTTPObfs(obfsHost, obfsURI, obfsUA)
p.obfsConn = httpObfs.NewConn
case "tls":
tlsObfs := NewTLSObfs(obfsHost)
p.obfsConn = tlsObfs.NewConn
default:
return nil, errors.New("[obfs] unknown obfs type: " + obfsType)
}
return p, nil
}
// NewObfsDialer returns a proxy dialer
func NewObfsDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
return NewObfs(s, dialer)
}
// Addr returns forwarder's address
func (s *Obfs) Addr() string { return s.addr }
// NextDialer returns the next dialer
func (s *Obfs) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
// Dial connects to the address addr on the network net via the proxy
func (s *Obfs) Dial(network, addr string) (net.Conn, error) {
c, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
log.F("[obfs] dial to %s error: %s", s.addr, err)
return nil, err
}
return s.obfsConn(c)
}
// DialUDP connects to the given address via the proxy
func (s *Obfs) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
return nil, nil, errors.New("obfs client does not support udp now")
}

257
proxy/obfs/tls.go Normal file
View File

@ -0,0 +1,257 @@
// https://www.ietf.org/rfc/rfc5246.txt
// https://golang.org/src/crypto/tls/handshake_messages.go
// NOTE:
// https://github.com/shadowsocks/simple-obfs/blob/master/src/obfs_tls.c
// The official obfs-server only checks 6 static bytes of client hello packet,
// so if we send a malformed packet, e.g: set a wrong length number of extensions,
// obfs-server will treat it as a correct packet, but in wireshak, it's malformed.
package obfs
import (
"bufio"
"bytes"
"crypto/rand"
"encoding/binary"
"io"
"net"
)
const (
lenSize = 2
chunkSize = 1 << 13 // 8192
)
// TLSObfs struct
type TLSObfs struct {
obfsHost string
}
// NewTLSObfs returns a TLSObfs object
func NewTLSObfs(obfsHost string) *TLSObfs {
return &TLSObfs{obfsHost: obfsHost}
}
// TLSObfsConn struct
type TLSObfsConn struct {
*TLSObfs
net.Conn
reqSent bool
reader *bufio.Reader
buf []byte
leftBytes int
}
// NewConn returns a new obfs connection
func (p *TLSObfs) NewConn(c net.Conn) (net.Conn, error) {
cc := &TLSObfsConn{
Conn: c,
TLSObfs: p,
buf: make([]byte, lenSize),
}
return cc, nil
}
func (c *TLSObfsConn) Write(b []byte) (int, error) {
if !c.reqSent {
c.reqSent = true
return c.handshake(b)
}
n := len(b)
for i := 0; i < n; i += chunkSize {
end := i + chunkSize
if end > n {
end = n
}
buf := new(bytes.Buffer)
buf.Write([]byte{0x17, 0x03, 0x03})
binary.Write(buf, binary.BigEndian, uint16(len(b[i:end])))
buf.Write(b[i:end])
_, err := c.Conn.Write(buf.Bytes())
if err != nil {
return 0, err
}
}
return n, nil
}
func (c *TLSObfsConn) Read(b []byte) (int, error) {
if c.reader == nil {
c.reader = bufio.NewReader(c.Conn)
// Server Hello
// TLSv1.2 Record Layer: Handshake Protocol: Server Hello (96 bytes)
// TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec (6 bytes)
c.reader.Discard(102)
}
if c.leftBytes == 0 {
// TLSv1.2 Record Layer:
// 1st packet: handshake encrypted message / following packets: application data
// 1 byte: Content Type: Handshake (22) / Application Data (23)
// 2 bytes: Version: TLS 1.2 (0x0303)
c.reader.Discard(3)
// get length
_, err := io.ReadFull(c.reader, c.buf[:lenSize])
if err != nil {
return 0, err
}
c.leftBytes = int(binary.BigEndian.Uint16(c.buf[:lenSize]))
}
readLen := len(b)
if readLen > c.leftBytes {
readLen = c.leftBytes
}
m, err := c.reader.Read(b[:readLen])
if err != nil {
return 0, err
}
c.leftBytes -= m
return m, nil
}
func (c *TLSObfsConn) handshake(b []byte) (int, error) {
buf := new(bytes.Buffer)
// prepare extension & clientHello content
bufExt, bufHello := extention(b, c.obfsHost), clientHello()
// prepare lengths
extLen := bufExt.Len()
helloLen := bufHello.Len() + 2 + extLen // 2: len(extContentLength)
handshakeLen := 4 + helloLen // 1: len(0x01) + 3: len(clientHelloContentLength)
// TLS Record Layer Begin
// Content Type: Handshake (22)
buf.WriteByte(0x16)
// Version: TLS 1.0 (0x0301)
buf.Write([]byte{0x03, 0x01})
// length
binary.Write(buf, binary.BigEndian, uint16(handshakeLen))
// Handshake Begin
// Handshake Type: Client Hello (1)
buf.WriteByte(0x01)
// length: uint24(3 bytes), but golang doesn't have this type
buf.Write([]byte{uint8(helloLen >> 16), uint8(helloLen >> 8), uint8(helloLen)})
// clientHello content
buf.Write(bufHello.Bytes())
// Extension Begin
// ext content length
binary.Write(buf, binary.BigEndian, uint16(extLen))
// ext content
buf.Write(bufExt.Bytes())
_, err := c.Conn.Write(buf.Bytes())
if err != nil {
return 0, err
}
return len(b), nil
}
func clientHello() *bytes.Buffer {
buf := new(bytes.Buffer)
// Version: TLS 1.2 (0x0303)
buf.Write([]byte{0x03, 0x03})
// Random
// https://tools.ietf.org/id/draft-mathewson-no-gmtunixtime-00.txt
random := make([]byte, 32)
rand.Read(random)
buf.Write(random)
// Session ID Length: 32
buf.WriteByte(32)
// Session ID
sessionID := make([]byte, 32)
rand.Read(sessionID)
buf.Write(sessionID)
// https://github.com/shadowsocks/simple-obfs/blob/7659eeccf473aa41eb294e92c32f8f60a8747325/src/obfs_tls.c#L57
// Cipher Suites Length: 56
buf.Write([]byte{0x00, 0x38})
// Cipher Suites (28 suites)
buf.Write([]byte{
0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f,
0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a,
0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d,
0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff,
})
// Compression Methods Length: 1
buf.WriteByte(0x01)
// Compression Methods (1 method)
buf.WriteByte(0x00)
return buf
}
func extention(b []byte, server string) *bytes.Buffer {
buf := new(bytes.Buffer)
// Extension: SessionTicket TLS
buf.Write([]byte{0x00, 0x23}) // type
// NOTE: send some data in sessionticket, the server will treat it as data too
binary.Write(buf, binary.BigEndian, uint16(len(b))) // length
buf.Write(b)
// Extension: server_name
buf.Write([]byte{0x00, 0x00}) // type
binary.Write(buf, binary.BigEndian, uint16(len(server)+5)) // length
binary.Write(buf, binary.BigEndian, uint16(len(server)+3)) // Server Name list length
buf.WriteByte(0x00) // Server Name Type: host_name (0)
binary.Write(buf, binary.BigEndian, uint16(len(server))) // Server Name length
buf.Write([]byte(server))
// https://github.com/shadowsocks/simple-obfs/blob/7659eeccf473aa41eb294e92c32f8f60a8747325/src/obfs_tls.c#L88
// Extension: ec_point_formats (len=4)
buf.Write([]byte{0x00, 0x0b}) // type
binary.Write(buf, binary.BigEndian, uint16(4)) // length
buf.WriteByte(0x03) // format length
buf.Write([]byte{0x01, 0x00, 0x02})
// Extension: supported_groups (len=10)
buf.Write([]byte{0x00, 0x0a}) // type
binary.Write(buf, binary.BigEndian, uint16(10)) // length
binary.Write(buf, binary.BigEndian, uint16(8)) // Supported Groups List Length: 8
buf.Write([]byte{0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18})
// Extension: signature_algorithms (len=32)
buf.Write([]byte{0x00, 0x0d}) // type
binary.Write(buf, binary.BigEndian, uint16(32)) // length
binary.Write(buf, binary.BigEndian, uint16(30)) // Signature Hash Algorithms Length: 30
buf.Write([]byte{
0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02,
0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
})
// Extension: encrypt_then_mac (len=0)
buf.Write([]byte{0x00, 0x16}) // type
binary.Write(buf, binary.BigEndian, uint16(0)) // length
// Extension: extended_master_secret (len=0)
buf.Write([]byte{0x00, 0x17}) // type
binary.Write(buf, binary.BigEndian, uint16(0)) // length
return buf
}

View File

@ -81,7 +81,6 @@ func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
InsecureSkipVerify: p.skipVerify, InsecureSkipVerify: p.skipVerify,
ClientSessionCache: stdtls.NewLRUClientSessionCache(64), ClientSessionCache: stdtls.NewLRUClientSessionCache(64),
MinVersion: stdtls.VersionTLS10, MinVersion: stdtls.VersionTLS10,
MaxVersion: stdtls.VersionTLS12,
} }
return p, err return p, err
@ -111,7 +110,6 @@ func NewTLSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
p.tlsConfig = &stdtls.Config{ p.tlsConfig = &stdtls.Config{
Certificates: []stdtls.Certificate{cert}, Certificates: []stdtls.Certificate{cert},
MinVersion: stdtls.VersionTLS10, MinVersion: stdtls.VersionTLS10,
MaxVersion: stdtls.VersionTLS12,
} }
p.server, err = proxy.ServerFromURL(transport[1], dialer) p.server, err = proxy.ServerFromURL(transport[1], dialer)