mirror of
https://github.com/amitbet/vncproxy.git
synced 2025-08-30 20:06:51 +00:00
Merge 2293f40fbf
into ea8f9b5109
This commit is contained in:
commit
24641a1b98
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.17
|
||||||
|
|
||||||
|
WORKDIR $GOPATH/src/github.com/amitbet/vncproxy
|
||||||
|
|
||||||
|
RUN mkdir -p $GOPATH/src/github.com/amitbet/vncproxy
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cd $GOPATH/src/github.com/amitbet/vncproxy/recorder/cmd && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /recorder .
|
||||||
|
RUN cd $GOPATH/src/github.com/amitbet/vncproxy/proxy/cmd && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /proxy .
|
||||||
|
RUN cd $GOPATH/src/github.com/amitbet/vncproxy/player/cmd && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /player .
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=0 /recorder /recorder
|
||||||
|
COPY --from=0 /proxy /proxy
|
||||||
|
COPY --from=0 /player /player
|
||||||
|
|
||||||
|
EXPOSE 5900
|
||||||
|
|
||||||
|
ENTRYPOINT ["/proxy"]
|
148
README.md
148
README.md
@ -1,66 +1,82 @@
|
|||||||
# VncProxy [](https://circleci.com/gh/amitbet/vncproxy/tree/master) [](https://raw.githubusercontent.com/CircleCI-Public/circleci-demo-go/master/LICENSE.md)
|
# VncProxy [](https://circleci.com/gh/amitbet/vncproxy/tree/master) [](https://raw.githubusercontent.com/CircleCI-Public/circleci-demo-go/master/LICENSE.md)
|
||||||
|
|
||||||
An RFB proxy, written in go that can save and replay FBS files
|
An RFB proxy, written in go that can save and replay FBS files
|
||||||
* Supports all modern encodings & most useful pseudo-encodings
|
* Supports all modern encodings & most useful pseudo-encodings
|
||||||
* Supports multiple VNC client connections & multi servers (chosen by sessionId)
|
* Supports multiple VNC client connections & multi servers (chosen by sessionId)
|
||||||
* Supports being a "websockify" proxy (for web clients like NoVnc)
|
* Supports being a "websockify" proxy (for web clients like NoVnc)
|
||||||
* Produces FBS files compatible with [tightvnc's rfb player](https://www.tightvnc.com/rfbplayer.php) (while using tight's default 3Byte color format)
|
* Produces FBS files compatible with [tightvnc's rfb player](https://www.tightvnc.com/rfbplayer.php) (while using tight's default 3Byte color format)
|
||||||
* Can also be used as:
|
* Can also be used as:
|
||||||
* A screen recorder vnc-client
|
* A screen recorder vnc-client
|
||||||
* A replay server to show fbs recordings to connecting clients
|
* A replay server to show fbs recordings to connecting clients
|
||||||
|
* Authentication proxy for reMarkable tablet (2.10+)
|
||||||
- Tested on tight encoding with:
|
|
||||||
- Tightvnc (client + java client + server)
|
- Tested on tight encoding with:
|
||||||
- FBS player (tightVnc Java player)
|
- Tightvnc (client + java client + server)
|
||||||
- NoVnc(web client) => use -wsPort to open a websocket
|
- FBS player (tightVnc Java player)
|
||||||
- ChickenOfTheVnc(client)
|
- NoVnc(web client) => use -wsPort to open a websocket
|
||||||
- VineVnc(server)
|
- ChickenOfTheVnc(client)
|
||||||
- TigerVnc(client)
|
- VineVnc(server)
|
||||||
- Qemu vnc(server)
|
- TigerVnc(client)
|
||||||
|
- Qemu vnc(server)
|
||||||
|
|
||||||
### Executables (see releases)
|
|
||||||
* proxy - the actual recording proxy, supports listening to tcp & ws ports and recording traffic to fbs files
|
### Executables (see releases)
|
||||||
* recorder - connects to a vnc server as a client and records the screen
|
* proxy - the actual recording proxy, supports listening to tcp & ws ports and recording traffic to fbs files
|
||||||
* player - a toy player that will replay a given fbs file to all incoming connections
|
* recorder - connects to a vnc server as a client and records the screen
|
||||||
|
* player - a toy player that will replay a given fbs file to all incoming connections
|
||||||
## Usage:
|
|
||||||
recorder -recFile=./recording.rbs -targHost=192.168.0.100 -targPort=5903 -targPass=@@@@@
|
## Usage:
|
||||||
player -fbsFile=./myrec.fbs -tcpPort=5905
|
recorder -recFile=./recording.rbs -targHost=192.168.0.100 -targPort=5903 -targPass=@@@@@
|
||||||
proxy -recDir=./recordings/ -targHost=192.168.0.100 -targPort=5903 -targPass=@@@@@ -tcpPort=5903 -wsPort=5905 -vncPass=@!@!@!
|
player -fbsFile=./myrec.fbs -tcpPort=5905
|
||||||
|
proxy -recDir=./recordings/ -targHost=192.168.0.100 -targPort=5903 -targPass=@@@@@ -tcpPort=5903 -wsPort=5905 -vncPass=@!@!@!
|
||||||
### Code usage examples
|
|
||||||
* player/main.go (fbs recording vnc client)
|
### Code usage examples
|
||||||
* Connects as client, records to FBS file
|
* player/main.go (fbs recording vnc client)
|
||||||
* proxy/proxy_test.go (vnc proxy with recording)
|
* Connects as client, records to FBS file
|
||||||
* Listens to both Tcp and WS ports
|
* proxy/proxy_test.go (vnc proxy with recording)
|
||||||
* Proxies connections to a hard-coded localhost vnc server
|
* Listens to both Tcp and WS ports
|
||||||
* Records session to an FBS file
|
* Proxies connections to a hard-coded localhost vnc server
|
||||||
* player/player_test.go (vnc replay server)
|
* Records session to an FBS file
|
||||||
* Listens to Tcp & WS ports
|
* player/player_test.go (vnc replay server)
|
||||||
* Replays a hard-coded FBS file in normal speed to all connecting vnc clients
|
* Listens to Tcp & WS ports
|
||||||
|
* Replays a hard-coded FBS file in normal speed to all connecting vnc clients
|
||||||
## **Architecture**
|
|
||||||
|
### Examples of using with reMarkable
|
||||||

