diff --git a/Makefile b/Makefile index db3181f..c02a21b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ vnc_rec: go build -o ./bin/vnc_recorder ./vnc_rec/cmd/ run_rec: - ./bin/vnc_recorder -recDir=./bin/recordings/ -targHost=localhost -targPort=5901 -targPass=boxware -tcpPort=5902 -vncPass=boxware + ./bin/vnc_recorder -recDir=./bin/recordings/ -logLevel=debug -targHost=localhost -targPort=5901 -targPass=boxware -tcpPort=5902 -vncPass=boxware clear: rm -rf bin/recordings/* \ No newline at end of file diff --git a/bin/recordings/recording_1562907518/client.rbs b/bin/recordings/recording_1562907518/client.rbs new file mode 100644 index 0000000..ab8851f Binary files /dev/null and b/bin/recordings/recording_1562907518/client.rbs differ diff --git a/bin/recordings/recording_1562907518/proto.rbs b/bin/recordings/recording_1562907518/proto.rbs new file mode 100644 index 0000000..1eaa89d Binary files /dev/null and b/bin/recordings/recording_1562907518/proto.rbs differ diff --git a/bin/recordings/recording_1562907518/server.rbs b/bin/recordings/recording_1562907518/server.rbs new file mode 100644 index 0000000..9eebc63 Binary files /dev/null and b/bin/recordings/recording_1562907518/server.rbs differ diff --git a/bin/vnc_reader b/bin/vnc_reader new file mode 100755 index 0000000..509adec Binary files /dev/null and b/bin/vnc_reader differ diff --git a/bin/vnc_recorder b/bin/vnc_recorder index b0cc113..ad1e6dc 100755 Binary files a/bin/vnc_recorder and b/bin/vnc_recorder differ diff --git a/player/fbs-reader.go b/player/fbs-reader.go index 27ec746..ee6aef6 100644 --- a/player/fbs-reader.go +++ b/player/fbs-reader.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "io" "os" + "github.com/amitbet/vncproxy/common" "github.com/amitbet/vncproxy/encodings" "github.com/amitbet/vncproxy/logger" @@ -184,6 +185,40 @@ func (fbs *FbsReader) ReadSegment() (*FbsSegment, error) { return seg, nil } +func (fbs *FbsReader) ReadSegmentTest() (*FbsSegment, uint32, uint32, []byte, uint32, error) { + reader := fbs.reader + var bytesLen uint32 + + //read length + err := binary.Read(reader, binary.BigEndian, &bytesLen) + if err != nil { + logger.Error("FbsReader.ReadStartSession: read len, error reading rbs file: ", err) + } + + paddedSize := (bytesLen + 3) & 0x7FFFFFFC + + //read bytes + bytes := make([]byte, paddedSize) + _, err = reader.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadSegment: read bytes, error reading rbs file: ", err) + } + + //remove padding + actualBytes := bytes[:bytesLen] + + //read timestamp + var timeSinceStart uint32 + binary.Read(reader, binary.BigEndian, &timeSinceStart) + if err != nil { + logger.Error("FbsReader.ReadSegment: read timestamp, error reading rbs file: ", err) + } + + //timeStamp := time.Unix(timeSinceStart, 0) + seg := &FbsSegment{bytes: actualBytes, timestamp: timeSinceStart} + return seg, bytesLen, paddedSize, bytes, timeSinceStart, nil +} + type FbsSegment struct { bytes []byte timestamp uint32 diff --git a/proto/demo.pb.go b/proto/demo.pb.go new file mode 100644 index 0000000..e9cc6ed --- /dev/null +++ b/proto/demo.pb.go @@ -0,0 +1,376 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: demo.proto + +package proto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type InitMsg struct { + RfbHeader string `protobuf:"bytes,1,opt,name=RfbHeader,json=rfbHeader,proto3" json:"RfbHeader,omitempty"` + RfbVersion string `protobuf:"bytes,2,opt,name=RfbVersion,json=rfbVersion,proto3" json:"RfbVersion,omitempty"` + FBHeight uint32 `protobuf:"varint,3,opt,name=FBHeight,json=fBHeight,proto3" json:"FBHeight,omitempty"` + FBWidth uint32 `protobuf:"varint,4,opt,name=FBWidth,json=fBWidth,proto3" json:"FBWidth,omitempty"` + SecType uint32 `protobuf:"varint,5,opt,name=SecType,json=secType,proto3" json:"SecType,omitempty"` + StartTime uint32 `protobuf:"varint,6,opt,name=StartTime,json=startTime,proto3" json:"StartTime,omitempty"` + DesktopName string `protobuf:"bytes,7,opt,name=DesktopName,json=desktopName,proto3" json:"DesktopName,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InitMsg) Reset() { *m = InitMsg{} } +func (m *InitMsg) String() string { return proto.CompactTextString(m) } +func (*InitMsg) ProtoMessage() {} +func (*InitMsg) Descriptor() ([]byte, []int) { + return fileDescriptor_ca53982754088a9d, []int{0} +} + +func (m *InitMsg) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InitMsg.Unmarshal(m, b) +} +func (m *InitMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InitMsg.Marshal(b, m, deterministic) +} +func (m *InitMsg) XXX_Merge(src proto.Message) { + xxx_messageInfo_InitMsg.Merge(m, src) +} +func (m *InitMsg) XXX_Size() int { + return xxx_messageInfo_InitMsg.Size(m) +} +func (m *InitMsg) XXX_DiscardUnknown() { + xxx_messageInfo_InitMsg.DiscardUnknown(m) +} + +var xxx_messageInfo_InitMsg proto.InternalMessageInfo + +func (m *InitMsg) GetRfbHeader() string { + if m != nil { + return m.RfbHeader + } + return "" +} + +func (m *InitMsg) GetRfbVersion() string { + if m != nil { + return m.RfbVersion + } + return "" +} + +func (m *InitMsg) GetFBHeight() uint32 { + if m != nil { + return m.FBHeight + } + return 0 +} + +func (m *InitMsg) GetFBWidth() uint32 { + if m != nil { + return m.FBWidth + } + return 0 +} + +func (m *InitMsg) GetSecType() uint32 { + if m != nil { + return m.SecType + } + return 0 +} + +func (m *InitMsg) GetStartTime() uint32 { + if m != nil { + return m.StartTime + } + return 0 +} + +func (m *InitMsg) GetDesktopName() string { + if m != nil { + return m.DesktopName + } + return "" +} + +type PointerEvent struct { + Mask uint32 `protobuf:"varint,1,opt,name=Mask,json=mask,proto3" json:"Mask,omitempty"` + X uint32 `protobuf:"varint,2,opt,name=X,json=x,proto3" json:"X,omitempty"` + Y uint32 `protobuf:"varint,3,opt,name=Y,json=y,proto3" json:"Y,omitempty"` + Timestamp uint32 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PointerEvent) Reset() { *m = PointerEvent{} } +func (m *PointerEvent) String() string { return proto.CompactTextString(m) } +func (*PointerEvent) ProtoMessage() {} +func (*PointerEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_ca53982754088a9d, []int{1} +} + +func (m *PointerEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PointerEvent.Unmarshal(m, b) +} +func (m *PointerEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PointerEvent.Marshal(b, m, deterministic) +} +func (m *PointerEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_PointerEvent.Merge(m, src) +} +func (m *PointerEvent) XXX_Size() int { + return xxx_messageInfo_PointerEvent.Size(m) +} +func (m *PointerEvent) XXX_DiscardUnknown() { + xxx_messageInfo_PointerEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_PointerEvent proto.InternalMessageInfo + +func (m *PointerEvent) GetMask() uint32 { + if m != nil { + return m.Mask + } + return 0 +} + +func (m *PointerEvent) GetX() uint32 { + if m != nil { + return m.X + } + return 0 +} + +func (m *PointerEvent) GetY() uint32 { + if m != nil { + return m.Y + } + return 0 +} + +func (m *PointerEvent) GetTimestamp() uint32 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type KeyEvent struct { + Down uint32 `protobuf:"varint,1,opt,name=Down,json=down,proto3" json:"Down,omitempty"` + Key uint32 `protobuf:"varint,2,opt,name=Key,json=key,proto3" json:"Key,omitempty"` + Timestamp uint32 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyEvent) Reset() { *m = KeyEvent{} } +func (m *KeyEvent) String() string { return proto.CompactTextString(m) } +func (*KeyEvent) ProtoMessage() {} +func (*KeyEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_ca53982754088a9d, []int{2} +} + +func (m *KeyEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_KeyEvent.Unmarshal(m, b) +} +func (m *KeyEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_KeyEvent.Marshal(b, m, deterministic) +} +func (m *KeyEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyEvent.Merge(m, src) +} +func (m *KeyEvent) XXX_Size() int { + return xxx_messageInfo_KeyEvent.Size(m) +} +func (m *KeyEvent) XXX_DiscardUnknown() { + xxx_messageInfo_KeyEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyEvent proto.InternalMessageInfo + +func (m *KeyEvent) GetDown() uint32 { + if m != nil { + return m.Down + } + return 0 +} + +func (m *KeyEvent) GetKey() uint32 { + if m != nil { + return m.Key + } + return 0 +} + +func (m *KeyEvent) GetTimestamp() uint32 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type FbsSegment struct { + Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"` + Timestamp uint32 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FbsSegment) Reset() { *m = FbsSegment{} } +func (m *FbsSegment) String() string { return proto.CompactTextString(m) } +func (*FbsSegment) ProtoMessage() {} +func (*FbsSegment) Descriptor() ([]byte, []int) { + return fileDescriptor_ca53982754088a9d, []int{3} +} + +func (m *FbsSegment) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FbsSegment.Unmarshal(m, b) +} +func (m *FbsSegment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FbsSegment.Marshal(b, m, deterministic) +} +func (m *FbsSegment) XXX_Merge(src proto.Message) { + xxx_messageInfo_FbsSegment.Merge(m, src) +} +func (m *FbsSegment) XXX_Size() int { + return xxx_messageInfo_FbsSegment.Size(m) +} +func (m *FbsSegment) XXX_DiscardUnknown() { + xxx_messageInfo_FbsSegment.DiscardUnknown(m) +} + +var xxx_messageInfo_FbsSegment proto.InternalMessageInfo + +func (m *FbsSegment) GetBytes() []byte { + if m != nil { + return m.Bytes + } + return nil +} + +func (m *FbsSegment) GetTimestamp() uint32 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type Demonstration struct { + Initmsg *InitMsg `protobuf:"bytes,1,opt,name=initmsg,proto3" json:"initmsg,omitempty"` + Segments []*FbsSegment `protobuf:"bytes,2,rep,name=segments,proto3" json:"segments,omitempty"` + Pointerevents []*PointerEvent `protobuf:"bytes,3,rep,name=pointerevents,proto3" json:"pointerevents,omitempty"` + Keyevents []*KeyEvent `protobuf:"bytes,4,rep,name=keyevents,proto3" json:"keyevents,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Demonstration) Reset() { *m = Demonstration{} } +func (m *Demonstration) String() string { return proto.CompactTextString(m) } +func (*Demonstration) ProtoMessage() {} +func (*Demonstration) Descriptor() ([]byte, []int) { + return fileDescriptor_ca53982754088a9d, []int{4} +} + +func (m *Demonstration) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Demonstration.Unmarshal(m, b) +} +func (m *Demonstration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Demonstration.Marshal(b, m, deterministic) +} +func (m *Demonstration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Demonstration.Merge(m, src) +} +func (m *Demonstration) XXX_Size() int { + return xxx_messageInfo_Demonstration.Size(m) +} +func (m *Demonstration) XXX_DiscardUnknown() { + xxx_messageInfo_Demonstration.DiscardUnknown(m) +} + +var xxx_messageInfo_Demonstration proto.InternalMessageInfo + +func (m *Demonstration) GetInitmsg() *InitMsg { + if m != nil { + return m.Initmsg + } + return nil +} + +func (m *Demonstration) GetSegments() []*FbsSegment { + if m != nil { + return m.Segments + } + return nil +} + +func (m *Demonstration) GetPointerevents() []*PointerEvent { + if m != nil { + return m.Pointerevents + } + return nil +} + +func (m *Demonstration) GetKeyevents() []*KeyEvent { + if m != nil { + return m.Keyevents + } + return nil +} + +func init() { + proto.RegisterType((*InitMsg)(nil), "proto.InitMsg") + proto.RegisterType((*PointerEvent)(nil), "proto.PointerEvent") + proto.RegisterType((*KeyEvent)(nil), "proto.KeyEvent") + proto.RegisterType((*FbsSegment)(nil), "proto.FbsSegment") + proto.RegisterType((*Demonstration)(nil), "proto.Demonstration") +} + +func init() { proto.RegisterFile("demo.proto", fileDescriptor_ca53982754088a9d) } + +var fileDescriptor_ca53982754088a9d = []byte{ + // 408 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x91, 0x4f, 0x8b, 0xdb, 0x30, + 0x10, 0xc5, 0x51, 0x9c, 0xac, 0xe3, 0x49, 0xdc, 0x3f, 0x6a, 0x0f, 0xa2, 0x94, 0x12, 0x72, 0xca, + 0x65, 0xf7, 0xb0, 0x3d, 0xf5, 0x56, 0x96, 0x34, 0x6c, 0x59, 0x76, 0x29, 0xca, 0xd2, 0x3f, 0xd0, + 0x8b, 0x5d, 0x8f, 0xbd, 0xc2, 0x48, 0x32, 0x92, 0xd8, 0xad, 0xbf, 0x66, 0x2f, 0xfd, 0x3a, 0xc5, + 0xb2, 0x1c, 0x37, 0x39, 0x99, 0x99, 0x37, 0xef, 0xe7, 0xa7, 0x19, 0x80, 0x02, 0xa5, 0xbe, 0x68, + 0x8c, 0x76, 0x9a, 0xce, 0xfc, 0x67, 0xfd, 0x97, 0x40, 0xfc, 0x59, 0x09, 0x77, 0x6b, 0x2b, 0xfa, + 0x16, 0x12, 0x5e, 0xe6, 0xd7, 0x98, 0x15, 0x68, 0x18, 0x59, 0x91, 0x4d, 0xc2, 0x13, 0x33, 0x34, + 0xe8, 0x3b, 0x00, 0x5e, 0xe6, 0x5f, 0xd1, 0x58, 0xa1, 0x15, 0x9b, 0x78, 0x19, 0xcc, 0xa1, 0x43, + 0xdf, 0xc0, 0x7c, 0x77, 0x75, 0x8d, 0xa2, 0x7a, 0x70, 0x2c, 0x5a, 0x91, 0x4d, 0xca, 0xe7, 0x65, + 0xa8, 0x29, 0x83, 0x78, 0x77, 0xf5, 0x4d, 0x14, 0xee, 0x81, 0x4d, 0xbd, 0x14, 0x97, 0x7d, 0xd9, + 0x29, 0x7b, 0xfc, 0x75, 0xdf, 0x36, 0xc8, 0x66, 0xbd, 0x62, 0xfb, 0xb2, 0x4b, 0xb3, 0x77, 0x99, + 0x71, 0xf7, 0x42, 0x22, 0x3b, 0xf3, 0x5a, 0x62, 0x87, 0x06, 0x5d, 0xc1, 0x62, 0x8b, 0xb6, 0x76, + 0xba, 0xb9, 0xcb, 0x24, 0xb2, 0xd8, 0xc7, 0x59, 0x14, 0x63, 0x6b, 0xfd, 0x13, 0x96, 0x5f, 0xb4, + 0x50, 0x0e, 0xcd, 0xa7, 0x47, 0x54, 0x8e, 0x52, 0x98, 0xde, 0x66, 0xb6, 0xf6, 0x0f, 0x4b, 0xf9, + 0x54, 0x66, 0xb6, 0xa6, 0x4b, 0x20, 0xdf, 0xfd, 0x53, 0x52, 0x4e, 0x7e, 0x77, 0xd5, 0x8f, 0x10, + 0x9d, 0xb4, 0xdd, 0xff, 0x9d, 0x90, 0x68, 0x5d, 0x26, 0x9b, 0x90, 0x7a, 0x6c, 0xac, 0xef, 0x60, + 0x7e, 0x83, 0xed, 0x81, 0xbc, 0xd5, 0x4f, 0x6a, 0x20, 0x17, 0xfa, 0x49, 0xd1, 0x17, 0x10, 0xdd, + 0x60, 0x1b, 0xd8, 0x51, 0x8d, 0x27, 0xbc, 0xe8, 0x94, 0xf7, 0x11, 0x60, 0x97, 0xdb, 0x3d, 0x56, + 0xb2, 0x23, 0xbe, 0x86, 0x59, 0xde, 0x3a, 0xb4, 0x1e, 0xb9, 0xe4, 0x7d, 0x71, 0x4c, 0x98, 0x9c, + 0x12, 0xfe, 0x10, 0x48, 0xb7, 0x28, 0xb5, 0xb2, 0xce, 0x64, 0xae, 0xbb, 0xc8, 0x06, 0x62, 0xa1, + 0x84, 0x93, 0xb6, 0xf2, 0x9c, 0xc5, 0xe5, 0xb3, 0xfe, 0xf6, 0x17, 0xe1, 0xe0, 0x7c, 0x90, 0xe9, + 0x39, 0xcc, 0x6d, 0xff, 0x6b, 0xcb, 0x26, 0xab, 0x68, 0xb3, 0xb8, 0x7c, 0x19, 0x46, 0xc7, 0x50, + 0xfc, 0x30, 0x42, 0x3f, 0x40, 0xda, 0xf4, 0xab, 0xc5, 0x47, 0xef, 0x89, 0xbc, 0xe7, 0x55, 0xf0, + 0xfc, 0xbf, 0x76, 0x7e, 0x3c, 0x49, 0xcf, 0x21, 0xa9, 0xb1, 0x0d, 0xb6, 0xa9, 0xb7, 0x3d, 0x0f, + 0xb6, 0x61, 0x9f, 0x7c, 0x9c, 0xc8, 0xcf, 0xbc, 0xf4, 0xfe, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xaa, 0x99, 0xea, 0x8d, 0xba, 0x02, 0x00, 0x00, +} diff --git a/proto/demo.proto b/proto/demo.proto new file mode 100644 index 0000000..76be2f6 --- /dev/null +++ b/proto/demo.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package proto; + +message InitMsg { + string RfbHeader = 1; + string RfbVersion = 2; + uint32 FBHeight = 3; + uint32 FBWidth = 4; + uint32 SecType = 5; + uint32 StartTime = 6; + string DesktopName = 7; +} + +message PointerEvent { + uint32 Mask = 1; + uint32 X = 2; + uint32 Y = 3; + uint32 timestamp = 4; + +} + +message KeyEvent { + uint32 Down = 1; + uint32 Key = 2; + uint32 timestamp = 3; +} + +message FbsSegment{ + bytes bytes = 1; + uint32 timestamp = 2; +} + +message Demonstration { + InitMsg initmsg = 1; + repeated FbsSegment segments = 2; + repeated PointerEvent pointerevents = 3; + repeated KeyEvent keyevents = 4; +} \ No newline at end of file diff --git a/vnc_read/main.go b/vnc_read/main.go new file mode 100644 index 0000000..baf75bd --- /dev/null +++ b/vnc_read/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + + "github.com/golang/protobuf/proto" + pb "github.com/sibeshkar/vncproxy/proto" +) + +func main() { + + if len(os.Args) != 2 { + log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0]) + } + fname := os.Args[1] + + // [START unmarshal_proto] + // Read the existing address book. + in, err := ioutil.ReadFile(fname) + if err != nil { + log.Fatalln("Error reading file:", err) + } + demonstration := &pb.Demonstration{} + if err := proto.Unmarshal(in, demonstration); err != nil { + log.Fatalln("Failed to parse demonstration file:", err) + } + + listPeople(os.Stdout, demonstration) + +} + +func listPeople(w io.Writer, demo *pb.Demonstration) { + fmt.Printf("FBHeight: %v \n", demo.Initmsg.GetFBHeight()) + fmt.Printf("FBWidth: %v \n", demo.Initmsg.GetFBWidth()) + fmt.Printf("RfbHeader: %v \n", demo.Initmsg.GetRfbHeader()) + fmt.Printf("RfbVersion: %v \n", demo.Initmsg.GetRfbVersion()) + fmt.Printf("SecType: %v \n", demo.Initmsg.GetSecType()) + fmt.Printf("StartTime: %v \n", demo.Initmsg.GetStartTime()) + fmt.Printf("DesktopName: %v \n", demo.Initmsg.GetDesktopName()) + + // for _, p := range demo.Segments { + // writePerson(w, p) + // } +} + +func writePerson(w io.Writer, p *pb.FbsSegment) { + fmt.Printf("Length: %v Timestamp: %v \n", len(p.GetBytes()), p.GetTimestamp()) + +} diff --git a/vnc_rec/client-recorder.go b/vnc_rec/client-recorder.go index 22795db..09cae4f 100644 --- a/vnc_rec/client-recorder.go +++ b/vnc_rec/client-recorder.go @@ -130,6 +130,7 @@ func (r *ClientRecorder) HandleRfbSegment(data *common.RfbSegment) error { logger.Warn("ClientRecorder.HandleRfbSegment: unknown message type:" + string(data.UpcomingObjectType)) } case common.SegmentConnectionClosed: + logger.Debugf("Segment Connection closed") r.writeToDisk() case common.SegmentRectSeparator: logger.Debugf("ClientRecorder.HandleRfbSegment: not writing rect") @@ -192,6 +193,8 @@ func (r *ClientRecorder) writeToDisk() error { //write timestamp binary.Write(r.writer, binary.BigEndian, uint32(timeSinceStart)) r.buffer.Reset() + + logger.Debugf("writeToDisk() Triggered Now") return err } diff --git a/vnc_rec/proto-recorder.go b/vnc_rec/proto-recorder.go new file mode 100644 index 0000000..16a74d1 --- /dev/null +++ b/vnc_rec/proto-recorder.go @@ -0,0 +1,261 @@ +package vnc_rec + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/amitbet/vncproxy/common" + "github.com/amitbet/vncproxy/logger" + "github.com/amitbet/vncproxy/server" + "github.com/golang/protobuf/proto" + pb "github.com/sibeshkar/vncproxy/proto" +) + +type ProtoRecorder struct { + //common.BytesListener + RBSFileName string + writer *os.File + demonstration *pb.Demonstration + //logger common.Logger + startTime int + buffer bytes.Buffer + serverInitMessage *common.ServerInit + sessionStartWritten bool + segmentChan chan *common.RfbSegment + maxWriteSize int +} + +func NewProtoRecorder(saveFilePath string) (*ProtoRecorder, error) { + //delete file if it exists + if _, err := os.Stat(saveFilePath); err == nil { + os.Remove(saveFilePath) + } + + rec := ProtoRecorder{RBSFileName: saveFilePath, startTime: getNowMillisec()} + var err error + + rec.maxWriteSize = 65535 + + in, err := ioutil.ReadFile(saveFilePath) + if err != nil { + if os.IsNotExist(err) { + fmt.Printf("%s: File not found. Creating new file.\n", saveFilePath) + } else { + log.Fatalln("Error reading file:", err) + } + } + + rec.demonstration = &pb.Demonstration{} + + if err := proto.Unmarshal(in, rec.demonstration); err != nil { + log.Fatalln("Failed to parse demonstration file:", err) + } + + // rec.writer, err = os.OpenFile(saveFilePath, os.O_RDWR|os.O_CREATE, 0644) + // if err != nil { + // logger.Errorf("unable to open file: %s, error: %v", saveFilePath, err) + // return nil, err + // } + + //buffer the channel so we don't halt the proxying flow for slow writes when under pressure + rec.segmentChan = make(chan *common.RfbSegment, 100) + go func() { + for { + data := <-rec.segmentChan + rec.HandleRfbSegment(data) + } + }() + + return &rec, nil +} + +func (r *ProtoRecorder) writeStartSession(initMsg *common.ServerInit) error { + r.sessionStartWritten = true + desktopName := string(initMsg.NameText) + framebufferWidth := initMsg.FBWidth + framebufferHeight := initMsg.FBHeight + // //write rfb header information (the only part done without the [size|data|timestamp] block wrapper) + // r.writer.WriteString("FBS 001.000\n") + // r.demonstration.Initmsg. + + // //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)) + + // buff := bytes.Buffer{} + // //binary.Write(&buff, binary.BigEndian, initMsg.FBWidth) + // //binary.Write(&buff, binary.BigEndian, initMsg.FBHeight) + // binary.Write(&buff, binary.BigEndian, initMsg.PixelFormat) + // buff.Write([]byte{0, 0, 0}) //padding + // r.buffer.Write(buff.Bytes()) + // //logger.Debugf(">>>>>>buffer for initMessage:%v ", buff.Bytes()) + + // //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))) + + // r.buffer.WriteString(desktopName) + + initMsgProto := &pb.InitMsg{ + RfbHeader: "FBS 001.000", + RfbVersion: versionMsg_3_3, + FBHeight: uint32(framebufferHeight), + FBWidth: uint32(framebufferWidth), + SecType: uint32(SecTypeNone), + StartTime: uint32(r.startTime), + DesktopName: desktopName, + } + r.demonstration.Initmsg = initMsgProto + //binary.Write(&r.buffer, binary.BigEndian, byte(0)) // add null termination for desktop string + + return nil +} + +func (r *ProtoRecorder) Consume(data *common.RfbSegment) error { + //using async writes so if chan buffer overflows, proxy will not be affected + select { + case r.segmentChan <- data: + // default: + // logger.Error("error: ProtoRecorder queue is full") + } + + return nil +} + +func (r *ProtoRecorder) HandleRfbSegment(data *common.RfbSegment) error { + defer func() { + if r := recover(); r != nil { + logger.Error("Recovered in HandleRfbSegment: ", r) + } + }() + + timeSinceStart := uint32(getNowMillisec() - r.startTime) + + switch data.SegmentType { + case common.SegmentMessageStart: + if !r.sessionStartWritten { + logger.Debugf("ProtoRecorder.HandleRfbSegment: writing start session segment: %v", r.serverInitMessage) + r.writeStartSession(r.serverInitMessage) + } + + switch common.ServerMessageType(data.UpcomingObjectType) { + case common.FramebufferUpdate: + logger.Debugf("ProtoRecorder.HandleRfbSegment: saving FramebufferUpdate segment") + //r.writeToDisk() + case common.SetColourMapEntries: + case common.Bell: + case common.ServerCutText: + default: + logger.Warn("ProtoRecorder.HandleRfbSegment: unknown message type:" + string(data.UpcomingObjectType)) + } + case common.SegmentConnectionClosed: + r.writeToDisk() + case common.SegmentRectSeparator: + logger.Debugf("ProtoRecorder.HandleRfbSegment: writing rect") + //r.writeToDisk() + case common.SegmentBytes: + logger.Debug("ProtoRecorder.HandleRfbSegment: writing bytes, len:", len(data.Bytes)) + // if r.buffer.Len()+len(data.Bytes) > r.maxWriteSize-4 { + // r.writeToDisk() + // } + segment := &pb.FbsSegment{ + Bytes: data.Bytes, + Timestamp: timeSinceStart, + } + + r.demonstration.Segments = append(r.demonstration.Segments, segment) + //_, err := r.buffer.Write(data.Bytes) + //return err + case common.SegmentServerInitMessage: + r.serverInitMessage = data.Message.(*common.ServerInit) + case common.SegmentFullyParsedClientMessage: + clientMsg := data.Message.(common.ClientMessage) + + switch clientMsg.Type() { + case common.SetPixelFormatMsgType: + clientMsg := data.Message.(*server.MsgSetPixelFormat) + logger.Debugf("ClientRecorder.HandleRfbSegment: client message %v", *clientMsg) + r.serverInitMessage.PixelFormat = clientMsg.PF + case common.KeyEventMsgType: + clientMsg := data.Message.(*server.MsgKeyEvent) + logger.Debug("Recorder.HandleRfbSegment: writing bytes for KeyEventMsgType, len:", *clientMsg) + //clientMsg.Write(r.writer) + keyevent := &pb.KeyEvent{ + Down: uint32(clientMsg.Down), + Key: uint32(clientMsg.Key), + Timestamp: timeSinceStart, + } + r.demonstration.Keyevents = append(r.demonstration.Keyevents, keyevent) + case common.PointerEventMsgType: + clientMsg := data.Message.(*server.MsgPointerEvent) + logger.Debug("Recorder.HandleRfbSegment: writing bytes for PointerEventMsgType, len:", *clientMsg) + //clientMsg.Write(r.writer) + pointerevent := &pb.PointerEvent{ + Mask: uint32(clientMsg.Mask), + X: uint32(clientMsg.X), + Y: uint32(clientMsg.Y), + Timestamp: timeSinceStart, + } + r.demonstration.Pointerevents = append(r.demonstration.Pointerevents, pointerevent) + + default: + //return errors.New("unknown client message type:" + string(data.UpcomingObjectType)) + } + + default: + //return errors.New("undefined RfbSegment type") + } + return nil +} + +func (r *ProtoRecorder) writeToDisk() error { + + out, err := proto.Marshal(r.demonstration) + if err != nil { + log.Fatalln("Failed to encode address book:", err) + } + if err := ioutil.WriteFile(r.RBSFileName, out, 0644); err != nil { + log.Fatalln("Failed to write address book:", err) + } + // 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 + + // //logger.Debugf("paddedSize=%d paddingSize=%d bytesLen=%d", paddedSize, paddingSize, bytesLen) + // //write buffer padded to 32bit + // _, err := r.buffer.WriteTo(r.writer) + // padding := make([]byte, paddingSize) + // //logger.Debugf("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 *ProtoRecorder) WriteUInt8(data uint8) error { +// buf := make([]byte, 1) +// buf[0] = byte(data) // cast int8 to byte +// return r.Write(buf) +// } + +func (r *ProtoRecorder) Close() { + r.writer.Close() +} diff --git a/vnc_rec/proxy.go b/vnc_rec/proxy.go index 6a6058d..4ad672b 100644 --- a/vnc_rec/proxy.go +++ b/vnc_rec/proxy.go @@ -81,6 +81,7 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server var rec_s *ServerRecorder var rec_c *ClientRecorder + var rec_p *ProtoRecorder if session.Type == SessionTypeRecordingProxy { timeCurrent := strconv.FormatInt(time.Now().Unix(), 10) @@ -105,6 +106,16 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server } sconn.Listeners.AddListener(rec_c) + + recProtoFile := "proto.rbs" + recProtoPath := path.Join(recFolder, recProtoFile) + rec_p, err = NewProtoRecorder(recProtoPath) + if err != nil { + logger.Errorf("Proxy.newServerConnHandler can't open ProtoRecorder save path: %s", recProtoPath) + return err + } + + sconn.Listeners.AddListener(rec_p) } session.Status = SessionStatusInit @@ -123,6 +134,7 @@ func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server if session.Type == SessionTypeRecordingProxy { cconn.Listeners.AddListener(rec_s) cconn.Listeners.AddListener(rec_c) + cconn.Listeners.AddListener(rec_p) } //creating cross-listeners between server and client parts to pass messages through the proxy: