From 1092739cd78c3e015c4b7f86b918af5b8b80d794 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Tue, 6 Dec 2016 18:28:15 +0800 Subject: [PATCH] add obfssh_scp --- client.go | 5 + obfssh_scp/.gitignore | 1 + obfssh_scp/README.md | 16 ++ obfssh_scp/scp.go | 371 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 obfssh_scp/.gitignore create mode 100644 obfssh_scp/README.md create mode 100644 obfssh_scp/scp.go diff --git a/client.go b/client.go index 8aab44b..ef29ef8 100644 --- a/client.go +++ b/client.go @@ -57,6 +57,11 @@ func NewClient(c net.Conn, config *ssh.ClientConfig, addr string, conf *Conf) (* return client, nil } +// Client return *ssh.Client +func (cc *Client) Client() *ssh.Client { + return cc.client +} + // Run wait ssh connection to finish func (cc *Client) Run() { select { diff --git a/obfssh_scp/.gitignore b/obfssh_scp/.gitignore new file mode 100644 index 0000000..08e177f --- /dev/null +++ b/obfssh_scp/.gitignore @@ -0,0 +1 @@ +obfssh_scp diff --git a/obfssh_scp/README.md b/obfssh_scp/README.md new file mode 100644 index 0000000..790bb33 --- /dev/null +++ b/obfssh_scp/README.md @@ -0,0 +1,16 @@ +obfssh\_scp +========= + +obfssh\_scp is a scp style sftp client support use obfssh encryption + + +usage +==== + + obfssh\_scp user@host:/path/to/file local + +or + + + obfssh\_scp local user@host:/path/to/file + diff --git a/obfssh_scp/scp.go b/obfssh_scp/scp.go new file mode 100644 index 0000000..f3e4d79 --- /dev/null +++ b/obfssh_scp/scp.go @@ -0,0 +1,371 @@ +package main + +import ( + "flag" + "fmt" + "github.com/fangdingjun/obfssh" + "github.com/kr/fs" + "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" + "io" + "io/ioutil" + "log" + "net" + "os" + "path/filepath" + "strings" + "syscall" + "time" +) + +func main() { + var user, port, pass, key string + var recursive bool + var obfsMethod, obfsKey string + var disableObfsAfterHandshake bool + + flag.StringVar(&port, "p", "22", "port") + flag.StringVar(&user, "l", os.Getenv("USER"), "user") + flag.StringVar(&pass, "pw", "", "password") + flag.StringVar(&key, "i", "", "private key") + flag.BoolVar(&recursive, "r", false, "recursively copy entries") + flag.StringVar(&obfsMethod, "obfs_method", "", "obfs encrypt method, rc4, aes or none") + flag.StringVar(&obfsKey, "obfs_key", "", "obfs encrypt key") + flag.BoolVar(&disableObfsAfterHandshake, "disable_obfs_after_handshake", false, "disable obfs after handshake") + flag.Parse() + + args := flag.Args() + + if len(args) < 2 { + fmt.Printf("Usage: \n\tscp user@host:path local\n\tor\n\tscp local... user@host:path\n") + os.Exit(1) + } + + var host, path string + r1 := "" + var toLocal = false + if strings.Contains(args[0], ":") { + toLocal = true + r1 = args[0] + } else { + toLocal = false + r1 = args[len(args)-1] + } + + if strings.Contains(r1, "@") { + ss1 := strings.SplitN(r1, "@", 2) + user = ss1[0] + r1 = ss1[1] + } + ss2 := strings.SplitN(r1, ":", 2) + if len(ss2) != 2 { + log.Fatal("Usage: \n\tscp user@host:path local\n\tor\n\tscp local... user@host:path") + } + host = ss2[0] + path = ss2[1] + + auths := []ssh.AuthMethod{} + + // read ssh agent and default auth key + if pass == "" && key == "" { + var pkeys []ssh.Signer + if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil { + //auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers)) + if signers, err := agent.NewClient(aconn).Signers(); err != nil { + pkeys = append(pkeys, signers...) + } + } + + home := os.Getenv("HOME") + for _, f := range []string{ + ".ssh/id_rsa", + ".ssh/id_dsa", + ".ssh/identity", + ".ssh/id_ecdsa", + ".ssh/id_ed25519", + } { + k1 := filepath.Join(home, f) + if pemBytes, err := ioutil.ReadFile(k1); err == nil { + if priKey, err := ssh.ParsePrivateKey(pemBytes); err == nil { + //log.Printf("add pri...") + //auths = append(auths, ssh.PublicKeys(priKey)) + pkeys = append(pkeys, priKey) + } + } + + } + if len(pkeys) != 0 { + auths = append(auths, ssh.PublicKeys(pkeys...)) + } + } + + if pass != "" { + auths = append(auths, ssh.Password(pass)) + } + + if key != "" { + if buf, err := ioutil.ReadFile(key); err == nil { + if p, err := ssh.ParsePrivateKey(buf); err == nil { + auths = append(auths, ssh.PublicKeys(p)) + } + } + } + config := &ssh.ClientConfig{ + User: user, + Auth: auths, + Timeout: 5 * time.Second, + } + + rhost := net.JoinHostPort(host, port) + + c, err := net.Dial("tcp", rhost) + if err != nil { + log.Fatal(err) + } + + conf := &obfssh.Conf{ + ObfsMethod: obfsMethod, + ObfsKey: obfsKey, + Timeout: 10 * time.Second, + KeepAliveInterval: 10 * time.Second, + KeepAliveMax: 5, + DisableObfsAfterHandshake: disableObfsAfterHandshake, + } + + conn, err := obfssh.NewClient(c, config, rhost, conf) + + //conn, err := ssh.Dial("tcp", h, config) + if err != nil { + log.Fatal(err) + } + + defer conn.Close() + + sftpConn, err := sftp.NewClient(conn.Client(), sftp.MaxPacket(64*1024)) + if err != nil { + log.Fatal(err) + } + defer sftpConn.Close() + + // download + if toLocal { + localFile := args[1] + st, err := sftpConn.Stat(path) + if err != nil { + log.Fatal(err) + } + + if st.Mode().IsDir() && !recursive { + log.Fatal("use -r to transfer the directory") + } + + st1, err := os.Stat(localFile) + if err == nil && !st1.Mode().IsDir() && st.Mode().IsDir() { + log.Fatal("can't transfer directory to file") + } + + if !st.Mode().IsDir() { + if st1.Mode().IsDir() { + bname := filepath.Base(path) + localFile = filepath.Join(localFile, bname) + } + if err := get(sftpConn, path, localFile); err != nil { + log.Fatal(err) + } + return + } + + // recursive download + rget(sftpConn, path, localFile) + + return + } + + // upload + if len(args) > 2 { + if st, err := sftpConn.Stat(path); err == nil { + if !st.Mode().IsDir() { + log.Fatal("multiple files can only been transferred to directory") + } + } else { + log.Fatalf("remote file or directory not exists") + } + } + + for i := 0; i < len(args)-1; i++ { + localFile := args[i] + st, err := os.Stat(localFile) + + // local file not exists + if err != nil { + log.Println(err) + continue + } + + // directory + if st.Mode().IsDir() { + if !recursive { + log.Printf("omit directory %s", localFile) + continue + } + // transfer directory + rput(sftpConn, localFile, path) + + // next entry + continue + } + + // file + remoteFile := filepath.Join(path, filepath.Base(localFile)) + if err := put(sftpConn, localFile, remoteFile); err != nil { + log.Printf("upload %s failed: %s", localFile, err.Error()) + } + } + +} + +func get(sftpConn *sftp.Client, remoteFile, localFile string) error { + fp, err := sftpConn.Open(remoteFile) + if err != nil { + //log.Fatal(err) + return err + } + + defer fp.Close() + + fp1, err := os.OpenFile(localFile, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_TRUNC, 0644) + if err != nil { + //log.Fatal(err) + return err + } + + defer fp1.Close() + + _, err = io.Copy(fp1, fp) + if err != nil { + //log.Fatal(err) + return err + } + + // set permission and modtime + st, _ := sftpConn.Stat(remoteFile) + if err := os.Chmod(localFile, st.Mode().Perm()); err != nil { + //log.Println(err) + return err + } + if err := os.Chtimes(localFile, st.ModTime(), st.ModTime()); err != nil { + //log.Println(err) + return err + } + return nil +} + +func put(sftpConn *sftp.Client, localFile, remoteFile string) error { + fpw, err := sftpConn.OpenFile(remoteFile, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_TRUNC) + if err != nil { + //log.Fatal(err) + return err + } + defer fpw.Close() + fpr, err := os.Open(localFile) + if err != nil { + //log.Fatal(err) + return err + } + defer fpr.Close() + _, err = io.Copy(fpw, fpr) + if err != nil { + //log.Fatal(err) + return err + } + + // set permission and modtime + st, _ := os.Stat(localFile) + if err := sftpConn.Chmod(remoteFile, st.Mode().Perm()); err != nil { + //log.Println(err) + return err + } + if err := sftpConn.Chtimes(remoteFile, st.ModTime(), st.ModTime()); err != nil { + //log.Println(err) + return err + } + return nil +} + +func rput(sftpConn *sftp.Client, localDir, remoteDir string) error { + walker := fs.Walk(localDir) + for walker.Step() { + if err := walker.Err(); err != nil { + log.Println(err) + continue + } + if st := walker.Stat(); st.Mode().IsDir() { + continue + } + p := walker.Path() + p1 := strings.Replace(p, localDir, "", 1) + p2 := filepath.Join(remoteDir, p1) + if err := makeDirs(p2, sftpConn); err != nil { + log.Println(err) + continue + } + + if err := put(sftpConn, p, p2); err != nil { + log.Printf("upload %s failed: %s", p, err.Error()) + } + } + return nil +} + +func rget(sftpConn *sftp.Client, remoteDir, localDir string) error { + walker := sftpConn.Walk(remoteDir) + for walker.Step() { + if err := walker.Err(); err != nil { + log.Println(err) + continue + } + if st := walker.Stat(); st.Mode().IsDir() { + continue + } + p := walker.Path() + p1 := strings.Replace(p, remoteDir, "", 1) + p2 := filepath.Join(localDir, p1) + if err := makeDirs(p2, fi{}); err != nil { + log.Println(err) + } + if err := get(sftpConn, p, p2); err != nil { + log.Printf("download %s failed: %s", p, err.Error()) + } + } + return nil +} + +type fi struct{} + +func (f fi) Stat(s string) (os.FileInfo, error) { + return os.Stat(s) +} + +func (f fi) Mkdir(s string) error { + return os.Mkdir(s, 0755) +} + +type fileInterface interface { + Stat(s string) (os.FileInfo, error) + Mkdir(s string) error +} + +func makeDirs(p string, c fileInterface) error { + for i := 1; i < len(p); i++ { + if p[i] == '/' { + p1 := p[:i] + if _, err := c.Stat(p1); err != nil { + if err := c.Mkdir(p1); err != nil { + return err + } + } + } + } + return nil +}