Devin Bidwell
Devin Bidwell

Reputation: 100

C# BitConverter.GetBytes() padding is incorrect?

I am working on writing my own DNS server in .net core. I'm at the stage where I am encoding the response payload to send back, and the schema shows that most of the numbers are encoded as 16 bit numbers. C#'s ints are 32 bit numbers. Not a big deal, I'm just dropping off the remaining 16 bits from the front of the number I have no problem with that.

I was doing this by hand until I discovered the System.BitConverter class. I tried using it, however, and the results I came up with were reversed of what it came up with.

For example:

using System;
                    
var myInt = 15;

byte[] data = new byte[2];

data[0] = (byte)(myInt >> 8);
data[1] = (byte)(myInt & 255);

var myIntStr = "";

foreach(var b in data)
{
    myIntStr += System.Convert.ToHexString(new byte[]{ b });
    myIntStr += " ";
}

Console.WriteLine(myIntStr);

var myShort = System.Convert.ToInt16(myInt);

byte[] data2 = System.BitConverter.GetBytes(myShort);

myIntStr = "";

foreach(var b in data2)
{
    myIntStr += System.Convert.ToHexString(new byte[]{ b });
    myIntStr += " ";
}

Console.WriteLine(myIntStr);

This code produces the following result:

00 0F 
0F 00 

It's my understanding that 000F is 15 where as 0F00 is 3840. Am I not understanding bit shifting correctly? I literally just started working with actual bits last night lol.

Thanks for reading this and thanks in advance for your help!

Upvotes: 3

Views: 649

Answers (1)

Devin Bidwell
Devin Bidwell

Reputation: 100

As per the comments on the Question, the answer resides in Endianness.

Network byte order sent from the dig command I am using to test with uses Big Endian order. However, my CPU architecture is Small Endian.

Dotnet behind the scenes in their UDPClient class reverses the bytes if your system is Small Endian when sending bytes, and vice verse when receiving bytes. But because I was creating the bytes by hand using bit shifting in the Big Endian format, they were then reversed to be in Non-Network Byte order while everything else was in Network Byte order.

The solution here is to either have conditional logic to test if your system is IsLittleEndian According to the Microsoft dotnet docs, or let the System.BitConverter class handle it for you.

For instance: in my above example I was trying to convert a 32 bit int into a 16 bit unsigned bit. I ended up replacing the above code with:

    public static byte[] IntTo16Bit(int input)
    {
        ushort input16;
        if (!UInt16.TryParse(input.ToString(), out input16))
        {
            throw new Exception($"Input was {input}");
        }
        if (BitConverter.IsLittleEndian)
        {
            return BitConverter.GetBytes(input16).Reverse().ToArray();
        }
        return BitConverter.GetBytes(input16);
    }

and plan on better handling when the i32 cannot be converted into a u16.

Upvotes: 2

Related Questions