vncrec 1.0.3 → 1.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3feee74e40b84f5e30207f99e01b2444f8f18d54
4
- data.tar.gz: 2da896794d70531e284c14b369e6f21c5c9085c1
3
+ metadata.gz: b496fdd1cf4345a163978a601c2f3defa77e4646
4
+ data.tar.gz: 205b7ca582a47c89da2e220d77fc5e6b4dc7f2b8
5
5
  SHA512:
6
- metadata.gz: 0c5ed155f4838c12c0b3a1b3288cd9b71c8526f6e04e2628187745b6bcd5ab02954e9a96816f3fa7cac5ac850345a31813ef116f173de96c949d3aef3100dcb5
7
- data.tar.gz: 1c7e9cd361f129e87c298caaebecb70526a6767d49a877356e80e4931001ad5adbe41f37219ea61456d7eed2bcd1ef8d201e9f7570a35bf2cd06f5d47b3413b6
6
+ metadata.gz: 8aeb8c8cd064a12dc28897625cefd801559fd1b920c7afe1a9970848388fd17d8e0473a8723b8407e329cd7d16abdff1a153dd7bcc50c54bc7099c01cbff2947
7
+ data.tar.gz: 6b3bcfb25adc7581a1a90c1a3b7103501a081329c473e05af99851f0338f1258f89fb583bede9a3f6684ae3c7fb2bdfb3162ad7e1226e6d5016355ec5b2a7775
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ##1.0.4
4
+
5
+ * Added authentication
6
+
7
+ ##1.0.3
8
+
3
9
  ##1.0.2
4
10
 
5
11
  * Fixed crash caused by sending `USR1` when `Writer` is not initialized
data/README.md CHANGED
@@ -4,6 +4,10 @@ VNCRec
4
4
  VNCRec is a gem that provides you
5
5
  tools to record VNC session.
6
6
 
7
+ ##Installation
8
+
9
+ `gem install vncrec`
10
+
7
11
  ##Usage
8
12
  There is a binary called `vncrec`, which is a recorder tool.
9
13
 
data/bin/vncrec CHANGED
@@ -64,6 +64,10 @@ OptionParser.new do |opts|
64
64
  opts.on('-s', '--server SERVER', 'Specify host address') do |s|
65
65
  options[:host] = s
66
66
  end
67
+
68
+ opts.on('--password PASSWORD', 'Specify password to use') do |s|
69
+ options[:auth] = [VNCRec::RFB::Authentication::VncAuthentication, s]
70
+ end
67
71
  end.parse!
68
72
 
69
73
  v = VNCRec::Recorder.new(options)
data/examples/auth.rb ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ # Authentication example.
5
+ # Currently only VNC Authentication type is supported.
6
+ # This type of auth should not be used over untrusted networks,
7
+ # because it uses DES in ECB mode.
8
+ #
9
+ # auth option is an Array. Either
10
+ # [VNCRec::RFB::Authentication::None] (default) or
11
+ # [VNCRec::RFB::Authentication::VncAuthentication, 'mypassword']
12
+
13
+ require 'vncrec'
14
+
15
+ VNCRec::Recorder.new(
16
+ encoding: VNCRec::EncHextile,
17
+ filename: 'file.mp4',
18
+ auth: [VNCRec::RFB::Authentication::VncAuthentication, 'mypassword']
19
+ )
20
+
@@ -13,6 +13,7 @@ end
13
13
 
14
14
  require 'vncrec/constants.rb'
15
15
  require 'vncrec/rfb/proxy.rb'
16
+ require 'vncrec/rfb/authentication.rb'
16
17
  require 'vncrec/writers.rb'
17
18
 
18
19
  module VNCRec
@@ -20,13 +21,14 @@ module VNCRec
20
21
 
21
22
  class Recorder
22
23
  DEFAULTS = {
23
- :pix_fmt => :BGR8,
24
- :debug => nil,
25
- :encoding => VNCRec::ENC_RAW,
26
- :filename => nil,
27
- :fps => 6,
28
- :input => nil,
29
- :port => 5900
24
+ :pix_fmt => :BGR8,
25
+ :debug => nil,
26
+ :encoding => VNCRec::ENC_RAW,
27
+ :filename => nil,
28
+ :fps => 6,
29
+ :input => nil,
30
+ :port => 5900,
31
+ :auth => [VNCRec::RFB::Authentication::None]
30
32
  }
