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.
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-08-29
2
+
3
+ - First public release
4
+
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.
@@ -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
+
@@ -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
@@ -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
+
@@ -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,6 @@
1
+ module Net
2
+ class VNC
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
6
+
@@ -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
+