netchk 0.0.1.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4af327a7ff918c38cb651ae7dbd06e6ce031e8f09f8f389972ecb2fa60caacfa
4
+ data.tar.gz: 54c330ab7509d734e4814ae8621324ba663f5014f868a35138f4768447be9f44
5
+ SHA512:
6
+ metadata.gz: c8e950c0eec0ee1c76378bcccda3c234f9e5f0288a94ad4aeabcc64a3e24f6e3f5f23a966d387f63086545e47552d892cafe9cc9477de5c2b0b0053e3e06a233
7
+ data.tar.gz: 6f301bf1526f13a0263624bbe33c3b65d3aa045adcceff2ff45bef2dc3a1bd41066ecf257b9332bc4e1da26099d4742359de4ef2bb3f143a08278c79430b52f2
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="NodePackageJsonFileManager">
4
+ <packageJsonPaths />
5
+ </component>
6
+ </project>
@@ -0,0 +1,162 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ChangeListManager">
4
+ <list default="true" id="2567b485-a0a4-477d-9983-3cf1b15b4c80" name="Default Changelist" comment="">
5
+ <change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
6
+ <change afterPath="$PROJECT_DIR$/.idea/$CACHE_FILE$" afterDir="false" />
7
+ <change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
8
+ <change afterPath="$PROJECT_DIR$/.travis.yml" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/Gemfile" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/LICENSE.txt" afterDir="false" />
11
+ <change afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
12
+ <change afterPath="$PROJECT_DIR$/Rakefile" afterDir="false" />
13
+ <change afterPath="$PROJECT_DIR$/bin/console" afterDir="false" />
14
+ <change afterPath="$PROJECT_DIR$/bin/netchk" afterDir="false" />
15
+ <change afterPath="$PROJECT_DIR$/bin/setup" afterDir="false" />
16
+ <change afterPath="$PROJECT_DIR$/lib/netchk.rb" afterDir="false" />
17
+ <change afterPath="$PROJECT_DIR$/lib/netchk/dns_resolv_verifier.rb" afterDir="false" />
18
+ <change afterPath="$PROJECT_DIR$/lib/netchk/dns_server_verifier.rb" afterDir="false" />
19
+ <change afterPath="$PROJECT_DIR$/lib/netchk/icmp.rb" afterDir="false" />
20
+ <change afterPath="$PROJECT_DIR$/lib/netchk/ip_verifier.rb" afterDir="false" />
21
+ <change afterPath="$PROJECT_DIR$/lib/netchk/tcp_ping.rb" afterDir="false" />
22
+ <change afterPath="$PROJECT_DIR$/lib/netchk/tcp_ping_verifier.rb" afterDir="false" />
23
+ <change afterPath="$PROJECT_DIR$/lib/netchk/version.rb" afterDir="false" />
24
+ <change afterPath="$PROJECT_DIR$/netchk.gemspec" afterDir="false" />
25
+ <change afterPath="$PROJECT_DIR$/test/netchk_test.rb" afterDir="false" />
26
+ <change afterPath="$PROJECT_DIR$/test/test_helper.rb" afterDir="false" />
27
+ </list>
28
+ <option name="SHOW_DIALOG" value="false" />
29
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
30
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
31
+ <option name="LAST_RESOLUTION" value="IGNORE" />
32
+ </component>
33
+ <component name="FileTemplateManagerImpl">
34
+ <option name="RECENT_TEMPLATES">
35
+ <list>
36
+ <option value="Ruby Class" />
37
+ <option value="Ruby File" />
38
+ </list>
39
+ </option>
40
+ </component>
41
+ <component name="Git.Settings">
42
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
43
+ </component>
44
+ <component name="ProjectId" id="1duf1zfYnsOoG9lsTWCRHRpTQmG" />
45
+ <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
46
+ <component name="ProjectViewState">
47
+ <option name="hideEmptyMiddlePackages" value="true" />
48
+ <option name="showLibraryContents" value="true" />
49
+ </component>
50
+ <component name="PropertiesComponent">
51
+ <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
52
+ <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
53
+ <property name="TERMINAL_CUSTOM_COMMANDS_GOT_IT" value="true" />
54
+ <property name="WebServerToolWindowFactoryState" value="false" />
55
+ <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
56
+ <property name="nodejs_npm_path_reset_for_default_project" value="true" />
57
+ <property name="nodejs_package_manager_path" value="npm" />
58
+ </component>
59
+ <component name="RecentsManager">
60
+ <key name="MoveFile.RECENT_KEYS">
61
+ <recent name="$PROJECT_DIR$/bin" />
62
+ </key>
63
+ </component>
64
+ <component name="RunManager">
65
+ <configuration name="netchk" type="RubyRunConfigurationType" factoryName="Ruby" temporary="true">
66
+ <module name="netchk" />
67
+ <RUBY_RUN_CONFIG NAME="RUBY_ARGS" VALUE="-I lib" />
68
+ <RUBY_RUN_CONFIG NAME="WORK DIR" VALUE="$MODULE_DIR$" />
69
+ <RUBY_RUN_CONFIG NAME="SHOULD_USE_SDK" VALUE="true" />
70
+ <RUBY_RUN_CONFIG NAME="ALTERN_SDK_NAME" VALUE="ruby-2.7.1-p83" />
71
+ <RUBY_RUN_CONFIG NAME="myPassParentEnvs" VALUE="true" />
72
+ <EXTENSION ID="BundlerRunConfigurationExtension" bundleExecEnabled="true" />
73
+ <EXTENSION ID="JRubyRunConfigurationExtension" NailgunExecEnabled="false" />
74
+ <EXTENSION ID="RubyCoverageRunConfigurationExtension" track_test_folders="true" runner="rcov">
75
+ <COVERAGE_PATTERN ENABLED="true">
76
+ <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
77
+ </COVERAGE_PATTERN>
78
+ </EXTENSION>
79
+ <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
80
+ <RUBY_RUN_CONFIG NAME="SCRIPT_PATH" VALUE="$MODULE_DIR$/bin/netchk" />
81
+ <RUBY_RUN_CONFIG NAME="SCRIPT_ARGS" VALUE="" />
82
+ <method v="2" />
83
+ </configuration>
84
+ <recent_temporary>
85
+ <list>
86
+ <item itemvalue="Ruby.netchk" />
87
+ </list>
88
+ </recent_temporary>
89
+ </component>
90
+ <component name="SpringUtil" SPRING_PRE_LOADER_OPTION="true" RAKE_SPRING_PRE_LOADER_OPTION="true" RAILS_SPRING_PRE_LOADER_OPTION="true" />
91
+ <component name="SvnConfiguration">
92
+ <configuration />
93
+ </component>
94
+ <component name="TaskManager">
95
+ <task active="true" id="Default" summary="Default task">
96
+ <changelist id="2567b485-a0a4-477d-9983-3cf1b15b4c80" name="Default Changelist" comment="" />
97
+ <created>1593279687801</created>
98
+ <option name="number" value="Default" />
99
+ <option name="presentableId" value="Default" />
100
+ <updated>1593279687801</updated>
101
+ <workItem from="1593279689105" duration="8923000" />
102
+ <workItem from="1593337253458" duration="6021000" />
103
+ </task>
104
+ <servers />
105
+ </component>
106
+ <component name="TypeScriptGeneratedFilesManager">
107
+ <option name="version" value="2" />
108
+ </component>
109
+ <component name="WindowStateProjectService">
110
+ <state x="744" y="462" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1593282010067">
111
+ <screen x="0" y="0" width="2560" height="1600" />
112
+ </state>
113
+ <state x="744" y="462" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.2560.1600@0.0.2560.1600" timestamp="1593282010067" />
114
+ <state x="1043" y="424" width="970" height="751" key="#xdebugger.evaluate" timestamp="1593282162463">
115
+ <screen x="0" y="0" width="2560" height="1600" />
116
+ </state>
117
+ <state x="1043" y="424" width="970" height="751" key="#xdebugger.evaluate/0.0.2560.1600@0.0.2560.1600" timestamp="1593282162463" />
118
+ <state width="2518" height="460" key="GridCell.Tab.0.bottom" timestamp="1593342482167">
119
+ <screen x="0" y="0" width="2560" height="1600" />
120
+ </state>
121
+ <state width="2518" height="460" key="GridCell.Tab.0.bottom/0.0.2560.1600@0.0.2560.1600" timestamp="1593342482167" />
122
+ <state width="2518" height="460" key="GridCell.Tab.0.bottom/0.23.2560.1577@0.23.2560.1577" timestamp="1593342310722" />
123
+ <state width="2518" height="460" key="GridCell.Tab.0.center" timestamp="1593342482166">
124
+ <screen x="0" y="0" width="2560" height="1600" />
125
+ </state>
126
+ <state width="2518" height="460" key="GridCell.Tab.0.center/0.0.2560.1600@0.0.2560.1600" timestamp="1593342482166" />
127
+ <state width="2518" height="460" key="GridCell.Tab.0.center/0.23.2560.1577@0.23.2560.1577" timestamp="1593342310721" />
128
+ <state width="2518" height="460" key="GridCell.Tab.0.left" timestamp="1593342482165">
129
+ <screen x="0" y="0" width="2560" height="1600" />
130
+ </state>
131
+ <state width="2518" height="460" key="GridCell.Tab.0.left/0.0.2560.1600@0.0.2560.1600" timestamp="1593342482165" />
132
+ <state width="2518" height="460" key="GridCell.Tab.0.left/0.23.2560.1577@0.23.2560.1577" timestamp="1593342310720" />
133
+ <state width="2518" height="460" key="GridCell.Tab.0.right" timestamp="1593342482167">
134
+ <screen x="0" y="0" width="2560" height="1600" />
135
+ </state>
136
+ <state width="2518" height="460" key="GridCell.Tab.0.right/0.0.2560.1600@0.0.2560.1600" timestamp="1593342482167" />
137
+ <state width="2518" height="460" key="GridCell.Tab.0.right/0.23.2560.1577@0.23.2560.1577" timestamp="1593342310721" />
138
+ <state width="2518" height="526" key="GridCell.Tab.1.bottom" timestamp="1593337783301">
139
+ <screen x="0" y="0" width="2560" height="1600" />
140
+ </state>
141
+ <state width="2518" height="526" key="GridCell.Tab.1.bottom/0.0.2560.1600@0.0.2560.1600" timestamp="1593337783301" />
142
+ <state width="2518" height="526" key="GridCell.Tab.1.center" timestamp="1593337783300">
143
+ <screen x="0" y="0" width="2560" height="1600" />
144
+ </state>
145
+ <state width="2518" height="526" key="GridCell.Tab.1.center/0.0.2560.1600@0.0.2560.1600" timestamp="1593337783300" />
146
+ <state width="2518" height="526" key="GridCell.Tab.1.left" timestamp="1593337783300">
147
+ <screen x="0" y="0" width="2560" height="1600" />
148
+ </state>
149
+ <state width="2518" height="526" key="GridCell.Tab.1.left/0.0.2560.1600@0.0.2560.1600" timestamp="1593337783300" />
150
+ <state width="2518" height="526" key="GridCell.Tab.1.right" timestamp="1593337783301">
151
+ <screen x="0" y="0" width="2560" height="1600" />
152
+ </state>
153
+ <state width="2518" height="526" key="GridCell.Tab.1.right/0.0.2560.1600@0.0.2560.1600" timestamp="1593337783301" />
154
+ <state x="945" y="371" width="670" height="676" key="search.everywhere.popup" timestamp="1593343800681">
155
+ <screen x="0" y="0" width="2560" height="1600" />
156
+ </state>
157
+ <state x="945" y="371" width="670" height="676" key="search.everywhere.popup/0.0.2560.1600@0.0.2560.1600" timestamp="1593343800681" />
158
+ </component>
159
+ <component name="com.intellij.coverage.CoverageDataManagerImpl">
160
+ <SUITE FILE_PATH="coverage/netchk@netchk.rcov" NAME="netchk Coverage Results" MODIFIED="1593342318395" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" MODULE_NAME="netchk" />
161
+ </component>
162
+ </project>
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Moray Baruh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # Netchk
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/netchk`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'netchk'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install netchk
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/netchk.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "netchk"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "netchk"
4
+ require 'netchk/ip_verifier'
5
+ require 'netchk/dns_server_verifier'
6
+ require 'netchk/dns_resolv_verifier'
7
+ require 'netchk/tcp_ping_verifier'
8
+
9
+ Netchk::IpVerifier.new.verify
10
+ Netchk::DNSServerVerifier.new.verify
11
+ Netchk::DNSResolvVerifier.new.verify
12
+ Netchk::TcpPingVerifier.new.verify
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ require "netchk/version"
2
+
3
+ module Netchk
4
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Netchk
3
+ class DNSResolvVerifier
4
+ def initialize
5
+ @domains = %w[google.com facebook.com]
6
+ end
7
+
8
+ def verify
9
+ ::Resolv::DNS.open do |dns|
10
+ @domains.each do |domain|
11
+ begin
12
+ dns.getaddress(domain)
13
+ rescue ::Resolv::ResolvError
14
+ $stderr.puts "Failed to resolve #{domain}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require 'resolv'
3
+ require 'socket'
4
+ require 'net/ping/tcp'
5
+
6
+ module Netchk
7
+ class DNSServerVerifier
8
+ def verify
9
+ nameservers = self.nameservers
10
+ if nameservers.empty?
11
+ $stderr.puts "No DNS server found. Verify your configuration."
12
+ else
13
+ puts "Using DNS servers #{nameservers.map { |ns| ns.join('#') }.join(', ')}"
14
+ nameservers.map do |ns|
15
+ verify_nameserver(*ns)
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+ private
22
+ def verify_nameserver(ip, port)
23
+ ping = Net::Ping::TCP.new(ip, port)
24
+ unless ping.ping?
25
+ $stderr.puts "Failed to ping DNS server #{ip}:#{port}"
26
+ end
27
+ end
28
+
29
+ def nameservers
30
+ # Dirty trick to get default nameserver list from /etc/resolv.conf
31
+ # without parsing the file manually.
32
+ ::Resolv::DNS.open do |dns|
33
+ dns.lazy_initialize
34
+ dns.instance_variable_get("@config").instance_variable_get("@nameserver_port")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,172 @@
1
+ require 'net/ping/ping'
2
+
3
+ if File::ALT_SEPARATOR
4
+ require 'win32/security'
5
+ end
6
+
7
+ # The Net module serves as a namespace only.
8
+ module Netchk
9
+
10
+ # The Net::Ping::ICMP class encapsulates an icmp ping.
11
+ class ICMP < ::Net::Ping
12
+ ICMP_ECHOREPLY = 0 # Echo reply
13
+ ICMP_ECHO = 8 # Echo request
14
+ ICMP_SUBCODE = 0
15
+
16
+ # You cannot set or change the port value. A value of 0 is always
17
+ # used internally for ICMP pings.
18
+ #
19
+ undef_method :port=
20
+
21
+ # Returns the data size, i.e. number of bytes sent on the ping. The
22
+ # default size is 56.
23
+ #
24
+ attr_reader :data_size
25
+
26
+ # Creates and returns a new Ping::ICMP object. This is similar to its
27
+ # superclass constructor, but must be created with root privileges (on
28
+ # UNIX systems), and the port value is ignored.
29
+ #
30
+ def initialize(host=nil, port=nil, timeout=5)
31
+ if File::ALT_SEPARATOR
32
+ unless Win32::Security.elevated_security?
33
+ raise 'requires elevated security'
34
+ end
35
+ end
36
+
37
+ @seq = 0
38
+ @bind_port = 0
39
+ @bind_host = nil
40
+ @data_size = 56
41
+ @data = ''
42
+
43
+ 0.upto(@data_size){ |n| @data << (n % 256).chr }
44
+
45
+ @ping_id = (Thread.current.object_id ^ Process.pid) & 0xffff
46
+
47
+ super(host, port, timeout)
48
+ @port = nil # This value is not used in ICMP pings.
49
+ end
50
+
51
+ # Sets the number of bytes sent in the ping method.
52
+ #
53
+ def data_size=(size)
54
+ @data_size = size
55
+ @data = ''
56
+ 0.upto(size){ |n| @data << (n % 256).chr }
57
+ end
58
+
59
+ # Associates the local end of the socket connection with the given
60
+ # +host+ and +port+. The default port is 0.
61
+ #
62
+ def bind(host, port = 0)
63
+ @bind_host = host
64
+ @bind_port = port
65
+ end
66
+
67
+ # Pings the +host+ specified in this method or in the constructor. If a
68
+ # host was not specified either here or in the constructor, an
69
+ # ArgumentError is raised.
70
+ #
71
+ def ping(host = @host)
72
+ super(host)
73
+ bool = false
74
+
75
+ socket = Socket.new(
76
+ Socket::PF_INET,
77
+ Socket::SOCK_DGRAM,
78
+ Socket::IPPROTO_ICMP
79
+ )
80
+
81
+ if @bind_host
82
+ saddr = Socket.pack_sockaddr_in(@bind_port, @bind_host)
83
+ socket.bind(saddr)
84
+ end
85
+
86
+ @seq = (@seq + 1) % 65536
87
+ pstring = 'C2 n3 A' << @data_size.to_s
88
+ timeout = @timeout
89
+
90
+ checksum = 0
91
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring)
92
+
93
+ checksum = checksum(msg)
94
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring)
95
+
96
+ begin
97
+ saddr = Socket.pack_sockaddr_in(0, host)
98
+ rescue Exception
99
+ socket.close unless socket.closed?
100
+ return bool
101
+ end
102
+
103
+ start_time = Time.now
104
+
105
+ socket.send(msg, 0, saddr) # Send the message
106
+
107
+ begin
108
+ Timeout.timeout(@timeout){
109
+ while true
110
+ io_array = select([socket], nil, nil, timeout)
111
+
112
+ if io_array.nil? || io_array[0].empty?
113
+ raise Timeout::Error if io_array.nil?
114
+ return false
115
+ end
116
+
117
+ ping_id = nil
118
+ seq = nil
119
+
120
+ data = socket.recvfrom(1500).first
121
+ type = data[20, 2].unpack('C2').first
122
+
123
+ case type
124
+ when ICMP_ECHOREPLY
125
+ if data.length >= 28
126
+ ping_id, seq = data[24, 4].unpack('n3')
127
+ end
128
+ else
129
+ if data.length > 56
130
+ ping_id, seq = data[52, 4].unpack('n3')
131
+ end
132
+ end
133
+
134
+ if ping_id == @ping_id && seq == @seq && type == ICMP_ECHOREPLY
135
+ bool = true
136
+ break
137
+ end
138
+ end
139
+ }
140
+ rescue Exception => err
141
+ @exception = err
142
+ ensure
143
+ socket.close if socket
144
+ end
145
+
146
+ # There is no duration if the ping failed
147
+ @duration = Time.now - start_time if bool
148
+ end
149
+
150
+ private
151
+
152
+ # Perform a checksum on the message. This is the sum of all the short
153
+ # words and it folds the high order bits into the low order bits.
154
+ #
155
+ def checksum(msg)
156
+ length = msg.length
157
+ num_short = length / 2
158
+ check = 0
159
+
160
+ msg.unpack("n#{num_short}").each do |short|
161
+ check += short
162
+ end
163
+
164
+ if length % 2 > 0
165
+ check += msg[length-1, 1].unpack('C').first << 8
166
+ end
167
+
168
+ check = (check >> 16) + (check & 0xffff)
169
+ return (~((check >> 16) + check) & 0xffff)
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+
4
+ module Netchk
5
+ class IpVerifier
6
+ def verify
7
+ socket = Socket.ip_address_list
8
+ addresses = socket.reject(&:ipv4_loopback?)
9
+ .reject(&:ipv6_loopback?)
10
+ .filter(&:ipv4?)
11
+ .map(&:inspect_sockaddr)
12
+
13
+ if addresses.empty?
14
+ $stderr.puts "No IPv4 address found. Verify your connection to your router."
15
+ else
16
+ puts "Found IP addresses #{addresses.join(', ')}."
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ module Netchk
3
+ def ping(host, count = 1, interval = 1, timeout = 5)
4
+
5
+ raise "Count must be an integer" unless count.is_a? Integer
6
+ raise "Timeout must be a number" unless timeout.is_a? Numeric
7
+
8
+ unless interval.is_a?(Numeric) && interval >= 0.2
9
+ raise "Interval must be a decimal greater than or equal to 0.2"
10
+ end
11
+
12
+ super(host)
13
+
14
+ pcmd = ['ping']
15
+
16
+ case RbConfig::CONFIG['host_os']
17
+ when /linux/i
18
+ pcmd += ['-c', count.to_s, '-W', timeout.to_s, host]
19
+ pcmd += ['-i', interval.to_s] unless RbConfig::CONFIG['busybox']
20
+ when /aix/i
21
+ pcmd += ['-c', count.to_s, '-w', timeout.to_s, host]
22
+ when /bsd|osx|mach|darwin/i
23
+ pcmd += ['-c', count.to_s, '-t', timeout.to_s, host]
24
+ when /solaris|sunos/i
25
+ pcmd += [host, timeout.to_s]
26
+ when /hpux/i
27
+ pcmd += [host, "-n#{count.to_s}", '-m', timeout.to_s]
28
+ when /win32|windows|msdos|mswin|cygwin|mingw/i
29
+ pcmd += ['-n', count.to_s, '-w', (timeout * 1000).to_s, host]
30
+ else
31
+ pcmd += [host]
32
+ end
33
+
34
+ begin
35
+ Open3.popen3(*pcmd) do |stdin, stdout, stderr, thread|
36
+ stdin.close
37
+ err = stderr.gets # Can't chomp yet, might be nil
38
+
39
+ case thread.value.exitstatus
40
+ when 0
41
+ info = stdout.read
42
+ if info =~ /unreachable/ix # Windows
43
+ raise StandardError, "Host unreachable"
44
+ else
45
+ # ignored
46
+ end
47
+
48
+ if err & (err =~ /warning/i)
49
+ @warning = err.chomp # TODO
50
+ end
51
+ when 2
52
+ raise StandardError, err&.chomp
53
+ else
54
+ if err
55
+ raise StandardError, err.chomp
56
+ else
57
+ stdout.each_line do |line|
58
+ if line =~ /(timed out|could not find host|packet loss)/i
59
+ @exception = line.chomp
60
+ raise StandardError, line.chomp
61
+ end
62
+ end
63
+ raise StandardError, "Unknown error"
64
+ end
65
+ end
66
+ end
67
+ rescue Exception => error
68
+ @exception = error.message
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'icmp'
3
+
4
+ class Array
5
+ def avg
6
+ reduce(:+) / count
7
+ end
8
+ end
9
+
10
+ module Netchk
11
+ class TcpPingVerifier
12
+ def initialize
13
+ @hosts = %w[1.1.1.1 8.8.8.8]
14
+ @count = 20
15
+ @interval = 0.2
16
+ end
17
+
18
+ def verify
19
+ stats = @hosts.map do |host|
20
+ host_stats = ping(host, @count)
21
+
22
+ average = host_stats[:durations].empty? ? "N/A" : (host_stats[:durations].avg * 1000).round(2)
23
+ errors = host_stats[:failures].to_f / (host_stats[:failures] + host_stats[:durations].count) * 100
24
+ puts "Stats for #{host} ping - average: #{average} ms, error rate: #{errors.round(2)}%"
25
+
26
+ [host, host_stats]
27
+ end.to_h
28
+
29
+ all_durations = stats.values.flat_map { |host_stats| host_stats[:durations] }
30
+ overall_average = all_durations.empty? ? "N/A" : (all_durations.avg * 1000).round(2)
31
+ overall_errors = stats.values.flat_map { |host_stats| host_stats[:failures].to_f / host_stats[:count] * 100 }.avg
32
+ puts "Overall stats for ping - average: #{overall_average} ms, errors: #{overall_errors.round(2)}%"
33
+ end
34
+
35
+ private
36
+ def ping(host, count)
37
+ stats = {
38
+ durations: [],
39
+ failures: 0,
40
+ count: count
41
+ }
42
+ ping = ::Netchk::ICMP.new(host, nil, 1)
43
+
44
+ count.times do
45
+ sleep @interval
46
+ if ping.ping
47
+ stats[:durations] << ping.duration
48
+ else
49
+ $stderr.puts "pinging #{host} failed: #{ping.exception || "Unknown error"}"
50
+ stats[:failures] += 1
51
+ end
52
+ end
53
+
54
+ stats
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,3 @@
1
+ module Netchk
2
+ VERSION = "0.0.1.beta"
3
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'lib/netchk/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "netchk"
5
+ spec.version = Netchk::VERSION
6
+ spec.authors = ["Moray Baruh"]
7
+
8
+ spec.summary = "Simple internet troubleshooter."
9
+ spec.description = <<~EOF
10
+ Find issues with your internet connection step by step. This script verifies:
11
+ - your computer has at least one IP assigned
12
+ - has access to at least one DNS server
13
+ - the configured DNS servers are reachable
14
+ - the configured DNS server are able to resolve host names
15
+
16
+ And finally, pings some hosts to show you average durations and error rates.
17
+ EOF
18
+ spec.homepage = "https://moraybaruh.com"
19
+ spec.license = "MIT"
20
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
21
+
22
+ # spec.metadata["homepage_uri"] = spec.homepage
23
+ # spec.metadata["source_code_uri"] = "https://moraybaruh.com"
24
+ # spec.metadata["changelog_uri"] = "https://moraybaruh.com"
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
+ end
30
+ spec.bindir = "bin"
31
+ spec.executables = ['netchk']
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency 'net-ping', '~> 2.0'
35
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: netchk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta
5
+ platform: ruby
6
+ authors:
7
+ - Moray Baruh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-ping
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: "Find issues with your internet connection step by step. This script
28
+ verifies:\n - your computer has at least one IP assigned \n - has access to at
29
+ least one DNS server\n - the configured DNS servers are reachable\n - the configured
30
+ DNS server are able to resolve host names\n \nAnd finally, pings some hosts to show
31
+ you average durations and error rates.\n"
32
+ email:
33
+ executables:
34
+ - netchk
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - ".gitignore"
39
+ - ".idea/$CACHE_FILE$"
40
+ - ".idea/workspace.xml"
41
+ - ".travis.yml"
42
+ - Gemfile
43
+ - LICENSE.txt
44
+ - README.md
45
+ - Rakefile
46
+ - bin/console
47
+ - bin/netchk
48
+ - bin/setup
49
+ - lib/netchk.rb
50
+ - lib/netchk/dns_resolv_verifier.rb
51
+ - lib/netchk/dns_server_verifier.rb
52
+ - lib/netchk/icmp.rb
53
+ - lib/netchk/ip_verifier.rb
54
+ - lib/netchk/tcp_ping.rb
55
+ - lib/netchk/tcp_ping_verifier.rb
56
+ - lib/netchk/version.rb
57
+ - netchk.gemspec
58
+ homepage: https://moraybaruh.com
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.3.0
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.1
76
+ requirements: []
77
+ rubygems_version: 3.1.2
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Simple internet troubleshooter.
81
+ test_files: []