31
33
 
32
34
  # @param geometry [String] geometry of the screen area to capture(+x,y offset is not implemented yet)
@@ -62,6 +64,9 @@ module VNCRec
62
64
 
63
65
  @client = nil
64
66
 
67
+ @auth = options[:auth]
68
+ @password = options[:password]
69
+
65
70
  @framerate = options[:fps]
66
71
  fail ArgumentError if !@framerate.is_a?(Numeric) || @framerate <= 0
67
72
 
@@ -164,7 +169,7 @@ module VNCRec
164
169
  @logger.info 'got client' if @logging
165
170
 
166
171
  end
167
- @client = VNCRec::RFB::Proxy.new(@server, 'RFB 003.008', @enc, @pix_fmt)
172
+ @client = VNCRec::RFB::Proxy.new(@server, 'RFB 003.008', @enc, @pix_fmt, @auth)
168
173
  @recording_starttime = Time.now if @filename.include?('DATE')
169
174
 
170
175
  w, h, name = @client.handshake
@@ -0,0 +1,180 @@
1
+ module VNCRec
2
+ module RFB
3
+ module Authentication
4
+ class Abstract
5
+ def initialize(io, *args)
6
+ @io = io
7
+ end
8
+
9
+ def to_msg
10
+ [@code].pack('C')
11
+ end
12
+
13
+ def handshake
14
+ types = get_security_types
15
+ handle_types(types)
16
+ send_type
17
+ perform_authentication
18
+ security_result
19
+ end
20
+
21
+ # Once the protocol version has been decided, the server and client
22
+ # must agree on the type of security to be used on the connection. The
23
+ # server lists the security types that it supports:
24
+ #
25
+ # +--------------------------+-------------+--------------------------+
26
+ # | No. of bytes | Type | Description |
27
+ # | | [Value] | |
28
+ # +--------------------------+-------------+--------------------------+
29
+ # | 1 | U8 | number-of-security-types |
30
+ # | number-of-security-types | U8 array | security-types |
31
+ # +--------------------------+-------------+--------------------------+
32
+ # If the server listed at least one valid security type supported by
33
+ # the client, the client sends back a single byte indicating which
34
+ # security type is to be used on the connection:
35
+ #
36
+ # +--------------+--------------+---------------+
37
+ # | No. of bytes | Type [Value] | Description |
38
+ # +--------------+--------------+---------------+
39
+ # | 1 | U8 | security-type |
40
+ # +--------------+--------------+---------------+
41
+ #
42
+ # If number-of-security-types is zero, then for some reason the
43
+ # connection failed (e.g., the server cannot support the desired
44
+ # protocol version). This is followed by a string describing the
45
+ # reason (where a string is specified as a length followed by that many
46
+ # ASCII characters):
47
+ #
48
+ # +---------------+--------------+---------------+
49
+ # | No. of bytes | Type [Value] | Description |
50
+ # +---------------+--------------+---------------+
51
+ # | 4 | U32 | reason-length |
52
+ # | reason-length | U8 array | reason-string |
53
+ # +---------------+--------------+---------------+
54
+ #
55
+ # The server closes the connection after sending the reason-string.
56
+ # @return [Integer] types
57
+ def get_security_types
58
+ num_of_st = @io.readbyte
59
+ if num_of_st == 0 # failed
60
+ reason_len = @io.readpartial(4).unpack('L>')[0]
61
+ reason = @io.readpartial(reason_len)
62
+ raise reason
63
+ else
64
+ result = []
65
+ num_of_st.times do
66
+ result << @io.readbyte
67
+ end
68
+ result
69
+ end
70
+ end
71
+
72
+ def handle_types(types)
73
+ raise 'The server does not support requested auth method' unless types.include? @code
74
+ end
75
+
76
+ # If the server listed at least one valid security type supported by
77
+ # the client, the client sends back a single byte indicating which
78
+ # security type is to be used on the connection.
79
+ def send_type
80
+ @io.syswrite to_msg
81
+ end
82
+
83
+ # The server sends a word to inform the client whether the security
84
+ # handshaking was successful.
85
+ #
86
+ # +--------------+--------------+-------------+
87
+ # | No. of bytes | Type [Value] | Description |
88
+ # +--------------+--------------+-------------+
89
+ # | 4 | U32 | status: |
90
+ # | | 0 | OK |
91
+ # | | 1 | failed |
92
+ # +--------------+--------------+-------------+
93
+ #
94
+ # If unsuccessful, the server sends a string describing the reason for
95
+ # the failure, and then closes the connection:
96
+ #
97
+ # +---------------+--------------+---------------+
98
+ # | No. of bytes | Type [Value] | Description |
99
+ # +---------------+--------------+---------------+
100
+ # | 4 | U32 | reason-length |
101
+ # | reason-length | U8 array | reason-string |
102
+ # +---------------+--------------+---------------+
103
+ #
104
+ def security_result
105
+ word = (@io.readpartial 4).unpack('L>').first
106
+ if word != 0
107
+ reason_len = (@io.readpartial 4).unpack('L>').first
108
+ reason = @io.readpartial(reason_len)
109
+ raise reason
110
+ end
111
+ end
112
+
113
+ def perform_authentication
114
+ raise 'NI'
115
+ end
116
+ end
117
+
118
+ class None < Abstract
119
+ def initialize(io, *args)
120
+ super
121
+ @code = 1
122
+ end
123
+
124
+ def perform_authentication
125
+ end
126
+ end
127
+
128
+ class VncAuthentication < Abstract
129
+ def initialize(io, *args)
130
+ super
131
+ @code = 2
132
+ @password = args.first
133
+ end
134
+
135
+ # The server sends a random 16-byte challenge:
136
+ #
137
+ # +--------------+--------------+-------------+
138
+ # | No. of bytes | Type [Value] | Description |
139
+ # +--------------+--------------+-------------+
140
+ # | 16 | U8 | challenge |
141
+ # +--------------+--------------+-------------+
142
+ #
143
+ # The client encrypts the challenge with DES (ECB), using a password supplied
144
+ # by the user as the key. To form the key, the password is truncated
145
+ # to eight characters, or padded with null bytes on the right.
146
+ # Actually, each byte is also reversed. Challenge string is split
147
+ # in two chunks of 8 bytes, which are encrypted separately and clashed together
148
+ # again. The client then sends the resulting 16-byte response:
149
+ #
150
+ # +--------------+--------------+-------------+
151
+ # | No. of bytes | Type [Value] | Description |
152
+ # +--------------+--------------+-------------+
153
+ # | 16 | U8 | response |
154
+ # +--------------+--------------+-------------+
155
+ #
156
+ # The protocol continues with the SecurityResult message.
157
+ def perform_authentication
158
+ require 'openssl'
159
+
160
+ challenge = @io.readpartial(16)
161
+ split_challenge = [challenge.slice(0, 8), challenge.slice(8, 8)]
162
+
163
+ cipher = OpenSSL::Cipher::DES.new(:ECB)
164
+ cipher.encrypt
165
+ cipher.key = normalized_password
166
+ encrypted = split_challenge.reduce('') { |a, e| cipher.reset; a << cipher.update(e) }
167
+ @io.syswrite encrypted
168
+ end
169
+
170
+ private
171
+
172
+ def normalized_password
173
+ rev = ->(n) { (0...8).reduce(0) { |a, e| a + 2**e * n[7 - e] } }
174
+ inv = @password.each_byte.map { |b| rev[b].chr }.join
175
+ inv.ljust(8, "\x00")
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -18,15 +18,20 @@ module VNCRec
18
18
  # @param pf [Hash] pixel format:
