Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get data out of network packet data in Java

In C if you have a certain type of packet, what you generally do is define some struct and cast the char * into a pointer to the struct. After this you have direct programmatic access to all data fields in the network packet. Like so :

struct rdp_header {
  int version;
  char serverId[20];
};

When you get a network packet you can do the following quickly :

char * packet;
// receive packet
rdp_header * pckt = (rdp_header * packet);
printf("Servername : %20.20s\n", pckt.serverId);

This technique works really great for UDP based protocols, and allows for very quick and very efficient packet parsing and sending using very little code, and trivial error handling (just check the length of the packet). Is there an equivalent, just as quick way in java to do the same ? Or are you forced to use stream based techniques ?

like image 649
christopher Avatar asked Jan 12 '09 21:01

christopher


3 Answers

Read your packet into a byte array, and then extract the bits and bytes you want from that.

Here's a sample, sans exception handling:

DatagramSocket s = new DatagramSocket(port);
DatagramPacket p;
byte buffer[] = new byte[4096];

while (true) {
    p = new DatagramPacket(buffer, buffer.length);
    s.receive(p);

    // your packet is now in buffer[];
    int version = buffer[0] << 24 + buffer[1] << 16 + buffer[2] < 8 + buffer[3];
    byte[] serverId = new byte[20];
    System.arraycopy(buffer, 4, serverId, 0, 20);

     // and process the rest
}

In practise you'll probably end up with helper functions to extract data fields in network order from the byte array, or as Tom points out in the comments, you can use a ByteArrayInputStream(), from which you can construct a DataInputStream() which has methods to read structured data from the stream:

...

while (true) {
    p = new DatagramPacket(buffer, buffer.length);
    s.receive(p);

    ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
    DataInput di = new DataInputStream(bais);

    int version = di.readInt();
    byte[] serverId = new byte[20];
    di.readFully(serverId);
    ...
}
like image 101
Alnitak Avatar answered Oct 21 '22 15:10

Alnitak


I don't believe this technique can be done in Java, short of using JNI and actually writing the protocol handler in C. The other way to do the technique you describe is variant records and unions, which Java doesn't have either.

If you had control of the protocol (it's your server and client) you could use serialized objects (inc. xml), to get the automagic (but not so runtime efficient) parsing of the data, but that's about it.

Otherwise you're stuck with parsing Streams or byte arrays (which can be treated as Streams).

Mind you the technique you describe is tremendously error prone and a source of security vulnerabilities for any protocol that is reasonably interesting, so it's not that great a loss.

like image 34
frankodwyer Avatar answered Oct 21 '22 16:10

frankodwyer


I wrote something to simplify this kind of work. Like most tasks, it was much easier to write a tool than to try to do everything by hand.

It consisted of two classes, Here's an example of how it was used:

    // Resulting byte array is 9 bytes long.
    byte[] ba = new ByteArrayBuilder()

     .writeInt(0xaaaa5555) // 4 bytes
     .writeByte(0x55) //      1 byte
     .writeShort(0x5A5A) //   2 bytes
     .write( (new BitBuilder())  //     2 bytes---0xBA12                
            .write(3, 5) //     101      (3 bits value of 5)
            .write(2, 3) //        11    (2 bits value of 3)
            .write(3, 2) //          010 (...)
            .write(2, 0) //     00
            .write(2, 1) //       01
            .write(4, 2) //         0002
        ).getBytes();

I wrote the ByteArrayBuilder to simply accumulate bits. I used a method chaining pattern (Just returning "this" from all methods) to make it easier to write a bunch of statements together.

All the methods in the ByteArrayBuilder were trivial, just like 1 or 2 lines of code (I just wrote everything to a data output stream)

This is to build a packet, but tearing one apart shouldn't be any harder.

The only interesting method in BitBuilder is this one:

public BitBuilder write(int bitCount, int value) {
    int bitMask=0xffffffff;  
    bitMask <<= bitCount;   // If bitcount is 4, bitmask is now ffffff00
    bitMask = ~bitMask;     // and now it's 000000ff, a great mask

    bitRegister <<= bitCount; // make room
    bitRegister |= (value & bitMask); // or in the value (masked for safety)
    bitsWritten += bitCount;
    return this;
}

Again, the logic could be inverted very easily to read a packet instead of build one.

edit: I had proposed a different approach in this answer, I'm going to post it as a separate answer because it's completely different.

like image 1
Bill K Avatar answered Oct 21 '22 16:10

Bill K