diff --git a/.gitignore b/.gitignore index b337459..3125662 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -*.bin -debug +*.bin +debug diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 96cc43e..3b3cb8d 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,22 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index e7bedf3..c7d1c5a 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/GOPATH__vncproxy_.xml b/.idea/libraries/GOPATH__vncproxy_.xml index d56667c..a3abc64 100644 --- a/.idea/libraries/GOPATH__vncproxy_.xml +++ b/.idea/libraries/GOPATH__vncproxy_.xml @@ -1,42 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 009d106..be9be36 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 1fb5146..06e0ada 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -1,8 +1,8 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..9661ac7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/vncproxy.iml b/.idea/vncproxy.iml index 2596329..fccf925 100644 --- a/.idea/vncproxy.iml +++ b/.idea/vncproxy.iml @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 93de0cb..0137706 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,1407 +1,1648 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Read - .Read - .read - server - uint8 - TightMinToCompress - - - - - - - - - - - - - - true - DEFINITION_ORDER - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - project - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1497275544790 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - file://$PROJECT_DIR$/main.go - 11 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 76 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 98 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 103 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 156 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 173 - - - file://$PROJECT_DIR$/vnc/enc-tight.go - 178 - - - file://$PROJECT_DIR$/vnc/server_messages.go - 78 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Go 1.6.2 - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Read + .Read + .read + server + uint8 + TightMinToCompress + mainloop + writeStartSession + padd + paddib + paddi + paddin + paddings + padding + paddingS + paddingSi + paddingSiz + read + paddingSize + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1497275544790 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/vnc/server_messages.go + 32 + + + + file://$PROJECT_DIR$/vnc/client-conn.go + 482 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 115 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 103 + + + + file://$PROJECT_DIR$/vnc/server_messages.go + 115 + + + + file://$PROJECT_DIR$/encodings/enc-tight.go + 113 + + + + file://$PROJECT_DIR$/encodings/enc-tight.go + 160 + + + + file://$PROJECT_DIR$/encodings/enc-tight.go + 168 + + + + file://$PROJECT_DIR$/encodings/enc-tight.go + 177 + + + + file://$PROJECT_DIR$/encodings/enc-tight.go + 180 + + + + file://$PROJECT_DIR$/common/readers.go + 138 + + + + file://$PROJECT_DIR$/common/readers.go + 147 + + + + file://$PROJECT_DIR$/common/readers.go + 137 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 28 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 77 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 108 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 119 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 142 + + + + file://$PROJECT_DIR$/listeners/recorder.go + 138 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Go 1.6.2 + + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 065906f..a0c94e2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,73 +1,73 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Test", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "program": "${workspaceRoot}/reader", - "args": [ - "-test.v" - ], - "osx": { - "env": { - //"GOPATH": "/Users/amitbet/Dropbox/go" - } - }, - "windows": { - "env": { - //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" - } - }, - "showLog": true - }, - { - "name": "Launch Grouper Tests", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "program": "${workspaceRoot}/consumer", - "args": [ - "-test.v" - ], - "osx": { - "env": { - //"GOPATH": "${env.HOME}/Dropbox/go" - } - }, - "windows": { - "env": { - //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" - } - }, - "showLog": true - }, - { - "name": "Launch", - "type": "go", - "request": "launch", - "mode": "debug", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}/", - "osx": { - "env": { - //"GOPATH": "${env.HOME}/Dropbox/go" - } - }, - "windows": { - "env": { - //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" - } - }, - "args": [], - "showLog": true - } - ] +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Test", + "type": "go", + "request": "launch", + "mode": "test", + "remotePath": "", + "port": 2345, + "program": "${workspaceRoot}/reader", + "args": [ + "-test.v" + ], + "osx": { + "env": { + //"GOPATH": "/Users/amitbet/Dropbox/go" + } + }, + "windows": { + "env": { + //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" + } + }, + "showLog": true + }, + { + "name": "Launch Grouper Tests", + "type": "go", + "request": "launch", + "mode": "test", + "remotePath": "", + "port": 2345, + "program": "${workspaceRoot}/consumer", + "args": [ + "-test.v" + ], + "osx": { + "env": { + //"GOPATH": "${env.HOME}/Dropbox/go" + } + }, + "windows": { + "env": { + //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" + } + }, + "showLog": true + }, + { + "name": "Launch", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${workspaceRoot}/", + "osx": { + "env": { + //"GOPATH": "${env.HOME}/Dropbox/go" + } + }, + "windows": { + "env": { + //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" + } + }, + "args": [], + "showLog": true + } + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b725b84..cef0593 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,46 +1,46 @@ -{ - "version": "0.1.0", - "command": "go", - "isShellCommand": true, - "echoCommand": true, - "showOutput": "always", - // "showOutput": "silent", - "options": { - // "env": { - // "GOPATH": "/Users/lukeh/dd/go" - // } - }, - "tasks": [ - { - "taskName": "install", - "args": [ - "-v", - "./..." - ], - "osx": { - "options": { - "env": { - //"GOPATH": "${env.HOME}/Dropbox/go" - } - } - }, - "windows": { - "options": { - "env": { - //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" - } - } - }, - "isBuildCommand": true, - "problemMatcher": "$go" - }, - { - "taskName": "test", - "args": [ - "-v", - "./..." - ], - "isTestCommand": true - } - ] +{ + "version": "0.1.0", + "command": "go", + "isShellCommand": true, + "echoCommand": true, + "showOutput": "always", + // "showOutput": "silent", + "options": { + // "env": { + // "GOPATH": "/Users/lukeh/dd/go" + // } + }, + "tasks": [ + { + "taskName": "install", + "args": [ + "-v", + "./..." + ], + "osx": { + "options": { + "env": { + //"GOPATH": "${env.HOME}/Dropbox/go" + } + } + }, + "windows": { + "options": { + "env": { + //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" + } + } + }, + "isBuildCommand": true, + "problemMatcher": "$go" + }, + { + "taskName": "test", + "args": [ + "-v", + "./..." + ], + "isTestCommand": true + } + ] } \ No newline at end of file diff --git a/LICENSE b/LICENSE index 84762d6..9641ea7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2017 Amit Bezalel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2017 Amit Bezalel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 317e3e0..2aff624 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VncProxy -A RFB proxy, written in go that can save and replay RBS files - -Still a work in progress, but the client side already includes all the required encodings -understanding the RemoteFrameBuffer protocol is important since we need to save the frameResponses with timestamps - -some server code will be added shortly, with the proxying logics. +# VncProxy +A RFB proxy, written in go that can save and replay RBS files + +Still a work in progress, but the client side already includes all the required encodings +understanding the RemoteFrameBuffer protocol is important since we need to save the frameResponses with timestamps + +some server code will be added shortly, with the proxying logics. diff --git a/common/Logger.go b/common/Logger.go index 069f906..fc70af6 100644 --- a/common/Logger.go +++ b/common/Logger.go @@ -1,14 +1,14 @@ -package common - -type Logger interface { - Debug(v ...interface{}) - Debugf(format string, v ...interface{}) - Info(v ...interface{}) - Infof(format string, v ...interface{}) - Warn(v ...interface{}) - Warnf(format string, v ...interface{}) - Error(v ...interface{}) - Errorf(format string, v ...interface{}) - Fatal(v ...interface{}) - Fatalf(format string, v ...interface{}) -} +package common + +type Logger interface { + Debug(v ...interface{}) + Debugf(format string, v ...interface{}) + Info(v ...interface{}) + Infof(format string, v ...interface{}) + Warn(v ...interface{}) + Warnf(format string, v ...interface{}) + Error(v ...interface{}) + Errorf(format string, v ...interface{}) + Fatal(v ...interface{}) + Fatalf(format string, v ...interface{}) +} diff --git a/common/encoding.go b/common/encoding.go index baab23c..a3d9203 100644 --- a/common/encoding.go +++ b/common/encoding.go @@ -1,9 +1,5 @@ package common -import ( - "io" -) - // An Encoding implements a method for encoding pixel data that is // sent by the server to the client. type Encoding interface { @@ -13,7 +9,7 @@ type Encoding interface { // Read reads the contents of the encoded pixel data from the reader. // This should return a new Encoding implementation that contains // the proper data. - Read(*PixelFormat, *Rectangle, io.Reader) (Encoding, error) + Read(*PixelFormat, *Rectangle, *RfbReadHelper) (Encoding, error) } const ( diff --git a/common/readers.go b/common/readers.go index 0f2212d..19397ce 100644 --- a/common/readers.go +++ b/common/readers.go @@ -1,45 +1,85 @@ -// Package vnc implements a VNC client. -// -// References: -// [PROTOCOL]: http://tools.ietf.org/html/rfc6143 package common import ( - "bytes" "encoding/binary" "fmt" "io" ) -// type DataSource struct { -// conn io.Reader -// output io.Writer -// passThrough bool -// PixelFormat PixelFormat -// }Color -type RfbReader struct { - reader io.Reader - saveBytes bool - savedBuff bytes.Buffer -} +var TightMinToCompress = 12 -func (r *RfbReader) Read(p []byte) (n int, err error) { - readLen, err := r.reader.Read(p) - r.savedBuff.Write(p) - return readLen, err +const ( + SegmentBytes SegmentType = iota + SegmentMessageSeparator + SegmentRectSeparator +) + +type SegmentType int + +type RfbSegment struct { + Bytes []byte + SegmentType SegmentType + UpcomingObjectType int } -func (r *RfbReader) SavedBuff() bytes.Buffer { - return r.savedBuff +type SegmentConsumer interface { + Consume(*RfbSegment) error } type RfbReadHelper struct { io.Reader + Listener SegmentConsumer } -func (d *RfbReadHelper) ReadBytes(count int) ([]byte, error) { +func (r *RfbReadHelper) ReadDiscrete(p []byte) (int, error) { + return r.Read(p) +} + +func (r *RfbReadHelper) SendRectSeparator(upcomingRectType int) error { + seg := &RfbSegment{SegmentType: SegmentRectSeparator, UpcomingObjectType: upcomingRectType} + if r.Listener != nil { + return nil + } + return r.Listener.Consume(seg) + +} + +func (r *RfbReadHelper) SendMessageSeparator(upcomingMessageType int) error { + seg := &RfbSegment{SegmentType: SegmentMessageSeparator, UpcomingObjectType: upcomingMessageType} + if r.Listener == nil { + return nil + } + return r.Listener.Consume(seg) +} + +func (r *RfbReadHelper) PublishBytes(p []byte) error { + seg := &RfbSegment{Bytes: p, SegmentType: SegmentBytes} + if r.Listener == nil { + return nil + } + return r.Listener.Consume(seg) +} + +func (r *RfbReadHelper) Read(p []byte) (n int, err error) { + readLen, err := r.Reader.Read(p) + if err != nil { + return 0, err + } + //write the bytes to the Listener for further processing + seg := &RfbSegment{Bytes: p, SegmentType: SegmentBytes} + if r.Listener == nil { + return 0,nil + } + r.Listener.Consume(seg) + if err != nil { + return 0, err + } + return readLen, err +} + +func (r *RfbReadHelper) ReadBytes(count int) ([]byte, error) { buff := make([]byte, count) - _, err := io.ReadFull(d.Reader, buff) + _, err := io.ReadFull(r, buff) if err != nil { //if err := binary.Read(d.conn, binary.BigEndian, &buff); err != nil { return nil, err @@ -47,41 +87,41 @@ func (d *RfbReadHelper) ReadBytes(count int) ([]byte, error) { return buff, nil } -func (d *RfbReadHelper) ReadUint8() (uint8, error) { +func (r *RfbReadHelper) ReadUint8() (uint8, error) { var myUint uint8 - if err := binary.Read(d.Reader, binary.BigEndian, &myUint); err != nil { + if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { return 0, err } //fmt.Printf("myUint=%d", myUint) return myUint, nil } -func (d *RfbReadHelper) ReadUint16() (uint16, error) { +func (r *RfbReadHelper) ReadUint16() (uint16, error) { var myUint uint16 - if err := binary.Read(d.Reader, binary.BigEndian, &myUint); err != nil { + if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { return 0, err } //fmt.Printf("myUint=%d", myUint) return myUint, nil } -func (d *RfbReadHelper) ReadUint32() (uint32, error) { +func (r *RfbReadHelper) ReadUint32() (uint32, error) { var myUint uint32 - if err := binary.Read(d.Reader, binary.BigEndian, &myUint); err != nil { + if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { return 0, err } //fmt.Printf("myUint=%d", myUint) return myUint, nil } -func (d *RfbReadHelper) ReadCompactLen() (int, error) { +func (r *RfbReadHelper) ReadCompactLen() (int, error) { var err error - part, err := d.ReadUint8() + part, err := r.ReadUint8() //byteCount := 1 len := uint32(part & 0x7F) if (part & 0x80) != 0 { - part, err = d.ReadUint8() + part, err = r.ReadUint8() //byteCount++ len |= uint32(part&0x7F) << 7 if (part & 0x80) != 0 { - part, err = d.ReadUint8() + part, err = r.ReadUint8() //byteCount++ len |= uint32(part&0xFF) << 14 } @@ -94,8 +134,6 @@ func (d *RfbReadHelper) ReadCompactLen() (int, error) { return int(len), err } -var TightMinToCompress int = 12 - func (r *RfbReadHelper) ReadTightData(dataSize int) ([]byte, error) { if int(dataSize) < TightMinToCompress { return r.ReadBytes(int(dataSize)) diff --git a/common/rectangle.go b/common/rectangle.go index 8125823..5261903 100644 --- a/common/rectangle.go +++ b/common/rectangle.go @@ -1,34 +1,34 @@ -package common - -import ( - "fmt" -) - -// Rectangle represents a rectangle of pixel data. -type Rectangle struct { - X uint16 - Y uint16 - Width uint16 - Height uint16 - Enc Encoding -} - -func (r *Rectangle) String() string { - return fmt.Sprintf("(%d,%d) (width: %d, height: %d), Enc= %d", r.X, r.Y, r.Width, r.Height, r.Enc.Type()) -} - -// PixelFormat describes the way a pixel is formatted for a VNC connection. -// -// See RFC 6143 Section 7.4 for information on each of the fields. -type PixelFormat struct { - BPP uint8 - Depth uint8 - BigEndian bool - TrueColor bool - RedMax uint16 - GreenMax uint16 - BlueMax uint16 - RedShift uint8 - GreenShift uint8 - BlueShift uint8 -} +package common + +import ( + "fmt" +) + +// Rectangle represents a rectangle of pixel data. +type Rectangle struct { + X uint16 + Y uint16 + Width uint16 + Height uint16 + Enc Encoding +} + +func (r *Rectangle) String() string { + return fmt.Sprintf("(%d,%d) (width: %d, height: %d), Enc= %d", r.X, r.Y, r.Width, r.Height, r.Enc.Type()) +} + +// PixelFormat describes the way a pixel is formatted for a VNC connection. +// +// See RFC 6143 Section 7.4 for information on each of the fields. +type PixelFormat struct { + BPP uint8 + Depth uint8 + BigEndian bool + TrueColor bool + RedMax uint16 + GreenMax uint16 + BlueMax uint16 + RedShift uint8 + GreenShift uint8 + BlueShift uint8 +} diff --git a/encodings/enc-copy-rect.go b/encodings/enc-copy-rect.go index 4cef62f..ae7ec15 100644 --- a/encodings/enc-copy-rect.go +++ b/encodings/enc-copy-rect.go @@ -1,26 +1,20 @@ -package encodings - -import ( - "io" - "vncproxy/common" -) - -type CopyRectEncoding struct { - //Colors []Color - copyRectSrcX uint16 - copyRectSrcY uint16 -} - -func (z *CopyRectEncoding) Type() int32 { - return 1 -} -func (z *CopyRectEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} - //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - //bytesPerPixel := c.PixelFormat.BPP / 8 - z.copyRectSrcX, _ = conn.ReadUint16() - z.copyRectSrcY, _ = conn.ReadUint16() - return z, nil -} - -////////// +package encodings + +import "vncproxy/common" + +type CopyRectEncoding struct { + //Colors []Color + copyRectSrcX uint16 + copyRectSrcY uint16 +} + +func (z *CopyRectEncoding) Type() int32 { + return 1 +} +func (z *CopyRectEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + z.copyRectSrcX, _ = r.ReadUint16() + z.copyRectSrcY, _ = r.ReadUint16() + return z, nil +} + +////////// diff --git a/encodings/enc-corre.go b/encodings/enc-corre.go index 6731691..bcce323 100644 --- a/encodings/enc-corre.go +++ b/encodings/enc-corre.go @@ -1,7 +1,6 @@ package encodings import ( - "io" "vncproxy/common" ) @@ -13,16 +12,15 @@ func (z *CoRREEncoding) Type() int32 { return 4 } -func (z *CoRREEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} +func (z *CoRREEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { bytesPerPixel := int(pixelFmt.BPP / 8) - numOfSubrectangles, _ := conn.ReadUint32() + numOfSubrectangles, _ := r.ReadUint32() //read whole rect background color - conn.ReadBytes(bytesPerPixel) + r.ReadBytes(bytesPerPixel) //read all individual rects (color=BPP + x=16b + y=16b + w=16b + h=16b) - _, err := conn.ReadBytes(int(numOfSubrectangles) * (bytesPerPixel + 4)) + _, err := r.ReadBytes(int(numOfSubrectangles) * (bytesPerPixel + 4)) if err != nil { return nil, err diff --git a/encodings/enc-hextile.go b/encodings/enc-hextile.go index 9bb8d6c..fd2c811 100644 --- a/encodings/enc-hextile.go +++ b/encodings/enc-hextile.go @@ -1,85 +1,80 @@ -package encodings - -import ( - "io" - "vncproxy/common" -) - -const ( - HextileRaw = 1 - HextileBackgroundSpecified = 2 - HextileForegroundSpecified = 4 - HextileAnySubrects = 8 - HextileSubrectsColoured = 16 -) - -type HextileEncoding struct { - //Colors []Color -} - -func (z *HextileEncoding) Type() int32 { - return 5 -} -func (z *HextileEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} - //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - bytesPerPixel := int(pixelFmt.BPP) / 8 - //buf := make([]byte, bytesPerPixel) - for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 { - th := 16 - if rect.Y+rect.Height-ty < 16 { - th = int(rect.Y) + int(rect.Height) - int(ty) - } - - for tx := rect.X; tx < rect.X+rect.Width; tx += 16 { - tw := 16 - if rect.X+rect.Width-tx < 16 { - tw = int(rect.X) + int(rect.Width) - int(tx) - } - - //handle Hextile Subrect(tx, ty, tw, th): - subencoding, err := conn.ReadUint8() - //fmt.Printf("hextile reader tile: (%d,%d) subenc=%d\n", ty, tx, subencoding) - if err != nil { - //fmt.Printf("error in hextile reader: %v\n", err) - return nil, err - } - - if (subencoding & HextileRaw) != 0 { - //ReadRawRect(c, rect, r) - conn.ReadBytes(tw * th * bytesPerPixel) - //fmt.Printf("hextile reader: HextileRaw\n") - continue - } - if (subencoding & HextileBackgroundSpecified) != 0 { - conn.ReadBytes(int(bytesPerPixel)) - } - if (subencoding & HextileForegroundSpecified) != 0 { - conn.ReadBytes(int(bytesPerPixel)) - } - if (subencoding & HextileAnySubrects) == 0 { - //fmt.Printf("hextile reader: no Subrects\n") - continue - } - //fmt.Printf("hextile reader: handling Subrects\n") - nSubrects, err := conn.ReadUint8() - if err != nil { - return nil, err - } - bufsize := int(nSubrects) * 2 - if (subencoding & HextileSubrectsColoured) != 0 { - bufsize += int(nSubrects) * int(bytesPerPixel) - } - //byte[] buf = new byte[bufsize]; - conn.ReadBytes(bufsize) - } - } - - // len, _ := readUint32(c.c) - // _, err := readBytes(c.c, int(len)) - - // if err != nil { - // return nil, err - // } - return z, nil -} +package encodings + +import "vncproxy/common" + +const ( + HextileRaw = 1 + HextileBackgroundSpecified = 2 + HextileForegroundSpecified = 4 + HextileAnySubrects = 8 + HextileSubrectsColoured = 16 +) + +type HextileEncoding struct { + //Colors []Color +} + +func (z *HextileEncoding) Type() int32 { + return 5 +} +func (z *HextileEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + bytesPerPixel := int(pixelFmt.BPP) / 8 + + for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 { + th := 16 + if rect.Y+rect.Height-ty < 16 { + th = int(rect.Y) + int(rect.Height) - int(ty) + } + + for tx := rect.X; tx < rect.X+rect.Width; tx += 16 { + tw := 16 + if rect.X+rect.Width-tx < 16 { + tw = int(rect.X) + int(rect.Width) - int(tx) + } + + //handle Hextile Subrect(tx, ty, tw, th): + subencoding, err := r.ReadUint8() + //fmt.Printf("hextile reader tile: (%d,%d) subenc=%d\n", ty, tx, subencoding) + if err != nil { + //fmt.Printf("error in hextile reader: %v\n", err) + return nil, err + } + + if (subencoding & HextileRaw) != 0 { + //ReadRawRect(c, rect, r) + r.ReadBytes(tw * th * bytesPerPixel) + //fmt.Printf("hextile reader: HextileRaw\n") + continue + } + if (subencoding & HextileBackgroundSpecified) != 0 { + r.ReadBytes(int(bytesPerPixel)) + } + if (subencoding & HextileForegroundSpecified) != 0 { + r.ReadBytes(int(bytesPerPixel)) + } + if (subencoding & HextileAnySubrects) == 0 { + //fmt.Printf("hextile reader: no Subrects\n") + continue + } + //fmt.Printf("hextile reader: handling Subrects\n") + nSubrects, err := r.ReadUint8() + if err != nil { + return nil, err + } + bufsize := int(nSubrects) * 2 + if (subencoding & HextileSubrectsColoured) != 0 { + bufsize += int(nSubrects) * int(bytesPerPixel) + } + //byte[] buf = new byte[bufsize]; + r.ReadBytes(bufsize) + } + } + + // len, _ := readUint32(c.c) + // _, err := readBytes(c.c, int(len)) + + // if err != nil { + // return nil, err + // } + return z, nil +} diff --git a/encodings/enc-raw.go b/encodings/enc-raw.go index e810c28..a462ab0 100644 --- a/encodings/enc-raw.go +++ b/encodings/enc-raw.go @@ -1,7 +1,6 @@ package encodings import ( - "io" "vncproxy/common" ) @@ -16,9 +15,9 @@ func (*RawEncoding) Type() int32 { return 0 } -func (*RawEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { +func (*RawEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - conn := common.RfbReadHelper{r} + //conn := common.RfbReadHelper{Reader:r} bytesPerPixel := int(pixelFmt.BPP / 8) //pixelBytes := make([]uint8, bytesPerPixel) @@ -31,7 +30,7 @@ func (*RawEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r for y := uint16(0); y < rect.Height; y++ { for x := uint16(0); x < rect.Width; x++ { - if _, err := conn.ReadBytes(bytesPerPixel); err != nil { + if _, err := r.ReadBytes(bytesPerPixel); err != nil { return nil, err } diff --git a/encodings/enc-rre.go b/encodings/enc-rre.go index 7c501d5..0f56984 100644 --- a/encodings/enc-rre.go +++ b/encodings/enc-rre.go @@ -1,28 +1,27 @@ -package encodings - -import "io" -import "vncproxy/common" - -type RREEncoding struct { - //Colors []Color -} - -func (z *RREEncoding) Type() int32 { - return 2 -} -func (z *RREEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} - bytesPerPixel := int(pixelFmt.BPP / 8) - numOfSubrectangles, _ := conn.ReadUint32() - - //read whole rect background color - conn.ReadBytes(bytesPerPixel) - - //read all individual rects (color=BPP + x=16b + y=16b + w=16b + h=16b) - _, err := conn.ReadBytes(int(numOfSubrectangles) * (bytesPerPixel + 8)) - - if err != nil { - return nil, err - } - return z, nil -} +package encodings + +import "vncproxy/common" + +type RREEncoding struct { + //Colors []Color +} + +func (z *RREEncoding) Type() int32 { + return 2 +} +func (z *RREEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + //conn := common.RfbReadHelper{Reader:r} + bytesPerPixel := int(pixelFmt.BPP / 8) + numOfSubrectangles, _ := r.ReadUint32() + + //read whole rect background color + r.ReadBytes(bytesPerPixel) + + //read all individual rects (color=BPP + x=16b + y=16b + w=16b + h=16b) + _, err := r.ReadBytes(int(numOfSubrectangles) * (bytesPerPixel + 8)) + + if err != nil { + return nil, err + } + return z, nil +} diff --git a/encodings/enc-tight.go b/encodings/enc-tight.go index 4d43bdf..f0d5e4e 100644 --- a/encodings/enc-tight.go +++ b/encodings/enc-tight.go @@ -1,334 +1,334 @@ -package encodings - -import ( - "errors" - "fmt" - "io" - "vncproxy/common" -) - -var TightMinToCompress int = 12 - -const ( - TightExplicitFilter = 0x04 - TightFill = 0x08 - TightJpeg = 0x09 - TightMaxSubencoding = 0x09 - TightFilterCopy = 0x00 - TightFilterPalette = 0x01 - TightFilterGradient = 0x02 -) - -type TightEncoding struct { - output io.Writer - logger common.Logger -} - -func (t *TightEncoding) SetOutput(output io.Writer) { - t.output = output -} - -func (*TightEncoding) Type() int32 { - return 7 -} - -// func ReadAndRecBytes(conn io.Reader, rec io.Writer, count int) ([]byte, error) { -// buf, err := readBytes(conn, count) -// rec.Write(buf) -// return buf, err -// } -// func ReadAndRecUint8(conn io.Reader, rec io.Writer) (uint8, error) { -// myUint, err := readUint8(conn) -// buf := make([]byte, 1) -// buf[0] = byte(myUint) // cast int8 to byte -// rec.Write(buf) -// return myUint, err -// } - -// func ReadAndRecUint16(conn io.Reader, rec io.Writer) (uint16, error) { -// myUint, err := readUint16(conn) -// buf := make([]byte, 2) -// //buf[0] = byte(myUint) // cast int8 to byte -// //var i int16 = 41 -// //b := make([]byte, 2) -// binary.LittleEndian.PutUint16(buf, uint16(myUint)) - -// rec.Write(buf) -// return myUint, err -// } - -func calcTightBytePerPixel(pf *common.PixelFormat) int { - bytesPerPixel := int(pf.BPP / 8) - - var bytesPerPixelTight int - if 24 == pf.Depth && 32 == pf.BPP { - bytesPerPixelTight = 3 - } else { - bytesPerPixelTight = bytesPerPixel - } - return bytesPerPixelTight -} - -func (t *TightEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, reader io.Reader) (common.Encoding, error) { - bytesPixel := calcTightBytePerPixel(pixelFmt) - conn := common.RfbReadHelper{reader} - //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - - //var subencoding uint8 - subencoding, err := conn.ReadUint8() - if err != nil { - fmt.Printf("error in handling tight encoding: %v\n", err) - return nil, err - } - fmt.Printf("bytesPixel= %d, subencoding= %d\n", bytesPixel, subencoding) - // if err := binary.Read(conn.c, binary.BigEndian, &subencoding); err != nil { - // return t, err - // } - - //move it to position (remove zlib flush commands) - compType := subencoding >> 4 & 0x0F - // for stream_id := 0; stream_id < 4; stream_id++ { - // // if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { - // // tightInflaters[stream_id] = null; - // // } - // subencoding >>= 1 - // } - - fmt.Printf("afterSHL:%d\n", compType) - switch compType { - case TightFill: - fmt.Printf("reading fill size=%d\n", bytesPixel) - //read color - conn.ReadBytes(int(bytesPixel)) - return t, nil - case TightJpeg: - if pixelFmt.BPP == 8 { - return nil, errors.New("Tight encoding: JPEG is not supported in 8 bpp mode") - } - - len, err := conn.ReadCompactLen() - if err != nil { - return nil, err - } - fmt.Printf("reading jpeg size=%d\n", len) - conn.ReadBytes(len) - return t, nil - default: - - if compType > TightJpeg { - fmt.Println("Compression control byte is incorrect!") - } - - handleTightFilters(subencoding, pixelFmt, rect, reader) - return t, nil - } -} - -func handleTightFilters(subencoding uint8, pixelFmt *common.PixelFormat, rect *common.Rectangle, reader io.Reader) { - conn := common.RfbReadHelper{reader} - var FILTER_ID_MASK uint8 = 0x40 - //var STREAM_ID_MASK uint8 = 0x30 - - //decoderId := (subencoding & STREAM_ID_MASK) >> 4 - var filterid uint8 - var err error - - if (subencoding & FILTER_ID_MASK) > 0 { // filter byte presence - filterid, err = conn.ReadUint8() - if err != nil { - fmt.Printf("error in handling tight encoding, reading filterid: %v\n", err) - return - } - fmt.Printf("read filter: %d\n", filterid) - } - - //var numColors uint8 - bytesPixel := calcTightBytePerPixel(pixelFmt) - - fmt.Printf("filter: %d\n", filterid) - // if rfb.rec != null { - // rfb.rec.writeByte(filter_id) - // } - lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height) - - switch filterid { - case TightFilterPalette: //PALETTE_FILTER - - colorCount, err := conn.ReadUint8() - paletteSize := colorCount + 1 // add one more - fmt.Printf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel) - //complete palette - conn.ReadBytes(int(paletteSize) * bytesPixel) - - var dataLength int - if paletteSize == 2 { - dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8) - } else { - dataLength = int(rect.Width * rect.Height) - } - _, err = conn.ReadTightData(dataLength) - if err != nil { - fmt.Printf("error in handling tight encoding, Reading Palette: %v\n", err) - return - } - case TightFilterGradient: //GRADIENT_FILTER - fmt.Printf("----GRADIENT_FILTER: bytesPixel=%d\n", bytesPixel) - //useGradient = true - fmt.Printf("usegrad: %d\n", filterid) - conn.ReadTightData(lengthCurrentbpp) - case TightFilterCopy: //BASIC_FILTER - fmt.Printf("----BASIC_FILTER: bytesPixel=%d\n", bytesPixel) - conn.ReadTightData(lengthCurrentbpp) - default: - fmt.Printf("Bad tight filter id: %d\n", filterid) - return - } - - //////////// - - // if numColors == 0 && bytesPixel == 4 { - // rowSize1 *= 3 - // } - // rowSize := (int(rect.Width)*bitsPixel + 7) / 8 - // dataSize := int(rect.Height) * rowSize - - // dataSize1 := rect.Height * rowSize1 - // fmt.Printf("datasize: %d, origDatasize: %d", dataSize, dataSize1) - // // Read, optionally uncompress and decode data. - // if int(dataSize1) < TightMinToCompress { - // // Data size is small - not compressed with zlib. - // if numColors != 0 { - // // Indexed colors. - // //indexedData := make([]byte, dataSize) - // readBytes(conn.c, int(dataSize1)) - // //readFully(indexedData); - // // if (rfb.rec != null) { - // // rfb.rec.write(indexedData); - // // } - // // if (numColors == 2) { - // // // Two colors. - // // if (bytesPixel == 1) { - // // decodeMonoData(x, y, w, h, indexedData, palette8); - // // } else { - // // decodeMonoData(x, y, w, h, indexedData, palette24); - // // } - // // } else { - // // // 3..255 colors (assuming bytesPixel == 4). - // // int i = 0; - // // for (int dy = y; dy < y + h; dy++) { - // // for (int dx = x; dx < x + w; dx++) { - // // pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF]; - // // } - // // } - // // } - // } else if useGradient { - // // "Gradient"-processed data - // //buf := make ( []byte,w * h * 3); - // dataByteCount := int(3) * int(rect.Width) * int(rect.Height) - // readBytes(conn.c, dataByteCount) - // // rfb.readFully(buf); - // // if (rfb.rec != null) { - // // rfb.rec.write(buf); - // // } - // // decodeGradientData(x, y, w, h, buf); - // } else { - // // Raw truecolor data. - // dataByteCount := int(bytesPixel) * int(rect.Width) * int(rect.Height) - // readBytes(conn.c, dataByteCount) - - // // if (bytesPixel == 1) { - // // for (int dy = y; dy < y + h; dy++) { - - // // rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); - // // if (rfb.rec != null) { - // // rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); - // // } - // // } - // // } else { - // // byte[] buf = new byte[w * 3]; - // // int i, offset; - // // for (int dy = y; dy < y + h; dy++) { - // // rfb.readFully(buf); - // // if (rfb.rec != null) { - // // rfb.rec.write(buf); - // // } - // // offset = dy * rfb.framebufferWidth + x; - // // for (i = 0; i < w; i++) { - // // pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF); - // // } - // // } - // // } - // } - // } else { - // // Data was compressed with zlib. - // zlibDataLen, err := readCompactLen(conn.c) - // fmt.Printf("compactlen=%d\n", zlibDataLen) - // if err != nil { - // return nil, err - // } - // //byte[] zlibData = new byte[zlibDataLen]; - // //rfb.readFully(zlibData); - // readBytes(conn.c, zlibDataLen) - - // // if (rfb.rec != null) { - // // rfb.rec.write(zlibData); - // // } - // // int stream_id = comp_ctl & 0x03; - // // if (tightInflaters[stream_id] == null) { - // // tightInflaters[stream_id] = new Inflater(); - // // } - // // Inflater myInflater = tightInflaters[stream_id]; - // // myInflater.setInput(zlibData); - // // byte[] buf = new byte[dataSize]; - // // myInflater.inflate(buf); - // // if (rfb.rec != null && !rfb.recordFromBeginning) { - // // rfb.recordCompressedData(buf); - // // } - - // // if (numColors != 0) { - // // // Indexed colors. - // // if (numColors == 2) { - // // // Two colors. - // // if (bytesPixel == 1) { - // // decodeMonoData(x, y, w, h, buf, palette8); - // // } else { - // // decodeMonoData(x, y, w, h, buf, palette24); - // // } - // // } else { - // // // More than two colors (assuming bytesPixel == 4). - // // int i = 0; - // // for (int dy = y; dy < y + h; dy++) { - // // for (int dx = x; dx < x + w; dx++) { - // // pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; - // // } - // // } - // // } - // // } else if (useGradient) { - // // // Compressed "Gradient"-filtered data (assuming bytesPixel == 4). - // // decodeGradientData(x, y, w, h, buf); - // // } else { - // // // Compressed truecolor data. - // // if (bytesPixel == 1) { - // // int destOffset = y * rfb.framebufferWidth + x; - // // for (int dy = 0; dy < h; dy++) { - // // System.arraycopy(buf, dy * w, pixels8, destOffset, w); - // // destOffset += rfb.framebufferWidth; - // // } - // // } else { - // // int srcOffset = 0; - // // int destOffset, i; - // // for (int dy = 0; dy < h; dy++) { - // // myInflater.inflate(buf); - // // destOffset = (y + dy) * rfb.framebufferWidth + x; - // // for (i = 0; i < w; i++) { - // // pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 | (buf[srcOffset + 1] & 0xFF) << 8 - // // | (buf[srcOffset + 2] & 0xFF); - // // srcOffset += 3; - // // } - // // } - // // } - // // } - // } - - return -} +package encodings + +import ( + "errors" + "fmt" + "io" + "vncproxy/common" +) + +var TightMinToCompress int = 12 + +const ( + TightExplicitFilter = 0x04 + TightFill = 0x08 + TightJpeg = 0x09 + TightMaxSubencoding = 0x09 + TightFilterCopy = 0x00 + TightFilterPalette = 0x01 + TightFilterGradient = 0x02 +) + +type TightEncoding struct { + output io.Writer + logger common.Logger +} + +func (t *TightEncoding) SetOutput(output io.Writer) { + t.output = output +} + +func (*TightEncoding) Type() int32 { + return 7 +} + +// func ReadAndRecBytes(conn io.Reader, rec io.Writer, count int) ([]byte, error) { +// buf, err := readBytes(conn, count) +// rec.Write(buf) +// return buf, err +// } +// func ReadAndRecUint8(conn io.Reader, rec io.Writer) (uint8, error) { +// myUint, err := readUint8(conn) +// buf := make([]byte, 1) +// buf[0] = byte(myUint) // cast int8 to byte +// rec.Write(buf) +// return myUint, err +// } + +// func ReadAndRecUint16(conn io.Reader, rec io.Writer) (uint16, error) { +// myUint, err := readUint16(conn) +// buf := make([]byte, 2) +// //buf[0] = byte(myUint) // cast int8 to byte +// //var i int16 = 41 +// //b := make([]byte, 2) +// binary.LittleEndian.PutUint16(buf, uint16(myUint)) + +// rec.Write(buf) +// return myUint, err +// } + +func calcTightBytePerPixel(pf *common.PixelFormat) int { + bytesPerPixel := int(pf.BPP / 8) + + var bytesPerPixelTight int + if 24 == pf.Depth && 32 == pf.BPP { + bytesPerPixelTight = 3 + } else { + bytesPerPixelTight = bytesPerPixel + } + return bytesPerPixelTight +} + +func (t *TightEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + bytesPixel := calcTightBytePerPixel(pixelFmt) + //conn := common.RfbReadHelper{Reader:reader} + //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} + + //var subencoding uint8 + subencoding, err := r.ReadUint8() + if err != nil { + fmt.Printf("error in handling tight encoding: %v\n", err) + return nil, err + } + fmt.Printf("bytesPixel= %d, subencoding= %d\n", bytesPixel, subencoding) + // if err := binary.Read(conn.c, binary.BigEndian, &subencoding); err != nil { + // return t, err + // } + + //move it to position (remove zlib flush commands) + compType := subencoding >> 4 & 0x0F + // for stream_id := 0; stream_id < 4; stream_id++ { + // // if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { + // // tightInflaters[stream_id] = null; + // // } + // subencoding >>= 1 + // } + + fmt.Printf("afterSHL:%d\n", compType) + switch compType { + case TightFill: + fmt.Printf("reading fill size=%d\n", bytesPixel) + //read color + r.ReadBytes(int(bytesPixel)) + return t, nil + case TightJpeg: + if pixelFmt.BPP == 8 { + return nil, errors.New("Tight encoding: JPEG is not supported in 8 bpp mode") + } + + len, err := r.ReadCompactLen() + if err != nil { + return nil, err + } + fmt.Printf("reading jpeg size=%d\n", len) + r.ReadBytes(len) + return t, nil + default: + + if compType > TightJpeg { + fmt.Println("Compression control byte is incorrect!") + } + + handleTightFilters(subencoding, pixelFmt, rect, r) + return t, nil + } +} + +func handleTightFilters(subencoding uint8, pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) { + //conn := common.RfbReadHelper{Reader:reader} + var FILTER_ID_MASK uint8 = 0x40 + //var STREAM_ID_MASK uint8 = 0x30 + + //decoderId := (subencoding & STREAM_ID_MASK) >> 4 + var filterid uint8 + var err error + + if (subencoding & FILTER_ID_MASK) > 0 { // filter byte presence + filterid, err = r.ReadUint8() + if err != nil { + fmt.Printf("error in handling tight encoding, reading filterid: %v\n", err) + return + } + fmt.Printf("read filter: %d\n", filterid) + } + + //var numColors uint8 + bytesPixel := calcTightBytePerPixel(pixelFmt) + + fmt.Printf("filter: %d\n", filterid) + // if rfb.rec != null { + // rfb.rec.writeByte(filter_id) + // } + lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height) + + switch filterid { + case TightFilterPalette: //PALETTE_FILTER + + colorCount, err := r.ReadUint8() + paletteSize := colorCount + 1 // add one more + fmt.Printf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel) + //complete palette + r.ReadBytes(int(paletteSize) * bytesPixel) + + var dataLength int + if paletteSize == 2 { + dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8) + } else { + dataLength = int(rect.Width * rect.Height) + } + _, err = r.ReadTightData(dataLength) + if err != nil { + fmt.Printf("error in handling tight encoding, Reading Palette: %v\n", err) + return + } + case TightFilterGradient: //GRADIENT_FILTER + fmt.Printf("----GRADIENT_FILTER: bytesPixel=%d\n", bytesPixel) + //useGradient = true + fmt.Printf("usegrad: %d\n", filterid) + r.ReadTightData(lengthCurrentbpp) + case TightFilterCopy: //BASIC_FILTER + fmt.Printf("----BASIC_FILTER: bytesPixel=%d\n", bytesPixel) + r.ReadTightData(lengthCurrentbpp) + default: + fmt.Printf("Bad tight filter id: %d\n", filterid) + return + } + + //////////// + + // if numColors == 0 && bytesPixel == 4 { + // rowSize1 *= 3 + // } + // rowSize := (int(rect.Width)*bitsPixel + 7) / 8 + // dataSize := int(rect.Height) * rowSize + + // dataSize1 := rect.Height * rowSize1 + // fmt.Printf("datasize: %d, origDatasize: %d", dataSize, dataSize1) + // // Read, optionally uncompress and decode data. + // if int(dataSize1) < TightMinToCompress { + // // Data size is small - not compressed with zlib. + // if numColors != 0 { + // // Indexed colors. + // //indexedData := make([]byte, dataSize) + // readBytes(conn.c, int(dataSize1)) + // //readFully(indexedData); + // // if (rfb.rec != null) { + // // rfb.rec.write(indexedData); + // // } + // // if (numColors == 2) { + // // // Two colors. + // // if (bytesPixel == 1) { + // // decodeMonoData(x, y, w, h, indexedData, palette8); + // // } else { + // // decodeMonoData(x, y, w, h, indexedData, palette24); + // // } + // // } else { + // // // 3..255 colors (assuming bytesPixel == 4). + // // int i = 0; + // // for (int dy = y; dy < y + h; dy++) { + // // for (int dx = x; dx < x + w; dx++) { + // // pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF]; + // // } + // // } + // // } + // } else if useGradient { + // // "Gradient"-processed data + // //buf := make ( []byte,w * h * 3); + // dataByteCount := int(3) * int(rect.Width) * int(rect.Height) + // readBytes(conn.c, dataByteCount) + // // rfb.readFully(buf); + // // if (rfb.rec != null) { + // // rfb.rec.write(buf); + // // } + // // decodeGradientData(x, y, w, h, buf); + // } else { + // // Raw truecolor data. + // dataByteCount := int(bytesPixel) * int(rect.Width) * int(rect.Height) + // readBytes(conn.c, dataByteCount) + + // // if (bytesPixel == 1) { + // // for (int dy = y; dy < y + h; dy++) { + + // // rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + // // if (rfb.rec != null) { + // // rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); + // // } + // // } + // // } else { + // // byte[] buf = new byte[w * 3]; + // // int i, offset; + // // for (int dy = y; dy < y + h; dy++) { + // // rfb.readFully(buf); + // // if (rfb.rec != null) { + // // rfb.rec.write(buf); + // // } + // // offset = dy * rfb.framebufferWidth + x; + // // for (i = 0; i < w; i++) { + // // pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF); + // // } + // // } + // // } + // } + // } else { + // // Data was compressed with zlib. + // zlibDataLen, err := readCompactLen(conn.c) + // fmt.Printf("compactlen=%d\n", zlibDataLen) + // if err != nil { + // return nil, err + // } + // //byte[] zlibData = new byte[zlibDataLen]; + // //rfb.readFully(zlibData); + // readBytes(conn.c, zlibDataLen) + + // // if (rfb.rec != null) { + // // rfb.rec.write(zlibData); + // // } + // // int stream_id = comp_ctl & 0x03; + // // if (tightInflaters[stream_id] == null) { + // // tightInflaters[stream_id] = new Inflater(); + // // } + // // Inflater myInflater = tightInflaters[stream_id]; + // // myInflater.setInput(zlibData); + // // byte[] buf = new byte[dataSize]; + // // myInflater.inflate(buf); + // // if (rfb.rec != null && !rfb.recordFromBeginning) { + // // rfb.recordCompressedData(buf); + // // } + + // // if (numColors != 0) { + // // // Indexed colors. + // // if (numColors == 2) { + // // // Two colors. + // // if (bytesPixel == 1) { + // // decodeMonoData(x, y, w, h, buf, palette8); + // // } else { + // // decodeMonoData(x, y, w, h, buf, palette24); + // // } + // // } else { + // // // More than two colors (assuming bytesPixel == 4). + // // int i = 0; + // // for (int dy = y; dy < y + h; dy++) { + // // for (int dx = x; dx < x + w; dx++) { + // // pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; + // // } + // // } + // // } + // // } else if (useGradient) { + // // // Compressed "Gradient"-filtered data (assuming bytesPixel == 4). + // // decodeGradientData(x, y, w, h, buf); + // // } else { + // // // Compressed truecolor data. + // // if (bytesPixel == 1) { + // // int destOffset = y * rfb.framebufferWidth + x; + // // for (int dy = 0; dy < h; dy++) { + // // System.arraycopy(buf, dy * w, pixels8, destOffset, w); + // // destOffset += rfb.framebufferWidth; + // // } + // // } else { + // // int srcOffset = 0; + // // int destOffset, i; + // // for (int dy = 0; dy < h; dy++) { + // // myInflater.inflate(buf); + // // destOffset = (y + dy) * rfb.framebufferWidth + x; + // // for (i = 0; i < w; i++) { + // // pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 | (buf[srcOffset + 1] & 0xFF) << 8 + // // | (buf[srcOffset + 2] & 0xFF); + // // srcOffset += 3; + // // } + // // } + // // } + // // } + // } + + return +} diff --git a/encodings/enc-zlib.go b/encodings/enc-zlib.go index 3da39d1..a7bfaab 100644 --- a/encodings/enc-zlib.go +++ b/encodings/enc-zlib.go @@ -1,24 +1,23 @@ -package encodings - -import "io" -import "vncproxy/common" - -type ZLibEncoding struct { - //Colors []Color -} - -func (z *ZLibEncoding) Type() int32 { - return 6 -} -func (z *ZLibEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} - //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - //bytesPerPixel := c.PixelFormat.BPP / 8 - len, _ := conn.ReadUint32() - _, err := conn.ReadBytes(int(len)) - - if err != nil { - return nil, err - } - return z, nil -} +package encodings + +import "vncproxy/common" + +type ZLibEncoding struct { + //Colors []Color +} + +func (z *ZLibEncoding) Type() int32 { + return 6 +} +func (z *ZLibEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + //conn := common.RfbReadHelper{Reader:r} + //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} + //bytesPerPixel := c.PixelFormat.BPP / 8 + len, _ := r.ReadUint32() + _, err := r.ReadBytes(int(len)) + + if err != nil { + return nil, err + } + return z, nil +} diff --git a/encodings/enc-zrle.go b/encodings/enc-zrle.go index 5059b7e..58f4f9d 100644 --- a/encodings/enc-zrle.go +++ b/encodings/enc-zrle.go @@ -1,24 +1,23 @@ -package encodings - -import "io" -import "vncproxy/common" - -type ZRLEEncoding struct { - //Colors []Color -} - -func (z *ZRLEEncoding) Type() int32 { - return 16 -} -func (z *ZRLEEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r io.Reader) (common.Encoding, error) { - conn := common.RfbReadHelper{r} - //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} - //bytesPerPixel := c.PixelFormat.BPP / 8 - len, _ := conn.ReadUint32() - _, err := conn.ReadBytes(int(len)) - - if err != nil { - return nil, err - } - return z, nil -} +package encodings + +import "vncproxy/common" + +type ZRLEEncoding struct { + //Colors []Color +} + +func (z *ZRLEEncoding) Type() int32 { + return 16 +} +func (z *ZRLEEncoding) Read(pixelFmt *common.PixelFormat, rect *common.Rectangle, r *common.RfbReadHelper) (common.Encoding, error) { + //conn := common.RfbReadHelper{Reader: r} + //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} + //bytesPerPixel := c.PixelFormat.BPP / 8 + len, _ := r.ReadUint32() + _, err := r.ReadBytes(int(len)) + + if err != nil { + return nil, err + } + return z, nil +} diff --git a/listeners/multiListener.go b/listeners/multiListener.go new file mode 100644 index 0000000..fe5dddf --- /dev/null +++ b/listeners/multiListener.go @@ -0,0 +1,22 @@ +package listeners + +import "vncproxy/common" + +type MultiListener struct { + listeners []common.SegmentConsumer +} + +func (m *MultiListener) AddListener(listener common.SegmentConsumer) { + m.listeners = append(m.listeners, listener) +} + +func (m *MultiListener) Consume(seg *common.RfbSegment) error { + for _, li := range m.listeners { + //fmt.Println(li) + err := li.Consume(seg) + if err != nil { + return err + } + } + return nil +} diff --git a/listeners/pass-to.go b/listeners/pass-to.go new file mode 100644 index 0000000..20b4ef4 --- /dev/null +++ b/listeners/pass-to.go @@ -0,0 +1,12 @@ +package listeners + +import "vncproxy/common" +import "io" + +type PassListener struct { + io.Writer +} + +func (*PassListener) Consume(seg *common.RfbSegment) error { + return nil +} diff --git a/listeners/recorder.go b/listeners/recorder.go new file mode 100644 index 0000000..5c5e4e6 --- /dev/null +++ b/listeners/recorder.go @@ -0,0 +1,155 @@ +package listeners + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "os" + "time" + "vncproxy/common" +) + +type Recorder struct { + //common.BytesListener + RBSFileName string + writer *os.File + logger common.Logger + startTime int + buffer bytes.Buffer +} + +func getNowMillisec() int { + return int(time.Now().UnixNano() / int64(time.Millisecond)) +} + +func NewRecorder(saveFilePath string, desktopName string, fbWidth uint16, fbHeight uint16) *Recorder { + //delete file if it exists + if _, err := os.Stat(saveFilePath); err == nil { + os.Remove(saveFilePath) + } + + rec := Recorder{RBSFileName: saveFilePath, startTime: getNowMillisec()} + var err error + + rec.writer, err = os.OpenFile(saveFilePath, os.O_RDWR|os.O_CREATE, 0755) + rec.writeStartSession(desktopName, fbWidth, fbHeight) + + if err != nil { + fmt.Printf("unable to open file: %s, error: %v", saveFilePath, err) + return nil + } + + return &rec +} + +const ( + FramebufferUpdate = 0 + SetColourMapEntries = 1 + Bell = 2 + ServerCutText = 3 +) +const versionMsg_3_3 = "RFB 003.003\n" +const versionMsg_3_7 = "RFB 003.007\n" +const versionMsg_3_8 = "RFB 003.008\n" + +// Security types +const ( + SecTypeInvalid = 0 + SecTypeNone = 1 + SecTypeVncAuth = 2 + SecTypeTight = 16 +) + +// func (r *Recorder) writeHeader() error { +// _, err := r.writer.WriteString("FBS 001.000\n") +// return err +// // df.write("FBS 001.000\n".getBytes()); +// } + +func (r *Recorder) writeStartSession(desktopName string, framebufferWidth uint16, framebufferHeight uint16) error { + + //write rfb header information (the only part done without the [size|data|timestamp] block wrapper) + r.buffer.WriteString("FBS 001.000\n") + r.buffer.WriteTo(r.writer) + r.buffer.Reset() + + //push the version message into the buffer so it will be written in the first rbs block + r.buffer.WriteString(versionMsg_3_3) + + //push sec type and fb dimensions + binary.Write(&r.buffer, binary.BigEndian, int32(SecTypeNone)) + binary.Write(&r.buffer, binary.BigEndian, int16(framebufferWidth)) + binary.Write(&r.buffer, binary.BigEndian, int16(framebufferHeight)) + + var fbsServerInitMsg = []byte{32, 24, 0, 1, 0, byte(0xFF), 0, byte(0xFF), 0, byte(0xFF), 16, 8, 0, 0, 0, 0} + r.buffer.Write(fbsServerInitMsg) + + binary.Write(&r.buffer, binary.BigEndian, uint32(len(desktopName)+1)) + + r.buffer.WriteString(desktopName) + binary.Write(&r.buffer, binary.BigEndian, byte(0)) // add null termination for desktop string + + return nil +} + +func (r *Recorder) Consume(data *common.RfbSegment) error { + switch data.SegmentType { + case common.SegmentMessageSeparator: + switch data.UpcomingObjectType { + case FramebufferUpdate: + r.writeToDisk() + case SetColourMapEntries: + case Bell: + case ServerCutText: + default: + return errors.New("unknown message type:" + string(data.UpcomingObjectType)) + } + + case common.SegmentRectSeparator: + r.writeToDisk() + case common.SegmentBytes: + _, err := r.buffer.Write(data.Bytes) + return err + + default: + return errors.New("undefined RfbSegment type") + } + return nil +} + +func (r *Recorder) writeToDisk() error { + timeSinceStart := getNowMillisec() - r.startTime + if r.buffer.Len() == 0 { + return nil + } + + //write buff length + bytesLen := r.buffer.Len() + binary.Write(r.writer, binary.BigEndian, uint32(bytesLen)) + paddedSize := (bytesLen + 3) & 0x7FFFFFFC + paddingSize := paddedSize - bytesLen + + fmt.Printf("paddedSize=%d paddingSize=%d bytesLen=%d", paddedSize, paddingSize, bytesLen) + //write buffer padded to 32bit + _, err := r.buffer.WriteTo(r.writer) + padding := make([]byte, paddingSize) + fmt.Printf("padding=%v ", padding) + + binary.Write(r.writer, binary.BigEndian, padding) + + //write timestamp + binary.Write(r.writer, binary.BigEndian, uint32(timeSinceStart)) + r.buffer.Reset() + return err +} + +// func (r *Recorder) WriteUInt8(data uint8) error { +// buf := make([]byte, 1) +// buf[0] = byte(data) // cast int8 to byte +// return r.Write(buf) +// } + +func (r *Recorder) Close() { + r.writer.Close() +} diff --git a/main.go b/main.go index c151fe9..705e6c2 100644 --- a/main.go +++ b/main.go @@ -16,7 +16,7 @@ func main() { nc, err := net.Dial("tcp", "localhost:5903") if err != nil { - fmt.Printf(";error connecting to vnc server: %s", err) + fmt.Printf("error connecting to vnc server: %s", err) } var noauth vnc.ClientAuthNone authArr := []vnc.ClientAuth{&vnc.PasswordAuth{Password: "Ch_#!T@8"}, &noauth} diff --git a/vnc/README.md b/vnc/README.md index cb0575b..abd2571 100644 --- a/vnc/README.md +++ b/vnc/README.md @@ -1,16 +1,16 @@ -# VNC Library for Go - -go-vnc is a VNC library for Go, initially supporting VNC clients but -with the goal of eventually implementing a VNC server. - -This library implements [RFC 6143](http://tools.ietf.org/html/rfc6143). - -## Usage & Installation - -The library is installable via standard `go get`. The package name is `vnc`. - -``` -$ go get github.com/mitchellh/go-vnc -``` - -Documentation is available on GoDoc: http://godoc.org/github.com/mitchellh/go-vnc +# VNC Library for Go + +go-vnc is a VNC library for Go, initially supporting VNC clients but +with the goal of eventually implementing a VNC server. + +This library implements [RFC 6143](http://tools.ietf.org/html/rfc6143). + +## Usage & Installation + +The library is installable via standard `go get`. The package name is `vnc`. + +``` +$ go get github.com/mitchellh/go-vnc +``` + +Documentation is available on GoDoc: http://godoc.org/github.com/mitchellh/go-vnc diff --git a/vnc/client-conn.go b/vnc/client-conn.go index 5710e0d..1a59085 100644 --- a/vnc/client-conn.go +++ b/vnc/client-conn.go @@ -1,503 +1,508 @@ -package vnc - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "net" - "unicode" - "vncproxy/common" -) - -// A ServerMessage implements a message sent from the server to the client. -type ServerMessage interface { - // The type of the message that is sent down on the wire. - Type() uint8 - String() string - // Read reads the contents of the message from the reader. At the point - // this is called, the message type has already been read from the reader. - // This should return a new ServerMessage that is the appropriate type. - Read(*ClientConn, io.Reader) (ServerMessage, error) -} - -// A ClientAuth implements a method of authenticating with a remote server. -type ClientAuth interface { - // SecurityType returns the byte identifier sent by the server to - // identify this authentication scheme. - SecurityType() uint8 - - // Handshake is called when the authentication handshake should be - // performed, as part of the general RFB handshake. (see 7.2.1) - Handshake(net.Conn) error -} - -type ClientConn struct { - conn net.Conn - - //c net.Conn - config *ClientConfig - - // If the pixel format uses a color map, then this is the color - // map that is used. This should not be modified directly, since - // the data comes from the server. - ColorMap [256]Color - - // Encodings supported by the client. This should not be modified - // directly. Instead, SetEncodings should be used. - Encs []common.Encoding - - // Width of the frame buffer in pixels, sent from the server. - FrameBufferWidth uint16 - - // Height of the frame buffer in pixels, sent from the server. - FrameBufferHeight uint16 - - // Name associated with the desktop, sent from the server. - DesktopName string - - // The pixel format associated with the connection. This shouldn't - // be modified. If you wish to set a new pixel format, use the - // SetPixelFormat method. - PixelFormat common.PixelFormat -} - -// A ClientConfig structure is used to configure a ClientConn. After -// one has been passed to initialize a connection, it must not be modified. -type ClientConfig struct { - // A slice of ClientAuth methods. Only the first instance that is - // suitable by the server will be used to authenticate. - Auth []ClientAuth - - // Exclusive determines whether the connection is shared with other - // clients. If true, then all other clients connected will be - // disconnected when a connection is established to the VNC server. - Exclusive bool - - // The channel that all messages received from the server will be - // sent on. If the channel blocks, then the goroutine reading data - // from the VNC server may block indefinitely. It is up to the user - // of the library to ensure that this channel is properly read. - // If this is not set, then all messages will be discarded. - ServerMessageCh chan<- ServerMessage - - // A slice of supported messages that can be read from the server. - // This only needs to contain NEW server messages, and doesn't - // need to explicitly contain the RFC-required messages. - ServerMessages []ServerMessage -} - -func Client(c net.Conn, cfg *ClientConfig) (*ClientConn, error) { - conn := &ClientConn{ - conn: c, - config: cfg, - } - - if err := conn.handshake(); err != nil { - conn.Close() - return nil, err - } - - go conn.mainLoop() - - return conn, nil -} - -func (c *ClientConn) Close() error { - return c.conn.Close() -} - -// CutText tells the server that the client has new text in its cut buffer. -// The text string MUST only contain Latin-1 characters. This encoding -// is compatible with Go's native string format, but can only use up to -// unicode.MaxLatin values. -// -// See RFC 6143 Section 7.5.6 -func (c *ClientConn) CutText(text string) error { - var buf bytes.Buffer - - // This is the fixed size data we'll send - fixedData := []interface{}{ - uint8(6), - uint8(0), - uint8(0), - uint8(0), - uint32(len(text)), - } - - for _, val := range fixedData { - if err := binary.Write(&buf, binary.BigEndian, val); err != nil { - return err - } - } - - for _, char := range text { - if char > unicode.MaxLatin1 { - return fmt.Errorf("Character '%s' is not valid Latin-1", char) - } - - if err := binary.Write(&buf, binary.BigEndian, uint8(char)); err != nil { - return err - } - } - - dataLength := 8 + len(text) - if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil { - return err - } - - return nil -} - -// Requests a framebuffer update from the server. There may be an indefinite -// time between the request and the actual framebuffer update being -// received. -// -// See RFC 6143 Section 7.5.3 -func (c *ClientConn) FramebufferUpdateRequest(incremental bool, x, y, width, height uint16) error { - var buf bytes.Buffer - var incrementalByte uint8 = 0 - - if incremental { - incrementalByte = 1 - } - - data := []interface{}{ - uint8(3), - incrementalByte, - x, y, width, height, - } - - for _, val := range data { - if err := binary.Write(&buf, binary.BigEndian, val); err != nil { - return err - } - } - - if _, err := c.conn.Write(buf.Bytes()[0:10]); err != nil { - return err - } - - return nil -} - -// KeyEvent indiciates a key press or release and sends it to the server. -// The key is indicated using the X Window System "keysym" value. Use -// Google to find a reference of these values. To simulate a key press, -// you must send a key with both a down event, and a non-down event. -// -// See 7.5.4. -func (c *ClientConn) KeyEvent(keysym uint32, down bool) error { - var downFlag uint8 = 0 - if down { - downFlag = 1 - } - - data := []interface{}{ - uint8(4), - downFlag, - uint8(0), - uint8(0), - keysym, - } - - for _, val := range data { - if err := binary.Write(c.conn, binary.BigEndian, val); err != nil { - return err - } - } - - return nil -} - -// PointerEvent indicates that pointer movement or a pointer button -// press or release. -// -// The mask is a bitwise mask of various ButtonMask values. When a button -// is set, it is pressed, when it is unset, it is released. -// -// See RFC 6143 Section 7.5.5 -func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error { - var buf bytes.Buffer - - data := []interface{}{ - uint8(5), - uint8(mask), - x, - y, - } - - for _, val := range data { - if err := binary.Write(&buf, binary.BigEndian, val); err != nil { - return err - } - } - - if _, err := c.conn.Write(buf.Bytes()[0:6]); err != nil { - return err - } - - return nil -} - -// SetEncodings sets the encoding types in which the pixel data can -// be sent from the server. After calling this method, the encs slice -// given should not be modified. -// -// See RFC 6143 Section 7.5.2 -func (c *ClientConn) SetEncodings(encs []common.Encoding) error { - data := make([]interface{}, 3+len(encs)) - data[0] = uint8(2) - data[1] = uint8(0) - data[2] = uint16(len(encs)) - - for i, enc := range encs { - data[3+i] = int32(enc.Type()) - } - - var buf bytes.Buffer - for _, val := range data { - if err := binary.Write(&buf, binary.BigEndian, val); err != nil { - return err - } - } - - dataLength := 4 + (4 * len(encs)) - if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil { - return err - } - - c.Encs = encs - - return nil -} - -// SetPixelFormat sets the format in which pixel values should be sent -// in FramebufferUpdate messages from the server. -// -// See RFC 6143 Section 7.5.1 -func (c *ClientConn) SetPixelFormat(format *common.PixelFormat) error { - var keyEvent [20]byte - keyEvent[0] = 0 - - pfBytes, err := writePixelFormat(format) - if err != nil { - return err - } - - // Copy the pixel format bytes into the proper slice location - copy(keyEvent[4:], pfBytes) - - // Send the data down the connection - if _, err := c.conn.Write(keyEvent[:]); err != nil { - return err - } - - // Reset the color map as according to RFC. - var newColorMap [256]Color - c.ColorMap = newColorMap - - return nil -} - -const pvLen = 12 // ProtocolVersion message length. - -func parseProtocolVersion(pv []byte) (uint, uint, error) { - var major, minor uint - - if len(pv) < pvLen { - return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), pvLen) - } - - l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor) - if l != 2 { - return 0, 0, fmt.Errorf("error parsing ProtocolVersion.") - } - if err != nil { - return 0, 0, err - } - - return major, minor, nil -} - -func (c *ClientConn) handshake() error { - var protocolVersion [pvLen]byte - - // 7.1.1, read the ProtocolVersion message sent by the server. - if _, err := io.ReadFull(c.conn, protocolVersion[:]); err != nil { - return err - } - - maxMajor, maxMinor, err := parseProtocolVersion(protocolVersion[:]) - if err != nil { - return err - } - if maxMajor < 3 { - return fmt.Errorf("unsupported major version, less than 3: %d", maxMajor) - } - if maxMinor < 8 { - return fmt.Errorf("unsupported minor version, less than 8: %d", maxMinor) - } - - // Respond with the version we will support - if _, err = c.conn.Write([]byte("RFB 003.008\n")); err != nil { - return err - } - - // 7.1.2 Security Handshake from server - var numSecurityTypes uint8 - if err = binary.Read(c.conn, binary.BigEndian, &numSecurityTypes); err != nil { - return err - } - - if numSecurityTypes == 0 { - return fmt.Errorf("no security types: %s", c.readErrorReason()) - } - - securityTypes := make([]uint8, numSecurityTypes) - if err = binary.Read(c.conn, binary.BigEndian, &securityTypes); err != nil { - return err - } - - clientSecurityTypes := c.config.Auth - if clientSecurityTypes == nil { - clientSecurityTypes = []ClientAuth{new(ClientAuthNone)} - } - - var auth ClientAuth -FindAuth: - for _, curAuth := range clientSecurityTypes { - for _, securityType := range securityTypes { - if curAuth.SecurityType() == securityType { - // We use the first matching supported authentication - auth = curAuth - break FindAuth - } - } - } - - if auth == nil { - return fmt.Errorf("no suitable auth schemes found. server supported: %#v", securityTypes) - } - - // Respond back with the security type we'll use - if err = binary.Write(c.conn, binary.BigEndian, auth.SecurityType()); err != nil { - return err - } - - if err = auth.Handshake(c.conn); err != nil { - return err - } - - // 7.1.3 SecurityResult Handshake - var securityResult uint32 - if err = binary.Read(c.conn, binary.BigEndian, &securityResult); err != nil { - return err - } - - if securityResult == 1 { - return fmt.Errorf("security handshake failed: %s", c.readErrorReason()) - } - - // 7.3.1 ClientInit - var sharedFlag uint8 = 1 - if c.config.Exclusive { - sharedFlag = 0 - } - - if err = binary.Write(c.conn, binary.BigEndian, sharedFlag); err != nil { - return err - } - - // 7.3.2 ServerInit - if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferWidth); err != nil { - return err - } - - if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferHeight); err != nil { - return err - } - - // Read the pixel format - if err = readPixelFormat(c.conn, &c.PixelFormat); err != nil { - return err - } - - var nameLength uint32 - if err = binary.Read(c.conn, binary.BigEndian, &nameLength); err != nil { - return err - } - - nameBytes := make([]uint8, nameLength) - if err = binary.Read(c.conn, binary.BigEndian, &nameBytes); err != nil { - return err - } - - c.DesktopName = string(nameBytes) - - return nil -} - -// mainLoop reads messages sent from the server and routes them to the -// proper channels for users of the client to read. -func (c *ClientConn) mainLoop() { - defer c.Close() - - // Build the map of available server messages - typeMap := make(map[uint8]ServerMessage) - - defaultMessages := []ServerMessage{ - new(FramebufferUpdateMessage), - new(SetColorMapEntriesMessage), - new(BellMessage), - new(ServerCutTextMessage), - } - - for _, msg := range defaultMessages { - typeMap[msg.Type()] = msg - } - - if c.config.ServerMessages != nil { - for _, msg := range c.config.ServerMessages { - typeMap[msg.Type()] = msg - } - } - - for { - var messageType uint8 - if err := binary.Read(c.conn, binary.BigEndian, &messageType); err != nil { - break - } - - msg, ok := typeMap[messageType] - if !ok { - // Unsupported message type! Bad! - break - } - - parsedMsg, err := msg.Read(c, c.conn) - if err != nil { - break - } - - if c.config.ServerMessageCh == nil { - continue - } - - c.config.ServerMessageCh <- parsedMsg - } -} - -func (c *ClientConn) readErrorReason() string { - var reasonLen uint32 - if err := binary.Read(c.conn, binary.BigEndian, &reasonLen); err != nil { - return "" - } - - reason := make([]uint8, reasonLen) - if err := binary.Read(c.conn, binary.BigEndian, &reason); err != nil { - return "" - } - - return string(reason) -} +package vnc + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "unicode" + "vncproxy/common" + "vncproxy/listeners" +) + +// A ServerMessage implements a message sent from the server to the client. +type ServerMessage interface { + // The type of the message that is sent down on the wire. + Type() uint8 + String() string + // Read reads the contents of the message from the reader. At the point + // this is called, the message type has already been read from the reader. + // This should return a new ServerMessage that is the appropriate type. + Read(*ClientConn, *common.RfbReadHelper) (ServerMessage, error) +} + +// A ClientAuth implements a method of authenticating with a remote server. +type ClientAuth interface { + // SecurityType returns the byte identifier sent by the server to + // identify this authentication scheme. + SecurityType() uint8 + + // Handshake is called when the authentication handshake should be + // performed, as part of the general RFB handshake. (see 7.2.1) + Handshake(net.Conn) error +} + +type ClientConn struct { + conn net.Conn + + //c net.Conn + config *ClientConfig + + // If the pixel format uses a color map, then this is the color + // map that is used. This should not be modified directly, since + // the data comes from the server. + ColorMap [256]Color + + // Encodings supported by the client. This should not be modified + // directly. Instead, SetEncodings should be used. + Encs []common.Encoding + + // Width of the frame buffer in pixels, sent from the server. + FrameBufferWidth uint16 + + // Height of the frame buffer in pixels, sent from the server. + FrameBufferHeight uint16 + + // Name associated with the desktop, sent from the server. + DesktopName string + + // The pixel format associated with the connection. This shouldn't + // be modified. If you wish to set a new pixel format, use the + // SetPixelFormat method. + PixelFormat common.PixelFormat +} + +// A ClientConfig structure is used to configure a ClientConn. After +// one has been passed to initialize a connection, it must not be modified. +type ClientConfig struct { + // A slice of ClientAuth methods. Only the first instance that is + // suitable by the server will be used to authenticate. + Auth []ClientAuth + + // Exclusive determines whether the connection is shared with other + // clients. If true, then all other clients connected will be + // disconnected when a connection is established to the VNC server. + Exclusive bool + + // The channel that all messages received from the server will be + // sent on. If the channel blocks, then the goroutine reading data + // from the VNC server may block indefinitely. It is up to the user + // of the library to ensure that this channel is properly read. + // If this is not set, then all messages will be discarded. + ServerMessageCh chan<- ServerMessage + + // A slice of supported messages that can be read from the server. + // This only needs to contain NEW server messages, and doesn't + // need to explicitly contain the RFC-required messages. + ServerMessages []ServerMessage +} + +func Client(c net.Conn, cfg *ClientConfig) (*ClientConn, error) { + conn := &ClientConn{ + conn: c, + config: cfg, + } + + if err := conn.handshake(); err != nil { + conn.Close() + return nil, err + } + + go conn.mainLoop() + + return conn, nil +} + +func (c *ClientConn) Close() error { + return c.conn.Close() +} + +// CutText tells the server that the client has new text in its cut buffer. +// The text string MUST only contain Latin-1 characters. This encoding +// is compatible with Go's native string format, but can only use up to +// unicode.MaxLatin values. +// +// See RFC 6143 Section 7.5.6 +func (c *ClientConn) CutText(text string) error { + var buf bytes.Buffer + + // This is the fixed size data we'll send + fixedData := []interface{}{ + uint8(6), + uint8(0), + uint8(0), + uint8(0), + uint32(len(text)), + } + + for _, val := range fixedData { + if err := binary.Write(&buf, binary.BigEndian, val); err != nil { + return err + } + } + + for _, char := range text { + if char > unicode.MaxLatin1 { + return fmt.Errorf("Character '%s' is not valid Latin-1", char) + } + + if err := binary.Write(&buf, binary.BigEndian, uint8(char)); err != nil { + return err + } + } + + dataLength := 8 + len(text) + if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil { + return err + } + + return nil +} + +// Requests a framebuffer update from the server. There may be an indefinite +// time between the request and the actual framebuffer update being +// received. +// +// See RFC 6143 Section 7.5.3 +func (c *ClientConn) FramebufferUpdateRequest(incremental bool, x, y, width, height uint16) error { + var buf bytes.Buffer + var incrementalByte uint8 = 0 + + if incremental { + incrementalByte = 1 + } + + data := []interface{}{ + uint8(3), + incrementalByte, + x, y, width, height, + } + + for _, val := range data { + if err := binary.Write(&buf, binary.BigEndian, val); err != nil { + return err + } + } + + if _, err := c.conn.Write(buf.Bytes()[0:10]); err != nil { + return err + } + + return nil +} + +// KeyEvent indiciates a key press or release and sends it to the server. +// The key is indicated using the X Window System "keysym" value. Use +// Google to find a reference of these values. To simulate a key press, +// you must send a key with both a down event, and a non-down event. +// +// See 7.5.4. +func (c *ClientConn) KeyEvent(keysym uint32, down bool) error { + var downFlag uint8 = 0 + if down { + downFlag = 1 + } + + data := []interface{}{ + uint8(4), + downFlag, + uint8(0), + uint8(0), + keysym, + } + + for _, val := range data { + if err := binary.Write(c.conn, binary.BigEndian, val); err != nil { + return err + } + } + + return nil +} + +// PointerEvent indicates that pointer movement or a pointer button +// press or release. +// +// The mask is a bitwise mask of various ButtonMask values. When a button +// is set, it is pressed, when it is unset, it is released. +// +// See RFC 6143 Section 7.5.5 +func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error { + var buf bytes.Buffer + + data := []interface{}{ + uint8(5), + uint8(mask), + x, + y, + } + + for _, val := range data { + if err := binary.Write(&buf, binary.BigEndian, val); err != nil { + return err + } + } + + if _, err := c.conn.Write(buf.Bytes()[0:6]); err != nil { + return err + } + + return nil +} + +// SetEncodings sets the encoding types in which the pixel data can +// be sent from the server. After calling this method, the encs slice +// given should not be modified. +// +// See RFC 6143 Section 7.5.2 +func (c *ClientConn) SetEncodings(encs []common.Encoding) error { + data := make([]interface{}, 3+len(encs)) + data[0] = uint8(2) + data[1] = uint8(0) + data[2] = uint16(len(encs)) + + for i, enc := range encs { + data[3+i] = int32(enc.Type()) + } + + var buf bytes.Buffer + for _, val := range data { + if err := binary.Write(&buf, binary.BigEndian, val); err != nil { + return err + } + } + + dataLength := 4 + (4 * len(encs)) + if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil { + return err + } + + c.Encs = encs + + return nil +} + +// SetPixelFormat sets the format in which pixel values should be sent +// in FramebufferUpdate messages from the server. +// +// See RFC 6143 Section 7.5.1 +func (c *ClientConn) SetPixelFormat(format *common.PixelFormat) error { + var keyEvent [20]byte + keyEvent[0] = 0 + + pfBytes, err := writePixelFormat(format) + if err != nil { + return err + } + + // Copy the pixel format bytes into the proper slice location + copy(keyEvent[4:], pfBytes) + + // Send the data down the connection + if _, err := c.conn.Write(keyEvent[:]); err != nil { + return err + } + + // Reset the color map as according to RFC. + var newColorMap [256]Color + c.ColorMap = newColorMap + + return nil +} + +const pvLen = 12 // ProtocolVersion message length. + +func parseProtocolVersion(pv []byte) (uint, uint, error) { + var major, minor uint + + if len(pv) < pvLen { + return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), pvLen) + } + + l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor) + if l != 2 { + return 0, 0, fmt.Errorf("error parsing ProtocolVersion.") + } + if err != nil { + return 0, 0, err + } + + return major, minor, nil +} + +func (c *ClientConn) handshake() error { + var protocolVersion [pvLen]byte + + // 7.1.1, read the ProtocolVersion message sent by the server. + if _, err := io.ReadFull(c.conn, protocolVersion[:]); err != nil { + return err + } + + maxMajor, maxMinor, err := parseProtocolVersion(protocolVersion[:]) + if err != nil { + return err + } + if maxMajor < 3 { + return fmt.Errorf("unsupported major version, less than 3: %d", maxMajor) + } + if maxMinor < 8 { + return fmt.Errorf("unsupported minor version, less than 8: %d", maxMinor) + } + + // Respond with the version we will support + if _, err = c.conn.Write([]byte("RFB 003.008\n")); err != nil { + return err + } + + // 7.1.2 Security Handshake from server + var numSecurityTypes uint8 + if err = binary.Read(c.conn, binary.BigEndian, &numSecurityTypes); err != nil { + return err + } + + if numSecurityTypes == 0 { + return fmt.Errorf("no security types: %s", c.readErrorReason()) + } + + securityTypes := make([]uint8, numSecurityTypes) + if err = binary.Read(c.conn, binary.BigEndian, &securityTypes); err != nil { + return err + } + + clientSecurityTypes := c.config.Auth + if clientSecurityTypes == nil { + clientSecurityTypes = []ClientAuth{new(ClientAuthNone)} + } + + var auth ClientAuth +FindAuth: + for _, curAuth := range clientSecurityTypes { + for _, securityType := range securityTypes { + if curAuth.SecurityType() == securityType { + // We use the first matching supported authentication + auth = curAuth + break FindAuth + } + } + } + + if auth == nil { + return fmt.Errorf("no suitable auth schemes found. server supported: %#v", securityTypes) + } + + // Respond back with the security type we'll use + if err = binary.Write(c.conn, binary.BigEndian, auth.SecurityType()); err != nil { + return err + } + + if err = auth.Handshake(c.conn); err != nil { + return err + } + + // 7.1.3 SecurityResult Handshake + var securityResult uint32 + if err = binary.Read(c.conn, binary.BigEndian, &securityResult); err != nil { + return err + } + + if securityResult == 1 { + return fmt.Errorf("security handshake failed: %s", c.readErrorReason()) + } + + // 7.3.1 ClientInit + var sharedFlag uint8 = 1 + if c.config.Exclusive { + sharedFlag = 0 + } + + if err = binary.Write(c.conn, binary.BigEndian, sharedFlag); err != nil { + return err + } + + // 7.3.2 ServerInit + if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferWidth); err != nil { + return err + } + + if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferHeight); err != nil { + return err + } + + // Read the pixel format + if err = readPixelFormat(c.conn, &c.PixelFormat); err != nil { + return err + } + + var nameLength uint32 + if err = binary.Read(c.conn, binary.BigEndian, &nameLength); err != nil { + return err + } + + nameBytes := make([]uint8, nameLength) + if err = binary.Read(c.conn, binary.BigEndian, &nameBytes); err != nil { + return err + } + + c.DesktopName = string(nameBytes) + + return nil +} + +// mainLoop reads messages sent from the server and routes them to the +// proper channels for users of the client to read. +func (c *ClientConn) mainLoop() { + defer c.Close() + rec := listeners.NewRecorder("/Users/amitbet/recording.rbs", c.DesktopName, c.FrameBufferWidth, c.FrameBufferHeight) + + reader := &common.RfbReadHelper{Reader: c.conn, Listener: rec} + // Build the map of available server messages + typeMap := make(map[uint8]ServerMessage) + + defaultMessages := []ServerMessage{ + new(FramebufferUpdateMessage), + new(SetColorMapEntriesMessage), + new(BellMessage), + new(ServerCutTextMessage), + } + + for _, msg := range defaultMessages { + typeMap[msg.Type()] = msg + } + + if c.config.ServerMessages != nil { + for _, msg := range c.config.ServerMessages { + typeMap[msg.Type()] = msg + } + } + + for { + var messageType uint8 + if err := binary.Read(c.conn, binary.BigEndian, &messageType); err != nil { + break + } + + msg, ok := typeMap[messageType] + if !ok { + // Unsupported message type! Bad! + break + } + reader.SendMessageSeparator(int(messageType)) + reader.PublishBytes([]byte{byte(messageType)}) + + parsedMsg, err := msg.Read(c, reader) + if err != nil { + break + } + + if c.config.ServerMessageCh == nil { + continue + } + + c.config.ServerMessageCh <- parsedMsg + } +} + +func (c *ClientConn) readErrorReason() string { + var reasonLen uint32 + if err := binary.Read(c.conn, binary.BigEndian, &reasonLen); err != nil { + return "" + } + + reason := make([]uint8, reasonLen) + if err := binary.Read(c.conn, binary.BigEndian, &reason); err != nil { + return "" + } + + return string(reason) +} diff --git a/vnc/client_auth.go b/vnc/client_auth.go index 89b4bd8..1b6a48e 100644 --- a/vnc/client_auth.go +++ b/vnc/client_auth.go @@ -1,114 +1,114 @@ -package vnc - -import ( - "net" - - "crypto/des" - "encoding/binary" -) - - -// ClientAuthNone is the "none" authentication. See 7.2.1 -type ClientAuthNone byte - -func (*ClientAuthNone) SecurityType() uint8 { - return 1 -} - -func (*ClientAuthNone) Handshake(net.Conn) error { - return nil -} - -// PasswordAuth is VNC authentication, 7.2.2 -type PasswordAuth struct { - Password string -} - -func (p *PasswordAuth) SecurityType() uint8 { - return 2 -} - -func (p *PasswordAuth) Handshake(c net.Conn) error { - randomValue := make([]uint8, 16) - if err := binary.Read(c, binary.BigEndian, &randomValue); err != nil { - return err - } - - crypted, err := p.encrypt(p.Password, randomValue) - - if (err != nil) { - return err - } - - if err := binary.Write(c, binary.BigEndian, &crypted); err != nil { - return err - } - - return nil -} - -func (p *PasswordAuth) reverseBits(b byte) byte { - var reverse = [256]int{ - 0, 128, 64, 192, 32, 160, 96, 224, - 16, 144, 80, 208, 48, 176, 112, 240, - 8, 136, 72, 200, 40, 168, 104, 232, - 24, 152, 88, 216, 56, 184, 120, 248, - 4, 132, 68, 196, 36, 164, 100, 228, - 20, 148, 84, 212, 52, 180, 116, 244, - 12, 140, 76, 204, 44, 172, 108, 236, - 28, 156, 92, 220, 60, 188, 124, 252, - 2, 130, 66, 194, 34, 162, 98, 226, - 18, 146, 82, 210, 50, 178, 114, 242, - 10, 138, 74, 202, 42, 170, 106, 234, - 26, 154, 90, 218, 58, 186, 122, 250, - 6, 134, 70, 198, 38, 166, 102, 230, - 22, 150, 86, 214, 54, 182, 118, 246, - 14, 142, 78, 206, 46, 174, 110, 238, - 30, 158, 94, 222, 62, 190, 126, 254, - 1, 129, 65, 193, 33, 161, 97, 225, - 17, 145, 81, 209, 49, 177, 113, 241, - 9, 137, 73, 201, 41, 169, 105, 233, - 25, 153, 89, 217, 57, 185, 121, 249, - 5, 133, 69, 197, 37, 165, 101, 229, - 21, 149, 85, 213, 53, 181, 117, 245, - 13, 141, 77, 205, 45, 173, 109, 237, - 29, 157, 93, 221, 61, 189, 125, 253, - 3, 131, 67, 195, 35, 163, 99, 227, - 19, 147, 83, 211, 51, 179, 115, 243, - 11, 139, 75, 203, 43, 171, 107, 235, - 27, 155, 91, 219, 59, 187, 123, 251, - 7, 135, 71, 199, 39, 167, 103, 231, - 23, 151, 87, 215, 55, 183, 119, 247, - 15, 143, 79, 207, 47, 175, 111, 239, - 31, 159, 95, 223, 63, 191, 127, 255, - } - - return byte(reverse[int(b)]) -} - -func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) { - keyBytes := []byte{0,0,0,0,0,0,0,0} - - if len(key) > 8 { - key = key[:8] - } - - for i := 0; i < len(key); i++ { - keyBytes[i] = p.reverseBits(key[i]) - } - - block, err := des.NewCipher(keyBytes) - - if err != nil { - return nil, err - } - - result1 := make([]byte, 8) - block.Encrypt(result1, bytes) - result2 := make([]byte, 8) - block.Encrypt(result2, bytes[8:]) - - crypted := append(result1, result2...) - - return crypted, nil -} +package vnc + +import ( + "net" + + "crypto/des" + "encoding/binary" +) + + +// ClientAuthNone is the "none" authentication. See 7.2.1 +type ClientAuthNone byte + +func (*ClientAuthNone) SecurityType() uint8 { + return 1 +} + +func (*ClientAuthNone) Handshake(net.Conn) error { + return nil +} + +// PasswordAuth is VNC authentication, 7.2.2 +type PasswordAuth struct { + Password string +} + +func (p *PasswordAuth) SecurityType() uint8 { + return 2 +} + +func (p *PasswordAuth) Handshake(c net.Conn) error { + randomValue := make([]uint8, 16) + if err := binary.Read(c, binary.BigEndian, &randomValue); err != nil { + return err + } + + crypted, err := p.encrypt(p.Password, randomValue) + + if (err != nil) { + return err + } + + if err := binary.Write(c, binary.BigEndian, &crypted); err != nil { + return err + } + + return nil +} + +func (p *PasswordAuth) reverseBits(b byte) byte { + var reverse = [256]int{ + 0, 128, 64, 192, 32, 160, 96, 224, + 16, 144, 80, 208, 48, 176, 112, 240, + 8, 136, 72, 200, 40, 168, 104, 232, + 24, 152, 88, 216, 56, 184, 120, 248, + 4, 132, 68, 196, 36, 164, 100, 228, + 20, 148, 84, 212, 52, 180, 116, 244, + 12, 140, 76, 204, 44, 172, 108, 236, + 28, 156, 92, 220, 60, 188, 124, 252, + 2, 130, 66, 194, 34, 162, 98, 226, + 18, 146, 82, 210, 50, 178, 114, 242, + 10, 138, 74, 202, 42, 170, 106, 234, + 26, 154, 90, 218, 58, 186, 122, 250, + 6, 134, 70, 198, 38, 166, 102, 230, + 22, 150, 86, 214, 54, 182, 118, 246, + 14, 142, 78, 206, 46, 174, 110, 238, + 30, 158, 94, 222, 62, 190, 126, 254, + 1, 129, 65, 193, 33, 161, 97, 225, + 17, 145, 81, 209, 49, 177, 113, 241, + 9, 137, 73, 201, 41, 169, 105, 233, + 25, 153, 89, 217, 57, 185, 121, 249, + 5, 133, 69, 197, 37, 165, 101, 229, + 21, 149, 85, 213, 53, 181, 117, 245, + 13, 141, 77, 205, 45, 173, 109, 237, + 29, 157, 93, 221, 61, 189, 125, 253, + 3, 131, 67, 195, 35, 163, 99, 227, + 19, 147, 83, 211, 51, 179, 115, 243, + 11, 139, 75, 203, 43, 171, 107, 235, + 27, 155, 91, 219, 59, 187, 123, 251, + 7, 135, 71, 199, 39, 167, 103, 231, + 23, 151, 87, 215, 55, 183, 119, 247, + 15, 143, 79, 207, 47, 175, 111, 239, + 31, 159, 95, 223, 63, 191, 127, 255, + } + + return byte(reverse[int(b)]) +} + +func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) { + keyBytes := []byte{0,0,0,0,0,0,0,0} + + if len(key) > 8 { + key = key[:8] + } + + for i := 0; i < len(key); i++ { + keyBytes[i] = p.reverseBits(key[i]) + } + + block, err := des.NewCipher(keyBytes) + + if err != nil { + return nil, err + } + + result1 := make([]byte, 8) + block.Encrypt(result1, bytes) + result2 := make([]byte, 8) + block.Encrypt(result2, bytes[8:]) + + crypted := append(result1, result2...) + + return crypted, nil +} diff --git a/vnc/client_auth_test.go b/vnc/client_auth_test.go index 0095eed..412e041 100644 --- a/vnc/client_auth_test.go +++ b/vnc/client_auth_test.go @@ -1,169 +1,169 @@ -package vnc - -import ( - "testing" - "net" - "time" - "bytes" -) - -type fakeNetConnection struct { - DataToSend []byte - Test *testing.T - ExpectData []byte - Finished bool - Matched bool -} - -func (fc fakeNetConnection) Read(b []byte) (n int, err error) { - for i := 0; i < 16; i++ { - b[i] = fc.DataToSend[i] - } - - fc.Finished = false - - return len(b), nil -} - -func (fc *fakeNetConnection) Write(b []byte) (n int, err error) { - fc.Matched = bytes.Equal(b, fc.ExpectData) - fc.Finished = true - - return len(b), nil -} - -func (fc *fakeNetConnection) Close() error { return nil; } -func (fc *fakeNetConnection) LocalAddr() net.Addr { return nil; } -func (fc *fakeNetConnection) RemoteAddr() net.Addr { return nil; } -func (fc *fakeNetConnection) SetDeadline(t time.Time) error { return nil; } -func (fc *fakeNetConnection) SetReadDeadline(t time.Time) error { return nil; } -func (fc *fakeNetConnection) SetWriteDeadline(t time.Time) error { return nil; } - -func TestClientAuthNone_Impl(t *testing.T) { - var raw interface{} - raw = new(ClientAuthNone) - if _, ok := raw.(ClientAuth); !ok { - t.Fatal("ClientAuthNone doesn't implement ClientAuth") - } -} - -func TestClientAuthPasswordSuccess_Impl(t *testing.T) { - // Values ripped using WireShark - randomValue := []byte{ - 0xa4, - 0x51, - 0x3f, - 0xa5, - 0x1f, - 0x87, - 0x06, - 0x10, - 0xa4, - 0x5f, - 0xae, - 0xbf, - 0x4d, - 0xac, - 0x12, - 0x22, - } - - expectedResponse := []byte{ - 0x71, - 0xe4, - 0x41, - 0x30, - 0x43, - 0x65, - 0x4e, - 0x39, - 0xda, - 0x6d, - 0x49, - 0x93, - 0x43, - 0xf6, - 0x5e, - 0x29, - } - - raw := PasswordAuth{Password: "Ch_#!T@8"} - - // Only about 12 hours into Go at the moment... - // if _, ok := raw.(ClientAuth); !ok { - // t.Fatal("PasswordAuth doesn't implement ClientAuth") - // } - - conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} - err := raw.Handshake(conn) - - if (err != nil) { - t.Fatal(err) - } - - if !conn.Matched { - t.Fatal("PasswordAuth didn't pass the right response back to the wire") - } - - if !conn.Finished { - t.Fatal("PasswordAuth didn't complete properly") - } -} - -func TestClientAuthPasswordReject_Impl(t *testing.T) { - // Values ripped using WireShark - randomValue := []byte{ - 0xa4, - 0x51, - 0x3f, - 0xa5, - 0x1f, - 0x87, - 0x06, - 0x10, - 0xa4, - 0x5f, - 0xae, - 0xbf, - 0x4d, - 0xac, - 0x12, - 0x22, - } - - expectedResponse := []byte{ - 0x71, - 0xe4, - 0x41, - 0x30, - 0x43, - 0x65, - 0x4e, - 0x39, - 0xda, - 0x6d, - 0x49, - 0x93, - 0x43, - 0xf6, - 0x5e, - 0x29, - } - - raw := PasswordAuth{Password: "Ch_#!T@"} - - conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} - err := raw.Handshake(conn) - - if (err != nil) { - t.Fatal(err) - } - - if conn.Matched { - t.Fatal("PasswordAuth didn't pass the right response back to the wire") - } - - if !conn.Finished { - t.Fatal("PasswordAuth didn't complete properly") - } +package vnc + +import ( + "testing" + "net" + "time" + "bytes" +) + +type fakeNetConnection struct { + DataToSend []byte + Test *testing.T + ExpectData []byte + Finished bool + Matched bool +} + +func (fc fakeNetConnection) Read(b []byte) (n int, err error) { + for i := 0; i < 16; i++ { + b[i] = fc.DataToSend[i] + } + + fc.Finished = false + + return len(b), nil +} + +func (fc *fakeNetConnection) Write(b []byte) (n int, err error) { + fc.Matched = bytes.Equal(b, fc.ExpectData) + fc.Finished = true + + return len(b), nil +} + +func (fc *fakeNetConnection) Close() error { return nil; } +func (fc *fakeNetConnection) LocalAddr() net.Addr { return nil; } +func (fc *fakeNetConnection) RemoteAddr() net.Addr { return nil; } +func (fc *fakeNetConnection) SetDeadline(t time.Time) error { return nil; } +func (fc *fakeNetConnection) SetReadDeadline(t time.Time) error { return nil; } +func (fc *fakeNetConnection) SetWriteDeadline(t time.Time) error { return nil; } + +func TestClientAuthNone_Impl(t *testing.T) { + var raw interface{} + raw = new(ClientAuthNone) + if _, ok := raw.(ClientAuth); !ok { + t.Fatal("ClientAuthNone doesn't implement ClientAuth") + } +} + +func TestClientAuthPasswordSuccess_Impl(t *testing.T) { + // Values ripped using WireShark + randomValue := []byte{ + 0xa4, + 0x51, + 0x3f, + 0xa5, + 0x1f, + 0x87, + 0x06, + 0x10, + 0xa4, + 0x5f, + 0xae, + 0xbf, + 0x4d, + 0xac, + 0x12, + 0x22, + } + + expectedResponse := []byte{ + 0x71, + 0xe4, + 0x41, + 0x30, + 0x43, + 0x65, + 0x4e, + 0x39, + 0xda, + 0x6d, + 0x49, + 0x93, + 0x43, + 0xf6, + 0x5e, + 0x29, + } + + raw := PasswordAuth{Password: "Ch_#!T@8"} + + // Only about 12 hours into Go at the moment... + // if _, ok := raw.(ClientAuth); !ok { + // t.Fatal("PasswordAuth doesn't implement ClientAuth") + // } + + conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} + err := raw.Handshake(conn) + + if (err != nil) { + t.Fatal(err) + } + + if !conn.Matched { + t.Fatal("PasswordAuth didn't pass the right response back to the wire") + } + + if !conn.Finished { + t.Fatal("PasswordAuth didn't complete properly") + } +} + +func TestClientAuthPasswordReject_Impl(t *testing.T) { + // Values ripped using WireShark + randomValue := []byte{ + 0xa4, + 0x51, + 0x3f, + 0xa5, + 0x1f, + 0x87, + 0x06, + 0x10, + 0xa4, + 0x5f, + 0xae, + 0xbf, + 0x4d, + 0xac, + 0x12, + 0x22, + } + + expectedResponse := []byte{ + 0x71, + 0xe4, + 0x41, + 0x30, + 0x43, + 0x65, + 0x4e, + 0x39, + 0xda, + 0x6d, + 0x49, + 0x93, + 0x43, + 0xf6, + 0x5e, + 0x29, + } + + raw := PasswordAuth{Password: "Ch_#!T@"} + + conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} + err := raw.Handshake(conn) + + if (err != nil) { + t.Fatal(err) + } + + if conn.Matched { + t.Fatal("PasswordAuth didn't pass the right response back to the wire") + } + + if !conn.Finished { + t.Fatal("PasswordAuth didn't complete properly") + } } \ No newline at end of file diff --git a/vnc/client_test.go b/vnc/client_test.go index 31591b4..b414567 100644 --- a/vnc/client_test.go +++ b/vnc/client_test.go @@ -1,95 +1,95 @@ -package vnc - -import ( - "fmt" - "net" - "testing" -) - -func newMockServer(t *testing.T, version string) string { - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("error listening: %s", err) - } - - go func() { - defer ln.Close() - c, err := ln.Accept() - if err != nil { - t.Fatalf("error accepting conn: %s", err) - } - defer c.Close() - - _, err = c.Write([]byte(fmt.Sprintf("RFB %s\n", version))) - if err != nil { - t.Fatal("failed writing version") - } - }() - - return ln.Addr().String() -} - -func TestClient_LowMajorVersion(t *testing.T) { - nc, err := net.Dial("tcp", newMockServer(t, "002.009")) - if err != nil { - t.Fatalf("error connecting to mock server: %s", err) - } - - _, err = Client(nc, &ClientConfig{}) - if err == nil { - t.Fatal("error expected") - } - - if err.Error() != "unsupported major version, less than 3: 2" { - t.Fatalf("unexpected error: %s", err) - } -} - -func TestClient_LowMinorVersion(t *testing.T) { - nc, err := net.Dial("tcp", newMockServer(t, "003.007")) - if err != nil { - t.Fatalf("error connecting to mock server: %s", err) - } - - _, err = Client(nc, &ClientConfig{}) - if err == nil { - t.Fatal("error expected") - } - - if err.Error() != "unsupported minor version, less than 8: 7" { - t.Fatalf("unexpected error: %s", err) - } -} - -func TestParseProtocolVersion(t *testing.T) { - tests := []struct { - proto []byte - major, minor uint - isErr bool - }{ - // Valid ProtocolVersion messages. - {[]byte{82, 70, 66, 32, 48, 48, 51, 46, 48, 48, 56, 10}, 3, 8, false}, // RFB 003.008\n - {[]byte{82, 70, 66, 32, 48, 48, 51, 46, 56, 56, 57, 10}, 3, 889, false}, // RFB 003.889\n -- OS X 10.10.3 - {[]byte{82, 70, 66, 32, 48, 48, 48, 46, 48, 48, 48, 10}, 0, 0, false}, // RFB 000.0000\n - // Invalid messages. - {[]byte{82, 70, 66, 32, 51, 46, 56, 10}, 0, 0, true}, // RFB 3.8\n -- too short; not zero padded - {[]byte{82, 70, 66, 10}, 0, 0, true}, // RFB\n -- too short - {[]byte{}, 0, 0, true}, // (empty) -- too short - } - - for _, tt := range tests { - major, minor, err := parseProtocolVersion(tt.proto) - if err != nil && !tt.isErr { - t.Fatalf("parseProtocolVersion(%v) unexpected error %v", tt.proto, err) - } - if err == nil && tt.isErr { - t.Fatalf("parseProtocolVersion(%v) expected error", tt.proto) - } - if major != tt.major { - t.Errorf("parseProtocolVersion(%v) major = %v, want %v", tt.proto, major, tt.major) - } - if major != tt.major { - t.Errorf("parseProtocolVersion(%v) minor = %v, want %v", tt.proto, minor, tt.minor) - } - } -} +package vnc + +import ( + "fmt" + "net" + "testing" +) + +func newMockServer(t *testing.T, version string) string { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("error listening: %s", err) + } + + go func() { + defer ln.Close() + c, err := ln.Accept() + if err != nil { + t.Fatalf("error accepting conn: %s", err) + } + defer c.Close() + + _, err = c.Write([]byte(fmt.Sprintf("RFB %s\n", version))) + if err != nil { + t.Fatal("failed writing version") + } + }() + + return ln.Addr().String() +} + +func TestClient_LowMajorVersion(t *testing.T) { + nc, err := net.Dial("tcp", newMockServer(t, "002.009")) + if err != nil { + t.Fatalf("error connecting to mock server: %s", err) + } + + _, err = Client(nc, &ClientConfig{}) + if err == nil { + t.Fatal("error expected") + } + + if err.Error() != "unsupported major version, less than 3: 2" { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestClient_LowMinorVersion(t *testing.T) { + nc, err := net.Dial("tcp", newMockServer(t, "003.007")) + if err != nil { + t.Fatalf("error connecting to mock server: %s", err) + } + + _, err = Client(nc, &ClientConfig{}) + if err == nil { + t.Fatal("error expected") + } + + if err.Error() != "unsupported minor version, less than 8: 7" { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestParseProtocolVersion(t *testing.T) { + tests := []struct { + proto []byte + major, minor uint + isErr bool + }{ + // Valid ProtocolVersion messages. + {[]byte{82, 70, 66, 32, 48, 48, 51, 46, 48, 48, 56, 10}, 3, 8, false}, // RFB 003.008\n + {[]byte{82, 70, 66, 32, 48, 48, 51, 46, 56, 56, 57, 10}, 3, 889, false}, // RFB 003.889\n -- OS X 10.10.3 + {[]byte{82, 70, 66, 32, 48, 48, 48, 46, 48, 48, 48, 10}, 0, 0, false}, // RFB 000.0000\n + // Invalid messages. + {[]byte{82, 70, 66, 32, 51, 46, 56, 10}, 0, 0, true}, // RFB 3.8\n -- too short; not zero padded + {[]byte{82, 70, 66, 10}, 0, 0, true}, // RFB\n -- too short + {[]byte{}, 0, 0, true}, // (empty) -- too short + } + + for _, tt := range tests { + major, minor, err := parseProtocolVersion(tt.proto) + if err != nil && !tt.isErr { + t.Fatalf("parseProtocolVersion(%v) unexpected error %v", tt.proto, err) + } + if err == nil && tt.isErr { + t.Fatalf("parseProtocolVersion(%v) expected error", tt.proto) + } + if major != tt.major { + t.Errorf("parseProtocolVersion(%v) major = %v, want %v", tt.proto, major, tt.major) + } + if major != tt.major { + t.Errorf("parseProtocolVersion(%v) minor = %v, want %v", tt.proto, minor, tt.minor) + } + } +} diff --git a/vnc/color.go b/vnc/color.go index 30df467..7e45b56 100644 --- a/vnc/color.go +++ b/vnc/color.go @@ -1,6 +1,6 @@ -package vnc - -// Color represents a single color in a color map. -type Color struct { - R, G, B uint16 -} +package vnc + +// Color represents a single color in a color map. +type Color struct { + R, G, B uint16 +} diff --git a/vnc/pixel_format.go b/vnc/pixel_format.go index e25af24..a9c8305 100644 --- a/vnc/pixel_format.go +++ b/vnc/pixel_format.go @@ -1,136 +1,136 @@ -package vnc - -import ( - "bytes" - "encoding/binary" - "io" - "vncproxy/common" -) - -func readPixelFormat(r io.Reader, result *common.PixelFormat) error { - var rawPixelFormat [16]byte - if _, err := io.ReadFull(r, rawPixelFormat[:]); err != nil { - return err - } - - var pfBoolByte uint8 - brPF := bytes.NewReader(rawPixelFormat[:]) - if err := binary.Read(brPF, binary.BigEndian, &result.BPP); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.Depth); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { - return err - } - - if pfBoolByte != 0 { - // Big endian is true - result.BigEndian = true - } - - if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { - return err - } - - if pfBoolByte != 0 { - // True Color is true. So we also have to read all the color max & shifts. - result.TrueColor = true - - if err := binary.Read(brPF, binary.BigEndian, &result.RedMax); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.GreenMax); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.BlueMax); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.RedShift); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.GreenShift); err != nil { - return err - } - - if err := binary.Read(brPF, binary.BigEndian, &result.BlueShift); err != nil { - return err - } - } - - return nil -} - -func writePixelFormat(format *common.PixelFormat) ([]byte, error) { - var buf bytes.Buffer - - // Byte 1 - if err := binary.Write(&buf, binary.BigEndian, format.BPP); err != nil { - return nil, err - } - - // Byte 2 - if err := binary.Write(&buf, binary.BigEndian, format.Depth); err != nil { - return nil, err - } - - var boolByte byte - if format.BigEndian { - boolByte = 1 - } else { - boolByte = 0 - } - - // Byte 3 (BigEndian) - if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { - return nil, err - } - - if format.TrueColor { - boolByte = 1 - } else { - boolByte = 0 - } - - // Byte 4 (TrueColor) - if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { - return nil, err - } - - // If we have true color enabled then we have to fill in the rest of the - // structure with the color values. - if format.TrueColor { - if err := binary.Write(&buf, binary.BigEndian, format.RedMax); err != nil { - return nil, err - } - - if err := binary.Write(&buf, binary.BigEndian, format.GreenMax); err != nil { - return nil, err - } - - if err := binary.Write(&buf, binary.BigEndian, format.BlueMax); err != nil { - return nil, err - } - - if err := binary.Write(&buf, binary.BigEndian, format.RedShift); err != nil { - return nil, err - } - - if err := binary.Write(&buf, binary.BigEndian, format.GreenShift); err != nil { - return nil, err - } - - if err := binary.Write(&buf, binary.BigEndian, format.BlueShift); err != nil { - return nil, err - } - } - - return buf.Bytes()[0:16], nil -} +package vnc + +import ( + "bytes" + "encoding/binary" + "io" + "vncproxy/common" +) + +func readPixelFormat(r io.Reader, result *common.PixelFormat) error { + var rawPixelFormat [16]byte + if _, err := io.ReadFull(r, rawPixelFormat[:]); err != nil { + return err + } + + var pfBoolByte uint8 + brPF := bytes.NewReader(rawPixelFormat[:]) + if err := binary.Read(brPF, binary.BigEndian, &result.BPP); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.Depth); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { + return err + } + + if pfBoolByte != 0 { + // Big endian is true + result.BigEndian = true + } + + if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { + return err + } + + if pfBoolByte != 0 { + // True Color is true. So we also have to read all the color max & shifts. + result.TrueColor = true + + if err := binary.Read(brPF, binary.BigEndian, &result.RedMax); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.GreenMax); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.BlueMax); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.RedShift); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.GreenShift); err != nil { + return err + } + + if err := binary.Read(brPF, binary.BigEndian, &result.BlueShift); err != nil { + return err + } + } + + return nil +} + +func writePixelFormat(format *common.PixelFormat) ([]byte, error) { + var buf bytes.Buffer + + // Byte 1 + if err := binary.Write(&buf, binary.BigEndian, format.BPP); err != nil { + return nil, err + } + + // Byte 2 + if err := binary.Write(&buf, binary.BigEndian, format.Depth); err != nil { + return nil, err + } + + var boolByte byte + if format.BigEndian { + boolByte = 1 + } else { + boolByte = 0 + } + + // Byte 3 (BigEndian) + if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { + return nil, err + } + + if format.TrueColor { + boolByte = 1 + } else { + boolByte = 0 + } + + // Byte 4 (TrueColor) + if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { + return nil, err + } + + // If we have true color enabled then we have to fill in the rest of the + // structure with the color values. + if format.TrueColor { + if err := binary.Write(&buf, binary.BigEndian, format.RedMax); err != nil { + return nil, err + } + + if err := binary.Write(&buf, binary.BigEndian, format.GreenMax); err != nil { + return nil, err + } + + if err := binary.Write(&buf, binary.BigEndian, format.BlueMax); err != nil { + return nil, err + } + + if err := binary.Write(&buf, binary.BigEndian, format.RedShift); err != nil { + return nil, err + } + + if err := binary.Write(&buf, binary.BigEndian, format.GreenShift); err != nil { + return nil, err + } + + if err := binary.Write(&buf, binary.BigEndian, format.BlueShift); err != nil { + return nil, err + } + } + + return buf.Bytes()[0:16], nil +} diff --git a/vnc/pointer.go b/vnc/pointer.go index 86d84e3..80f3f13 100644 --- a/vnc/pointer.go +++ b/vnc/pointer.go @@ -1,16 +1,16 @@ -package vnc - -// ButtonMask represents a mask of pointer presses/releases. -type ButtonMask uint8 - -// All available button mask components. -const ( - ButtonLeft ButtonMask = 1 << iota - ButtonMiddle - ButtonRight - Button4 - Button5 - Button6 - Button7 - Button8 -) +package vnc + +// ButtonMask represents a mask of pointer presses/releases. +type ButtonMask uint8 + +// All available button mask components. +const ( + ButtonLeft ButtonMask = 1 << iota + ButtonMiddle + ButtonRight + Button4 + Button5 + Button6 + Button7 + Button8 +) diff --git a/vnc/recorder.go b/vnc/recorder.go deleted file mode 100644 index 83b900d..0000000 --- a/vnc/recorder.go +++ /dev/null @@ -1,38 +0,0 @@ -package vnc - -import ( - "os" - "vncproxy/common" -) - -type Recorder struct { - RBSFileName string - fileHandle *os.File - logger common.Logger -} - -func NewRecorder(saveFilePath string, logger common.Logger) *Recorder { - rec := Recorder{RBSFileName: saveFilePath} - var err error - rec.fileHandle, err = os.OpenFile(saveFilePath, os.O_RDWR|os.O_CREATE, 0755) - if err != nil { - logger.Errorf("unable to open file: %s, error: %v", saveFilePath, err) - return nil - } - return &rec -} - -func (r *Recorder) Write(data []byte) error { - _, err := r.fileHandle.Write(data) - return err -} - -// func (r *Recorder) WriteUInt8(data uint8) error { -// buf := make([]byte, 1) -// buf[0] = byte(data) // cast int8 to byte -// return r.Write(buf) -// } - -func (r *Recorder) Close() { - r.fileHandle.Close() -} diff --git a/vnc/server_messages.go b/vnc/server_messages.go index fb1ad2e..aaeb167 100644 --- a/vnc/server_messages.go +++ b/vnc/server_messages.go @@ -27,7 +27,8 @@ func (*FramebufferUpdateMessage) Type() uint8 { return 0 } -func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) { +func (fbm *FramebufferUpdateMessage) Read(c *ClientConn, r *common.RfbReadHelper) (ServerMessage, error) { + // Read off the padding var padding [1]byte if _, err := io.ReadFull(r, padding[:]); err != nil { @@ -53,8 +54,9 @@ func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage rects := make([]common.Rectangle, numRects) for i := uint16(0); i < numRects; i++ { fmt.Printf("###############rect################: %d\n", i) - var encodingType int32 + var encodingType int32 + r.SendRectSeparator(-1) rect := &rects[i] data := []interface{}{ &rect.X, @@ -108,7 +110,7 @@ func (*SetColorMapEntriesMessage) Type() uint8 { return 1 } -func (*SetColorMapEntriesMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) { +func (*SetColorMapEntriesMessage) Read(c *ClientConn, r *common.RfbReadHelper) (ServerMessage, error) { // Read off the padding var padding [1]byte if _, err := io.ReadFull(r, padding[:]); err != nil { @@ -161,7 +163,7 @@ func (*BellMessage) Type() uint8 { return 2 } -func (*BellMessage) Read(*ClientConn, io.Reader) (ServerMessage, error) { +func (*BellMessage) Read(*ClientConn, *common.RfbReadHelper) (ServerMessage, error) { return new(BellMessage), nil } @@ -180,22 +182,31 @@ func (*ServerCutTextMessage) Type() uint8 { return 3 } -func (*ServerCutTextMessage) Read(conn *ClientConn, r io.Reader) (ServerMessage, error) { +func (*ServerCutTextMessage) Read(conn *ClientConn, r *common.RfbReadHelper) (ServerMessage, error) { + //reader := common.RfbReadHelper{Reader: r} + // Read off the padding - var padding [1]byte + var padding [3]byte if _, err := io.ReadFull(r, padding[:]); err != nil { return nil, err } - - var textLength uint32 - if err := binary.Read(r, binary.BigEndian, &textLength); err != nil { + textLength, err := r.ReadUint32() + if err != nil { return nil, err } - - textBytes := make([]uint8, textLength) - if err := binary.Read(r, binary.BigEndian, &textBytes); err != nil { + textBytes, err := r.ReadBytes(int(textLength)) + if err != nil { return nil, err } + //var textLength uint32 + // if err := binary.Read(r, binary.BigEndian, &textLength); err != nil { + // return nil, err + // } + + // textBytes := make([]uint8, textLength) + // if err := binary.Read(r, binary.BigEndian, &textBytes); err != nil { + // return nil, err + // } return &ServerCutTextMessage{string(textBytes)}, nil }