You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

913 lines
21 KiB
Go

package sshd
import (
"bufio"
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"regexp"
"strings"
"sync"
"time"
"b612.me/starainrt"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
var ShellRes, ShellErr string
var ShellExit bool
type Sshd struct {
SSHC *ssh.Session
infile io.Writer
outfile io.Reader
errfile io.Reader
thread bool
counter int
}
type StarSSH struct {
Client *ssh.Client
PublicKey ssh.PublicKey
PubkeyBase64 string
user string
password string
host string
key string
port int
online bool
}
type StarShell struct {
Keyword string
UseWaitDefault bool
Session *ssh.Session
in io.Writer
out *bufio.Reader
er *bufio.Reader
outbyte []byte
errbyte []byte
lastout int64
errors error
isprint bool
isfuncs bool
iscolor bool
isecho bool
rw sync.RWMutex
funcs func(string)
}
func (this *StarShell) ShellWait(cmd string) (string, string, error) {
var outc, errc string = " ", " "
this.Clear()
defer this.Clear()
echo := "echo b7Y85R56TUY6R5UTb612"
err := this.WriteCommand(cmd)
if err != nil {
return "", "", err
}
time.Sleep(time.Millisecond * 20)
err = this.WriteCommand(echo)
if err != nil {
return "", "", err
}
for {
time.Sleep(time.Millisecond * 120)
outs := string(this.outbyte)
errs := string(this.errbyte)
outs = strings.TrimSpace(strings.ReplaceAll(outs, "\r\n", "\n"))
errs = strings.TrimSpace(strings.ReplaceAll(errs, "\r\n", "\n"))
if len(outs) >= len(cmd+"\n"+echo) && outs[0:len(cmd+"\n"+echo)] == cmd+"\n"+echo {
outs = outs[len(cmd+"\n"+echo):]
} else if len(outs) >= len(cmd) && outs[0:len(cmd)] == cmd {
outs = outs[len(cmd):]
}
if len(errs) >= len(cmd) && errs[0:len(cmd)] == cmd {
errs = errs[len(cmd):]
}
if this.UseWaitDefault {
if strings.Index(string(outs), "b7Y85R56TUY6R5UTb612") >= 0 {
list := strings.Split(string(outs), "\n")
for _, v := range list {
if strings.Index(v, "b7Y85R56TUY6R5UTb612") < 0 {
outc += v + "\n"
}
}
break
}
if strings.Index(string(errs), "b7Y85R56TUY6R5UTb612") >= 0 {
list := strings.Split(string(errs), "\n")
for _, v := range list {
if strings.Index(v, "b7Y85R56TUY6R5UTb612") < 0 {
errc += v + "\n"
}
}
break
}
}
if this.Keyword != "" {
if strings.Index(string(outs), this.Keyword) >= 0 {
list := strings.Split(string(outs), "\n")
for _, v := range list {
if strings.Index(v, this.Keyword) < 0 && strings.Index(v, "b7Y85R56TUY6R5UTb612") < 0 {
outc += v + "\n"
}
}
break
}
if strings.Index(string(errs), this.Keyword) >= 0 {
list := strings.Split(string(errs), "\n")
for _, v := range list {
if strings.Index(v, this.Keyword) < 0 && strings.Index(v, "b7Y85R56TUY6R5UTb612") < 0 {
errc += v + "\n"
}
}
break
}
}
}
return this.TrimColor(strings.TrimSpace(outc)), this.TrimColor(strings.TrimSpace(errc)), err
}
func (this *StarShell) Close() error {
return this.Session.Close()
}
func (this *StarShell) SwitchNoColor(is bool) {
this.iscolor = is
}
func (this *StarShell) SwitchEcho(is bool) {
this.isecho = is
}
func (this *StarShell) TrimColor(str string) string {
if this.iscolor {
return SedColor(str)
}
return str
}
/*
本函数控制是否在本地屏幕上打印远程Shell的输出内容[true|false]
*/
func (this *StarShell) SwitchPrint(run bool) {
this.isprint = run
}
/*
本函数控制是否立即处理远程Shell输出每一行内容[true|false]
*/
func (this *StarShell) SwitchFunc(run bool) {
this.isfuncs = run
}
func (this *StarShell) SetFunc(funcs func(string)) {
this.funcs = funcs
}
func (this *StarShell) Clear() {
defer this.rw.Unlock()
this.rw.Lock()
this.outbyte = []byte{}
this.errbyte = []byte{}
time.Sleep(time.Millisecond * 15)
}
func (this *StarShell) ShellClear(cmd string, sleep int) (string, string, error) {
defer this.Clear()
this.Clear()
return this.Shell(cmd, sleep)
}
func (this *StarShell) Shell(cmd string, sleep int) (string, string, error) {
if err := this.WriteCommand(cmd); err != nil {
return "", "", err
}
tmp1, tmp2, err := this.GetResult(sleep)
tmps := this.TrimColor(strings.TrimSpace(string(tmp1)))
if this.isecho {
n := len(strings.Split(cmd, "\n"))
if n == 1 {
list := strings.SplitN(tmps, "\n", 2)
if len(list) == 2 {
tmps = list[1]
}
} else {
list := strings.Split(tmps, "\n")
cmds := strings.Split(cmd, "\n")
for _, v := range cmds {
for k, v2 := range list {
if strings.TrimSpace(v2) == strings.TrimSpace(v) {
list[k] = ""
break
}
}
}
tmps = ""
for _, v := range list {
if v != "" {
tmps += v + "\n"
}
}
tmps = tmps[0 : len(tmps)-1]
}
}
return tmps, this.TrimColor(strings.TrimSpace(string(tmp2))), err
}
func (this *StarShell) GetResult(sleep int) ([]byte, []byte, error) {
if this.errors != nil {
this.Session.Close()
return this.outbyte, this.errbyte, this.errors
}
if sleep > 0 {
time.Sleep(time.Millisecond * time.Duration(sleep))
}
return this.outbyte, this.errbyte, nil
}
func (this *StarShell) WriteCommand(cmd string) error {
return this.Write([]byte(cmd + "\n"))
}
func (this *StarShell) Write(bstr []byte) error {
if this.errors != nil {
this.Session.Close()
return this.errors
}
_, err := this.in.Write(bstr)
return err
}
func (this *StarShell) gohub() {
go func() {
var cache []byte
for {
read, err := this.er.ReadByte()
if err != nil {
this.errors = err
return
}
this.errbyte = append(this.errbyte, read)
if this.isprint {
fmt.Print(string([]byte{read}))
}
cache = append(cache, read)
if read == '\n' {
if this.isfuncs {
go this.funcs(this.TrimColor(strings.TrimSpace(string(cache))))
cache = []byte{}
}
}
}
}()
var cache []byte
for {
read, err := this.out.ReadByte()
if err != nil {
this.errors = err
return
}
this.rw.Lock()
this.outbyte = append(this.outbyte, read)
cache = append(cache, read)
this.rw.Unlock()
if read == '\n' {
if this.isfuncs {
go this.funcs(strings.TrimSpace(string(cache)))
cache = []byte{}
}
}
if this.isprint {
fmt.Print(string([]byte{read}))
}
}
}
func (this *StarShell) GetUid() string {
res, _, _ := this.ShellWait(`id | grep -oP "(?<=uid\=)\d+"`)
return strings.TrimSpace(res)
}
func (this *StarShell) GetGid() string {
res, _, _ := this.ShellWait(`id | grep -oP "(?<=gid\=)\d+"`)
return strings.TrimSpace(res)
}
func (this *StarShell) GetUser() string {
res, _, _ := this.ShellWait(`id | grep -oP "(?<=\().*?(?=\))" | head -n 1`)
return strings.TrimSpace(res)
}
func (this *StarShell) GetGroup() string {
res, _, _ := this.ShellWait(`id | grep -oP "(?<=\().*?(?=\))" | head -n 2 | tail -n 1`)
return strings.TrimSpace(res)
}
func (this *StarSSH) NewShell() (shell *StarShell, err error) {
shell = new(StarShell)
shell.Session, err = this.NewSession()
if err != nil {
return
}
shell.in, _ = shell.Session.StdinPipe()
tmp, _ := shell.Session.StdoutPipe()
shell.out = bufio.NewReader(tmp)
tmp, _ = shell.Session.StderrPipe()
shell.er = bufio.NewReader(tmp)
err = shell.Session.Shell()
shell.isecho = true
go shell.Session.Wait()
shell.UseWaitDefault = true
shell.WriteCommand("bash")
time.Sleep(500 * time.Millisecond)
shell.WriteCommand("export PS1= ")
shell.WriteCommand("export PS2= ")
go shell.gohub()
time.Sleep(500 * time.Millisecond)
shell.Clear()
shell.Clear()
return
}
func (this *StarSSH) Connect(user, password, host, key string, port int) error {
var err error
if this.online {
this.online = false
this.Client.Close()
}
this.Client, this.PublicKey, err = Connect(user, password, host, key, port, []string{})
if err != nil {
return err
}
this.PubkeyBase64 = base64.StdEncoding.EncodeToString(this.PublicKey.Marshal())
this.online = true
this.host = host
this.password = password
this.key = key
this.port = port
return nil
}
func (this *StarSSH) Close() error {
if this.online {
return this.Client.Close()
}
return nil
}
func (this *StarSSH) NewSession() (*ssh.Session, error) {
return NewSession(this.Client)
}
func (this *StarSSH) ShellOne(cmd string) (string, error) {
newsess, err := this.NewSession()
if err != nil {
return "", err
}
data, err := newsess.CombinedOutput(cmd)
newsess.Close()
return strings.TrimSpace(string(data)), err
}
func (this *StarSSH) Exists(filepath string) bool {
res, _ := this.ShellOne(`echo 1 && [ ! -e "` + filepath + `" ] && echo 2`)
if res == "1" {
return true
} else {
return false
}
}
func (this *StarSSH) GetUid() string {
res, _ := this.ShellOne(`id | grep -oP "(?<=uid\=)\d+"`)
return strings.TrimSpace(res)
}
func (this *StarSSH) GetGid() string {
res, _ := this.ShellOne(`id | grep -oP "(?<=gid\=)\d+"`)
return strings.TrimSpace(res)
}
func (this *StarSSH) GetUser() string {
res, _ := this.ShellOne(`id | grep -oP "(?<=\().*?(?=\))" | head -n 1`)
return strings.TrimSpace(res)
}
func (this *StarSSH) GetGroup() string {
res, _ := this.ShellOne(`id | grep -oP "(?<=\().*?(?=\))" | head -n 2 | tail -n 1`)
return strings.TrimSpace(res)
}
func (this *StarSSH) IsFile(filepath string) bool {
res, _ := this.ShellOne(`echo 1 && [ ! -f "` + filepath + `" ] && echo 2`)
if res == "1" {
return true
} else {
return false
}
}
func (this *StarSSH) IsFolder(filepath string) bool {
res, _ := this.ShellOne(`echo 1 && [ ! -d "` + filepath + `" ] && echo 2`)
if res == "1" {
return true
} else {
return false
}
}
func (this *StarSSH) ShellOneShowScreen(cmd string) (string, error) {
newsess, err := this.NewSession()
if err != nil {
return "", err
}
var bytes, errbytes []byte
tmp, _ := newsess.StdoutPipe()
reader := bufio.NewReader(tmp)
tmp, _ = newsess.StderrPipe()
errder := bufio.NewReader(tmp)
err = newsess.Start(cmd)
if err != nil {
return "", err
}
c := make(chan int, 1)
go newsess.Wait()
go func() {
for {
byt, err := reader.ReadByte()
if err != nil {
break
}
fmt.Print(string([]byte{byt}))
bytes = append(bytes, byt)
}
c <- 1
}()
for {
byt, err := errder.ReadByte()
if err != nil {
break
}
fmt.Print(string([]byte{byt}))
errbytes = append(errbytes, byt)
}
_ = <-c
newsess.Close()
if len(errbytes) != 0 {
err = errors.New(strings.TrimSpace(string(errbytes)))
} else {
err = nil
}
return strings.TrimSpace(string(bytes)), err
}
func (this *StarSSH) ShellOneToFunc(cmd string, callback func(string)) (string, error) {
newsess, err := this.NewSession()
if err != nil {
return "", err
}
var bytes, errbytes []byte
tmp, _ := newsess.StdoutPipe()
reader := bufio.NewReader(tmp)
tmp, _ = newsess.StderrPipe()
errder := bufio.NewReader(tmp)
err = newsess.Start(cmd)
if err != nil {
return "", err
}
c := make(chan int, 1)
go newsess.Wait()
go func() {
for {
byt, err := reader.ReadByte()
if err != nil {
break
}
callback(string([]byte{byt}))
bytes = append(bytes, byt)
}
c <- 1
}()
for {
byt, err := errder.ReadByte()
if err != nil {
break
}
callback(string([]byte{byt}))
errbytes = append(errbytes, byt)
}
_ = <-c
newsess.Close()
if len(errbytes) != 0 {
err = errors.New(strings.TrimSpace(string(errbytes)))
} else {
err = nil
}
return strings.TrimSpace(string(bytes)), err
}
func NewTransferSession(client *ssh.Client) (*ssh.Session, error) {
session, err := client.NewSession()
return session, err
}
func NewSession(client *ssh.Client) (*ssh.Session, error) {
var session *ssh.Session
var err error
// create session
if session, err = client.NewSession(); err != nil {
return nil, err
}
modes := ssh.TerminalModes{
ssh.ECHO: 1, // 还是要强制开启
//ssh.IGNCR: 0,
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 500, 250, modes); err != nil {
return nil, err
}
return session, nil
}
func Connect(user, password, host, key string, port int, cipherList []string) (*ssh.Client, ssh.PublicKey, error) {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
config ssh.Config
err error
pubkey ssh.PublicKey
)
// get auth method
auth = make([]ssh.AuthMethod, 0)
if key == "" {
keyboardInteractiveChallenge := func(
user,
instruction string,
questions []string,
echos []bool,
) (answers []string, err error) {
if len(questions) == 0 {
return []string{}, nil
}
return []string{password}, nil
}
auth = append(auth, ssh.Password(password))
auth = append(auth, ssh.KeyboardInteractive(keyboardInteractiveChallenge))
} else {
pemBytes, err := ioutil.ReadFile(key)
if err != nil {
return nil, nil, err
}
var signer ssh.Signer
if password == "" {
signer, err = ssh.ParsePrivateKey(pemBytes)
} else {
signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
}
if err != nil {
return nil, nil, err
}
auth = append(auth, ssh.PublicKeys(signer))
}
if len(cipherList) == 0 {
config = ssh.Config{
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc", "chacha20-poly1305@openssh.com"},
}
} else {
config = ssh.Config{
Ciphers: cipherList,
}
}
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: 10 * time.Second,
Config: config,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
pubkey = key
return nil
},
}
// connet to ssh
addr = fmt.Sprintf("%s:%d", host, port)
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return nil, nil, err
}
return client, pubkey, nil
}
func ScpTransfer(src, dst string, session *ssh.Session) error {
go func() {
//Buf := make([]byte, 1024)
w, _ := session.StdinPipe()
defer w.Close()
File, err := os.Open(src)
if err != nil {
panic(err)
}
info, _ := File.Stat()
fmt.Fprintln(w, "C0777", info.Size(), info.Name())
io.Copy(w, File)
fmt.Fprintln(w, "\x00")
}()
if err := session.Run("/usr/bin/scp -qrt " + dst); err != nil {
return err
}
return nil
}
func CreateSftp(client *ssh.Client) (*sftp.Client, error) {
sftpClient, err := sftp.NewClient(client)
return sftpClient, err
}
func FtpTransferOut(localFilePath, remotePath string, sftpClient *sftp.Client) error {
srcFile, err := os.Open(localFilePath)
if err != nil {
return err
}
defer srcFile.Close()
// var remoteFileName = filepath.Base(localFilePath)
dstFile, err := sftpClient.Create(remotePath)
if err != nil {
return err
}
defer dstFile.Close()
for {
buf := make([]byte, 1024)
n, err := srcFile.Read(buf)
dstFile.Write(buf[:n])
if err == io.EOF {
break
}
}
return nil
}
func FtpTransferOutByte(localData []byte, remotePath string, sftpClient *sftp.Client) error {
dstFile, err := sftpClient.Create(remotePath)
if err != nil {
return err
}
defer dstFile.Close()
_, err = dstFile.Write(localData)
return err
}
func FtpTransferOutFunc(localFilePath, remotePath string, bufcap int, rtefunc func(float64), sftpClient *sftp.Client) error {
num := 0
srcFile, err := os.Open(localFilePath)
if err != nil {
return err
}
defer srcFile.Close()
stat, _ := os.Stat(localFilePath)
filebig := float64(stat.Size())
//var remoteFileName = filepath.Base(localFilePath)
dstFile, err := sftpClient.Create(remotePath)
if err != nil {
return err
}
defer dstFile.Close()
for {
buf := make([]byte, bufcap)
n, err := srcFile.Read(buf)
num += n
go rtefunc(float64(num) / filebig * 100)
dstFile.Write(buf[:n])
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return nil
}
func FtpTransferInByte(remotePath string, sftpClient *sftp.Client) ([]byte, error) {
dstFile, err := sftpClient.Open(remotePath)
if err != nil {
return []byte{}, err
}
defer dstFile.Close()
buf := new(bytes.Buffer)
_, err = dstFile.WriteTo(buf)
return buf.Bytes(), err
}
func FtpTransferIn(src, dst string, sftpClient *sftp.Client) error {
srcFile, err := sftpClient.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
//var localFileName = filepath.Base(src)
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
if _, err = srcFile.WriteTo(dstFile); err != nil {
return err
}
return nil
}
func FtpTransferInFunc(src, dst string, bufcap int, rtefunc func(float64), sftpClient *sftp.Client) error {
num := 0
srcFile, err := sftpClient.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
stat, _ := srcFile.Stat()
filebig := float64(stat.Size())
//var localFileName = filepath.Base(src)
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
for {
buf := make([]byte, bufcap)
n, err := srcFile.Read(buf)
num += n
go rtefunc(float64(num) / filebig * 100)
dstFile.Write(buf[:n])
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return nil
}
func Command(session *ssh.Session, cmdstr string) (string, error) {
var res bytes.Buffer
session.Stdout = &res
err := session.Run(cmdstr)
if err != nil {
return res.String(), err
}
return res.String(), nil
}
func SSHPipeShell(session *ssh.Session, cmdstr string) (*Sshd, error) {
var err error
lovessh := Sshd{}
lovessh.SSHC = session
lovessh.infile, err = lovessh.SSHC.StdinPipe()
if err != nil {
return &lovessh, err
}
lovessh.outfile, err = lovessh.SSHC.StdoutPipe()
if err != nil {
return &lovessh, err
}
lovessh.errfile, err = lovessh.SSHC.StderrPipe()
if err != nil {
return &lovessh, err
}
if err := lovessh.SSHC.Start(cmdstr); err != nil {
return &lovessh, err
}
go func() {
lovessh.SSHC.Wait()
}()
ShellExit = false
lovessh.thread = false
return &lovessh, nil
}
func SedColor(str string) string {
reg := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
//fmt.Println("regexp:", reg.Match([]byte(str)))
return string(reg.ReplaceAll([]byte(str), []byte("")))
}
func (this Sshd) GetResult(maxtime int) (string, string, bool) {
var stop bool
reader := bufio.NewReader(this.outfile)
erreader := bufio.NewReader(this.errfile)
if !this.thread {
this.thread = true
go func() {
var line2 string
var stack bool = false
stop = false
for {
if !stack {
go func() {
stack = true
if erreader.Size() > 0 {
line2, _ = erreader.ReadString('\n')
ShellErr += line2
line2 = ""
}
stack = false
}()
}
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
stop = true
break
}
this.counter++
ShellRes += line
}
}()
}
for !stop {
if maxtime > 0 {
time.Sleep(time.Millisecond * time.Duration(maxtime))
}
restr := SedColor(ShellRes)
ShellRes = ""
errstr := SedColor(ShellErr)
ShellErr = ""
return strings.TrimSpace(restr), strings.TrimSpace(errstr), false
}
ShellExit = true
this.thread = false
restr := SedColor(ShellRes)
ShellRes = ""
errstr := SedColor(ShellErr)
ShellErr = ""
return strings.TrimSpace(restr), strings.TrimSpace(errstr), true
}
func (this Sshd) Exec(cmdstr string, maxtime int) (string, string, bool) {
this.infile.Write([]byte(cmdstr + "\n"))
return this.GetResult(maxtime)
}
func (this Sshd) WriteCmd(cmdstr string) {
this.infile.Write([]byte(cmdstr + "\n"))
return
}
func (this Sshd) NeverTimeOut() {
this.infile.Write([]byte("export TMOUT=0 \n"))
}
func (this Sshd) SetBash() {
this.infile.Write([]byte("export PS1= \n"))
}
func (this Sshd) Clear() {
this.GetResult(50)
}
func (this Sshd) IsExit() bool {
return ShellExit
}
func (this Sshd) Client(sakura func(string)) {
var stop bool
reader := bufio.NewReader(this.outfile)
if !this.thread {
this.thread = true
stop = false
go func() {
for {
line, err2 := reader.ReadByte()
if err2 != nil {
stop = true
break
}
sakura(string(line))
}
this.counter++
}()
}
for !stop {
cmd := starainrt.MessageBox("", "")
if cmd == "ctrlc" {
cmd = string('\x03')
}
if cmd == `\ctrlc` {
cmd = "ctrlc"
}
this.WriteCmd(cmd)
}
}