tracepath 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +19 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/docs/platforms.md +94 -0
- data/docs/releasing.md +16 -0
- data/docs/verification.md +116 -0
- data/exe/tracepath +8 -0
- data/lib/tracepath/cli/elevated.rb +27 -0
- data/lib/tracepath/cli/privilege_check.rb +65 -0
- data/lib/tracepath/cli/privileges.rb +76 -0
- data/lib/tracepath/cli/text_output.rb +122 -0
- data/lib/tracepath/cli.rb +254 -0
- data/lib/tracepath/errors.rb +33 -0
- data/lib/tracepath/models.rb +138 -0
- data/lib/tracepath/options.rb +89 -0
- data/lib/tracepath/packet.rb +433 -0
- data/lib/tracepath/pcap.rb +219 -0
- data/lib/tracepath/resolver.rb +82 -0
- data/lib/tracepath/runner.rb +248 -0
- data/lib/tracepath/transport.rb +705 -0
- data/lib/tracepath/version.rb +5 -0
- data/lib/tracepath.rb +110 -0
- metadata +126 -0
data/lib/tracepath.rb
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tracepath/version"
|
|
4
|
+
require_relative "tracepath/errors"
|
|
5
|
+
require_relative "tracepath/models"
|
|
6
|
+
require_relative "tracepath/options"
|
|
7
|
+
require_relative "tracepath/resolver"
|
|
8
|
+
require_relative "tracepath/packet"
|
|
9
|
+
require_relative "tracepath/transport"
|
|
10
|
+
require_relative "tracepath/runner"
|
|
11
|
+
|
|
12
|
+
module Tracepath
|
|
13
|
+
class << self
|
|
14
|
+
def trace(target, cancellation: nil, rate_limit: 50.0, on_event: nil, **options)
|
|
15
|
+
unless on_event.nil? || on_event.respond_to?(:call)
|
|
16
|
+
raise ArgumentError, "on_event must be callable"
|
|
17
|
+
end
|
|
18
|
+
request = target.is_a?(Request) ? target : Request.new(target)
|
|
19
|
+
settings = Options.new(**options.merge(request.options))
|
|
20
|
+
token = cancellation_token(cancellation)
|
|
21
|
+
limiter = RateLimiter.new(rate_limit)
|
|
22
|
+
events = Queue.new
|
|
23
|
+
progress = on_event && ->(event) { events << [:progress, event] }
|
|
24
|
+
worker = start_worker(events, token) do
|
|
25
|
+
result = Runner.new(request: request, options: settings, cancellation: token, limiter: limiter,
|
|
26
|
+
on_event: progress).run do |hop|
|
|
27
|
+
events << [:hop, hop]
|
|
28
|
+
end
|
|
29
|
+
events << [:result, result]
|
|
30
|
+
end
|
|
31
|
+
loop do
|
|
32
|
+
kind, value = events.pop
|
|
33
|
+
case kind
|
|
34
|
+
when :progress then on_event.call(value)
|
|
35
|
+
when :hop then yield(value) if block_given?
|
|
36
|
+
when :result then return value
|
|
37
|
+
when :exception then raise value
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
ensure
|
|
41
|
+
token&.cancel
|
|
42
|
+
worker&.join
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def trace_many(targets, concurrency: 4, cancellation: nil, rate_limit: 50.0, **options)
|
|
46
|
+
unless concurrency.is_a?(Integer) && (1..256).cover?(concurrency)
|
|
47
|
+
raise ArgumentError, "concurrency must be an integer in 1..256"
|
|
48
|
+
end
|
|
49
|
+
raise ArgumentError, "targets must be an enumerable of hostnames or Requests" unless targets.respond_to?(:map)
|
|
50
|
+
# Validate the entire plan before starting any worker or resolver.
|
|
51
|
+
Options.new(**options)
|
|
52
|
+
entries = targets.map do |target|
|
|
53
|
+
request = target.is_a?(Request) ? target : Request.new(target)
|
|
54
|
+
[request, Options.new(**options.merge(request.options))]
|
|
55
|
+
end.to_a
|
|
56
|
+
token = cancellation_token(cancellation)
|
|
57
|
+
limiter = RateLimiter.new(rate_limit)
|
|
58
|
+
started = Clock.now
|
|
59
|
+
results = Array.new(entries.length)
|
|
60
|
+
return BatchResult.new(results: results, duration: 0.0) if entries.empty?
|
|
61
|
+
events = Queue.new
|
|
62
|
+
work = Queue.new
|
|
63
|
+
entries.each_with_index { |entry, index| work << [entry, index] }
|
|
64
|
+
workers = []
|
|
65
|
+
[concurrency, entries.size].min.times do
|
|
66
|
+
workers << start_worker(events, token) do
|
|
67
|
+
loop do
|
|
68
|
+
item = begin
|
|
69
|
+
work.pop(true)
|
|
70
|
+
rescue ThreadError
|
|
71
|
+
break
|
|
72
|
+
end
|
|
73
|
+
(request, settings), index = item
|
|
74
|
+
result = Runner.new(request: request, options: settings, cancellation: token, limiter: limiter).run
|
|
75
|
+
events << [:result, result, index]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
entries.size.times do
|
|
80
|
+
kind, value, index = events.pop
|
|
81
|
+
raise value if kind == :exception
|
|
82
|
+
results[index] = value
|
|
83
|
+
yield(value, index) if block_given?
|
|
84
|
+
end
|
|
85
|
+
BatchResult.new(results: results, duration: Clock.now - started)
|
|
86
|
+
ensure
|
|
87
|
+
token&.cancel
|
|
88
|
+
workers&.each(&:join)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def cancellation_token(parent)
|
|
94
|
+
if !parent.nil? && !parent.is_a?(CancellationToken)
|
|
95
|
+
raise ArgumentError, "cancellation must be a Tracepath::CancellationToken"
|
|
96
|
+
end
|
|
97
|
+
CancellationToken.new(parent: parent)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def start_worker(events, token, &block)
|
|
101
|
+
Thread.new do
|
|
102
|
+
Thread.current.report_on_exception = false
|
|
103
|
+
block.call
|
|
104
|
+
rescue Exception => error # transfer the original exception to the caller thread
|
|
105
|
+
token.cancel
|
|
106
|
+
events << [:exception, error]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: tracepath
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tracepath contributors
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: fiddle
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.1'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '2'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '1.1'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '2'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: json
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '2.9'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '4'
|
|
42
|
+
type: :runtime
|
|
43
|
+
prerelease: false
|
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '2.9'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '4'
|
|
52
|
+
- !ruby/object:Gem::Dependency
|
|
53
|
+
name: resolv
|
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0.6'
|
|
59
|
+
- - "<"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0.6'
|
|
69
|
+
- - "<"
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '1'
|
|
72
|
+
description: IPv4/IPv6 UDP, ICMP and TCP SYN traceroute with immutable observations,
|
|
73
|
+
cancellation, and structured batch results.
|
|
74
|
+
executables:
|
|
75
|
+
- tracepath
|
|
76
|
+
extensions: []
|
|
77
|
+
extra_rdoc_files: []
|
|
78
|
+
files:
|
|
79
|
+
- CHANGELOG.md
|
|
80
|
+
- LICENSE
|
|
81
|
+
- README.md
|
|
82
|
+
- docs/platforms.md
|
|
83
|
+
- docs/releasing.md
|
|
84
|
+
- docs/verification.md
|
|
85
|
+
- exe/tracepath
|
|
86
|
+
- lib/tracepath.rb
|
|
87
|
+
- lib/tracepath/cli.rb
|
|
88
|
+
- lib/tracepath/cli/elevated.rb
|
|
89
|
+
- lib/tracepath/cli/privilege_check.rb
|
|
90
|
+
- lib/tracepath/cli/privileges.rb
|
|
91
|
+
- lib/tracepath/cli/text_output.rb
|
|
92
|
+
- lib/tracepath/errors.rb
|
|
93
|
+
- lib/tracepath/models.rb
|
|
94
|
+
- lib/tracepath/options.rb
|
|
95
|
+
- lib/tracepath/packet.rb
|
|
96
|
+
- lib/tracepath/pcap.rb
|
|
97
|
+
- lib/tracepath/resolver.rb
|
|
98
|
+
- lib/tracepath/runner.rb
|
|
99
|
+
- lib/tracepath/transport.rb
|
|
100
|
+
- lib/tracepath/version.rb
|
|
101
|
+
homepage: https://github.com/gatework/tracepath
|
|
102
|
+
licenses:
|
|
103
|
+
- MIT
|
|
104
|
+
metadata:
|
|
105
|
+
allowed_push_host: https://rubygems.org
|
|
106
|
+
rubygems_mfa_required: 'true'
|
|
107
|
+
source_code_uri: https://github.com/gatework/tracepath
|
|
108
|
+
changelog_uri: https://github.com/gatework/tracepath/blob/main/CHANGELOG.md
|
|
109
|
+
rdoc_options: []
|
|
110
|
+
require_paths:
|
|
111
|
+
- lib
|
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '3.4'
|
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '0'
|
|
122
|
+
requirements: []
|
|
123
|
+
rubygems_version: 4.0.20
|
|
124
|
+
specification_version: 4
|
|
125
|
+
summary: Native Ruby traceroute and bounded batch network diagnostics
|
|
126
|
+
test_files: []
|