meaning of python socket address info output - python

I'm tring to understand the meaning of the python socket address info output.
import socket
rawSocket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
pkt = rawSocket.recvfrom(2048)
print pkt[1]
('ens33', 2048, 1, 1, 'HE \xfd\x12h')
ens33 is the interface sending the data.
I guess that 2048 is the buffer size.
I have no idea what the first "1" is. Sometimes it's "0".
I noticed the second "1" relates to the interface (i.e. "772" for "lo")
'HE \xfd\x12h' : Reverting the converted hex values, we get '\x48\x45\x20\xfd\x12\x68', it gives the mac address of host machine in a VM bridged connection.
So, the main question is for #3. What 1 or 0 means here ?

In short, the third 1 means it's a broadcast packet. 0 would mean it was a packet addressed to the machine running the Python code. Details follow.
This is based on Python 3.6, but the answer should be similar for other Py2 or Py3 versions. The answer is split between the source for the socket module, the packet(7) man page, and the Linux source.
The Python library includes function makesockaddr(). For PF_PACKET sockets (same as AF_PACKET), the relevant portion gives you the following order of fields. Explanations from the man page are italicized.
ifname (e.g., ens33) (the interface name, as you noted)
sll_protocol (e.g., 2048) Physical-layer protocol
sll_pkttype (e.g., 1) Packet type
sll_hatype (e.g., 1) ARP hardware type
sll_addr (e.g., the MAC address, as you noted above) Physical-layer address
The Linux source gives the various values for packet type. In that list, 1 is PACKET_BROADCAST. 0 is PACKET_HOST, explained as "To us".

Related

interfaces eth0 nothing en0 list error for mac os

import netifaces as ni
ip = ni.ifaddresses("eth0")[ni.AF_INET]['addr']
error
ip = ni.ifaddresses("eth0")[ni.AF_INET]['addr']
ValueError: You must specify a valid interface name.
ip = ni.ifaddresses("en0")[ni.AF_INET]['addr']
error
ip = ni.ifaddresses("en0")[ni.AF_INET]['addr']
TypeError: list indices must be integers or slices, not str
Does anyone know why the mac is giving such errors?
The first error means that there is no interface named eth0. Indeed, this is a common interface name on Linux, but not on MacOS.
The second error means that you are trying to extract a field which doesn't exist. There is information about en0 but it is an array, not a dict. This is like saying "hello"["addr"], there is no way to access the "addr":th element of a sequence. You apparently mean something like
ip = ni.ifaddresses("en0")[ni.AF_INET][0]['addr']
though there is no way out of context to tell whether getting only one address is actually what you want. The array you get represents a number of bindings; perhaps you want all of them?
addrs = ni.ifaddresses('en0')
ips = [x['addr'] for x in addrs[ni.AF_INET]]
The netifaces documentation actually explains this in quite some detail.

Mixed formatted List/String from Serial

