ether_shell 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -6
- data/Gemfile.lock +15 -9
- data/README.rdoc +6 -2
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/ether_shell.gemspec +16 -17
- data/lib/ether_shell.rb +0 -2
- data/lib/ether_shell/shell_dsl.rb +3 -1
- data/spec/ether_shell/shell_dsl_spec.rb +5 -5
- metadata +55 -31
- data/lib/ether_shell/high_socket.rb +0 -122
- data/lib/ether_shell/raw_socket.rb +0 -102
- data/spec/ether_shell/high_socket_spec.rb +0 -102
- data/spec/ether_shell/raw_socket_spec.rb +0 -41
data/Gemfile
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
source
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
# Add dependencies required to use your gem here.
|
3
3
|
# Example:
|
4
|
-
|
4
|
+
gem 'ethernet', '>= 0.1.0'
|
5
5
|
|
6
6
|
# Add dependencies to develop your gem here.
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
8
8
|
group :development do
|
9
|
-
gem
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
9
|
+
gem 'rdoc', '>= 3.6.1'
|
10
|
+
gem 'rspec', '~> 2.6.0'
|
11
|
+
gem 'bundler', '~> 1.0.0'
|
12
|
+
gem 'jeweler', '~> 1.5.2'
|
13
|
+
gem 'rcov', '>= 0'
|
13
14
|
end
|
data/Gemfile.lock
CHANGED
@@ -2,27 +2,33 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
diff-lcs (1.1.2)
|
5
|
+
ethernet (0.1.1)
|
6
|
+
system-getifaddrs (~> 0.1.1)
|
5
7
|
git (1.2.5)
|
6
8
|
jeweler (1.5.2)
|
7
9
|
bundler (~> 1.0.0)
|
8
10
|
git (>= 1.2.5)
|
9
11
|
rake
|
10
|
-
rake (0.
|
12
|
+
rake (0.9.0)
|
11
13
|
rcov (0.9.9)
|
12
|
-
|
13
|
-
|
14
|
-
rspec-
|
15
|
-
rspec-
|
16
|
-
|
17
|
-
rspec-
|
14
|
+
rdoc (3.6.1)
|
15
|
+
rspec (2.6.0)
|
16
|
+
rspec-core (~> 2.6.0)
|
17
|
+
rspec-expectations (~> 2.6.0)
|
18
|
+
rspec-mocks (~> 2.6.0)
|
19
|
+
rspec-core (2.6.3)
|
20
|
+
rspec-expectations (2.6.0)
|
18
21
|
diff-lcs (~> 1.1.2)
|
19
|
-
rspec-mocks (2.
|
22
|
+
rspec-mocks (2.6.0)
|
23
|
+
system-getifaddrs (0.1.1)
|
20
24
|
|
21
25
|
PLATFORMS
|
22
26
|
ruby
|
23
27
|
|
24
28
|
DEPENDENCIES
|
25
29
|
bundler (~> 1.0.0)
|
30
|
+
ethernet (>= 0.1.0)
|
26
31
|
jeweler (~> 1.5.2)
|
27
32
|
rcov
|
28
|
-
|
33
|
+
rdoc (>= 3.6.1)
|
34
|
+
rspec (~> 2.6.0)
|
data/README.rdoc
CHANGED
@@ -48,7 +48,11 @@ documetation. You're most likely interested in the spec for ShellDsl.
|
|
48
48
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
49
49
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
50
50
|
|
51
|
+
== Author
|
52
|
+
|
53
|
+
Victor Costan victor@costan.us
|
54
|
+
|
51
55
|
== Copyright
|
52
56
|
|
53
|
-
Copyright (c) 2011 Massachusetts Institute of Technology.
|
54
|
-
further details.
|
57
|
+
Copyright (c) 2011 Massachusetts Institute of Technology.
|
58
|
+
See LICENSE.txt for further details.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.1
|
data/ether_shell.gemspec
CHANGED
@@ -5,15 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ether_shell}
|
8
|
-
s.version = "0.9.
|
8
|
+
s.version = "0.9.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
13
|
-
s.default_executable = %q{ether_shell}
|
11
|
+
s.authors = [%q{Victor Costan}]
|
12
|
+
s.date = %q{2011-05-27}
|
14
13
|
s.description = %q{IRB session specialized for testing Ethernet devices}
|
15
14
|
s.email = %q{victor@costan.us}
|
16
|
-
s.executables = [
|
15
|
+
s.executables = [%q{ether_shell}]
|
17
16
|
s.extra_rdoc_files = [
|
18
17
|
"LICENSE.txt",
|
19
18
|
"README.rdoc"
|
@@ -32,11 +31,7 @@ Gem::Specification.new do |s|
|
|
32
31
|
"ether_shell.gemspec",
|
33
32
|
"lib/ether_shell.rb",
|
34
33
|
"lib/ether_shell/expectation_error.rb",
|
35
|
-
"lib/ether_shell/high_socket.rb",
|
36
|
-
"lib/ether_shell/raw_socket.rb",
|
37
34
|
"lib/ether_shell/shell_dsl.rb",
|
38
|
-
"spec/ether_shell/high_socket_spec.rb",
|
39
|
-
"spec/ether_shell/raw_socket_spec.rb",
|
40
35
|
"spec/ether_shell/shell_dsl_spec.rb",
|
41
36
|
"spec/ether_shell_spec.rb",
|
42
37
|
"spec/spec_helper.rb",
|
@@ -44,13 +39,11 @@ Gem::Specification.new do |s|
|
|
44
39
|
"spec/support/shell_stub.rb"
|
45
40
|
]
|
46
41
|
s.homepage = %q{http://github.com/pwnall/ether_shell}
|
47
|
-
s.licenses = [
|
48
|
-
s.require_paths = [
|
49
|
-
s.rubygems_version = %q{1.
|
42
|
+
s.licenses = [%q{MIT}]
|
43
|
+
s.require_paths = [%q{lib}]
|
44
|
+
s.rubygems_version = %q{1.8.4}
|
50
45
|
s.summary = %q{IRB session specialized for testing Ethernet devices}
|
51
46
|
s.test_files = [
|
52
|
-
"spec/ether_shell/high_socket_spec.rb",
|
53
|
-
"spec/ether_shell/raw_socket_spec.rb",
|
54
47
|
"spec/ether_shell/shell_dsl_spec.rb",
|
55
48
|
"spec/ether_shell_spec.rb",
|
56
49
|
"spec/spec_helper.rb",
|
@@ -62,20 +55,26 @@ Gem::Specification.new do |s|
|
|
62
55
|
s.specification_version = 3
|
63
56
|
|
64
57
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
-
s.
|
58
|
+
s.add_runtime_dependency(%q<ethernet>, [">= 0.1.0"])
|
59
|
+
s.add_development_dependency(%q<rdoc>, [">= 3.6.1"])
|
60
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
66
61
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
67
62
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
68
63
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
69
64
|
s.add_development_dependency(%q<rspec>, ["> 1.2.3"])
|
70
65
|
else
|
71
|
-
s.add_dependency(%q<
|
66
|
+
s.add_dependency(%q<ethernet>, [">= 0.1.0"])
|
67
|
+
s.add_dependency(%q<rdoc>, [">= 3.6.1"])
|
68
|
+
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
72
69
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
73
70
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
74
71
|
s.add_dependency(%q<rcov>, [">= 0"])
|
75
72
|
s.add_dependency(%q<rspec>, ["> 1.2.3"])
|
76
73
|
end
|
77
74
|
else
|
78
|
-
s.add_dependency(%q<
|
75
|
+
s.add_dependency(%q<ethernet>, [">= 0.1.0"])
|
76
|
+
s.add_dependency(%q<rdoc>, [">= 3.6.1"])
|
77
|
+
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
79
78
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
80
79
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
81
80
|
s.add_dependency(%q<rcov>, [">= 0"])
|
data/lib/ether_shell.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ethernet'
|
2
|
+
|
1
3
|
# :nodoc: namespace
|
2
4
|
module EtherShell
|
3
5
|
|
@@ -15,7 +17,7 @@ module ShellDsl
|
|
15
17
|
def connect(eth_device, ether_type, dest_mac)
|
16
18
|
raise "Already connected. did you forget to call disconnect?" if @_socket
|
17
19
|
mac_bytes = EtherShell::ShellDsl.parse_mac_data dest_mac
|
18
|
-
@_socket =
|
20
|
+
@_socket = Ethernet.socket eth_device, ether_type
|
19
21
|
@_socket.connect mac_bytes
|
20
22
|
if @_verbose
|
21
23
|
print ['Connected to ', mac_bytes.unpack('H*').first, ' using ',
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe 'ShellDsl' do
|
4
|
-
let(:eth_device) {
|
4
|
+
let(:eth_device) { Ethernet::Devices.all.sort.first }
|
5
5
|
let(:eth_type) { 0x0800 }
|
6
6
|
let(:eth_type_hex) { '0800' }
|
7
|
-
let(:mac) {
|
7
|
+
let(:mac) { Ethernet::Devices.mac eth_device }
|
8
8
|
let(:dest_mac) { "\x00\x11\x22\x33\x44\x55" }
|
9
9
|
let(:dest_mac_hex) { '001122334455' }
|
10
10
|
let(:bcast_mac) do
|
@@ -38,7 +38,7 @@ describe 'ShellDsl' do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'should not accept a socket again' do
|
41
|
-
raw_socket =
|
41
|
+
raw_socket = Ethernet.socket eth_device, eth_type
|
42
42
|
lambda {
|
43
43
|
shell.socket raw_socket
|
44
44
|
}.should raise_error(RuntimeError)
|
@@ -55,7 +55,7 @@ describe 'ShellDsl' do
|
|
55
55
|
|
56
56
|
describe 'connected to a live socket' do
|
57
57
|
let(:live_socket) do
|
58
|
-
socket =
|
58
|
+
socket = Ethernet.socket eth_device, eth_type
|
59
59
|
socket.connect bcast_mac
|
60
60
|
socket
|
61
61
|
end
|
@@ -90,7 +90,7 @@ describe 'ShellDsl' do
|
|
90
90
|
])
|
91
91
|
end
|
92
92
|
let(:stubbed_shell_socket) do
|
93
|
-
socket =
|
93
|
+
socket = Ethernet::FrameSocket.new socket_stub, eth_type, mac
|
94
94
|
socket.connect dest_mac
|
95
95
|
socket
|
96
96
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ether_shell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 57
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
9
|
+
- 1
|
10
|
+
version: 0.9.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Victor Costan
|
@@ -15,28 +15,59 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
19
|
-
default_executable: ether_shell
|
18
|
+
date: 2011-05-27 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
|
-
type: :
|
21
|
+
type: :runtime
|
23
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
23
|
none: false
|
25
24
|
requirements:
|
26
|
-
- -
|
25
|
+
- - ">="
|
27
26
|
- !ruby/object:Gem::Version
|
28
27
|
hash: 27
|
29
28
|
segments:
|
30
|
-
- 2
|
31
|
-
- 5
|
32
29
|
- 0
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
- 1
|
31
|
+
- 0
|
32
|
+
version: 0.1.0
|
36
33
|
prerelease: false
|
34
|
+
version_requirements: *id001
|
35
|
+
name: ethernet
|
37
36
|
- !ruby/object:Gem::Dependency
|
38
37
|
type: :development
|
39
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 29
|
44
|
+
segments:
|
45
|
+
- 3
|
46
|
+
- 6
|
47
|
+
- 1
|
48
|
+
version: 3.6.1
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: *id002
|
51
|
+
name: rdoc
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
type: :development
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 23
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 6
|
63
|
+
- 0
|
64
|
+
version: 2.6.0
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: *id003
|
67
|
+
name: rspec
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
type: :development
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
40
71
|
none: false
|
41
72
|
requirements:
|
42
73
|
- - ~>
|
@@ -47,12 +78,12 @@ dependencies:
|
|
47
78
|
- 0
|
48
79
|
- 0
|
49
80
|
version: 1.0.0
|
50
|
-
version_requirements: *id002
|
51
|
-
name: bundler
|
52
81
|
prerelease: false
|
82
|
+
version_requirements: *id004
|
83
|
+
name: bundler
|
53
84
|
- !ruby/object:Gem::Dependency
|
54
85
|
type: :development
|
55
|
-
requirement: &
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
56
87
|
none: false
|
57
88
|
requirements:
|
58
89
|
- - ~>
|
@@ -63,12 +94,12 @@ dependencies:
|
|
63
94
|
- 5
|
64
95
|
- 2
|
65
96
|
version: 1.5.2
|
66
|
-
version_requirements: *id003
|
67
|
-
name: jeweler
|
68
97
|
prerelease: false
|
98
|
+
version_requirements: *id005
|
99
|
+
name: jeweler
|
69
100
|
- !ruby/object:Gem::Dependency
|
70
101
|
type: :development
|
71
|
-
requirement: &
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
72
103
|
none: false
|
73
104
|
requirements:
|
74
105
|
- - ">="
|
@@ -77,12 +108,12 @@ dependencies:
|
|
77
108
|
segments:
|
78
109
|
- 0
|
79
110
|
version: "0"
|
80
|
-
version_requirements: *id004
|
81
|
-
name: rcov
|
82
111
|
prerelease: false
|
112
|
+
version_requirements: *id006
|
113
|
+
name: rcov
|
83
114
|
- !ruby/object:Gem::Dependency
|
84
115
|
type: :development
|
85
|
-
requirement: &
|
116
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
117
|
none: false
|
87
118
|
requirements:
|
88
119
|
- - ">"
|
@@ -93,9 +124,9 @@ dependencies:
|
|
93
124
|
- 2
|
94
125
|
- 3
|
95
126
|
version: 1.2.3
|
96
|
-
version_requirements: *id005
|
97
|
-
name: rspec
|
98
127
|
prerelease: false
|
128
|
+
version_requirements: *id007
|
129
|
+
name: rspec
|
99
130
|
description: IRB session specialized for testing Ethernet devices
|
100
131
|
email: victor@costan.us
|
101
132
|
executables:
|
@@ -119,17 +150,12 @@ files:
|
|
119
150
|
- ether_shell.gemspec
|
120
151
|
- lib/ether_shell.rb
|
121
152
|
- lib/ether_shell/expectation_error.rb
|
122
|
-
- lib/ether_shell/high_socket.rb
|
123
|
-
- lib/ether_shell/raw_socket.rb
|
124
153
|
- lib/ether_shell/shell_dsl.rb
|
125
|
-
- spec/ether_shell/high_socket_spec.rb
|
126
|
-
- spec/ether_shell/raw_socket_spec.rb
|
127
154
|
- spec/ether_shell/shell_dsl_spec.rb
|
128
155
|
- spec/ether_shell_spec.rb
|
129
156
|
- spec/spec_helper.rb
|
130
157
|
- spec/support/raw_socket_stub.rb
|
131
158
|
- spec/support/shell_stub.rb
|
132
|
-
has_rdoc: true
|
133
159
|
homepage: http://github.com/pwnall/ether_shell
|
134
160
|
licenses:
|
135
161
|
- MIT
|
@@ -159,13 +185,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
185
|
requirements: []
|
160
186
|
|
161
187
|
rubyforge_project:
|
162
|
-
rubygems_version: 1.
|
188
|
+
rubygems_version: 1.8.4
|
163
189
|
signing_key:
|
164
190
|
specification_version: 3
|
165
191
|
summary: IRB session specialized for testing Ethernet devices
|
166
192
|
test_files:
|
167
|
-
- spec/ether_shell/high_socket_spec.rb
|
168
|
-
- spec/ether_shell/raw_socket_spec.rb
|
169
193
|
- spec/ether_shell/shell_dsl_spec.rb
|
170
194
|
- spec/ether_shell_spec.rb
|
171
195
|
- spec/spec_helper.rb
|
@@ -1,122 +0,0 @@
|
|
1
|
-
# :nodoc: namespace
|
2
|
-
module EtherShell
|
3
|
-
|
4
|
-
# Wraps an Ethernet socket and abstracts away the Ethernet II frame.
|
5
|
-
class HighSocket
|
6
|
-
# Creates a wrapper around a raw Ethernet socket.
|
7
|
-
#
|
8
|
-
# Args:
|
9
|
-
# raw_socket_or_device:: a raw Ethernet socket or a string containing an
|
10
|
-
# Ethernet device name
|
11
|
-
# ether_type:: 2-byte Ethernet packet type number
|
12
|
-
# mac_address:: 6-byte MAC address for the Ethernet socket (optional if
|
13
|
-
# raw_socket_or_device is an Ethernet device name)
|
14
|
-
#
|
15
|
-
# Raises:
|
16
|
-
# RuntimeError:: if mac isn't exactly 6-bytes long
|
17
|
-
def initialize(raw_socket_or_device, ether_type, mac_address = nil)
|
18
|
-
check_mac mac_address if mac_address
|
19
|
-
|
20
|
-
if raw_socket_or_device.respond_to? :to_str
|
21
|
-
@source_mac = mac_address || RawSocket.mac(raw_socket_or_device)
|
22
|
-
@socket = RawSocket.socket raw_socket_or_device, ether_type
|
23
|
-
else
|
24
|
-
raise 'MAC address needed with raw socket' unless mac_address
|
25
|
-
@source_mac = mac_address.dup
|
26
|
-
@socket = raw_socket_or_device
|
27
|
-
end
|
28
|
-
|
29
|
-
@dest_mac = nil
|
30
|
-
@ether_type = [ether_type].pack('n')
|
31
|
-
end
|
32
|
-
|
33
|
-
# Sets the destination MAC address for future calls to send.
|
34
|
-
#
|
35
|
-
# Args:
|
36
|
-
# mac:: 6-byte MAC address for the Ethernet socket
|
37
|
-
#
|
38
|
-
# Raises:
|
39
|
-
# RuntimeError:: if mac isn't exactly 6-bytes long
|
40
|
-
def connect(mac_address)
|
41
|
-
check_mac mac_address
|
42
|
-
@dest_mac = mac_address
|
43
|
-
end
|
44
|
-
|
45
|
-
# Closes the underlying socket.
|
46
|
-
def close
|
47
|
-
@socket.close
|
48
|
-
end
|
49
|
-
|
50
|
-
# Sends an Ethernet II frame.
|
51
|
-
#
|
52
|
-
# Args:
|
53
|
-
# data:: the data bytes to be sent
|
54
|
-
#
|
55
|
-
# Raises:
|
56
|
-
# RuntimeError:: if connect wasn' previously called
|
57
|
-
def send(data, send_flags = 0)
|
58
|
-
raise "Not connected" unless @dest_mac
|
59
|
-
send_to @dest_mac, data, send_flags
|
60
|
-
end
|
61
|
-
|
62
|
-
# Sends an Ethernet II frame.
|
63
|
-
#
|
64
|
-
# Args:
|
65
|
-
# mac_address:: the destination MAC address
|
66
|
-
# data:: the data bytes to be sent
|
67
|
-
#
|
68
|
-
# Raises:
|
69
|
-
# RuntimeError:: if connect wasn' previously called
|
70
|
-
def send_to(mac_address, data, send_flags = 0)
|
71
|
-
check_mac mac_address
|
72
|
-
|
73
|
-
padding = (data.length < 46) ? "\0" * (46 - data.length) : ''
|
74
|
-
packet = [mac_address, @source_mac, @ether_type, data, padding].join
|
75
|
-
@socket.send packet, send_flags
|
76
|
-
end
|
77
|
-
|
78
|
-
# Receives an Ethernet II frame.
|
79
|
-
#
|
80
|
-
# Args:
|
81
|
-
# buffer_size:: optional maximum packet size argument passed to the raw
|
82
|
-
# socket's recv method
|
83
|
-
#
|
84
|
-
# Returns the data and the source MAC address in the frame.
|
85
|
-
#
|
86
|
-
# This will discard incoming frames that don't match the MAC address that the
|
87
|
-
# socket is connected to, or the Ethernet packet type.
|
88
|
-
def recv(buffer_size = 8192)
|
89
|
-
raise "Not connected" unless @dest_mac
|
90
|
-
loop do
|
91
|
-
data, mac_address = recv_from buffer_size
|
92
|
-
return data if @dest_mac == mac_address
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Receives an Ethernet II frame.
|
97
|
-
#
|
98
|
-
# Args:
|
99
|
-
# buffer_size:: optional maximum packet size argument passed to the raw
|
100
|
-
# socket's recv method
|
101
|
-
#
|
102
|
-
# Returns the data in the frame.
|
103
|
-
#
|
104
|
-
# This will discard incoming frames that don't match the MAC address that the
|
105
|
-
# socket is connected to, or the Ethernet packet type.
|
106
|
-
def recv_from(buffer_size = 8192)
|
107
|
-
loop do
|
108
|
-
packet = @socket.recv buffer_size
|
109
|
-
next unless packet[12, 2] == @ether_type
|
110
|
-
next unless packet[0, 6] == @source_mac
|
111
|
-
return packet[14..-1], packet[6, 6]
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Raises an exception if the given MAC address is invalid.
|
116
|
-
def check_mac(mac_address)
|
117
|
-
raise "Invalid MAC address" unless mac_address.length == 6
|
118
|
-
end
|
119
|
-
private :check_mac
|
120
|
-
end # class EtherShell::HighSocket
|
121
|
-
|
122
|
-
end # namespace EtherShell
|
@@ -1,102 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
|
3
|
-
# :nodoc: namespace
|
4
|
-
module EtherShell
|
5
|
-
|
6
|
-
# Low-level socket creation functionality.
|
7
|
-
module RawSocket
|
8
|
-
# A raw socket will receive all Ethernet frames, and send raw frames.
|
9
|
-
#
|
10
|
-
# Args:
|
11
|
-
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
12
|
-
# ether_type:: Ethernet protocol number
|
13
|
-
def self.socket(eth_device = nil, ether_type = nil)
|
14
|
-
ether_type ||= all_ethernet_protocols
|
15
|
-
socket = Socket.new raw_address_family, Socket::SOCK_RAW, htons(ether_type)
|
16
|
-
socket.setsockopt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
|
17
|
-
set_socket_eth_device(socket, eth_device, ether_type) if eth_device
|
18
|
-
socket
|
19
|
-
end
|
20
|
-
|
21
|
-
# The MAC address for an Ethernet card.
|
22
|
-
#
|
23
|
-
# Args:
|
24
|
-
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
25
|
-
def self.mac(eth_device)
|
26
|
-
case RUBY_PLATFORM
|
27
|
-
when /linux/
|
28
|
-
# /usr/include/net/if.h, structure ifreq
|
29
|
-
ifreq = [eth_device].pack 'a32'
|
30
|
-
# 0x8927 is SIOCGIFHWADDR in /usr/include/bits/ioctls.h
|
31
|
-
socket.ioctl 0x8927, ifreq
|
32
|
-
ifreq[18, 6]
|
33
|
-
else
|
34
|
-
raise "Unsupported platform #{RUBY_PLATFORM}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class <<self
|
39
|
-
# Sets the Ethernet interface and protocol type for a socket.
|
40
|
-
def set_socket_eth_device(socket, eth_device, ether_type)
|
41
|
-
case RUBY_PLATFORM
|
42
|
-
when /linux/
|
43
|
-
if_number = get_interface_number eth_device
|
44
|
-
# struct sockaddr_ll in /usr/include/linux/if_packet.h
|
45
|
-
socket_address = [raw_address_family, htons(ether_type), if_number,
|
46
|
-
0xFFFF, 0, 0, ""].pack 'SSISCCa8'
|
47
|
-
socket.bind socket_address
|
48
|
-
else
|
49
|
-
raise "Unsupported platform #{RUBY_PLATFORM}"
|
50
|
-
end
|
51
|
-
socket
|
52
|
-
end
|
53
|
-
private :set_socket_eth_device
|
54
|
-
|
55
|
-
# The interface number for an Ethernet interface.
|
56
|
-
def get_interface_number(eth_device)
|
57
|
-
case RUBY_PLATFORM
|
58
|
-
when /linux/
|
59
|
-
# /usr/include/net/if.h, structure ifreq
|
60
|
-
ifreq = [eth_device].pack 'a32'
|
61
|
-
# 0x8933 is SIOCGIFINDEX in /usr/include/bits/ioctls.h
|
62
|
-
socket.ioctl 0x8933, ifreq
|
63
|
-
ifreq[16, 4].unpack('I').first
|
64
|
-
else
|
65
|
-
raise "Unsupported platform #{RUBY_PLATFORM}"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
private :get_interface_number
|
69
|
-
|
70
|
-
# The protocol number for listening to all ethernet protocols.
|
71
|
-
def all_ethernet_protocols
|
72
|
-
case RUBY_PLATFORM
|
73
|
-
when /linux/
|
74
|
-
3
|
75
|
-
else
|
76
|
-
raise "Unsupported platform #{RUBY_PLATFORM}"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
private :all_ethernet_protocols
|
80
|
-
|
81
|
-
# The AF / PF number for raw sockets.
|
82
|
-
def raw_address_family
|
83
|
-
case RUBY_PLATFORM
|
84
|
-
when /linux/
|
85
|
-
17 # cat /usr/include/bits/socket.h | grep PF_PACKET
|
86
|
-
when /darwin/
|
87
|
-
18 # cat /usr/include/sys/socket.h | grep AF_LINK
|
88
|
-
else
|
89
|
-
raise "Unsupported platform #{RUBY_PLATFORM}"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
private :raw_address_family
|
93
|
-
|
94
|
-
# Converts a 16-bit integer from host-order to network-order.
|
95
|
-
def htons(short_integer)
|
96
|
-
[short_integer].pack('n').unpack('S').first
|
97
|
-
end
|
98
|
-
private :htons
|
99
|
-
end
|
100
|
-
end # module EtherShell::RawSocket
|
101
|
-
|
102
|
-
end # namespace EtherShell
|
@@ -1,102 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe 'HighSocket' do
|
4
|
-
let(:eth_device) { 'eth0' }
|
5
|
-
let(:eth_type) { 0x0800 }
|
6
|
-
let(:mac) { EtherShell::RawSocket.mac eth_device }
|
7
|
-
let(:dest_mac) { "\x00\x11\x22\x33\x44\x55" }
|
8
|
-
let(:bcast_mac) { "\xff" * 6 }
|
9
|
-
|
10
|
-
shared_examples_for 'a real socket' do
|
11
|
-
it 'should output a packet' do
|
12
|
-
@socket.send_to dest_mac, "\r\n"
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should receive some network noise' do
|
16
|
-
@socket.recv_from.first.should_not be_empty
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe 'on eth0' do
|
21
|
-
before { @socket = EtherShell::HighSocket.new eth_device, eth_type }
|
22
|
-
after { @socket.close }
|
23
|
-
|
24
|
-
it_should_behave_like 'a real socket'
|
25
|
-
end
|
26
|
-
|
27
|
-
describe 'from raw socket' do
|
28
|
-
before do
|
29
|
-
raw_socket = EtherShell::RawSocket.socket eth_device, eth_type
|
30
|
-
@socket = EtherShell::HighSocket.new raw_socket, eth_type, mac
|
31
|
-
end
|
32
|
-
after { @socket.close }
|
33
|
-
|
34
|
-
it_should_behave_like 'a real socket'
|
35
|
-
end
|
36
|
-
|
37
|
-
describe 'stubbed' do
|
38
|
-
let(:socket_stub) do
|
39
|
-
RawSocketStub.new([
|
40
|
-
[mac, dest_mac, "\x88\xB7", 'Wrong Ethernet type'].join,
|
41
|
-
[bcast_mac, dest_mac, [eth_type].pack('n'), 'Wrong dest MAC'].join,
|
42
|
-
[mac, bcast_mac, [eth_type].pack('n'), 'Bcast'].join,
|
43
|
-
[mac, dest_mac, [eth_type].pack('n'), 'Correct'].join,
|
44
|
-
])
|
45
|
-
end
|
46
|
-
let(:socket) { EtherShell::HighSocket.new socket_stub, eth_type, mac }
|
47
|
-
|
48
|
-
shared_examples_for 'after a small send call' do
|
49
|
-
it 'should send a single packet' do
|
50
|
-
socket_stub.sends.length.should == 1
|
51
|
-
end
|
52
|
-
it 'should pad the packet' do
|
53
|
-
socket_stub.sends.first.length.should == 60
|
54
|
-
end
|
55
|
-
it 'should assemble packet correctly in send' do
|
56
|
-
gold = [dest_mac, mac, [eth_type].pack('n'), 'Send data'].join
|
57
|
-
socket_stub.sends.first[0, gold.length].should == gold
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe 'send_to' do
|
62
|
-
before { socket.send_to dest_mac, 'Send data' }
|
63
|
-
it_should_behave_like 'after a small send call'
|
64
|
-
end
|
65
|
-
|
66
|
-
describe 'recv_from' do
|
67
|
-
it 'should filter down to the correct packet' do
|
68
|
-
socket.recv_from.should == ['Bcast', bcast_mac]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe 'unconnected' do
|
73
|
-
it 'should complain in recv' do
|
74
|
-
lambda { socket.recv }.should raise_error(RuntimeError)
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'should complain in send' do
|
78
|
-
lambda { socket.send 'Send data' }.should raise_error(RuntimeError)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
describe 'connected' do
|
83
|
-
before { socket.connect dest_mac }
|
84
|
-
|
85
|
-
describe 'send' do
|
86
|
-
before { socket.send 'Send data' }
|
87
|
-
it_should_behave_like 'after a small send call'
|
88
|
-
end
|
89
|
-
|
90
|
-
describe 'recv' do
|
91
|
-
it 'should filter down to the correct packet' do
|
92
|
-
socket.recv.should == 'Correct'
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'should delegate close' do
|
98
|
-
socket_stub.should_receive(:close).once
|
99
|
-
socket.close
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe 'RawSocket' do
|
4
|
-
let(:eth_device) { 'eth0' }
|
5
|
-
let(:mac) { EtherShell::RawSocket.mac eth_device }
|
6
|
-
|
7
|
-
describe 'mac' do
|
8
|
-
let(:golden_mac) do
|
9
|
-
hex_mac = `ifconfig #{eth_device}`[/HWaddr .*$/][7..-1]
|
10
|
-
[hex_mac.gsub(':', '').strip].pack('H*')
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'should have 6 bytes' do
|
14
|
-
mac.length.should == 6
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'should match ifconfig output' do
|
18
|
-
mac.should == golden_mac
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe 'socket' do
|
23
|
-
let(:eth_type) { 0x88B7 }
|
24
|
-
|
25
|
-
before { @socket = EtherShell::RawSocket.socket eth_device }
|
26
|
-
after { @socket.close }
|
27
|
-
|
28
|
-
it 'should be able to receive data' do
|
29
|
-
@socket.should respond_to(:recv)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should output a packet' do
|
33
|
-
packet = [mac, mac, [eth_type].pack('n'), "\r\n" * 32].join
|
34
|
-
@socket.send packet, 0
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should receive some network noise' do
|
38
|
-
@socket.recv(8192).should_not be_empty
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|