Commit 28089dfb authored by rui.zheng's avatar rui.zheng

add remote tcp port forwarding

parent a8bd1671
...@@ -55,7 +55,7 @@ scheme://[bind_address]:port/[host]:hostport ...@@ -55,7 +55,7 @@ scheme://[bind_address]:port/[host]:hostport
> bind_address:port - 本地监听地址 > bind_address:port - 本地监听地址
> host:hostport - 远程监听地址 > host:hostport - 远程地址
当在bind_address:port上收到连接信息,则会(通过转发链)与host:hostport建立连接,创建一条数据通道。 当在bind_address:port上收到连接信息,则会(通过转发链)与host:hostport建立连接,创建一条数据通道。
......
...@@ -52,10 +52,12 @@ func listenAndServe(arg Args) error { ...@@ -52,10 +52,12 @@ func listenAndServe(arg Args) error {
case "tls": // tls connection case "tls": // tls connection
ln, err = tls.Listen("tcp", arg.Addr, ln, err = tls.Listen("tcp", arg.Addr,
&tls.Config{Certificates: []tls.Certificate{arg.Cert}}) &tls.Config{Certificates: []tls.Certificate{arg.Cert}})
case "tcp": // TCP port forwarding case "tcp": // Local TCP port forwarding
return listenAndServeTcpForward(arg) return listenAndServeTcpForward(arg)
case "udp": // UDP port forwarding case "udp": // Local UDP port forwarding
return listenAndServeUdpForward(arg) return listenAndServeUdpForward(arg)
case "rtcp": // Remote TCP port forwarding
return serveRTcpForward(arg)
default: default:
ln, err = net.Listen("tcp", arg.Addr) ln, err = net.Listen("tcp", arg.Addr)
} }
...@@ -76,6 +78,49 @@ func listenAndServe(arg Args) error { ...@@ -76,6 +78,49 @@ func listenAndServe(arg Args) error {
} }
} }
func serveRTcpForward(arg Args) error {
if arg.Forward == "" {
ln, err := net.Listen("tcp", arg.Addr)
if err != nil {
return err
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
glog.V(LWARNING).Infoln(err)
continue
}
tc := conn.(*net.TCPConn)
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(time.Second * 60)
go handleRTcpForwardConn(conn, arg)
}
} else {
retry := 0
for {
conn, err := Connect(arg.Forward)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err)
time.Sleep((1 << uint(retry)) * time.Second)
if retry < 5 {
retry++
}
continue
}
retry = 0
if err := connectRTcpForward(conn, arg); err != nil {
conn.Close()
time.Sleep(10 * time.Second)
}
}
}
}
func listenAndServeTcpForward(arg Args) error { func listenAndServeTcpForward(arg Args) error {
ln, err := net.Listen("tcp", arg.Addr) ln, err := net.Listen("tcp", arg.Addr)
if err != nil { if err != nil {
...@@ -122,10 +167,10 @@ func listenAndServeUdpForward(arg Args) error { ...@@ -122,10 +167,10 @@ func listenAndServeUdpForward(arg Args) error {
func handleConn(conn net.Conn, arg Args) { func handleConn(conn net.Conn, arg Args) {
atomic.AddInt32(&connCounter, 1) atomic.AddInt32(&connCounter, 1)
glog.V(LINFO).Infof("%s connected, connections: %d", glog.V(LDEBUG).Infof("%s connected, connections: %d",
conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) conn.RemoteAddr(), atomic.LoadInt32(&connCounter))
if glog.V(LINFO) { if glog.V(LDEBUG) {
defer func() { defer func() {
glog.Infof("%s disconnected, connections: %d", glog.Infof("%s disconnected, connections: %d",
conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) conn.RemoteAddr(), atomic.LoadInt32(&connCounter))
...@@ -153,7 +198,7 @@ func handleConn(conn net.Conn, arg Args) { ...@@ -153,7 +198,7 @@ func handleConn(conn net.Conn, arg Args) {
case "http": case "http":
req, err := http.ReadRequest(bufio.NewReader(conn)) req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("http:", err) glog.V(LWARNING).Infoln("[http]", err)
return return
} }
handleHttpRequest(req, conn, arg) handleHttpRequest(req, conn, arg)
...@@ -162,7 +207,7 @@ func handleConn(conn net.Conn, arg Args) { ...@@ -162,7 +207,7 @@ func handleConn(conn net.Conn, arg Args) {
conn = gosocks5.ServerConn(conn, selector) conn = gosocks5.ServerConn(conn, selector)
req, err := gosocks5.ReadRequest(conn) req, err := gosocks5.ReadRequest(conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("socks5:", err) glog.V(LWARNING).Infoln("[socks5] request:", err)
return return
} }
handleSocks5Request(req, conn) handleSocks5Request(req, conn)
...@@ -177,7 +222,7 @@ func handleConn(conn net.Conn, arg Args) { ...@@ -177,7 +222,7 @@ func handleConn(conn net.Conn, arg Args) {
n, err := io.ReadAtLeast(conn, b, 2) n, err := io.ReadAtLeast(conn, b, 2)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("client:", err) glog.V(LWARNING).Infoln("[client]", err)
return return
} }
...@@ -186,26 +231,26 @@ func handleConn(conn net.Conn, arg Args) { ...@@ -186,26 +231,26 @@ func handleConn(conn net.Conn, arg Args) {
length := 2 + mn length := 2 + mn
if n < length { if n < length {
if _, err := io.ReadFull(conn, b[n:length]); err != nil { if _, err := io.ReadFull(conn, b[n:length]); err != nil {
glog.V(LWARNING).Infoln("socks5:", err) glog.V(LWARNING).Infoln("[socks5]", err)
return return
} }
} }
methods := b[2 : 2+mn] methods := b[2 : 2+mn]
method := selector.Select(methods...) method := selector.Select(methods...)
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil {
glog.V(LWARNING).Infoln("socks5 select:", err) glog.V(LWARNING).Infoln("[socks5] select:", err)
return return
} }
c, err := selector.OnSelected(method, conn) c, err := selector.OnSelected(method, conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("socks5 onselected:", err) glog.V(LWARNING).Infoln("[socks5] onselected:", err)
return return
} }
conn = c conn = c
req, err := gosocks5.ReadRequest(conn) req, err := gosocks5.ReadRequest(conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("socks5 request:", err) glog.V(LWARNING).Infoln("[socks5] request:", err)
return return
} }
handleSocks5Request(req, conn) handleSocks5Request(req, conn)
...@@ -214,7 +259,7 @@ func handleConn(conn net.Conn, arg Args) { ...@@ -214,7 +259,7 @@ func handleConn(conn net.Conn, arg Args) {
req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn)))
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("http:", err) glog.V(LWARNING).Infoln("[http]", err)
return return
} }
handleHttpRequest(req, conn, arg) handleHttpRequest(req, conn, arg)
...@@ -247,7 +292,7 @@ func Connect(addr string) (conn net.Conn, err error) { ...@@ -247,7 +292,7 @@ func Connect(addr string) (conn net.Conn, err error) {
addr += ":80" addr += ":80"
} }
if len(forwardArgs) == 0 { if len(forwardArgs) == 0 {
return net.DialTimeout("tcp", addr, time.Second*60) return net.DialTimeout("tcp", addr, time.Second*90)
} }
var end Args var end Args
...@@ -306,7 +351,7 @@ func forward(conn net.Conn, arg Args) (net.Conn, error) { ...@@ -306,7 +351,7 @@ func forward(conn net.Conn, arg Args) (net.Conn, error) {
if trans == "" { // default is tcp if trans == "" { // default is tcp
trans = "tcp" trans = "tcp"
} }
glog.Infof("forward: %s/%s %s", proto, trans, arg.Addr) glog.V(LDEBUG).Infof("forward: %s/%s %s", proto, trans, arg.Addr)
} }
var tlsUsed bool var tlsUsed bool
......
package main package main
import ( import (
"errors"
"github.com/ginuerzh/gosocks5" "github.com/ginuerzh/gosocks5"
"github.com/golang/glog" "github.com/golang/glog"
"net" "net"
...@@ -10,6 +11,7 @@ import ( ...@@ -10,6 +11,7 @@ import (
func handleTcpForward(conn net.Conn, arg Args) { func handleTcpForward(conn net.Conn, arg Args) {
defer conn.Close() defer conn.Close()
if !strings.Contains(arg.Forward, ":") { if !strings.Contains(arg.Forward, ":") {
arg.Forward += ":22" // default is ssh service arg.Forward += ":22" // default is ssh service
} }
...@@ -21,9 +23,9 @@ func handleTcpForward(conn net.Conn, arg Args) { ...@@ -21,9 +23,9 @@ func handleTcpForward(conn net.Conn, arg Args) {
} }
defer c.Close() defer c.Close()
glog.V(LINFO).Infof("[tcp-forward] %s <-> %s OK", conn.RemoteAddr(), arg.Forward) glog.V(LINFO).Infof("[tcp-forward] %s <-> %s", conn.RemoteAddr(), arg.Forward)
Transport(conn, c) Transport(conn, c)
glog.V(LINFO).Infof("[tcp-forward] %s >-< %s DISCONNECTED", conn.RemoteAddr(), arg.Forward) glog.V(LINFO).Infof("[tcp-forward] %s >-< %s", conn.RemoteAddr(), arg.Forward)
} }
func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) { func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) {
...@@ -51,7 +53,7 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar ...@@ -51,7 +53,7 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar
glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err)
return return
} }
glog.V(LINFO).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data)) glog.V(LDEBUG).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data))
b := udpPool.Get().([]byte) b := udpPool.Get().([]byte)
defer udpPool.Put(b) defer udpPool.Put(b)
...@@ -61,12 +63,12 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar ...@@ -61,12 +63,12 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar
glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err)
return return
} }
glog.V(LINFO).Infof("[udp-forward] %s <<< %s length %d", raddr, addr, n) glog.V(LDEBUG).Infof("[udp-forward] %s <<< %s length %d", raddr, addr, n)
if _, err := conn.WriteToUDP(b[:n], raddr); err != nil { if _, err := conn.WriteToUDP(b[:n], raddr); err != nil {
glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err)
} }
glog.V(LINFO).Infof("[udp-forward] %s >-< %s DONE", raddr, arg.Forward) glog.V(LINFO).Infof("[udp-forward] %s >-< %s", raddr, arg.Forward)
return return
} }
...@@ -99,29 +101,179 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar ...@@ -99,29 +101,179 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar
glog.V(LWARNING).Infof("[udp-forward] %s <- %s ASSOCIATE failured", raddr, arg.Forward) glog.V(LWARNING).Infof("[udp-forward] %s <- %s ASSOCIATE failured", raddr, arg.Forward)
return return
} }
glog.V(LINFO).Infof("[udp-forward] %s <-> %s ASSOCIATE ON %s OK", raddr, arg.Forward, rep.Addr) glog.V(LINFO).Infof("[udp-forward] %s <-> %s ASSOCIATE ON %s", raddr, arg.Forward, rep.Addr)
dgram := gosocks5.NewUDPDatagram( dgram := gosocks5.NewUDPDatagram(
gosocks5.NewUDPHeader(uint16(len(data)), 0, ToSocksAddr(faddr)), data) gosocks5.NewUDPHeader(uint16(len(data)), 0, ToSocksAddr(faddr)), data)
fconn.SetWriteDeadline(time.Now().Add(time.Second * 60)) fconn.SetWriteDeadline(time.Now().Add(time.Second * 90))
if err = dgram.Write(fconn); err != nil { if err = dgram.Write(fconn); err != nil {
glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err)
return return
} }
glog.V(LINFO).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data)) glog.V(LDEBUG).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data))
fconn.SetReadDeadline(time.Now().Add(time.Second * 60)) fconn.SetReadDeadline(time.Now().Add(time.Second * 90))
dgram, err = gosocks5.ReadUDPDatagram(fconn) dgram, err = gosocks5.ReadUDPDatagram(fconn)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err)
return return
} }
glog.V(LINFO).Infof("[udp-forward] %s <<< %s length %d", raddr, dgram.Header.Addr, len(dgram.Data)) glog.V(LDEBUG).Infof("[udp-forward] %s <<< %s length %d", raddr, dgram.Header.Addr, len(dgram.Data))
if _, err = conn.WriteToUDP(dgram.Data, raddr); err != nil { if _, err = conn.WriteToUDP(dgram.Data, raddr); err != nil {
glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err)
} }
glog.V(LINFO).Infof("[udp-forward] %s >-< %s DONE", raddr, arg.Forward) glog.V(LINFO).Infof("[udp-forward] %s >-< %s", raddr, arg.Forward)
}
func handleRTcpForwardConn(conn net.Conn, arg Args) {
defer conn.Close()
req, err := gosocks5.ReadRequest(conn)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", conn.RemoteAddr(), arg.Addr, err)
return
}
bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String())
ln, err := net.ListenTCP("tcp", bindAddr)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
rep := gosocks5.NewReply(gosocks5.Failure, nil)
if err := rep.Write(conn); err != nil {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
}
return
}
addr := ToSocksAddr(ln.Addr())
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
if err := rep.Write(conn); err != nil {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
ln.Close()
return
}
glog.V(LINFO).Infof("[rtcp] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr)
lnChan := make(chan net.Conn, 1)
go func() {
defer close(lnChan)
c, err := ln.AcceptTCP()
if err != nil {
// glog.V(LWARNING).Infof("[rtcp] %s <- %s ACCEPT : %s", conn.RemoteAddr(), addr, err)
return
}
lnChan <- c
}()
peerChan := make(chan *gosocks5.Reply, 1)
go func() {
defer close(peerChan)
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return
}
peerChan <- reply
}()
var pconn net.Conn
for {
select {
case c := <-lnChan:
ln.Close() // only accept one peer
if c == nil {
if err := gosocks5.NewReply(gosocks5.Failure, nil).Write(conn); err != nil {
glog.V(LWARNING).Infoln("[rtcp] %s <- %s : %s", conn.RemoteAddr(), addr, err)
}
glog.V(LWARNING).Infof("[rtcp] %s >-< %s : %s", conn.RemoteAddr(), addr)
return
}
glog.V(LINFO).Infof("[rtcp] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, c.RemoteAddr())
gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(c.RemoteAddr())).Write(conn)
pconn = c
lnChan = nil
ln = nil
case reply := <-peerChan:
if reply == nil {
if ln != nil {
ln.Close()
}
if pconn != nil {
pconn.Close()
}
glog.V(LWARNING).Infof("[rtcp] %s >-< %s", conn.RemoteAddr(), addr)
return
}
goto out
}
}
out:
defer pconn.Close()
glog.V(LINFO).Infof("[rtcp] %s <-> %s", conn.RemoteAddr(), pconn.RemoteAddr())
Transport(conn, pconn)
glog.V(LINFO).Infof("[rtcp] %s >-< %s", conn.RemoteAddr(), pconn.RemoteAddr())
}
func connectRTcpForward(conn net.Conn, arg Args) error {
glog.V(LINFO).Infof("[rtcp] %s - %s", arg.Addr, arg.Forward)
addr, _ := net.ResolveTCPAddr("tcp", arg.Bind)
req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(addr))
if err := req.Write(conn); err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err)
return err
}
// first reply, bind status
rep, err := gosocks5.ReadReply(conn)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err)
return err
}
if rep.Rep != gosocks5.Succeeded {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : bind on %s failure", arg.Addr, arg.Forward, arg.Bind)
return errors.New("Bind on " + arg.Bind + " failure")
}
glog.V(LINFO).Infof("[rtcp] %s - %s BIND ON %s OK", arg.Addr, arg.Forward, arg.Bind)
// second reply, peer connection
rep, err = gosocks5.ReadReply(conn)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err)
return err
}
if rep.Rep != gosocks5.Succeeded {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : peer connect failure", arg.Addr, arg.Forward)
return errors.New("peer connect failure")
}
glog.V(LINFO).Infof("[rtcp] %s <- %s PEER %s CONNECTED", conn.RemoteAddr(), req.Addr, rep.Addr)
go func() {
defer conn.Close()
lconn, err := net.Dial("tcp", arg.Addr)
if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err)
return
}
defer lconn.Close()
if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err)
return
}
glog.V(LINFO).Infof("[rtcp] %s <-> %s", arg.Addr, arg.Forward)
Transport(lconn, conn)
glog.V(LINFO).Infof("[rtcp] %s >-< %s", arg.Addr, arg.Forward)
}()
return nil
} }
...@@ -73,9 +73,9 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { ...@@ -73,9 +73,9 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
} }
} }
glog.V(LINFO).Infof("[http] %s <-> %s OK", conn.RemoteAddr(), req.Host) glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
Transport(conn, c) Transport(conn, c)
glog.V(LINFO).Infof("[http] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Host) glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
} }
func basicAuth(authInfo string) (username, password string, ok bool) { func basicAuth(authInfo string) (username, password string, ok bool) {
......
This diff is collapsed.
...@@ -22,11 +22,12 @@ func (ss *strSlice) Set(value string) error { ...@@ -22,11 +22,12 @@ func (ss *strSlice) Set(value string) error {
// admin:123456@localhost:8080 // admin:123456@localhost:8080
type Args struct { type Args struct {
Addr string // host:port Addr string // host:port
Protocol string // protocol: http/socks(5)/ss Protocol string // protocol: http/socks(5)/ss
Transport string // transport: ws(s)/tls/tcp/udp Transport string // transport: ws(s)/tls/tcp/udp/rtcp/rudp
Forward string // forward address, used by tcp/udp port forwarding Forward string // forward address, used by local tcp/udp port forwarding
User *url.Userinfo Bind string // remote binding port, used by remote tcp/udp port forwarding
User *url.Userinfo // authentication for proxy
Cert tls.Certificate // tls certificate Cert tls.Certificate // tls certificate
} }
...@@ -52,10 +53,9 @@ func parseArgs(ss []string) (args []Args) { ...@@ -52,10 +53,9 @@ func parseArgs(ss []string) (args []Args) {
} }
arg := Args{ arg := Args{
Addr: u.Host, Addr: u.Host,
User: u.User, User: u.User,
Cert: tlsCert, Cert: tlsCert,
Forward: strings.Trim(u.EscapedPath(), "/"),
} }
schemes := strings.Split(u.Scheme, "+") schemes := strings.Split(u.Scheme, "+")
...@@ -76,8 +76,14 @@ func parseArgs(ss []string) (args []Args) { ...@@ -76,8 +76,14 @@ func parseArgs(ss []string) (args []Args) {
switch arg.Transport { switch arg.Transport {
case "ws", "wss", "tls": case "ws", "wss", "tls":
case "tcp", "udp": // started from v2.1, tcp and udp are for port forwarding case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
arg.Protocol = "" arg.Forward = strings.Trim(u.EscapedPath(), "/")
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding\
if a := strings.Split(strings.Trim(u.EscapedPath(), "/"), ":"); len(a) == 3 {
arg.Forward = a[0] + ":" + a[1]
arg.Bind = ":" + a[2]
glog.V(LINFO).Infoln(arg.Forward, arg.Bind)
}
default: default:
arg.Transport = "" arg.Transport = ""
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment