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
}
代码详解:
-
配置ssh.ClientConfig
建议TimeOut自定义一个比较端的时间
自定义HostKeyCallback 如果像简便就使用 ssh.InsecureIgnoreHostKey回调, 这种方式不是很安全
publicKeyAuthFunc 如果使用key登陆 就需要着用这个函数量读取id_rsa私钥,当然你可以自定义这个访问让他支持字符串 -
ssh.Dial创建ssh客户端
拼接字符串得到ssh连接地址,同时不要忘记 defer client.Close() -
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)
}