def test_normalize_data(self): variables, _ = protocol.Query.normalize_data( protocol.QueryType.open_ticket, self.OPEN_TICKET_VARS_RETRY, util.DataChunk(b'\x00\x00\x00', 0)) for var, data in variables.items(): self.assertIsInstance(data, int) variables, _ = protocol.Query.normalize_data( protocol.QueryType.open_ticket, self.REQUEST_DATA_VARS_RETRY, util.DataChunk(b'\x00\x00\x00', 0)) for var, data in variables.items(): if var == 'bf': self.assertEqual(b'\xab\xcd\xef\xab\xcd\xef\xab\xcd\xef', data) else: self.assertIsInstance(data, int) variables, _ = protocol.Query.normalize_data( protocol.QueryType.open_ticket, self.CHECK_REQUEST_VARS_RETRY, util.DataChunk(b'\x00\x00\x00', 0)) for var, data in variables.items(): self.assertIsInstance(data, int) variables, _ = protocol.Query.normalize_data( protocol.QueryType.open_ticket, self.FETCH_RESPONSE_VARS_RETRY, util.DataChunk(b'\x00\x00\x00', 0)) for var, data in variables.items(): self.assertIsInstance(data, int) variables, _ = protocol.Query.normalize_data( protocol.QueryType.open_ticket, self.CLOSE_TICKET_VARS_RETRY, util.DataChunk(b'\x00\x00\x00', 0)) self.assertIsInstance(variables['retry'], int) self.assertIsInstance(variables['ac'], bool) # unchanged self.assertIsInstance(variables['id'], int)
def test_encode(self): payload = util.DataChunk(b'\x00\x00\x00', 0) query = protocol.Query.create('0', self.OPEN_TICKET_VARS, payload) record = query.encode() self.assertEqual( 'sz-00000044.rn-12345678.id-00000001.v0.tun.vpnoverdns.com.', record.fqdn) self.assertEqual(protocol.chunk_to_ipv4(payload), record.value) query = protocol.Query.create('0', self.REQUEST_DATA_VARS, payload) record = query.encode() self.assertEqual( 'bf-abcdefabcdefabcdef.wr-00000000.id-98765432.v0.tun.vpnoverdns.com.', record.fqdn) self.assertEqual(protocol.chunk_to_ipv4(payload), record.value) query = protocol.Query.create('0', self.CHECK_REQUEST_VARS, payload) record = query.encode() self.assertEqual('ck-00000020.id-98765432.v0.tun.vpnoverdns.com.', record.fqdn) self.assertEqual(protocol.chunk_to_ipv4(payload), record.value) query = protocol.Query.create('0', self.FETCH_RESPONSE_VARS, payload) record = query.encode() self.assertEqual( 'ln-00000048.rd-00000000.id-98765432.v0.tun.vpnoverdns.com.', record.fqdn) self.assertEqual(protocol.chunk_to_ipv4(payload), record.value) query = protocol.Query.create('0', self.CLOSE_TICKET_VARS, payload) record = query.encode() self.assertEqual('ac.id-98765432.v0.tun.vpnoverdns.com.', record.fqdn) self.assertEqual(protocol.chunk_to_ipv4(payload), record.value)
def test_build_from_records_text_request_data(self): payload = util.DataChunk(b'E\x00', 0) # E0 means no error (success) queries = [ protocol.Query.create( '0', { 'bf': binascii.hexlify( self.TEXT_REQUEST[i:i + 30]).decode('ascii'), 'wr': i, 'id': 12345678 }, payload) for i in range(0, len(self.TEXT_REQUEST), 30) ] records = [q.encode() for q in queries] self._random.shuffle(records) self._db.build_from_records(records) self.assertIn(12345678, self._db) self.assertEqual(12345678, self._db[12345678].ticket_id) self.assertFalse(self._db[12345678].collision) self.assertIsNone(self._db[12345678].random_number) self.assertEqual(len(self.TEXT_REQUEST), self._db[12345678].raw_request_length) self.assertEqual(self.TEXT_REQUEST, self._db[12345678].raw_request_data) self.assertEqual(self.TEXT_REQUEST_MESSAGE, self._db[12345678].request_message) self.assertIsNone(self._db[12345678].request_data)
def test_type_deduction(self): querytype = protocol.QueryType.deduce( '0', self.OPEN_TICKET_VARS, util.DataChunk(b'\x00\x00\x00', 0)) self.assertEqual(protocol.QueryType.open_ticket, querytype) querytype = protocol.QueryType.deduce( '0', self.REQUEST_DATA_VARS, util.DataChunk(b'\x00\x00\x00', 0)) self.assertEqual(protocol.QueryType.request_data, querytype) querytype = protocol.QueryType.deduce( '0', self.CHECK_REQUEST_VARS, util.DataChunk(b'\x00\x00\x00', 0)) self.assertEqual(protocol.QueryType.check_request, querytype) querytype = protocol.QueryType.deduce( '0', self.FETCH_RESPONSE_VARS, util.DataChunk(b'\x00\x00\x00', 0)) self.assertEqual(protocol.QueryType.fetch_response, querytype) querytype = protocol.QueryType.deduce( '0', self.CLOSE_TICKET_VARS, util.DataChunk(b'\x00\x00\x00', 0)) self.assertEqual(protocol.QueryType.close_ticket, querytype)
def test_build_from_records_binary_response_data(self): compressed_length = len(self.COMPRESSED_BINARY_RESPONSE) response_segments = [ self.COMPRESSED_BINARY_RESPONSE[i:i + 48] for i in range(0, compressed_length, 48) ] records = [] for i, segment in enumerate(response_segments): query_vars = {'ln': len(segment), 'rd': i * 48, 'id': 12345678} for off in range(0, len(segment), 3): query = protocol.Query.create( '0', query_vars, util.DataChunk(segment[off:off + 3], off)) records.append(query.encode()) self._random.shuffle(records) self._db.build_from_records(records) self.assertIn(12345678, self._db) self.assertEqual(12345678, self._db[12345678].ticket_id) self.assertFalse(self._db[12345678].collision) self.assertIsNone(self._db[12345678].random_number) self.assertIsNone(self._db[12345678].raw_request_data) self.assertIsNone(self._db[12345678].raw_request_length) self.assertEqual(len(self.COMPRESSED_BINARY_RESPONSE), self._db[12345678].raw_response_length) self.assertEqual(self.COMPRESSED_BINARY_RESPONSE, self._db[12345678].raw_response_data) self.assertEqual(self.BINARY_RESPONSE_DATA, self._db[12345678].response_data) self.assertEqual(self.BINARY_RESPONSE_MESSAGE, self._db[12345678].response_message)
def _generate_data(self, length, alignment): assert length > 0 assert alignment > 0 data = os.urandom(length) return data, [ util.DataChunk(data[i:i + alignment], i) for i in range(0, len(data), alignment) ]
def ipv4_to_chunk(addr): octets = addr.split('.') if len(octets) != 4: raise ValueError('IPv4 addresses must have 4 octets') octets = map(int, octets) try: data = ipv4_to_chunk.packer.pack(*octets) except struct.error as e: raise ValueError('every octet must be within [0,255]') from e length = (data[0] >> 6) & 0x3 offset = (data[0] & 0x3f) * 3 return util.DataChunk(data[1:length + 1], offset)
class TestQueryParser(unittest.TestCase): DEFAULT_RECORD = dnsrecord.DnsRecord( 'foo-12345678.bar-00000000.v0.tun.vpnoverdns.com.', 'IN', 'A', '192.178.115.214') CUSTOM_RECORD = dnsrecord.DnsRecord( 'foo-12345678.bar-00000000.v0.illinois.edu.', 'IN', 'A', '192.178.115.214') VARIABLES = {'foo': '12345678', 'bar': '00000000'} DATA = util.DataChunk(b'\xb2\x73\xd6', 0) def test_default_suffix(self): parser = protocol.QueryParser() query = parser.parse(self.DEFAULT_RECORD) self.assertEqual('0', query.version) self.assertEqual(self.VARIABLES, query.variables) self.assertEqual(self.DATA, query.payload) def test_custom_suffix_nodot(self): parser = protocol.QueryParser(fqdn_suffix='illinois.edu') query = parser.parse(self.CUSTOM_RECORD) self.assertEqual('0', query.version) self.assertEqual(self.VARIABLES, query.variables) self.assertEqual(self.DATA, query.payload) def test_custom_suffix_begindot(self): parser = protocol.QueryParser(fqdn_suffix='.illinois.edu') query = parser.parse(self.CUSTOM_RECORD) self.assertEqual('0', query.version) self.assertEqual(self.VARIABLES, query.variables) self.assertEqual(self.DATA, query.payload) def test_custom_suffix_enddot(self): parser = protocol.QueryParser(fqdn_suffix='illinois.edu.') query = parser.parse(self.CUSTOM_RECORD) self.assertEqual('0', query.version) self.assertEqual(self.VARIABLES, query.variables) self.assertEqual(self.DATA, query.payload) def test_flags(self): parser = protocol.QueryParser() record = dnsrecord.DnsRecord('ac.' + self.DEFAULT_RECORD.fqdn, *self.DEFAULT_RECORD[1:]) expected_vars = self.VARIABLES expected_vars['ac'] = True query = parser.parse(record) self.assertEqual('0', query.version) self.assertEqual(expected_vars, query.variables) self.assertEqual(self.DATA, query.payload)
def test_error(self): error_payload = util.DataChunk(b'E\x0a', 0) query = protocol.Query.create('0', self.OPEN_TICKET_VARS, error_payload) self.assertEqual(10, query.error) query = protocol.Query.create('0', self.REQUEST_DATA_VARS, error_payload) self.assertEqual(10, query.error) query = protocol.Query.create('0', self.CHECK_REQUEST_VARS, error_payload) self.assertEqual(10, query.error) query = protocol.Query.create('0', self.FETCH_RESPONSE_VARS, error_payload) self.assertEqual(10, query.error) query = protocol.Query.create('0', self.CLOSE_TICKET_VARS, error_payload) self.assertEqual(10, query.error)
def chunk_to_ipv4(chunk): if not isinstance(chunk, util.DataChunk): chunk = util.DataChunk(*chunk) length = len(chunk.data) if length <= 0: raise ValueError('length must be at least 1') elif length > 3: raise ValueError('cannot encode chunks longer than 3 bytes') elif chunk.offset % 3 != 0: raise ValueError('chunk offset must be multiples of 3') elif chunk.offset < 0: raise ValueError('chunk offset cannot be negative') elif chunk.offset // 3 >= 0x3f: raise ValueError('chunk offset cannot exceed {}'.format(0x3f)) return '{}.{}.{}.{}'.format((length << 6) + (chunk.offset // 3), chunk.data[0], chunk.data[1] if length >= 2 else 255, chunk.data[2] if length == 3 else 255)
def test_type_deduction_unknown_version(self): with self.assertRaises(protocol.UnknownVersionError): # Version 1 is not known to the implementation protocol.QueryType.deduce('1', self.OPEN_TICKET_VARS, util.DataChunk(b'\x00', 0))