ruby-vnc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +4 -0
- data/README +26 -0
- data/Rakefile +83 -0
- data/data/keys.yaml +128 -0
- data/lib/cipher/des.rb +439 -0
- data/lib/net/vnc.rb +306 -0
- data/lib/net/vnc/version.rb +6 -0
- data/spec/cipher_des_spec.rb +142 -0
- data/spec/net_vnc_spec.rb +136 -0
- metadata +66 -0
data/ChangeLog
ADDED
data/README
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
= Introduction
|
2
|
+
|
3
|
+
The ruby-vnc library provides for simple rfb-protocol based control of a
|
4
|
+
VNC server. This can be used, eg, to automate applications (sometimes there
|
5
|
+
is no better way), or script some sort of interaction.
|
6
|
+
|
7
|
+
Being VNC based gives it the advantage of being able to be run against
|
8
|
+
servers on any platform.
|
9
|
+
|
10
|
+
The primary documentation is for the Net::VNC class.
|
11
|
+
|
12
|
+
= Thanks
|
13
|
+
|
14
|
+
Code borrows a lot from Tim Waugh's excellent rfbplaymacro. So far all it
|
15
|
+
really offers on top of that is access to the host clipboard, and the ease
|
16
|
+
with which it can be scripted, ie taking conditional actions based on the
|
17
|
+
contents thereof.
|
18
|
+
|
19
|
+
= TODO
|
20
|
+
|
21
|
+
* Having the ability to take and analyse screen dumps would be useful.
|
22
|
+
|
23
|
+
* Finish the specs.
|
24
|
+
|
25
|
+
* Fix issues with the way we use a background thread to handle the clipboard
|
26
|
+
etc chatter from the server.
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'spec/rake/verify_rcov'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/lib/net/vnc/version'
|
8
|
+
|
9
|
+
PKG_NAME = 'ruby-vnc'
|
10
|
+
PKG_VERSION = Net::VNC::VERSION
|
11
|
+
|
12
|
+
task :default => :spec
|
13
|
+
|
14
|
+
desc 'Run all specs'
|
15
|
+
Spec::Rake::SpecTask.new :spec do |t|
|
16
|
+
t.spec_opts = ['--format specdoc --colour']
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Run all specs and generate html spec document'
|
20
|
+
namespace :spec do
|
21
|
+
Spec::Rake::SpecTask.new :html do |t|
|
22
|
+
t.spec_opts = ['--format html:spec.html']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Run all specs and generate coverage'
|
27
|
+
Spec::Rake::SpecTask.new :rcov do |t|
|
28
|
+
t.rcov = true
|
29
|
+
t.rcov_opts = ['--exclude', 'spec']
|
30
|
+
t.rcov_opts << '--xrefs'
|
31
|
+
t.rcov_opts << '--text-report'
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace :rcov do
|
35
|
+
RCov::VerifyTask.new :verify => :rcov do |t|
|
36
|
+
t.threshold = 100.0
|
37
|
+
t.index_html = 'coverage/index.html'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::RDocTask.new do |t|
|
42
|
+
t.rdoc_dir = 'doc'
|
43
|
+
t.rdoc_files.include 'lib/**/*.rb'
|
44
|
+
t.rdoc_files.include 'README'
|
45
|
+
t.title = "#{PKG_NAME} documentation"
|
46
|
+
t.options += %w[--line-numbers --inline-source --tab-width 2]
|
47
|
+
t.main = 'README'
|
48
|
+
end
|
49
|
+
|
50
|
+
spec = Gem::Specification.new do |s|
|
51
|
+
s.name = PKG_NAME
|
52
|
+
s.version = PKG_VERSION
|
53
|
+
s.summary = %q{Ruby VNC library.}
|
54
|
+
s.description = %q{A library which implements the client VNC protocol to control VNC servers.}
|
55
|
+
s.authors = ['Charles Lowe']
|
56
|
+
s.email = %q{aquasync@gmail.com}
|
57
|
+
# not yet registered
|
58
|
+
#s.homepage = %q{http://code.google.com/p/ruby-vnc}
|
59
|
+
#s.rubyforge_project = %q{ruby-vnc}
|
60
|
+
|
61
|
+
# none yet
|
62
|
+
#s.executables = ['oletool']
|
63
|
+
s.files = ['Rakefile', 'README', 'ChangeLog', 'data/keys.yaml']
|
64
|
+
s.files += FileList['lib/**/*.rb']
|
65
|
+
s.files += FileList['spec/*_spec.rb']
|
66
|
+
# is there an rspec equivalent?
|
67
|
+
#s.test_files = FileList['test/test_*.rb']
|
68
|
+
|
69
|
+
s.has_rdoc = true
|
70
|
+
s.rdoc_options += [
|
71
|
+
'--main', 'README',
|
72
|
+
'--title', "#{PKG_NAME} documentation",
|
73
|
+
'--tab-width', '2'
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
Rake::GemPackageTask.new(spec) do |t|
|
78
|
+
t.gem_spec = spec
|
79
|
+
t.need_tar = false
|
80
|
+
t.need_zip = false
|
81
|
+
t.package_dir = 'build'
|
82
|
+
end
|
83
|
+
|
data/data/keys.yaml
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
backspace: 0xff08
|
2
|
+
tab: 0xff09
|
3
|
+
linefeed: 0xff0a
|
4
|
+
clear: 0xff0b
|
5
|
+
return: 0xff0d
|
6
|
+
pause: 0xff13
|
7
|
+
scroll_lock: 0xff14
|
8
|
+
sys_req: 0xff15
|
9
|
+
escape: 0xff1b
|
10
|
+
delete: 0xffff
|
11
|
+
home: 0xff50
|
12
|
+
left: 0xff51
|
13
|
+
up: 0xff52
|
14
|
+
right: 0xff53
|
15
|
+
down: 0xff54
|
16
|
+
prior: 0xff55
|
17
|
+
page_up: 0xff55
|
18
|
+
next: 0xff56
|
19
|
+
page_down: 0xff56
|
20
|
+
end: 0xff57
|
21
|
+
begin: 0xff58
|
22
|
+
select: 0xff60
|
23
|
+
print: 0xff61
|
24
|
+
execute: 0xff62
|
25
|
+
insert: 0xff63
|
26
|
+
undo: 0xff65
|
27
|
+
redo: 0xff66
|
28
|
+
menu: 0xff67
|
29
|
+
find: 0xff68
|
30
|
+
cancel: 0xff69
|
31
|
+
help: 0xff6a
|
32
|
+
break: 0xff6b
|
33
|
+
mode_switch: 0xff7e
|
34
|
+
script_switch: 0xff7e
|
35
|
+
num_lock: 0xff7f
|
36
|
+
kp_space: 0xff80
|
37
|
+
kp_tab: 0xff89
|
38
|
+
kp_enter: 0xff8d
|
39
|
+
kp_f1: 0xff91
|
40
|
+
kp_f2: 0xff92
|
41
|
+
kp_f3: 0xff93
|
42
|
+
kp_f4: 0xff94
|
43
|
+
kp_home: 0xff95
|
44
|
+
kp_left: 0xff96
|
45
|
+
kp_up: 0xff97
|
46
|
+
kp_right: 0xff98
|
47
|
+
kp_down: 0xff99
|
48
|
+
kp_prior: 0xff9a
|
49
|
+
kp_page_up: 0xff9a
|
50
|
+
kp_next: 0xff9b
|
51
|
+
kp_page_down: 0xff9b
|
52
|
+
kp_end: 0xff9c
|
53
|
+
kp_begin: 0xff9d
|
54
|
+
kp_insert: 0xff9e
|
55
|
+
kp_delete: 0xff9f
|
56
|
+
kp_equal: 0xffbd
|
57
|
+
kp_multiply: 0xffaa
|
58
|
+
kp_add: 0xffab
|
59
|
+
kp_separator: 0xffac
|
60
|
+
kp_subtract: 0xffad
|
61
|
+
kp_decimal: 0xffae
|
62
|
+
kp_divide: 0xffaf
|
63
|
+
kp_0: 0xffb0
|
64
|
+
kp_1: 0xffb1
|
65
|
+
kp_2: 0xffb2
|
66
|
+
kp_3: 0xffb3
|
67
|
+
kp_4: 0xffb4
|
68
|
+
kp_5: 0xffb5
|
69
|
+
kp_6: 0xffb6
|
70
|
+
kp_7: 0xffb7
|
71
|
+
kp_8: 0xffb8
|
72
|
+
kp_9: 0xffb9
|
73
|
+
f1: 0xffbe
|
74
|
+
f2: 0xffbf
|
75
|
+
f3: 0xffc0
|
76
|
+
f4: 0xffc1
|
77
|
+
f5: 0xffc2
|
78
|
+
f6: 0xffc3
|
79
|
+
f7: 0xffc4
|
80
|
+
f8: 0xffc5
|
81
|
+
f9: 0xffc6
|
82
|
+
f10: 0xffc7
|
83
|
+
f11: 0xffc8
|
84
|
+
f12: 0xffc9
|
85
|
+
f13: 0xffca
|
86
|
+
f14: 0xffcb
|
87
|
+
f15: 0xffcc
|
88
|
+
f16: 0xffcd
|
89
|
+
f17: 0xffce
|
90
|
+
f18: 0xffcf
|
91
|
+
f19: 0xffd0
|
92
|
+
f20: 0xffd1
|
93
|
+
f21: 0xffd2
|
94
|
+
f22: 0xffd3
|
95
|
+
f23: 0xffd4
|
96
|
+
f24: 0xffd5
|
97
|
+
f25: 0xffd6
|
98
|
+
f26: 0xffd7
|
99
|
+
f27: 0xffd8
|
100
|
+
f28: 0xffd9
|
101
|
+
f29: 0xffda
|
102
|
+
f30: 0xffdb
|
103
|
+
f31: 0xffdc
|
104
|
+
f32: 0xffdd
|
105
|
+
f33: 0xffde
|
106
|
+
f34: 0xffdf
|
107
|
+
f35: 0xffe0
|
108
|
+
left_shift: 0xffe1
|
109
|
+
right_shift: 0xffe2
|
110
|
+
left_control: 0xffe3
|
111
|
+
right_control: 0xffe4
|
112
|
+
caps_lock: 0xffe5
|
113
|
+
shift_lock: 0xffe6
|
114
|
+
left_meta: 0xffe7
|
115
|
+
right_meta: 0xffe8
|
116
|
+
left_alt: 0xffe9
|
117
|
+
right_alt: 0xffea
|
118
|
+
left_super: 0xffeb
|
119
|
+
right_super: 0xffec
|
120
|
+
left_hyper: 0xffed
|
121
|
+
right_hyper: 0xffee
|
122
|
+
# some convenience aliases
|
123
|
+
shift: 0xffe1
|
124
|
+
control: 0xffe3
|
125
|
+
meta: 0xffe7
|
126
|
+
alt: 0xffe9
|
127
|
+
super: 0xffeb
|
128
|
+
hyper: 0xffed
|
data/lib/cipher/des.rb
ADDED
@@ -0,0 +1,439 @@
|
|
1
|
+
module Cipher
|
2
|
+
#
|
3
|
+
# = Brief
|
4
|
+
#
|
5
|
+
# The Cipher::DES class allows for encryption and decryption of plain
|
6
|
+
# text using the "Data Encryption Standard". This version is the modified
|
7
|
+
# version which is part of the VNC authentication scheme.
|
8
|
+
#
|
9
|
+
# Usage is pretty straight forward:
|
10
|
+
#
|
11
|
+
# des = Cipher::DES.new 'mysecretkey', :encrypt
|
12
|
+
# str = des.update 'plain text'
|
13
|
+
# str << des.update 'more plain text'
|
14
|
+
# str << final
|
15
|
+
#
|
16
|
+
# Or just use the shortcut class methods:
|
17
|
+
#
|
18
|
+
# str = Cipher::DES.encrypt 'mysecretkey', 'plain text'
|
19
|
+
#
|
20
|
+
# = About
|
21
|
+
#
|
22
|
+
# This code was ported from the file "d3des.c", for portability reasons.
|
23
|
+
# It is not expected to be quick, but is only being used currently for the
|
24
|
+
# VNC authentication handshake. If you wanted to cipher a lot of text, you
|
25
|
+
# should probably compile the original C as an extension.
|
26
|
+
#
|
27
|
+
# I've included the following copyright info from the C source verbatim:
|
28
|
+
#
|
29
|
+
# This is D3DES (V5.09) by Richard Outerbridge with the double and
|
30
|
+
# triple-length support removed for use in VNC. Also the bytebit[] array
|
31
|
+
# has been reversed so that the most significant bit in each byte of the
|
32
|
+
# key is ignored, not the least significant.
|
33
|
+
#
|
34
|
+
# These changes are:
|
35
|
+
# Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
36
|
+
#
|
37
|
+
# This software is distributed in the hope that it will be useful,
|
38
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
39
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
40
|
+
#
|
41
|
+
# D3DES (V5.09)
|
42
|
+
#
|
43
|
+
# A portable, public domain, version of the Data Encryption Standard.
|
44
|
+
#
|
45
|
+
# Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
|
46
|
+
# Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
|
47
|
+
# code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
|
48
|
+
# Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
|
49
|
+
# for humouring me on.
|
50
|
+
#
|
51
|
+
# Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
|
52
|
+
# (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
|
53
|
+
#
|
54
|
+
class DES
|
55
|
+
BLOCK_SIZE = 8
|
56
|
+
|
57
|
+
attr_reader :key, :mode
|
58
|
+
|
59
|
+
# Create a des cipher object. +key+ should be cipher key to use, and +mode+ should
|
60
|
+
# be either <tt>:encrypt</tt> or <tt>:decrypt</tt>.
|
61
|
+
#
|
62
|
+
# It will expand +key+ to be 8 bytes by padding with null bytes. If it is longer than
|
63
|
+
# 8 bytes, the additional data is discarded.
|
64
|
+
def initialize key, mode
|
65
|
+
unless [:encrypt, :decrypt].include? mode
|
66
|
+
raise ArgumentError, 'invalid mode argument - %s' % mode
|
67
|
+
end
|
68
|
+
@mode = mode
|
69
|
+
|
70
|
+
# ensure key is 8 bytes. pad with nulls as needed
|
71
|
+
key = key[0, BLOCK_SIZE]
|
72
|
+
key << 0.chr * (BLOCK_SIZE - key.length)
|
73
|
+
@key = key
|
74
|
+
|
75
|
+
# now expand the key schedule
|
76
|
+
@keys = self.class.send :prepare_key_stage2, self.class.send(:prepare_key_stage1, key, mode)
|
77
|
+
|
78
|
+
# this internal buffer is used because we must process data in chunks of 8 bytes
|
79
|
+
@buf = ''
|
80
|
+
end
|
81
|
+
|
82
|
+
# This updates the cipher with +data+, returning any available ciphered output. The +data+ is
|
83
|
+
# processed in blocks of 8 bytes, so any residual is added to an internal buffer.
|
84
|
+
def update data
|
85
|
+
result = ''
|
86
|
+
data = @buf + data unless @buf.empty?
|
87
|
+
num_blocks, residual = data.length.divmod BLOCK_SIZE
|
88
|
+
num_blocks.times do |i|
|
89
|
+
block = data[i * BLOCK_SIZE, BLOCK_SIZE].unpack('N2')
|
90
|
+
result << self.class.send(:desfunc, block, @keys).pack('N2')
|
91
|
+
end
|
92
|
+
@buf = residual == 0 ? '' : data[-residual..-1]
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# This flushes the internal buffer by padding it out with null bytes, and doing a final
|
97
|
+
# DES round. Note that this means the ciphered text is always padded out to a multiple of
|
98
|
+
# 8 bytes.
|
99
|
+
def final
|
100
|
+
if @buf.empty?
|
101
|
+
''
|
102
|
+
else
|
103
|
+
update 0.chr * (BLOCK_SIZE - @buf.length)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# A shortcut method to create a cipher object using +key+, and fully encrypt +data+
|
108
|
+
def self.encrypt key, data
|
109
|
+
des = new key, :encrypt
|
110
|
+
des.update(data) << des.final
|
111
|
+
end
|
112
|
+
|
113
|
+
# A shortcut method to create a cipher object using +key+, and fully decrypt +data+
|
114
|
+
def self.decrypt key, data
|
115
|
+
des = new key, :decrypt
|
116
|
+
des.update(data) << des.final
|
117
|
+
end
|
118
|
+
|
119
|
+
class << self #:nodoc: all
|
120
|
+
BYTEBIT = [
|
121
|
+
01, 02, 04, 010, 020, 040, 0100, 0200
|
122
|
+
]
|
123
|
+
|
124
|
+
BIGBYTE = [
|
125
|
+
0x800000, 0x400000, 0x200000, 0x100000,
|
126
|
+
0x080000, 0x040000, 0x020000, 0x010000,
|
127
|
+
0x008000, 0x004000, 0x002000, 0x001000,
|
128
|
+
0x000800, 0x000400, 0x000200, 0x000100,
|
129
|
+
0x000080, 0x000040, 0x000020, 0x000010,
|
130
|
+
0x000008, 0x000004, 0x000002, 0x000001
|
131
|
+
]
|
132
|
+
|
133
|
+
# Use the key schedule specified in the Standard (ANSI X3.92-1981).
|
134
|
+
|
135
|
+
PC1 = [
|
136
|
+
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
|
137
|
+
9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
|
138
|
+
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
|
139
|
+
13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
|
140
|
+
]
|
141
|
+
|
142
|
+
TOTROT = [
|
143
|
+
1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
|
144
|
+
]
|
145
|
+
|
146
|
+
PC2 = [
|
147
|
+
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
|
148
|
+
22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
|
149
|
+
40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
|
150
|
+
43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
|
151
|
+
]
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
#
|
156
|
+
# Prepares +key+ to be used
|
157
|
+
#
|
158
|
+
# +key+:: String
|
159
|
+
# +mode+:: :encrypt or :decrypt
|
160
|
+
#
|
161
|
+
# Thanks to James Gillogly & Phil Karn!
|
162
|
+
#
|
163
|
+
# corresponds to "deskey"
|
164
|
+
#
|
165
|
+
def prepare_key_stage1 key, mode
|
166
|
+
pcr = [nil] * 56
|
167
|
+
kn = [nil] * 32
|
168
|
+
|
169
|
+
pc1m = (0...56).map do |j|
|
170
|
+
l = PC1[j]
|
171
|
+
m = l & 07
|
172
|
+
(key[l >> 3] & BYTEBIT[m]) != 0 ? 1 : 0;
|
173
|
+
end
|
174
|
+
|
175
|
+
16.times do |i|
|
176
|
+
m = mode == :encrypt ? i << 1 : (15 - i) << 1
|
177
|
+
n = m + 1
|
178
|
+
kn[m] = kn[n] = 0
|
179
|
+
28.times do |j|
|
180
|
+
l = (j + TOTROT[i]) % 28
|
181
|
+
pcr[j] = pc1m[l]
|
182
|
+
pcr[j + 28] = pc1m[l + 28]
|
183
|
+
end
|
184
|
+
24.times do |j|
|
185
|
+
kn[m] |= BIGBYTE[j] if pcr[PC2[j]] != 0
|
186
|
+
kn[n] |= BIGBYTE[j] if pcr[PC2[j+24]] != 0
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
kn
|
191
|
+
end
|
192
|
+
|
193
|
+
# corresponds to "cookey"
|
194
|
+
def prepare_key_stage2(raw1)
|
195
|
+
cook = []
|
196
|
+
|
197
|
+
16.times do |i|
|
198
|
+
a = raw1[i * 2 + 0]
|
199
|
+
b = raw1[i * 2 + 1]
|
200
|
+
x = (a & 0x00fc0000) << 6
|
201
|
+
x |= (a & 0x00000fc0) << 10
|
202
|
+
x |= (b & 0x00fc0000) >> 10
|
203
|
+
x |= (b & 0x00000fc0) >> 6
|
204
|
+
cook << x
|
205
|
+
x = (a & 0x0003f000) << 12
|
206
|
+
x |= (a & 0x0000003f) << 16
|
207
|
+
x |= (b & 0x0003f000) >> 4
|
208
|
+
x |= (b & 0x0000003f)
|
209
|
+
cook << x
|
210
|
+
end
|
211
|
+
|
212
|
+
cook
|
213
|
+
end
|
214
|
+
|
215
|
+
SP1 = [
|
216
|
+
0x01010400, 0x00000000, 0x00010000, 0x01010404,
|
217
|
+
0x01010004, 0x00010404, 0x00000004, 0x00010000,
|
218
|
+
0x00000400, 0x01010400, 0x01010404, 0x00000400,
|
219
|
+
0x01000404, 0x01010004, 0x01000000, 0x00000004,
|
220
|
+
0x00000404, 0x01000400, 0x01000400, 0x00010400,
|
221
|
+
0x00010400, 0x01010000, 0x01010000, 0x01000404,
|
222
|
+
0x00010004, 0x01000004, 0x01000004, 0x00010004,
|
223
|
+
0x00000000, 0x00000404, 0x00010404, 0x01000000,
|
224
|
+
0x00010000, 0x01010404, 0x00000004, 0x01010000,
|
225
|
+
0x01010400, 0x01000000, 0x01000000, 0x00000400,
|
226
|
+
0x01010004, 0x00010000, 0x00010400, 0x01000004,
|
227
|
+
0x00000400, 0x00000004, 0x01000404, 0x00010404,
|
228
|
+
0x01010404, 0x00010004, 0x01010000, 0x01000404,
|
229
|
+
0x01000004, 0x00000404, 0x00010404, 0x01010400,
|
230
|
+
0x00000404, 0x01000400, 0x01000400, 0x00000000,
|
231
|
+
0x00010004, 0x00010400, 0x00000000, 0x01010004
|
232
|
+
]
|
233
|
+
|
234
|
+
SP2 = [
|
235
|
+
0x80108020, 0x80008000, 0x00008000, 0x00108020,
|
236
|
+
0x00100000, 0x00000020, 0x80100020, 0x80008020,
|
237
|
+
0x80000020, 0x80108020, 0x80108000, 0x80000000,
|
238
|
+
0x80008000, 0x00100000, 0x00000020, 0x80100020,
|
239
|
+
0x00108000, 0x00100020, 0x80008020, 0x00000000,
|
240
|
+
0x80000000, 0x00008000, 0x00108020, 0x80100000,
|
241
|
+
0x00100020, 0x80000020, 0x00000000, 0x00108000,
|
242
|
+
0x00008020, 0x80108000, 0x80100000, 0x00008020,
|
243
|
+
0x00000000, 0x00108020, 0x80100020, 0x00100000,
|
244
|
+
0x80008020, 0x80100000, 0x80108000, 0x00008000,
|
245
|
+
0x80100000, 0x80008000, 0x00000020, 0x80108020,
|
246
|
+
0x00108020, 0x00000020, 0x00008000, 0x80000000,
|
247
|
+
0x00008020, 0x80108000, 0x00100000, 0x80000020,
|
248
|
+
0x00100020, 0x80008020, 0x80000020, 0x00100020,
|
249
|
+
0x00108000, 0x00000000, 0x80008000, 0x00008020,
|
250
|
+
0x80000000, 0x80100020, 0x80108020, 0x00108000
|
251
|
+
]
|
252
|
+
|
253
|
+
SP3 = [
|
254
|
+
0x00000208, 0x08020200, 0x00000000, 0x08020008,
|
255
|
+
0x08000200, 0x00000000, 0x00020208, 0x08000200,
|
256
|
+
0x00020008, 0x08000008, 0x08000008, 0x00020000,
|
257
|
+
0x08020208, 0x00020008, 0x08020000, 0x00000208,
|
258
|
+
0x08000000, 0x00000008, 0x08020200, 0x00000200,
|
259
|
+
0x00020200, 0x08020000, 0x08020008, 0x00020208,
|
260
|
+
0x08000208, 0x00020200, 0x00020000, 0x08000208,
|
261
|
+
0x00000008, 0x08020208, 0x00000200, 0x08000000,
|
262
|
+
0x08020200, 0x08000000, 0x00020008, 0x00000208,
|
263
|
+
0x00020000, 0x08020200, 0x08000200, 0x00000000,
|
264
|
+
0x00000200, 0x00020008, 0x08020208, 0x08000200,
|
265
|
+
0x08000008, 0x00000200, 0x00000000, 0x08020008,
|
266
|
+
0x08000208, 0x00020000, 0x08000000, 0x08020208,
|
267
|
+
0x00000008, 0x00020208, 0x00020200, 0x08000008,
|
268
|
+
0x08020000, 0x08000208, 0x00000208, 0x08020000,
|
269
|
+
0x00020208, 0x00000008, 0x08020008, 0x00020200
|
270
|
+
]
|
271
|
+
|
272
|
+
SP4 = [
|
273
|
+
0x00802001, 0x00002081, 0x00002081, 0x00000080,
|
274
|
+
0x00802080, 0x00800081, 0x00800001, 0x00002001,
|
275
|
+
0x00000000, 0x00802000, 0x00802000, 0x00802081,
|
276
|
+
0x00000081, 0x00000000, 0x00800080, 0x00800001,
|
277
|
+
0x00000001, 0x00002000, 0x00800000, 0x00802001,
|
278
|
+
0x00000080, 0x00800000, 0x00002001, 0x00002080,
|
279
|
+
0x00800081, 0x00000001, 0x00002080, 0x00800080,
|
280
|
+
0x00002000, 0x00802080, 0x00802081, 0x00000081,
|
281
|
+
0x00800080, 0x00800001, 0x00802000, 0x00802081,
|
282
|
+
0x00000081, 0x00000000, 0x00000000, 0x00802000,
|
283
|
+
0x00002080, 0x00800080, 0x00800081, 0x00000001,
|
284
|
+
0x00802001, 0x00002081, 0x00002081, 0x00000080,
|
285
|
+
0x00802081, 0x00000081, 0x00000001, 0x00002000,
|
286
|
+
0x00800001, 0x00002001, 0x00802080, 0x00800081,
|
287
|
+
0x00002001, 0x00002080, 0x00800000, 0x00802001,
|
288
|
+
0x00000080, 0x00800000, 0x00002000, 0x00802080
|
289
|
+
]
|
290
|
+
|
291
|
+
SP5 = [
|
292
|
+
0x00000100, 0x02080100, 0x02080000, 0x42000100,
|
293
|
+
0x00080000, 0x00000100, 0x40000000, 0x02080000,
|
294
|
+
0x40080100, 0x00080000, 0x02000100, 0x40080100,
|
295
|
+
0x42000100, 0x42080000, 0x00080100, 0x40000000,
|
296
|
+
0x02000000, 0x40080000, 0x40080000, 0x00000000,
|
297
|
+
0x40000100, 0x42080100, 0x42080100, 0x02000100,
|
298
|
+
0x42080000, 0x40000100, 0x00000000, 0x42000000,
|
299
|
+
0x02080100, 0x02000000, 0x42000000, 0x00080100,
|
300
|
+
0x00080000, 0x42000100, 0x00000100, 0x02000000,
|
301
|
+
0x40000000, 0x02080000, 0x42000100, 0x40080100,
|
302
|
+
0x02000100, 0x40000000, 0x42080000, 0x02080100,
|
303
|
+
0x40080100, 0x00000100, 0x02000000, 0x42080000,
|
304
|
+
0x42080100, 0x00080100, 0x42000000, 0x42080100,
|
305
|
+
0x02080000, 0x00000000, 0x40080000, 0x42000000,
|
306
|
+
0x00080100, 0x02000100, 0x40000100, 0x00080000,
|
307
|
+
0x00000000, 0x40080000, 0x02080100, 0x40000100
|
308
|
+
]
|
309
|
+
|
310
|
+
SP6 = [
|
311
|
+
0x20000010, 0x20400000, 0x00004000, 0x20404010,
|
312
|
+
0x20400000, 0x00000010, 0x20404010, 0x00400000,
|
313
|
+
0x20004000, 0x00404010, 0x00400000, 0x20000010,
|
314
|
+
0x00400010, 0x20004000, 0x20000000, 0x00004010,
|
315
|
+
0x00000000, 0x00400010, 0x20004010, 0x00004000,
|
316
|
+
0x00404000, 0x20004010, 0x00000010, 0x20400010,
|
317
|
+
0x20400010, 0x00000000, 0x00404010, 0x20404000,
|
318
|
+
0x00004010, 0x00404000, 0x20404000, 0x20000000,
|
319
|
+
0x20004000, 0x00000010, 0x20400010, 0x00404000,
|
320
|
+
0x20404010, 0x00400000, 0x00004010, 0x20000010,
|
321
|
+
0x00400000, 0x20004000, 0x20000000, 0x00004010,
|
322
|
+
0x20000010, 0x20404010, 0x00404000, 0x20400000,
|
323
|
+
0x00404010, 0x20404000, 0x00000000, 0x20400010,
|
324
|
+
0x00000010, 0x00004000, 0x20400000, 0x00404010,
|
325
|
+
0x00004000, 0x00400010, 0x20004010, 0x00000000,
|
326
|
+
0x20404000, 0x20000000, 0x00400010, 0x20004010
|
327
|
+
]
|
328
|
+
|
329
|
+
SP7 = [
|
330
|
+
0x00200000, 0x04200002, 0x04000802, 0x00000000,
|
331
|
+
0x00000800, 0x04000802, 0x00200802, 0x04200800,
|
332
|
+
0x04200802, 0x00200000, 0x00000000, 0x04000002,
|
333
|
+
0x00000002, 0x04000000, 0x04200002, 0x00000802,
|
334
|
+
0x04000800, 0x00200802, 0x00200002, 0x04000800,
|
335
|
+
0x04000002, 0x04200000, 0x04200800, 0x00200002,
|
336
|
+
0x04200000, 0x00000800, 0x00000802, 0x04200802,
|
337
|
+
0x00200800, 0x00000002, 0x04000000, 0x00200800,
|
338
|
+
0x04000000, 0x00200800, 0x00200000, 0x04000802,
|
339
|
+
0x04000802, 0x04200002, 0x04200002, 0x00000002,
|
340
|
+
0x00200002, 0x04000000, 0x04000800, 0x00200000,
|
341
|
+
0x04200800, 0x00000802, 0x00200802, 0x04200800,
|
342
|
+
0x00000802, 0x04000002, 0x04200802, 0x04200000,
|
343
|
+
0x00200800, 0x00000000, 0x00000002, 0x04200802,
|
344
|
+
0x00000000, 0x00200802, 0x04200000, 0x00000800,
|
345
|
+
0x04000002, 0x04000800, 0x00000800, 0x00200002
|
346
|
+
]
|
347
|
+
|
348
|
+
SP8 = [
|
349
|
+
0x10001040, 0x00001000, 0x00040000, 0x10041040,
|
350
|
+
0x10000000, 0x10001040, 0x00000040, 0x10000000,
|
351
|
+
0x00040040, 0x10040000, 0x10041040, 0x00041000,
|
352
|
+
0x10041000, 0x00041040, 0x00001000, 0x00000040,
|
353
|
+
0x10040000, 0x10000040, 0x10001000, 0x00001040,
|
354
|
+
0x00041000, 0x00040040, 0x10040040, 0x10041000,
|
355
|
+
0x00001040, 0x00000000, 0x00000000, 0x10040040,
|
356
|
+
0x10000040, 0x10001000, 0x00041040, 0x00040000,
|
357
|
+
0x00041040, 0x00040000, 0x10041000, 0x00001000,
|
358
|
+
0x00000040, 0x10040040, 0x00001000, 0x00041040,
|
359
|
+
0x10001000, 0x00000040, 0x10000040, 0x10040000,
|
360
|
+
0x10040040, 0x10000000, 0x00040000, 0x10001040,
|
361
|
+
0x00000000, 0x10041040, 0x00040040, 0x10000040,
|
362
|
+
0x10040000, 0x10001000, 0x10001040, 0x00000000,
|
363
|
+
0x10041040, 0x00041000, 0x00041000, 0x00001040,
|
364
|
+
0x00001040, 0x00040040, 0x10000000, 0x10041000
|
365
|
+
]
|
366
|
+
|
367
|
+
def desfunc block, keys
|
368
|
+
leftt = block[0]
|
369
|
+
right = block[1]
|
370
|
+
|
371
|
+
work = ((leftt >> 4) ^ right) & 0x0f0f0f0f
|
372
|
+
right ^= work
|
373
|
+
leftt ^= (work << 4)
|
374
|
+
work = ((leftt >> 16) ^ right) & 0x0000ffff
|
375
|
+
right ^= work
|
376
|
+
leftt ^= (work << 16)
|
377
|
+
work = ((right >> 2) ^ leftt) & 0x33333333
|
378
|
+
leftt ^= work
|
379
|
+
right ^= (work << 2)
|
380
|
+
work = ((right >> 8) ^ leftt) & 0x00ff00ff
|
381
|
+
leftt ^= work
|
382
|
+
right ^= (work << 8)
|
383
|
+
right = ((right << 1) | ((right >> 31) & 1)) & 0xffffffff
|
384
|
+
work = (leftt ^ right) & 0xaaaaaaaa
|
385
|
+
leftt ^= work
|
386
|
+
right ^= work
|
387
|
+
leftt = ((leftt << 1) | ((leftt >> 31) & 1)) & 0xffffffff
|
388
|
+
|
389
|
+
8.times do |i|
|
390
|
+
work = (right << 28) | (right >> 4)
|
391
|
+
work ^= keys[i * 4 + 0]
|
392
|
+
fval = SP7[ work & 0x3f]
|
393
|
+
fval |= SP5[(work >> 8) & 0x3f]
|
394
|
+
fval |= SP3[(work >> 16) & 0x3f]
|
395
|
+
fval |= SP1[(work >> 24) & 0x3f]
|
396
|
+
work = right ^ keys[i * 4 + 1]
|
397
|
+
fval |= SP8[ work & 0x3f]
|
398
|
+
fval |= SP6[(work >> 8) & 0x3f]
|
399
|
+
fval |= SP4[(work >> 16) & 0x3f]
|
400
|
+
fval |= SP2[(work >> 24) & 0x3f]
|
401
|
+
leftt ^= fval
|
402
|
+
work = (leftt << 28) | (leftt >> 4)
|
403
|
+
work ^= keys[i * 4 + 2]
|
404
|
+
fval = SP7[ work & 0x3f]
|
405
|
+
fval |= SP5[(work >> 8) & 0x3f]
|
406
|
+
fval |= SP3[(work >> 16) & 0x3f]
|
407
|
+
fval |= SP1[(work >> 24) & 0x3f]
|
408
|
+
work = leftt ^ keys[i * 4 + 3]
|
409
|
+
fval |= SP8[ work & 0x3f]
|
410
|
+
fval |= SP6[(work >> 8) & 0x3f]
|
411
|
+
fval |= SP4[(work >> 16) & 0x3f]
|
412
|
+
fval |= SP2[(work >> 24) & 0x3f]
|
413
|
+
right ^= fval
|
414
|
+
end
|
415
|
+
|
416
|
+
right = ((right << 31) | (right >> 1)) & 0xffffffff
|
417
|
+
work = (leftt ^ right) & 0xaaaaaaaa
|
418
|
+
leftt ^= work
|
419
|
+
right ^= work
|
420
|
+
leftt = ((leftt << 31) | (leftt >> 1)) & 0xffffffff
|
421
|
+
work = ((leftt >> 8) ^ right) & 0x00ff00ff
|
422
|
+
right ^= work
|
423
|
+
leftt ^= (work << 8)
|
424
|
+
work = ((leftt >> 2) ^ right) & 0x33333333
|
425
|
+
right ^= work
|
426
|
+
leftt ^= (work << 2)
|
427
|
+
work = ((right >> 16) ^ leftt) & 0x0000ffff
|
428
|
+
leftt ^= work
|
429
|
+
right ^= (work << 16)
|
430
|
+
work = ((right >> 4) ^ leftt) & 0x0f0f0f0f
|
431
|
+
leftt ^= work
|
432
|
+
right ^= (work << 4)
|
433
|
+
|
434
|
+
[right, leftt]
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
data/lib/net/vnc.rb
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'yaml'
|
3
|
+
require 'thread'
|
4
|
+
require 'cipher/des'
|
5
|
+
require 'net/vnc/version'
|
6
|
+
|
7
|
+
module Net
|
8
|
+
#
|
9
|
+
# The VNC class provides for simple rfb-protocol based control of
|
10
|
+
# a VNC server. This can be used, eg, to automate applications.
|
11
|
+
#
|
12
|
+
# Sample usage:
|
13
|
+
#
|
14
|
+
# # launch xclock on localhost. note that there is an xterm in the top-left
|
15
|
+
# Net::VNC.open 'localhost:0', :shared => true, :password => 'mypass' do |vnc|
|
16
|
+
# vnc.pointer_move 10, 10
|
17
|
+
# vnc.type 'xclock'
|
18
|
+
# vnc.key_press :return
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# = TODO
|
22
|
+
#
|
23
|
+
# * The server read loop seems a bit iffy. Not sure how best to do it.
|
24
|
+
# * Should probably be changed to be more of a lower-level protocol wrapping thing, with the
|
25
|
+
# actual VNCClient sitting on top of that. all it should do is read/write the packets over
|
26
|
+
# the socket.
|
27
|
+
#
|
28
|
+
class VNC
|
29
|
+
class PointerState
|
30
|
+
attr_reader :x, :y, :button
|
31
|
+
|
32
|
+
def initialize vnc
|
33
|
+
@x = @y = @button = 0
|
34
|
+
@vnc = vnc
|
35
|
+
end
|
36
|
+
|
37
|
+
# could have the same for x=, and y=
|
38
|
+
def button= button
|
39
|
+
@button = button
|
40
|
+
refresh
|
41
|
+
end
|
42
|
+
|
43
|
+
def update x, y, button=@button
|
44
|
+
@x, @y, @button = x, y, button
|
45
|
+
refresh
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh
|
49
|
+
packet = 0.chr * 6
|
50
|
+
packet[0] = 5
|
51
|
+
packet[1] = button
|
52
|
+
packet[2, 2] = [x].pack 'n'
|
53
|
+
packet[4, 2] = [y].pack 'n'
|
54
|
+
@vnc.socket.write packet
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
BASE_PORT = 5900
|
59
|
+
CHALLENGE_SIZE = 16
|
60
|
+
DEFAULT_OPTIONS = {
|
61
|
+
:shared => false,
|
62
|
+
:wait => 0.1
|
63
|
+
}
|
64
|
+
|
65
|
+
keys_file = File.dirname(__FILE__) + '/../../data/keys.yaml'
|
66
|
+
KEY_MAP = YAML.load_file(keys_file).inject({}) { |h, (k, v)| h.update k.to_sym => v }
|
67
|
+
def KEY_MAP.[] key
|
68
|
+
super or raise ArgumentError.new('Invalid key name - %s' % key)
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :server, :display, :options, :socket, :pointer
|
72
|
+
|
73
|
+
def initialize display=':0', options={}
|
74
|
+
@server = 'localhost'
|
75
|
+
if display =~ /^(.*)(:\d+)$/
|
76
|
+
@server, display = $1, $2
|
77
|
+
end
|
78
|
+
@display = display[1..-1].to_i
|
79
|
+
@options = DEFAULT_OPTIONS.merge options
|
80
|
+
@clipboard = nil
|
81
|
+
@pointer = PointerState.new self
|
82
|
+
@mutex = Mutex.new
|
83
|
+
connect
|
84
|
+
@packet_reading_state = nil
|
85
|
+
@packet_reading_thread = Thread.new { packet_reading_thread }
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.open display=':0', options={}
|
89
|
+
vnc = new display, options
|
90
|
+
if block_given?
|
91
|
+
begin
|
92
|
+
yield vnc
|
93
|
+
ensure
|
94
|
+
vnc.close
|
95
|
+
end
|
96
|
+
else
|
97
|
+
vnc
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def port
|
102
|
+
BASE_PORT + @display
|
103
|
+
end
|
104
|
+
|
105
|
+
def connect
|
106
|
+
@socket = TCPSocket.open server, port
|
107
|
+
unless socket.read(12) =~ /^RFB (\d{3}.\d{3})\n$/
|
108
|
+
raise 'invalid server response'
|
109
|
+
end
|
110
|
+
@server_version = $1
|
111
|
+
socket.write "RFB 003.003\n"
|
112
|
+
data = socket.read(4)
|
113
|
+
auth = data.to_s.unpack('N')[0]
|
114
|
+
case auth
|
115
|
+
when 0, nil
|
116
|
+
raise 'connection failed'
|
117
|
+
when 1
|
118
|
+
# ok...
|
119
|
+
when 2
|
120
|
+
password = @options[:password] or raise 'Need to authenticate but no password given'
|
121
|
+
challenge = socket.read CHALLENGE_SIZE
|
122
|
+
response = Cipher::DES.encrypt password, challenge
|
123
|
+
socket.write response
|
124
|
+
ok = socket.read(4).to_s.unpack('N')[0]
|
125
|
+
raise 'Unable to authenticate - %p' % ok unless ok == 0
|
126
|
+
else
|
127
|
+
raise 'Unknown authentication scheme - %d' % auth
|
128
|
+
end
|
129
|
+
|
130
|
+
# ClientInitialisation
|
131
|
+
socket.write((options[:shared] ? 1 : 0).chr)
|
132
|
+
|
133
|
+
# ServerInitialisation
|
134
|
+
# TODO: parse this.
|
135
|
+
socket.read(20)
|
136
|
+
data = socket.read(4)
|
137
|
+
# read this many bytes in chunks of 20
|
138
|
+
size = data.to_s.unpack('N')[0]
|
139
|
+
while size > 0
|
140
|
+
len = [20, size].min
|
141
|
+
# this is the hostname, and other stuff i think...
|
142
|
+
socket.read(len)
|
143
|
+
size -= len
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# this types +text+ on the server
|
148
|
+
def type text, options={}
|
149
|
+
packet = 0.chr * 8
|
150
|
+
packet[0] = 4
|
151
|
+
text.split(//).each do |char|
|
152
|
+
packet[7] = char[0]
|
153
|
+
packet[1] = 1
|
154
|
+
socket.write packet
|
155
|
+
packet[1] = 0
|
156
|
+
socket.write packet
|
157
|
+
end
|
158
|
+
wait options
|
159
|
+
end
|
160
|
+
|
161
|
+
# this takes an array of keys, and successively holds each down then lifts them up in
|
162
|
+
# reverse order.
|
163
|
+
# FIXME: should wait. can't recurse in that case.
|
164
|
+
def key_press(*args)
|
165
|
+
options = Hash === args.last ? args.pop : {}
|
166
|
+
keys = args
|
167
|
+
raise ArgumentError, 'Must have at least one key argument' if keys.empty?
|
168
|
+
begin
|
169
|
+
key_down keys.first
|
170
|
+
if keys.length == 1
|
171
|
+
yield if block_given?
|
172
|
+
else
|
173
|
+
key_press(*(keys[1..-1] + [options]))
|
174
|
+
end
|
175
|
+
ensure
|
176
|
+
key_up keys.first
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_key_code which
|
181
|
+
if String === which
|
182
|
+
if which.length != 1
|
183
|
+
raise ArgumentError, 'can only get key_code of single character strings'
|
184
|
+
end
|
185
|
+
which[0]
|
186
|
+
else
|
187
|
+
KEY_MAP[which]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
private :get_key_code
|
191
|
+
|
192
|
+
def key_down which, options={}
|
193
|
+
packet = 0.chr * 8
|
194
|
+
packet[0] = 4
|
195
|
+
key_code = get_key_code which
|
196
|
+
packet[4, 4] = [key_code].pack('N')
|
197
|
+
packet[1] = 1
|
198
|
+
socket.write packet
|
199
|
+
wait options
|
200
|
+
end
|
201
|
+
|
202
|
+
def key_up which, options={}
|
203
|
+
packet = 0.chr * 8
|
204
|
+
packet[0] = 4
|
205
|
+
key_code = get_key_code which
|
206
|
+
packet[4, 4] = [key_code].pack('N')
|
207
|
+
packet[1] = 0
|
208
|
+
socket.write packet
|
209
|
+
wait options
|
210
|
+
end
|
211
|
+
|
212
|
+
def pointer_move x, y, options={}
|
213
|
+
# options[:relative]
|
214
|
+
pointer.update x, y
|
215
|
+
wait options
|
216
|
+
end
|
217
|
+
|
218
|
+
BUTTON_MAP = {
|
219
|
+
:left => 0
|
220
|
+
}
|
221
|
+
|
222
|
+
def button_press button=:left, options={}
|
223
|
+
begin
|
224
|
+
button_down button, options
|
225
|
+
yield if block_given?
|
226
|
+
ensure
|
227
|
+
button_up button, options
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def button_down which=:left, options={}
|
232
|
+
button = BUTTON_MAP[which] || which
|
233
|
+
raise ArgumentError, 'Invalid button - %p' % which unless (0..2) === button
|
234
|
+
pointer.button |= 1 << button
|
235
|
+
wait options
|
236
|
+
end
|
237
|
+
|
238
|
+
def button_up which=:left, options={}
|
239
|
+
button = BUTTON_MAP[which] || which
|
240
|
+
raise ArgumentError, 'Invalid button - %p' % which unless (0..2) === button
|
241
|
+
pointer.button &= ~(1 << button)
|
242
|
+
wait options
|
243
|
+
end
|
244
|
+
|
245
|
+
def wait options={}
|
246
|
+
sleep options[:wait] || @options[:wait]
|
247
|
+
end
|
248
|
+
|
249
|
+
def close
|
250
|
+
# destroy packet reading thread
|
251
|
+
if @packet_reading_state == :loop
|
252
|
+
@packet_reading_state = :stop
|
253
|
+
while @packet_reading_state
|
254
|
+
# do nothing
|
255
|
+
end
|
256
|
+
end
|
257
|
+
socket.close
|
258
|
+
end
|
259
|
+
|
260
|
+
def clipboard
|
261
|
+
if block_given?
|
262
|
+
@clipboard = nil
|
263
|
+
yield
|
264
|
+
60.times do
|
265
|
+
clipboard = @mutex.synchronize { @clipboard }
|
266
|
+
return clipboard if clipboard
|
267
|
+
sleep 0.5
|
268
|
+
end
|
269
|
+
warn 'clipboard still empty after 30s'
|
270
|
+
nil
|
271
|
+
else
|
272
|
+
@mutex.synchronize { @clipboard }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def read_packet type
|
279
|
+
case type
|
280
|
+
when 3 # ServerCutText
|
281
|
+
socket.read 3 # discard padding bytes
|
282
|
+
len = socket.read(4).unpack('N')[0]
|
283
|
+
@mutex.synchronize { @clipboard = socket.read len }
|
284
|
+
else
|
285
|
+
raise NotImplementedError, 'unhandled server packet type - %d' % type
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def packet_reading_thread
|
290
|
+
@packet_reading_state = :loop
|
291
|
+
loop do
|
292
|
+
begin
|
293
|
+
break if @packet_reading_state != :loop
|
294
|
+
next unless IO.select [socket], nil, nil, 2
|
295
|
+
type = socket.read(1)[0]
|
296
|
+
read_packet type
|
297
|
+
rescue
|
298
|
+
warn "exception in packet_reading_thread: #{$!.class}:#{$!}"
|
299
|
+
break
|
300
|
+
end
|
301
|
+
end
|
302
|
+
@packet_reading_state = nil
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'cipher/des'
|
3
|
+
|
4
|
+
describe 'Cipher::DES' do
|
5
|
+
DES = Cipher::DES
|
6
|
+
|
7
|
+
DATA = [
|
8
|
+
['0xcafecafecafecafe', :encrypt, [
|
9
|
+
11571950, 6765055, 15777490, 16705475, 7667282, 16705355, 6747478, 16709450, 7259479,
|
10
|
+
16578410, 7323963, 16580202, 11502011, 16580218, 12520091, 12451450, 3759051, 13328317,
|
11
|
+
3242445, 14375869, 1142229, 13851581, 5598645, 13843389, 14132645, 15940541, 14386855,
|
12
|
+
7551935, 16356014, 7813567, 14794410, 12511175
|
13
|
+
], [
|
14
|
+
738924839, 154022719, 1010515743, 185740803, 490290973, 252849675, 420822813, 789982986,
|
15
|
+
454377245, 739708714, 453263161, 1010503466, 721829689, 943394618, 791293753, 807083834,
|
16
|
+
237974078, 353056061, 203896366, 386741565, 70726702, 370488637, 355873838, 372585277,
|
17
|
+
890649646, 975516477, 907680814, 942093119, 1040850214, 422458175, 943337247, 455749127
|
18
|
+
], [276147755, 314564801]],
|
19
|
+
['0xcafecafecafecafe', :decrypt, [
|
20
|
+
14794410, 12511175, 16356014, 7813567, 14386855, 7551935, 14132645, 15940541, 5598645,
|
21
|
+
13843389, 1142229, 13851581, 3242445, 14375869, 3759051, 13328317, 12520091, 12451450,
|
22
|
+
11502011, 16580218, 7323963, 16580202, 7259479, 16578410, 6747478, 16709450, 7667282,
|
23
|
+
16705355, 15777490, 16705475, 11571950, 6765055
|
24
|
+
], [
|
25
|
+
943337247, 455749127, 1040850214, 422458175, 907680814, 942093119, 890649646, 975516477,
|
26
|
+
355873838, 372585277, 70726702, 370488637, 203896366, 386741565, 237974078, 353056061,
|
27
|
+
791293753, 807083834, 721829689, 943394618, 453263161, 1010503466, 454377245, 739708714,
|
28
|
+
420822813, 789982986, 490290973, 252849675, 1010515743, 185740803, 738924839, 154022719
|
29
|
+
], [3868695016, 412139341]],
|
30
|
+
['0xdeadbeefdeadbeef', :encrypt, [
|
31
|
+
7466991, 16631125, 16695036, 16227052, 14614126, 5292027, 15400830, 4176957, 15531887,
|
32
|
+
7044594, 14942075, 3008831, 15597555, 15162582, 16252891, 13468671, 11426815, 16115512,
|
33
|
+
12548989, 7945806, 9404409, 16576702, 14647293, 2457327, 14680009, 12499187, 6029295,
|
34
|
+
10997623, 16383439, 2076626, 14630590, 13906815
|
35
|
+
], [
|
36
|
+
473906965, 506403861, 1060846891, 725367084, 926487615, 791546683, 977080112, 792607549,
|
37
|
+
993860151, 254752562, 943524644, 1060838975, 993999155, 523449622, 1027552015, 1058740287,
|
38
|
+
724516124, 624893496, 791486009, 926749454, 591347458, 926486334, 926878011, 926750511,
|
39
|
+
926887715, 1057565491, 373238077, 1060060215, 1043793727, 521091602, 926561549, 859702079
|
40
|
+
], [1755543026, 929731926]],
|
41
|
+
['0xdeadbeefdeadbeef', :decrypt, [
|
42
|
+
14630590, 13906815, 16383439, 2076626, 6029295, 10997623, 14680009, 12499187, 14647293,
|
43
|
+
2457327, 9404409, 16576702, 12548989, 7945806, 11426815, 16115512, 16252891, 13468671,
|
44
|
+
15597555, 15162582, 14942075, 3008831, 15531887, 7044594, 15400830, 4176957, 14614126,
|
45
|
+
5292027, 16695036, 16227052, 7466991, 16631125
|
46
|
+
], [
|
47
|
+
926561549, 859702079, 1043793727, 521091602, 373238077, 1060060215, 926887715, 1057565491,
|
48
|
+
926878011, 926750511, 591347458, 926486334, 791486009, 926749454, 724516124, 624893496,
|
49
|
+
1027552015, 1058740287, 993999155, 523449622, 943524644, 1060838975, 993860151, 254752562,
|
50
|
+
977080112, 792607549, 926487615, 791546683, 1060846891, 725367084, 473906965, 506403861
|
51
|
+
], [2531611598, 527835150]]
|
52
|
+
]
|
53
|
+
|
54
|
+
describe '(private class methods)' do
|
55
|
+
it 'can prepare keys for encryption and decryption' do
|
56
|
+
DATA.each do |hex_key, mode, stage1, stage2, expect|
|
57
|
+
key = [hex_key[2..-1]].pack('H*')
|
58
|
+
DES.send(:prepare_key_stage1, key, mode).should == stage1
|
59
|
+
DES.send(:prepare_key_stage2, stage1).should == stage2
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can prepare perform a DES round on a block of data' do
|
64
|
+
DATA.each do |hex_key, mode, stage1, stage2, expect|
|
65
|
+
DES.send(:desfunc, [0, 0], stage2).should == expect
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#initialize' do
|
71
|
+
it 'can create a DES object from a key and a mode' do
|
72
|
+
hex_key, mode, stage1, stage2, expect = DATA[0]
|
73
|
+
key = [hex_key[2..-1]].pack('H*')
|
74
|
+
des = DES.new key, mode
|
75
|
+
des.key.should == key
|
76
|
+
des.mode.should == mode
|
77
|
+
des.instance_variable_get(:@buf).should == ''
|
78
|
+
des.instance_variable_get(:@keys).should == stage2
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'will reject invalid modes' do
|
82
|
+
lambda { DES.new 'key', :encryptify }.should raise_error(ArgumentError)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'expands or truncates the key to 8 bytes' do
|
86
|
+
DES.new('my-really-long-key', :encrypt).key.should == 'my-reall'
|
87
|
+
DES.new('key', :encrypt).key.should == "key\000\000\000\000\000"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#update' do
|
92
|
+
before :each do
|
93
|
+
hex_key, mode, stage1, stage2, @expect = DATA[0]
|
94
|
+
key = [hex_key[2..-1]].pack('H*')
|
95
|
+
@des = DES.new key, mode
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'will return the data in ciphered form' do
|
99
|
+
@des.update([0, 0].pack('N2')).should == @expect.pack('N2')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'will store the residual in buffer' do
|
103
|
+
@des.update([0].pack('N')).should == ''
|
104
|
+
@des.instance_variable_get(:@buf).should == [0].pack('N')
|
105
|
+
@des.update([0].pack('N')).should == @expect.pack('N2')
|
106
|
+
@des.instance_variable_get(:@buf).should == ''
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#final' do
|
111
|
+
before :each do
|
112
|
+
hex_key, mode, stage1, stage2, @expect = DATA[0]
|
113
|
+
key = [hex_key[2..-1]].pack('H*')
|
114
|
+
@des = DES.new key, mode
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'will flush the buffer by padding with null bytes' do
|
118
|
+
@des.final.should == ''
|
119
|
+
@des.update([0].pack('N')).should == ''
|
120
|
+
@des.final.should == @expect.pack('N2')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '.encrypt' do
|
125
|
+
it 'is a shortcut class method for DES encryption' do
|
126
|
+
hex_key, mode, stage1, stage2, expect = DATA[0]
|
127
|
+
key = [hex_key[2..-1]].pack('H*')
|
128
|
+
mode.should == :encrypt
|
129
|
+
DES.encrypt(key, [0].pack('N')).should == expect.pack('N2')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '.decrypt' do
|
134
|
+
it 'is a shortcut class method for DES decryption' do
|
135
|
+
hex_key, mode, stage1, stage2, expect = DATA[1]
|
136
|
+
key = [hex_key[2..-1]].pack('H*')
|
137
|
+
mode.should == :decrypt
|
138
|
+
DES.decrypt(key, [0].pack('N')).should == expect.pack('N2')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'net/vnc'
|
3
|
+
|
4
|
+
=begin
|
5
|
+
class SocketMock
|
6
|
+
class MockIOError < IOError
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# this can be used to make detailed assertions
|
11
|
+
@trace = []
|
12
|
+
@read_buf = ''
|
13
|
+
@write_buf = ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def read len
|
17
|
+
@trace << [:read, len]
|
18
|
+
if @read_buf.length < len
|
19
|
+
msg = 'bad socket read sequence - read(%d) but only %d byte(s) available' % [len, @read_buf.length]
|
20
|
+
raise MockIOError, msg
|
21
|
+
end
|
22
|
+
@read_buf.slice! 0, len
|
23
|
+
end
|
24
|
+
|
25
|
+
def write data
|
26
|
+
@trace << [:write, data]
|
27
|
+
@write_buf << data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class VNCServerSocketMock < SocketMock
|
32
|
+
TICK_TIME = 0.1
|
33
|
+
|
34
|
+
def initialize(&block)
|
35
|
+
super
|
36
|
+
|
37
|
+
@pending_read = nil
|
38
|
+
obj = self
|
39
|
+
@t = Thread.new { block.call obj; @pending_read = nil }
|
40
|
+
100.times do |i|
|
41
|
+
break if @pending_read
|
42
|
+
if i == 99
|
43
|
+
msg = 'blah'
|
44
|
+
raise MockIOError, msg
|
45
|
+
end
|
46
|
+
sleep TICK_TIME
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
yield
|
52
|
+
100.times do |i|
|
53
|
+
break unless @pending_read
|
54
|
+
if i == 99
|
55
|
+
msg = 'missing socket write sequence'
|
56
|
+
raise MockIOError, msg
|
57
|
+
end
|
58
|
+
sleep TICK_TIME
|
59
|
+
end
|
60
|
+
raise 'wrote to much' if @write_buf.length != 0
|
61
|
+
raise 'did not read enough' if @read_buf.length != 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def read len
|
65
|
+
@trace << [:read, len]
|
66
|
+
100.times do |i|
|
67
|
+
break if @read_buf.length >= len
|
68
|
+
if i == 99
|
69
|
+
msg = 'timeout during socket read sequence - read(%d) but only %d byte(s) available' % [len, @read_buf.length]
|
70
|
+
raise MockIOError, msg
|
71
|
+
end
|
72
|
+
sleep TICK_TIME
|
73
|
+
end
|
74
|
+
@read_buf.slice! 0, len
|
75
|
+
end
|
76
|
+
|
77
|
+
def write data
|
78
|
+
unless @read_buf.empty?
|
79
|
+
raise MockIOError, 'tried to write with non empty read buffer - (%p, %p)' % [@read_buf, data]
|
80
|
+
end
|
81
|
+
super
|
82
|
+
if !@pending_read
|
83
|
+
raise MockIOError, "wrote to socket but server is not expecting it"
|
84
|
+
end
|
85
|
+
if @write_buf.length >= @pending_read
|
86
|
+
@pending_read = @write_buf.slice!(0, @pending_read)
|
87
|
+
sleep TICK_TIME while @pending_read.is_a? String
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def provide_data data
|
92
|
+
@read_buf << data
|
93
|
+
end
|
94
|
+
|
95
|
+
def expect_data len
|
96
|
+
@pending_read = len
|
97
|
+
sleep TICK_TIME while @pending_read.is_a? Fixnum
|
98
|
+
@pending_read
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'Net::VNC' do
|
103
|
+
VNC = Net::VNC
|
104
|
+
|
105
|
+
it 'should do something' do
|
106
|
+
=begin
|
107
|
+
socket_mock.should_receive(:read).once.ordered.with(12).and_return("RFB 003.003\n")
|
108
|
+
socket_mock.should_receive(:write).once.ordered.with(/^RFB (\d{3}.\d{3})\n$/)
|
109
|
+
socket_mock.should_receive(:read).once.ordered.with(4).and_return([1].pack('N'))
|
110
|
+
socket_mock.should_receive(:write).once.ordered.with("\000")
|
111
|
+
socket_mock.should_receive(:read).once.ordered.with(20).and_return('')
|
112
|
+
socket_mock.should_receive(:read).once.ordered.with(4).and_return([0].pack('N'))
|
113
|
+
#m = mock('my mock')
|
114
|
+
#m.should_receive(:test1).ordered.once.with('argument').and_return(1)
|
115
|
+
#m.should_receive(:test2).ordered.once.with('argument').and_return(2)
|
116
|
+
#p m.test1('argument')
|
117
|
+
#p m.test2('argument')
|
118
|
+
vnc = VNC.open('192.168.0.1:0')
|
119
|
+
#=end
|
120
|
+
|
121
|
+
server = VNCServerSocketMock.new do |s|
|
122
|
+
s.provide_data "RFB 003.003\n"
|
123
|
+
p :read => s.expect_data(12)
|
124
|
+
s.provide_data [1].pack('N')
|
125
|
+
p :read => s.expect_data(1)
|
126
|
+
s.provide_data ' ' * 20
|
127
|
+
s.provide_data [0].pack('N')
|
128
|
+
end
|
129
|
+
server.run do
|
130
|
+
TCPSocket.should_receive(:open).with('192.168.0.1', 5900).and_return(server)
|
131
|
+
vnc = VNC.open('192.168.0.1:0')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
=end
|
136
|
+
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-vnc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Charles Lowe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-29 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A library which implements the client VNC protocol to control VNC servers.
|
17
|
+
email: aquasync@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Rakefile
|
26
|
+
- README
|
27
|
+
- ChangeLog
|
28
|
+
- data/keys.yaml
|
29
|
+
- lib/cipher/des.rb
|
30
|
+
- lib/net/vnc/version.rb
|
31
|
+
- lib/net/vnc.rb
|
32
|
+
- spec/cipher_des_spec.rb
|
33
|
+
- spec/net_vnc_spec.rb
|
34
|
+
has_rdoc: true
|
35
|
+
homepage:
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README
|
40
|
+
- --title
|
41
|
+
- ruby-vnc documentation
|
42
|
+
- --tab-width
|
43
|
+
- "2"
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.2.0
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: Ruby VNC library.
|
65
|
+
test_files: []
|
66
|
+
|