Solved

Parsing and data types problem...

Posted on 2003-10-21
10
276 Views
Last Modified: 2010-04-15
...won't this hell ever end?

Is there any to "build" a UDP packet that can contain ints and chars? recvfrom() won't mind what kind of data it receives, that's up to the parsing, so can I create an array that will contain both these types? And this brings me another question; I'm parsing the received packets in the server side like this:

strncpy(oneInt,buffer,4); /*i copied a (char)int */
strncpy(string,&buffer[4],64); /*copied a 64B string*/

But how can I know if a char casted int will really take up 4B? Nevertheless, I need to send the array containing that int, so how can I retrieve it from the array later? I know I have to get 4B but how do I specify that in terms of positioning? Like this: myvar=buffer[68]; ??? I don't think this is right! :((
0
Comment
Question by:bass20
  • 3
  • 2
  • 2
  • +3
10 Comments
 
LVL 23

Expert Comment

by:brettmjohnson
ID: 9592393
First, you cannot use functions (like strncpy) that treat the buffer as a NUL-teminated
ASCII string.  If your integer is a very small value (like 1), 3 of its four bytes will be 0,
which will be interpreted as "end of string" for all the string handling functions.  

If you want to store a 4 byte integer into the buffer you will have to use a cast like this:

oneInt = *(int *)buffer;

which means "force buffer to be a pointer to an integer (rather than a pointer to char),
then set oneInt to the value of the integer pointed to."

or

myvar = *(int *)(buffer+68);

which means "force the address of the 68th byte of buffer to be interpreted as a pointer
to an integer, then set to the value of the integer pointed to."

0
 
LVL 45

Expert Comment

by:Kdo
ID: 9592412

A packet will "send" anything that you put into it.  It's up to your client and server to be in agreement on the contents of the data.

This is why data to/from public servers is usually in ASCII (or UNICODE or some other portable mechanism).  "65536" is always "65536" when sent as a string of characters and the receiver is responsible for interrogating it.  Sending 65536 as a binary value can result in the receiver accessing the value is if it were 0, 1, 65536, or other values.


Kent
0
 
LVL 3

Expert Comment

by:komar
ID: 9592506
Try putting all data types you want to send in VARIANT type array and send that array instead.

for example, to send one integer and one character then you have to do the following:

     int nIntegerToSend = 1;
     char cCharToSend = 'a';

     VARIANT vArray[2];

     VariantInit(&vArray[0]);
     vArray[0].vt = VT_I4;
     vArray[0].lVal = (long)nIntegerToSend;

     VariantInit(&vArray[1]);
     vArray[0].vt = VT_UI1;
     vArray[0].bVal = (unsigned char)cCharToSend;

Now you have to send the buffer pointer to by vArray (length = sizeof(vArray)).

When you receive the buffer you will need to do something like this:

     int nBufferLength = /* Length of buffer received from socket (this has to be multible sizeof(VARIANT) */
     void *pBuffer = /*Start of Buffer received from socket*/

     void *pStart = pBuffer;
     VARIANT vt;
     while(1 == 1)
     {
          memcpy(&vt, pBuffer, sizoef(VARIANT));
     
          switch(vt.vt)
          {
             case VT_I4:
                int nIntegerReceived = (int)vt.lVal;
                break;
     
             case VT_UI1:
                char cCharReceived = (char)vt.bVal;
                break;
     
              /* Handle other datatypes */
          }
     
          /* Loop until you reach the end of buffer received */
         pBuffer += sizoef(VARIANT); /* make sure to handle buffer length not to get beyond the received buffer length :-) */
     
         if(pStart + nBufferLength <= pBuffer)
             break;
     };

Thanks,
Khalid.
0
 
LVL 5

Expert Comment

by:mtmike
ID: 9593854
Watch out for the int casting. If you're doing something like

char buffer[SIZE];
recvfrom(s, buffer, SIZE, ...);

You cannot safely cast a byte pointer to an int pointer. Some cpus aren't as forgiving wrt unaligned memory accesses as the x86.

As for the size of an int, you don't know whether its really four bytes or something else. Use some fixed-width type such as 'uint32_t' instead.

You'll also have to cater for byte-ordering differences between cpus by using a predetermined byte order when sending data over a network. Network byte order is big-endian.

