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
Related
I have to handle the communication between a computer running a program written in Python and microcontroller using C code. The protocol struct is designed to prevent padding using uin32_t for all fields except the value which is restricted to be either int or double. It looks something like this:
struct head {
#define CMD_SET 1
#define CMD_GET 2
uint32_t cmd;
uint32_t err;
#define TYPE_INT 1
#define TYPE_DOUBLE 2
uint32_t type;
};
struct frame {
struct head *head;
void *val;
};
If I initially get the raw bytes in a uint8_t buffer and the buffer was made to comply with the struct layout above, can I cast to and from this buffer to struct head and to void *val?
I have tried the code below and it seems to be working but I am afraid that it might invoke UB. Is the code below safe? If not, what techniques should I use to handle this process? Use memcpy? Maybe #pragma pack?
// Example:
uint8_t buf[sizeof(struct head) * 2] = {0}; // Make the buffer big enough
void process_cmd(uint8_t *buf)
{
struct frame frame = {0};
frame.head = (struct frame *)buf; // UB?
frame.val = frame.head + 1
if (frame.head->cmd == CMD_SET) {
if (frame.head->type == TYPE_INT) {
do_work_with_int(*(int *)frame.val); // UB?
} else if (frame.head->type == TYPE_DOUBLE) {
do_work_with_dobule(*(double *)frame.val); // UB?
}
} else if (frame.head->cmd == CMD_GET) {
if (frame.head->type == TYPE_INT) {
int val = 1;
memcpy(frame.val, &val, sizeof(val));
} else if (frame.head->type == TYPE_DOUBLE) {
double val = 2.0;
memcpy(frame.val, &val, sizeof(val));
}
} else {
frame.err = 1;
}
}
int main(void)
{
uart_read(uart_hdlr, buf, sizeof(buf));
process_cmd(buf);
uart_send(uart_hdlr, buf, sizeof(buf));
}
I need to build a function that returns the most frequent sourceIP (and then Destination IP) within an array of structs. This is my structure.
typedef enum { IN_IN, IN_OUT, OUT_IN} Direction;
typedef enum { IGMP, TCP, UDP} Protocol;
typedef unsigned int Count;
#define NUM 6
typedef struct {
long long timestamp;
Direction direction;
char sourceIP[16];
char destIP[16];
Protocol protocol;
unsigned int port;
Count bytes;
}Packet;
I need to implement a function which receives the size of the array, an array type Packet and returns the most frequent IP source address within an array of structs. Here is my STRUCT array:
Packet packetsarray[NUM] = {
{42069, IN_OUT, "192.100.200.2", "192.10.0.6", TCP, 30, 256 },
{666, OUT_IN, "148.201.196.208", "148.201.196.1", UDP, 80,666},
{1002, IN_IN, "192.100.200.2", "10.10.0.6", IGMP, 30, 256 },
{1002, IN_OUT, "192.100.200.2", "10.10.0.6", IGMP, 30, 256 },
{1002, OUT_IN, "192.100.200.2", "10.10.0.6", IGMP, 30, 256 },
{1002, IN_OUT, "192.100.200.2", "10.10.0.6", IGMP, 30, 256 }};
However my teacher explicitly states do the operation without using any brackets, ONLY using pointer arithmetrics this is what I have so far for my function:
void CountDirections(Packet packets[], int N, int *c1, int *c2, int *c3)
{
*c1 = 0;
*c2 = 0;
*c3 = 0;
Packet *packetpointer = packets;
Packet *packetpointer2 = packets;
packetpointer2++;
int i, maxcount = 0, count = 0;
char word[16];
for (i= 0; i < N; i++)
{
if(packetpointer->direction == IN_OUT)
*c2 += 1;
else if(packetpointer->direction == OUT_IN)
*c3 += 1;
else if (packetpointer->direction == IN_IN)
*c1 += 1;
packetpointer++;
}
packetpointer = packets;
for (i= 0; i < N; i++)
{
strcpy(word, packetpointer->sourceIP);
printf("%s\n", word);
packetpointer++;
}
}
I have NO CLUE how to find the most frequent SourceIP within the array of structs without using brackets [] a friend told me to use a MODE algorithm but I don't know how to implement it using pointers only. Please help.
I am building NKE(Network Kernal Extension) for filtering and modifying the packets on the fly. myipfilter_output_redirect callback gives mbuf_t pointer and based on the researched knowledge it has every information related to the network call.
I want to read the html from this mbuf_t and inject one css/html into it. how can I achieve it?
static errno_t myipfilter_output(void* cookie, mbuf_t* data, ipf_pktopts_t options) {
if (data)
log_ip_packet(data, kMyFiltDirOut);
return 0;
}
static errno_t myipfilter_input(void* cookie, mbuf_t* data, int offset, u_int8_t protocol) {
if (data)
log_ip_packet(data, kMyFiltDirIn);
return 0;
}
static void myipfilter_detach(void* cookie) {
/* cookie isn't dynamically allocated, no need to free in this case */
struct myfilter_stats* stats = (struct myfilter_stats*)cookie;
printf("UDP_IN %lu UDP OUT: %lu TCP_IN: %lu TCP_OUT: %lu ICMP_IN: %lu ICMP OUT: %lu OTHER_IN: %lu OTHER_OUT: %lu\n",
stats->udp_packets[kMyFiltDirIn],
stats->udp_packets[kMyFiltDirOut],
stats->tcp_packets[kMyFiltDirIn],
stats->tcp_packets[kMyFiltDirOut],
stats->icmp_packets[kMyFiltDirIn],
stats->icmp_packets[kMyFiltDirOut],
stats->other_packets[kMyFiltDirIn],
stats->other_packets[kMyFiltDirOut]);
g_filter_detached = TRUE;
}
static struct ipf_filter g_my_ip_filter = {
&g_filter_stats,
"com.xxx.NetworKext",
myipfilter_input,
myipfilter_output_redirect, // myipfilter_output,
myipfilter_detach
};
kern_return_t MyIPFilter_start () {
printf("MyIPFilter_start called");
int result;
result = ipf_addv4(&g_my_ip_filter, &g_filter_ref);
return result;
}
kern_return_t MyIPFilter_stop () {
printf("MyIPFilter_stop called");
ipf_remove(g_filter_ref);
return KERN_SUCCESS;
}
static errno_t myipfilter_output_redirect(void* cookie, mbuf_t* data, ipf_pktopts_t options)
{
// not printing all html and css tags
printf("myipfilter_output_redirect called");
unsigned char* dataString = NULL;
for (mbuf_t mb = *data; mb; mb = mbuf_next(mb))
{
dataString = mbuf_data(mb);
size_t len = mbuf_len(mb);
for (size_t i = 0; i < len; i++)
{
printf("%c", dataString[i]);
}
}
printf("dataString: %s", dataString);
}
I have made a sample repo if you can help here anything.
you should choose socket filter and in order to retrieve HTML payload you should read mbuf_t using mbuf_t data. Below method prints every bytes from the starts so put it in your sf_data_in_func call back.
print_mbuf_data(*data);
This would work for you.
static void print_mbuf_data(mbuf_t mb){
// unsigned char *tmp_buffer = (unsigned char *) mbuf_datastart(mb);
unsigned char *tmp_buffer = (unsigned char *) mbuf_data(mb);
unsigned long line = 0, index = 0, character = 0, hex_length = 0x80; // hex_length has limit of 64 decimal
unsigned long length = mbuf_len(mb);
unsigned char hex_temp [0x80]; // buffer has limit of 64 decimal
for (index = 0; index < length; index += 0x80)
{
memset(hex_temp, 0, hex_length);
line = length - index > 0x80 ? 0x80 : length - index;
for (character = 0; character < line; character++)
{
snprintf(((char *) hex_temp + strlen((char *) hex_temp)),
hex_length - strlen((char *) hex_temp), "%c", tmp_buffer[index + character]);
}
printf("%s", hex_temp);
}
}
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.
How can I read data from a packet in C and convert it into a structure? I mean, there's a structure like
|=======================================================================
|0123456701234567012345670123456701234567012345670123456701234567.......
| type | length | MSG HDR | data
into a struct like
struct msg {
char type;
size_t length;
int hdr;
struct data * data;
};
Is the following code fine?
bool parse_packet(char * packet, size_t packet_len, struct msg * result) {
if(packet_len < 5) return false;
result->type = *packet++;
result->length = ntohl(*(int*)packet);
packet+=4;
if(result->length + 4 + 5 > packet_len)
return false;
if(result->length < 2)
return false;
result->hdr = ntohs(*(short*)packet);
packet+=2;
return parse_data(result, packet);
}
It's usually good practice to check that packet and result are non-null.
Why are you checking that packet_len < 5 when the header is 7 bytes? Why not just ensure that the packet is at least 7 bytes and get it over with? Or is hdr not present for some type?
I'm not sure what you're trying to achieve with
if(result->length + 4 + 5 > packet_len)
result->hdr = ntohs(*(short*)packet);
packet+=2;
If the declared message length plus nine is greater than the received message length, you read another two bytes from the message. Then regardless of the length of the data, you add two to the pointer and try to parse something out of it. What if packet_len is 5 and result->length is 4294967295? You're going to read off the end of your buffer, just like in Heartbleed. You need to always verify that your reads are in bounds, and never trust the size declared in the packet.
You have a completely standard situation. There's nothing deep or surprising here.
Start with a specification of the wire format. You can use pseudo-code or actual C types for that, but the implication is that the data is packed into bytes on the wire:
struct Message // wire format, pseudo code
{
uint8_t type;
uint32_t length; // big-endian on the wire
uint8_t header[2];
uint8_t data[length];
};
Now start parsing:
// parses a Message from (buf, size)
// precondition: "buf" points to "size" bytes of data; "msg" points to Message
// returns true on success
// msg->data is malloc()ed and contains the data on success
bool parse_message(unsigned char * buf, std::size_t size, Message * msg)
{
if (size < 7) { return false; }
// parse length
uint32_t n;
memcpy(&n, buf + 1, 4);
n = ntohl(n); // convert big-endian (wire) to native
if (n > SIZE_MAX - 7)
{
// this is an implementation limit!
return false;
}
if (size != 7 + n) { return false; }
// copy data
unsigned char * p = malloc(n);
if (!p) { return false; }
memcpy(p, buf + 7, n);
// populate result
msg->type = buf[0];
msg->length = n;
msg->header[0] = buf[5];
msg->header[1] = buf[6];
msg->data = p;
return true;
}
An alternative way of parsing the length is like this, directly:
uint32_t n = (buf[1] << 24) + (buf[2] << 16) + (buf[1] << 8) + (buf[0]);
This code assumes that buf contains exactly one message. If you're taking messages off of a stream, you need to modify the code (namely the if (size != 7 + n)) to check if there is at least as much data available as required, and return the amount of consumed data, too, so the caller can advance their stream position accordingly. (The caller could in this case compute the amount of data that was parsed as msg->length + 7, but relying on that is not scalable.)
Note: As #user points out, if your size_t is not wider than uint32_t, then this implementation will wrongly reject very large messages. Specifically, messages for which it is not true that 7 + n > n will be rejected. I included a dynamic check for this (unlikely) condition.