-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Shannon Wynter
committed
May 18, 2016
0 parents
commit 8a7f999
Showing
4 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# TestTerminal | ||
|
||
Proof of concept terminal. | ||
|
||
This is a small component of a much larger system including authentication, access control and security - do not use it. | ||
|
||
## Getting | ||
|
||
``` | ||
go get github.com/freman/goterm | ||
``` | ||
|
||
## Running | ||
|
||
Not included is https://github.com/sourcelair/xterm.js | ||
|
||
``` | ||
cd src/github.com/freman/goterm/assets | ||
git clone https://github.com/sourcelair/xterm.js | ||
$GOPATH/bin/goterm -assets `pwd` | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>Xterm</title> | ||
<link rel="stylesheet" href="/main.css" /> | ||
<link rel="stylesheet" href="/xterm.js/src/xterm.css" /> | ||
<link rel="stylesheet" href="/xterm.js/addons/fullscreen/fullscreen.css" /> | ||
</head> | ||
<body> | ||
<div id="xterm"></div> | ||
|
||
<script src="/xterm.js/src/xterm.js" ></script> | ||
<script src="/xterm.js/addons/fit/fit.js" ></script> | ||
<script src="/xterm.js/addons/fullscreen/fullscreen.js" ></script> | ||
<script> | ||
var term; | ||
var websocket = new WebSocket("ws://" + window.location.hostname + ":" + window.location.port + "/term"); | ||
websocket.binaryType = "arraybuffer"; | ||
|
||
function ab2str(buf) { | ||
return String.fromCharCode.apply(null, new Uint8Array(buf)); | ||
} | ||
|
||
websocket.onopen = function(evt) { | ||
term = new Terminal({ | ||
screenKeys: true, | ||
useStyle: true, | ||
cursorBlink: true, | ||
}); | ||
|
||
term.on('data', function(data) { | ||
websocket.send(new TextEncoder().encode("\x00" + data)); | ||
}); | ||
|
||
term.on('resize', function(evt) { | ||
websocket.send(new TextEncoder().encode("\x01" + JSON.stringify({cols: evt.cols, rows: evt.rows}))) | ||
}); | ||
|
||
term.on('title', function(title) { | ||
document.title = title; | ||
}); | ||
|
||
term.open(document.getElementById('xterm')); | ||
term.fit(); | ||
websocket.onmessage = function(evt) { | ||
if (evt.data instanceof ArrayBuffer) { | ||
term.write(ab2str(evt.data)); | ||
} else { | ||
alert(evt.data) | ||
} | ||
} | ||
|
||
websocket.onclose = function(evt) { | ||
term.write("Session terminated"); | ||
term.destroy(); | ||
} | ||
|
||
websocket.onerror = function(evt) { | ||
if (typeof console.log == "function") { | ||
console.log(evt) | ||
} | ||
} | ||
} | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
body { | ||
font-family: helvetica, sans-serif, arial; | ||
font-size: 1em; | ||
color: #111; | ||
} | ||
|
||
#terminal-container { | ||
width: 960px; | ||
height: 600px; | ||
margin: 0 auto; | ||
padding: 2px; | ||
} | ||
|
||
#terminal-container .terminal { | ||
background-color: #111; | ||
color: #fafafa; | ||
padding: 2px; | ||
} | ||
|
||
#terminal-container .terminal .terminal-cursor { | ||
background-color: #fafafa; | ||
} | ||
|
||
/* | ||
.xterm-rows div { | ||
height: 16px; | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"io" | ||
"net/http" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"syscall" | ||
"unsafe" | ||
|
||
log "github.com/Sirupsen/logrus" | ||
"github.com/gorilla/mux" | ||
"github.com/gorilla/websocket" | ||
"github.com/kr/pty" | ||
) | ||
|
||
type windowSize struct { | ||
Rows uint16 `json:"rows"` | ||
Cols uint16 `json:"cols"` | ||
X uint16 | ||
Y uint16 | ||
} | ||
|
||
var upgrader = websocket.Upgrader{ | ||
ReadBufferSize: 1024, | ||
WriteBufferSize: 1024, | ||
} | ||
|
||
func handleWebsocket(w http.ResponseWriter, r *http.Request) { | ||
l := log.WithField("remoteaddr", r.RemoteAddr) | ||
conn, err := upgrader.Upgrade(w, r, nil) | ||
if err != nil { | ||
l.WithError(err).Error("Unable to upgrade connection") | ||
return | ||
} | ||
|
||
cmd := exec.Command("/bin/bash", "-l") | ||
cmd.Env = append(os.Environ(), "TERM=xterm") | ||
|
||
tty, err := pty.Start(cmd) | ||
if err != nil { | ||
l.WithError(err).Error("Unable to start pty/cmd") | ||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error())) | ||
return | ||
} | ||
defer tty.Close() | ||
|
||
go func() { | ||
defer conn.Close() | ||
for { | ||
buf := make([]byte, 1024) | ||
read, err := tty.Read(buf) | ||
if err != nil { | ||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error())) | ||
l.WithError(err).Error("Unable to read from pty/cmd") | ||
return | ||
} | ||
conn.WriteMessage(websocket.BinaryMessage, buf[:read]) | ||
} | ||
}() | ||
|
||
for { | ||
messageType, reader, err := conn.NextReader() | ||
if err != nil { | ||
l.WithError(err).Error("Unable to grab next reader") | ||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error())) | ||
return | ||
} | ||
|
||
if messageType == websocket.TextMessage { | ||
l.Warn("Unexpected text message") | ||
conn.WriteMessage(websocket.TextMessage, []byte("Unexpected text message")) | ||
continue | ||
} | ||
|
||
dataTypeBuf := make([]byte, 1) | ||
read, err := reader.Read(dataTypeBuf) | ||
if err != nil { | ||
l.WithError(err).Error("Unable to read message type from reader") | ||
conn.WriteMessage(websocket.TextMessage, []byte("Unable to read message type from reader")) | ||
return | ||
} | ||
|
||
if read != 1 { | ||
l.WithField("bytes", read).Error("Unexpected number of bytes read") | ||
return | ||
} | ||
|
||
switch dataTypeBuf[0] { | ||
case 0: | ||
copied, err := io.Copy(tty, reader) | ||
if err != nil { | ||
l.WithError(err).Errorf("Error after copying %d bytes", copied) | ||
} | ||
case 1: | ||
decoder := json.NewDecoder(reader) | ||
resizeMessage := windowSize{} | ||
err := decoder.Decode(&resizeMessage) | ||
if err != nil { | ||
conn.WriteMessage(websocket.TextMessage, []byte("Error decoding resize message: "+err.Error())) | ||
continue | ||
} | ||
log.WithField("resizeMessage", resizeMessage).Info("Resizing terminal") | ||
_, _, errno := syscall.Syscall( | ||
syscall.SYS_IOCTL, | ||
tty.Fd(), | ||
syscall.TIOCSWINSZ, | ||
uintptr(unsafe.Pointer(&resizeMessage)), | ||
) | ||
if errno != 0 { | ||
l.WithError(syscall.Errno(errno)).Error("Unable to resize terminal") | ||
} | ||
default: | ||
l.WithField("dataType", dataTypeBuf[0]).Error("Unknown data type") | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
var listen = flag.String("listen", "127.0.0.1:3000", "Host:port to listen on") | ||
var assetsPath = flag.String("assets", "./assets", "Path to assets") | ||
|
||
flag.Parse() | ||
|
||
r := mux.NewRouter() | ||
|
||
r.HandleFunc("/term", handleWebsocket) | ||
r.PathPrefix("/").Handler(http.FileServer(http.Dir(*assetsPath))) | ||
|
||
log.Info("Demo Websocket/Xterm terminal") | ||
log.Warn("Warning, this is a completely insecure daemon that permits anyone to connect and control your computer, please don't run this anywhere") | ||
|
||
if !(strings.HasPrefix(*listen, "127.0.0.1") || strings.HasPrefix(*listen, "localhost")) { | ||
log.Warn("Danger Will Robinson - This program has no security built in and should not be exposed beyond localhost, you've been warned") | ||
} | ||
|
||
if err := http.ListenAndServe(*listen, r); err != nil { | ||
log.WithError(err).Fatal("Something went wrong with the webserver") | ||
} | ||
} |