scapy: get DNSQR / DNSRR field values in symbolic/string form - python

I'm trying to decode DNS traffic and print query/response data and I'm using python/scapy to decode the packets.
A code snippet:
def dns_sniff_v2(pkt):
if IP in pkt:
if pkt.haslayer(DNS):
dns = pkt.getlayer(DNS)
pkt_time = pkt.sprintf('%sent.time%')
if pkt.haslayer(DNSQR):
qr = pkt.getlayer(DNSQR) # DNS query
values = [ pkt_time, str(ip_src), str(ip_dst), str(dns.id), str(qr.qname), str(qr.qtype), str(qr.qclass) ]
print "|".join(values)
sniff(iface="eth0", filter="port 53", prn=dns_sniff_v2, store=0)
The problem is that qr.qtype or qr.qclass is getting me the enum's internal int representation (1) instead of the symbolic string value ("A", or "IN"). The same applies to the DNSRR section of response packets.
How can I get a DNSQR or DNSRR field in the symbolic form?

You can get the symbolic string value of qr.qtype and of qr.qclass by invoking the following:
qr.get_field('qtype').i2repr(qr, qr.qtype)
qr.get_field('qclass').i2repr(qr, qr.qclass)
Note that rather than invoking qr.get_field('qtype') and qr.get_field('qclass') over and over again, you can invoke it once in advance:
qtype_field = qr.get_field('qtype')
qclass_field = qr.get_field('qclass')
...
qtype_field.i2repr(qr, qr.qtype)
qclass_field.i2repr(qr, qr.qclass)

Related

how I do decode buffer message with struct

I write python socket client, but server return remaining time how to decode this structure in python.
header = Buffer.from( [03, 00, 00, 00]);
time = new Uint16Array(1);
time[0] = remaining_time_in_sec;
packet = Buffer.concat( [header, Buffer.from(time.buffer)]);
this.socket.write(packet);
this function return like
b'\x03\x00\x00\x00\t\x01'
i think solve this problem
this return binary code
b'\x03\x00\x00\x00\x0e\x01'
this here packet type
b'\x03\x00\x00\x00'
unpack('h',b'\x0e\x01')
return <======================
270

Controlling the feed of incoming bytes using twisted

I need to address the following issue
As a client I connect to a server, the server sends blocks of data in the following form:
[4 bytes][msg - block of bytes the size of int(previous 4 bytes)]
When using twisted I need to make dataReceived(self, data) to be called with the msg part, I don't mind receiving the the 4 bytes prefix, but I have I need to make sure I get the entire message block in one piece, not fragmented, one at a time.
Please advise.
StatefulProtocol is helpful for protocols like this.
from twisted.protocols.stateful import StatefulProtocol
HEADER_LENGTH = 4
class YourProtocol(StatefulProtocol):
# Define the first handler and what data it expects.
def getInitialState(self):
return (
# The first handler is self._header
self._header,
# And it expects HEADER_LENGTH (4) bytes
HEADER_LENGTH,
)
# When HEADER_LENGTH bytes have been received, this is called.
def _header(self, data):
# It returns a tuple representing the next state handler.
return (
# The next thing we can handle is a response
self._response,
# And the response is made up of this many bytes.
int.from_bytes(header, byteorder='big'),
)
# When the number of bytes from the header has been received,
# this is called.
def _response(self, data):
# Application dispatch of the data
self.responseReceived(data)
# Return to the initial state to process the next received data.
return self.getInitialState()
I've ended up writing the following Custom Receiver
HEADER_LENGTH = 4
class CustomReceiver(Protocol):
_buffer = b''
def dataReceived(self, data):
logger.info(f'DATA RECEIVED: {data}')
data = (self._buffer + data)
header = data[:HEADER_LENGTH]
logger.info(f'header: {header} len: {len(header)}')
while len(header) == HEADER_LENGTH:
response_length = int.from_bytes(header, byteorder='big')
response = data[HEADER_LENGTH:][:response_length]
self.responseReceived(response)
data = data[HEADER_LENGTH + response_length:]
header = data[:HEADER_LENGTH]
self._buffer = header
I'm not sure if I should add a locking mechanism for dataReceived(), simultaneous invocations will corrupt the _buffer data.

Read TTL from an ICMP message received via Python raw sockets

