ruby-netmon-qt 1.0.0 → 1.0.2

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
  SHA256:
3
- metadata.gz: a978565563fb163a935e9f0a3e9e8f2cfdfaff0dcf06689aa0461e3848b2bfa1
4
- data.tar.gz: 461e57c1fac0d69aaa6bc09b23b8171a6bb1be2ba7a0e1d01f51bdd27d27e270
3
+ metadata.gz: 1b5ae43229614572636751dc0765e94da577124b603fdfe034464b9ed7b4aac7
4
+ data.tar.gz: 810e536eb5b67bf68c419eae154567c006deb70ef59cdeeb403ec6eaf6c3ac9d
5
5
  SHA512:
6
- metadata.gz: d70a7bbacf6c59cb4b7f7467b4e2ab7abe6a8b5c9f8e6f94bf69f1cae475a2cecb00451da1293e48331eb656bed714316f1653b1321466d446ef7f91be27f0d4
7
- data.tar.gz: f6b49eed6b11ab1a0e169f4e3c7dd90d833b192aa169d01104f3f46959e6192f3930d67fac45c8a40ee7844236d77f48fe079438ef4dac9c10e1d64857002ec2
6
+ metadata.gz: 3b530cefa1913b5253f33dc071f220dc7c88742e6150a9f0c3664b6a4e96f8a6a9137bf6dd30cc9559beeb1ddb7ee875c19ffc271823557ef759a7b18babff99
7
+ data.tar.gz: d96e4ed33ca77f5eb5e163d6ca618c2963ff309d4f7829fa0b33b1029a85a934f179a53fb1775c122a9873c5f954b8300af06a58789313f42fe7270742f41a08
@@ -25,9 +25,11 @@ class ConnsTableView < RubyQt6::Bando::QWidget
25
25
  when COLUMN_PROCESS_NAME
26
26
  less_than_process_name(lhs, rhs)
27
27
  when COLUMN_LOCAL_ADDRESS, COLUMN_REMOTE_ADDRESS
28
- less_than_column_address(lhs, rhs)
28
+ less_than_address(lhs, rhs)
29
29
  when COLUMN_PROCESS_ID, COLUMN_LOCAL_PORT, COLUMN_REMOTE_PORT
30
- less_than_column_numeric(lhs, rhs)
30
+ less_than_numeric(lhs, rhs)
31
+ when COLUMN_SINCE
32
+ less_than_since(lhs, rhs)
31
33
  else
32
34
  super
33
35
  end
@@ -50,7 +52,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
50
52
  end
51
53
  end
52
54
 
53
- def less_than_column_address(lhs, rhs)
55
+ def less_than_address(lhs, rhs)
54
56
  lhs_data = IPAddr.new(source_model.data(lhs).value.to_s)
55
57
  rhs_data = IPAddr.new(source_model.data(rhs).value.to_s)
56
58
  return false if lhs_data.ipv6? && rhs_data.ipv4?
@@ -58,10 +60,27 @@ class ConnsTableView < RubyQt6::Bando::QWidget
58
60
  lhs_data < rhs_data
59
61
  end
60
62
 
61
- def less_than_column_numeric(lhs, rhs)
63
+ def less_than_numeric(lhs, rhs)
62
64
  lhs_data = source_model.data(lhs).value.to_s.to_i
63
65
  rhs_data = source_model.data(rhs).value.to_s.to_i
64
66
  lhs_data < rhs_data
65
67
  end
68
+
69
+ def less_than_since(lhs, rhs)
70
+ lhs_data, rhs_data = [lhs, rhs].map do |index|
71
+ s = source_model.data(index).value.to_s
72
+ next 0 if s.empty?
73
+ s.scan(/\d+\w/).map do |value_unit|
74
+ value, unit = value_unit[..-1].to_i, value_unit[-1]
75
+ case unit
76
+ when "d" then value * 24 * 60 * 60
77
+ when "h" then value * 60 * 60
78
+ when "m" then value * 60
79
+ when "s" then value
80
+ end
81
+ end.reduce(:+)
82
+ end
83
+ lhs_data < rhs_data
84
+ end
66
85
  end
67
86
  end
@@ -10,6 +10,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
10
10
  COLUMN_LOCAL_PORT = 7
11
11
  COLUMN_REMOTE_ADDRESS = 8
12
12
  COLUMN_REMOTE_PORT = 9
13
+ COLUMN_SINCE = 10
13
14
 
14
15
  # TCP: Use Traffic Light Color Palette
