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
0 parents
commit 9f0a147
Showing
27 changed files
with
7,868 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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,8 @@ | |||
**/node_modules/ | |||
.DS_Store | |||
bundle.js | |||
playground/ | |||
.vscode/ | |||
tty_sender | |||
tty_server | |||
tmp-* |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,33 @@ | |||
TTY_SERVER=tty_server | |||
TTY_SENDER=tty_sender | |||
|
|||
TTY_SERVER_SRC=$(wildcard ./tty-server/*.go) | |||
TTY_SENDER_SRC=$(wildcard ./tty-sender/*.go) | |||
EXTRA_BUILD_DEPS=$(wildcard ./common/*go) | |||
|
|||
all: $(TTY_SERVER) $(TTY_SENDER) | |||
@echo "All done" | |||
|
|||
$(TTY_SERVER): $(TTY_SERVER_SRC) $(EXTRA_BUILD_DEPS) | |||
go build -o $@ $(TTY_SERVER_SRC) | |||
|
|||
$(TTY_SENDER): $(TTY_SENDER_SRC) $(EXTRA_BUILD_DEPS) | |||
go build -o $@ $(TTY_SENDER_SRC) | |||
|
|||
frontend: FORCE | |||
cd frontend && npm run build && cd - | |||
|
|||
clean: | |||
@rm -f $(TTY_SERVER) $(TTY_SENDER) | |||
@echo "Cleaned" | |||
|
|||
runs: $(TTY_SERVER) | |||
@./$(TTY_SERVER) --url http://localhost:9090 --web_address :9090 --sender_address :7654 | |||
|
|||
runc: $(TTY_SENDER) | |||
@./$(TTY_SENDER) --logfile output.log | |||
|
|||
test: | |||
@go test github.com/elisescu/tty-share/testing -v | |||
|
|||
FORCE: |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,24 @@ | |||
TTY Share | |||
======== | |||
|
|||
A small tool to allow sharing a terminal command with others via Internet. | |||
Shortly, the user can start a command in the terminal and then others can watch that command via | |||
Internet in the browser. | |||
More info to come. | |||
|
|||
|
|||
Run the code | |||
=========== | |||
|
|||
* Build the frontend | |||
``` | |||
cd tty-share/frontend | |||
npm install | |||
npm run build | |||
``` | |||
|
|||
* Run the server | |||
``` | |||
cd tty-share | |||
make run | |||
``` |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,132 @@ | |||
package common | |||
|
|||
import ( | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"io" | |||
) | |||
|
|||
type ProtocolMessageIDType string | |||
|
|||
const ( | |||
MsgIDSenderInitRequest = "SenderInitRequest" | |||
MsgIDSenderInitReply = "SenderInitReply" | |||
MsgIDReceiverInitRequest = "ReceiverInitRequest" | |||
MsgIDReceiverInitReply = "ReceiverInitReply" | |||
MsgIDWrite = "Write" | |||
MsgIDWinSize = "WinSize" | |||
) | |||
|
|||
type MsgAll struct { | |||
Type ProtocolMessageIDType | |||
Data []byte | |||
} | |||
|
|||
type MsgTTYSenderInitRequest struct { | |||
Salt string | |||
PasswordVerifierA string | |||
} | |||
|
|||
type MsgTTYSenderInitReply struct { | |||
ReceiverURLWebReadWrite string | |||
} | |||
|
|||
type MsgTTYReceiverInitRequest struct { | |||
ChallengeReply string | |||
} | |||
|
|||
type MsgTTYReceiverInitReply struct { | |||
} | |||
|
|||
type MsgTTYWrite struct { | |||
Data []byte | |||
Size int | |||
} | |||
|
|||
type MsgTTYWinSize struct { | |||
Cols int | |||
Rows int | |||
} | |||
|
|||
func ReadAndUnmarshalMsg(reader io.Reader, aMessage interface{}) (err error) { | |||
var wrapperMsg MsgAll | |||
// Wait here for the right message to come | |||
dec := json.NewDecoder(reader) | |||
err = dec.Decode(&wrapperMsg) | |||
|
|||
if err != nil { | |||
return errors.New("Cannot decode message: " + err.Error()) | |||
} | |||
|
|||
err = json.Unmarshal(wrapperMsg.Data, aMessage) | |||
|
|||
if err != nil { | |||
return errors.New("Cannot decode message: " + err.Error()) | |||
} | |||
return | |||
} | |||
|
|||
func MarshalMsg(aMessage interface{}) (_ []byte, err error) { | |||
var msg MsgAll | |||
|
|||
if initRequestMsg, ok := aMessage.(MsgTTYSenderInitRequest); ok { | |||
msg.Type = MsgIDSenderInitRequest | |||
msg.Data, err = json.Marshal(initRequestMsg) | |||
if err != nil { | |||
return | |||
} | |||
return json.Marshal(msg) | |||
} | |||
|
|||
if initReplyMsg, ok := aMessage.(MsgTTYSenderInitReply); ok { | |||
msg.Type = MsgIDSenderInitReply | |||
msg.Data, err = json.Marshal(initReplyMsg) | |||
if err != nil { | |||
return | |||
} | |||
return json.Marshal(msg) | |||
} | |||
|
|||
if writeMsg, ok := aMessage.(MsgTTYWrite); ok { | |||
msg.Type = MsgIDWrite | |||
msg.Data, err = json.Marshal(writeMsg) | |||
//fmt.Printf("Sent write message %s\n", string(writeMsg.Data)) | |||
if err != nil { | |||
return | |||
} | |||
return json.Marshal(msg) | |||
} | |||
|
|||
if winChangedMsg, ok := aMessage.(MsgTTYWinSize); ok { | |||
msg.Type = MsgIDWinSize | |||
msg.Data, err = json.Marshal(winChangedMsg) | |||
if err != nil { | |||
return | |||
} | |||
return json.Marshal(msg) | |||
} | |||
|
|||
return nil, nil | |||
} | |||
|
|||
func MarshalAndWriteMsg(writer io.Writer, aMessage interface{}) (err error) { | |||
b, err := MarshalMsg(aMessage) | |||
|
|||
if err != nil { | |||
return | |||
} | |||
|
|||
n, err := writer.Write(b) | |||
|
|||
if n != len(b) { | |||
err = fmt.Errorf("Unable to write : wrote %d out of %d bytes", n, len(b)) | |||
return | |||
} | |||
|
|||
if err != nil { | |||
return | |||
} | |||
|
|||
return | |||
} |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,118 @@ | |||
package common | |||
|
|||
import ( | |||
"encoding/json" | |||
"io" | |||
) | |||
|
|||
type ServerSessionInfo struct { | |||
URLWebReadWrite string | |||
} | |||
|
|||
type ReceiverSessionInfo struct { | |||
} | |||
|
|||
type SenderSessionInfo struct { | |||
Salt string | |||
PasswordVerifierA string | |||
} | |||
|
|||
// TTYProtocolConn is the interface used to communicate with the sending (master) side of the TTY session | |||
type TTYProtocolConn struct { | |||
netConnection io.ReadWriteCloser | |||
jsonDecoder *json.Decoder | |||
} | |||
|
|||
func NewTTYProtocolConn(conn io.ReadWriteCloser) *TTYProtocolConn { | |||
return &TTYProtocolConn{ | |||
netConnection: conn, | |||
jsonDecoder: json.NewDecoder(conn), | |||
} | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) ReadMessage() (msg MsgAll, err error) { | |||
// TODO: perhaps read here the error, and transform it to something that's understandable | |||
// from the outside in the context of this object | |||
err = protoConn.jsonDecoder.Decode(&msg) | |||
return | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) SetWinSize(cols, rows int) error { | |||
msgWinChanged := MsgTTYWinSize{ | |||
Cols: cols, | |||
Rows: rows, | |||
} | |||
return MarshalAndWriteMsg(protoConn.netConnection, msgWinChanged) | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) Close() error { | |||
return protoConn.netConnection.Close() | |||
} | |||
|
|||
// Function to send data from one the sender to the server and the other way around. | |||
func (protoConn *TTYProtocolConn) Write(buff []byte) (int, error) { | |||
msgWrite := MsgTTYWrite{ | |||
Data: buff, | |||
Size: len(buff), | |||
} | |||
return len(buff), MarshalAndWriteMsg(protoConn.netConnection, msgWrite) | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) WriteRawData(buff []byte) (int, error) { | |||
return protoConn.netConnection.Write(buff) | |||
} | |||
|
|||
// Function to be called on the sender side, and which blocks until the protocol has been | |||
// initialised | |||
func (protoConn *TTYProtocolConn) InitSender(senderInfo SenderSessionInfo) (serverInfo ServerSessionInfo, err error) { | |||
var replyMsg MsgTTYSenderInitReply | |||
|
|||
msgInitReq := MsgTTYSenderInitRequest{ | |||
Salt: senderInfo.Salt, | |||
PasswordVerifierA: senderInfo.PasswordVerifierA, | |||
} | |||
|
|||
// Send the InitRequest message | |||
if err = MarshalAndWriteMsg(protoConn.netConnection, msgInitReq); err != nil { | |||
return | |||
} | |||
|
|||
// Wait here for the InitReply message | |||
if err = ReadAndUnmarshalMsg(protoConn.netConnection, &replyMsg); err != nil { | |||
return | |||
} | |||
|
|||
serverInfo = ServerSessionInfo{ | |||
URLWebReadWrite: replyMsg.ReceiverURLWebReadWrite, | |||
} | |||
return | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) InitServer(serverInfo ServerSessionInfo) (senderInfo SenderSessionInfo, err error) { | |||
var requestMsg MsgTTYSenderInitRequest | |||
|
|||
// Wait here and expect a InitRequest message | |||
if err = ReadAndUnmarshalMsg(protoConn.netConnection, &requestMsg); err != nil { | |||
return | |||
} | |||
|
|||
// Send back a InitReply message | |||
if err = MarshalAndWriteMsg(protoConn.netConnection, MsgTTYSenderInitReply{ | |||
ReceiverURLWebReadWrite: serverInfo.URLWebReadWrite}); err != nil { | |||
return | |||
} | |||
|
|||
senderInfo = SenderSessionInfo{ | |||
Salt: requestMsg.Salt, | |||
PasswordVerifierA: requestMsg.PasswordVerifierA, | |||
} | |||
return | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) InitServerReceiverConn(serverInfo ServerSessionInfo) (receiverInfo ReceiverSessionInfo, err error) { | |||
return | |||
} | |||
|
|||
func (protoConn *TTYProtocolConn) InitReceiverServerConn(receiverInfo ReceiverSessionInfo) (serverInfo ServerSessionInfo, err error) { | |||
return | |||
} |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,58 @@ | |||
## Overview of the architecture | |||
|
|||
``` | |||
B | |||
A +-------------+ | |||
+-----------------+ | | C | |||
| TTYSender(cmd) | <+-> | TTYProxy | +-------------------+ | |||
+-----------------+ | Server | <-+->| TTYReceiver(web) | | |||
| | +-------------------+ | |||
| | | |||
| | D | |||
| | +-------------------+ | |||
| | <-+->| TTYReceiver(ssh) | | |||
| | +-------------------+ | |||
| | | |||
M | | N | |||
+-----------------+ | | +-------------------+ | |||
| TTYSender(cmd) | <+-> | | <-+->| TTYREceiver(web) | | |||
+-----------------+ +-------------+ +-------------------+ | |||
``` | |||
## | |||
``` | |||
A <-> C, D | |||
M <-> N | |||
``` | |||
|
|||
### A | |||
Where A is the TTYSender, which will be used by the user Alice to share her terminal session. She will start it in the command line, with something like: | |||
``` | |||
tty-share bash | |||
``` | |||
If everything is successful, A will output to stdout 3 URLs, which, something like: | |||
``` | |||
1. read-only: https://tty-share.io/s/0ENHQGjqaB | |||
2. write: https://tty-share.io/s/4HGFN8jahg | |||
3. terminal: ssh://0ENHQGjqaB@tty-share.io.com -p1234 | |||
4. admin: http://localhost:5456/admin | |||
``` | |||
Url number 1. will provide read-only access to the command shared. Which means the user will not be able to interact with the terminal. | |||
Url number 2. will allow the user to interact with the terminale. | |||
Url number 3. ssh access, to follow the remote command from a remote terminal. | |||
Url number 4. provides an interface to control various options related to sharing. | |||
### B | |||
B is the TTYProxyServer, which will be publicly accessible and to which the TTYSender will connect to. On the TTYProxyServer will be created te sessions (read-only and write), and URLs will be returned back to A. Whent the command that A started exits, the session will end, so C should know. | |||
### C | |||
C is the browser via which user Chris will receive the terminal which Alice has shared. | |||
|
|||
### Corner cases | |||
Corner cases to test for: | |||
* AB connection cannot be done | |||
* AB is established, but CB can't be done | |||
* AB connection can go down | |||
* CB connection can go down: | |||
- The websocket connection can go down | |||
- The browser refreshed. Command is still running, so the session is still valid | |||
* All users from the C side close their connection | |||
* The commmand finishes |
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 | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,2 @@ | |||
# Sat Oct 28 16:42:04 CEST 2017 | |||
Got the first end-to-end communication between the tty-share command and the |
Oops, something went wrong.