19
19
  # * {VNCRec::PIX_FMT_BGR8} - 8 bits per pixel
20
20
  # * {VNCRec::PIX_FMT_BGR32} - 32 bits per pixel
21
- # @param w width of the screen area
22
- # @param h height of the screen area
23
- def initialize(io, rfbv, enc, pf)
21
+ # @param auth [Array]:
22
+ # * Constant class e.g. VNCRec::Authentication::VncAuthentication
23
+ # * optional argument, e.g. password string
24
+ def initialize(io, rfbv, enc, pf, auth)
24
25
  @io = io
25
26
  @version = rfbv
26
27
  @enc = enc
27
28
  @pf = pf
29
+ @auth = auth.first.new(@io, *auth.drop(1))
28
30
  end
29
31
 
32
+ # @param w width of the screen area
33
+ # @param h height of the screen area
34
+ # @param bpp bits per pixel
30
35
  def prepare_framebuffer(w, h, bpp)
31
36
  @w = w
32
37
  @h = h
@@ -43,24 +48,10 @@ module VNCRec
43
48
  version = @io.readpartial 12
44
49
  @io.syswrite(@version + "\n")
45
50
 
46
- # security
47
- num_of_st = @io.readbyte
48
- if num_of_st == 0 # failed
49
- reason_len = @io.readpartial(4).unpack('L>')[0]
50
- reason = @io.readpartial(reason_len)
51
- fail reason
52
- else
53
- num_of_st.times do
54
- @io.readbyte
55
- end
56
- end
57
-
58
- reply = [1].pack('C') # security type:none
59
- @io.syswrite reply
51
+ @auth.handshake
60
52
 
