ruby-vnc 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|