webrtc远程控制系统源码学习笔记(三)

1、ssh.go //主要实现ssh登录,及执行远程命令

//主要完成ssh连接,使用密码认证
func initSSH(sshUser, sshPassword string, dc *webrtc.DataChannel, rtcin chan string) (*ssh.Session, error) {
//创建sshp登陆配置
config := &ssh.ClientConfig{
	Timeout:         time.Second, //ssh 连接time out 时间一秒钟, 如果ssh验证错误 会在一秒内返回
	User:            sshUser,
	HostKeyCallback: ssh.InsecureIgnoreHostKey(), //这个可以, 但是不够安全
	//HostKeyCallback: hostKeyCallBackFunc(h.Host),
}
config.Auth = []ssh.AuthMethod{ssh.Password(sshPassword)} //获取鉴定

//dial 获取ssh client
addr := fmt.Sprintf("%s:%d", Conf.SSHHost, Conf.SSHPort)
sshClient, err := ssh.Dial("tcp", addr, config)
if err != nil {
	return nil, err
}
//创建session
sshSession, err := sshClient.NewSession()
if err != nil {
	return nil, err
}
dc.SendText("success")

//开启线程执行远程命令
go sshHandler(sshClient, sshSession, dc, rtcin)
return sshSession, nil
}

代码详解:

  1. 配置ssh.ClientConfig
    建议TimeOut自定义一个比较端的时间
    自定义HostKeyCallback 如果像简便就使用 ssh.InsecureIgnoreHostKey回调, 这种方式不是很安全
    publicKeyAuthFunc 如果使用key登陆 就需要着用这个函数量读取id_rsa私钥,当然你可以自定义这个访问让他支持字符串

  2. ssh.Dial创建ssh客户端
    拼接字符串得到ssh连接地址,同时不要忘记 defer client.Close()

  3. sshClient.NewSession 创建session 会话
    可以自定义stdin,stdout
    可以创建pty
    可以SetEnv

 //主要功能是使得可以执行交互式命令
 func sshHandler(sshClient *ssh.Client, sshSession *ssh.Session, dc *webrtc.DataChannel, rtcin chan string) error {	
	defer sshClient.Close()
	defer sshSession.Close()
    //将 session 的 Stdout 和 Stderr 重定向,与webrtc.DataChannel绑定
	sshSession.Stdout = &Wrap{dc}
	sshSession.Stderr = &Wrap{dc}
	//sshSession.Stdin = &Wrap{dc}
	//下面两行代码完成sshin的重定向
	sshin, _ := sshSession.StdinPipe()
	go stdinHandler(sshin, rtcin) //开启线程将channel rtcin的数据与sshin绑定
	// Set up terminal modes
	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}
	// Request pseudo terminal
	if err := sshSession.RequestPty("xterm", 40, 80, modes); err != nil {
		return err
	}
	// Start remote shell
	if err := sshSession.Shell(); err != nil {
		return err
	}
	if err := sshSession.Wait(); err != nil {
		return err
	}

	return nil
}

//将channel rtcin中的数据写入StdinPipe
func stdinHandler(sshin io.Writer, rtcin chan string) {
	for input := range rtcin {
		sshin.Write([]byte(input))
	}

参考资料1:https://studygolang.com/articles/20592
参考资料2:http://www.01happy.com/golang-exec-remote-command/

2、contro.go //实现远程指令功能

func controlHandler(msg []byte) string {
resq := make(map[string]string)
err := json.Unmarshal(msg, &resq)//解码
if err != nil {
	return "{\"result\":\"failure\",\"msg\":\"" + err.Error() + "\",\"timestamp\":" + strconv.FormatInt(time.Now().Unix(), 10) + "}"
}

//找到预先编写的命令文件,然后运行
if !isExist("./control/" + resq["device_id"]) {
	return "{\"result\":\"failure\",\"msg\":\"device_id not exist\",\"timestamp\":" + strconv.FormatInt(time.Now().Unix(), 10) + "}"
}

if !isExist("./control/" + resq["device_id"] + "/" + resq["operate"]) {
	return "{\"result\":\"failure\",\"msg\":\"operate not exist\",\"timestamp\":" + strconv.FormatInt(time.Now().Unix(), 10) + "}"
}

cmd := exec.Command("sh",  "./control/"+resq["device_id"]+"/"+resq["operate"])  //执行改文件夹
out, err := cmd.Output()
if err != nil {
	return "{\"result\":\"failure\",\"msg\":\"" + err.Error() + "\",\"timestamp\":" + strconv.FormatInt(time.Now().Unix(), 10) + "}"
}
return "{\"result\":\"success\",\"msg\":\"" + string(out) + "\",\"timestamp\":" + strconv.FormatInt(time.Now().Unix(), 10) + "}"
}

//查找文件是否存在
func isExist(f string) bool {
_, err := os.Stat(f)
return err == nil || os.IsExist(err)
}