├── LICENSE ├── README.md ├── client.go ├── client_auth.go ├── client_auth_test.go ├── client_test.go ├── color.go ├── encoding.go ├── pixel_format.go ├── pointer.go └── server_messages.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mitchell Hashimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VNC Library for Go 2 | 3 | go-vnc is a VNC library for Go, initially supporting VNC clients but 4 | with the goal of eventually implementing a VNC server. 5 | 6 | This library implements [RFC 6143](http://tools.ietf.org/html/rfc6143). 7 | 8 | ## Usage & Installation 9 | 10 | The library is installable via standard `go get`. The package name is `vnc`. 11 | 12 | ``` 13 | $ go get github.com/mitchellh/go-vnc 14 | ``` 15 | 16 | Documentation is available on GoDoc: http://godoc.org/github.com/mitchellh/go-vnc 17 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // Package vnc implements a VNC client. 2 | // 3 | // References: 4 | // [PROTOCOL]: http://tools.ietf.org/html/rfc6143 5 | package vnc 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | "net" 13 | "unicode" 14 | ) 15 | 16 | type ClientConn struct { 17 | c net.Conn 18 | config *ClientConfig 19 | 20 | // If the pixel format uses a color map, then this is the color 21 | // map that is used. This should not be modified directly, since 22 | // the data comes from the server. 23 | ColorMap [256]Color 24 | 25 | // Encodings supported by the client. This should not be modified 26 | // directly. Instead, SetEncodings should be used. 27 | Encs []Encoding 28 | 29 | // Width of the frame buffer in pixels, sent from the server. 30 | FrameBufferWidth uint16 31 | 32 | // Height of the frame buffer in pixels, sent from the server. 33 | FrameBufferHeight uint16 34 | 35 | // Name associated with the desktop, sent from the server. 36 | DesktopName string 37 | 38 | // The pixel format associated with the connection. This shouldn't 39 | // be modified. If you wish to set a new pixel format, use the 40 | // SetPixelFormat method. 41 | PixelFormat PixelFormat 42 | } 43 | 44 | // A ClientConfig structure is used to configure a ClientConn. After 45 | // one has been passed to initialize a connection, it must not be modified. 46 | type ClientConfig struct { 47 | // A slice of ClientAuth methods. Only the first instance that is 48 | // suitable by the server will be used to authenticate. 49 | Auth []ClientAuth 50 | 51 | // Exclusive determines whether the connection is shared with other 52 | // clients. If true, then all other clients connected will be 53 | // disconnected when a connection is established to the VNC server. 54 | Exclusive bool 55 | 56 | // The channel that all messages received from the server will be 57 | // sent on. If the channel blocks, then the goroutine reading data 58 | // from the VNC server may block indefinitely. It is up to the user 59 | // of the library to ensure that this channel is properly read. 60 | // If this is not set, then all messages will be discarded. 61 | ServerMessageCh chan<- ServerMessage 62 | 63 | // A slice of supported messages that can be read from the server. 64 | // This only needs to contain NEW server messages, and doesn't 65 | // need to explicitly contain the RFC-required messages. 66 | ServerMessages []ServerMessage 67 | } 68 | 69 | func Client(c net.Conn, cfg *ClientConfig) (*ClientConn, error) { 70 | conn := &ClientConn{ 71 | c: c, 72 | config: cfg, 73 | } 74 | 75 | if err := conn.handshake(); err != nil { 76 | conn.Close() 77 | return nil, err 78 | } 79 | 80 | go conn.mainLoop() 81 | 82 | return conn, nil 83 | } 84 | 85 | func (c *ClientConn) Close() error { 86 | return c.c.Close() 87 | } 88 | 89 | // CutText tells the server that the client has new text in its cut buffer. 90 | // The text string MUST only contain Latin-1 characters. This encoding 91 | // is compatible with Go's native string format, but can only use up to 92 | // unicode.MaxLatin values. 93 | // 94 | // See RFC 6143 Section 7.5.6 95 | func (c *ClientConn) CutText(text string) error { 96 | var buf bytes.Buffer 97 | 98 | // This is the fixed size data we'll send 99 | fixedData := []interface{}{ 100 | uint8(6), 101 | uint8(0), 102 | uint8(0), 103 | uint8(0), 104 | uint32(len(text)), 105 | } 106 | 107 | for _, val := range fixedData { 108 | if err := binary.Write(&buf, binary.BigEndian, val); err != nil { 109 | return err 110 | } 111 | } 112 | 113 | for _, char := range text { 114 | if char > unicode.MaxLatin1 { 115 | return fmt.Errorf("Character '%s' is not valid Latin-1", char) 116 | } 117 | 118 | if err := binary.Write(&buf, binary.BigEndian, uint8(char)); err != nil { 119 | return err 120 | } 121 | } 122 | 123 | dataLength := 8 + len(text) 124 | if _, err := c.c.Write(buf.Bytes()[0:dataLength]); err != nil { 125 | return err 126 | } 127 | 128 | return nil 129 | } 130 | 131 | // Requests a framebuffer update from the server. There may be an indefinite 132 | // time between the request and the actual framebuffer update being 133 | // received. 134 | // 135 | // See RFC 6143 Section 7.5.3 136 | func (c *ClientConn) FramebufferUpdateRequest(incremental bool, x, y, width, height uint16) error { 137 | var buf bytes.Buffer 138 | var incrementalByte uint8 = 0 139 | 140 | if incremental { 141 | incrementalByte = 1 142 | } 143 | 144 | data := []interface{}{ 145 | uint8(3), 146 | incrementalByte, 147 | x, y, width, height, 148 | } 149 | 150 | for _, val := range data { 151 | if err := binary.Write(&buf, binary.BigEndian, val); err != nil { 152 | return err 153 | } 154 | } 155 | 156 | if _, err := c.c.Write(buf.Bytes()[0:10]); err != nil { 157 | return err 158 | } 159 | 160 | return nil 161 | } 162 | 163 | // KeyEvent indiciates a key press or release and sends it to the server. 164 | // The key is indicated using the X Window System "keysym" value. Use 165 | // Google to find a reference of these values. To simulate a key press, 166 | // you must send a key with both a down event, and a non-down event. 167 | // 168 | // See 7.5.4. 169 | func (c *ClientConn) KeyEvent(keysym uint32, down bool) error { 170 | var downFlag uint8 = 0 171 | if down { 172 | downFlag = 1 173 | } 174 | 175 | data := []interface{}{ 176 | uint8(4), 177 | downFlag, 178 | uint8(0), 179 | uint8(0), 180 | keysym, 181 | } 182 | 183 | for _, val := range data { 184 | if err := binary.Write(c.c, binary.BigEndian, val); err != nil { 185 | return err 186 | } 187 | } 188 | 189 | return nil 190 | } 191 | 192 | // PointerEvent indicates that pointer movement or a pointer button 193 | // press or release. 194 | // 195 | // The mask is a bitwise mask of various ButtonMask values. When a button 196 | // is set, it is pressed, when it is unset, it is released. 197 | // 198 | // See RFC 6143 Section 7.5.5 199 | func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error { 200 | var buf bytes.Buffer 201 | 202 | data := []interface{}{ 203 | uint8(5), 204 | uint8(mask), 205 | x, 206 | y, 207 | } 208 | 209 | for _, val := range data { 210 | if err := binary.Write(&buf, binary.BigEndian, val); err != nil { 211 | return err 212 | } 213 | } 214 | 215 | if _, err := c.c.Write(buf.Bytes()[0:6]); err != nil { 216 | return err 217 | } 218 | 219 | return nil 220 | } 221 | 222 | // SetEncodings sets the encoding types in which the pixel data can 223 | // be sent from the server. After calling this method, the encs slice 224 | // given should not be modified. 225 | // 226 | // See RFC 6143 Section 7.5.2 227 | func (c *ClientConn) SetEncodings(encs []Encoding) error { 228 | data := make([]interface{}, 3+len(encs)) 229 | data[0] = uint8(2) 230 | data[1] = uint8(0) 231 | data[2] = uint16(len(encs)) 232 | 233 | for i, enc := range encs { 234 | data[3+i] = int32(enc.Type()) 235 | } 236 | 237 | var buf bytes.Buffer 238 | for _, val := range data { 239 | if err := binary.Write(&buf, binary.BigEndian, val); err != nil { 240 | return err 241 | } 242 | } 243 | 244 | dataLength := 4 + (4 * len(encs)) 245 | if _, err := c.c.Write(buf.Bytes()[0:dataLength]); err != nil { 246 | return err 247 | } 248 | 249 | c.Encs = encs 250 | 251 | return nil 252 | } 253 | 254 | // SetPixelFormat sets the format in which pixel values should be sent 255 | // in FramebufferUpdate messages from the server. 256 | // 257 | // See RFC 6143 Section 7.5.1 258 | func (c *ClientConn) SetPixelFormat(format *PixelFormat) error { 259 | var keyEvent [20]byte 260 | keyEvent[0] = 0 261 | 262 | pfBytes, err := writePixelFormat(format) 263 | if err != nil { 264 | return err 265 | } 266 | 267 | // Copy the pixel format bytes into the proper slice location 268 | copy(keyEvent[4:], pfBytes) 269 | 270 | // Send the data down the connection 271 | if _, err := c.c.Write(keyEvent[:]); err != nil { 272 | return err 273 | } 274 | 275 | // Reset the color map as according to RFC. 276 | var newColorMap [256]Color 277 | c.ColorMap = newColorMap 278 | 279 | return nil 280 | } 281 | 282 | const pvLen = 12 // ProtocolVersion message length. 283 | 284 | func parseProtocolVersion(pv []byte) (uint, uint, error) { 285 | var major, minor uint 286 | 287 | if len(pv) < pvLen { 288 | return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), pvLen) 289 | } 290 | 291 | l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor) 292 | if l != 2 { 293 | return 0, 0, fmt.Errorf("error parsing ProtocolVersion.") 294 | } 295 | if err != nil { 296 | return 0, 0, err 297 | } 298 | 299 | return major, minor, nil 300 | } 301 | 302 | func (c *ClientConn) handshake() error { 303 | var protocolVersion [pvLen]byte 304 | 305 | // 7.1.1, read the ProtocolVersion message sent by the server. 306 | if _, err := io.ReadFull(c.c, protocolVersion[:]); err != nil { 307 | return err 308 | } 309 | 310 | maxMajor, maxMinor, err := parseProtocolVersion(protocolVersion[:]) 311 | if err != nil { 312 | return err 313 | } 314 | if maxMajor < 3 { 315 | return fmt.Errorf("unsupported major version, less than 3: %d", maxMajor) 316 | } 317 | if maxMinor < 8 { 318 | return fmt.Errorf("unsupported minor version, less than 8: %d", maxMinor) 319 | } 320 | 321 | // Respond with the version we will support 322 | if _, err = c.c.Write([]byte("RFB 003.008\n")); err != nil { 323 | return err 324 | } 325 | 326 | // 7.1.2 Security Handshake from server 327 | var numSecurityTypes uint8 328 | if err = binary.Read(c.c, binary.BigEndian, &numSecurityTypes); err != nil { 329 | return err 330 | } 331 | 332 | if numSecurityTypes == 0 { 333 | return fmt.Errorf("no security types: %s", c.readErrorReason()) 334 | } 335 | 336 | securityTypes := make([]uint8, numSecurityTypes) 337 | if err = binary.Read(c.c, binary.BigEndian, &securityTypes); err != nil { 338 | return err 339 | } 340 | 341 | clientSecurityTypes := c.config.Auth 342 | if clientSecurityTypes == nil { 343 | clientSecurityTypes = []ClientAuth{new(ClientAuthNone)} 344 | } 345 | 346 | var auth ClientAuth 347 | FindAuth: 348 | for _, curAuth := range clientSecurityTypes { 349 | for _, securityType := range securityTypes { 350 | if curAuth.SecurityType() == securityType { 351 | // We use the first matching supported authentication 352 | auth = curAuth 353 | break FindAuth 354 | } 355 | } 356 | } 357 | 358 | if auth == nil { 359 | return fmt.Errorf("no suitable auth schemes found. server supported: %#v", securityTypes) 360 | } 361 | 362 | // Respond back with the security type we'll use 363 | if err = binary.Write(c.c, binary.BigEndian, auth.SecurityType()); err != nil { 364 | return err 365 | } 366 | 367 | if err = auth.Handshake(c.c); err != nil { 368 | return err 369 | } 370 | 371 | // 7.1.3 SecurityResult Handshake 372 | var securityResult uint32 373 | if err = binary.Read(c.c, binary.BigEndian, &securityResult); err != nil { 374 | return err 375 | } 376 | 377 | if securityResult == 1 { 378 | return fmt.Errorf("security handshake failed: %s", c.readErrorReason()) 379 | } 380 | 381 | // 7.3.1 ClientInit 382 | var sharedFlag uint8 = 1 383 | if c.config.Exclusive { 384 | sharedFlag = 0 385 | } 386 | 387 | if err = binary.Write(c.c, binary.BigEndian, sharedFlag); err != nil { 388 | return err 389 | } 390 | 391 | // 7.3.2 ServerInit 392 | if err = binary.Read(c.c, binary.BigEndian, &c.FrameBufferWidth); err != nil { 393 | return err 394 | } 395 | 396 | if err = binary.Read(c.c, binary.BigEndian, &c.FrameBufferHeight); err != nil { 397 | return err 398 | } 399 | 400 | // Read the pixel format 401 | if err = readPixelFormat(c.c, &c.PixelFormat); err != nil { 402 | return err 403 | } 404 | 405 | var nameLength uint32 406 | if err = binary.Read(c.c, binary.BigEndian, &nameLength); err != nil { 407 | return err 408 | } 409 | 410 | nameBytes := make([]uint8, nameLength) 411 | if err = binary.Read(c.c, binary.BigEndian, &nameBytes); err != nil { 412 | return err 413 | } 414 | 415 | c.DesktopName = string(nameBytes) 416 | 417 | return nil 418 | } 419 | 420 | // mainLoop reads messages sent from the server and routes them to the 421 | // proper channels for users of the client to read. 422 | func (c *ClientConn) mainLoop() { 423 | defer c.Close() 424 | 425 | // Build the map of available server messages 426 | typeMap := make(map[uint8]ServerMessage) 427 | 428 | defaultMessages := []ServerMessage{ 429 | new(FramebufferUpdateMessage), 430 | new(SetColorMapEntriesMessage), 431 | new(BellMessage), 432 | new(ServerCutTextMessage), 433 | } 434 | 435 | for _, msg := range defaultMessages { 436 | typeMap[msg.Type()] = msg 437 | } 438 | 439 | if c.config.ServerMessages != nil { 440 | for _, msg := range c.config.ServerMessages { 441 | typeMap[msg.Type()] = msg 442 | } 443 | } 444 | 445 | for { 446 | var messageType uint8 447 | if err := binary.Read(c.c, binary.BigEndian, &messageType); err != nil { 448 | break 449 | } 450 | 451 | msg, ok := typeMap[messageType] 452 | if !ok { 453 | // Unsupported message type! Bad! 454 | break 455 | } 456 | 457 | parsedMsg, err := msg.Read(c, c.c) 458 | if err != nil { 459 | break 460 | } 461 | 462 | if c.config.ServerMessageCh == nil { 463 | continue 464 | } 465 | 466 | c.config.ServerMessageCh <- parsedMsg 467 | } 468 | } 469 | 470 | func (c *ClientConn) readErrorReason() string { 471 | var reasonLen uint32 472 | if err := binary.Read(c.c, binary.BigEndian, &reasonLen); err != nil { 473 | return "" 474 | } 475 | 476 | reason := make([]uint8, reasonLen) 477 | if err := binary.Read(c.c, binary.BigEndian, &reason); err != nil { 478 | return "" 479 | } 480 | 481 | return string(reason) 482 | } 483 | -------------------------------------------------------------------------------- /client_auth.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "net" 5 | 6 | "crypto/des" 7 | "encoding/binary" 8 | ) 9 | 10 | // A ClientAuth implements a method of authenticating with a remote server. 11 | type ClientAuth interface { 12 | // SecurityType returns the byte identifier sent by the server to 13 | // identify this authentication scheme. 14 | SecurityType() uint8 15 | 16 | // Handshake is called when the authentication handshake should be 17 | // performed, as part of the general RFB handshake. (see 7.2.1) 18 | Handshake(net.Conn) error 19 | } 20 | 21 | // ClientAuthNone is the "none" authentication. See 7.2.1 22 | type ClientAuthNone byte 23 | 24 | func (*ClientAuthNone) SecurityType() uint8 { 25 | return 1 26 | } 27 | 28 | func (*ClientAuthNone) Handshake(net.Conn) error { 29 | return nil 30 | } 31 | 32 | // PasswordAuth is VNC authentication, 7.2.2 33 | type PasswordAuth struct { 34 | Password string 35 | } 36 | 37 | func (p *PasswordAuth) SecurityType() uint8 { 38 | return 2 39 | } 40 | 41 | func (p *PasswordAuth) Handshake(c net.Conn) error { 42 | randomValue := make([]uint8, 16) 43 | if err := binary.Read(c, binary.BigEndian, &randomValue); err != nil { 44 | return err 45 | } 46 | 47 | crypted, err := p.encrypt(p.Password, randomValue) 48 | 49 | if (err != nil) { 50 | return err 51 | } 52 | 53 | if err := binary.Write(c, binary.BigEndian, &crypted); err != nil { 54 | return err 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func (p *PasswordAuth) reverseBits(b byte) byte { 61 | var reverse = [256]int{ 62 | 0, 128, 64, 192, 32, 160, 96, 224, 63 | 16, 144, 80, 208, 48, 176, 112, 240, 64 | 8, 136, 72, 200, 40, 168, 104, 232, 65 | 24, 152, 88, 216, 56, 184, 120, 248, 66 | 4, 132, 68, 196, 36, 164, 100, 228, 67 | 20, 148, 84, 212, 52, 180, 116, 244, 68 | 12, 140, 76, 204, 44, 172, 108, 236, 69 | 28, 156, 92, 220, 60, 188, 124, 252, 70 | 2, 130, 66, 194, 34, 162, 98, 226, 71 | 18, 146, 82, 210, 50, 178, 114, 242, 72 | 10, 138, 74, 202, 42, 170, 106, 234, 73 | 26, 154, 90, 218, 58, 186, 122, 250, 74 | 6, 134, 70, 198, 38, 166, 102, 230, 75 | 22, 150, 86, 214, 54, 182, 118, 246, 76 | 14, 142, 78, 206, 46, 174, 110, 238, 77 | 30, 158, 94, 222, 62, 190, 126, 254, 78 | 1, 129, 65, 193, 33, 161, 97, 225, 79 | 17, 145, 81, 209, 49, 177, 113, 241, 80 | 9, 137, 73, 201, 41, 169, 105, 233, 81 | 25, 153, 89, 217, 57, 185, 121, 249, 82 | 5, 133, 69, 197, 37, 165, 101, 229, 83 | 21, 149, 85, 213, 53, 181, 117, 245, 84 | 13, 141, 77, 205, 45, 173, 109, 237, 85 | 29, 157, 93, 221, 61, 189, 125, 253, 86 | 3, 131, 67, 195, 35, 163, 99, 227, 87 | 19, 147, 83, 211, 51, 179, 115, 243, 88 | 11, 139, 75, 203, 43, 171, 107, 235, 89 | 27, 155, 91, 219, 59, 187, 123, 251, 90 | 7, 135, 71, 199, 39, 167, 103, 231, 91 | 23, 151, 87, 215, 55, 183, 119, 247, 92 | 15, 143, 79, 207, 47, 175, 111, 239, 93 | 31, 159, 95, 223, 63, 191, 127, 255, 94 | } 95 | 96 | return byte(reverse[int(b)]) 97 | } 98 | 99 | func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) { 100 | keyBytes := []byte{0,0,0,0,0,0,0,0} 101 | 102 | if len(key) > 8 { 103 | key = key[:8] 104 | } 105 | 106 | for i := 0; i < len(key); i++ { 107 | keyBytes[i] = p.reverseBits(key[i]) 108 | } 109 | 110 | block, err := des.NewCipher(keyBytes) 111 | 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | result1 := make([]byte, 8) 117 | block.Encrypt(result1, bytes) 118 | result2 := make([]byte, 8) 119 | block.Encrypt(result2, bytes[8:]) 120 | 121 | crypted := append(result1, result2...) 122 | 123 | return crypted, nil 124 | } 125 | -------------------------------------------------------------------------------- /client_auth_test.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "testing" 5 | "net" 6 | "time" 7 | "bytes" 8 | ) 9 | 10 | type fakeNetConnection struct { 11 | DataToSend []byte 12 | Test *testing.T 13 | ExpectData []byte 14 | Finished bool 15 | Matched bool 16 | } 17 | 18 | func (fc fakeNetConnection) Read(b []byte) (n int, err error) { 19 | for i := 0; i < 16; i++ { 20 | b[i] = fc.DataToSend[i] 21 | } 22 | 23 | fc.Finished = false 24 | 25 | return len(b), nil 26 | } 27 | 28 | func (fc *fakeNetConnection) Write(b []byte) (n int, err error) { 29 | fc.Matched = bytes.Equal(b, fc.ExpectData) 30 | fc.Finished = true 31 | 32 | return len(b), nil 33 | } 34 | 35 | func (fc *fakeNetConnection) Close() error { return nil; } 36 | func (fc *fakeNetConnection) LocalAddr() net.Addr { return nil; } 37 | func (fc *fakeNetConnection) RemoteAddr() net.Addr { return nil; } 38 | func (fc *fakeNetConnection) SetDeadline(t time.Time) error { return nil; } 39 | func (fc *fakeNetConnection) SetReadDeadline(t time.Time) error { return nil; } 40 | func (fc *fakeNetConnection) SetWriteDeadline(t time.Time) error { return nil; } 41 | 42 | func TestClientAuthNone_Impl(t *testing.T) { 43 | var raw interface{} 44 | raw = new(ClientAuthNone) 45 | if _, ok := raw.(ClientAuth); !ok { 46 | t.Fatal("ClientAuthNone doesn't implement ClientAuth") 47 | } 48 | } 49 | 50 | func TestClientAuthPasswordSuccess_Impl(t *testing.T) { 51 | // Values ripped using WireShark 52 | randomValue := []byte{ 53 | 0xa4, 54 | 0x51, 55 | 0x3f, 56 | 0xa5, 57 | 0x1f, 58 | 0x87, 59 | 0x06, 60 | 0x10, 61 | 0xa4, 62 | 0x5f, 63 | 0xae, 64 | 0xbf, 65 | 0x4d, 66 | 0xac, 67 | 0x12, 68 | 0x22, 69 | } 70 | 71 | expectedResponse := []byte{ 72 | 0x71, 73 | 0xe4, 74 | 0x41, 75 | 0x30, 76 | 0x43, 77 | 0x65, 78 | 0x4e, 79 | 0x39, 80 | 0xda, 81 | 0x6d, 82 | 0x49, 83 | 0x93, 84 | 0x43, 85 | 0xf6, 86 | 0x5e, 87 | 0x29, 88 | } 89 | 90 | raw := PasswordAuth{Password: "Ch_#!T@8"} 91 | 92 | // Only about 12 hours into Go at the moment... 93 | // if _, ok := raw.(ClientAuth); !ok { 94 | // t.Fatal("PasswordAuth doesn't implement ClientAuth") 95 | // } 96 | 97 | conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} 98 | err := raw.Handshake(conn) 99 | 100 | if (err != nil) { 101 | t.Fatal(err) 102 | } 103 | 104 | if !conn.Matched { 105 | t.Fatal("PasswordAuth didn't pass the right response back to the wire") 106 | } 107 | 108 | if !conn.Finished { 109 | t.Fatal("PasswordAuth didn't complete properly") 110 | } 111 | } 112 | 113 | func TestClientAuthPasswordReject_Impl(t *testing.T) { 114 | // Values ripped using WireShark 115 | randomValue := []byte{ 116 | 0xa4, 117 | 0x51, 118 | 0x3f, 119 | 0xa5, 120 | 0x1f, 121 | 0x87, 122 | 0x06, 123 | 0x10, 124 | 0xa4, 125 | 0x5f, 126 | 0xae, 127 | 0xbf, 128 | 0x4d, 129 | 0xac, 130 | 0x12, 131 | 0x22, 132 | } 133 | 134 | expectedResponse := []byte{ 135 | 0x71, 136 | 0xe4, 137 | 0x41, 138 | 0x30, 139 | 0x43, 140 | 0x65, 141 | 0x4e, 142 | 0x39, 143 | 0xda, 144 | 0x6d, 145 | 0x49, 146 | 0x93, 147 | 0x43, 148 | 0xf6, 149 | 0x5e, 150 | 0x29, 151 | } 152 | 153 | raw := PasswordAuth{Password: "Ch_#!T@"} 154 | 155 | conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t} 156 | err := raw.Handshake(conn) 157 | 158 | if (err != nil) { 159 | t.Fatal(err) 160 | } 161 | 162 | if conn.Matched { 163 | t.Fatal("PasswordAuth didn't pass the right response back to the wire") 164 | } 165 | 166 | if !conn.Finished { 167 | t.Fatal("PasswordAuth didn't complete properly") 168 | } 169 | } -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "testing" 7 | ) 8 | 9 | func newMockServer(t *testing.T, version string) string { 10 | ln, err := net.Listen("tcp", "127.0.0.1:0") 11 | if err != nil { 12 | t.Fatalf("error listening: %s", err) 13 | } 14 | 15 | go func() { 16 | defer ln.Close() 17 | c, err := ln.Accept() 18 | if err != nil { 19 | t.Fatalf("error accepting conn: %s", err) 20 | } 21 | defer c.Close() 22 | 23 | _, err = c.Write([]byte(fmt.Sprintf("RFB %s\n", version))) 24 | if err != nil { 25 | t.Fatal("failed writing version") 26 | } 27 | }() 28 | 29 | return ln.Addr().String() 30 | } 31 | 32 | func TestClient_LowMajorVersion(t *testing.T) { 33 | nc, err := net.Dial("tcp", newMockServer(t, "002.009")) 34 | if err != nil { 35 | t.Fatalf("error connecting to mock server: %s", err) 36 | } 37 | 38 | _, err = Client(nc, &ClientConfig{}) 39 | if err == nil { 40 | t.Fatal("error expected") 41 | } 42 | 43 | if err.Error() != "unsupported major version, less than 3: 2" { 44 | t.Fatalf("unexpected error: %s", err) 45 | } 46 | } 47 | 48 | func TestClient_LowMinorVersion(t *testing.T) { 49 | nc, err := net.Dial("tcp", newMockServer(t, "003.007")) 50 | if err != nil { 51 | t.Fatalf("error connecting to mock server: %s", err) 52 | } 53 | 54 | _, err = Client(nc, &ClientConfig{}) 55 | if err == nil { 56 | t.Fatal("error expected") 57 | } 58 | 59 | if err.Error() != "unsupported minor version, less than 8: 7" { 60 | t.Fatalf("unexpected error: %s", err) 61 | } 62 | } 63 | 64 | func TestParseProtocolVersion(t *testing.T) { 65 | tests := []struct { 66 | proto []byte 67 | major, minor uint 68 | isErr bool 69 | }{ 70 | // Valid ProtocolVersion messages. 71 | {[]byte{82, 70, 66, 32, 48, 48, 51, 46, 48, 48, 56, 10}, 3, 8, false}, // RFB 003.008\n 72 | {[]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 73 | {[]byte{82, 70, 66, 32, 48, 48, 48, 46, 48, 48, 48, 10}, 0, 0, false}, // RFB 000.0000\n 74 | // Invalid messages. 75 | {[]byte{82, 70, 66, 32, 51, 46, 56, 10}, 0, 0, true}, // RFB 3.8\n -- too short; not zero padded 76 | {[]byte{82, 70, 66, 10}, 0, 0, true}, // RFB\n -- too short 77 | {[]byte{}, 0, 0, true}, // (empty) -- too short 78 | } 79 | 80 | for _, tt := range tests { 81 | major, minor, err := parseProtocolVersion(tt.proto) 82 | if err != nil && !tt.isErr { 83 | t.Fatalf("parseProtocolVersion(%v) unexpected error %v", tt.proto, err) 84 | } 85 | if err == nil && tt.isErr { 86 | t.Fatalf("parseProtocolVersion(%v) expected error", tt.proto) 87 | } 88 | if major != tt.major { 89 | t.Errorf("parseProtocolVersion(%v) major = %v, want %v", tt.proto, major, tt.major) 90 | } 91 | if major != tt.major { 92 | t.Errorf("parseProtocolVersion(%v) minor = %v, want %v", tt.proto, minor, tt.minor) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /color.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | // Color represents a single color in a color map. 4 | type Color struct { 5 | R, G, B uint16 6 | } 7 | -------------------------------------------------------------------------------- /encoding.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | // An Encoding implements a method for encoding pixel data that is 9 | // sent by the server to the client. 10 | type Encoding interface { 11 | // The number that uniquely identifies this encoding type. 12 | Type() int32 13 | 14 | // Read reads the contents of the encoded pixel data from the reader. 15 | // This should return a new Encoding implementation that contains 16 | // the proper data. 17 | Read(*ClientConn, *Rectangle, io.Reader) (Encoding, error) 18 | } 19 | 20 | // RawEncoding is raw pixel data sent by the server. 21 | // 22 | // See RFC 6143 Section 7.7.1 23 | type RawEncoding struct { 24 | Colors []Color 25 | } 26 | 27 | func (*RawEncoding) Type() int32 { 28 | return 0 29 | } 30 | 31 | func (*RawEncoding) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { 32 | bytesPerPixel := c.PixelFormat.BPP / 8 33 | pixelBytes := make([]uint8, bytesPerPixel) 34 | 35 | var byteOrder binary.ByteOrder = binary.LittleEndian 36 | if c.PixelFormat.BigEndian { 37 | byteOrder = binary.BigEndian 38 | } 39 | 40 | colors := make([]Color, int(rect.Height)*int(rect.Width)) 41 | 42 | for y := uint16(0); y < rect.Height; y++ { 43 | for x := uint16(0); x < rect.Width; x++ { 44 | if _, err := io.ReadFull(r, pixelBytes); err != nil { 45 | return nil, err 46 | } 47 | 48 | var rawPixel uint32 49 | if c.PixelFormat.BPP == 8 { 50 | rawPixel = uint32(pixelBytes[0]) 51 | } else if c.PixelFormat.BPP == 16 { 52 | rawPixel = uint32(byteOrder.Uint16(pixelBytes)) 53 | } else if c.PixelFormat.BPP == 32 { 54 | rawPixel = byteOrder.Uint32(pixelBytes) 55 | } 56 | 57 | color := &colors[int(y)*int(rect.Width)+int(x)] 58 | if c.PixelFormat.TrueColor { 59 | color.R = uint16((rawPixel >> c.PixelFormat.RedShift) & uint32(c.PixelFormat.RedMax)) 60 | color.G = uint16((rawPixel >> c.PixelFormat.GreenShift) & uint32(c.PixelFormat.GreenMax)) 61 | color.B = uint16((rawPixel >> c.PixelFormat.BlueShift) & uint32(c.PixelFormat.BlueMax)) 62 | } else { 63 | *color = c.ColorMap[rawPixel] 64 | } 65 | } 66 | } 67 | 68 | return &RawEncoding{colors}, nil 69 | } 70 | -------------------------------------------------------------------------------- /pixel_format.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | ) 8 | 9 | // PixelFormat describes the way a pixel is formatted for a VNC connection. 10 | // 11 | // See RFC 6143 Section 7.4 for information on each of the fields. 12 | type PixelFormat struct { 13 | BPP uint8 14 | Depth uint8 15 | BigEndian bool 16 | TrueColor bool 17 | RedMax uint16 18 | GreenMax uint16 19 | BlueMax uint16 20 | RedShift uint8 21 | GreenShift uint8 22 | BlueShift uint8 23 | } 24 | 25 | func readPixelFormat(r io.Reader, result *PixelFormat) error { 26 | var rawPixelFormat [16]byte 27 | if _, err := io.ReadFull(r, rawPixelFormat[:]); err != nil { 28 | return err 29 | } 30 | 31 | var pfBoolByte uint8 32 | brPF := bytes.NewReader(rawPixelFormat[:]) 33 | if err := binary.Read(brPF, binary.BigEndian, &result.BPP); err != nil { 34 | return err 35 | } 36 | 37 | if err := binary.Read(brPF, binary.BigEndian, &result.Depth); err != nil { 38 | return err 39 | } 40 | 41 | if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { 42 | return err 43 | } 44 | 45 | if pfBoolByte != 0 { 46 | // Big endian is true 47 | result.BigEndian = true 48 | } 49 | 50 | if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil { 51 | return err 52 | } 53 | 54 | if pfBoolByte != 0 { 55 | // True Color is true. So we also have to read all the color max & shifts. 56 | result.TrueColor = true 57 | 58 | if err := binary.Read(brPF, binary.BigEndian, &result.RedMax); err != nil { 59 | return err 60 | } 61 | 62 | if err := binary.Read(brPF, binary.BigEndian, &result.GreenMax); err != nil { 63 | return err 64 | } 65 | 66 | if err := binary.Read(brPF, binary.BigEndian, &result.BlueMax); err != nil { 67 | return err 68 | } 69 | 70 | if err := binary.Read(brPF, binary.BigEndian, &result.RedShift); err != nil { 71 | return err 72 | } 73 | 74 | if err := binary.Read(brPF, binary.BigEndian, &result.GreenShift); err != nil { 75 | return err 76 | } 77 | 78 | if err := binary.Read(brPF, binary.BigEndian, &result.BlueShift); err != nil { 79 | return err 80 | } 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func writePixelFormat(format *PixelFormat) ([]byte, error) { 87 | var buf bytes.Buffer 88 | 89 | // Byte 1 90 | if err := binary.Write(&buf, binary.BigEndian, format.BPP); err != nil { 91 | return nil, err 92 | } 93 | 94 | // Byte 2 95 | if err := binary.Write(&buf, binary.BigEndian, format.Depth); err != nil { 96 | return nil, err 97 | } 98 | 99 | var boolByte byte 100 | if format.BigEndian { 101 | boolByte = 1 102 | } else { 103 | boolByte = 0 104 | } 105 | 106 | // Byte 3 (BigEndian) 107 | if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { 108 | return nil, err 109 | } 110 | 111 | if format.TrueColor { 112 | boolByte = 1 113 | } else { 114 | boolByte = 0 115 | } 116 | 117 | // Byte 4 (TrueColor) 118 | if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil { 119 | return nil, err 120 | } 121 | 122 | // If we have true color enabled then we have to fill in the rest of the 123 | // structure with the color values. 124 | if format.TrueColor { 125 | if err := binary.Write(&buf, binary.BigEndian, format.RedMax); err != nil { 126 | return nil, err 127 | } 128 | 129 | if err := binary.Write(&buf, binary.BigEndian, format.GreenMax); err != nil { 130 | return nil, err 131 | } 132 | 133 | if err := binary.Write(&buf, binary.BigEndian, format.BlueMax); err != nil { 134 | return nil, err 135 | } 136 | 137 | if err := binary.Write(&buf, binary.BigEndian, format.RedShift); err != nil { 138 | return nil, err 139 | } 140 | 141 | if err := binary.Write(&buf, binary.BigEndian, format.GreenShift); err != nil { 142 | return nil, err 143 | } 144 | 145 | if err := binary.Write(&buf, binary.BigEndian, format.BlueShift); err != nil { 146 | return nil, err 147 | } 148 | } 149 | 150 | return buf.Bytes()[0:16], nil 151 | } 152 | -------------------------------------------------------------------------------- /pointer.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | // ButtonMask represents a mask of pointer presses/releases. 4 | type ButtonMask uint8 5 | 6 | // All available button mask components. 7 | const ( 8 | ButtonLeft ButtonMask = 1 << iota 9 | ButtonMiddle 10 | ButtonRight 11 | Button4 12 | Button5 13 | Button6 14 | Button7 15 | Button8 16 | ) 17 | -------------------------------------------------------------------------------- /server_messages.go: -------------------------------------------------------------------------------- 1 | package vnc 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | // A ServerMessage implements a message sent from the server to the client. 10 | type ServerMessage interface { 11 | // The type of the message that is sent down on the wire. 12 | Type() uint8 13 | 14 | // Read reads the contents of the message from the reader. At the point 15 | // this is called, the message type has already been read from the reader. 16 | // This should return a new ServerMessage that is the appropriate type. 17 | Read(*ClientConn, io.Reader) (ServerMessage, error) 18 | } 19 | 20 | // FramebufferUpdateMessage consists of a sequence of rectangles of 21 | // pixel data that the client should put into its framebuffer. 22 | type FramebufferUpdateMessage struct { 23 | Rectangles []Rectangle 24 | } 25 | 26 | // Rectangle represents a rectangle of pixel data. 27 | type Rectangle struct { 28 | X uint16 29 | Y uint16 30 | Width uint16 31 | Height uint16 32 | Enc Encoding 33 | } 34 | 35 | func (*FramebufferUpdateMessage) Type() uint8 { 36 | return 0 37 | } 38 | 39 | func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) { 40 | // Read off the padding 41 | var padding [1]byte 42 | if _, err := io.ReadFull(r, padding[:]); err != nil { 43 | return nil, err 44 | } 45 | 46 | var numRects uint16 47 | if err := binary.Read(r, binary.BigEndian, &numRects); err != nil { 48 | return nil, err 49 | } 50 | 51 | // Build the map of encodings supported 52 | encMap := make(map[int32]Encoding) 53 | for _, enc := range c.Encs { 54 | encMap[enc.Type()] = enc 55 | } 56 | 57 | // We must always support the raw encoding 58 | rawEnc := new(RawEncoding) 59 | encMap[rawEnc.Type()] = rawEnc 60 | 61 | rects := make([]Rectangle, numRects) 62 | for i := uint16(0); i < numRects; i++ { 63 | var encodingType int32 64 | 65 | rect := &rects[i] 66 | data := []interface{}{ 67 | &rect.X, 68 | &rect.Y, 69 | &rect.Width, 70 | &rect.Height, 71 | &encodingType, 72 | } 73 | 74 | for _, val := range data { 75 | if err := binary.Read(r, binary.BigEndian, val); err != nil { 76 | return nil, err 77 | } 78 | } 79 | 80 | enc, ok := encMap[encodingType] 81 | if !ok { 82 | return nil, fmt.Errorf("unsupported encoding type: %d", encodingType) 83 | } 84 | 85 | var err error 86 | rect.Enc, err = enc.Read(c, rect, r) 87 | if err != nil { 88 | return nil, err 89 | } 90 | } 91 | 92 | return &FramebufferUpdateMessage{rects}, nil 93 | } 94 | 95 | // SetColorMapEntriesMessage is sent by the server to set values into 96 | // the color map. This message will automatically update the color map 97 | // for the associated connection, but contains the color change data 98 | // if the consumer wants to read it. 99 | // 100 | // See RFC 6143 Section 7.6.2 101 | type SetColorMapEntriesMessage struct { 102 | FirstColor uint16 103 | Colors []Color 104 | } 105 | 106 | func (*SetColorMapEntriesMessage) Type() uint8 { 107 | return 1 108 | } 109 | 110 | func (*SetColorMapEntriesMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) { 111 | // Read off the padding 112 | var padding [1]byte 113 | if _, err := io.ReadFull(r, padding[:]); err != nil { 114 | return nil, err 115 | } 116 | 117 | var result SetColorMapEntriesMessage 118 | if err := binary.Read(r, binary.BigEndian, &result.FirstColor); err != nil { 119 | return nil, err 120 | } 121 | 122 | var numColors uint16 123 | if err := binary.Read(r, binary.BigEndian, &numColors); err != nil { 124 | return nil, err 125 | } 126 | 127 | result.Colors = make([]Color, numColors) 128 | for i := uint16(0); i < numColors; i++ { 129 | 130 | color := &result.Colors[i] 131 | data := []interface{}{ 132 | &color.R, 133 | &color.G, 134 | &color.B, 135 | } 136 | 137 | for _, val := range data { 138 | if err := binary.Read(r, binary.BigEndian, val); err != nil { 139 | return nil, err 140 | } 141 | } 142 | 143 | // Update the connection's color map 144 | c.ColorMap[result.FirstColor+i] = *color 145 | } 146 | 147 | return &result, nil 148 | } 149 | 150 | // Bell signals that an audible bell should be made on the client. 151 | // 152 | // See RFC 6143 Section 7.6.3 153 | type BellMessage byte 154 | 155 | func (*BellMessage) Type() uint8 { 156 | return 2 157 | } 158 | 159 | func (*BellMessage) Read(*ClientConn, io.Reader) (ServerMessage, error) { 160 | return new(BellMessage), nil 161 | } 162 | 163 | // ServerCutTextMessage indicates the server has new text in the cut buffer. 164 | // 165 | // See RFC 6143 Section 7.6.4 166 | type ServerCutTextMessage struct { 167 | Text string 168 | } 169 | 170 | func (*ServerCutTextMessage) Type() uint8 { 171 | return 3 172 | } 173 | 174 | func (*ServerCutTextMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) { 175 | // Read off the padding 176 | var padding [1]byte 177 | if _, err := io.ReadFull(r, padding[:]); err != nil { 178 | return nil, err 179 | } 180 | 181 | var textLength uint32 182 | if err := binary.Read(r, binary.BigEndian, &textLength); err != nil { 183 | return nil, err 184 | } 185 | 186 | textBytes := make([]uint8, textLength) 187 | if err := binary.Read(r, binary.BigEndian, &textBytes); err != nil { 188 | return nil, err 189 | } 190 | 191 | return &ServerCutTextMessage{string(textBytes)}, nil 192 | } 193 | --------------------------------------------------------------------------------