working with bits

I

iwasinnihon

I am working on creating a DNS resolver. According to RPC 1035 a
number of fields a certain number of bits long need to be sent with
certain information in it. For example: a 1-bit field followed by a 4-
bit field followed by 3 1-bit fields, followed by a 16-bit field. How
do I create these fields and combine them to send to a dns server?
 
G

Gianni Mariani

iwasinnihon said:
I am working on creating a DNS resolver. According to RPC 1035 a
number of fields a certain number of bits long need to be sent with
certain information in it. For example: a 1-bit field followed by a 4-
bit field followed by 3 1-bit fields, followed by a 16-bit field. How
do I create these fields and combine them to send to a dns server?

You can try to use bitfields but I'm not sure how portable that is.
Last resort is to create the values by bit manipulation put it directly
into a buffer.

Do you have the specs handy ?
 
J

James Kanze

I am working on creating a DNS resolver. According to RPC 1035 a
number of fields a certain number of bits long need to be sent with
certain information in it. For example: a 1-bit field followed by a 4-
bit field followed by 3 1-bit fields, followed by a 16-bit field. How
do I create these fields and combine them to send to a dns server?

Use shifting (<< and >> operators), masking and or'ing (&, | and
~ operators).

For example, for:
+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|
+--+--+--+--+--+--+--+--+

unsigned char byte = (QR << 7)
| (Opcode << 3)
| (AA << 2)
| (TC << 1)
| RD ;

if (and only if) the variables in question can only hold
legitmate values (and have unsigned type, which they should).
To extract:

QR = (byte & 0x80) >> 7 ;
Opcode = (byte & 0x78) >> 3 ;
AA = (byte & 0x04) >> 2 ;
TC = (byte & 0x02) >> 1 ;
RD = byte & 0x01 ;

If you just need to compare the value, you can skip the shift:

if ( (byte & 0x80) != 0 ) ...
or
switch( byte & 0x78 ) {
case 0 << 3 :
// ...
break ;
case 1 << 3 :
// ...
}

To make the code more readable, I'll generally define manifest
constants along the lines of:
int const QRmask = 0x80 ;
int const QRshift = 7 ;
int const Opcodemask = 0x78 ;
int const Opcodeshift = 3 ;
// ...

Note that you'll have to do the same thing in reverse for
entities larger than an octet:

unsigned char buffer[ 4 ] ;
buffer[ 0 ] = QDCOUNT >> 24 ;
buffer[ 1 ] = QDCOUNT >> 16 ;
buffer[ 2 ] = QDCOUNT >> 8 ;
buffer[ 3 ] = QDCOUNT ;

And to read:

QDC0UNT = buffer[ 0 ] << 24
| buffer[ 1 ] << 16
| buffer[ 2 ] << 8
| buffer[ 3 ] ;

(Formally, even the above is not guaranteed to work in all
cases, since it supposes 8 bit bytes, etc., and if QDC0UNT is
signed, 2's complement. In practice, machines which don't fit
this model are rare enough that I don't usually have to consider
them. And I generally use the C types int32_t/uint32_t and
int8_t/uint8_t, so that the code won't compile on a machine
where this isn't the case.)
 
I

iwasinnihon

I am still working on this project (Creating a DNS resolver). My
programming skills have a lot to be desired. I need to combine 4
buffers together and then use the sendto() socket command to send the
message to the DNS server. Here are my buffers. The message needs to
be a char*.

unsigned int header_buffer = (ID << 80) | (Flags.Flag << 64) |
(QDCount << 48) | (ANCount << 32) | (NSCount << 16) | ARCount;
unsigned char QNAME;
unsigned __int16 QTYPE;
unsigned __int16 QCLASS;

I am at a loss as to how to combine these and then get it converted
into a char* so that I can use sendto().

Does anyone have any idea?
 
J

Jerry Coffin

I am still working on this project (Creating a DNS resolver). My
programming skills have a lot to be desired. I need to combine 4
buffers together and then use the sendto() socket command to send the
message to the DNS server. Here are my buffers. The message needs to
be a char*.

unsigned int header_buffer = (ID << 80) | (Flags.Flag << 64) |
(QDCount << 48) | (ANCount << 32) | (NSCount << 16) | ARCount;
unsigned char QNAME;
unsigned __int16 QTYPE;
unsigned __int16 QCLASS;

I am at a loss as to how to combine these and then get it converted
into a char* so that I can use sendto().

Typically you'd do something like:

struct name_def {
unsigned int header_buffer = (ID << 80) | // ...
// (ID << 80)? Really?
unsigned char QNAME;
unsigned _int16 QTYPE;
unsigned _int16 QCLASS;
};

name_def some_name;

sendto(s, reinterpret_cast<char *>(&some_name), ...);

You can also do the type-pun via a union -- in theory, this has a minor
advantage, but AFAIK it's _purely_ theoretical. If you really want to
get into the gory details, there's a thread from a few months back
starting at:

http://tinyurl.com/39ayvm
 
J

Jerry Coffin

[ ... ]
Typically you'd do something like:

struct name_def {
unsigned int header_buffer = (ID << 80) | // ...
// (ID << 80)? Really?
unsigned char QNAME;
unsigned _int16 QTYPE;
unsigned _int16 QCLASS;
};

Oops -- I should have pointed out that you'll have to either make
header_buffer a const static member, or else do its initialization in a
ctor intead of inline like this.
 
J

James Kanze

I'm not sure what hardware you're working on, but it looks
pretty exotic, if you have over 80 bits in an int.
Typically you'd do something like:
struct name_def {
unsigned int header_buffer = (ID << 80) | // ...
// (ID << 80)? Really?
unsigned char QNAME;
unsigned _int16 QTYPE;
unsigned _int16 QCLASS;
};

I rather doubt that this would work either. The compiler will
probably insert some padding between QNAME and QTYPE. And
whether the internal format of __int16 corresponds to what is
required on the line or not is not at all defined. (__int16
looks like some old Microsoft stuff. In which case, he's
probably on an Intel based PC, and the internal format does
*not* correspond to line format.)

In practice, you have to build up a byte buffer, byte by byte,
as I explained in my earlier answer. And respect the external
format in all cases; QNAME is in fact a string, in the format
one byte length+characters; I suspect that it also has to be
padded to align to either 2 or 4 bytes (but I've not verified in
detail)---if not, QTYPE and QCLASS might be mis-aligned. At any
rate, QNAME is a variable length field, so there's no way you
can use a struct here, even if all internal fields had the
correct format.

The way I usually handle this sort of thing is with push_back,
into an std::vector<char>; I then consider byte by byte what I
have to insert. (Alignment can be handled using the % operator
on v.size().) In some cases, where e.g. I have to insert the
number of bytes in what follows, I'll simply note the current
offset (v.size()), insert a placeholder, and then come back
later and write the actual value. Finally, I'll write &v[0],
v.size().

Obviously, this requires a fairly good understanding of the
lower levels of what is going on. I'd say that that is a
pre-requisite, however, for implementing any Internet protocol.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
474,298
Messages
2,571,542
Members
48,284
Latest member
RedaBruno6

Latest Threads

Top