-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsend-mqtt-packets.py
More file actions
296 lines (242 loc) · 10.3 KB
/
Copy pathsend-mqtt-packets.py
File metadata and controls
296 lines (242 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#!/usr/bin/env python3
# andor quotes bot - sends random quotes from the Andor TV series
# mostly based on https://github.com/pdxlocations/Meshtastic-Python-Examples/blob/main/MQTT/send-mqtt-packets.py
from meshtastic.protobuf import mesh_pb2, mqtt_pb2, portnums_pb2
from meshtastic import BROADCAST_NUM
import paho.mqtt.client as mqtt
import random
import time
import ssl
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import base64
import re
import configparser
#### Debug Options
debug = True
### Default settings
config = configparser.ConfigParser()
config.read('mqtt-packets.ini')
mqtt_broker = config.get('mqtt', 'broker')
mqtt_port = config.getint('mqtt', 'port')
mqtt_username = config.get('mqtt', 'username')
mqtt_password = config.get('mqtt', 'password')
root_topic = config.get('mqtt', 'root_topic')
channel = config.get('mqtt', 'channel')
key = config.get('mqtt', 'key')
random_hex_chars = ''.join(random.choices('0123456789abcdef', k=4))
node_name = config.get('node', 'name', fallback = '!abcd' + random_hex_chars)
andor_quotes = [
"I have friends everywhere.",
"I don't need surprises.",
"Must everythng boring and sad?",
"The pace of repression outstrips our ability to understand it.",
"I've learned from Palpatine. I show you the stone in my hand, you miss the knife at your throat.",
"That's just love. Nothing you can do about that.",
"Wait, I'm just a tourist!",
"The very worst thing you can do right now is bore me.",
"You think they care what we say? Nobody's listening. Nobody.",
"I'd rather die trying to take them down than die giving them what they want.",
"There is only one way out.",
"I burn my life to make a sunrise that I know I'll never see.",
"Fight the empire!",
"On program! Now!"
]
message_text = random.choice(andor_quotes)
node_number = int(node_name.replace("!", ""), 16)
global_message_id = random.getrandbits(32)
client_short_name = "ANDO"
client_long_name = "Andor Quotes Bot"
mqtt_client_id = client_long_name.replace(' ', '-')
lat = "0"
lon = "0"
alt = "0"
client_hw_model = 255
#################################
### Program variables
default_key = "1PG7OiApB1nwvP+rz05pAQ==" # AKA AQ==
#################################
# Program Base Functions
def set_topic():
global publish_topic
node_name = '!' + hex(node_number)[2:]
publish_topic = root_topic + channel + "/" + node_name
def xor_hash(data):
result = 0
for char in data:
result ^= char
return result
def generate_hash(name, key):
replaced_key = key.replace('-', '+').replace('_', '/')
key_bytes = base64.b64decode(replaced_key.encode('utf-8'))
h_name = xor_hash(bytes(name, 'utf-8'))
h_key = xor_hash(key_bytes)
result = h_name ^ h_key
return result
#################################
# Send Messages
def direct_message(destination_id):
if debug: print("direct_message")
if destination_id:
try:
destination_id = int(destination_id[1:], 16)
send_message(destination_id)
except Exception as e:
if debug: print(f"Error converting destination_id: {e}")
def send_message(destination_id, message_text):
if not client.is_connected():
connect_mqtt()
if debug: print(f"Sending Text Message Packet to {str(destination_id)}")
if message_text:
encoded_message = mesh_pb2.Data()
encoded_message.portnum = portnums_pb2.TEXT_MESSAGE_APP
encoded_message.payload = message_text.encode("utf-8")
encoded_message.bitfield = 1
generate_mesh_packet(destination_id, encoded_message)
else:
return
def send_traceroute(destination_id):
if not client.is_connected():
connect_mqtt()
if debug: print(f"Sending Traceroute Packet to {str(destination_id)}")
encoded_message = mesh_pb2.Data()
encoded_message.portnum = portnums_pb2.TRACEROUTE_APP
encoded_message.want_response = True
encoded_message.bitfield = 1
destination_id = int(destination_id[1:], 16)
generate_mesh_packet(destination_id, encoded_message)
def send_node_info(destination_id, want_response):
if client.is_connected():
if debug: print(f"Sending NodeInfo Packet to {str(destination_id)}")
user_payload = mesh_pb2.User()
setattr(user_payload, "id", node_name)
setattr(user_payload, "long_name", client_long_name)
setattr(user_payload, "short_name", client_short_name)
setattr(user_payload, "hw_model", client_hw_model)
user_payload = user_payload.SerializeToString()
encoded_message = mesh_pb2.Data()
encoded_message.portnum = portnums_pb2.NODEINFO_APP
encoded_message.payload = user_payload
encoded_message.bitfield = 1
encoded_message.want_response = want_response # Request NodeInfo back
generate_mesh_packet(destination_id, encoded_message)
def send_position(destination_id):
if client.is_connected():
if debug: print(f"Sending Position Packet to {str(destination_id)}")
pos_time = int(time.time())
latitude = int(float(lat) * 1e7)
longitude = int(float(lon) * 1e7)
altitude_units = 1 / 3.28084 if 'ft' in str(alt) else 1.0
altitude = int(altitude_units * float(re.sub('[^0-9.]', '', str(alt))))
position_payload = mesh_pb2.Position()
setattr(position_payload, "latitude_i", latitude)
setattr(position_payload, "longitude_i", longitude)
setattr(position_payload, "altitude", altitude)
setattr(position_payload, "time", pos_time)
position_payload = position_payload.SerializeToString()
encoded_message = mesh_pb2.Data()
encoded_message.portnum = portnums_pb2.POSITION_APP
encoded_message.payload = position_payload
encoded_message.bitfield = 1
encoded_message.want_response = True
generate_mesh_packet(destination_id, encoded_message)
def generate_mesh_packet(destination_id, encoded_message):
global global_message_id
mesh_packet = mesh_pb2.MeshPacket()
# Use the global message ID and increment it for the next call
mesh_packet.id = global_message_id
global_message_id += 1
setattr(mesh_packet, "from", node_number)
mesh_packet.to = destination_id
mesh_packet.want_ack = False
mesh_packet.channel = generate_hash(channel, key)
mesh_packet.hop_limit = 3
mesh_packet.hop_start = 3
if key == "":
mesh_packet.decoded.CopyFrom(encoded_message)
else:
mesh_packet.encrypted = encrypt_message(channel, key, mesh_packet, encoded_message)
service_envelope = mqtt_pb2.ServiceEnvelope()
service_envelope.packet.CopyFrom(mesh_packet)
service_envelope.channel_id = channel
service_envelope.gateway_id = node_name
payload = service_envelope.SerializeToString()
client.publish(publish_topic, payload)
def encrypt_message(channel, key, mesh_packet, encoded_message):
mesh_packet.channel = generate_hash(channel, key)
key_bytes = base64.b64decode(key.encode('ascii'))
nonce_packet_id = mesh_packet.id.to_bytes(8, "little")
nonce_from_node = node_number.to_bytes(8, "little")
nonce = nonce_packet_id + nonce_from_node
cipher = Cipher(algorithms.AES(key_bytes), modes.CTR(nonce), backend=default_backend())
encryptor = cipher.encryptor()
encrypted_bytes = encryptor.update(encoded_message.SerializeToString()) + encryptor.finalize()
return encrypted_bytes
def send_ack(destination_id, message_id):
if debug: print("Sending ACK")
encoded_message = mesh_pb2.Data()
encoded_message.portnum = portnums_pb2.ROUTING_APP
encoded_message.request_id = message_id
encoded_message.payload = b"\030\000"
generate_mesh_packet(destination_id, encoded_message)
#################################
# MQTT Server
def connect_mqtt():
if "tls_configured" not in connect_mqtt.__dict__: #Persistent variable to remember if we've configured TLS yet
connect_mqtt.tls_configured = False
if debug: print("connect_mqtt")
global mqtt_broker, mqtt_port, mqtt_username, mqtt_password, root_topic, channel, node_number, db_file_path, key
if not client.is_connected():
try:
if ':' in mqtt_broker:
mqtt_broker,mqtt_port = mqtt_broker.split(':')
mqtt_port = int(mqtt_port)
if key == "AQ==":
if debug: print("key is default, expanding to AES128")
key = "1PG7OiApB1nwvP+rz05pAQ=="
padded_key = key.ljust(len(key) + ((4 - (len(key) % 4)) % 4), '=')
replaced_key = padded_key.replace('-', '+').replace('_', '/')
key = replaced_key
client.username_pw_set(mqtt_username, mqtt_password)
if mqtt_port == 8883 and connect_mqtt.tls_configured == False:
client.tls_set(ca_certs="cacert.pem", tls_version=ssl.PROTOCOL_TLSv1_2)
client.tls_insecure_set(False)
connect_mqtt.tls_configured = True
client.connect(mqtt_broker, mqtt_port, 60)
client.loop_start()
except Exception as e:
print (e)
def disconnect_mqtt():
if client.is_connected():
client.disconnect()
if debug: print("Client Disconnected")
def on_connect(client, userdata, flags, reason_code, properties):
set_topic()
if client.is_connected():
print("client is connected")
if reason_code == 0:
if debug: print(f"Connected to sever: {mqtt_broker}")
if debug: print(f"Publish Topic is: {publish_topic}\n")
############################
# Main
def main():
global client
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=mqtt_client_id, clean_session=True, userdata=None)
client.on_connect = on_connect
connect_mqtt()
time.sleep(1)
if client.is_connected:
send_node_info(BROADCAST_NUM, want_response=False)
time.sleep(4)
send_position(BROADCAST_NUM)
time.sleep(4)
send_message(BROADCAST_NUM, message_text)
print(message_text)
time.sleep(4)
send_message(4079836450, "I have friends everywhere") # norfolk - ng
# send_message(2697735096, message_text) # lincoln - ng
# send_message(2697666200, message_text) # frankenode
time.sleep(4)
disconnect_mqtt()
if __name__ == "__main__":
main()