Related
Say you have a struct in C:
typedef struct ID_Info {
uint16_t model_number;
uint16_t serial_number;
uint16_t firmware_version;
} ;
ID_Info id_info;
Now, say I need to set each uint16 variable in this struct to the values of data received byte by byte. So for example, if I received the following bytes: 0x00, 0x11, 0x22, 0x33, 0x44 and 0x55 in some data array data[], I now need to set the values as follows:
id_info.model_number = data[1]*256 + data[0]; // 0x1100
id_info.serial_number = data[3]*256 + data[2]; // 0x3322;
id_info.firmware_version = data[5]*256 + data[4]; // 0x5544;
This is easy enough to hard code as shown above. However, I'd like to be able to do this without hard-coding values and iteratively if possible. Therefore, if I needed to add a variable to the struct, my code and loop would automatically know I need to iterate for two more bytes (assuming a unit16). So this loop would need to iterate foreach member in the struct. Furthermore, is there a way to infer the variable type to know how many bytes I need? Say I needed to add a uint8, and in this case the code could know I only need one byte.
So maybe the pseudo-code would look something like this:
int i = 0;
foreach(member in id_info)
if(member is uint8)
id_info.member = data[i];
i =+ 1;
else if (member is uint16)
id_info.member = data[i] + 256*data[i+1];
i =+ 2;
else
throw error
This way I could easily add and removed struct members without many changes to the code. Thanks in advance for any insight!
If it's not a performance issue (your sample data looks like it isn't), instead of a hard-coded structure with C types, you could define a structure where the type information is encoded, perhaps based on an enum, the name information as a string, and that along with a large enough value type.
The enum type might look like this:
typedef enum {
ui16, ui8
} Type;
One entry could be defined as:
struct entry {
Type type;
char *name;
long value;
};
It is assumed that long is large enough for the largest data type.
A small, self-contained C test program based on your example might then look like the following:
#include <stdio.h>
typedef enum {
ui16, ui8
} Type;
struct entry {
Type type;
char *name;
long value;
};
struct entry id_info[] = {
{ui16, "model_number", 0},
{ui16, "serial_number", 0},
{ui16, "firmware_version", 0}
};
int main(void) {
unsigned char data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
int x = 0;
for (int i = 0; i < sizeof(id_info) / sizeof(id_info[0]); i++) {
struct entry *current = &id_info[i];
switch (current->type) {
case ui8:
current->value = data[x];
x++;
break;
case ui16:
current->value = data[x] + 256 * data[x + 1];
x += 2;
break;
}
}
//and now print it
for (int i = 0; i < sizeof(id_info) / sizeof(id_info[0]); i++) {
struct entry *current = &id_info[i];
switch (current->type) {
case ui8:
printf("uint8_t %s: %02lx\n", current->name, current->value);
break;
case ui16:
printf("uint16_t %s: %04lx\n", current->name, current->value);
break;
}
}
return 0;
}
The program would produce the following output on the debug console:
uint16_t model_number: 1100
uint16_t serial_number: 3322
uint16_t firmware_version: 5544
One way to do this is with preprocessor macros.
With this method, it is easy to add new elements. And, the import/export functions will be automatically updated.
#ifndef NOINC
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#endif
// define all struct members
#define ALLSTRUCT(_cmd) \
_cmd(uint16_t,"%u",model_number) \
_cmd(uint16_t,"%u",serial_number) \
_cmd(uint16_t,"%u",firmware_version)
// define symbol
#define SYMDEF(_typ,_fmt,_sym) \
_typ _sym;
// define struct
typedef struct ID_Info {
ALLSTRUCT(SYMDEF)
} ID_Info;
ID_Info id_info;
// deserialize
#define SYMIN(_typ,_fmt,_sym) \
do { \
str->_sym = *(_typ *) ptr; \
ptr += sizeof(_typ); \
} while (0);
// serialize
#define SYMOUT(_typ,_fmt,_sym) \
do { \
*(_typ *) ptr = str->_sym; \
ptr += sizeof(_typ); \
} while (0);
// print
#define SYMPRT(_typ,_fmt,_sym) \
printf(" " #_sym "=" _fmt " (%8.8X)\n",str->_sym,str->_sym);
// struct_out -- output struct to byte array
uint8_t *
struct_out(const ID_Info *str,uint8_t *ptr)
{
ALLSTRUCT(SYMOUT)
return ptr;
}
// struct_in -- input struct from byte array
const uint8_t *
struct_in(ID_Info *str,const uint8_t *ptr)
{
ALLSTRUCT(SYMIN)
return ptr;
}
// struct_prt -- print struct to byte array
void
struct_prt(const ID_Info *str)
{
printf("struct_prt:\n");
ALLSTRUCT(SYMPRT)
}
// prtu8 -- print byte array
void
prtu8(const uint8_t *ptr,size_t count,const char *sym)
{
printf("%s:",sym);
for (size_t idx = 0; idx < count; ++idx)
printf(" %2.2X",ptr[idx]);
printf("\n");
}
int
main(void)
{
uint8_t data_in[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t data_out[sizeof(data_in)];
// show original byte array
prtu8(data_in,sizeof(data_in),"data_in");
// import data into struct
struct_in(&id_info,data_in);
// show struct values
struct_prt(&id_info);
// export data from struct
struct_out(&id_info,data_out);
// show exported byte array
prtu8(data_out,sizeof(data_out),"data_out");
// reimport the struct data
struct_in(&id_info,data_out);
// show struct data
struct_prt(&id_info);
return 0;
}
Here is the [redacted] preprocessor output:
typedef struct ID_Info {
uint16_t model_number;
uint16_t serial_number;
uint16_t firmware_version;
} ID_Info;
ID_Info id_info;
uint8_t *
struct_out(const ID_Info * str, uint8_t * ptr)
{
do {
*(uint16_t *) ptr = str->model_number;
ptr += sizeof(uint16_t);
} while (0);
do {
*(uint16_t *) ptr = str->serial_number;
ptr += sizeof(uint16_t);
} while (0);
do {
*(uint16_t *) ptr = str->firmware_version;
ptr += sizeof(uint16_t);
} while (0);
return ptr;
}
const uint8_t *
struct_in(ID_Info * str, const uint8_t * ptr)
{
do {
str->model_number = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
do {
str->serial_number = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
do {
str->firmware_version = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
return ptr;
}
void
struct_prt(const ID_Info * str)
{
printf("struct_prt:\n");
printf(" " "model_number" "=" "%u" " (%8.8X)\n", str->model_number, str->model_number);
printf(" " "serial_number" "=" "%u" " (%8.8X)\n", str->serial_number, str->serial_number);
printf(" " "firmware_version" "=" "%u" " (%8.8X)\n", str->firmware_version, str->firmware_version);
}
void
prtu8(const uint8_t * ptr, size_t count, const char *sym)
{
printf("%s:", sym);
for (size_t idx = 0; idx < count; ++idx)
printf(" %2.2X", ptr[idx]);
printf("\n");
}
int
main(void)
{
uint8_t data_in[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t data_out[sizeof(data_in)];
prtu8(data_in, sizeof(data_in), "data_in");
struct_in(&id_info, data_in);
struct_prt(&id_info);
struct_out(&id_info, data_out);
prtu8(data_out, sizeof(data_out), "data_out");
struct_in(&id_info, data_out);
struct_prt(&id_info);
return 0;
}
Here is the test program output:
data_in: 00 11 22 33 44 55
struct_prt:
model_number=4352 (00001100)
serial_number=13090 (00003322)
firmware_version=21828 (00005544)
data_out: 00 11 22 33 44 55
struct_prt:
model_number=4352 (00001100)
serial_number=13090 (00003322)
firmware_version=21828 (00005544)
I need to send a message from Main thread to my Created Thread using WinAPI and Ring Buffer.
I defined structures and wrote functions for my Ring buffer.
Ring Buffer - it contains head, tail, size and pointer to the structure Descriptor which has length of Data and data itself. As I need to send 2 parameters to CreateThread function, I created the third structure ThreadParams to keep 2 parameters.
I want to leave this structures how they are now, not changeable.
typedef struct _Descriptor
{
uint32_t dataLen;
void * data;
} Descriptor;
typedef struct _ringBuffer
{
Descriptor *bufferData;
int head;
int tail;
int size;
} ringBuffer;
typedef struct _ThreadParams
{
void * ptr1;
void * ptr2;
} ThreadParams;
There are my realisations of Ring Buffer functions:
void bufferFree(ringBuffer *buffer)
{
free(buffer->bufferData);
}
void ringInitialization(ringBuffer *buffer, int size)
{
buffer->size = size;
buffer->head = 0;
buffer->tail = 0;
buffer->bufferData = (Descriptor*)malloc(sizeof(Descriptor) * size);
}
int pushBack(ringBuffer *buffer, void * data) // fill buffer
{
buffer->bufferData[buffer->tail++] = *(Descriptor*)data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
return 0;
}
int popFront(ringBuffer *buffer)
{
if (buffer->head != buffer->tail)
{
buffer->head++;
if (buffer->head == buffer->size)
{
buffer->head = 0;
}
}
return 0;
}
My main: I checked that I can send a few bytes (the memory is shared between threads), now I need to send a big message (> BUFF_SIZE) though Ring Buffer what I'm trying to do in while() cycle. Here is the question: how should I do it? My thing doesn't work because I catch an exception in printf() function (memory acces violation).
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <strsafe.h>
#include <stdint.h>
#define RING_SIZE 256
#define BUFFER_SIZE 1024
DWORD WINAPI HandleSendThread(LPVOID params);
uint8_t * getPointer(uint8_t *buffer, uint32_t index)
{
uint8_t * ptr = ((uint8_t*)buffer) + index * BUFFER_SIZE;
return ptr;
}
int main(int argc, char * argv[])
{
//Descriptor * ringData = (Descriptor *)malloc(sizeof(Descriptor) * RING_SIZE);
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
ring.bufferData[0].data = currentBuffer;
ring.bufferData[0].dataLen = strlen(str);
int currentSize = 0;
int ringSize = RING_SIZE;
while(ring.bufferData[0].data != '\0')
{
for (int i = currentSize; i < ringSize; i + RING_SIZE)
{
pushBack(&ring, currentBuffer);
printf("h = %s, tail = %s, dataBuffer = %s\n", (char*)ring.head, (char*)ring.tail, (char*)ring.bufferData[i].data);
}
currentSize = ringSize;
ringSize = 2 * ringSize;
popFront(&ring);
}
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, ¶ms, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
And my CreateThread function:
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
//ring->bufferData[0].dataLen = sizeof(buffer) + sizeof(ring->bufferData[0])*1024;
printf("Shared memory check: ringBuffer data = \"%s\", \nlength = %d\n", (char*)ring->bufferData[0].data, ring->bufferData[0].dataLen);
return 0;
}
Your most immediate problem is the inconsistency between the code in pushBack(), which expects data to point to a Descriptor, and the code in your main function, which passes in a pointer to a string instead.
If you had declared pushBack() properly, i.e.,
void pushBack(ringBuffer *buffer, Descriptor * data)
{
buffer->bufferData[buffer->tail++] = *data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
}
Then the compiler would have been able to warn you about the discrepancy.
You also have an infinite loop here:
for (int i = currentSize; i < ringSize; i + RING_SIZE)
You probably meant
for (int i = currentSize; i < ringSize; i += RING_SIZE)
... although it still doesn't look to me like it will do anything sensible. Nor do I understand the purpose of the outer loop, which compares a pointer to a character.
Found a solution
int main(int argc, char * argv[])
{
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
Descriptor temp = { 0 };
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
temp.dataLen = strlen(str);
temp.data = currentBuffer;
pushBack(&ring, &temp);
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, ¶ms, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
Descriptor * temp = &ring->bufferData[ring->head];
for (int i = 0; i < temp->dataLen; i++)
{
printf("%c", ((char*)temp->data)[i]);
}
printf("\n");
return 0;
}
In the moment I am working on a project on which a few processors are communication between then using can bus. The main controler (beagle bone) controls the other device using a can bus. Using a socket can linux framework I wrote a proccess that reads the can messages send from the other devices, and now I want to put the messages I get into a FIFO bufer, and then to obrabotam the messages.
So I need to write the FIFO buffer with can_frame structure inside.
For example:
struct can_buffer {
struct can_frame *frames;
int head;
int tail;
int size;
};
can_buffer new_can_buffer (size_t capacity)
{
can_buffer rb = malloc(sizeof(struct can_buffer));
if (rb) {
/* One byte is used for detecting the full condition. */
rb->size = capacity + 1;
rb->frames = malloc(rb->size * sizeof(struct can_frame));
if (rb->frames)
can_buffer_reset(rb);
else {
free(rb);
return 0;
}
}
return rb;
}
size_t can_buffer_size(const struct can_buffer *rb)
{
return rb->size;
}
size_t can_buffer_capacity(const struct can_buffer *rb)
{
return can_buffer_buffer_size(rb) - 1;
}
size_t can_buffer_free(const struct can_buffer *rb)
{
if (rb->head >= rb->tail)
return can_buffer_capacity(rb) - (rb->head - rb->tail);
else
return rb->tail - rb->head - 1;
}
int can_buffer_is_full(const struct can_buffer *rb)
{
return can_buffer_free(rb) == 0;
}
int can_buffer_is_empty(const struct can_buffer *rb)
{
return can_buffer_free(rb) ==can_buffer_capacity(rb);
}
void can_buffer_reset(can_buffer rb)
{
rb->head = rb->tail = 0;
}
.........
........
/* Add message to the end of the queue. */
void can_buffer_push(struct can_buffer *cb, struct can_frame *frame)
{
memcpy(&cb->frames[cb->tail], frame, sizeof(struct can_frame));
cb->tail = (cb->tail + 1) % cb->size;
}
/* Retrieve message from the start of the queue. */
can_frame *can_buffer_pop(struct can_buffer *cb)
{
struct can_frame *frame;
memcpy(frame, &cb->frames[cb->head], sizeof(struct can_frame));
cb->head = (cb->head + 1) % cb->size;
return frame;
}
But I canoot do it successfully. I think the problem is that every can_frame structure inside is a structure again,that is the problem (for example int, char etc), but I do not know how to solve this issue.
How can I make a FIFO buffer that can store the can_frame structure inside?
I need to write this in C lagnuage
in main i call
can_buffer can_buff;
can_buff = new_can_buffer(100);
can_buffer_push(can_buff,frame);
frame = can_frame i received
can_buff = fifo buffer
Well, you have incompletely modified the ringbuf routines. Specifically, you don't allocate enough space for the structures here:
if (rb) {
/* One byte is used for detecting the full condition. */
rb->size = capacity + 1;
rb->frames = malloc(rb->size);
if (rb->frames)
ringbuf_reset(rb);
else {
free(rb);
return 0;
}
}
The malloc needs to be
rb->frames = malloc(rb->size * sizeof(struct can_frame));
And the you should update the ringbuf_reset() call on the next line to your renamed can_buffer_reset()
Addendum:
I just noticed that you also need to update your ringbuf_reset() function to rb->head = rb->tail = 0
Addendum 2:
Referencing the newly added code, can_buffer_pop() will not work correctly as it doesn't check for the message existing and it doesn't allocate memory for the popped message.
There is also a typo in can_buffer_capacity().
Editorial:
I would strongly suggest writing a simple test program that executes these functions. It's frustrating but will catch a number of these small gotchas.
I want to send packets over a socket which have the following format:
struct Packet{
uint32_t seqnum;
uint16_t check;
char data[1024]
};
Each packet has a sequence number, a checksum, and the data that the packet contains. How could I put these 3 fields into a buffer to be sent over a socket, and then when received, the fields can be extracted? For example, is it possible to maybe make the first 4 bytes of the buffer the seqnum, the next 4 bytes the check, and then the remainder the data which is 1024 bytes? So the receiver will expect to receive a total of 1032 bytes and then should be able to extract the first 4 and make that seqnum, the next 4 and make that check sum and the last 1024 as the data? I am doing this over UDP so I cannot send the fields separately.
edit - OP didn't ask for C++, however the underlying implementation is done in C if that helps.
I modified a class from the Boost example website to suit your needs, it's a convenience wrapper for your buffers and struct. This should suit your needs, but it doesn't take into account big or little endianess.
It makes it a lot easier when your message is always a fixed size, but adding another 4 byte integer to your header that holds message length will save you a lot of bandwidth and it's very simple to do.
message myMessage;
myMessage.setSequence(nextSequenceNumber);
myMessage.setCheckSum(mycheckSum);
//assuming the message you have is a std::string
memcpy( myMessage.body(), message.c_str(), myMessage.body_length() );
myMessage.encode_header();
sendto(socket, myMessage.data(), myMessage.length(), 0, &my_sockaddrsizeof(my_sockaddr));
myMessage.reset();
recvfrom(socket, myMessage.data(), myMessage.length(), 0, &my_sockaddr, &my_sockaddr_len);
if(!myMessage.decodeHeader()){
// handle bad header/corrupt message
}
int32_t sequence = myMessage.getSequence();
int32_t checkSum = myMessage.getSheckSum();
class message
{
public:
enum { check_length = 4 };
enum { seq_length = 4 };
enum { body_length = 1024 };
message() : _checkSum(0), _sequence(0), _body_length(1024), _header_length(8) {} // constructor + init list
const char* data() const { return data_; }
char* data() { return data_; }
const char* body() const { return data_ + check_length + seq_length; }
char* body() { return data_ + check_length + seq_length; }
std::size_t length() { return _body_length + _header_length; }
std::size_t body_length() { return _body_length; }
int32_t const& getCheckSum() const { return _checkSum; }
int32_t const& getSequence() const { return _sequence; }
void setCheckSum(const int32_t& s) { _checkSum = s; }
void setSequence(const int32_t& s) { _sequence = s; }
bool decode_header()
{
char check_sum[check_length + seq_length + 1] = "";
char sequence[check_length + seq_length + 1] = "";
strncat_s(check_sum, data_, seq_length);
strncat_s(sequence, data_ + check_length, check_length);
_checkSum = std::atoi(check_sum);
_sequence = std::atoi(sequence);
if (_checkSum == 0 || _sequence == 0)
{
std::cout << "Header malfunction" << std::endl;
return false;
}
return true;
}
void encode_header()
{
char header[check_length + seq_length + 1] = "";
std::memcpy(header, &_sequence, sizeof(int32_t));
std::memcpy(header + seq_length, &_checkSum, sizeof(int32_t));
std::memcpy(data_, header, check_length + seq_length);
}
void reset()
{
memset(&data_[0], 0, sizeof(data_));
int32_t _checkSum = 0;
int32_t _sequence = 0;
}
private:
char data_[check_length + seq_length + body_length];
int32_t _body_length, _header_length;
int32_t _checkSum;
int32_t _sequence;
};
You have to make sure your struct is set up properly to have fields properly aligned at the proper byte boundaries.
So this is fine:
struct Packet{
uint32_t seqnum;
uint32_t check;
char data[1024];
};
But this is not:
struct Packet{
uint16_t other;
// 2 bytes of padding exist here so seqnum sits on a 4-byte boundary
uint32_t seqnum;
uint32_t check;
char data[1024];
};
Any integer fields larger than 8 bit should be converted to network byte order before sending and back to host byte order after receiving. Use htonl and ntohl for 32-bit values and htons and ntohs for 16-bit values.
Once you've done that, you can send that structure directly:
struct Packet packet;
// populate packet
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// perform error checking, call bind, etc.
int sendLen = sendto(socket, &packet, sizeof(packet), 0, &my_sockaddr, sizeof(my_sockaddr));
And similarly receive it:
struct Packet packet;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// perform error checking, call bind, etc.
int recvLen = recvfrom(socket, &packet, sizeof(packet), 0, &my_sockaddr, &my_sockaddr_len);
// read packet
I was looking at basil00's torwall, and for fun was trying to pare it down to just intercept DNS. (provide an answer back to myself of 127.0.0.1 for webfiltering purposes, learning project)
however, at this point, I have it hijacking the dns packet, but it does not return a correct address. for every "blocked" domain, it's different.
for example, I put cbc.ca in my hosts.deny file (blacklist), and it returns an address of 0.4.114.2
then blacklisting slashdot, it will return 0.4.0.1
this has been quite confusing and frustrating, and after three days of research, I am out of ideas.
Here is the code to the redirect portion of my program, which seems to be where things go awry.
(note some of the comments will be goofy as I was hacking down a program for a different purpose and haven't cleaned it up yet)
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "windivert.h"
#include "domain.h"
#include "main.h"
#include "redirect.h"
#define MAX_PACKET 4096
#define NUM_WORKERS 4
// DNS headers
#define DNS_MAX_NAME 254
struct dnshdr
{
uint16_t id;
uint16_t options;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} __attribute__((__packed__));
struct dnsq
{
uint16_t type;
uint16_t class;
} __attribute__((__packed__));
struct dnsa
{
uint16_t name;
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t length;
uint32_t addr;
} __attribute__((__packed__));
static DWORD redirect_worker(LPVOID arg);
static int handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr,
PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data,
size_t data_len);
// State:
static bool redirect_on = false;
static HANDLE handle = INVALID_HANDLE_VALUE;
static HANDLE workers[NUM_WORKERS] = {NULL}; // Worker threads
// Send a packet asynchronously:
static void send_packet(HANDLE handle, void *packet, size_t packet_len,
PWINDIVERT_ADDRESS addr)
{
addr->Direction = WINDIVERT_DIRECTION_INBOUND;
WinDivertHelperCalcChecksums(packet, packet_len, 0);
if (!WinDivertSend(handle, packet, packet_len, addr, NULL))
debug("Send packet failed (err=%d)\n", (int)GetLastError());
}
// Start traffic redirect through Tor:
extern void redirect_start(void)
{
debug("DNS divert START\n");
if (handle != INVALID_HANDLE_VALUE)
return;
handle = WinDivertOpen(
"outbound and udp.DstPort == 53 or inbound and udp.DstPort = 53", 0, 0, 0);
// Launch threads:
redirect_on = true;
for (size_t i = 0; i < NUM_WORKERS; i++)
{
workers[i] = CreateThread(NULL, MAX_PACKET*3,
(LPTHREAD_START_ROUTINE)redirect_worker, (LPVOID)handle, 0, NULL);
if (workers[i] == NULL)
{
exit(EXIT_FAILURE);
}
}
}
// Stop traffic redirect through Tor:
extern void redirect_stop(void)
{
debug("DNS divert STOP\n");
if (handle == INVALID_HANDLE_VALUE)
return;
// Close the WinDivert handle; will cause the workers to exit.
redirect_on = false;
if (!WinDivertClose(handle))
{
exit(EXIT_FAILURE);
}
handle = INVALID_HANDLE_VALUE;
for (size_t i = 0; i < NUM_WORKERS; i++)
{
WaitForSingleObject(workers[i], INFINITE);
workers[i] = NULL;
}
}
// Redirect worker thread:
static DWORD redirect_worker(LPVOID arg)
{
HANDLE handle = (HANDLE)arg;
// Packet processing loop:
char packet[MAX_PACKET];
UINT packet_len;
WINDIVERT_ADDRESS addr;
while (redirect_on)
{
if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
{
// Silently ignore any error.
continue;
}
PWINDIVERT_IPHDR iphdr = NULL;
PWINDIVERT_TCPHDR tcphdr = NULL;
PWINDIVERT_UDPHDR udphdr = NULL;
PVOID data = NULL;
UINT data_len;
WinDivertHelperParsePacket(packet, packet_len, &iphdr, NULL, NULL,
NULL, &tcphdr, &udphdr, &data, &data_len);
int dnshandle = 0;
if (udphdr != NULL && ntohs(udphdr->DstPort) == 53)
dnshandle = handle_dns(handle, &addr, iphdr, udphdr, data, data_len);
if(dnshandle != 1)
{
if (!WinDivertSend(handle, packet, packet_len, &addr, NULL))
{
}
}
}
return 0;
}
// Handle DNS requests.
// NOTES:
// - If anything goes wrong, we simply drop the packet without error.
// - An alternative approach would be to let Tor resolve the address, however,
// this would be slow.
static int handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr,
PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data,
size_t data_len)
{
struct dnshdr *dnshdr = (struct dnshdr *)data;
data += sizeof(struct dnshdr);
data_len -= sizeof(struct dnshdr);
char name[DNS_MAX_NAME + 8]; // 8 bytes extra.
size_t i = 0;
while (i < data_len && data[i] != 0)
{
size_t len = data[i];
if (i + len >= DNS_MAX_NAME)
return -1;
name[i++] = '.';
for (size_t j = 0; j < len; j++, i++)
name[i] = data[i];
}
name[i++] = '\0';
// Generate a fake IP address and associate it with this domain name:
uint32_t fake_addr = domain_lookup_addr(name);
if (fake_addr == 0)
{
// This domain is blocked; so ignore the request.
// Construct a query response:
size_t len = sizeof(struct dnshdr) + data_len + sizeof(struct dnsa);
if (len > 512) // Max DNS packet size.
return -1;
len += sizeof(WINDIVERT_IPHDR) + sizeof(WINDIVERT_UDPHDR) + len;
char buf[len + 8]; // 8 bytes extra.
PWINDIVERT_IPHDR riphdr = (PWINDIVERT_IPHDR)buf;
PWINDIVERT_UDPHDR rudphdr = (PWINDIVERT_UDPHDR)(riphdr + 1);
struct dnshdr *rdnshdr = (struct dnshdr *)(rudphdr + 1);
char *rdata = (char *)(rdnshdr + 1);
UINT local_ip;
DivertHelperParseIPv4Address("127.0.0.1",&local_ip);
memset(riphdr, 0, sizeof(WINDIVERT_IPHDR));
riphdr->Version = 4;
riphdr->HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(uint32_t);
riphdr->Length = htons(len);
riphdr->Id = htons(0xF00D);
WINDIVERT_IPHDR_SET_DF(riphdr, 1);
riphdr->TTL = 64;
riphdr->Protocol = IPPROTO_UDP;
riphdr->SrcAddr = iphdr->DstAddr;
riphdr->DstAddr = iphdr->SrcAddr;
memset(rudphdr, 0, sizeof(WINDIVERT_UDPHDR));
rudphdr->SrcPort = htons(53); // DNS
rudphdr->DstPort = udphdr->SrcPort;
rudphdr->Length = htons(len - sizeof(WINDIVERT_IPHDR));
rdnshdr->id = dnshdr->id;
rdnshdr->options = htons(0x8180); // Standard DNS response.
rdnshdr->qdcount = htons(0x0001);
rdnshdr->ancount = htons(0x0001);
rdnshdr->nscount = 0;
rdnshdr->arcount = 0;
memcpy(rdata, data, data_len);
struct dnsa *rdnsa = (struct dnsa *)(rdata + data_len);
rdnsa->name = htons(0xC00C);
rdnsa->type = htons(0x0001); // (A)
rdnsa->class = htons(0x0001); // (IN)
rdnsa->ttl = htonl(0x00000258) ; // 1 second
rdnsa->length = htons(0x0004);
rdnsa->addr = htonl(local_ip); // Fake address
send_packet(handle, &buf, len, addr);
debug("address: %u\n",addr->Direction);
debug("Intercept DNS %s\n", (name[0] == '.'? name+1: name));
return 1;
}
// Re-inject the matching packet.
/*
/
*/
return 0;
}
Here's the domain lookup side of it (mostly just hacked down to try to get the results I want:
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "domain.h"
#include "main.h"
#define RATE_LIMIT 8000
#define rand16() \
(rand() & 0xFF) | ((rand() & 0xFF) << 8)
// Domain blacklist:
struct blacklist
{
size_t size;
size_t len;
char **names;
};
static struct blacklist *blacklist = NULL;
// State:
static struct name *names[UINT16_MAX] = {NULL};
static HANDLE names_lock = NULL;
// Prototypes:
static struct blacklist *domain_blacklist_read(const char *filename);
static bool domain_blacklist_lookup(struct blacklist *blacklist,
const char *name);
static int __cdecl domain_blacklist_compare_0(const void *x, const void *y);
static int domain_blacklist_compare(const char *name0, size_t len,
const char *name1);
// Initialize this module:
extern void domain_init(void)
{
// Load the domain blacklist.
blacklist = domain_blacklist_read("hosts.deny");
}
// Lookup an address given a domain name. If the name does not exist then
// create one.
extern uint32_t domain_lookup_addr(const char *name0)
{
if (name0[0] == '.')
name0++;
if (domain_blacklist_lookup(blacklist, name0))
{
debug("Block %s\n", name0);
return 0; // Blocked!
}
return;
}
// Read the blacklist file:
static struct blacklist *domain_blacklist_read(const char *filename)
{
struct blacklist *blacklist =
(struct blacklist *)malloc(sizeof(struct blacklist));
if (blacklist == NULL)
{
exit(EXIT_FAILURE);
}
blacklist->size = 0;
blacklist->len = 0;
blacklist->names = NULL;
FILE *stream = fopen(filename, "r");
if (stream == NULL)
{
return blacklist;
}
// Read blocked domains:
int c;
char buf[256];
while (true)
{
while (isspace(c = getc(stream)))
;
if (c == EOF)
break;
if (c == '#')
{
while ((c = getc(stream)) != '\n' && c != EOF)
;
continue;
}
size_t i = 0;
while (i < sizeof(buf)-1 && (c == '-' || c == '.' || isalnum(c)))
{
buf[i++] = c;
c = getc(stream);
}
if (i >= sizeof(buf)-1 || !isspace(c))
{
exit(EXIT_FAILURE);
}
buf[i] = '\0';
if (blacklist->len >= blacklist->size)
{
blacklist->size = (blacklist->size == 0? 32: 2 * blacklist->size);
blacklist->names = (char **)realloc(blacklist->names,
blacklist->size * sizeof(char *));
if (blacklist->names == NULL)
{
exit(EXIT_FAILURE);
}
}
size_t size = (i+1) * sizeof(char);
char *name = (char *)malloc(size);
if (name == NULL)
{
exit(EXIT_FAILURE);
}
for (size_t j = 0; j < i; j++)
name[j] = buf[i - 1 - j];
name[i] = '\0';
blacklist->names[blacklist->len++] = name;
}
fclose(stream);
qsort(blacklist->names, blacklist->len, sizeof(char *),
domain_blacklist_compare_0);
return blacklist;
}
// Check if a domain matches the blacklist or not:
static bool domain_blacklist_lookup(struct blacklist *blacklist,
const char *name)
{
if (blacklist->len == 0)
return false;
size_t len = strlen(name);
ssize_t lo = 0, hi = blacklist->len-1;
while (lo <= hi)
{
ssize_t mid = (lo + hi) / 2;
int cmp = domain_blacklist_compare(name, len, blacklist->names[mid]);
if (cmp > 0)
hi = mid-1;
else if (cmp < 0)
lo = mid+1;
else
return true;
}
return false;
}
// Domain compare function(s):
static int __cdecl domain_blacklist_compare_0(const void *x, const void *y)
{
const char *name0 = *(const char **)x;
const char *name1 = *(const char **)y;
return strcmp(name0, name1);
}
static int domain_blacklist_compare(const char *name0, size_t len,
const char *name1)
{
size_t i = 0;
ssize_t j = (ssize_t)len - 1;
for (; j >= 0 && name1[i] != '\0'; i++, j--)
{
int cmp = (int)name1[i] - (int)name0[j];
if (cmp != 0)
return cmp;
}
if (j < 0 && name1[i] != '\0')
return 1;
return 0;
}
any assistance is appreciated.
Also, I have uploaded the code to: Github
Thank you.
There's one of two things going on here. You're reading and writing DNS packets incorrectly, and or you're failing to convert the addresses to or from host to network order before working with them.
I'm going to bet on you reading and writing DNS packets incorrectly. I've implemented my own filtering systems using WinDivert where I was hijacking and passing all DNS traffic through my own local DNS server and I've seen these fudged addresses come out exactly as the results you're getting when I was parsing and writing DNS packets incorrectly.
The bad news is, I can't point you to a full DNS library in C/C++ as I know of none that actually make the job easier (maybe, see edits). I personally had my diversion code written in C++ with WinDivert and then use Arsoft.Tools.Net C# DNS library for actually running a local DNS server and manipulating DNS responses.
There is one project in C++ that calls itself boost::net::dns because I think the writer was hoping it would be part of boost, which it isn't and probably won't be. Avoid this library as the internal mechanism for parsing and storing multiple A Records is bugged out and broken and you'll get the same wacky results you're getting here. I tried to work with him to get it fixed but, he was only interested in blaming my code.
I'll have a gander again and update my answer if I can find a decent lib for working with DNS packets in C++. Don't try to do it yourself, we're talking about entire protocols with entire books of RFC's to do this properly yourself.
Update
As promised, here are some results from my search:
Mozilla Necko (Formerly Netlib)
http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/
C-Ares:
https://github.com/bagder/c-ares
A list of alternative libraries C-Ares compiled:
http://c-ares.haxx.se/otherlibs.html
Also, it is linux specific but pretty clean code. You might be able to pilfer through libcrafter's DNS classes and move them to your own project.
https://github.com/pellegre/libcrafter/blob/master/libcrafter/crafter/Protocols/DNS.h
Again, unless your "learning project" is understanding and recreating one of the foundational pillars of the internet, don't implement this yourself. Try and use one of the previously listed libraries to handle your DNS packets.