15
16
  COLORS_TCP_STATE = {
@@ -49,7 +50,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
49
50
 
50
51
  def initialize(parent)
51
52
  @geoiolookup = begin
52
- MaxMind::DB.new(NetmonQt.settings.GET_geolite2_mmdb, mode: MaxMind::DB::MODE_MEMORY)
53
+ MaxMind::DB.new(NetmonQt.settings.GET_geoip_country_mmdb, mode: MaxMind::DB::MODE_MEMORY)
53
54
  rescue Errno::EACCES, Errno::ENOENT
54
55
  end
55
56
 
@@ -92,6 +93,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
92
93
  .push("Local Port")
93
94
  .push("Remote Address")
94
95
  .push("Remote Port")
96
+ .push("Since")
95
97
  )
96
98
  end
97
99
 
@@ -152,7 +154,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
152
154
  def initialize_icon_ipaddress(ipaddress)
153
155
  return if @geoiolookup.nil?
154
156
 
155
- islocal = Netmon.local_address?(ipaddress)
157
+ islocal = Netmon::LocalAddressChecker.local?(ipaddress)
156
158
  return QIcon.from_theme(QIcon::ThemeIcon::Computer) if islocal
157
159
 
158
160
  record = @geoiolookup.get(ipaddress)
@@ -188,7 +190,8 @@ class ConnsTableView < RubyQt6::Bando::QWidget
188
190
  initialize_standarditem(initialize_icon_ipaddress(conn.local_address), conn.local_address),
189
191
  initialize_standarditem(conn.local_port.to_s),
190
192
  initialize_standarditem(initialize_icon_ipaddress(conn.remote_address), conn.remote_address),
191
- initialize_standarditem(conn.remote_port.to_s)
193
+ initialize_standarditem(conn.remote_port.to_s),
194
+ initialize_standarditem(conn.since_text)
192
195
  )
193
196
 
194
197
  @active_processes.add(comm)
@@ -213,7 +216,8 @@ class ConnsTableView < RubyQt6::Bando::QWidget
213
216
  {
214
217
  COLUMN_PROCESS_ID => conn.pid_text,
215
218
  COLUMN_STATE => conn.state,
216
- COLUMN_USER => conn.uname
219
+ COLUMN_USER => conn.uname,
220
+ COLUMN_SINCE => conn.since_text
217
221
  }.each do |column, text|
218
222
  item = @itemmodel.item_from_index(keyitemindex.sibling_at_column(column))
219
223
  item.set_text(text)
@@ -24,9 +24,6 @@ class ConnsTableView < RubyQt6::Bando::QWidget
24
24
  slot "_on_autorefreshbtn_toggled(bool)"
25
25
  slot "_on_autorefresh_timer_timeout()"
26
26
  slot "_on_tableview_custom_context_menu_requested(QPoint)"
27
- slot "_on_endprocess_action_triggered()"
28
- slot "_on_whois_action_triggered()"
29
- slot "_on_copy_action_triggered()"
30
27
  end
31
28
 
32
29
  attr_reader :processfilter, :protocolfilter, :statefilter, :userfilter
@@ -59,15 +56,13 @@ class ConnsTableView < RubyQt6::Bando::QWidget
59
56
  end
60
57
 
61
58
  def initialize_actions
62
- @endprocess_action = initialize_actions_act(QIcon::ThemeIcon::ApplicationExit, "End Process", :_on_endprocess_action_triggered)
63
- @whois_action = initialize_actions_act(QIcon::ThemeIcon::EditFind, "", :_on_whois_action_triggered)
64
- @copy_action = initialize_actions_act(QIcon::ThemeIcon::EditCopy, "", :_on_copy_action_triggered)
59
+ @endprocess_action = initialize_actions_act(QIcon::ThemeIcon::ApplicationExit, "End Process")
60
+ @whois_action = initialize_actions_act(QIcon::ThemeIcon::EditFind, "")
61
+ @copy_action = initialize_actions_act(QIcon::ThemeIcon::EditCopy, "")
65
62
  end
66
63
 
67
- def initialize_actions_act(icon, text, slot)
68
- action = QAction.new(QIcon.from_theme(icon), text, self)
69
- action.triggered.connect(self, slot)
70
- action
64
+ def initialize_actions_act(icon, text)
65
+ QAction.new(QIcon.from_theme(icon), text, self)
71
66
  end
72
67
 
73
68
  def initialize_toolbar
@@ -140,6 +135,7 @@ class ConnsTableView < RubyQt6::Bando::QWidget
140
135
  @tableview.set_column_width(COLUMN_PROCESS_NAME, 128)
141
136
  @tableview.set_column_width(COLUMN_LOCAL_ADDRESS, 192)