I'm building a traceroute-ish tool to determine the number of hops required for a UDP packet to reach an address using only one probe. To do this, I want to extract the TTL from the ICMP message I receive after sending the probe. I'm doing the following and successfully receiving the ICMP message:
data, source = in_socket.recvfrom(d_bufsize)
But I have no idea how to turn data into something that I can read the TTL from. in_socket is declared like this:
in_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
Here, icmp_proto is just the protocol number for ICMP (obtained by doing icmp_proto = socket.getprotobyname("icmp")).
Any help would be much appreciated!
But I have no idea how to turn data into something that I can read
the TTL from.
pyping does it this way:
def header2dict(self, names, struct_format, data):
""" unpack the raw received IP and ICMP header informations to a dict """
unpacked_data = struct.unpack(struct_format, data)
return dict(zip(names, unpacked_data))
…
packet_data, address = current_socket.recvfrom(ICMP_MAX_RECV)
icmp_header = self.header2dict(
names=[
"type", "code", "checksum",
"packet_id", "seq_number"
],
struct_format="!BBHHH",
data=packet_data[20:28]
)
if icmp_header["packet_id"] == self.own_id: # Our packet
ip_header = self.header2dict(
names=[
"version", "type", "length",
"id", "flags", "ttl", "protocol",
"checksum", "src_ip", "dest_ip"
],
struct_format="!BBHHHBBHII",
data=packet_data[:20]
)
packet_size = len(packet_data) - 28
ip = socket.inet_ntoa(struct.pack("!I", ip_header["src_ip"]))
The TTL can then be read from ip_header["ttl"].

How do you make an IPv4 address printable with the getdnsapi Python API?

I'm working with the new getdns Python API.
I'm specifically trying to get DNSSEC_BOGUS answers. To do this I need to look at the results.reply_tree.reply[n]['answer']['rdata']['ipv4_address'] elements. This is a 4-byte binary representation of the IPv4 address. Is there an easy way within getdns to change this into a printable IPv4 address?
I don't want to use the results.just_address_answers field, because there is no guarantee that the addresses are in the same order.
Here is my sample code:
import getdns, sys
def get_ip(hostname, ctx, extensions):
print("host: {} extensions: {}".format(hostname, extensions))
results = ctx.address(name=hostname, extensions=extensions)
print("addresses:")
for addr in results.just_address_answers:
print(" {}".format(addr['address_data']))
print("replies:")
for reply in results.replies_tree:
for a in reply['answer']:
if a['type']==getdns.RRTYPE_A:
try:
print(" type={} data={} dnssec_status={}".format(a['type'], a['rdata']['ipv4_address'], reply['dnssec_status']))
except KeyError:
print(" no dnssec_status")
print("")
if __name__=="__main__":
print("getdns.DNSSEC_SECURE={}".format(getdns.DNSSEC_SECURE))
print("getdns.DNSSEC_INDETERMINATE={}".format(getdns.DNSSEC_INDETERMINATE))
print("getdns.DNSSEC_INSECURE={}".format(getdns.DNSSEC_INSECURE))
print("getdns.DNSSEC_BOGUS={}".format(getdns.DNSSEC_BOGUS))
failed = 'www.dnssec-failed.org'
ctx = getdns.Context()
get_ip(failed, ctx, {})
get_ip(failed, ctx, {'dnssec_return_status' : getdns.EXTENSION_TRUE })
get_ip(failed, ctx, {"dnssec_return_validation_chain" : getdns.EXTENSION_TRUE})
Although this works, I really want the data= that prints to print an IPv4 address, not just four binary bytes.
bin_addr = results.reply_tree.reply[n]['answer']['rdata']['ipv4_address']
string_addr = '.'.join(map(str, map(ord, bin_addr)))

Dpkt lib - how to add timestamp to packet

So I am trying to create my own pcap file, I've created a msg using dpkt but I am not understanding how to maneuver timestamp, from what I've seen it is the 3rd parameter in writepkt but I don't understand how to initial the variable.. this is a part of my code
output_pcapfile = dpkt.pcap.Writer(open(__file__+'.pcap','wb'))
tcpSrc.data = 'Some data\r\n'
ipSrc.data = tcpSrc
ipSrc.len = len(str(ipSrc))
ethSrc.data = ipSrc
packet_count = 1
output_pcapfile.writepkt(ethSrc,packet_count) # somehow get timestamp for third parameter!!
Anyone has an idea? thanks !
looking at the 1.7 source for pcap.py,
I see:
def writepkt(self, pkt, ts=None):
if ts is None:
ts = time.time()
#some more code..
This function takes two non-self arguments
The timestamp will be created if you only pass in the pkt itself, e.g.
output_pcapfile.writepkt(ethSrc)

Categories