meshtastic 0.0.42 → 0.0.43

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ffe0a1e8528090281aedce5b132f1f57aa82db0c8c46131cff53d00004e4dcd
4
- data.tar.gz: 56bb82c58cdcbced0c401802180931aa4625e4314e4954ab30349811a614d399
3
+ metadata.gz: 105d223bd94e02d4473ac5fd66ed0b8dc7eef73d44578eb427fc3d55eb51ae21
4
+ data.tar.gz: 216a3957a439408579d1d7b48dd552d394885bbc09b8dc3410d776e4c1eb37d6
5
5
  SHA512:
6
- metadata.gz: dd84aa7e0607992de00be53a0f8ccb2d69077004dd21ebc2838be38582064a797c0f13317c0abd375ab67199dd494640187895384ea74110c77c9b478a218654
7
- data.tar.gz: a1135c741f44f38765421ea2020173b40395478fffdbb24da760d75f8483920f99cf2f0d110f7173fb9617e7522a644dc418f937aef79a35f1b04d89ed94737c
6
+ metadata.gz: 1381a13cff943403ef269c5a349529ab9c2f12e3a3af5b3e88c589f7fd427a6e45b23543251449e1d520471fdf5f6c8a11c55c56bfc24553dc2b02db7ace28d9
7
+ data.tar.gz: 01d01863635a22c2d3c42ace2a34076c4094ece8ef9629f08ae0e8da7c0a98bbc5815db26e2e4063fa462619caf710938628a8cd086f62411d1cd885017665a4
@@ -64,120 +64,91 @@ module Meshtastic
64
64
  end
65
65
 
66
66
  # Supported Method Parameters::
67
- # Meshtastic::MQQT.recursively_decode_payloads(
68
- # object: 'required - object to recursively decode',
67
+ # Meshtastic::MQQT.decode_payload(
68
+ # payload: 'required - payload to recursively decode',
69
69
  # msg_type: 'required - message type (e.g. :TEXT_MESSAGE_APP)',
70
- # gps_metadata: 'optional - include GPS metadata in output (default: false)'
70
+ # gps_metadata: 'optional - include GPS metadata in output (default: false)',
71
71
  # )
72
72
 
73
- private_class_method def self.recursively_decode_payloads(opts = {})
74
- object = opts[:object]
73
+ public_class_method def self.decode_payload(opts = {})
74
+ payload = opts[:payload]
75
75
  msg_type = opts[:msg_type]
76
76
  gps_metadata = opts[:gps_metadata]
77
77
 