142
137
  @tableview.set_column_width(COLUMN_REMOTE_ADDRESS, 192)
138
+ @tableview.set_column_width(COLUMN_SINCE, 128)
143
139
 
144
140
  @tableview.sort_by_column(COLUMN_PROCESS_NAME, Qt::AscendingOrder)
145
141
  @tableview.set_sorting_enabled(true)
@@ -200,40 +196,36 @@ class ConnsTableView < RubyQt6::Bando::QWidget
200
196
  menu.set_attribute(Qt::WA_DeleteOnClose)
201
197
 
202
198
  pid = @lastindex.sibling_at_column(COLUMN_PROCESS_ID).data.value.to_s
203
- remote_address = @lastindex.sibling_at_column(COLUMN_REMOTE_ADDRESS).data.value
204
- remote_address = remote_address.contains(".") ? remote_address : "#{remote_address[0, 12]}..."
199
+ remote_address = @lastindex.sibling_at_column(COLUMN_REMOTE_ADDRESS).data.value.to_s
200
+ remote_address = remote_address.include?(".") ? remote_address : "#{remote_address[0, 12]}..."
201
+ text = @lastindex.data.value.to_s
205
202
 
206
203
  @endprocess_action.set_enabled(pid.to_i.nonzero?)
207
204
  @whois_action.set_text("Whois #{remote_address} - via IPinfo")
208
- @copy_action.set_text("Copy \"#{@lastindex.data.value}\"")
205
+ @copy_action.set_text("Copy \"#{text}\"")
209
206
 
210
207
  menu.add_action(@endprocess_action)
211
208
  menu.add_separator
212
209
  menu.add_action(@whois_action)
213
210
  menu.add_action(@copy_action)
214
211
 
215
- menu.exec(@tableview.viewport.map_to_global(position))
212
+ case menu.exec(@tableview.viewport.map_to_global(position))
213
+ when @endprocess_action then _on_endprocess_action_triggered(pid)
214
+ when @whois_action then _on_whois_action_triggered(remote_address)
215
+ when @copy_action then _on_copy_action_triggered(text)
216
+ end
216
217
  end
217
218
 
218
- def _on_endprocess_action_triggered
219
- return unless @lastindex.valid?
220
-
221
- pid = @lastindex.sibling_at_column(COLUMN_PROCESS_ID).data.value
219
+ def _on_endprocess_action_triggered(pid)
222
220
  QProcess.execute("kill", QStringList.new << "-9" << pid)
223
221
  end
224
222
 
225
- def _on_whois_action_triggered
226
- return unless @lastindex.valid?
227
-
228
- remote_address = @lastindex.sibling_at_column(COLUMN_REMOTE_ADDRESS).data.value
223
+ def _on_whois_action_triggered(remote_address)
229
224
  url = QUrl.new("https://ipinfo.io/#{remote_address}")
230
225
  QDesktopServices.open_url(url)
231
226
  end
232
227
 
233
- def _on_copy_action_triggered
234
- return unless @lastindex.valid?
235
-
236
- text = @lastindex.data.value
228
+ def _on_copy_action_triggered(text)
237
229
  QApplication.clipboard.set_text(text)
238
230
  end
239
231
  end
@@ -14,13 +14,13 @@ class ConnsLookupService
14
14
  end
15
15
  end
16
16
 
17
- Dir.glob("/proc/[1-9]*/fd/[1-9]*") do |dir|
18
- ino = File.stat(dir).ino
19
- conn = ino2conn[ino]
17
+ Dir.glob("/proc/[1-9]*/fd/[1-9]*") do |filepath|
18
+ conn = ino2conn[File.stat(filepath).ino]
20
19
  next if conn.nil?
21
20
 
22
- conn.pid = dir.split("/")[2].to_i
21
+ conn.pid = filepath.split("/")[2].to_i
23
22
  conn.comm = File.read("/proc/#{conn.pid}/comm").strip
23
+ conn.created_at = File.lstat(filepath).ctime
24
24
  rescue Errno::EACCES, Errno::ENOENT
25
25
  end
26
26
 
@@ -39,8 +39,6 @@ class ConnsLookupService
39
39
  Netmon::Connection::PROTOCOL_UDP4
40
40
  when "udp6"
41
41
  Netmon::Connection::PROTOCOL_UDP6
42
- else
43
- ""
44
42
  end
45
43
  end
46
44
  end
@@ -16,8 +16,8 @@ module NetmonQt
16
16
  default_connections_check_interval
17
17
  end
18
18
 
19
- def GET_geolite2_mmdb
20
- default_geolite2_mmdb
19
+ def GET_geoip_country_mmdb
20
+ default_geoip_country_mmdb
21
21
  end