61
- stype = (@io.readpartial 4).unpack('H' * 8)
62
53
  # client init
63
- @io.syswrite reply
54
+ @io.syswrite "\x01"
64
55
 
65
56
  # server init
66
57
  w = @io.readpartial(2).unpack('S>')[0]
@@ -69,8 +60,6 @@ module VNCRec
69
60
  nlen = @io.readpartial(4).unpack('L>')[0]
70
61
  @name = @io.readpartial nlen
71
62
  return [w, h, @name]
72
- rescue
73
- return nil
74
63
  end
75
64
 
76
65
  # Set a way that server should use to represent pixel data
@@ -1,3 +1,3 @@
1
1
  module VNCRec
2
- VERSION = "1.0.3"
2
+ VERSION = "1.0.4"
3
3
  end
data/vncrec.gemspec CHANGED
@@ -28,6 +28,8 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'rspec'
29
29
  spec.add_development_dependency 'rspec-its'
30
30
  spec.add_development_dependency 'activesupport'
31
+ spec.add_development_dependency 'pry'
32
+ spec.add_development_dependency 'pry-byebug'
31
33
 
32
34
  spec.requirements << 'ffmpeg'
33
35
  spec.requirements << 'x11vnc'
metadata CHANGED
@@ -1,97 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vncrec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - d-theus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-05 00:00:00.000000000 Z
11
+ date: 2017-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.7'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake-compiler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec-its
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: activesupport
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
125
  description: |-
@@ -106,14 +134,15 @@ extensions:
106
134
  - ext/enchex_c/extconf.rb
107
135
  extra_rdoc_files: []
108
136
  files:
109
- - .gitignore
110
- - .rspec
137
+ - ".gitignore"
138
+ - ".rspec"
111
139
  - CHANGELOG.md
112
140
  - Gemfile
113
141
  - LICENSE.txt
114
142
  - README.md
115
143
  - Rakefile
116
144
  - bin/vncrec
145
+ - examples/auth.rb
117
146
  - examples/exit.rb
118
147
  - examples/mp4.rb
119
148
  - examples/mp4audio.rb
@@ -122,6 +151,7 @@ files:
122
151
  - lib/vncrec.rb
123
152
  - lib/vncrec/constants.rb
124
153
  - lib/vncrec/recorder.rb
154
+ - lib/vncrec/rfb/authentication.rb
125
155
  - lib/vncrec/rfb/enchex.rb
126
156
  - lib/vncrec/rfb/encraw.rb
127
157
  - lib/vncrec/rfb/enczrle.rb
@@ -143,19 +173,19 @@ require_paths:
143
173
  - lib
144
174
  required_ruby_version: !ruby/object:Gem::Requirement
145
175
  requirements:
146
- - - '>='
176
+ - - ">="
147
177
  - !ruby/object:Gem::Version
148
178
  version: '0'
149
179
  required_rubygems_version: !ruby/object:Gem::Requirement
150
180
  requirements:
151
- - - '>='
181
+ - - ">="
152
182
  - !ruby/object:Gem::Version
153
183
  version: '0'
154
184
  requirements:
155
185
  - ffmpeg
156
186
  - x11vnc
157
187
  rubyforge_project:
158
- rubygems_version: 2.0.14
188
+ rubygems_version: 2.4.5.2
159
189
  signing_key:
160
190
  specification_version: 4
161
191
  summary: VNC session recording