78
- # puts "OBJECT: #{object.inspect}"
79
- object = Meshtastic::Data.decode(object).to_h if object.is_a?(String)
80
-
81
- object.each do |key, value|
82
- if value.is_a?(Hash)
83
- # Recursive call if the value is a hash
84
- recursively_decode_payloads(object: value)
85
- elsif value.is_a?(Array)
86
- # Process each element if it's an array
87
- value.each do |item|
88
- recursively_decode_payloads(object: item) if item.is_a?(Hash)
89
- end
90
- end
91
- # Check if the key matches 'payload' and decode
92
- case key
93
- when :latitude_i
94
- object[:latitude] = object[:latitude_i] * 0.0000001
95
- when :longitude_i
96
- object[:longitude] = object[:longitude_i] * 0.0000001
97
- when :macaddr
98
- object[key] = object[key].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join(':')
99
- when :payload
100
- case msg_type
101
- when :ADMIN_APP
102
- decoder = Meshtastic::AdminMessage.decode(payload)
103
- when :ATAK_FORWARDER, :ATAK_PLUGIN
104
- decoder = Meshtastic::TAKPacket
105
- # when :AUDIO_APP
106
- # decoder = Meshtastic::Audio
107
- when :DETECTION_SENSOR_APP
108
- decoder = Meshtastic::DeviceState
109
- # when :IP_TUNNEL_APP
110
- # decoder = Meshtastic::IpTunnel
111
- when :MAP_REPORT_APP
112
- decoder = Meshtastic::MapReport
113
- # when :MAX
114
- # decoder = Meshtastic::Max
115
- when :NEIGHBORINFO_APP
116
- decoder = Meshtastic::NeighborInfo
117
- when :NODEINFO_APP
118
- decoder = Meshtastic::User
119
- when :PAXCOUNTER_APP
120
- decoder = Meshtastic::Paxcount
121
- when :POSITION_APP
122
- decoder = Meshtastic::Position
123
- # when :PRIVATE_APP
124
- # decoder = Meshtastic::Private
125
- when :RANGE_TEST_APP
126
- # Unsure if this is the correct protobuf object
127
- decoder = Meshtastic::FromRadio
128
- when :REMOTE_HARDWARE_APP
129
- decoder = Meshtastic::HardwareMessage
130
- # when :REPLY_APP
131
- # decoder = Meshtastic::Reply
132
- when :ROUTING_APP
133
- decoder = Meshtastic::Routing
134
- when :SERIAL_APP
135
- decoder = Meshtastic::SerialConnectionStatus
136
- when :SIMULATOR_APP
137
- decoder = Meshtastic::Compressed
138
- when :STORE_FORWARD_APP
139
- decoder = Meshtastic::StoreAndForward
140
- when :TEXT_MESSAGE_APP
141
- # Unsure if this is the correct protobuf object
142
- # decoder = Meshtastic::MqttClientProxyMessage
143
- decoder = Meshtastic::Data
144
- when :TELEMETRY_APP
145
- decoder = Meshtastic::Telemetry
146
- when :TRACEROUTE_APP
147
- decoder = Meshtastic::RouteDiscovery
148
- when :UNKNOWN_APP
149
- decoder = Meshtastic::Data.decode
150
- when :WAYPOINT_APP
151
- decoder = Meshtastic::Waypoint
152
- # when :ZPS_APP
153
- # decoder = Meshtastic::Zps
154
- else
155
- puts "WARNING: Unknown message type: #{msg_type}"
156
- decoder = Meshtastic::Data.decode
157
- end
158
-
159
- # begin
160
- # # If the value is base64 encoded, base64 decode and then decode
161
- # base64_decoded_value = Base64.strict_decode64(value)
162
- # object[key] = decoder.decode(base64_decoded_value).to_h
163
- # rescue ArgumentError => e
164
- # # Otherwise, just decode the value
165
- object[key] = decoder.decode(value).to_h
166
- # end
167
-
78
+ case msg_type
79
+ when :ADMIN_APP
80
+ decoder = Meshtastic::AdminMessage
81
+ when :ATAK_FORWARDER, :ATAK_PLUGIN
82
+ decoder = Meshtastic::TAKPacket
83
+ # when :AUDIO_APP
84
+ # decoder = Meshtastic::Audio
85
+ when :DETECTION_SENSOR_APP
86
+ decoder = Meshtastic::DeviceState
87
+ # when :IP_TUNNEL_APP
88
+ # decoder = Meshtastic::IpTunnel
89
+ when :MAP_REPORT_APP
90
+ decoder = Meshtastic::MapReport
91
+ # when :MAX
92
+ # decoder = Meshtastic::Max
93
+ when :NEIGHBORINFO_APP
94
+ decoder = Meshtastic::NeighborInfo
95
+ when :NODEINFO_APP
96
+ decoder = Meshtastic::User
97
+ when :PAXCOUNTER_APP
98
+ decoder = Meshtastic::Paxcount
99
+ when :POSITION_APP
100
+ decoder = Meshtastic::Position
101
+ # when :PRIVATE_APP
102
+ # decoder = Meshtastic::Private
103
+ when :RANGE_TEST_APP
104
+ # Unsure if this is the correct protobuf object
105
+ decoder = Meshtastic::FromRadio
106
+ when :REMOTE_HARDWARE_APP
107
+ decoder = Meshtastic::HardwareMessage
108
+ # when :REPLY_APP
109
+ # decoder = Meshtastic::Reply
110
+ when :ROUTING_APP
111
+ decoder = Meshtastic::Routing
112
+ when :SERIAL_APP
113
+ decoder = Meshtastic::SerialConnectionStatus
114
+ when :SIMULATOR_APP
115
+ decoder = Meshtastic::Compressed
116
+ when :STORE_FORWARD_APP
117
+ decoder = Meshtastic::StoreAndForward
118
+ when :TEXT_MESSAGE_APP
119
+ # Unsure if this is the correct protobuf object
120
+ # decoder = Meshtastic::MqttClientProxyMessage
121
+ decoder = Meshtastic::Data
122
+ when :TELEMETRY_APP
123
+ decoder = Meshtastic::Telemetry
124
+ when :TRACEROUTE_APP
125
+ decoder = Meshtastic::RouteDiscovery
126
+ when :WAYPOINT_APP
127
+ decoder = Meshtastic::Waypoint
128
+ # when :ZPS_APP
129
+ # decoder = Meshtastic::Zps
130
+ end
131
+ payload = decoder.decode(payload).to_h
132
+ if payload.keys.include?(:latitude_i) && payload.keys.include?(:longitude_i)
133
+ lat = payload[:latitude_i] * 0.0000001
134
+ lon = payload[:longitude_i] * 0.0000001
135
+ payload[:latitude] = lat
136
+ payload[:longitude] = lon
137
+ if gps_metadata
138
+ gps_search_resp = Meshtastic.gps_search(lat: lat, lon: lon)
139
+ payload[:gps_metadata] = gps_search_resp
168
140
  end