22
22
 
23
23
  private
@@ -26,8 +26,15 @@ module NetmonQt
26
26
  4_000
27
27
  end
28
28
 
29
- def default_geolite2_mmdb
30
- "/usr/share/GeoIP/GeoLite2-Country.mmdb"
29
+ def default_geoip_country_mmdb
30
+ candidates = [
31
+ "/usr/local/share/GeoIP/Country.mmdb",
32
+ "/usr/local/share/GeoIP/GeoLite2-Country.mmdb",
33
+ "/usr/share/GeoIP/Country.mmdb",
34
+ "/usr/share/GeoIP/GeoLite2-Country.mmdb"
35
+ ]
36
+ candidates.each { |filepath| return filepath if File.exist?(filepath) }
37
+ candidates[0]
31
38
  end
32
39
  end
33
40
  end
@@ -29,6 +29,7 @@ module Netmon
29
29
  attr_accessor :remote_address, :remote_port
30
30
  attr_accessor :state, :uid, :inode
31
31
  attr_accessor :pid, :comm
32
+ attr_accessor :created_at
32
33
 
33
34
  def initialize(attrs)
34
35
  attrs.each do |attr, value|
@@ -53,5 +54,15 @@ module Netmon
53
54
  return comm if comm
54
55
  inode.zero? ? COMM_DEFUNCT : COMM_UNKNOWN
55
56
  end
57
+
58
+ def since_text
59
+ return "" if created_at.nil?
60
+
61
+ m, s = (Time.now - created_at).to_i.divmod(60)
62
+ h, m = m.divmod(60)
63
+ d, h = h.divmod(24)
64
+ units = {"d" => d, "h" => h, "m" => m, "s" => s}
65
+ units.map { |unit, value| value.zero? ? nil : "#{value}#{unit}" }.compact.join(" ")
66
+ end
56
67
  end
57
68
  end
@@ -0,0 +1,19 @@
1
+ module Netmon
2
+ module LocalAddressChecker
3
+ CIDR_LIST = [
4
+ IPAddr.new("224.0.0.0/24"), # Local Multicast - IPv4
5
+ IPAddr.new("ff02::/16"), # Local Multicast - IPv6
6
+ IPAddr.new("0.0.0.0"), # Unspecified - IPv4
7
+ IPAddr.new("::") # Unspecified - IPv6
8
+ ].freeze
9
+
10
+ def self.local?(address)
11
+ ip = IPAddr.new(address)
12
+ return true if ip.link_local?
13
+ return true if ip.loopback?
14
+ return true if ip.private?
15
+ return true if CIDR_LIST.any? { |cidr| cidr.include?(address) }
16
+ false
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  require_relative "netmon/connection"
2
- require_relative "netmon/localaddresscheck"
2
+ require_relative "netmon/localaddresschecker"
3
3
  require_relative "netmon/procnet"
@@ -1,3 +1,3 @@
1
1
  module NetmonQt
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/netmon-qt.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "etc"
2
+ require "ipaddr"
2
3
 
3
4
  require "maxmind/db"
4
5
  require "qt6/qtwidgets"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-netmon-qt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Doe
@@ -343,7 +343,7 @@ files:
343
343
  - lib/netmon-qt/lib/contrib/sighandler.rb
344
344
  - lib/netmon-qt/lib/netmon.rb
345
345
  - lib/netmon-qt/lib/netmon/connection.rb
346
- - lib/netmon-qt/lib/netmon/localaddresscheck.rb
346
+ - lib/netmon-qt/lib/netmon/localaddresschecker.rb
347
347
  - lib/netmon-qt/lib/netmon/procnet.rb
348
348
  - lib/netmon-qt/version.rb
349
349
  - misc/screenshots/mainwindow.png
@@ -1,19 +0,0 @@
1
- require "ipaddr"
2
-
3
- module Netmon
4
- CIDR_LIST = [
5
- IPAddr.new("224.0.0.0/24"), # Local Multicast - IPv4
6
- IPAddr.new("ff02::/16"), # Local Multicast - IPv6
7
- IPAddr.new("0.0.0.0"), # Unspecified - IPv4
8
- IPAddr.new("::") # Unspecified - IPv6
9
- ].freeze
10
-
11
- def self.local_address?(address)
12
- ip = IPAddr.new(address)
13
- return true if ip.link_local?
14
- return true if ip.loopback?
15
- return true if ip.private?
16
- return true if CIDR_LIST.any? { |cidr| cidr.include?(address) }
17
- false
18
- end
19
- end