mirror of
				https://github.com/amitbet/vncproxy.git
				synced 2025-10-31 00:58:48 +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) | ||||
|     - 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 -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="" | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package logger | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| var simpleLogger = SimpleLogger{LogLevelDebug} | ||||
| var simpleLogger = SimpleLogger{LogLevelInfo} | ||||
|  | ||||
| type Logger interface { | ||||
| 	Debug(v ...interface{}) | ||||
|   | ||||
| @@ -80,7 +80,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) | ||||
|   | ||||
							
								
								
									
										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 | ||||
| } | ||||
| @@ -42,7 +42,7 @@ func main() { | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 		TcpListeningUrl:  tcpUrl, | ||||
| 		ProxyVncPassword: *vncPass, //empty = no auth | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -67,7 +67,11 @@ func main() { | ||||
| 		}) | ||||
|  | ||||
| 	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() | ||||
|  | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -189,6 +189,11 @@ func (r *Recorder) writeToDisk() error { | ||||
| 	paddedSize := (bytesLen + 3) & 0x7FFFFFFC | ||||
| 	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) | ||||
| 	//write buffer padded to 32bit | ||||
| 	_, err := r.buffer.WriteTo(r.writer) | ||||
|   | ||||
| @@ -13,6 +13,8 @@ type RfbRequester struct { | ||||
| 	Width                  uint16 | ||||
| 	Height                 uint16 | ||||
| 	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 { | ||||
| @@ -29,6 +31,7 @@ func (p *RfbRequester) Consume(seg *common.RfbSegment) error { | ||||
| 		p.Height = serverInitMessage.FBHeight | ||||
| 		p.lastRequestTime = time.Now() | ||||
| 		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.SegmentRectSeparator: | ||||
| @@ -39,7 +42,20 @@ func (p *RfbRequester) Consume(seg *common.RfbSegment) error { | ||||
| 		// timeForNextReq := p.lastRequestTime.Unix() + minTimeBetweenReq.Nanoseconds()/1000 | ||||
| 		// if seg.UpcomingObjectType == int(common.FramebufferUpdate) && time.Now().Unix() > timeForNextReq { | ||||
| 		//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: | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user