141
+ end
169
142
 
170
- next unless gps_metadata && object[:latitude] && object[:longitude]
171
-
172
- object[:gps_metadata] = gps_search(
173
- lat: object[:latitude],
174
- lon: object[:longitude]
175
- ).first.data
143
+ if payload.keys.include?(:macaddr)
144
+ mac_hex_arr = payload[:macaddr].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }
145
+ mac_str = mac_hex_arr.join(':')
146
+ payload[:macaddr] = mac_str
176
147
  end
177
148
 
178
- object
149
+ payload
179
150
  rescue Google::Protobuf::ParseError
180
- object
151
+ payload
181
152
  rescue StandardError => e
182
153
  raise e
183
154
  end
@@ -256,8 +227,8 @@ module Meshtastic
256
227
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
257
228
 
258
229
  psk = psks[:LongFast]
259
- target_chanel = decoded_payload_hash[:channel_id].to_s.to_sym
260
- psk = psks[target_chanel] if psks.keys.include?(target_chanel)
230
+ target_channel = decoded_payload_hash[:channel_id].to_s.to_sym
231
+ psk = psks[target_channel] if psks.keys.include?(target_channel)
261
232
  dec_psk = Base64.strict_decode64(psk)
262
233
 
263
234
  cipher = OpenSSL::Cipher.new('AES-128-CTR')
@@ -275,8 +246,8 @@ module Meshtastic
275
246
  # payload = Meshtastic::Data.decode(message[:decoded][:payload]).to_h
276
247
  payload = message[:decoded][:payload]
277
248
  msg_type = message[:decoded][:portnum]