i am new to python. I have some experience with Pascal and a little bit with C++.
At the moment i have to program some code for a research project demonstrator.
The setup is as follows:
We have a 868MHz radio master device. i can communicate with this device via a USB port (COM4 at the moment but may change in the future).
The 868MHz master communicates with a slave unit. The slave unit replies with a message that i can read from the USB port.
Until this point everything works good. I request data packages and also receive them.
From the moment of receiving the data packages i have a propblem i seem not
able to solve on myself.
I use Anaconda 32 bit with the Spyder editor
# -*- coding: utf-8 -*-
"""
Created on Thu May 7 13:35:59 2015
#author: roland
"""
import serial
portnr = 3 #Serial Port Number 0=Com1, 3=Com4
portbaud = 38400 #Baud rate
tiout = 0.1 #Timout in seconds
i = 1
wrword = ([0x02,0x04,0x00,0x00,0x00,0x02,0x71,0xF8])
try:
ser = serial.Serial(portnr, portbaud, timeout=tiout) # open port
except:
ser.close() # close port
ser = serial.Serial(portnr, portbaud, timeout=tiout) # open port
print(ser.name) # check which port was really used
while (i < 100):
ser.write(wrword)
seread = ser.readline()
print(seread)
i = i+1
sere = seread.split()
try:
readdat = str(sere[0])
except:
print("Index Error")
retlen = len(readdat)
print(retlen)
readdat = readdat[2:retlen-1]
print(readdat)
ser.close() # close port
The variable wrword is my request to the 868MHz radio master.
The Format is as follows:
0x02 Address of the unit
0x04 Command to send information from a certain register range
0x00 0x00 Address of first Register (Start address 0 is valid!)
0x00 0x02 Information how much registers are to be sent (in this case Registers 0 and 1 shall be transmitted to the Radio master)
0x71 0xF8 Checksum of the command sentence.
The program sends the command sequence successful to the master unit and the slave unit answers. Each time the command is send an answer is expected. Nevertheless it may happen that now correct answer is given thats why the
try command is in use.
I know i use ser.readline() but this is sufficient for the application.
I receive a list as answer from the USB Port.
The data look as follows:
b'\x02\x04\x04\x12\xb6\x12\xa5\xe0\xc1' (This is the Output from print(seread) )
For clarification this answer is correct and must be read as follows:
\x02 Address of the answering unit
\x04 Function that was executed (Read from certain register area)
\x04 Number of Bytes of the answer
\x12 \xb6 Value of first register (2 Byte)
\x12 \xa5 Value of second register (2 Byte)
\xe0 \xc1 Checksum of answer
If the data from the com port had all this Format i might be able to get the data values from the both Registers. But unfortunately the data format is not always the same.
Sometimes i receive answers in the following style:
b'\x02\x04\x04\x12\x8e\x12{\xe1T'
The answer is similar to the example above (different values in the Registers and different checksum) but the Format i receive has changed.
If i use the hex ASCII codes for the symbols obviously not hex values i find a valid answer telegram.
b'\x02\x04\x04\x12\x8e\x12{\xe1T'
becomes
b'\x02\x04\x04\x12\x8e\x12\x7b\xe1\x54'
when i Exchange the ASCII symbols by their hex code by Hand.
So my questions are:
Is it possible to force Python to give me the answer in a defined Format?
If not is it possible to handle the list or the string i can derive from the list in such a way that i get my values in the required format?
Does somebody can give me a hint how to extract my register values from the list and convert the two hex numbers of each register into one integer value for each register (the first value is the high byte, the second the low byte)?
Thanks in advance for your answer(s)
sincerely
Roland
I found a solution.
During a small testpiece of program i stumbled upon the fact that the variable seread contains already the data in a suitable and usable format for me.
I assume that the Spyder Editor causes the format change when displaying byte type objects.
If i Access the single Bytes using seread[i] while i is in range 0 to len(seread)-1 i receive the correct values for the single bytes.
So i can acess my data and calculate my measurement values as required.
Nevertheless thanks to keety for reading my question.

Python and Netbios

