diff --git a/ipsc.py b/ipsc.py index 23ca7eb..e73da68 100644 --- a/ipsc.py +++ b/ipsc.py @@ -105,6 +105,23 @@ def validate_auth(_key, _data): _log(' AUTH: Invalid - Payload: %s, Hash: %s', binascii.b2a_hex(_payload), binascii.b2a_hex(_hash)) return False +# Forward Group Voice Packet +# THIS IS BROKEN - BEGIN WORK HERE. REPLACING SEGMENTS ISN'T REFERENCING THE RIGHT SUBSTITUTE DATA AND +# I'M GOIGN TO BED NOW. +def fwd_group_voice(_network, _data): + _src_group = _data[9:12] + _src_ipsc = _data[1:5] + for source in NETWORK[_network]['GROUP_VOICE']: + print(binascii.b2a_hex(_src_group), ' ', binascii.b2a_hex(source['SRC_GROUP'])) + if source['SRC_GROUP'] == _src_group: + _data.replace(_src_ipsc, NETWORK[source]['DST_NET']['LOCAL']['RADIO_ID']) + _data.replace(_src_group, source['DST_GROUP']) + _data = hashed_packet(NETWORK[source]['DST_NET']['LOCAL']['AUTH_KEY'], _data) + print(binascii.b2a_hex(_data)) + # Match source group to a rule + # Write destination group to packet + # Hash packet + # Send packet # Take a recieved peer list and the network it belongs to, process and populate the # data structure in my_ipsc_config with the results. @@ -238,7 +255,12 @@ class IPSC(DatagramProtocol): # self._peers = self._config['PEERS'] # + # This is a regular list to store peers for the IPSC. At times, parsing a simple list is much less + # Spendy than iterating a list of dictionaries... Maybe I'll find a better way in the future. Also + # We have to know when we have a new peer list, so a variable to indicate we do (or don't) + # self._peer_list = [] + self._peer_list_new = False args = () @@ -252,7 +274,7 @@ class IPSC(DatagramProtocol): self.PEER_REG_REPLY_PKT = (PEER_REG_REPLY + self._local_id + IPSC_VER) self.PEER_ALIVE_REQ_PKT = (PEER_ALIVE_REQ + self._local_id + self.TS_FLAGS) self.PEER_ALIVE_REPLY_PKT = (PEER_ALIVE_REPLY + self._local_id + self.TS_FLAGS) - self._peer_list_new = False + else: # If we didn't get called correctly, log it! # @@ -278,20 +300,15 @@ class IPSC(DatagramProtocol): # TIMED LOOP - MY CONNECTION MAINTENANCE #************************************************ - def timed_loop(self): - logger.debug('timed loop started') # temporary debugging to make sure this part runs - + def timed_loop(self): print_peer_list(self._network) - - _master_connected = self._master_stat['CONNECTED'] - _peer_list_rx = self._master_stat['PEER-LIST'] - if (_master_connected == False): + if (self._master_stat['CONNECTED'] == False): reg_packet = hashed_packet(self._local['AUTH_KEY'], self.MASTER_REG_REQ_PKT) self.transport.write(reg_packet, (self._master_sock)) logger.debug('->> (%s) Master Registration Request To:%s From:%s', self._network, self._master_sock, binascii.b2a_hex(self._local_id)) - elif (_master_connected == True): + elif (self._master_stat['CONNECTED'] == True): master_alive_packet = hashed_packet(self._local['AUTH_KEY'], self.MASTER_ALIVE_PKT) self.transport.write(master_alive_packet, (self._master_sock)) logger.debug('->> (%s) Master Keep-alive %s Sent To:%s', self._network, self._master_stat['KEEP_ALIVES_SENT'], self._master_sock) @@ -309,12 +326,12 @@ class IPSC(DatagramProtocol): else: logger.error('->> (%s) Master in UNKOWN STATE:%s:%s', self._network, self._master_sock) - if ((_master_connected == True) and (_peer_list_rx == False)): + if ((self._master_stat['CONNECTED'] == True) and (self._master_stat['PEER-LIST'] == False)): peer_list_req_packet = hashed_packet(self._local['AUTH_KEY'], self.PEER_LIST_REQ_PKT) self.transport.write(peer_list_req_packet, (self._master_sock)) logger.debug('->> (%s) List Reqested from Master:%s', self._network, self._master_sock) - if (_peer_list_rx == True): + if (self._master_stat['PEER-LIST'] == True): for peer in (self._peers): if (peer['RADIO_ID'] == self._local_id): # We are in the peer-list, but don't need to talk to ourselves continue @@ -332,8 +349,8 @@ class IPSC(DatagramProtocol): if peer['STATUS']['KEEP_ALIVES_OUTSTANDING'] >= self._local['MAX_MISSED']: peer['STATUS']['CONNECTED'] = False - self._peer_list.remove(peer['RADIO_ID']) - self._peers.remove(peer) + self._peer_list.remove(peer['RADIO_ID']) # Remove the peer from the simple list FIRST + self._peers.remove(peer) # Becuase once it's out of the dictionary, you can't use it for anything else. logger.error('Maximum Peer Keep-Alives Missed -- De-registering the Peer: %s', peer) peer['STATUS']['KEEP_ALIVES_SENT'] += 1 @@ -347,8 +364,7 @@ class IPSC(DatagramProtocol): # RECEIVED DATAGRAM - ACT IMMEDIATELY!!! #************************************************ - # Work in progress -- at the very least, notify we have the packet. Ultimately - # call a function or process immediately if only a few actions + # Actions for recieved packets by type: Call a function or process here... # def datagramReceived(self, data, (host, port)): logger.debug('received %r from %s:%d', binascii.b2a_hex(data), host, port) @@ -357,17 +373,26 @@ class IPSC(DatagramProtocol): _peerid = data[1:5] _dec_peerid = int(binascii.b2a_hex(_peerid), 16) + # First action: if Authentication is active, authenticate the packet + # if bool(self._local['AUTH_KEY']) == True: if validate_auth(self._local['AUTH_KEY'], data) == False: logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, binascii.b2a_hex(_packettype), _dec_peerid) return + data = strip_hash(data) + # Packets generated by "users" that are the most common should come first for efficiency. + # if (_packettype == GROUP_VOICE): if not(valid_master(self._network, _peerid) == False or valid_peer(self._peer_list, _peerid) == False): logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, _dec_peerid) return + fwd_group_voice(self._network, data) + logger.debug('<<- (%s) Group Voice Packet From:%s:%s', self._network, host, port) + # IPSC keep alives, master and peer, come next in processing priority + # elif (_packettype == PEER_ALIVE_REQ): if valid_peer(self._peer_list, _peerid) == False: @@ -392,7 +417,10 @@ class IPSC(DatagramProtocol): for peer in self._config['PEERS']: if peer['RADIO_ID'] == _peerid: peer['STATUS']['KEEP_ALIVES_OUTSTANDING'] = 0 - + + # Registration requests and replies are infrequent, but important. Peer lists can go here too as a part + # of the registration process. + # elif (_packettype == MASTER_REG_REQ): logger.debug('<<- (%s) Master Registration Packet Recieved', self._network) @@ -414,13 +442,12 @@ class IPSC(DatagramProtocol): if peer['RADIO_ID'] == _peerid: peer['STATUS']['CONNECTED'] = True - elif (_packettype == XCMP_XNL): - logger.debug('<<- (%s) XCMP_XNL From:%s:%s, but we did not indicate XCMP capable!', self._network, host, port) - elif (_packettype == PEER_LIST_REPLY): logger.debug('<<- (%s) Peer List Received From:%s:%s', self._network, host, port) self._peer_list = process_peer_list(data, self._network, self._peer_list) - + + # Other "user" related packet types that we don't do much or anything with yet + # elif (_packettype == PVT_VOICE): logger.debug('<<- (%s) Voice Packet From:%s:%s', self._network, host, port) @@ -430,18 +457,24 @@ class IPSC(DatagramProtocol): elif (_packettype == PVT_DATA): logger.debug('<<- (%s) Private Data Packet From From:%s:%s', self._network, host, port) - elif (_packettype == RPT_WAKE_UP): - logger.debug('<<- (%s) Repeater Wake-Up Packet From:%s:%s', self._network, host, port) - elif (_packettype == DE_REG_REQ): logger.debug('<<- (%s) Peer De-Registration Request From:%s:%s', self._network, host, port) elif (_packettype == DE_REG_REPLY): logger.debug('<<- (%s) Peer De-Registration Reply From:%s:%s', self._network, host, port) + elif (_packettype == RPT_WAKE_UP): + logger.debug('<<- (%s) Repeater Wake-Up Packet From:%s:%s', self._network, host, port) + + # Technically, we're not paying any attention to these types because we're not part of the XCMP call control structure + # + elif (_packettype == XCMP_XNL): + logger.debug('<<- (%s) XCMP_XNL From:%s:%s, but we did not indicate XCMP capable!', self._network, host, port) + elif (_packettype in (CALL_CTL_1, CALL_CTL_2, CALL_CTL_3)): logger.debug('<<- (%s) Call Control Packet From:%s:%s', self._network, host, port) + # If there's a packet type we don't know aobut, it should be logged so we can figure it out and take an appropriate action! else: packet_type = binascii.b2a_hex(_packettype) logger.error('<<- (%s) Received Unprocessed Type %s From:%s:%s', self._network, packet_type, host, port) diff --git a/my_ipsc_config_SAMPLE.py b/my_ipsc_config_SAMPLE.py index e4ea246..20130d1 100644 --- a/my_ipsc_config_SAMPLE.py +++ b/my_ipsc_config_SAMPLE.py @@ -9,6 +9,9 @@ # Configuration file for IPSC.py -- each network has several parts, some of this is muted by the script once it runs, thus there are placeholders NETWORK = { 'IPSC1': { + 'GROUP_VOICE': [ + {'SRC_GROUP': b'\x00\x00\x01', 'DST_NET': 'IPSC2', 'DST_GROUP': b'\x00\x00\x02'} + ], 'LOCAL': { 'MODE': b'\x6A', # Decoded values below 'PEER_OPER': True,