278
- message[:decoded][:payload] = recursively_decode_payloads(
279
- object: payload,
249
+ message[:decoded][:payload] = decode_payload(
250
+ payload: payload,
280
251
  msg_type: msg_type,
281
252
  gps_metadata: gps_metadata
282
253
  )
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meshtastic
4
- VERSION = '0.0.42'
4
+ VERSION = '0.0.43'
5
5
  end
data/lib/meshtastic.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  # Plugin used to interact with Meshtastic nodes
4
4
  module Meshtastic
5
+ require 'base64'
5
6
  # Protocol Buffers for Meshtastic
6
7
  require 'meshtastic/admin_pb'
7
8
  require 'nanopb_pb'
@@ -25,6 +26,7 @@ module Meshtastic
25
26
  require 'meshtastic/telemetry_pb'
26
27
  require 'meshtastic/version'
27
28
  require 'meshtastic/xmodem_pb'
29
+ require 'openssl'
28
30
 
29
31
  autoload :Admin, 'meshtastic/admin'
30
32
  autoload :Apponly, 'meshtastic/apponly'
@@ -47,6 +49,191 @@ module Meshtastic
47
49
  autoload :Telemetry, 'meshtastic/telemetry'
48
50
  autoload :Xmodem, 'meshtastic/xmodem'
49
51
 
52
+ # Supported Method Parameters::
53
+ # Meshtastic.send_text(
54
+ # from_id: 'optional - Source ID (Default: 0)',
55
+ # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
56
+ # text: 'optional - Text Message (Default: SYN)',
57
+ # want_ack: 'optional - Want Acknowledgement (Default: false)',
58
+ # want_response: 'optional - Want Response (Default: false)',
59
+ # hop_limit: 'optional - Hop Limit (Default: 3)',
60
+ # on_response: 'optional - Callback on Response',
61
+ # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
62
+ # )
63
+ public_class_method def self.send_text(opts = {})
64
+ # Send a text message to a node
65
+ from_id = opts[:from_id].to_i
66
+ dest_id = opts[:dest_id] ||= 0xFFFFFFFF
67
+ text = opts[:text] ||= 'SYN'
68
+ want_ack = opts[:want_ack] ||= false
69
+ want_response = opts[:want_response] ||= false
70
+ hop_limit = opts[:hop_limit] ||= 3
71
+ on_response = opts[:on_response]
72
+ psk = opts[:psk]
73
+
74
+ # TODO: verify text length validity
75
+ max_txt_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
76
+ raise "ERROR: Text Length > #{max_txt_len} Bytes" if text.length > max_txt_len
77
+
78
+ port_num = Meshtastic::PortNum::TEXT_MESSAGE_APP
79
+
80
+ data = Meshtastic::Data.new
81
+ data.payload = text
82
+ data.portnum = port_num
83
+ data.want_response = want_response
84
+ puts data.to_h
85
+
86
+ send_data(
87
+ from_id: from_id,
88
+ dest_id: dest_id,
89
+ data: data,
90
+ want_ack: want_ack,
91
+ want_response: want_response,
92
+ hop_limit: hop_limit,
93
+ port_num: port_num,
94
+ on_response: on_response,
95
+ psk: psk
96
+ )
97
+ rescue StandardError => e
98
+ raise e
99
+ end
100
+
101
+ # Supported Method Parameters::
102
+ # Meshtastic.send_data(
103
+ # from_id: 'optional - Source ID (Default: 0)',
104
+ # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
105
+ # data: 'required - Data to Send',
106
+ # want_ack: 'optional - Want Acknowledgement (Default: false)',
107
+ # hop_limit: 'optional - Hop Limit (Default: 3)',
108
+ # port_num: 'optional - (Default: Meshtastic::PortNum::PRIVATE_APP)',
109
+ # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
110
+ # )
111
+ public_class_method def self.send_data(opts = {})
112
+ # Send a text message to a node
113
+ from_id = opts[:from_id].to_i
114
+ dest_id = opts[:dest_id] ||= 0xFFFFFFFF
115
+ data = opts[:data]
116
+ want_ack = opts[:want_ack] ||= false
117
+ hop_limit = opts[:hop_limit] ||= 3
118
+ port_num = opts[:port_num] ||= Meshtastic::PortNum::PRIVATE_APP
119
+ max_port_num = Meshtastic::PortNum::MAX
120
+ raise "ERROR: Invalid port_num" unless port_num.positive? && port_num < max_port_num
121
+
122
+ psk = opts[:psk]
123
+
124
+ data_len = data.payload.length
125
+ max_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
126
+ raise "ERROR: Data Length > #{max_len} Bytes" if data_len > max_len
127
+
128
+ mesh_packet = Meshtastic::MeshPacket.new
129
+ mesh_packet.decoded = data
130
+
131
+ send_packet(
132
+ mesh_packet: mesh_packet,
133
+ from_id: from_id,
134
+ dest_id: dest_id,
135
+ want_ack: want_ack,
136
+ hop_limit: hop_limit,
137
+ psk: psk
138
+ )
139
+ rescue StandardError => e
140
+ raise e
141
+ end
142
+
143
+ # Supported Method Parameters::
144
+ # Meshtastic.send_packet(
145
+ # mesh_packet: 'required - Mesh Packet to Send',
146
+ # from_id: 'optional - Source ID (Default: 0)',
147
+ # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
148
+ # want_ack: 'optional - Want Acknowledgement (Default: false)',
149
+ # hop_limit: 'optional - Hop Limit (Default: 3)',
150
+ # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
151
+ # )
152
+ public_class_method def self.send_packet(opts = {})
153
+ mesh_packet = opts[:mesh_packet]
154
+ from_id = opts[:from_id] ||= 0
155
+ dest_id = opts[:dest_id] ||= 0xFFFFFFFF
156
+ want_ack = opts[:want_ack] ||= false
157
+ hop_limit = opts[:hop_limit] ||= 3
158
+ psk = opts[:psk]
159
+
160
+ # my_info = Meshtastic::FromRadio.my_info
161
+ # wait_connected if dest_id != my_info.my_node_num && my_info.is_a(Meshtastic::Deviceonly::MyInfo)
162
+
163
+ node_num = dest_id
164
+ node_num_hex = dest_id.bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if dest_id.is_a?(String)
165
+ node_num = node_num_hex.to_i(16) if node_num_hex
166
+
167
+ mesh_packet.from = from_id
168
+ mesh_packet.to = node_num
169
+ mesh_packet.want_ack = want_ack
170
+ mesh_packet.hop_limit = hop_limit
171
+
172
+ mesh_packet.id = generate_packet_id if mesh_packet.id.zero?
173
+
174
+ if psk
175
+ nonce_packet_id = [mesh_packet.id].pack('V').ljust(8, "\x00")
176
+ nonce_from_node = [from_id].pack('V').ljust(8, "\x00")
177
+ nonce = "#{nonce_packet_id}#{nonce_from_node}".b
178
+
179
+ dec_psk = Base64.strict_decode64(psk)
180
+ cipher = OpenSSL::Cipher.new('AES-128-CTR')
181
+ cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
182
+ cipher.encrypt
183
+ cipher.key = dec_psk
184
+ cipher.iv = nonce
185
+
186
+ decrypted_payload = mesh_packet.decoded.to_s
187
+ encrypted_payload = cipher.update(decrypted_payload) + cipher.final
188
+
189
+ mesh_packet.encrypted = encrypted_payload
190
+ end
191
+ # puts mesh_packet.to_h
192
+
193
+ # to_radio = Meshtastic::ToRadio.new
194
+ # to_radio.packet = mesh_packet
195
+ # send_to_radio(to_radio: to_radio)
196
+
197
+ mesh_packet
198
+ rescue StandardError => e
199
+ raise e
200
+ end
201
+
202
+ # Supported Method Parameters::
203
+ # packet_id = Meshtastic.generate_packet_id(
204
+ # last_packet_id: 'optional - Last Packet ID (Default: 0)'
205
+ # )
206
+ public_class_method def self.generate_packet_id(opts = {})
207
+ last_packet_id = opts[:last_packet_id] ||= 0
208
+
209
+ packet_id = last_packet_id + 1 if last_packet_id.positive?
210
+ packet_id = rand(2**32) if last_packet_id.zero?
211
+
212
+ packet_id
213
+ end
214
+
215
+ # Supported Method Parameters::
216
+ # Meshtastic.send_to_radio(
217
+ # to_radio: 'required - ToRadio Message to Send'
218
+ # )
219
+ public_class_method def self.send_to_radio(opts = {})
220
+ to_radio = opts[:to_radio]
221
+
222
+ raise 'ERROR: Invalid ToRadio Message' unless to_radio.is_a?(Meshtastic::ToRadio)
223
+
224
+ to_radio.to_proto
225
+ rescue StandardError => e
226
+ raise e
227
+ end
228
+
229
+ # Author(s):: 0day Inc. <support@0dayinc.com>
230
+
231
+ public_class_method def self.authors
232
+ "AUTHOR(S):
233
+ 0day Inc. <support@0dayinc.com>
234
+ "
235
+ end
236
+
50
237
  # Display a List of Every Meshtastic Module
51
238
 
52
239
  public_class_method def self.help
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meshtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.42
4
+ version: 0.0.43
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-03 00:00:00.000000000 Z
11
+ date: 2024-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler