From bd5b0a074de46784ad915806d91037e9c500478e Mon Sep 17 00:00:00 2001 From: Cort Buffington Date: Thu, 11 Jul 2013 19:45:09 -0500 Subject: [PATCH] Initial Uplaod of Re-Org'd Files --- README.md | 3 +- ipsc.py | 305 +++++++++++++++++++----------------------- ipsc_auth.py | 25 ---- ipsc_logger.py | 55 ++++++++ ipsc_mask.py | 43 ++++++ ipsc_message_types.py | 38 ++++++ list.py | 137 ------------------- 7 files changed, 273 insertions(+), 333 deletions(-) delete mode 100644 ipsc_auth.py create mode 100644 ipsc_logger.py create mode 100644 ipsc_mask.py create mode 100644 ipsc_message_types.py delete mode 100644 list.py diff --git a/README.md b/README.md index 9adda44..4614e0d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ The following sections of this document will include various packet types. This CALL_CTL_3 = 0x63 | XCMP_XNL = 0x70 Control protocol messages GROUP_VOICE = 0x80 This is a group voice call + PVT_VOICE = 0x81 This is a private voice call GROUP_DATA = 0x83 This is a group data call PVT_DATA = 0x84 This is a private data call RPT_WAKE_UP = 0x85 Wakes up all repeaters on the IPSC @@ -118,7 +119,7 @@ PEER LIST RESPONSE: Byte 1 - 0x00 = Unknown Byte 2 - 0x00 = Unknown Byte 3 - BIT FLAGS: - x... .... = CBSK Message + x... .... = CSBK Message .x.. .... = Repeater Call Monitoring ..x. .... = 3rd Party "Console" Application ...x xxxx = Unknown - default to 0 diff --git a/ipsc.py b/ipsc.py index 66c8eea..71aa1fa 100644 --- a/ipsc.py +++ b/ipsc.py @@ -2,141 +2,88 @@ from __future__ import print_function from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor from twisted.internet import task +import sys import argparse import binascii import hmac import hashlib -import logging -from logging.config import dictConfig - -dictConfig({ - 'version': 1, - 'disable_existing_loggers': False, - 'filters': { - }, - 'formatters': { - 'verbose': { - 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' - }, - 'timed': { - 'format': '%(levelname)s %(asctime)s %(message)s' - }, - 'simple': { - 'format': '%(levelname)s %(message)s' - }, - }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'simple' - }, - 'file': { - 'level': 'DEBUG', - 'class': 'logging.FileHandler', - 'formatter': 'simple', - 'filename': '/tmp/ipsc.log', - }, - 'console-timed': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'timed' - }, - 'file-timed': { - 'level': 'DEBUG', - 'class': 'logging.FileHandler', - 'formatter': 'timed', - 'filename': '/tmp/ipsc.log', - }, - }, - 'loggers': { - 'ipsc': { -# 'handlers': ['file-timed', 'console-timed'], - 'handlers': ['file', 'console'], - 'level': 'DEBUG', - 'propagate': True, - } - } -}) -logger = logging.getLogger('ipsc') +#from logging.config import dictConfig +#import logging -# Data structure for holding IPSC information + +#************************************************ +# IMPORTING OTHER FILES - '#include' +#************************************************ + +# Import system logger configuration +try: + from ipsc_logger import logger +except ImportError: + sys.exit('System logger configuraiton not found or invalid') + +# Import configuration and informational data structures try: from my_ipsc_config import NETWORK except ImportError: - NETWORK = { - 'IPSC1': { - 'LOCAL': { - 'DESCRIPTION': 'IPSC Network #1', - 'MODE': b'\x65', - 'FLAGS': b'\x00\x00\x80\xDC', - 'PORT': 50001, - 'ALIVE_TIMER': 10, # Seconds between keep-alives and registration attempts - 'RADIO_ID': binascii.unhexlify('0000000A'), - 'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000000001') - }, - 'MASTER': { - 'IP': '1.2.3.4', - 'PORT': 50000, - 'STATUS': { - 'RADIO_ID': '', - 'CONNECTED': 0, - 'KEEP_ALIVES_MISSED': 0, - 'MODE': b'\x00', - 'FLAGS': b'\x00\x00\x00\x00', - } - }, - 'PEERS': [] - } - } -# each list item contains { -# 'IP': '100.200.1.1', -# 'PORT': 50000, -# 'RADIO_ID': b'\x00\x00\x00\xFF', -# 'STATUS': { -# 'CONNECTED': 0, -# 'KEEP_ALIVES_MISSED': 0 -# } -# }, -# -# IPSC2.... etc... repeat as many times as you have resources for + sys.exit('Configuration file not found, or not valid formatting') + +# Import IPSC message types and version information +try: + from ipsc_message_types import * +except ImportError: + sys.exit('IPSC message types file not found or invalid') + +# Import IPSC flag mask values +try: + from ipsc_mask import * +except ImportError: + sys.exit('IPSC mask values file not found or invalid') + -# Known IPSC Message Types -CALL_CTL_1 = b'\x61' # | -CALL_CTL_2 = b'\x62' # | Exact meaning unknown -CALL_CTL_3 = b'\x63' # | -XCMP_XNL = b'\x70' # XCMP/XNL control message -GROUP_VOICE = b'\x80' -GROUP_DATA = b'\x83' -PVT_DATA = b'\x84' -RPT_WAKE_UP = b'\x85' # Similar to OTA DMR "wake up" -MASTER_REG_REQ = b'\x90' # FROM peer TO master -MASTER_REG_REPLY = b'\x91' # FROM master TO peer -PEER_LIST_REQ = b'\x92' -PEER_LIST_REPLY = b'\x93' -PEER_REG_REQUEST = b'\x94' # Peer registration request -PEER_REG_REPLY = b'\x95' # Peer registration reply -MASTER_ALIVE_REQ = b'\x96' # FROM peer TO master -MASTER_ALIVE_REPLY = b'\x97' # FROM master TO peer -PEER_ALIVE_REQ = b'\x98' # Peer keep alive request -PEER_ALIVE_REPLY = b'\x99' # Peer keep alive reply -DE_REG_REQ = b'\x9A' # Request de-registration from system -DE_REG_REPLY = b'\x9B' # De-registration reply +#************************************************ +# GLOBALLY SCOPED FUNCTIONS +#************************************************ -# IPSC Version Information -IPSC_OP_VER = b'\x04\x03' # 0x04, 0x03 -- seems to be current version of IPSC -IPSC_OLD_VER = b'\x04\x00' # 0x04, 0x02 -- oldest version of IPSC suppoerted -IPSC_VER = IPSC_OP_VER + IPSC_OLD_VER +def hashed_packet(_key, _data): + hash = binascii.unhexlify((hmac.new(_key,_data,hashlib.sha1)).hexdigest()[:20]) + return (_data + hash) +def validate_auth(_key, _data): + return -def hashed_packet(key, data): - hash = binascii.unhexlify((hmac.new(key,data,hashlib.sha1)).hexdigest()[:20]) - return (data + hash) - +def print_mode_decode(_mode): + _mode = int(_mode, 16) + link_op = _mode & PEER_OP_MSK + link_mode = _mode & PEER_MODE_MSK + ts1 = _mode & IPSC_TS1_MSK + ts2 = _mode & IPSC_TS2_MSK + + if link_op == 0b01000000: + logger.info('\t\tPeer Operational') + elif link_op == 0b00000000: + logger.info('\t\tPeer Not Operational') + else: + logger.warning('\t\tPeer Mode Invalid') + + if link_mode == 0b00000000: + logger.info('\t\tNo RF Interface') + elif link_mode == 0b00010000: + logger.info('\t\tRadio in Analog Mode') + elif link_mode == 0b00100000: + logger.info('\t\tRadio in Digital Mode') + else: + logger.warning('\t\tRadio Mode Invalid') + + if ts1 == 0b00001000: + logger.info('\t\tIPSC Enabled on TS1') + + if ts2 == 0b00000010: + logger.info('\t\tIPSC Enabled on TS2') + def print_peer_list(_ipsc_network): - logger.info('%s', _ipsc_network['LOCAL']['DESCRIPTION']) + logger.info('\t%s', _ipsc_network['LOCAL']['DESCRIPTION']) for dictionary in _ipsc_network['PEERS']: hex_address = dictionary['IP'] hex_port = dictionary['PORT'] @@ -149,8 +96,14 @@ def print_peer_list(_ipsc_network): logger.info('\tIP Address: %s.%s.%s.%s:%s', address[0], address[1], address[2], address[3], port) logger.info('\tRADIO ID: %s ', radio_id) - logger.info("\tIPSC Mode: %s", hex_mode) + logger.info("\tIPSC Mode:") + print_mode_decode(hex_mode) logger.info("") + + +#************************************************ +# IPSC Network Engine +#************************************************ class IPSC(DatagramProtocol): @@ -173,35 +126,34 @@ class IPSC(DatagramProtocol): # logger.debug("keepAlive Routine Running in Condition %s", _master_connected) if (_master_connected == 0): - logger.info("*** Starting up IPSC Client and Registering to the Master ***") reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_REG_REQ_PKT) self.transport.write(reg_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT'])) - logger.info("->> Sending Registration to Master:%s:%sFrom:%s\n", self._config['MASTER']['IP'], self._config['MASTER']['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID'])) + logger.info("->> Master Registration Request To:%s:%s From:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID'])) elif (_master_connected in (1,2)): if (_master_connected == 1): peer_list_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_LIST_REQ_PKT) self.transport.write(peer_list_req_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT'])) - logger.info("->> Peer List Reqested from Master:%s:%s\n", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) + logger.info("->> List Reqested from Master:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT) self.transport.write(master_alive_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT'])) - logger.info("->> Master Keep Alive Sent To:%s:%s\n", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) + logger.info("->> Master Keep-alive Sent To:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) elif (_master_connected == 2): master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT) self.transport.write(master_alive_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT'])) - logger.info("->> Master Keep Alive Sent To:%s:%s\n", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) + logger.info("->> Master Keep-alive Sent To:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) else: - logger.error("->> Resetting Masteter in UNKOWN STATE:%s:%s\n", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) + logger.error("->> Master in UNKOWN STATE:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT']) # logger.debug("keepAlive Routine ending at Condition %s", _master_connected) def startProtocol(self): - logger.debug("*** config: %s", self._config) - logger.info("") + #logger.debug("*** config: %s", self._config) + #logger.info("") self._call = task.LoopingCall(self.keepAlive) self._loop = self._call.start(self._config['LOCAL']['ALIVE_TIMER']) @@ -212,40 +164,41 @@ class IPSC(DatagramProtocol): #logger.info("received %r from %s:%d", binascii.b2a_hex(data), host, port) _packettype = (data[0:1]) + _peerid = (data[1:5]) - if (_packettype == MASTER_REG_REQ): - logger.info("<<- Registration Packet Recieved\n") + if (_packettype == PEER_ALIVE_REQ): + logger.info("<<- Peer Keep-alive Request From Peer ID %s at:%s:%s", int(binascii.b2a_hex(_peerid), 16), host, port) + peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT) + peer_alive_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REPLY_PKT) + self.transport.write(peer_alive_reply_packet, (host, port)) + logger.info("->> Peer Keep-alive Reply sent To:%s:%s", host, port) + self.transport.write(peer_alive_req_packet, (host, port)) + logger.info("->> Peer Keep-alive Request sent To:%s:%s", host, port) + #logger.info(binascii.b2a_hex(peer_alive_req_packet)) + + elif (_packettype == MASTER_ALIVE_REPLY): + logger.info("<<- Master Keep-alive Reply From:%s:%s", host, port) + + elif (_packettype == PEER_ALIVE_REPLY): + logger.info("<<- Peer Keep-alive Reply From:%s:%s", host, port) + + elif (_packettype == MASTER_REG_REQ): + logger.info("<<- Registration Packet Recieved") elif (_packettype == MASTER_REG_REPLY): self._config['MASTER']['STATUS']['CONNECTED'] = 1 logger.info("<<- Master Registration Reply From:%s:%s Setting Condition %s", host, port,self._config['MASTER']['STATUS']['CONNECTED']) elif (_packettype == PEER_REG_REQUEST): - logger.info("<<- Peer Registration Request From:%s:%s", host, port) + logger.info("<<- Peer Registration Request From Peer ID %s at:%s:%s", int(binascii.b2a_hex(_peerid), 16), host, port) peer_reg_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_REG_REPLY_PKT) self.transport.write(peer_reg_reply_packet, (host, port)) - logger.info("->> Peer Registration Reply Sent To:%s:%s\n", host, port) + logger.info("->> Peer Registration Reply Sent To:%s:%s", host, port) #logger.info("%s:%s", host, port) #logger.info(binascii.b2a_hex(peer_reg_reply_packet)) - elif (_packettype == PEER_ALIVE_REQ): - logger.info("<<- Received Peer Keep Alive From:%s:%s", host, port) - peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT) - peer_alive_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REPLY_PKT) - self.transport.write(peer_alive_reply_packet, (host, port)) - logger.info("->> Sent Peer Keep Alive Reply To:%s:%s", host, port) - self.transport.write(peer_alive_req_packet, (host, port)) - logger.info("->> Sent Peer Keep Alive Request To:%s:%s\n", host, port) - #logger.info(binascii.b2a_hex(peer_alive_req_packet)) - - elif (_packettype == MASTER_ALIVE_REPLY): - logger.info("<<- Keep Alive Received from Master:%s:%s\n", host, port) - - elif (_packettype == PEER_ALIVE_REPLY): - logger.info("<<- Keep Alive Received from Peer:%s:%s\n", host, port) - elif (_packettype == XCMP_XNL): - logger.info("<<- XCMP_XNL and/or Control Packet From:%s:%s\n", host, port) + logger.warning("<<- XCMP_XNL Packet From:%s:%s - We did not indicate XCMP capable!", host, port) elif (_packettype == PEER_LIST_REPLY): logger.info("<<- The Peer List has been Received from Master:%s:%s Setting Condition 2", host, port) @@ -263,28 +216,40 @@ class IPSC(DatagramProtocol): print_peer_list(self._config) logger.info("") - + elif (_packettype == GROUP_VOICE): + logger.info("<<- Group Voice Packet From:%s:%s", host, port) + + elif (_packettype == PVT_VOICE): + logger.info("<<- Voice Packet From:%s:%s", host, port) + + elif (_packettype == GROUP_DATA): + logger.info("<<- Group Data Packet From:%s:%s", host, port) + + elif (_packettype == PVT_DATA): + logger.info("<<- Private Data Packet From From:%s:%s", host, port) + + elif (_packettype == RPT_WAKE_UP): + logger.info("<<- Repeater Wake-Up Packet From:%s:%s", host, port) + + elif (_packettype == DE_REG_REQ): + logger.info("<<- Peer De-Registration Request From:%s:%s", host, port) + + elif (_packettype == DE_REG_REPLY): + logger.info("<<- Peer De-Registration Reply From:%s:%s", host, port) + + elif (_packettype in (CALL_CTL_1, CALL_CTL_2, CALL_CTL_3)): + logger.info("<<- Call Control Packet From:%s:%s", host, port) + else: packet_type = binascii.b2a_hex(_packettype) -# logger.error("<<- Received Unprocessed Type %s From:%s:%s\n", packet_type, host, port) + logger.error("<<- Received Unprocessed Type %s From:%s:%s", packet_type, host, port) +#************************************************ +# MAIN PROGRAM LOOP STARTS HERE +#************************************************ if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Start an IPSC client.") - parser.add_argument('-n', '--network', required=False) - args = parser.parse_args() - - if args.network is not None: - if args.network in NETWORK: - logger.info("Connecting to %s", args.network) - reactor.listenUDP(NETWORK[args.network]['LOCAL']['PORT'], IPSC(NETWORK[args.network])) - else: - logger.info("%s is not a configured ISPC network.", args.network) - exit() - - else: # connect to all - logger.info("No network supplied, connecting to all networks.") - for ipsc_network in NETWORK: - reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], IPSC(NETWORK[ipsc_network])) - - reactor.run() + logger.info('SYSTEM STARTING UP') + for ipsc_network in NETWORK: + reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], IPSC(NETWORK[ipsc_network])) + reactor.run() \ No newline at end of file diff --git a/ipsc_auth.py b/ipsc_auth.py deleted file mode 100644 index cca82d8..0000000 --- a/ipsc_auth.py +++ /dev/null @@ -1,25 +0,0 @@ -import binascii -import hmac -import hashlib - -PAYLOAD = binascii.unhexlify('9000c832686a0000e03c04030402') -AUTH_KEY = binascii.unhexlify('0000000000000000000000000000000000012345') - -print type(PAYLOAD) -print type(AUTH_KEY) -print "" - -HASH = (hmac.new(AUTH_KEY,PAYLOAD,hashlib.sha1)).hexdigest()[:20] - -#PAY_HASH = HASH.hexdigest()[:20] - -print binascii.b2a_hex(PAYLOAD) -print binascii.b2a_hex(AUTH_KEY) -print HASH - -PACKET = binascii.b2a_hex(PAYLOAD) + HASH - -print PACKET - - -# 27 86 3f 89 d5 a7 15 a8 31 55 \ No newline at end of file diff --git a/ipsc_logger.py b/ipsc_logger.py new file mode 100644 index 0000000..4623c20 --- /dev/null +++ b/ipsc_logger.py @@ -0,0 +1,55 @@ +# Logging system configuration + +from logging.config import dictConfig +import logging + +dictConfig({ + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + }, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'timed': { + 'format': '%(levelname)s %(asctime)s %(message)s' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'simple' + }, + 'file': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'formatter': 'simple', + 'filename': '/tmp/ipsc.log', + }, + 'console-timed': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'timed' + }, + 'file-timed': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'formatter': 'timed', + 'filename': '/tmp/ipsc.log', + }, + }, + 'loggers': { + 'ipsc': { +# 'handlers': ['file-timed', 'console-timed'], + 'handlers': ['file-timed', 'console-timed'], + 'level': 'DEBUG', + 'propagate': True, + } + } +}) +logger = logging.getLogger('ipsc') diff --git a/ipsc_mask.py b/ipsc_mask.py new file mode 100644 index 0000000..6fd8081 --- /dev/null +++ b/ipsc_mask.py @@ -0,0 +1,43 @@ +# LINKING STATUS: +# Byte 1 - BIT FLAGS: +# xx.. .... = Peer Operational (01 only known valid value) +# ..xx .... = Peer MODE: 00 - No Radio, 01 - Analog, 10 - Digital +# .... xx.. = IPSC Slot 1: 10 on, 01 off +# .... ..xx = IPSC Slot 2: 10 on, 01 off +# MASK VALUES: +PEER_OP_MSK = 0b11000000 +PEER_MODE_MSK = 0b00110000 +IPSC_TS1_MSK = 0b00001100 +IPSC_TS2_MSK = 0b00000011 + +#SERVICE FLAGS: + +# Byte 1 - 0x00 = Unknown +# Byte 2 - 0x00 = Unknown +# Byte 3 - BIT FLAGS: +# x... .... = CBSK Message +# .x.. .... = Repeater Call Monitoring +# ..x. .... = 3rd Party "Console" Application +# ...x xxxx = Unknown - default to 0 +# MASK VALUES: +CSBK_MSK = 0b10000000 +RPT_MON_MSK = 0b01000000 +CON_APP_MSK = 0b00100000 + +# Byte 4 = BIT FLAGS: +# x... .... = XNL Connected (1=true) +# .x.. .... = XNL Master Device +# ..x. .... = XNL Slave Device +# ...x .... = Set if packets are authenticated +# .... x... = Set if data calls are supported +# .... .x.. = Set if voice calls are supported +# .... ..x. = Unknown - default to 0 +# .... ...x = Set if master +# MASK VALUES: +XNL_STAT_MSK = 0b10000000 +XNL_MSTR_MSK = 0b01000000 +XNL_SLAVE_MSK = 0b00100000 +PKT_AUTH_MSK = 0b00010000 +DATA_CALL_MSK = 0b00001000 +VOICE_CALL_MSK = 0b00000100 +MSTR_PEER_MSK = 0b00000001 \ No newline at end of file diff --git a/ipsc_message_types.py b/ipsc_message_types.py new file mode 100644 index 0000000..8595144 --- /dev/null +++ b/ipsc_message_types.py @@ -0,0 +1,38 @@ +# Known IPSC Message Types +CALL_CTL_1 = b'\x61' # | +CALL_CTL_2 = b'\x62' # | Exact meaning unknown +CALL_CTL_3 = b'\x63' # | +XCMP_XNL = b'\x70' # XCMP/XNL control message +GROUP_VOICE = b'\x80' +PVT_VOICE = b'\x81' +GROUP_DATA = b'\x83' +PVT_DATA = b'\x84' +RPT_WAKE_UP = b'\x85' # Similar to OTA DMR "wake up" +MASTER_REG_REQ = b'\x90' # FROM peer TO master +MASTER_REG_REPLY = b'\x91' # FROM master TO peer +PEER_LIST_REQ = b'\x92' +PEER_LIST_REPLY = b'\x93' +PEER_REG_REQUEST = b'\x94' # Peer registration request +PEER_REG_REPLY = b'\x95' # Peer registration reply +MASTER_ALIVE_REQ = b'\x96' # FROM peer TO master +MASTER_ALIVE_REPLY = b'\x97' # FROM master TO peer +PEER_ALIVE_REQ = b'\x98' # Peer keep alive request +PEER_ALIVE_REPLY = b'\x99' # Peer keep alive reply +DE_REG_REQ = b'\x9A' # Request de-registration from system +DE_REG_REPLY = b'\x9B' # De-registration reply + +# IPSC Version Information +IPSC_VER_14 = b'\x00' +IPSC_VER_15 = b'\x00' +IPSC_VER_15A = b'\x00' +IPSC_VER_16 = b'\x01' +IPSC_VER_17 = b'\x02' +IPSC_VER_18 = b'\x02' +IPSC_VER_19 = b'\x03' +IPSC_VER_22 = b'\x04' + +# Link Type Values - assumed that cap+, etc. are different, this is all I can confirm +LINK_TYPE_IPSC = b'\x04' + +# IPSC Version and Link Type are Used for a 4-byte version field in registration packets +IPSC_VER = LINK_TYPE_IPSC + IPSC_VER_19 + LINK_TYPE_IPSC + IPSC_VER_17 diff --git a/list.py b/list.py deleted file mode 100644 index 95b63f2..0000000 --- a/list.py +++ /dev/null @@ -1,137 +0,0 @@ -import socket -import binascii -import hmac -import hashlib - - -# Data structure for holding IPSC information -NETWORK = { - 'IPSC1': { - 'LOCAL': { - 'DESCRIPTION': 'K0USY Lecompton, KS - Master', - 'MODE': b'\x6A', - 'PORT': 50001, - 'RADIO_ID': binascii.unhexlify('00000001'), - 'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000012345') - }, - 'MASTER': { - 'IP': '24.143.49.121', - 'MODE': b'\x6A', - 'PORT': 50000, - 'RADIO_ID': '', - }, - 'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT - #{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'}, - ] - } -} - -# Known IPSC Message Types -RDAC_CTL = b'\x70' -GROUP_VOICE = b'\x80' -GROUP_DATA = b'\x83' -PVT_DATA = b'\x84' -REG_REQ = b'\x90' -REG_REPLY = b'\x91' -PEER_LIST_REQ = b'\x92' -PEER_LIST_REPLY = b'\x93' -PEER_KEEP_ALIVE_REQ = b'\x94' -PEER_KEEP_ALIVE_REPLY = b'\x95' -KEEP_ALIVE_REQ = b'\x96' -KEEP_ALIVE_REPLY = b'\x97' - -# IPSC information -IPSC_TS_BOTH = b'\x6A' # Both Timeslots IPSC enabled -IPSC_OP_VER = b'\x04\x03' # 0x04, 0x03 -- seems to be current version of IPSC -IPSC_OLD_VER = b'\x04\x00' # 0x04, 0x02 -- oldest version of IPSC suppoerted -IPSC_FLAGS = b'\x00\x00\x80\xDC' # Just accept this... it works, we know some of the pieces - - -#********** FUNCTIONS THAT WE WILL USE - -# function to send a payload to a defined socket -def send_auth_packet (_dest_addr, _dest_port, _socket, _data, _key): - _hash = binascii.unhexlify((hmac.new(_key,_data,hashlib.sha1)).hexdigest()[:20]) - print("==> Sending Authenticated Packet") - print(" Destination IP:", _dest_addr) - print(" Destination UDP Port:", _dest_port) - print(" Raw Packet:", binascii.b2a_hex(_data + _hash)) - _socket.sendto((_data+_hash), (_dest_addr, _dest_port)) - return - -# Note: This function ignores authentiation information!!! -def receive_packet(_socket): - _data = (_socket.recv(1024)) - _peer_id = str(int(binascii.b2a_hex(_data[2:5]), 16)) - _mode = binascii.b2a_hex(_data[5:6]) - print('<== Response Recieved from Radio ID:', _peer_id) - print(' Raw Packet:', binascii.b2a_hex(_data)) - # Parse returned information - - _packettype = (_data[0:1]) - _sock = 'IPSC1' - - if (_packettype == REG_REQ): - print(" >> This is a registration packet") - - elif (_packettype == REG_REPLY): - print(" >> This is a registration reply packet") - - elif (_packettype == PEER_LIST_REPLY): - print(">> This packet is a peer list from the master") - _num_peers = int(str(int(binascii.b2a_hex(_data[5:7]), 16))[1:]) - # print('>>There are', binascii.b2a_hex(_data[5:7]), 'peers in this IPSC (RAW)') - print('>> There are', _num_peers, 'peers in this IPSC') - for i in range(7, (_num_peers*11)+7, 11): - NETWORK[_sock]['PEERS'].append({ - 'RADIO_ID': binascii.b2a_hex(_data[i:i+4]), - 'IP': binascii.b2a_hex(_data[i+4:i+8]), - 'PORT': binascii.b2a_hex(_data[i+8:i+10]), - 'MODE': binascii.b2a_hex(_data[i+10:i+11]) - }) - print_peer_list('IPSC1') - - return _data, _packettype - -def print_peer_list(_ipsc_network): - print(NETWORK[_ipsc_network]['LOCAL']['DESCRIPTION']) - for dictionary in NETWORK[_ipsc_network]['PEERS']: - hex_address = dictionary['IP'] - hex_port = dictionary['PORT'] - hex_radio_id = dictionary['RADIO_ID'] - hex_mode = dictionary['MODE'] - - address = [int(hex_address[0:2], 16), int(hex_address[2:4], 16), int(hex_address[4:6], 16), int(hex_address[6:8], 16)] - port = int(hex_port, 16) - radio_id = int(hex_radio_id, 16) - - print(address[0],".",address[1],".",address[2],".",address[3],"\t", sep='', end='') - print(port, radio_id, hex_mode, sep='\t') - return - -#********** THE ACTUAL MEAT - -# Create a socket to conetact IPSC Network #1 -ipsc1_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -ipsc1_sock.bind(('', NETWORK['IPSC1']['LOCAL']['PORT'])) -ipsc1_sock.setblocking(0) -ipsc1_sock.settimeout(60) - -CTL_SUFFIX = (IPSC_TS_BOTH + IPSC_FLAGS + IPSC_OP_VER + IPSC_OLD_VER) -REG_REQ_PACKET = (REG_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX) -KEEP_ALIVE_PACKET = (KEEP_ALIVE_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX) -PEER_LIST_REQ_PACKET = (PEER_LIST_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID']) - -# Send registration packet -send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, REG_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY']) -receive_packet(ipsc1_sock) - -# Send keep alive packet -send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, KEEP_ALIVE_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY']) -receive_packet(ipsc1_sock) - -# Request peer list from master -send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, PEER_LIST_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY']) -receive_packet(ipsc1_sock) - -ipsc1_sock.close \ No newline at end of file