|
* Simply run the proxy with the `-reMarkable DEVICE_ID` flag
|
||||||
|
* To get the `DEVICE_ID`:
|
||||||
Communication to vnc-server & vnc-client are done in the RFB binary protocol in the standard ways.
|
* Log into reMarkable via SSH
|
||||||
Internal communication inside the proxy is done by listeners (a pub-sub system) that provide a stream of bytes, parsed by delimiters which provide information about RFB message start & type / rectangle start / communication closed, etc.
|
* Extract the `devicetoken` string (exclude the `@ByteArray` wrapper) the string from `/etc/remarkable.conf`
|
||||||
This method allows for minimal delays in transfer, while retaining the ability to buffer and manipulate any part of the protocol.
|
* Run the following Python snippet to decrypt the `devicetoken`:
|
||||||
|
```
|
||||||
For the client messages which are smaller, we send fully parsed messages going trough the same listener system.
|
pip3 install --user PyJWT
|
||||||
Currently client messages are used to determine the correct pixel format, since the client can change it by sending a SetPixelFormatMessage.
|
python3 -c 'import sys,jwt;t=jwt.decode(sys.argv[1],options={"verify_signature":False});print(t)' '(DEVICE TOKEN HERE)'
|
||||||
|
```
|
||||||
Tracking the bytes that are read from the actual vnc-server is made simple by using the RfbReadHelper (implements io.Reader) which sends the bytes to the listeners, this negates the need for manually keeping track of each byte read in order to write it into the recorder.
|
* In output, you should get a string starting with `auth0|`. The whole string is your device ID which should
|
||||||
|
be passed to be `-reMarkable` flag.
|
||||||
RFB Encoding-reader implementations do not decode pixel information, since this is not required for the proxy implementation.
|
* After you should be able to connect to the reMarkable via the proxy with a normal
|
||||||
|
VNC client (tested with TightVNC)
|
||||||
|
|
||||||
This listener system was chosen over direct use of channels, since it allows the listening side to decide whether or not it wants to run in parallel, in contrast having channels inside the server/client objects which require you to create go routines (this creates problems when using go's native websocket implementation)
|
## **Architecture**
|
||||||
|
|
||||||
The Recorder uses channels and runs in parallel to avoid hampering the communication through the proxy.
|

|
||||||
|
|
||||||
|
Communication to vnc-server & vnc-client are done in the RFB binary protocol in the standard ways.
|
||||||

|
Internal communication inside the proxy is done by listeners (a pub-sub system) that provide a stream of bytes, parsed by delimiters which provide information about RFB message start & type / rectangle start / communication closed, etc.
|
||||||
|
This method allows for minimal delays in transfer, while retaining the ability to buffer and manipulate any part of the protocol.
|
||||||
The code is based on several implementations of go-vnc including the original one by *Mitchell Hashimoto*, and the recentely active fork by *Vasiliy Tolstov*.
|
|
||||||
|
For the client messages which are smaller, we send fully parsed messages going trough the same listener system.
|
||||||
|
Currently client messages are used to determine the correct pixel format, since the client can change it by sending a SetPixelFormatMessage.
|
||||||
|
|
||||||
|
Tracking the bytes that are read from the actual vnc-server is made simple by using the RfbReadHelper (implements io.Reader) which sends the bytes to the listeners, this negates the need for manually keeping track of each byte read in order to write it into the recorder.
|
||||||
|
|
||||||
|
RFB Encoding-reader implementations do not decode pixel information, since this is not required for the proxy implementation.
|
||||||
|
|
||||||
|
|
||||||
|
This listener system was chosen over direct use of channels, since it allows the listening side to decide whether or not it wants to run in parallel, in contrast having channels inside the server/client objects which require you to create go routines (this creates problems when using go's native websocket implementation)
|
||||||
|
|
||||||
|
The Recorder uses channels and runs in parallel to avoid hampering the communication through the proxy.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The code is based on several implementations of go-vnc including the original one by *Mitchell Hashimoto*, and the recentely active fork by *Vasiliy Tolstov*.
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/amitbet/vncproxy/common"
|
|
||||||
"github.com/amitbet/vncproxy/logger"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/amitbet/vncproxy/common"
|
||||||
|
"github.com/amitbet/vncproxy/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A ServerMessage implements a message sent from the server to the client.
|
// A ServerMessage implements a message sent from the server to the client.
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/des"
|
"crypto/des"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/amitbet/vncproxy/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientAuthNone is the "none" authentication. See 7.2.1
|
// ClientAuthNone is the "none" authentication. See 7.2.1
|
||||||
@ -110,3 +114,46 @@ func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) {
|
|||||||
|
|
||||||
return crypted, nil
|
return crypted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemarkableAuth is the authentication method used by reMarkable tablet's
|
||||||
|
// Screen Sharing feature
|
||||||
|
type RemarkableAuth struct {
|
||||||
|
RemarkableTimestamp uint64
|
||||||
|
RemarkableDeviceId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RemarkableAuth) SecurityType() uint8 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RemarkableAuth) Handshake(c io.ReadWriteCloser) error {
|
||||||
|
userIdHash := sha256.Sum256([]byte(p.RemarkableDeviceId))
|
||||||
|
|
||||||
|
var tsBuf []byte
|
||||||
|
pb := bytes.NewBuffer(tsBuf)
|
||||||
|
err := binary.Write(pb, binary.BigEndian, p.RemarkableTimestamp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Debugf("Hash with timestamp: %x", pb.Bytes())
|
||||||
|
userIdAndTs := append(pb.Bytes(), userIdHash[:]...)
|
||||||
|
logger.Debugf("Hash with timestamp and user ID hash: %x", userIdAndTs)
|
||||||
|
|
||||||
|
challenge := sha256.Sum256(userIdAndTs)
|
||||||
|
var challengeLength uint32 = uint32(len(challenge))
|
||||||
|
logger.Debugf("Writing challenge length: %v", challengeLength)
|
||||||
|
if err := binary.Write(c, binary.BigEndian, challengeLength); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Debugf("Writing challenge: %x", challenge)
|
||||||
|
if err := binary.Write(c, binary.BigEndian, challenge); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some reason there is another security result... we're just gonna ignore this.
|
||||||
|
var securityResult uint8
|
||||||
|
if err = binary.Read(c, binary.BigEndian, &securityResult); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module github.com/amitbet/vncproxy
|
module github.com/amitbet/vncproxy
|
||||||
|
|
||||||
require golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76
|
require golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/amitbet/vncproxy/logger"
|
"github.com/amitbet/vncproxy/logger"
|
||||||
vncproxy "github.com/amitbet/vncproxy/proxy"
|
vncproxy "github.com/amitbet/vncproxy/proxy"
|
||||||
@ -20,7 +22,9 @@ func main() {
|
|||||||
var targetVncHost = flag.String("targHost", "", "target vnc server host (deprecated, use -target)")
|
var targetVncHost = flag.String("targHost", "", "target vnc server host (deprecated, use -target)")
|
||||||
var targetVncPass = flag.String("targPass", "", "target vnc password")
|
var targetVncPass = flag.String("targPass", "", "target vnc password")
|
||||||
var logLevel = flag.String("logLevel", "info", "change logging level")
|
var logLevel = flag.String("logLevel", "info", "change logging level")
|
||||||
|
var reMarkable = flag.String("reMarkable", "", "reMarkable device ID (enable reMarkable 2.10+ support)")
|
||||||
|
var overrideEncodings = flag.String("overrideEncodings", "", "force a specific set of encodings to be sent to the target vnc server (encoding types separated by comma)")
|
||||||
|
var tls = flag.Bool("tls", false, "use TLS connection (turned on automatically if reMarkable)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
logger.SetLogLevel(*logLevel)
|
logger.SetLogLevel(*logLevel)
|
||||||
|
|
||||||
@ -48,18 +52,31 @@ func main() {
|
|||||||
if *wsPort != "" {
|
if *wsPort != "" {
|
||||||
wsURL = "http://0.0.0.0:" + string(*wsPort) + "/"
|
wsURL = "http://0.0.0.0:" + string(*wsPort) + "/"
|
||||||
}
|
}
|
||||||
|
var overrideEncodingsList []uint32
|
||||||
|
if *overrideEncodings != "" {
|
||||||
|
for _, enc := range strings.Split(*overrideEncodings, ",") {
|
||||||
|
encI, err := strconv.Atoi(enc)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
overrideEncodingsList = append(overrideEncodingsList, uint32(encI))
|
||||||
|
}
|
||||||
|
}
|
||||||
proxy := &vncproxy.VncProxy{
|
proxy := &vncproxy.VncProxy{
|
||||||
WsListeningURL: wsURL, // empty = not listening on ws
|
WsListeningURL: wsURL, // empty = not listening on ws
|
||||||
TCPListeningURL: tcpURL,
|
TCPListeningURL: tcpURL,
|
||||||
ProxyVncPassword: *vncPass, //empty = no auth
|
ProxyVncPassword: *vncPass, //empty = no auth
|
||||||
SingleSession: &vncproxy.VncSession{
|
SingleSession: &vncproxy.VncSession{
|
||||||
Target: *targetVnc,
|
Target: *targetVnc,
|
||||||
TargetHostname: *targetVncHost,
|
TargetHostname: *targetVncHost,
|
||||||
TargetPort: *targetVncPort,
|
TargetPort: *targetVncPort,
|
||||||
TargetPassword: *targetVncPass, //"vncPass",
|
TargetPassword: *targetVncPass, //"vncPass",
|
||||||
ID: "dummySession",
|
ID: "dummySession",
|
||||||
Status: vncproxy.SessionStatusInit,
|
Status: vncproxy.SessionStatusInit,
|
||||||
Type: vncproxy.SessionTypeProxyPass,
|
Type: vncproxy.SessionTypeProxyPass,
|
||||||
|
RemarkableDeviceId: *reMarkable,
|
||||||
|
TLS: *reMarkable != "" || *tls,
|
||||||
|
OverrideEncodings: overrideEncodingsList,
|
||||||
}, // to be used when not using sessions
|
}, // to be used when not using sessions
|
||||||
UsingSessions: false, //false = single session - defined in the var above
|
UsingSessions: false, //false = single session - defined in the var above
|
||||||
}
|
}
|
||||||
@ -75,6 +92,10 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
logger.Info("FBS recording is turned off")
|
logger.Info("FBS recording is turned off")
|
||||||
}
|
}
|
||||||
|
if *reMarkable != "" {
|
||||||
|
logger.Info("reMarkable 2.10+ support turned on")
|
||||||
|
proxy.Remarkable = true
|
||||||
|
}
|
||||||
|
|
||||||
proxy.StartListening()
|
proxy.StartListening()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ClientUpdater struct {
|
type ClientUpdater struct {
|
||||||
conn *client.ClientConn
|
conn *client.ClientConn
|
||||||
|
suppressedMessageTypes []common.ClientMessageType
|
||||||
|
overrideEncodings []common.EncodingType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume recieves vnc-server-bound messages (Client messages) and updates the server part of the proxy
|
// Consume recieves vnc-server-bound messages (Client messages) and updates the server part of the proxy
|
||||||
@ -19,8 +21,15 @@ func (cc *ClientUpdater) Consume(seg *common.RfbSegment) error {
|
|||||||
case common.SegmentFullyParsedClientMessage:
|
case common.SegmentFullyParsedClientMessage:
|
||||||
clientMsg := seg.Message.(common.ClientMessage)
|
clientMsg := seg.Message.(common.ClientMessage)
|
||||||
logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) got ClientMessage type=%s", clientMsg.Type())
|
logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) got ClientMessage type=%s", clientMsg.Type())
|
||||||
switch clientMsg.Type() {
|
|
||||||
|
|
||||||
|
switch clientMsg.Type() {
|
||||||
|
case common.SetEncodingsMsgType:
|
||||||
|
if len(cc.overrideEncodings) > 0 {
|
||||||
|
logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) overriding supported encodings with %v", cc.overrideEncodings)
|
||||||
|
encodingsMsg := clientMsg.(*server.MsgSetEncodings)
|
||||||
|
encodingsMsg.EncNum = uint16(len(cc.overrideEncodings))
|
||||||
|
encodingsMsg.Encodings = cc.overrideEncodings
|
||||||
|
}
|
||||||
case common.SetPixelFormatMsgType:
|
case common.SetPixelFormatMsgType:
|
||||||
// update pixel format
|
// update pixel format
|
||||||
logger.Debugf("ClientUpdater.Consume: updating pixel format")
|
logger.Debugf("ClientUpdater.Consume: updating pixel format")
|
||||||
@ -28,6 +37,18 @@ func (cc *ClientUpdater) Consume(seg *common.RfbSegment) error {
|
|||||||
cc.conn.PixelFormat = pixFmtMsg.PF
|
cc.conn.PixelFormat = pixFmtMsg.PF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suppressMessage := false
|
||||||
|
for _, suppressed := range cc.suppressedMessageTypes {
|
||||||
|
if suppressed == clientMsg.Type() {
|
||||||
|
suppressMessage = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if suppressMessage {
|
||||||
|
logger.Infof("ClientUpdater.Consume:(vnc-server-bound) Suppressing client message type=%s", clientMsg.Type())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err := clientMsg.Write(cc.conn)
|
err := clientMsg.Write(cc.conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("ClientUpdater.Consume (vnc-server-bound, SegmentFullyParsedClientMessage): problem writing to port: %s", err)
|
logger.Errorf("ClientUpdater.Consume (vnc-server-bound, SegmentFullyParsedClientMessage): problem writing to port: %s", err)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -22,10 +25,11 @@ type VncProxy struct {
|
|||||||
ProxyVncPassword string //empty = no auth
|
ProxyVncPassword string //empty = no auth
|
||||||
SingleSession *VncSession // to be used when not using sessions
|
SingleSession *VncSession // to be used when not using sessions
|
||||||
UsingSessions bool //false = single session - defined in the var above
|
UsingSessions bool //false = single session - defined in the var above
|
||||||
|
Remarkable bool
|
||||||
sessionManager *SessionManager
|
sessionManager *SessionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vp *VncProxy) createClientConnection(target string, vncPass string) (*client.ClientConn, error) {
|
func (vp *VncProxy) createClientConnection(target string, vncPass string, tlsEnabled bool, reMarkableDeviceId string) (*client.ClientConn, error) {
|
||||||
var (
|
var (
|
||||||
nc net.Conn
|
nc net.Conn
|
||||||
err error
|
err error
|
||||||
@ -35,6 +39,19 @@ func (vp *VncProxy) createClientConnection(target string, vncPass string) (*clie
|
|||||||
nc, err = net.Dial("unix", target)
|
nc, err = net.Dial("unix", target)
|
||||||
} else {
|
} else {
|
||||||
nc, err = net.Dial("tcp", target)
|
nc, err = net.Dial("tcp", target)
|
||||||
|
if tlsEnabled {
|
||||||
|
logger.Info("Upgrading to TLS connection...")
|
||||||
|
config := tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
tc := tls.Client(nc, &config)
|
||||||
|
err = tc.Handshake()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc = tc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -44,6 +61,31 @@ func (vp *VncProxy) createClientConnection(target string, vncPass string) (*clie
|
|||||||
|
|
||||||
var noauth client.ClientAuthNone
|
var noauth client.ClientAuthNone
|
||||||
authArr := []client.ClientAuth{&client.PasswordAuth{Password: vncPass}, &noauth}
|
authArr := []client.ClientAuth{&client.PasswordAuth{Password: vncPass}, &noauth}
|
||||||
|
var rmTimestamp uint64
|
||||||
|
if reMarkableDeviceId != "" {
|
||||||
|
logger.Info("Enabling reMarkable 2.10+ support")
|
||||||
|
|
||||||
|
authC, err := net.ListenPacket("udp", ":5901")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer authC.Close()
|
||||||
|
|
||||||
|
buffer := make([]byte, 128) // datagram size 51 bytes
|
||||||
|
n, addr, err := authC.ReadFrom(buffer)
|
||||||
|
logger.Debugf("Received datagram from reMarkable (%v, %v bytes)", addr, n)
|
||||||
|
|
||||||
|
pb := bytes.NewBuffer(buffer)
|
||||||
|
err = binary.Read(pb, binary.BigEndian, &rmTimestamp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Debugf("reMarkable timestamp is %v", rmTimestamp)
|
||||||
|
authArr = append(authArr, &client.RemarkableAuth{
|
||||||
|
RemarkableTimestamp: rmTimestamp,
|
||||||
|
RemarkableDeviceId: reMarkableDeviceId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
clientConn, err := client.NewClientConn(nc,
|
clientConn, err := client.NewClientConn(nc,
|
||||||
&client.ClientConfig{
|
&client.ClientConfig{
|
||||||
@ -100,7 +142,7 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server
|
|||||||
target = session.TargetHostname + ":" + session.TargetPort
|
target = session.TargetHostname + ":" + session.TargetPort
|
||||||
}
|
}
|
||||||
|
|
||||||
cconn, err := vp.createClientConnection(target, session.TargetPassword)
|
cconn, err := vp.createClientConnection(target, session.TargetPassword, session.TLS, session.RemarkableDeviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Status = SessionStatusError
|
session.Status = SessionStatusError
|
||||||
logger.Errorf("Proxy.newServerConnHandler error creating connection: %s", err)
|
logger.Errorf("Proxy.newServerConnHandler error creating connection: %s", err)
|
||||||
@ -119,7 +161,16 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server
|
|||||||
|
|
||||||
// gets the messages from the server part (from vnc-client),
|
// gets the messages from the server part (from vnc-client),
|
||||||
// and write through the client to the actual vnc-server
|
// and write through the client to the actual vnc-server
|
||||||
clientUpdater := &ClientUpdater{cconn}
|
var clientUpdater *ClientUpdater
|
||||||
|
clientUpdater = &ClientUpdater{conn: cconn}
|
||||||
|
if session.RemarkableDeviceId != "" {
|
||||||
|
clientUpdater.suppressedMessageTypes = []common.ClientMessageType{common.SetPixelFormatMsgType}
|
||||||
|
}
|
||||||
|
if len(session.OverrideEncodings) > 0 {
|
||||||
|
for _, enc := range session.OverrideEncodings {
|
||||||
|
clientUpdater.overrideEncodings = append(clientUpdater.overrideEncodings, common.EncodingType(enc))
|
||||||
|
}
|
||||||
|
}
|
||||||
sconn.Listeners.AddListener(clientUpdater)
|
sconn.Listeners.AddListener(clientUpdater)
|
||||||
|
|
||||||
err = cconn.Connect()
|
err = cconn.Connect()
|
||||||
@ -185,6 +236,9 @@ func (vp *VncProxy) StartListening() {
|
|||||||
NewConnHandler: vp.newServerConnHandler,
|
NewConnHandler: vp.newServerConnHandler,
|
||||||
UseDummySession: !vp.UsingSessions,
|
UseDummySession: !vp.UsingSessions,
|
||||||
}
|
}
|
||||||
|
if vp.Remarkable {
|
||||||
|
cfg.DesktopName = []byte("reMarkable rfb")
|
||||||
|
}
|
||||||
|
|
||||||
if vp.TCPListeningURL != "" && vp.WsListeningURL != "" {
|
if vp.TCPListeningURL != "" && vp.WsListeningURL != "" {
|
||||||
logger.Infof("running two listeners: tcp port: %s, ws url: %s", vp.TCPListeningURL, vp.WsListeningURL)
|
logger.Infof("running two listeners: tcp port: %s, ws url: %s", vp.TCPListeningURL, vp.WsListeningURL)
|
||||||
|
@ -16,12 +16,15 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type VncSession struct {
|
type VncSession struct {
|
||||||
Target string
|
Target string
|
||||||
TargetHostname string
|
TargetHostname string
|
||||||
TargetPort string
|
TargetPort string
|
||||||
TargetPassword string
|
TargetPassword string
|
||||||
ID string
|
ID string
|
||||||
Status SessionStatus
|
Status SessionStatus
|
||||||
Type SessionType
|
Type SessionType
|
||||||
ReplayFilePath string
|
ReplayFilePath string
|
||||||
|
RemarkableDeviceId string
|
||||||
|
TLS bool
|
||||||
|
OverrideEncodings []uint32
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/amitbet/vncproxy/common"
|
"github.com/amitbet/vncproxy/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -313,10 +314,10 @@ func (msg *MsgClientCutText) Write(c io.Writer) error {
|
|||||||
|
|
||||||
// MsgClientQemuExtendedKey holds the wire format message, for qemu keys
|
// MsgClientQemuExtendedKey holds the wire format message, for qemu keys
|
||||||
type MsgClientQemuExtendedKey struct {
|
type MsgClientQemuExtendedKey struct {
|
||||||
SubType uint8 // sub type
|
SubType uint8 // sub type
|
||||||
IsDown uint16 // button down indicator
|
IsDown uint16 // button down indicator
|
||||||
KeySym uint32 // key symbol
|
KeySym uint32 // key symbol
|
||||||
KeyCode uint32 // key code
|
KeyCode uint32 // key code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MsgClientQemuExtendedKey) Type() common.ClientMessageType {
|
func (*MsgClientQemuExtendedKey) Type() common.ClientMessageType {
|
||||||
|
Loading…
Reference in New Issue
Block a user