From 094a2f6cc8b823949486ae53a8821e8a7d13e8cc Mon Sep 17 00:00:00 2001 From: hzsunshx Date: Thu, 10 Jan 2019 14:24:01 +0800 Subject: [PATCH] add more code to fa share --- adb/client.go | 28 +++-- adb/conn.go | 52 +++++---- adb/tcpusb.go | 4 +- go.mod | 1 + main.go | 37 ++++++- {tunnel => tunnel-test}/main.go | 0 tunnel/tunnel.go | 185 ++++++++++++++++++++++++++++++++ 7 files changed, 274 insertions(+), 33 deletions(-) rename {tunnel => tunnel-test}/main.go (100%) create mode 100644 tunnel/tunnel.go diff --git a/adb/client.go b/adb/client.go index 4a54820..cd1ff86 100644 --- a/adb/client.go +++ b/adb/client.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "io/ioutil" - "log" "net" "os" "os/exec" @@ -27,7 +26,6 @@ func NewClient(addr string) *Client { func (c *Client) dial() (conn *ADBConn, err error) { nc, err := net.DialTimeout("tcp", c.Addr, 2*time.Second) - log.Println("dial") if err != nil { if err = c.StartServer(); err != nil { err = errors.Wrap(err, "adb start-server") @@ -55,7 +53,7 @@ func (c *Client) roundTripSingleResponse(data string) (string, error) { return "", err } defer conn.Close() - if err := conn.Check(); err != nil { + if err := conn.CheckOKAY(); err != nil { return "", err } return conn.DecodeString() @@ -113,7 +111,7 @@ func (c *Client) KillServer() error { return err } defer conn.Close() - return conn.Check() + return conn.CheckOKAY() } func (c *Client) Device(descriptor DeviceDescriptor) *Device { @@ -142,8 +140,20 @@ func (d *Device) Serial() (serial string, err error) { return } +// OpenTransport is a low level function +// Connect to adbd.exe and send :transport and check OKAY +// conn should be Close after using func (d *Device) OpenTransport() (conn *ADBConn, err error) { - return + req := "host:" + d.descriptor.getTransportDescriptor() + conn, err = d.client.roundTrip(req) + if err != nil { + return + } + conn.CheckOKAY() + if conn.Err() != nil { + conn.Close() + } + return conn, conn.Err() } func (d *Device) OpenShell(cmd string) (rwc io.ReadWriteCloser, err error) { @@ -152,9 +162,9 @@ func (d *Device) OpenShell(cmd string) (rwc io.ReadWriteCloser, err error) { if err != nil { return } - conn.Check() + conn.CheckOKAY() conn.EncodeString("shell:" + cmd) - conn.Check() + conn.CheckOKAY() if conn.Err() != nil { conn.Close() } @@ -211,11 +221,11 @@ func (d *Device) Stat(path string) (info os.FileInfo, err error) { return } defer conn.Close() - if err = conn.Check(); err != nil { + if err = conn.CheckOKAY(); err != nil { return } conn.EncodeString("sync:") - conn.Check() + conn.CheckOKAY() conn.WriteObjects("STAT", uint32(len(path)), path) id, err := conn.ReadNString(4) diff --git a/adb/conn.go b/adb/conn.go index b34f799..a56bbeb 100644 --- a/adb/conn.go +++ b/adb/conn.go @@ -13,23 +13,45 @@ import ( ) type ADBConn struct { - io.ReadWriter + rw io.ReadWriter io.Closer err error } func NewADBConn(conn net.Conn) *ADBConn { - prw := DebugProxyConn{ + proxyRW := debugProxyConn{ R: bufio.NewReader(conn), W: conn, Debug: true} return &ADBConn{ - ReadWriter: prw, - Closer: conn, + rw: proxyRW, + Closer: conn, } } +func (conn *ADBConn) Err() error { + return conn.err +} + +func (conn *ADBConn) Read(p []byte) (n int, err error) { + if conn.err != nil { + return 0, conn.err + } + n, err = conn.rw.Read(p) + conn.err = err + return +} + +func (conn *ADBConn) Write(p []byte) (n int, err error) { + if conn.err != nil { + return 0, conn.err + } + n, err = conn.rw.Write(p) + conn.err = err + return +} + func (conn *ADBConn) Encode(v []byte) error { val := string(v) return conn.EncodeString(val) @@ -100,20 +122,8 @@ func (conn *ADBConn) DecodeString() (string, error) { return conn.ReadNString(length) } -func (conn *ADBConn) Err() error { - return conn.err -} - -func (conn *ADBConn) Check() error { - if conn.err != nil { - return conn.err - } - conn.err = conn.respCheck() - return conn.err -} - -// respCheck check OKAY, or FAIL -func (conn *ADBConn) respCheck() error { +// CheckOKAY check OKAY, or FAIL +func (conn *ADBConn) CheckOKAY() error { status, _ := conn.ReadNString(4) switch status { case _OKAY: @@ -129,13 +139,13 @@ func (conn *ADBConn) respCheck() error { } } -type DebugProxyConn struct { +type debugProxyConn struct { R io.Reader W io.Writer Debug bool } -func (px DebugProxyConn) Write(data []byte) (int, error) { +func (px debugProxyConn) Write(data []byte) (int, error) { if px.Debug { m := regexp.MustCompile(`^[-:/0-9a-zA-Z ]+$`) if m.Match(data) { @@ -155,7 +165,7 @@ func reverseBytes(b []byte) []byte { return out } -func (px DebugProxyConn) Read(data []byte) (int, error) { +func (px debugProxyConn) Read(data []byte) (int, error) { n, err := px.R.Read(data) if px.Debug { m := regexp.MustCompile(`^[-:/0-9a-zA-Z ]+$`) diff --git a/adb/tcpusb.go b/adb/tcpusb.go index 2117e48..c26112d 100644 --- a/adb/tcpusb.go +++ b/adb/tcpusb.go @@ -46,7 +46,7 @@ func (s *Session) writePacket(cmd string, arg0, arg1 uint32, body []byte) error return err } -func (s *Session) handle() { +func (s *Session) Serve() { defer s.conn.Close() pr := NewPacketReader(s.conn) @@ -155,6 +155,6 @@ func Serve(l net.Listener) error { return err } sess := NewSession(conn) - go sess.handle() + go sess.Serve() } } diff --git a/go.mod b/go.mod index 1e8e9bb..a2e8113 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/qiniu/log v0.0.0-20140728010919-a304a74568d6 github.com/shogo82148/androidbinary v0.0.0-20180627093851-01c4bfa8b3b5 github.com/stretchr/testify v1.2.2 + golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect golang.org/x/tools v0.0.0-20181214171254-3c39ce7b6105 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.27 diff --git a/main.go b/main.go index caa7272..88e3270 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" "log" + "net" + "net/http" "os" "os/exec" "path/filepath" @@ -14,10 +16,13 @@ import ( "runtime" "strings" "syscall" + "time" shellquote "github.com/kballard/go-shellquote" tty "github.com/mattn/go-tty" + "github.com/codeskyblue/fa/adb" + "github.com/codeskyblue/fa/tunnel" "github.com/manifoldco/promptui" cli "gopkg.in/urfave/cli.v1" ) @@ -448,7 +453,37 @@ func main() { Name: "share", Usage: "TODO: share device as address for adb connect", Action: func(ctx *cli.Context) error { - log.Println("NotImplemented") + // log.Println("NotImplemented") + // return nil + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, "Hello world!") + }) + + c := &tunnel.Configuration{ + Host: "labstack.me:22", + RemoteHost: "0.0.0.0", + RemotePort: 8000, + Channel: make(chan int), + InBoundConnectionHook: func(in net.Conn) error { + log.Println("Accept new connection", in.RemoteAddr().String()) + // http.Serve() + // http.Serve + adb.NewSession(in).Serve() + io.WriteString(in, "Nice to meed you") + in.Close() + return nil + }, + } + + CREATE: + go tunnel.Create(c) + event := <-c.Channel + if event == tunnel.EventReconnect { + log.Println("trying to reconnect") + time.Sleep(1 * time.Second) + goto CREATE + } return nil // serial, err := chooseOne() // if err != nil { diff --git a/tunnel/main.go b/tunnel-test/main.go similarity index 100% rename from tunnel/main.go rename to tunnel-test/main.go diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go new file mode 100644 index 0000000..43f0a42 --- /dev/null +++ b/tunnel/tunnel.go @@ -0,0 +1,185 @@ +package tunnel + +import ( + "bufio" + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + + "github.com/qiniu/log" + "golang.org/x/crypto/ssh" +) + +type ( + Configuration struct { + Protocol string `json:"protocol"` + Subdomain string `json:"subdomain"` + Domain string `json:"domain"` + Port int `json:"port"` + Host string `json:"host"` + User string + RemoteHost string + RemotePort int + TargetHost string + TargetPort int + InBoundConnectionHook func(net.Conn) error + Channel chan int + HideBanner bool + } + + Error struct { + Code int `json:"code,omitempty"` + Message string `json:"message"` + } +) + +const ( + _ = iota + EventReconnect +) + +var ( + hostBytes = []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDoSLknvlFrFzroOlh1cqvcIFelHO+Wvj1UZ/p3J9bgsJGiKfh3DmBqEw1DOEwpHJz4zuV375TyjGuHuGZ4I4xztnwauhFplfEvriVHQkIDs6UnGwJVr15XUQX04r0i6mLbJs5KqIZTZuZ9ZGOj7ZWnaA7C07nPHGrERKV2Fm67rPvT6/qFikdWUbCt7KshbzdwwfxUohmv+NI7vw2X6vPU8pDaNEY7vS3YgwD/WlvQx+WDF2+iwLVW8OWWjFuQso6Eg1BSLygfPNhAHoiOWjDkijc8U9LYkUn7qsDCnvJxCoTTNmdECukeHfzrUjTSw72KZoM5KCRV78Wrctai1Qn6yRQz9BOSguxewLfzHtnT43/MLdwFXirJ/Ajquve2NAtYmyGCq5HcvpDAyi7lQ0nFBnrWv5zU3YxrISIpjovVyJjfPx8SCRlYZwVeUq6N2yAxCzJxbElZPtaTSoXBIFtoas2NXnCWPgenBa/2bbLQqfgbN8VQ9RaUISKNuYDIn4+eO72+RxF9THzZeV17pnhTVK88XU4asHot1gXwAt4vEhSjdUBC9KUIkfukI6F4JFxtvuO96octRahdV1Qg0vF+D0+SPy2HxqjgZWgPE2Xh/NmuIXwbE0wkymR2wrgj8Hd4C92keo2NBRh9dD7D2negnVYaYsC+3k/si5HNuCHnHQ== tunnel@labstack.com") +) + +func Create(c *Configuration) { + hostKey, _, _, _, err := ssh.ParseAuthorizedKey(hostBytes) + if err != nil { + log.Fatalf("failed to parse host key: %v", err) + } + config := &ssh.ClientConfig{ + User: c.User, + Auth: []ssh.AuthMethod{ + ssh.Password("password"), + }, + HostKeyCallback: ssh.FixedHostKey(hostKey), + BannerCallback: func(message string) error { + if !c.HideBanner { + fmt.Print(message) + } + return nil + }, + } + client := new(ssh.Client) + + // Connect + proxy := os.Getenv("http_proxy") + if proxy != "" { + proxyURL, err := url.Parse(proxy) + if err != nil { + log.Fatalf("cannot open new session: %v", err) + } + tcp, err := net.Dial("tcp", proxyURL.Hostname()) + if err != nil { + log.Fatalf("cannot open new session: %v", err) + } + connReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Path: c.Host}, + Host: c.Host, + Header: make(http.Header), + } + if proxyURL.User != nil { + if p, ok := proxyURL.User.Password(); ok { + connReq.SetBasicAuth(proxyURL.User.Username(), p) + } + } + connReq.Write(tcp) + resp, err := http.ReadResponse(bufio.NewReader(tcp), connReq) + if err != nil { + log.Fatalf("cannot open new session: %v", err) + } + defer resp.Body.Close() + + conn, chans, reqs, err := ssh.NewClientConn(tcp, c.Host, config) + if err != nil { + log.Fatalf("cannot open new session: %v", err) + } + client = ssh.NewClient(conn, chans, reqs) + } else { + client, err = ssh.Dial("tcp", c.Host, config) + } + if err != nil { + log.Errorf("failed to connect: %v", err) + c.Channel <- EventReconnect + return + } + defer client.Close() + + // Session + sess, err := client.NewSession() + if err != nil { + log.Fatalf("failed to create session: %v", err) + } + defer sess.Close() + r, err := sess.StdoutPipe() + if err != nil { + log.Print(err) + } + br := bufio.NewReader(r) + go func() { + for { + line, _, err := br.ReadLine() + if err != nil { + if err == io.EOF { + c.Channel <- EventReconnect + return + } else { + log.Fatalf("failed to read: %v", err) + } + } + fmt.Printf("%s\n", line) + } + }() + + // Remote listener + ln, err := client.Listen("tcp", fmt.Sprintf("%s:%d", c.RemoteHost, c.RemotePort)) + if err != nil { + log.Fatalf("failed to listen on remote host: %v", err) + } + defer ln.Close() + + for { + // Handle inbound connection + in, err := ln.Accept() + if err != nil { + log.Printf("failed to accept connection: %v", err) + return + } + + if c.InBoundConnectionHook != nil { + go c.InBoundConnectionHook(in) + continue + } + + go func(in net.Conn) { + defer in.Close() + + // Target connection + out, err := net.Dial("tcp", fmt.Sprintf("%s:%d", c.TargetHost, c.TargetPort)) + if err != nil { + log.Printf("failed to connect to target: %v", err) + return + } + defer out.Close() + + // Copy + errCh := make(chan error, 2) + cp := func(dst io.Writer, src io.Reader) { + _, err := io.Copy(dst, src) + errCh <- err + } + go cp(in, out) + go cp(out, in) + + // Handle error + err = <-errCh + if err != nil && err != io.EOF { + log.Printf("failed to copy: %v", err) + } + }(in) + } +}