Furthermore, it's probably a good idea to use some defensive programming when receiving UDP packets over a network. Imagine what would happen if someone sends you a malformed packet.

To avoid these headaches, better use a text encoding (as Kdo suggested). This is much less error-prone and easier to write.

mtmike
0
 
LVL 1

Author Comment

by:bass20
ID: 9594248
So there isn't a way to use a type like void, wich will hold any kind of data? For ex:

void buffer[10];

strncopy(buffer,"hello",5);
buffer[5]=123;

I've posted a similar question before; I just can't quite figure out how to "blend" the two different types inside one array to use with sendto(). Should I convert all types to char?
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 5

Accepted Solution

by:
mtmike earned 50 total points
ID: 9594473
The variant approach:

struct variant {
  char type;
  union {
    char c;
    short s;
    int i;
    long l;
    float f;
    double d;
  };
};

Still has the same size/byte-order problems (not too mention overhead).

Maybe explicit encoding/decoding is a better idea:

typedef unsigned char uint8_t;

void uint_encode(uint8_t *buffer, unsigned int data)
{
      buffer[0] = (data >> 24) & 0xff;
      buffer[1] = (data >> 16) & 0xff;
      buffer[2] = (data >> 8) & 0xff;
      buffer[3] = data & 0xff;
}

unsigned int uint_decode(const uint8_t *buffer)
{
      int data;

      data = (unsigned int) buffer[0];
      data = (data << 8) | (unsigned int) buffer[1];
      data = (data << 8) | (unsigned int) buffer[2];
      data = (data << 8) | (unsigned int) buffer[3];
      return data;
}

Caters for size, byte-order and alignment problems.
0
 
LVL 22

Expert Comment

by:grg99
ID: 9595637
The simplest way to interpret an incoming buffer of bytes as a struct is to just overlay the buffer with a struct pointer.

Something like:


char Buffer[1000];  // the incoming raw net data

typedef struct { int FirstField; double SecondField } TheNetPacket;  // how we want to look at the data

typedef  TheNetPacket *  TheNetPacketPtrType;

TheNetPacketPtrType   TP;


recv( Buffer );

TP = (TheNetPacketPtrType) Buffer;   // have TP point to the incoming data

printf("Received integrer '%d' and double '%f' \n", TP->FirstField, TP->SecondField );

---------------
But watch out, as others have noted, this will only work between compatible computers.
If it has to work between any two computers, they have to put the data into some mutually agreed format.


0
 
LVL 45

Expert Comment

by:Kdo
ID: 9599398
> So there isn't a way to use a type like void, wich will hold any kind of data? For ex:

> void buffer[10];

> strncopy(buffer,"hello",5);
> buffer[5]=123;

Yes, you can do something very similar to this.  But you must be very careful that the receiver knows the format well enough to decode the data.  Look what happens with slightly different data values:

main ()
{
  unsigned char Buffer[10];

  strcpy (Buffer, "hello");
  Buffer[5] = 44;
  Buffer[6] = 0;

  puts (Buffer);
}

hello.

Note that the decimal value of 44 becomes the period.  If the receiving field is expecting exactly 5 ascii characters and then an 8-bit value, no problem.  But if the text is "free form" as I've inferred, the receiver will have a monsterous time decoding the data.


> I've posted a similar question before; I just can't quite figure out how to "blend" the two different types inside one array to use with sendto(). Should I convert all types to char?

The variant approach that mtmike suggested will solve a lot of this.  Fixed length (fixed position) fields that can be described with struct{} will, too.  So will sending everything in ASCII (unicode).

Implementing variants is pretty easy.  For a first cut I would limit the data types to native types shown in mtmike's example.  Each item will be sizeof (variant) bytes long, but that's ok for starters.  Once the basics are in place for you to send/receive data you can then pack the data and include a string type.


Kent
0
 
LVL 1

Author Comment

by:bass20
ID: 9606088
The recipient of the packet always knows the format in order to parse it; here's how I'm doing it now, I'm about to start testing it:

char buffer[20];
int hey=3;

memcpy(buffer,(char *)hey,4);

sendto(...,buffer,...);

Comments, please?
0
 
LVL 45

Expert Comment

by:Kdo
ID: 9606339

That should be fine.

Kent
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand recursion in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use switch statements in the C programming language.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now