2

I have this simple client code which is sending a message to a server and then echoes the server response.

import socket, sys, time


HOST = 'localhost'   
PORT = 11000             
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
msg = raw_input()
s.send(msg)
data = s.recv(len(msg))
s.close()
print 'Received: ', data

I wish to use raw socket here in order to be able to get the IP_ID field out of the server's response packet, but I have no idea how.

No matter how much I read about it on the internet I did not find a good example of how to do it.

(Just to clarify, I don't really need to send a raw packet to the server, I can use the normal SOCK_STREAM to send the message, but I need to use raw socket to get the response' IP_ID.)

Can anyone just edit my code and show me, in the simpliest way, how to do it? All I managed to find out is that I need to replace socket.SOCK_STREAM with socket.SOCK_RAW, but im still far away.

Thanks alot

6
  • What's the IP_ID field? Is that a part of the server's response? If so, then you have to parse it (and it depends on the format). Also getting IP of a server is pointles since you need to know it in order to connect to it. Unless some DNS round robin is involved, then you can retrieve it by calling socket.getpeername(). Commented Nov 25, 2013 at 21:23
  • He's talking about field at offset 4 of the IPv4 header. Commented Nov 25, 2013 at 21:30
  • IP_ID is a part of every tcp packet. Its just the identifier used to determine which fragments constitues a datagram Commented Nov 25, 2013 at 21:31
  • @Jjang - Can you explain why you want to inspect the IP_ID field? The best way to do it depends upon precisely what you are trying to achieve. Commented Nov 25, 2013 at 21:39
  • I'm pretty sure you can't use the same socket in raw mode and stream mode at the same time, so you're going to need to replace that connect and send with a bunch of much more complicated code that constructs and sends the appropriate TCP packets (this post gets you started) to initiate the connection before you can receive anything interesting. Commented Nov 25, 2013 at 21:44

1 Answer 1

4

Reading from a raw socket and parsing out the IP_ID is trivial:

response, addr = s.recvfrom(65535)
response_id = struct.unpack('!H', response[4:6])
print response_id

The hard part is getting someone to send you a packet in the first place. I'm pretty sure you can't use the same socket in raw mode and stream mode at the same time, so you're going to need to replace that connect and send with a bunch of much more complicated code that constructs and sends the appropriate TCP packets to initiate the connection. There are libraries like scapy that will do all that hard stuff for you, but if you want to do it manually, you just need to read RFC 791 and RFC 793 carefully, do all the tedious stuff (making sure you get all the endianness right), and you're on your way.

On *BSD (including OS X), the kernel will fill in the IP length, TCP length, and, best of all, the TCP checksum. It gets much more painful if you have to handle those yourself. (If this doesn't work on your platform, you probably do… either that, or I screwed up something else that OS X fixes for me automagically and your platform doesn't.)

import socket
import struct

def make_ip(proto, srcip, dstip, ident=54321):
    saddr = socket.inet_aton(srcip)
    daddr = socket.inet_aton(dstip)
    ihl_ver = (4 << 4) | 5
    return struct.pack('!BBHHHBBH4s4s' , 
                       ihl_ver, 0, 0, ident, 0, 255, proto, 0, saddr, daddr)

def make_tcp(srcport, dstport, payload, seq=123, ackseq=0,
             fin=False, syn=True, rst=False, psh=False, ack=False, urg=False,
             window=5840):
    offset_res = (5 << 4) | 0
    flags = (fin | (syn << 1) | (rst << 2) | 
             (psh <<3) | (ack << 4) | (urg << 5))
    return struct.pack('!HHLLBBHHH', 
                       srcport, dstport, seq, ackseq, offset_res, 
                       flags, window, 0, 0)

srcip = dstip = '127.0.0.1'
srcport, dstport = 11001, 11000
payload = '[TESTING]\n'

ip = make_ip(socket.IPPROTO_TCP, srcip, dstip)
tcp = make_tcp(srcport, dstport, payload)
packet = ip + tcp + payload

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.sendto(packet, (dstip, 0))
response, addr = s.recvfrom(65535)
response_id = struct.unpack('!H', response[4:6])
print response_id

Each time I run this, I get a nak (if no one's listening on port 11000) or ack packet with a randomized IP_ID, just as you'd expect.

Sign up to request clarification or add additional context in comments.

8 Comments

Hi, thats an excellent answer! P.S is there any way to do it without struct import?
@Jjang: Sure. I assume you that if you don't want the struct module, you don't want the ctypes module either. So, without any imports, you could manually pack each 16- and 32-bit value into network-endian bytes with the & and >> operators, stick the bytes in a bytearray, and convert it to a str, and then on the receive side do the same thing—convert the str to a bytearray and manually unpack bytes 4 and 5 as a network-endian short. But why would you want to do this? The standard library is there to be used, to make your life easier.
is working with raw socket different than working with normal sockets? My server doesnt seem to recognize that I connected to it, Plus, I manage to get a response even if my server is down... but if I use my original code, server regocnize connection, and I fail to send packets if server is not up... here's my simple echo server code: stackoverflow.com/questions/20198003/…
P.S,assume I wanted to make a UDP packet, the only thing I had to do was to change make_tcp to make_udp? and if yes, how?
@Jjang: For the second question: yes. You just need to read RFC 768 instead of RFC 793. (If you can't remember the numbers—I certainly can't—a quick google for "RFC UDP" turns up the official RFC, the Wikipedia page, and other useful sources as the top hits.) You may want to search around and see if someone has already done the work constructing packets for you, of course.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.