mirror of
https://github.com/amitbet/vncproxy.git
synced 2025-11-02 02:06:38 +00:00
Compare commits
5 Commits
1.01
...
keyframes_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bce301125 | ||
|
|
f19a75749b | ||
|
|
02dde77daa | ||
|
|
b46f708726 | ||
|
|
f298567976 |
@@ -16,13 +16,17 @@ An RFB proxy, written in go that can save and replay FBS files
|
|||||||
- VineVnc(server)
|
- VineVnc(server)
|
||||||
- TigerVnc(client)
|
- TigerVnc(client)
|
||||||
|
|
||||||
## Usage:
|
|
||||||
|
|
||||||
### Executables (see releases)
|
### Executables (see releases)
|
||||||
* proxy - the actual recording proxy, supports listening to tcp & ws ports and recording traffic to fbs files
|
* 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
|
* 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
|
* 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 -vncPass=@!@!@!
|
||||||
|
|
||||||
### Code usage examples
|
### Code usage examples
|
||||||
* player/main.go (fbs recording vnc client)
|
* player/main.go (fbs recording vnc client)
|
||||||
* Connects as client, records to FBS file
|
* Connects as client, records to FBS file
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -2,7 +2,7 @@
|
|||||||
sum="sha1sum"
|
sum="sha1sum"
|
||||||
|
|
||||||
# VERSION=`date -u +%Y%m%d`
|
# VERSION=`date -u +%Y%m%d`
|
||||||
VERSION="v1.01"
|
VERSION="v1.02"
|
||||||
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
||||||
GCFLAGS=""
|
GCFLAGS=""
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package logger
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var simpleLogger = SimpleLogger{LogLevelDebug}
|
var simpleLogger = SimpleLogger{LogLevelInfo}
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Debug(v ...interface{})
|
Debug(v ...interface{})
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
url := "http://localhost:" + *wsPort + "/"
|
url := "http://0.0.0.0:" + *wsPort + "/"
|
||||||
|
|
||||||
if *tcpPort != "" && *wsPort != "" {
|
if *tcpPort != "" && *wsPort != "" {
|
||||||
logger.Infof("running two listeners: tcp port: %s, ws url: %s", *tcpPort, url)
|
logger.Infof("running two listeners: tcp port: %s, ws url: %s", *tcpPort, url)
|
||||||
|
|||||||
199
player/rfb-reader.go
Normal file
199
player/rfb-reader.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"vncproxy/common"
|
||||||
|
"vncproxy/encodings"
|
||||||
|
"vncproxy/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RfbReader struct {
|
||||||
|
reader io.Reader
|
||||||
|
buffer bytes.Buffer
|
||||||
|
currentTimestamp int
|
||||||
|
pixelFormat *common.PixelFormat
|
||||||
|
encodings []common.IEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
** RFB File documentation:
|
||||||
|
** Sections:
|
||||||
|
** 0. header:
|
||||||
|
* index seek position
|
||||||
|
*
|
||||||
|
** 1. init message
|
||||||
|
** 2. content
|
||||||
|
* frame message:
|
||||||
|
* size, timestamp, type, content
|
||||||
|
** 3. index:
|
||||||
|
* each frame message start position, full/incremental, timestamp
|
||||||
|
*
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
|
func (rfb *RfbReader) CurrentTimestamp() int {
|
||||||
|
return rfb.currentTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfb *RfbReader) Read(p []byte) (n int, err error) {
|
||||||
|
if rfb.buffer.Len() < len(p) {
|
||||||
|
seg, err := rfb.ReadSegment()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.Read: error reading rfbsegment: ", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rfb.buffer.Write(seg.bytes)
|
||||||
|
rfb.currentTimestamp = int(seg.timestamp)
|
||||||
|
}
|
||||||
|
return rfb.buffer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfb *RfbReader) CurrentPixelFormat() *common.PixelFormat { return rfb.pixelFormat }
|
||||||
|
|
||||||
|
//func (rfb *rfbReader) CurrentColorMap() *common.ColorMap { return &common.ColorMap{} }
|
||||||
|
func (rfb *RfbReader) Encodings() []common.IEncoding { return rfb.encodings }
|
||||||
|
|
||||||
|
func NewRfbReader(rfbFile string) (*RfbReader, error) {
|
||||||
|
|
||||||
|
reader, err := os.OpenFile(rfbFile, os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("NewrfbReader: can't open rfb file: ", rfbFile)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &RfbReader{reader: reader,
|
||||||
|
encodings: []common.IEncoding{
|
||||||
|
&encodings.CopyRectEncoding{},
|
||||||
|
&encodings.ZLibEncoding{},
|
||||||
|
&encodings.ZRLEEncoding{},
|
||||||
|
&encodings.CoRREEncoding{},
|
||||||
|
&encodings.HextileEncoding{},
|
||||||
|
&encodings.TightEncoding{},
|
||||||
|
&encodings.TightPngEncoding{},
|
||||||
|
&encodings.EncCursorPseudo{},
|
||||||
|
&encodings.RawEncoding{},
|
||||||
|
&encodings.RREEncoding{},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfb *RfbReader) ReadStartSession() (*common.ServerInit, error) {
|
||||||
|
|
||||||
|
initMsg := common.ServerInit{}
|
||||||
|
reader := rfb.reader
|
||||||
|
|
||||||
|
var framebufferWidth uint16
|
||||||
|
var framebufferHeight uint16
|
||||||
|
var SecTypeNone uint32
|
||||||
|
//read rfb header information (the only part done without the [size|data|timestamp] block wrapper)
|
||||||
|
//.("rfb 001.000\n")
|
||||||
|
bytes := make([]byte, 12)
|
||||||
|
_, err := reader.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init message - rfb file Version:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//read the version message into the buffer so it will be written in the first rbs block
|
||||||
|
//RFB 003.008\n
|
||||||
|
bytes = make([]byte, 12)
|
||||||
|
_, err = rfb.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - RFB Version: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//push sec type and fb dimensions
|
||||||
|
binary.Read(rfb, binary.BigEndian, &SecTypeNone)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - SecType: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//read frame buffer width, height
|
||||||
|
binary.Read(rfb, binary.BigEndian, &framebufferWidth)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - FBWidth: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
initMsg.FBWidth = framebufferWidth
|
||||||
|
|
||||||
|
binary.Read(rfb, binary.BigEndian, &framebufferHeight)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - FBHeight: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
initMsg.FBHeight = framebufferHeight
|
||||||
|
|
||||||
|
//read pixel format
|
||||||
|
pixelFormat := &common.PixelFormat{}
|
||||||
|
binary.Read(rfb, binary.BigEndian, pixelFormat)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - Pixelformat: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
initMsg.PixelFormat = *pixelFormat
|
||||||
|
//read padding
|
||||||
|
bytes = make([]byte, 3)
|
||||||
|
rfb.Read(bytes)
|
||||||
|
rfb.pixelFormat = pixelFormat
|
||||||
|
|
||||||
|
//read desktop name
|
||||||
|
var desknameLen uint32
|
||||||
|
binary.Read(rfb, binary.BigEndian, &desknameLen)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - deskname Len: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
initMsg.NameLength = desknameLen
|
||||||
|
|
||||||
|
bytes = make([]byte, desknameLen)
|
||||||
|
rfb.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: error reading rbs init - desktopName: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
initMsg.NameText = bytes
|
||||||
|
|
||||||
|
return &initMsg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfb *RfbReader) ReadSegment() (*FbsSegment, error) {
|
||||||
|
reader := rfb.reader
|
||||||
|
var bytesLen uint32
|
||||||
|
|
||||||
|
//read length
|
||||||
|
err := binary.Read(reader, binary.BigEndian, &bytesLen)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadStartSession: read len, error reading rbs file: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
paddedSize := (bytesLen + 3) & 0x7FFFFFFC
|
||||||
|
|
||||||
|
//read bytes
|
||||||
|
bytes := make([]byte, paddedSize)
|
||||||
|
_, err = reader.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadSegment: read bytes, error reading rbs file: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove padding
|
||||||
|
actualBytes := bytes[:bytesLen]
|
||||||
|
|
||||||
|
//read timestamp
|
||||||
|
var timeSinceStart uint32
|
||||||
|
binary.Read(reader, binary.BigEndian, &timeSinceStart)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("rfbReader.ReadSegment: read timestamp, error reading rbs file: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//timeStamp := time.Unix(timeSinceStart, 0)
|
||||||
|
seg := &FbsSegment{bytes: actualBytes, timestamp: timeSinceStart}
|
||||||
|
return seg, nil
|
||||||
|
}
|
||||||
@@ -35,15 +35,15 @@ func main() {
|
|||||||
if *recordDir == "" {
|
if *recordDir == "" {
|
||||||
logger.Warn("FBS recording is turned off")
|
logger.Warn("FBS recording is turned off")
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpUrl := ""
|
tcpUrl := ""
|
||||||
if *tcpPort != "" {
|
if *tcpPort != "" {
|
||||||
tcpUrl = ":" + string(*tcpPort)
|
tcpUrl = ":" + string(*tcpPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy := &proxy.VncProxy{
|
proxy := &proxy.VncProxy{
|
||||||
WsListeningUrl: "http://localhost:" + string(*wsPort) + "/", // empty = not listening on ws
|
WsListeningUrl: "http://0.0.0.0:" + string(*wsPort) + "/", // empty = not listening on ws
|
||||||
RecordingDir: *recordDir, //"/Users/amitbet/vncRec", // empty = no recording
|
RecordingDir: *recordDir, //"/Users/amitbet/vncRec", // empty = no recording
|
||||||
TcpListeningUrl: tcpUrl,
|
TcpListeningUrl: tcpUrl,
|
||||||
ProxyVncPassword: *vncPass, //empty = no auth
|
ProxyVncPassword: *vncPass, //empty = no auth
|
||||||
SingleSession: &proxy.VncSession{
|
SingleSession: &proxy.VncSession{
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ func TestProxy(t *testing.T) {
|
|||||||
//create default session if required
|
//create default session if required
|
||||||
|
|
||||||
proxy := &VncProxy{
|
proxy := &VncProxy{
|
||||||
WsListeningUrl: "http://localhost:7777/", // empty = not listening on ws
|
WsListeningUrl: "http://0.0.0.0:7778/", // empty = not listening on ws
|
||||||
RecordingDir: "/Users/amitbet/vncRec", // empty = no recording
|
RecordingDir: "d:\\", // empty = no recording
|
||||||
TcpListeningUrl: ":5904",
|
TcpListeningUrl: ":5904",
|
||||||
//recordingDir: "C:\\vncRec", // empty = no recording
|
//RecordingDir: "C:\\vncRec", // empty = no recording
|
||||||
ProxyVncPassword: "1234", //empty = no auth
|
ProxyVncPassword: "1234", //empty = no auth
|
||||||
SingleSession: &VncSession{
|
SingleSession: &VncSession{
|
||||||
TargetHostname: "localhost",
|
TargetHostname: "192.168.1.101",
|
||||||
TargetPort: "5903",
|
TargetPort: "5901",
|
||||||
TargetPassword: "Ch_#!T@8",
|
TargetPassword: "123456",
|
||||||
ID: "dummySession",
|
ID: "dummySession",
|
||||||
Status: SessionStatusInit,
|
Status: SessionStatusInit,
|
||||||
Type: SessionTypeRecordingProxy,
|
Type: SessionTypeRecordingProxy,
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
clientConn.Listeners.AddListener(rec)
|
clientConn.Listeners.AddListener(rec)
|
||||||
clientConn.Listeners.AddListener(&recorder.RfbRequester{Conn: clientConn, Name: "Rfb Requester"})
|
clientConn.Listeners.AddListener(&recorder.RfbRequester{
|
||||||
|
Conn: clientConn,
|
||||||
|
Name: "Rfb Requester",
|
||||||
|
FullScreenRefreshInSec: 30, //create a full refresh key frame every 30sec for seeking
|
||||||
|
})
|
||||||
clientConn.Connect()
|
clientConn.Connect()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -189,6 +189,11 @@ func (r *Recorder) writeToDisk() error {
|
|||||||
paddedSize := (bytesLen + 3) & 0x7FFFFFFC
|
paddedSize := (bytesLen + 3) & 0x7FFFFFFC
|
||||||
paddingSize := paddedSize - bytesLen
|
paddingSize := paddedSize - bytesLen
|
||||||
|
|
||||||
|
/// KeyFramePos, _ := r.writer.Seek(0, os.SEEK_CUR)
|
||||||
|
/// fi, err := r.writer.Stat()
|
||||||
|
/// KeyFramePos := fi.Size() + KeyFramePosInBuffer
|
||||||
|
// now save the KF pos in some file
|
||||||
|
|
||||||
//logger.Debugf("paddedSize=%d paddingSize=%d bytesLen=%d", paddedSize, paddingSize, bytesLen)
|
//logger.Debugf("paddedSize=%d paddingSize=%d bytesLen=%d", paddedSize, paddingSize, bytesLen)
|
||||||
//write buffer padded to 32bit
|
//write buffer padded to 32bit
|
||||||
_, err := r.buffer.WriteTo(r.writer)
|
_, err := r.buffer.WriteTo(r.writer)
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type RfbRequester struct {
|
type RfbRequester struct {
|
||||||
Conn *client.ClientConn
|
Conn *client.ClientConn
|
||||||
Name string
|
Name string
|
||||||
Width uint16
|
Width uint16
|
||||||
Height uint16
|
Height uint16
|
||||||
lastRequestTime time.Time
|
lastRequestTime time.Time
|
||||||
|
nextFullScreenRefresh time.Time
|
||||||
|
FullScreenRefreshInSec int // refresh interval (creates keyframes) if 0, disables keyframe creation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RfbRequester) Consume(seg *common.RfbSegment) error {
|
func (p *RfbRequester) Consume(seg *common.RfbSegment) error {
|
||||||
@@ -29,6 +31,7 @@ func (p *RfbRequester) Consume(seg *common.RfbSegment) error {
|
|||||||
p.Height = serverInitMessage.FBHeight
|
p.Height = serverInitMessage.FBHeight
|
||||||
p.lastRequestTime = time.Now()
|
p.lastRequestTime = time.Now()
|
||||||
p.Conn.FramebufferUpdateRequest(false, 0, 0, p.Width, p.Height)
|
p.Conn.FramebufferUpdateRequest(false, 0, 0, p.Width, p.Height)
|
||||||
|
p.nextFullScreenRefresh = time.Now().Add(time.Duration(p.FullScreenRefreshInSec) * time.Second)
|
||||||
|
|
||||||
case common.SegmentMessageStart:
|
case common.SegmentMessageStart:
|
||||||
case common.SegmentRectSeparator:
|
case common.SegmentRectSeparator:
|
||||||
@@ -39,7 +42,20 @@ func (p *RfbRequester) Consume(seg *common.RfbSegment) error {
|
|||||||
// timeForNextReq := p.lastRequestTime.Unix() + minTimeBetweenReq.Nanoseconds()/1000
|
// timeForNextReq := p.lastRequestTime.Unix() + minTimeBetweenReq.Nanoseconds()/1000
|
||||||
// if seg.UpcomingObjectType == int(common.FramebufferUpdate) && time.Now().Unix() > timeForNextReq {
|
// if seg.UpcomingObjectType == int(common.FramebufferUpdate) && time.Now().Unix() > timeForNextReq {
|
||||||
//time.Sleep(300 * time.Millisecond)
|
//time.Sleep(300 * time.Millisecond)
|
||||||
p.Conn.FramebufferUpdateRequest(true, 0, 0, p.Width, p.Height)
|
p.lastRequestTime = time.Now()
|
||||||
|
incremental := true
|
||||||
|
|
||||||
|
if p.FullScreenRefreshInSec > 0 {
|
||||||
|
// if p.nextFullScreenRefresh.IsZero() {
|
||||||
|
// p.nextFullScreenRefresh = time.Now().Add(time.Duration(p.FullScreenRefreshInSec) * time.Second)
|
||||||
|
// }
|
||||||
|
if time.Now().Sub(p.nextFullScreenRefresh) <= 0 {
|
||||||
|
logger.Warn(">>Creating keyframe")
|
||||||
|
p.nextFullScreenRefresh = time.Now().Add(time.Duration(p.FullScreenRefreshInSec) * time.Second)
|
||||||
|
incremental = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Conn.FramebufferUpdateRequest(incremental, 0, 0, p.Width, p.Height)
|
||||||
//}
|
//}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user