tree checksum vpatch file split hunks
all signers:
antecedents: 9992-handle-edge-cases-add-feedback genesis
press order:
genesis | |
9992-handle-edge-cases-add-feedback | |
9991-improved-logging |
patch:
(25 . 15)(25 . 8)
5 GETTING STARTED
6
7 1. Launch blatta with something like the following command:
8 ./blatta --port=6668 --address-table-path config.py
9 ./blatta --irc-port=6668
10 2. Add a peer using the peer command.
11 3. Use genkey to generate a key.
12 4. Add the key to the peer using the key command.
13 5. Add an address for the peer using the address command.
14
15 NOTES FOR DIFF/PATCH N00B5
16
17 To apply the genesis patch (or any patch) to the current directory
18 and recreate the directory structure of the original:
19
20 patch -p1 -ruN < <wherever>/genesis.vdiff
- 01F00EF74CD2143C7B07EA439284CEA743C0406301E7D9255ED9287A022D8C2DB04125A346CAA2B1085E38BFD44D1FE342A52467C2511F6C714C79DB9954F89D(18 . 7)(18 . 7)- 29051FFF4C05169A752059F5102291F981A73EC3C5112CB8135865919A2A8CE0F02DB32C7C95639F39B51E2F444AF5229FEBCA7FE113D06B7D5C347C3F46BF30
25 def main(argv):
26 op = OptionParser(
27 version=VERSION,
28 description="alcuin is a small and limited IRC server emulator for gossip networks.")
29 description="Blatta is an implementation of a Pest network station.")
30 op.add_option(
31 "-a", "--address-table-path",
32 help="Import address table from specified file")
(50 . 18)(50 . 14)
34 metavar="X",
35 help="display file X as message of the day")
36 op.add_option(
37 "-s", "--ssl-pem-file",
38 metavar="FILE",
39 help="enable SSL and use FILE as the .pem certificate+key")
40 op.add_option(
41 "-p", "--password",
42 metavar="X",
43 help="require connection password X; default: no password")
44 op.add_option(
45 "--ports",
46 "--irc-ports",
47 metavar="X",
48 help="listen to ports X (a list separated by comma or whitespace);"
49 " default: 6667 or 6697 if SSL is enabled")
50 help="listen on ports X for irc client connections (a list separated by comma or whitespace);"
51 " default: 6667")
52 op.add_option(
53 "--udp-port",
54 metavar="X",
(90 . 17)(86 . 14)
56 (options, args) = op.parse_args(argv[1:])
57 if options.debug:
58 options.verbose = True
59 if options.ports is None:
60 if options.ssl_pem_file is None:
61 options.ports = "6667"
62 else:
63 options.ports = "6697"
64 if options.irc_ports is None:
65 options.irc_ports = "6697"
66 if options.udp_port is None:
67 options.udp_port = 7778
68 else:
69 options.udp_port = int(options.udp_port)
70 if options.db_path is None:
71 options.db_path = "alcuin.db"
72 options.db_path = "blatta.db"
73 if options.config_file_path is None:
74 options.config_file_path = "config.py"
75 if options.chroot:
(127 . 13)(120 . 13)
77 " startup. If you really intend to run as root, use"
78 " \"--setuid root\".")
79
80 ports = []
81 for port in re.split(r"[,\s]+", options.ports):
82 irc_ports = []
83 for port in re.split(r"[,\s]+", options.irc_ports):
84 try:
85 ports.append(int(port))
86 irc_ports.append(int(port))
87 except ValueError:
88 op.error("bad port: %r" % port)
89 options.ports = ports
90 options.irc_ports = irc_ports
91 server = Server(options)
92 if options.daemon:
93 server.daemonize()
(1 . 3)(1 . 5)
98 import os
99
100 class Channel(object):
101 def __init__(self, server, name):
102 self.server = server
- 622DC40643FD936B33DF0F60BAC3167453DD1684021504C1D7201E07AEA2140B17C4D857CC106DDD6DF9963A8003E84D938696796C5D2001DE2B68DBE5533F23(485 . 8)(485 . 11)- AE8FE58A8F92689212F3CCDB9A30A530CD19B0CE5AD7AEECDF5EEE1C6C9505AE56DF43A3132CA534D0BDB745D469CDBCE7F2A51EF30900259DADA9E894288827
107
108 def peer_handler():
109 if len(arguments) == 1:
110 self.server.state.add_peer(arguments[0])
111 self.pest_reply("added new peer %s" % arguments[0])
112 try:
113 self.server.state.add_peer(arguments[0])
114 self.pest_reply("added new peer %s" % arguments[0])
115 except:
116 self.pest_reply("error attempting to add peer %s" % arguments[0])
117 else:
118 self.pest_reply("Usage: PEER <HANDLE>")
119
(538 . 7)(541 . 8)
121 address_ip, port = string.split(address, ":")
122 self.server.state.update_address_table({"handle": handle,
123 "address": address_ip,
124 "port": port})
125 "port": port},
126 False)
127 self.pest_reply("updated address table: %s %s" % (handle, address))
128 return
129 elif len(arguments) > 2:
(548 . 7)(552 . 7)
131 for address in at:
132 self.pest_reply("%s %s %s" % (address["handle"],
133 address["address"],
134 address["updated_at"]))
135 address["active_at"]))
136 else:
137 self.pest_reply("no results")
138
(24 . 6)(24 . 10)- B26A086B1BEDE8FCFE1AF5AA32D9FEFCAE5BA0011D32CA698B65B2B5026734F9F8B8BA18B6D3925572308CFFDEF5454D595F17A2A74F9775CF0748DFE468C776
143 MESSAGE_PACKET_FORMAT = "<q32s32s32s324s"
144 MAX_MESSAGE_LENGTH = 428
145 MAX_BOUNCES = 3
146 STALE_PACKET = 0
147 DUPLICATE_PACKET = 1
148 MALFORMED_PACKET = 2
149 INVALID_SIGNATURE = 3
150
151 class Infosec(object):
152 def __init__(self, server=None):
(110 . 15)(114 . 14)
154 black_packet_bytes, signature_bytes = struct.unpack(BLACK_PACKET_FORMAT, black_packet)
155 except:
156 self.server.print_error("Discarding malformed black packet from %s" % peer.get_key())
157 return None
158 return { "error_code": MALFORMED_PACKET }
159
160 # check signature
161
162 signature_check_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest()
163
164 if(signature_check_bytes != signature_bytes):
165 self.server.print_debug("rejected black packet with bad signature for %s: %s" % (peer.get_key(), base64.b64encode(signature_check_bytes)))
166 return None
167 return { "error_code": INVALID_SIGNATURE }
168
169 # try to decrypt black packet
170
(137 . 12)(140 . 10)
172 # check timestamp
173
174 if(int_ts not in self._ts_range()):
175 self.server.print_debug("rejected message with timestamp out of range")
176 return None
177 return { "error_code": STALE_PACKET }
178
179 if(self.server.recent.has(int_ts)):
180 self.server.print_debug("rejected known message: %d" % int_ts)
181 return None
182 return { "error_code": DUPLICATE_PACKET }
183 else:
184 self.server.recent.insert(int_ts)
185
(175 . 7)(176 . 8)
187 "bounces": bounces,
188 "self_chain": self_chain,
189 "net_chain": net_chain,
190 "self_chain_valid": self_chain_valid }
191 "self_chain_valid": self_chain_valid,
192 "error_code": None}
193
194 def _pad(self, text, size):
195 return text.ljust(size)
(23 . 10)(23 . 13)
200 def send(self, msg):
201 try:
202 full_message = str.encode(msg["body"])
203 self.server.print_debug("sending message: %s" % full_message)
204
205 signed_packet_bytes = self.infosec.pack(self, msg)
206 self.server.print_debug("packing message: %s" % full_message)
207 self.socket.sendto(signed_packet_bytes, (self.address, self.port))
208 self.server.print_debug("[%s:%d] <- %s" % (self.address,
209 self.port,
210 binascii.hexlify(signed_packet_bytes)[0:16]))
211
212 except Exception as ex:
213 print("Exception while attempting to encode message: %s" % ex)
- 851FE9967EA2562F0BD30255F3E9B1CA1487E7FD56A649E93A0EFBA4D6DE28A70F0FF6C1B3DDBEF90EE4D6370403D18A27A31CDCD062A46E7116DB5F4479F7A3(1 . 4)(1 . 4)- 5E6216FB339F461E197C6C2F878148CBF2032CE6C433F83DF2C29E88B31C467F9C8B689E14D5B0471A6275E7A40E286AC9DBEA69C60D3A16171E4BAE6B091339
218 VERSION = "9992"
219 VERSION = "9991"
220
221 import os
222 import select
(8 . 12)(8 . 18)
224 import tempfile
225 import time
226 import string
227 import binascii
228 import datetime
229 from datetime import datetime
230 from lib.client import Client
231 from lib.state import State
232 from lib.channel import Channel
233 from lib.infosec import PACKET_SIZE
234 from lib.infosec import MAX_BOUNCES
235 from lib.infosec import STALE_PACKET
236 from lib.infosec import DUPLICATE_PACKET
237 from lib.infosec import MALFORMED_PACKET
238 from lib.infosec import INVALID_SIGNATURE
239 from lib.infosec import Infosec
240 from lib.peer import Peer
241 from lib.ringbuffer import Ringbuffer
(25 . 10)(31 . 9)
243
244 class Server(object):
245 def __init__(self, options):
246 self.ports = options.ports
247 self.irc_ports = options.irc_ports
248 self.udp_port = options.udp_port
249 self.password = options.password
250 self.ssl_pem_file = options.ssl_pem_file
251 self.motdfile = options.motd
252 self.verbose = options.verbose
253 self.debug = options.debug
(112 . 7)(117 . 7)
255
256 def print_debug(self, msg):
257 if self.debug:
258 print(msg)
259 print("%s %s" % (datetime.now(), msg))
260 sys.stdout.flush()
261
262 def print_error(self, msg):
(144 . 11)(149 . 16)
264 def handle_udp_data(self, bytes_address_pair):
265 data = bytes_address_pair[0]
266 address = bytes_address_pair[1]
267 packet_info = (address[0],
268 address[1],
269 binascii.hexlify(data)[0:16])
270 self.print_debug("[%s:%d] -> %s" % packet_info)
271 for peer in self.state.get_peers():
272 if peer.get_key() != None:
273 message = self.infosec.unpack(peer, data)
274 if(message != None):
275 self.print_debug("valid message from peer: %s" % peer.handles[0])
276 error_code = message["error_code"]
277 if(error_code == None):
278 self.print_debug("[%s] -> %s" % (peer.handles[0], message["body"]))
279
280 # we only update the address table if the speaker is same as peer
281
(159 . 8)(169 . 8)
283
284 if idx != None:
285 self.state.update_address_table({"handle": message["speaker"],
286 "address": address[0],
287 "port": address[1]
288 "address": address[0],
289 "port": address[1]
290 })
291 # send the message to all clients
292 for c in self.clients:
(171 . 7)(181 . 18)
294 if(message["command"] == BROADCAST) and message["bounces"] < MAX_BOUNCES:
295 self.rebroadcast(peer, message)
296 return
297 self.print_debug("Unknown peer: %s %d" % (address[0], address[1]))
298 elif error_code == STALE_PACKET:
299 self.print_debug("[%s:%d] -> stale packet: %s" % packet_info)
300 return
301 elif error_code == DUPLICATE_PACKET:
302 self.print_debug("[%s:%d] -> duplicate packet: %s" % packet_info)
303 return
304 elif error_code == MALFORMED_PACKET:
305 self.print_debug("[%s:%d] -> malformed packet: %s" % packet_info)
306 return
307 elif error_code == INVALID_SIGNATURE:
308 pass
309 self.print_debug("[%s:%d] -> martian packet: %s" % packet_info)
310
311 def peer_message(self, message):
312 message["original"] = True
(201 . 9)(222 . 10)
314 # Setup UDP first
315 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
316 self.udp_server_socket.bind((self.address, self.udp_port))
317 self.print_info("Listening for Pest packets on udp port %d." % self.udp_port)
318
319 serversockets = []
320 for port in self.ports:
321 for port in self.irc_ports:
322 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
323 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
324 try:
(214 . 7)(236 . 7)
326 s.listen(5)
327 serversockets.append(s)
328 del s
329 self.print_info("Listening on port %d." % port)
330 self.print_info("Listening for IRC connections on port %d." % port)
331 if self.chroot:
332 os.chdir(self.chroot)
333 os.chroot(self.chroot)
(232 . 7)(254 . 7)
335 [x.socket for x in self.clients.values()
336 if x.write_queue_size() > 0],
337 [],
338 0)
339 1)
340 for x in inputready:
341 if x == self.udp_server_socket:
342 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
(242 . 19)(264 . 6)
344 self.clients[x].socket_readable_notification()
345 else:
346 (conn, addr) = x.accept()
347 if self.ssl_pem_file:
348 import ssl
349 try:
350 conn = ssl.wrap_socket(
351 conn,
352 server_side=True,
353 certfile=self.ssl_pem_file,
354 keyfile=self.ssl_pem_file)
355 except ssl.SSLError as e:
356 self.print_error(
357 "SSL error for connection from %s:%s: %s" % (
358 addr[0], addr[1], e))
359 continue
360 self.clients[conn] = Client(self, conn)
361 self.print_info("Accepted connection from %s:%s." % (
362 addr[0], addr[1]))
(13 . 6)(13 . 7)- 158EE3120EDAF743C2990BADDEA12D89E0626521BA45034B0C92CD182D75CEA88F19758460C4758439ED866F902749B3217333613EC1F4668E9257A95C71C138
367 self.cursor.execute("create table if not exists at(handle_id integer,\
368 address text not null,\
369 port integer not null,\
370 active_at datetime default null,\
371 updated_at datetime default current_timestamp,\
372 unique(handle_id, address, port))")
373
(37 . 7)(38 . 7)
375 def get_at(self, handle=None):
376 at = []
377 if handle == None:
378 results = self.cursor.execute("select handle_id,address,port,updated_at from at\
379 results = self.cursor.execute("select handle_id,address,port,active_at from at\
380 order by updated_at desc").fetchall()
381 else:
382 result = self.cursor.execute("select handle_id from handles where handle=?",
(46 . 7)(47 . 7)
384 handle_id = result[0]
385 else:
386 return []
387 results = self.cursor.execute("select handle_id,address,port,updated_at from at \
388 results = self.cursor.execute("select handle_id,address,port,active_at from at \
389 where handle_id=? order by updated_at desc",
390 (handle_id,)).fetchall()
391 for result in results:
(55 . 7)(56 . 7)
393 (handle_id,)).fetchone()[0]
394 at.append({"handle": h,
395 "address": "%s:%s" % (address, port),
396 "updated_at": updated_at})
397 "active_at": updated_at})
398 return at
399
400
(96 . 30)(97 . 33)
402 self.cursor.execute("insert into handles(peer_id, handle) values(?, ?)",
403 (peer_id, peer["name"]))
404 handle_id = self.cursor.lastrowid
405 self.cursor.execute("insert into at(handle_id, address, port) values(?, ?, ?)",
406 (handle_id, peer["address"], peer["port"]))
407 self.cursor.execute("insert into at(handle_id, address, port, updated_at) values(?, ?, ?, ?)",
408 (handle_id, peer["address"], peer["port"], None))
409 self.cursor.execute("insert into keys(peer_id, key) values(?, ?)",
410 (peer_id, key))
411
412 self.conn.commit()
413
414 def update_address_table(self, peer):
415 def update_address_table(self, peer, set_active_at=True):
416 row = self.cursor.execute("select handle_id from handles where handle=?",
417 (peer["handle"],)).fetchone()
418 if row != None:
419 handle_id = row[0]
420 else:
421 return
422
423
424 try:
425 self.cursor.execute("insert into at(handle_id, address, port) values(?, ?, ?)",
426 (handle_id, peer["address"], peer["port"]))
427 self.conn.commit()
428 except sqlite3.IntegrityError as ex:
429 self.cursor.execute("update at set updated_at = current_timestamp\
430 where handle_id=? and address=? and port=?",
431 (handle_id, peer["address"], peer["port"]))
432 self.conn.commit()
433 if set_active_at:
434 self.cursor.execute("update at set active_at = current_timestamp\
435 where handle_id=? and address=? and port=?",
436 (handle_id, peer["address"], peer["port"]))
437 self.conn.commit()
438
439 def add_peer(self, handle):
440 self.cursor.execute("insert into wot(peer_id) values(null)")
(127 . 6)(131 . 7)
442 self.cursor.execute("insert into handles(peer_id, handle) values(?, ?)",
443 (peer_id, handle))
444 self.conn.commit()
445
446
447 def remove_peer(self, handle):
448 # get peer id
(1 . 6)(1 . 6)
453 #!/bin/bash
454
455 # start 3 servers on different ports
456 ./blatta --debug --port 6668 --udp-port 7778 --db-path a.db --address-table-path test_net_configs/a.py > logs/a &
457 ./blatta --debug --port 6669 --udp-port 7779 --db-path b.db --address-table-path test_net_configs/b.py > logs/b &
458 ./blatta --debug --port 6670 --udp-port 7780 --db-path c.db --address-table-path test_net_configs/c.py > logs/c &
459 ./blatta --debug --irc-port 6668 --udp-port 7778 --db-path a.db --address-table-path test_net_configs/a.py > logs/a &
460 ./blatta --debug --irc-port 6669 --udp-port 7779 --db-path b.db --address-table-path test_net_configs/b.py > logs/b &
461 ./blatta --debug --irc-port 6670 --udp-port 7780 --db-path c.db --address-table-path test_net_configs/c.py > logs/c &