mirror of
https://github.com/amitbet/vncproxy.git
synced 2025-10-26 14:42:16 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1596f0daee | ||
|
|
7cecffecf2 | ||
|
|
f57b698430 | ||
|
|
e03c85f7ab | ||
|
|
931b08376b | ||
|
|
2ccc4009f4 | ||
|
|
6e1a343b5f | ||
|
|
446bc0e537 | ||
|
|
a870f3cbb9 | ||
|
|
f19a75749b | ||
|
|
02dde77daa | ||
|
|
b46f708726 | ||
|
|
f298567976 |
@@ -11,18 +11,22 @@ An RFB proxy, written in go that can save and replay FBS files
|
||||
- Tested on tight encoding with:
|
||||
- Tightvnc (client + java client + server)
|
||||
- FBS player (tightVnc Java player)
|
||||
- NoVnc(web client)
|
||||
- NoVnc(web client) => use -wsPort to open a websocket
|
||||
- ChickenOfTheVnc(client)
|
||||
- VineVnc(server)
|
||||
- TigerVnc(client)
|
||||
|
||||
## Usage:
|
||||
|
||||
### Executables (see releases)
|
||||
* proxy - the actual recording proxy, supports listening to tcp & ws ports and recording traffic to fbs files
|
||||
* 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 -recDir=./recording.rbs -targHost=192.168.0.100 -targPort=5903 -targPass=@@@@@
|
||||
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)
|
||||
* Connects as client, records to FBS file
|
||||
|
||||
2
build.sh
2
build.sh
@@ -2,7 +2,7 @@
|
||||
sum="sha1sum"
|
||||
|
||||
# VERSION=`date -u +%Y%m%d`
|
||||
VERSION="v1.01"
|
||||
VERSION="v1.02"
|
||||
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
||||
GCFLAGS=""
|
||||
|
||||
|
||||
@@ -506,7 +506,7 @@ func (c *ClientConn) mainLoop() {
|
||||
// Unsupported message type! Bad!
|
||||
break
|
||||
}
|
||||
logger.Infof("ClientConn.MainLoop: got ServerMessage:%s", common.ServerMessageType(messageType))
|
||||
logger.Debugf("ClientConn.MainLoop: got ServerMessage:%s", common.ServerMessageType(messageType))
|
||||
reader.SendMessageStart(common.ServerMessageType(messageType))
|
||||
reader.PublishBytes([]byte{byte(messageType)})
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ func (fbm *MsgFramebufferUpdate) Read(c common.IClientConn, r *common.RfbReadHel
|
||||
// We must always support the raw encoding
|
||||
rawEnc := new(encodings.RawEncoding)
|
||||
encMap[rawEnc.Type()] = rawEnc
|
||||
logger.Infof("MsgFramebufferUpdate.Read: numrects= %d", numRects)
|
||||
logger.Debugf("MsgFramebufferUpdate.Read: numrects= %d", numRects)
|
||||
|
||||
rects := make([]common.Rectangle, numRects)
|
||||
for i := uint16(0); i < numRects; i++ {
|
||||
@@ -90,7 +90,7 @@ func (fbm *MsgFramebufferUpdate) Read(c common.IClientConn, r *common.RfbReadHel
|
||||
|
||||
encType := common.EncodingType(encodingTypeInt)
|
||||
|
||||
logger.Infof("MsgFramebufferUpdate.Read: rect# %d, rect hdr data: enctype=%s, data: %s", i, encType, string(jBytes))
|
||||
logger.Debugf("MsgFramebufferUpdate.Read: rect# %d, rect hdr data: enctype=%s, data: %s", i, encType, string(jBytes))
|
||||
enc, supported := encMap[encodingTypeInt]
|
||||
if supported {
|
||||
var err error
|
||||
|
||||
@@ -73,6 +73,8 @@ func (enct EncodingType) String() string {
|
||||
return "EncJPEGQualityLevelPseudo1"
|
||||
case EncCursorPseudo:
|
||||
return "EncCursorPseudo"
|
||||
case EncLedStatePseudo:
|
||||
return "EncLedStatePseudo"
|
||||
case EncDesktopSizePseudo:
|
||||
return "EncDesktopSizePseudo"
|
||||
case EncLastRectPseudo:
|
||||
@@ -183,6 +185,7 @@ const (
|
||||
EncQEMUPointerMotionChangePseudo EncodingType = -257
|
||||
EncQEMUExtendedKeyEventPseudo EncodingType = -258
|
||||
EncTightPng EncodingType = -260
|
||||
EncLedStatePseudo EncodingType = -261
|
||||
EncExtendedDesktopSizePseudo EncodingType = -308
|
||||
EncXvpPseudo EncodingType = -309
|
||||
EncFencePseudo EncodingType = -312
|
||||
|
||||
31
encodings/enc-led-state.go
Normal file
31
encodings/enc-led-state.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package encodings
|
||||
|
||||
import (
|
||||
"io"
|
||||
"vncproxy/common"
|
||||
"vncproxy/logger"
|
||||
)
|
||||
|
||||
type EncLedStatePseudo struct {
|
||||
LedState uint8
|
||||
}
|
||||
|
||||
func (pe *EncLedStatePseudo) Type() int32 {
|
||||
return int32(common.EncLedStatePseudo)
|
||||
}
|
||||
func (pe *EncLedStatePseudo) WriteTo(w io.Writer) (n int, err error) {
|
||||
w.Write([]byte{pe.LedState})
|
||||
return 1, nil
|
||||
}
|
||||
func (pe *EncLedStatePseudo) Read(pf *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.IEncoding, error) {
|
||||
if rect.Width*rect.Height == 0 {
|
||||
return pe, nil
|
||||
}
|
||||
u8, err := r.ReadUint8()
|
||||
pe.LedState = u8
|
||||
if err != nil {
|
||||
logger.Error("error while reading led state: ", err)
|
||||
return pe, err
|
||||
}
|
||||
return pe, nil
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func handleTightFilters(subencoding uint8, pixelFmt *common.PixelFormat, rect *c
|
||||
return
|
||||
}
|
||||
|
||||
paletteSize := colorCount + 1 // add one more
|
||||
paletteSize := int(colorCount) + 1 // add one more
|
||||
logger.Debugf("handleTightFilters: ----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel)
|
||||
//complete palette
|
||||
_, err = r.ReadBytes(int(paletteSize) * bytesPixel)
|
||||
|
||||
@@ -2,7 +2,31 @@ package logger
|
||||
|
||||
import "fmt"
|
||||
|
||||
var simpleLogger = SimpleLogger{LogLevelDebug}
|
||||
var simpleLogger = SimpleLogger{LogLevelInfo}
|
||||
|
||||
func SetLogLevel(logLevel string) {
|
||||
level := GetLogLevel(logLevel)
|
||||
fmt.Println("Log level set to: ", logLevel)
|
||||
simpleLogger = SimpleLogger{level}
|
||||
}
|
||||
|
||||
func GetLogLevel(logLevel string) LogLevel {
|
||||
switch logLevel {
|
||||
case "trace":
|
||||
return LogLevelTrace
|
||||
case "debug":
|
||||
return LogLevelDebug
|
||||
case "info":
|
||||
return LogLevelInfo
|
||||
case "warn":
|
||||
return LogLevelWarn
|
||||
case "error":
|
||||
return LogLevelError
|
||||
case "fatal":
|
||||
return LogLevelFatal
|
||||
}
|
||||
return LogLevelInfo
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Debug(v ...interface{})
|
||||
@@ -127,6 +151,13 @@ func Debugf(format string, v ...interface{}) {
|
||||
simpleLogger.Debugf(format, v...)
|
||||
}
|
||||
|
||||
func Trace(v ...interface{}) {
|
||||
simpleLogger.Trace(v...)
|
||||
}
|
||||
func Tracef(format string, v ...interface{}) {
|
||||
simpleLogger.Tracef(format, v...)
|
||||
}
|
||||
|
||||
func Info(v ...interface{}) {
|
||||
simpleLogger.Info(v...)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ func main() {
|
||||
wsPort := flag.String("wsPort", "", "websocket port for player to listen to client connections")
|
||||
tcpPort := flag.String("tcpPort", "", "tcp port for player to listen to client connections")
|
||||
fbsFile := flag.String("fbsFile", "", "fbs file to serve to all connecting clients")
|
||||
logLevel := flag.String("logLevel", "info", "change logging level")
|
||||
|
||||
flag.Parse()
|
||||
logger.SetLogLevel(*logLevel)
|
||||
|
||||
fmt.Println("**************************************************************************")
|
||||
fmt.Println("*** This is a toy server that replays a single FBS file to all clients ***")
|
||||
@@ -80,7 +82,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
url := "http://localhost:" + *wsPort + "/"
|
||||
url := "http://0.0.0.0:" + *wsPort + "/"
|
||||
|
||||
if *tcpPort != "" && *wsPort != "" {
|
||||
logger.Infof("running two listeners: tcp port: %s, ws url: %s", *tcpPort, url)
|
||||
|
||||
@@ -58,6 +58,7 @@ func NewFbsReader(fbsFile string) (*FbsReader, error) {
|
||||
&encodings.TightEncoding{},
|
||||
&encodings.TightPngEncoding{},
|
||||
&encodings.EncCursorPseudo{},
|
||||
&encodings.EncLedStatePseudo{},
|
||||
&encodings.RawEncoding{},
|
||||
&encodings.RREEncoding{},
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestServer(t *testing.T) {
|
||||
&encodings.RawEncoding{},
|
||||
&encodings.TightEncoding{},
|
||||
&encodings.EncCursorPseudo{},
|
||||
&encodings.EncLedStatePseudo{},
|
||||
//encodings.TightPngEncoding{},
|
||||
&encodings.RREEncoding{},
|
||||
&encodings.ZLibEncoding{},
|
||||
|
||||
@@ -14,8 +14,10 @@ func main() {
|
||||
var targetVncPort = flag.String("targPort", "", "target vnc server port")
|
||||
var targetVncHost = flag.String("targHost", "", "target vnc server host")
|
||||
var targetVncPass = flag.String("targPass", "", "target vnc password")
|
||||
var logLevel = flag.String("logLevel", "info", "change logging level")
|
||||
|
||||
flag.Parse()
|
||||
logger.SetLogLevel(*logLevel)
|
||||
|
||||
if *tcpPort == "" && *wsPort == "" {
|
||||
logger.Error("no listening port defined")
|
||||
@@ -42,8 +44,8 @@ func main() {
|
||||
}
|
||||
|
||||
proxy := &proxy.VncProxy{
|
||||
WsListeningUrl: "http://localhost:" + string(*wsPort) + "/", // empty = not listening on ws
|
||||
RecordingDir: *recordDir, //"/Users/amitbet/vncRec", // empty = no recording
|
||||
WsListeningUrl: "http://0.0.0.0:" + string(*wsPort) + "/", // empty = not listening on ws
|
||||
RecordingDir: *recordDir, //"/Users/amitbet/vncRec", // empty = no recording
|
||||
TcpListeningUrl: tcpUrl,
|
||||
ProxyVncPassword: *vncPass, //empty = no auth
|
||||
SingleSession: &proxy.VncSession{
|
||||
|
||||
@@ -13,7 +13,7 @@ type ClientUpdater struct {
|
||||
|
||||
// Consume recieves vnc-server-bound messages (Client messages) and updates the server part of the proxy
|
||||
func (cc *ClientUpdater) Consume(seg *common.RfbSegment) error {
|
||||
//logger.Debugf("ClientUpdater.Consume (vnc-server-bound): got segment type=%s bytes: %v", seg.SegmentType, seg.Bytes)
|
||||
logger.Tracef("ClientUpdater.Consume (vnc-server-bound): got segment type=%s bytes: %v", seg.SegmentType, seg.Bytes)
|
||||
switch seg.SegmentType {
|
||||
|
||||
case common.SegmentFullyParsedClientMessage:
|
||||
|
||||
@@ -118,6 +118,7 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server
|
||||
&encodings.RawEncoding{},
|
||||
&encodings.TightEncoding{},
|
||||
&encodings.EncCursorPseudo{},
|
||||
&encodings.EncLedStatePseudo{},
|
||||
&encodings.TightPngEncoding{},
|
||||
&encodings.RREEncoding{},
|
||||
&encodings.ZLibEncoding{},
|
||||
|
||||
@@ -6,15 +6,15 @@ func TestProxy(t *testing.T) {
|
||||
//create default session if required
|
||||
|
||||
proxy := &VncProxy{
|
||||
WsListeningUrl: "http://localhost:7777/", // empty = not listening on ws
|
||||
RecordingDir: "/Users/amitbet/vncRec", // empty = no recording
|
||||
WsListeningUrl: "http://0.0.0.0:7778/", // empty = not listening on ws
|
||||
RecordingDir: "d:\\", // empty = no recording
|
||||
TcpListeningUrl: ":5904",
|
||||
//recordingDir: "C:\\vncRec", // empty = no recording
|
||||
//RecordingDir: "C:\\vncRec", // empty = no recording
|
||||
ProxyVncPassword: "1234", //empty = no auth
|
||||
SingleSession: &VncSession{
|
||||
TargetHostname: "localhost",
|
||||
TargetPort: "5903",
|
||||
TargetPassword: "Ch_#!T@8",
|
||||
TargetHostname: "192.168.1.101",
|
||||
TargetPort: "5901",
|
||||
TargetPassword: "123456",
|
||||
ID: "dummySession",
|
||||
Status: SessionStatusInit,
|
||||
Type: SessionTypeRecordingProxy,
|
||||
|
||||
@@ -16,12 +16,14 @@ func main() {
|
||||
// var tcpPort = flag.String("tcpPort", "", "tcp port")
|
||||
// var wsPort = flag.String("wsPort", "", "websocket port")
|
||||
// var vncPass = flag.String("vncPass", "", "password on incoming vnc connections to the proxy, defaults to no password")
|
||||
var recordDir = flag.String("recDir", "", "path to save FBS recordings WILL NOT RECORD IF EMPTY.")
|
||||
var recordDir = flag.String("recFile", "", "FBS file to create, recordings WILL NOT RECORD IF EMPTY.")
|
||||
var targetVncPort = flag.String("targPort", "", "target vnc server port")
|
||||
var targetVncPass = flag.String("targPass", "", "target vnc password")
|
||||
var targetVncHost = flag.String("targHost", "localhost", "target vnc hostname")
|
||||
var logLevel = flag.String("logLevel", "info", "change logging level")
|
||||
|
||||
flag.Parse()
|
||||
logger.SetLogLevel(*logLevel)
|
||||
|
||||
if *targetVncHost == "" {
|
||||
logger.Error("no target vnc server host defined")
|
||||
@@ -40,6 +42,8 @@ func main() {
|
||||
}
|
||||
if *recordDir == "" {
|
||||
logger.Warn("FBS recording is turned off")
|
||||
} else {
|
||||
logger.Infof("Recording rfb stream into file: '%s'", *recordDir)
|
||||
}
|
||||
|
||||
//nc, err := net.Dial("tcp", "192.168.1.101:5903")
|
||||
|
||||
@@ -310,3 +310,34 @@ func (msg *MsgClientCutText) Write(c io.Writer) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgClientQemuExtendedKey holds the wire format message, for qemu keys
|
||||
type MsgClientQemuExtendedKey struct {
|
||||
SubType uint8 // sub type
|
||||
IsDown uint16 // button down indicator
|
||||
KeySym uint32 // key symbol
|
||||
KeyCode uint32 // key code
|
||||
}
|
||||
|
||||
func (*MsgClientQemuExtendedKey) Type() common.ClientMessageType {
|
||||
return common.QEMUExtendedKeyEventMsgType
|
||||
}
|
||||
|
||||
func (*MsgClientQemuExtendedKey) Read(c io.Reader) (common.ClientMessage, error) {
|
||||
msg := MsgClientQemuExtendedKey{}
|
||||
|
||||
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
func (msg *MsgClientQemuExtendedKey) Write(c io.Writer) error {
|
||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func ServerSecurityHandler(cfg *ServerConfig, c *ServerConn) error {
|
||||
|
||||
sType, ok := secTypes[secType]
|
||||
if !ok {
|
||||
return fmt.Errorf("server type %d not implemented")
|
||||
return fmt.Errorf("server type %d not implemented", secType)
|
||||
}
|
||||
|
||||
var authCode uint32
|
||||
@@ -135,7 +135,7 @@ func ServerServerInitHandler(cfg *ServerConfig, c *ServerConn) error {
|
||||
NameLength: uint32(len(cfg.DesktopName)),
|
||||
NameText: []byte(cfg.DesktopName),
|
||||
}
|
||||
logger.Infof("Server.ServerServerInitHandler initMessage: %v", srvInit)
|
||||
logger.Debugf("Server.ServerServerInitHandler initMessage: %v", srvInit)
|
||||
if err := binary.Write(c, binary.BigEndian, srvInit.FBWidth); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -168,16 +168,17 @@ func (c *ServerConn) handle() error {
|
||||
default:
|
||||
var messageType common.ClientMessageType
|
||||
if err := binary.Read(c, binary.BigEndian, &messageType); err != nil {
|
||||
logger.Errorf("IServerConn.handle error: %v", err)
|
||||
logger.Errorf("ServerConn.handle error: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debugf("ServerConn.handle: got messagetype, %d", messageType)
|
||||
msg, ok := clientMessages[messageType]
|
||||
logger.Debugf("ServerConn.handle: found message type, %v", ok)
|
||||
if !ok {
|
||||
return fmt.Errorf("IServerConn.Handle: unsupported message-type: %v", messageType)
|
||||
logger.Errorf("ServerConn.handle: unsupported message-type: %v", messageType)
|
||||
}
|
||||
|
||||
parsedMsg, err := msg.Read(c)
|
||||
|
||||
logger.Debugf("ServerConn.handle: got parsed messagetype, %v", parsedMsg)
|
||||
//update connection for pixel format / color map changes
|
||||
switch parsedMsg.Type() {
|
||||
case common.SetPixelFormatMsgType:
|
||||
@@ -196,7 +197,7 @@ func (c *ServerConn) handle() error {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("IServerConn.Handle got ClientMessage: %s, %v", parsedMsg.Type(), parsedMsg)
|
||||
logger.Debugf("IServerConn.Handle got ClientMessage: %s, %v", parsedMsg.Type(), parsedMsg)
|
||||
//TODO: treat set encodings by allowing only supported encoding in proxy configurations
|
||||
//// if parsedMsg.Type() == common.SetEncodingsMsgType{
|
||||
//// c.cfg.Encodings
|
||||
|
||||
@@ -15,6 +15,7 @@ var DefaultClientMessages = []common.ClientMessage{
|
||||
&MsgKeyEvent{},
|
||||
&MsgPointerEvent{},
|
||||
&MsgClientCutText{},
|
||||
&MsgClientQemuExtendedKey{},
|
||||
}
|
||||
|
||||
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
||||
|
||||
Reference in New Issue
Block a user