I have been toying with a few ideas and would like to use Netbios to do some checks on the network. So after some research decided pysmb's nmb.Netbios was a good place to start.
I have constructed a simple queryName function that I was hoping would return an ip address. But it seems after checking some wireshark pcap dumps it isnt even broadcasting.
Ive found an example in the pysmb docs but that doesnt seem to broadcast either. Below is my test function, any pointers would be appreciated.
from nmb.NetBIOS import NetBIOS
def queryNam(name):
n = NetBIOS(broadcast=True, listen_port=0)
ip = n.queryName(name, timeout=30)
return ip
name = "Computer-Name"
ip = queryNam(name)
print ip
I worked out the issue myself. Initially I wasnt using the correct computername as NetBios Broadcasts seem to broadcast the name in uppercase. I was presenting a lowercase so the system was not responding.
So supplying the value in uppercase resolved the problem. (Even though a hostname check on the client showed an Uppercase followed by lowercase characters.

python ioctl creating ifreq struct

I am very new to system programming. I am trying to query some NIC information using Python with ioctl, I easily got the code but having some difficulty in understanding
Python code to get the ip address
nic = "eth1"
# Create socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# **How did they conclude the formate to be 16sH14s and why 14 times \x00 ? Please advice**
ifreq = struct.pack('16sH14s', nic, socket.AF_INET, '\x00'*14)
result = fcntl.ioctl(fd, SIOCGIFADDR, ifreq)
# My wild guess **unpack format would be same as pack**. But I am wrong
ip = struct.unpack('16sH2x4s8x', result)[2]
print socket.inet_ntoa(ip)
Can somebody advice how to decide upon the format and why/how to decide upon number of null characters ?
This link seemed to be almost same question as mine but couldn't find my answer
http://www.unix.com/programming/148374-python-struct-pack.html
I found another way of creating ifreq.. ifreq = struct.pack('256s', self.iface). If possible please help me understand the difference.

Creating an ICMP traceroute in Python

I am trying to implement an ICMP based Traceroute in Python. I found a very helpful guide ( https://blogs.oracle.com/ksplice/entry/learning_by_doing_writing_your ) that has allowed me to create a UDP based Traceroute so just needs modification. However I have looked around and am having trouble changing the send socket and making it work. Is anybody able to assist me?
#!/usr/bin/python
import socket
def main(dest_name):
dest_addr = socket.gethostbyname(dest_name)
port = 33434
max_hops = 30
icmp = socket.getprotobyname('icmp')
udp = socket.getprotobyname('udp')
ttl = 1
while True:
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, udp)
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
recv_socket.bind(("", port))
send_socket.sendto("", (dest_name, port))
curr_addr = None
curr_name = None
try:
_, curr_addr = recv_socket.recvfrom(512)
curr_addr = curr_addr[0]
try:
curr_name = socket.gethostbyaddr(curr_addr)[0]
except socket.error:
curr_name = curr_addr
except socket.error:
pass
finally:
send_socket.close()
recv_socket.close()
if curr_addr is not None:
curr_host = "%s (%s)" % (curr_name, curr_addr)
else:
curr_host = "*"
print "%d\t%s" % (ttl, curr_host)
ttl += 1
if curr_addr == dest_addr or ttl > max_hops:
break
if __name__ == "__main__":
main('google.com')
Not sure why you chose scapy (nice module though it is), as this is certainly possible using only python. To send an ICMP packet, you simply send out your recv_socket. In order to send out this socket, you'll need to first create an ICMP packet.
However, it seems what you want is to send out a UDP packet over an ICMP socket. This won't work as you might think.
First, let me say apparently there exists a patch to the Linux kernel that'll allow SOCK_DGRAM with IPPROTO_ICMP: ICMP sockets (linux). I've not tested this.
Generally, though, this combination of socket flags won't work. This is because an ICMP socket expects an ICMP header. If you were to send an empty string over recv_socket similarly to send_socket, the kernel will drop the packet. Further, if you were to layer a UDP header over an ICMP header, the receiving system will only react to the received ICMP header, and treat the UDP header as nothing more than just data appended to ICMP. In fact, in its ICMP reply to you, the remote system will include your UDP header simply as data you sent to it in the first place.
The reason you're able to send an empty string over send_socket is because the kernel has created the UDP header for you, and what you send over that UDP socket is simply appended as data to the UDP header. It's not like that with an ICMP socket. As I wrote, you'll need to create the icmp header to send on this socket.
The actuality of what happens in a UDP "ping" is this: A UDP packet is sent over a UDP socket to a remote system, using a (hopefully) unopened port as the destination port. This elicits an ICMP response back from the remote system (type 3, code 3). At this point, you will need an ICMP-registered socket to handle the ICMP reply back, which requires root (or administrator on Windows) privileges.
To create an icmp echo header is quite easy:
import struct
icmp = struct.pack(">BBHHH", 8, 0, 0, 0, 0)
icmp = struct.pack(">BBHHH", 8, 0, checksum(icmp), 0, 0)
A couple things to note with this header. First, arguments four and five are the "Identifier" and "Sequence Number" fields. Typically the identifier is set to a process ID while the sequence number starts at 1 so you can track the sequence of sends to replies. In this example, I've set the numbers to zero just for illustration (although, it is perfectly normal to keep it this way if you don't care about them).
Second, see the checksum(icmp) function I allude to? I'm assuming you have access to code that does this. It is known as the 1's complement checksum, and is ratified under RFC 1071. The checksum is vital because, without it being correctly computed, the receiving system might not forward the packet to any of the ICMP socket handlers.
Third, if you're wondering why I initially created the header with a zero checksum, it's because the kernel bases a checksum result on that field being zero, before the actual checksum is created and added back to the header.
Because the struct module deals with what's called "packed" binary data, I advise that you familiarize yourself with bit shifting and bitwise operators. You're going to need this as you advance further into raw sockets, particulary if and when you need to extract certain bits from bit fields that may or may not overlap. For example, the following is the IP header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 |Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4 | Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 | Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 | Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 | Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Assuming you wanted to insert IP address 192.168.1.10 in the source field, first you must note its length, 4 bytes. To correctly insert this into struct (just this field, not rest of header, as an example), you must:
struct.pack(">I", 192 << 24| 168 << 16| 1 << 8| 10)
Doing this adds the proper integer for this field, 3232235786L.
(Some readers may point out that the same result could be had with struct.pack(">BBBB", 192, 168, 1, 10). Although this would work for this use case, it is incorrect in general. In this case, it works because an IP address is byte-oriented; however, fields that are not byte-oriented, such as the checksum field, will fail because the resulting value of a checksum is greater than 255, that is to say, it is expected to be a 16-bit value. So don't do this in general i.e. use the exact bits expected by a protocol field.)
As another example for learning bit shifting and operations, take the VER field of the IP header. This is a nibble-sized field, i.e. 4 bits. In order to extract this, you would do the following as an example (assuming IHL is zero):
# Assume ip[] already has a full IP header.
ver = struct.unpack("!B", ip[0])[0]
# This produces the integer 4, which is required for sending IPV4
ver >> 4
In a nutshell, I've right-shifted a one-byte value down 4 bits. In this downshift, the new value compared to the old value now looks like this:
# OLD VALUE IN BINARY; value is 64
# 01000000
# NEW VALUE IN BINARY; value is 4
# 00000100
It's important to note that, without this shift, checking the raw binary value would result in 64, which isn't what is expected. For the inverse, in order to "pack" the value 4 into the upper 4 bits of one byte, do this:
# New value will be 64, or binary 01000000
x = 4 << 4
Hope this points you in the right direction.
Old question, but adding another point for clarity as I recently had to do just this.
You can write a native Python version of ICMP traceroute using sockets. The two important issues to solve are:
creating the ICMP header with the correct checksum (as Eugene states above)
setting the TTL of the socket using sockopts
For the first issue, there's a great example in the pyping module that works perfectly; it's what I used when I wrote my traceroute utility.
For the second, it's as easy as:
current_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW,
socket.getprotobyname("icmp"))
current_socket.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
where 'ttl' is the integer value of the TTL you're setting
Then it's just a matter of doing a send/recv and watching the type/code in the return packets, in a control structure that increments the TTL.
I modeled my header pack/unpack after what was already written in the pyping module; just look for header type = 11 with code 0 for the intermediate hops, and header type = 0 for when you've reached your destination.
Scapy works fine, but it's slower, and is an additional external dependency. I was able to write a robust traceroute in an afternoon with AS-lookup (via RADb) and geolocation, using nothing but sockets.
Ended up writing my own using scapy as this wasn't possible.

Categories

Resources