I want to manually build a dns query to the dns sever using socket APIs (it's part of the project and can not be changed). So I want to copy a struct like:
typedef struct {
uint16_t dns_id; /* identification number */
uint16_t dns_flags; /* DNS flags */
uint16_t dns_qdc; /* number of question entries */
uint16_t dns_anc; /* number of answer entries */
uint16_t dns_nsc; /* number of authority entries */
uint16_t dns_arc; /* number of resource entries */
unsigned char *host;
unsigned short qtype;
unsigned short qclass;
} DNS_QUERY;
DNS_QUERY* dns_query = (DNS_QUERY *) malloc(sizeof(DNS_QUERY));
into a buffer like:
char* dns_buf = malloc(500);
but when I do that, nothing has been copied into the buffer. When I input different strings to the *host element, the sizeof(dns_query) stays the same. When I use
memcpy(dns_buf, dns_query, sizeof(DNS_QUERY));
it gives output like:
dns_buf:
strlen(dns_buf)= 0
Also when I print it using for loop like:
for (i = 0 ; i<strlen(dns_buf) ; i++){
printf("%c", dns_buf[i]);
}
it doesn't output anything
When I use pointers instead of memcpy, like:
dns_buf = dns_query;
it gives the same output. Could anybody instruct me on what I should do?
This is the whole code:
int udp_connect(char *hostname, char *dns_name, char buf[]){
int sockfd;
int num_bytes;
struct sockaddr_in servaddr;
char str[INET_ADDRSTRLEN];
struct hostent *hptr; /* For gethostbyname() */
char **pptr; /* For inet_ntop() */
char* dns_buf, recv_buf = NULL;
int len = 0;
int i = 0;
static unsigned short id = 0; /* For the query ID */
/* Allocating memory for structs */
DNS_QUERY* dns_query = malloc(sizeof(DNS_QUERY));
/* QUESTION *dns_question = malloc(sizeof(QUESTION));*/
recv_buf = malloc(MAXDATASIZE);
if((hptr = gethostbyname(dns_name)) == NULL){
perror("Error in gethostbyname()\n");
exit(1);
}
if ((pptr = hptr->h_addr_list) != NULL) { /* (hptr->h_addrtype == AF_INET) && */
printf("The address is: %s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
} else {
perror("Error in inet_ntop() \n");
}
memset(&servaddr, 0, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(DNS_PORT);
inet_pton(AF_INET, str, &servaddr.sin_addr);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
perror("Error while making a socket");
return -1;
}
/* Setting hostname into the name segment of the query */
ChangetoDnsNameFormat(hostname);
dns_query->host = hostname;
printf("\ndns_query->host: %s\n", dns_query->host);
/* memcpy(dns_buf, dns_query, sizeof(DNS_QUERY)); */
/* setting up the header */
dns_query->dns_id = htons(id); /* id */
dns_query->dns_flags = htons(0x10); /* 0000000100000000 recursion is desired*/
dns_query->dns_qdc = htons(0x01); /* We have one question */
dns_query->dns_anc = htons(0x00);
dns_query->dns_nsc = htons(0x00);
dns_query->dns_arc = htons(0x00);
dns_query->qtype = htons(1); /* For IPv4 */
dns_query->qclass = htons(1); /* For internet */
len = sizeof(DNS_QUERY) + strlen(hostname); /* Calculating the length to use it in sendto() */
dns_buf = malloc(len);
dns_buf = dns_query;
/* memcpy(dns_buf, dns_query, sizeof(DNS_QUERY)); */
for (i = 0 ; i<sizeof(DNS_QUERY) ; i++){
printf("%02X", dns_buf[i]);
}
/* to check if the same as dns_query, use the following print also */
for (i = 0 ; i<sizeof(DNS_QUERY) ; i++){
printf("%02X", dns_query[i]);
}
print("\n");
/* Sending the datagram to the dns server */
printf("\n--------------------\nSending datagram: \n%s\n", dns_buf);
if ((num_bytes = sendto(sockfd, dns_buf, len, 0, (struct sockaddr *) &servaddr, sizeof(servaddr))) == -1){
perror("Error in sendto");
exit(1);
}
/* Receiving the datagram from the dns server */
printf("Receiving datagram...\n");
num_bytes = recvfrom(sockfd, recv_buf, MAXDATASIZE, 0, NULL, NULL);
printf("Received %i bytes of datagram...\n", num_bytes);
printf("dns_buf in udp connect: %s\n", recv_buf);
id = id + 1;
free(dns_query);
freeaddrinfo((struct addrinfo *) &servaddr);
close(sockfd);
return sockfd;
}
/******************************************************************************/
void ChangetoDnsNameFormat(char *hostname)
{
int walker=0;
int i;
int counter = 0;
char tmp[40];
strcat(hostname, "$"); /* For having a $ at the end of the dns name format */
for(i=0 ; i< (int) strlen((char*)hostname) ; i++)
{
if(hostname[i]=='.')
{
tmp[walker] = (char) counter + 48;
/*printf("%s\n", tmp);*/
for( ; walker < i ; walker++)
{
tmp[walker + 1] = hostname[walker];
}
walker++;
counter = -1;
}else if(hostname[i]=='$'){
tmp[walker] = (char) counter + 47;
/*printf("%s\n", tmp);*/
for( ; walker < i ; walker++)
{
tmp[walker + 1] = hostname[walker];
}
walker++;
counter = -1;
}
counter++;
}
walker--;
tmp[walker] = '\0'; /* Terminate the string */
strcat(tmp, "0"); /* For having a 0 at the end of the dns name format */
strcpy(hostname, tmp);
}
The struct doesn't really contain the array of character host but rather a pointer to the first element.
This means that, although you are copying the whole structure, you just copy the pointer to the data contained in host (which then points to the same string). What you shall do is something like:
DNS_QUERY *dns_buf = malloc(sizeof(DNS_QUERY));
memcpy(dns_buf, dns_query, sizeof(DNS_QUERY));
dns_buf.host = "yourmodifiedhost";
dns_buf is not a string (which finish with null charcter).
it's a pointer (as char *) to a memory space and the memory could contains 0 as values at the beginning so that's why when you use strlen(dns_buf) you get it = to 0 that means the fisrt element in the dns_buf is 0
to see the dns_buf content you can use:
for (i = 0 ; i<sizeof(DNS_QUERY) ; i++){
printf("%02X", dns_buf[i]);
}
print("\n");
// to check if the same as dns_query, use the following print also
for (i = 0 ; i<sizeof(DNS_QUERY) ; i++){
printf("%02X", dns_query[i]);
}
print("\n");
Related
I am learning socket programming in C language, and this is an incomprehensible problem I encountered during my study.
Today I am trying to send a HTTP request to my test server which host an Apache example website, then receive the response from test server. Here is a part of my receive code.
unsigned long recv_size = 0;
unsigned long response_size = 4096;
int ret = 0;
char *recv_buff = (char *)malloc(response_size);
while (1)
{
// ret = recv(socket, recv_buff, response_size, MSG_WAITALL); // cannot get all data
ret = read(socket, recv_buff, response_size); // same effect as the above
recv_size += ret;
if (ret < 0)
error(strerror(errno));
else if (ret == 0)
break; // all data recved
}
The normal result of my test with burpsuit is this.
But what I received with the C language program was incomplete data.
I searched the reason for one night, but I still did not find a solution for my problem. Whether it is to set the buff to a super large size or any other method, the complete data cannot be accepted at all.
The traffic monitored from wireshark is ok, but my program still cannot receive the complete data. What is the problem?
If you know why, please let me know. THX. (o゜▽゜)o☆
UPDATE
The while loop will execute twice, and first time the value of ret is 3343, and second time is 0, so the loop will stop here.
You can get a short read on a socket.
But, your code to handle that has a few issues.
You're allocating a buffer of size response_size. You are always reading that amount instead of reducing the amount read by the amount you've already read on a prior loop iteration.
This can cause you to read past the end of the buffer causing UB (undefined behavior).
Your termination condition is if (ret == 0). This can fail if another packet arrives "early". You'll never see a ret of 0, because the partial data from the next packet will make it non-zero
Here's the corrected code:
#if 0
unsigned long recv_size = 0;
#endif
unsigned long response_size = 4096;
int ret = 0;
char *recv_buff = (char *) malloc(response_size);
#if 1
unsigned long remaining_size = response_size;
unsigned long offset = 0;
#endif
for (; remaining_size > 0; remaining_size -= ret, offset += ret) {
ret = read(socket, &recv_buff[offset], remaining_size);
if (ret < 0)
error(strerror(errno));
}
UPDATE:
The above code corrects some of the issues. But, for a variable length source [such as http], we don't know how much to read at the outset.
So, we have to parse the headers and look for the "Content-Length" field. This will tell us how much to read.
So, we'd like to have line oriented input for the headers. Or, manage our own buffer
Assuming we can parse that value, we have to wait for the empty line to denote the start of the payload. And, then we can loop on that exact amount.
Here's some code that attempts the header parsing and saving of the payload. I've coded it, but not compiled it. So, you can take it as pseudo code:
unsigned long recv_size = 0;
unsigned long response_size = 4096;
char *recv_buff = malloc(response_size + 1);
// line oriented header buffer
char *endl = NULL;
unsigned long linelen;
char linebuf[1000];
int ret = 0;
// read headers
while (1) {
// fill up a chunk of data
while (recv_size < response_size) {
recv_buff[recv_size] = 0;
// do we have a line end?
endl = strstr(recv_buff,"\r\n");
if (endl != NULL)
break;
ret = read(socket, &recv_buff[recv_size], response_size - recv_size);
if (ret < 0)
error(strerror(errno));
if (ret == 0)
break;
recv_size += ret;
}
// error -- no line end but short read
if (endl == NULL)
error(strerror(errno));
// copy header to work buffer
linelen = endl - recv_buff;
memcpy(linebuf,recv_buff,linelen);
linebuf[linelen] = 0;
// remove header from receive buffer
linelen += 2;
recv_size -= linelen;
if (recv_size > 0)
memcpy(recv_buff,&recv_buff[linelen],recv_size);
// stop on end of headers (back to back "\r\n")
if ((recv_size >= 2) && (recv_buff[0] == '\r') && (recv_buff[1] == '\n')) {
memcpy(recv_buff,&recv_buff[2],recv_size - 2);
recv_size -= 2;
break;
}
// parse line work buffer for keywords ... (e.g.)
content_length = ...;
}
// save payload to file
while (content_length > 0) {
// write out prior payload amount
if (recv_size > 0) {
write(file_fd,recv_buff,recv_size);
content_length -= recv_size;
recv_size = 0;
continue;
}
recv_size = read(socket,recv_buff,response_size);
if (recv_size < 0)
error(strerror(errno));
if (recv_size == 0)
break;
}
UPDATE #2:
Yeah, it hard to make the pseudo code run, and the returned values are all garbled
Okay, here is a soup-to-nuts working version that I've tested against my own http server.
I had to create my own routines for the parts you didn't post (e.g. connect, etc.).
At the core, there might have been a minor tweak to the buffer slide code [it was sliding by an extra 2 bytes in one place], but, otherwise it was pretty close to my previous version
// htprcv/htprcv.c -- HTTP receiver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <error.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
typedef unsigned char byte;
#define HTPSLIDE(_rmlen) \
recv_size = htpslide(recv_buff,recv_size,_rmlen)
#define _dbgprt(_fmt...) \
fprintf(stderr,_fmt)
#if DEBUG || _USE_ZPRT_
#define dbgprt(_lvl,_fmt...) \
do { \
if (dbgok(_lvl)) \
_dbgprt(_fmt); \
} while (0)
#define dbgexec(_lvl,_expr) \
do { \
if (dbgok(_lvl)) \
_expr; \
} while (0)
#else
#define dbgprt(_lvl,_fmt...) \
do { \
} while (0)
#define dbgexec(_lvl,_expr) \
do { \
} while (0)
#endif
#define dbgok(_lvl) \
opt_d[(byte) #_lvl[0]]
byte opt_d[256];
char *opt_o;
#define HEXMAX 16
// htpconn -- do connect to server
int
htpconn(const char *hostname,unsigned short portno)
{
struct addrinfo hints, *res;
struct hostent *hostent;
int ret;
char portstr[20];
int sockfd;
/* Prepare hint (socket address input). */
hostent = gethostbyname(hostname);
if (hostent == NULL)
error(1,errno,"htpconn: gethostbyname -- %s\n",hostname);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
sprintf(portstr, "%d", portno);
getaddrinfo(NULL, portstr, &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0)
error(1,errno,"htpconn: socket\n");
// do the actual connection
ret = connect(sockfd, res->ai_addr, res->ai_addrlen);
if (ret < 0)
error(1,errno,"htprcv: read header\n");
return sockfd;
}
// htpslide -- slide buffer (strip out processed data)
size_t
htpslide(char *recv_buff,size_t recv_size,int slidelen)
{
size_t new_size;
if (slidelen > recv_size)
slidelen = recv_size;
new_size = recv_size - slidelen;
dbgprt(S,"htpslide: slidelen=%d recv_size=%zu new_size=%zu\n",
slidelen,recv_size,new_size);
memcpy(&recv_buff[0],&recv_buff[slidelen],new_size);
return new_size;
}
// _htphex -- dump a line in hex
void
_htphex(unsigned long off,const void *vp,size_t xlen)
{
const byte *bp = vp;
int idx;
int chr;
char hexbuf[200];
char alfbuf[200];
char *hexptr = hexbuf;
char *alfptr = alfbuf;
for (idx = 0; idx < HEXMAX; ++idx) {
chr = bp[idx];
if ((idx % 4) == 0)
*hexptr++ = ' ';
if (idx < xlen) {
hexptr += sprintf(hexptr,"%2.2X",chr);
if ((chr < 0x20) || (chr > 0x7E))
chr = '.';
}
else {
hexptr += sprintf(hexptr," ");
chr = ' ';
}
*alfptr++ = chr;
}
*hexptr = 0;
*alfptr = 0;
_dbgprt(" %8.8lX: %s *%s*\n",off,hexbuf,alfbuf);
}
// htphex -- dump a buffer in hex
void
htphex(const char *buf,size_t buflen,const char *reason)
{
size_t off = 0;
size_t xlen;
if (reason != NULL)
_dbgprt("htphex: DUMP buf=%p buflen=%zu (from %s)\n",
buf,buflen,reason);
for (; buflen > 0; buflen -= xlen, buf += xlen, off += xlen) {
xlen = buflen;
if (xlen > HEXMAX)
xlen = HEXMAX;
_htphex(off,buf,xlen);
}
}
// htpsym -- get symbol/value
int
htpsym(char *linebuf,char *sym,char *val)
{
char *cp;
int match;
dbgprt(H,"htpsym: PARAM linebuf='%s'\n",linebuf);
// FORMAT:
// foo-bar: baz
do {
match = 0;
cp = strchr(linebuf,':');
if (cp == NULL)
break;
*cp++ = 0;
strcpy(sym,linebuf);
for (; (*cp == ' ') || (*cp == '\t'); ++cp);
strcpy(val,cp);
match = 1;
dbgprt(H,"htpsym: SYMBOL sym='%s' val='%s'\n",sym,val);
} while (0);
return match;
}
// htprcv -- receive server response
void
htprcv(int sockfd,int fdout)
{
size_t recv_size = 0;
size_t response_size = 4096;
char *recv_buff = malloc(response_size + 1);
// line oriented header buffer
char *endl = NULL;
size_t linelen;
char linebuf[1000];
ssize_t ret = 0;
off_t content_length = 0;
// read headers
while (1) {
// fill up a chunk of data
while (recv_size < response_size) {
recv_buff[recv_size] = 0;
// do we have a line end?
endl = strstr(recv_buff,"\r\n");
if (endl != NULL)
break;
// read a chunk of data
ret = read(sockfd,&recv_buff[recv_size],response_size - recv_size);
if (ret < 0)
error(1,errno,"htprcv: read header\n");
if (ret == 0)
break;
recv_size += ret;
dbgprt(R,"htprcv: READ ret=%zd\n",ret);
dbgexec(R,htphex(recv_buff,recv_size,"htprcv/READ"));
}
// error -- no line end but short read
if (endl == NULL)
error(1,0,"htprcv: no endl\n");
// copy header to work buffer
linelen = endl - recv_buff;
memcpy(linebuf,recv_buff,linelen);
linebuf[linelen] = 0;
// remove header from receive buffer
linelen += 2;
HTPSLIDE(linelen);
// stop on end of headers (back to back "\r\n")
if ((recv_size >= 2) &&
(recv_buff[0] == '\r') && (recv_buff[1] == '\n')) {
HTPSLIDE(2);
break;
}
// parse line work buffer for keywords ...
char sym[100];
char val[1000];
if (! htpsym(linebuf,sym,val))
continue;
if (strcasecmp(sym,"Content-Length") == 0) {
content_length = atoi(val);
continue;
}
}
// save payload to file
while (content_length > 0) {
// write out prior payload amount
if (recv_size > 0) {
dbgexec(W,htphex(recv_buff,recv_size,"htprcv/WRITE"));
ret = write(fdout,recv_buff,recv_size);
if (ret < 0)
error(1,errno,"htprcv: write body\n");
content_length -= recv_size;
recv_size = 0;
continue;
}
// read in new chunk of payload
ret = read(sockfd,recv_buff,response_size);
if (ret < 0)
error(1,errno,"htprcv: read body\n");
if (ret == 0)
break;
recv_size = ret;
}
free(recv_buff);
}
// htpget -- do initial dialog
void
htpget(int sockfd,const char *hostname,const char *file)
{
char *bp;
char buf[1024];
ssize_t resid;
ssize_t xlen;
size_t off;
bp = buf;
if (file == NULL)
file = "/";
bp += sprintf(bp,"GET %s HTTP/1.1\r\n",file);
if (hostname == NULL)
hostname = "localhost";
bp += sprintf(bp,"Host: %s\r\n",hostname);
if (0) {
bp += sprintf(bp,"User-Agent: %s\r\n","curl/7.61.1");
}
else {
bp += sprintf(bp,"User-Agent: %s\r\n","htprcv");
}
bp += sprintf(bp,"Accept: */*\r\n");
bp += sprintf(bp,"\r\n");
resid = bp - buf;
off = 0;
for (; resid > 0; resid -= xlen, off += xlen) {
xlen = write(sockfd,buf,resid);
if (xlen < 0)
error(1,errno,"htpget: write error\n");
}
}
// main -- main program
int
main(int argc,char **argv)
{
char *cp;
char *portstr;
unsigned short portno;
int sockfd;
int filefd;
char url[1000];
--argc;
++argv;
//setlinebuf(stdout);
setlinebuf(stderr);
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch(cp[-1]) {
case 'd': // debug options
if (*cp == 0)
cp = "SHRW";
for (; *cp != 0; ++cp)
opt_d[(byte) *cp] = 1;
break;
case 'o': // output file
opt_o = cp;
break;
}
}
// get the remote host:port
do {
if (argc <= 0) {
strcpy(url,"localhost:80");
break;
}
strcpy(url,*argv++);
--argc;
} while (0);
// get remote port number
portstr = strchr(url,':');
if (portstr != NULL)
*portstr++ = 0;
else
portstr = "80";
portno = atoi(portstr);
// open the output file (or send to stdout)
do {
if (opt_o == NULL) {
filefd = 1;
break;
}
filefd = open(opt_o,O_WRONLY | O_CREAT,0644);
if (filefd < 0)
filefd = 1;
} while (0);
// establish connection
sockfd = htpconn(url,portno);
// send the file request
htpget(sockfd,NULL,"/");
// receive the server response
htprcv(sockfd,filefd);
close(sockfd);
return 0;
}
I am trying to build a chat application between the server and the client. My doubt is for sending information from the client or from the server I was able to handle the partial send with the help of the loop, but I am unable to find out the length of the send data bytes from the client to the server or from the server to the client, thereby having problem in creating the memory for the received bytes and printing.
My chat function code for the client:
int chat_function(int sockfd)
{
char ch;
char *buf;
char *newp;
int ret_send = 0;
int ret_recv = 0;
int buf_size = 0;
while(1) {
printf("From client, enter the message : ");
buf = (char *)malloc(sizeof(char));
if (buf == NULL)
return -1;
while ((ch = getchar()) != '\n') {
buf[buf_size++] = ch;
newp = (char *)realloc(buf, (buf_size + 1) * sizeof(char));
if ( newp == NULL) {
free(buf);
return -1;
}
buf = newp;
}
buf[buf_size] = '\0';
ret_send = send_all(sockfd, buf, buf_size);
if (ret_send == -1)
error(1, errno, "error in send() function call\n");
memset(buf, 0, buf_size);
ret_recv = recv_all(sockfd, buf, buf_size);
if (ret_recv == -1) {
error(1, errno, "error in recv() function call\n");
} else if (ret_recv == -2) {
printf("Oops the server has closed the connection\n");
free(buf);
break;
}
printf("From Server : %s", buf);
if ((strncmp(buf, "exit", 4)) == 0) {
printf("Client Exit...\n");
free(buf);
break;
}
free(buf);
}
}
For handling partial send:
int send_all(int sockfd, char *buf, int buf_size)
{
int bytes_left = 0;
size_t send_bytes = 0;
bytes_left = buf_size
while (1) {
send_bytes = send(fd, buf, bytes_left, 0);
if (send_bytes == -1)
return -1;
buf = buf + send_bytes;
bytes_left = bytes_left - send_bytes;
if (bytes_left == 0)
break;
}
return 0;
}
TCP is a stream protocol, meaning there are no message boundaries: it is just a full-duplex (meaning data flows in both directions at the same time, as if there were two separate lanes) more or less continuous stream of data.
UDP is a datagram protocol, and does have message boundaries. There is an ioctl (FIONREAD/SIOCINQ) that provides the length of the next datagram, but because it involves a syscall, doing that for every message you receive is going to be slow and inefficient. Instead, you normally use a buffer large enough to hold the largest acceptable message, and copy it if/when necessary. However, UDP also has no reliability guarantees, and often UDP datagrams are completely lost without any trace or discernible reason; that's just what happens.
For a chat client-server connection, you'll want to use TCP.
Since the underlying connection is just a stream of data, you need to design a protocol for the communications, so that the stream can be split into messages, with each message processed separately.
The simplest case would be to use the nul character, \0, as a message separator.
The "send" function would then look something like this:
/* Returns 0 if message successfully sent,
nonzero errno code otherwise. */
int send_message(int descriptor, const char *message)
{
/* If message is NULL, we cannot use strlen(); use zero for that. */
const size_t message_len = (message) ? strlen(message) : 0;
/* Temporary variables for the sending part. */
const char *ptr = message;
const char *const end = message + message_len + 1; /* Include '\0' at end */
ssize_t bytes;
/* Check valid descriptor and message length. */
if (descriptor == -1 || message_len < 1)
return errno = EINVAL;
/* Write loop for sending the entire message. */
while (ptr < end) {
bytes = write(descriptor, ptr, (size_t)(end - ptr));
if (bytes > 0) {
ptr += bytes;
} else
if (bytes != -1) {
/* This should never happen. */
return errno = EIO;
} else
if (errno != EINTR) {
/* We do not consider EINTR an actual error; others we do. */
return errno;
}
}
return 0;
}
The above send_message() function writes the specified string, including the string terminating nul character \0, to the specified descriptor.
On the read end, we need a buffer large enough to hold at least one full message. Instead of always waiting for incoming data, we need to check if the buffer already contains a full message, and if it does, return that. Also, you do not necessarily want to always wait for an incoming message, because that would mean you cannot send two messages in a row.
So, here's my suggestion:
static int incoming_desc = -1;
static char *incoming_data = NULL;
static size_t incoming_size = 0;
static char *incoming_next = NULL; /* First received but not handled */
static char *incoming_ends = NULL; /* Last received but not handled */
#define INCOMING_CHUNK 4096
/* Receive a new message into dynamically allocated buffer,
and return the length. Returns 0 when no message, with errno set.
Waits at most ms milliseconds for a new message to arrive.
errno == EAGAIN: no message, timeout elapsed.
errno == ECONNABORTED: other end closed the connection.
*/
size_t get_message(char **message, size_t *size, long ms)
{
struct timeval timeout;
/* Make sure the parameters are sane. */
if (!message || !size || ms < 0) {
errno = EINVAL;
return 0;
}
/* For this function to work like getline() and getdelim() do,
we need to treat *message as NULL if *size == 0. */
if (!*size)
*message = NULL;
timeout.tv_sec = ms / 1000;
timeout.tv_usec = (ms % 1000) * 1000;
/* Timeout loop. */
while (1) {
fd_set readfds;
ssize_t bytes;
size_t used;
int result;
/* Is there a pending complete message in the buffer? */
if (incoming_ends > incoming_next) {
char *endmark = memchr(incoming_next, '\0', (size_t)(incoming_ends - incoming_next));
if (endmark) {
const size_t len = (size_t)(endmark - incoming_next) + 1;
/* Reallocate the message buffer, if necessary. */
if (len > *size) {
char *temp = realloc(*message, len);
if (!temp) {
errno = ENOMEM;
return 0;
}
*message = temp;
*size = len;
}
/* Copy message, */
memcpy(*message, incoming_next, len);
/* and remove it from the buffer. */
incoming_next += len;
/* In case the other end sent just the separator, clear errno. */
errno = 0;
/* We return the length sans the separator. */
return len - 1;
}
}
/* Do we have time left to check for input? */
if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0)
break; /* Nope. */
/* Is incoming_desc one we can select() for? */
if (incoming_desc < 0 || incoming_desc >= FD_SETSIZE)
break; /* Nope. */
FD_ZERO(&readfds);
FD_SET(incoming_desc, &readfds);
result = select(incoming_desc + 1, &readfds, NULL, NULL, &timeout);
if (result < 1)
break; /* Nothing interesting happened (we ignore error here). */
if (!FD_ISSET(incoming_fd, &readfds))
break;
/* Number of bytes used in the buffer right now. */
used = (size_t)(incoming_ends - incoming_data);
/* Do we have at least INCOMING_CHUNK bytes available? */
if (used + INCOMING_CHUNK >= incoming_size) {
/* Nope. Repack the incoming buffer first. */
if (incoming_next > incoming_data) {
const size_t len = (size_t)(incoming_ends - incoming_next);
if (len > 0)
memmove(incoming_data, incoming_next, len);
incoming_next = incoming_data;
incoming_ends = incoming_data + len;
}
/* Recalculate the number of bytes we have free now. Enough? */
used = (size_t)(incoming_ends - incoming_data);
if (used + INCOMING_CHUNK > incoming_size) {
/* Grow incoming buffer. */
const size_t newsize = used + INCOMING_CHUNK;
char *temp = realloc(incoming_data, newsize);
if (!temp) {
errno = ENOMEM;
return 0;
}
incoming_next = temp + (size_t)(incoming_next - incoming_data);
incoming_ends = temp + used;
incoming_data = temp;
incoming_size = newsize;
}
}
/* Read more data into the buffer; up to a full buffer. */
bytes = read(incoming_fd, incoming_ends, incoming_size - used);
if (bytes > 0) {
incoming_ends += bytes;
} else
if (bytes == 0) {
/* Other end closed the connection. We may have a partial message
in the buffer, and should handle that too, but for now, we
just error out. */
errno = ECONNABORTED;
return 0;
} else
if (bytes != -1) {
/* Should never happen. */
errno = EIO;
return 0;
} else
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
/* No data yet, interrupted by signal delivery, etc. */
continue;
} else {
/* errno is set to indicate which error happened. */
return 0;
}
}
/* Timeout. */
errno = EAGAIN;
return 0;
}
Note that get_message() works like getline(): you do e.g.
char *msg = NULL;
size_t size = 0;
size_t len;
len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */
if (len) {
/* msg contains a full message of len characters */
} else
if (errno == ECONNABORTED) {
/* Other end closed the connection */
} else
if (errno != EAGAIN) {
fprintf(stderr, "Error receiving data: %s.\n", strerror(errno));
}
Then, you can reuse the same dynamically allocated buffer by just calling e.g.
len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */
again.
There is no such mechanism built into TCP or UDP. You need to implement your own protocol on top of it. One of the possible solutions is:
If the content delivered is static.
If the sending end knows the size of the data that is being delivered prior, your client and server can agree on specific terms. For example, the first four bytes sent by the server is the size of the remaining message represented in network byte order.
Server code
uint32_t n_size = htonl(size); // Convert the data size into network byte order.
write(sockfd, &n_size, sizeof(n_size)); // Send to the client.
Client code
uint32_t n_size;
int n_read = 0;
for ( ; ; ) {
int rd_status = read(sockfd, (void*) &n_size + n_read, sizeof(n_size) - n_read);
if (rd_status <= 0)
goto handle_this_case;
n_read = n_read + rd_status;
if (n_read == sizeof(n_size))
break;
}
uint32_t size = ntohl(n_size);
If the content delivered is generated on the fly.
In this case, even the server is not aware of the size of the message. You need to build your functions for handling this case. Below I have shown a bare minimal implementation:
Client-Side:
struct data_unit
{
void* data;
int size;
};
struct data_storage
{
struct data_unit unit;
struct data_storage* next;
};
void append_data(struct data_storage* storage, struct data_unit* unit);
struct data_unit* dump_data(struct data_storage* storage);
int main()
{
struct data_storage storage;
struct data_unit unit;
unit.data = malloc(MAX_SIZE);
for ( ; ; ) {
int rd_status = read(sockfd, unit.data, MAX_SIZE);
if (rd_status < 0)
goto handle_this_case;
else if (rd_status == 0)
break;
unit.size = rd_status;
append_data(&storage, &unit);
}
struct data_unit* t_data = dump_data(&storage);
}
I am new to DPDK and trying to create a packet to send it from one DPDK enabled machine to another connected directly via an ethernet. I modified an example/rxtx_callbacks/main.c provided with DPDK at both side. However, I am not receiving anything at the receiver. What wrong am I doing?
Modified function at transmitter: lcore_main is modified:
static __attribute__((noreturn)) void lcore_main()
{
uint16_t port;
struct ether_hdr *eth_hdr;
struct ether_addr daddr;
daddr.addr_bytes[0] = 116;
daddr.addr_bytes[1] = 225;
daddr.addr_bytes[2] = 228;
daddr.addr_bytes[3] = 204;
daddr.addr_bytes[4] = 106;
daddr.addr_bytes[5] = 82;
//rte_eth_macaddr_get(portid, &addr);
struct ipv4_hdr *ipv4_hdr;
int32_t i;
int ret;
RTE_ETH_FOREACH_DEV(port)
if (rte_eth_dev_socket_id(port) > 0 &&
rte_eth_dev_socket_id(port) !=
(int)rte_socket_id())
printf("WARNING, port %u is on remote NUMA node to "
"polling thread.\n\tPerformance will "
"not be optimal.\n", port);
printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
rte_lcore_id());
//struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool);
struct rte_mbuf *m_head[BURST_SIZE];
for (;;) {
RTE_ETH_FOREACH_DEV(port) {
if(rte_pktmbuf_alloc_bulk(mbuf_pool, m_head, BURST_SIZE)!=0)
{
printf("Allocation problem\n");
}
for(i = 0; i < BURST_SIZE; i++) {
eth_hdr = rte_pktmbuf_mtod(m_head[i], struct ether_hdr *);
//eth_hdr = (struct ether_hdr *)rte_pktmbuf_append(m_head[i],
// sizeof(struct ether_hdr));
eth_hdr->ether_type = htons(ETHER_TYPE_IPv4);
rte_memcpy(&(eth_hdr->s_addr), &addr, sizeof(struct ether_addr));
rte_memcpy(&(eth_hdr->d_addr), &daddr, sizeof(struct ether_addr));
}
const uint16_t nb_tx = rte_eth_tx_burst(port, 0, m_head, BURST_SIZE);
if (unlikely(nb_tx < BURST_SIZE)) {
uint16_t buf;
for (buf = nb_tx; buf < BURST_SIZE; buf++)
rte_pktmbuf_free(m_head[buf]);
}
}
}
}
receiver side RTE_ETH_FOREACH_DEV of tx part is modified to:
RTE_ETH_FOREACH_DEV(port) {
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, bufs, BURST_SIZE);
//printf("Number of Packets received %d\n", nb_rx);
for(i = 0; i < nb_rx; i++) {
//ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
// sizeof(struct ether_hdr));
//printf("Packet ip received %d\n", ipv4_hdr->src_addr);
eth_hdr = rte_pktmbuf_mtod(bufs[i], struct ether_hdr *);
printf("Packet ip received %d\n", eth_hdr->ether_type);
}
if (unlikely(nb_rx == 0))
continue;
const uint16_t nb_tx = 0; // = rte_eth_tx_burst(port ^ 1, 0, bufs, nb_rx);
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}
}
Please let me know if I missed something.
There are few issues with the code:
eth_hdr = rte_pktmbuf_mtod(m_head[i], struct ether_hdr *);
Unlike rte_pktmbuf_append(), the rte_pktmbuf_mtod() does not change the packet length, so it should be set manually before the tx.
eth_hdr->ether_type = htons(ETHER_TYPE_IPv4);
If we set ETHER_TYPE_IPv4, a correct IPv4 header must follow. So we need either to add the header or to change the ether_type.
rte_memcpy(&(eth_hdr->s_addr), &addr, sizeof(struct ether_addr));
Where is the source address comes from?
const uint16_t nb_tx = rte_eth_tx_burst(port, 0, m_head, BURST_SIZE);
Looks like we transmit a burst of zero-sized packets with invalid IPv4 headers. Please also make sure the source/destination addresses are correct.
As suggested by #andriy-berestovsky, I used rte_eth_stats_get() and it shows packets are present in ethernet ring via the field ipackets but rte_eth_rx_burst is not returning any packets. Full code is included here, please let me know what I am doing wrong. (I am using testpmd at transmitter side)
#include <stdint.h>
#include <inttypes.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_ip.h>
#include <rte_mbuf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#define MAX_SOURCE_SIZE (0x100000)
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = ETHER_MAX_LEN,
},
};
static struct {
uint64_t total_cycles;
uint64_t total_pkts;
} latency_numbers;
static volatile bool force_quit;
struct rte_mempool *mbuf_pool;
static void
signal_handler(int signum)
{
struct rte_eth_stats eth_stats;
int i;
if (signum == SIGINT || signum == SIGTERM) {
printf("\n\nSignal %d received, preparing to exit...\n",
signum);
RTE_ETH_FOREACH_DEV(i) {
rte_eth_stats_get(i, ð_stats);
printf("Total number of packets received %llu, dropped rx full %llu and rest= %llu, %llu, %llu\n", eth_stats.ipackets, eth_stats.imissed, eth_stats.ierrors, eth_stats.rx_nombuf, eth_stats.q_ipackets[0]);
}
force_quit = true;
}
}
struct ether_addr addr;
/*
* Initialises a given port using global settings and with the rx buffers
* coming from the mbuf_pool passed as parameter
*/
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
if (!rte_eth_dev_is_valid_port(port))
return -1;
rte_eth_dev_info_get(port, &dev_info);
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
if (retval != 0) {
printf("Error in adjustment\n");
return retval;
}
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0) {
printf("RX queue setup prob\n");
return retval;
}
}
txconf = dev_info.default_txconf;
txconf.offloads = port_conf.txmode.offloads;
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), &txconf);
if (retval < 0)
return retval;
}
retval = rte_eth_dev_start(port);
if (retval < 0) {
printf("Error in start\n");
return retval;
}
rte_eth_macaddr_get(port, &addr);
printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
(unsigned)port,
addr.addr_bytes[0], addr.addr_bytes[1],
addr.addr_bytes[2], addr.addr_bytes[3],
addr.addr_bytes[4], addr.addr_bytes[5]);
rte_eth_promiscuous_enable(port);
return 0;
}
/*
* Main thread that does the work, reading from INPUT_PORT
* and writing to OUTPUT_PORT
*/
static __attribute__((noreturn)) void
lcore_main(void)
{
uint16_t port;
struct ether_hdr *eth_hdr;
//struct ether_addr addr;
//rte_eth_macaddr_get(portid, &addr);
struct ipv4_hdr *ipv4_hdr;
int32_t i;
RTE_ETH_FOREACH_DEV(port)
{
if (rte_eth_dev_socket_id(port) > 0 &&
rte_eth_dev_socket_id(port) !=
(int)rte_socket_id())
printf("WARNING, port %u is on remote NUMA node to "
"polling thread.\n\tPerformance will "
"not be optimal.\n", port);
}
printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
rte_lcore_id());
for (;;) {
RTE_ETH_FOREACH_DEV(port) {
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, 0,bufs, BURST_SIZE);
for(i = 0; i < nb_rx; i++) {
ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *, sizeof(struct ether_hdr));
printf("Packet ip received %d\n", ipv4_hdr->src_addr);
}
if (unlikely(nb_rx == 0))
continue;
const uint16_t nb_tx = 0; // = rte_eth_tx_burst(port ^ 1, 0, bufs, nb_rx);
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}
}
if(force_quit)
break;
}
}
/* Main function, does initialisation and calls the per-lcore functions */
int
main(int argc, char *argv[])
{
uint16_t nb_ports;
uint16_t portid, port;
/* init EAL */
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
argc -= ret;
argv += ret;
force_quit = false;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
nb_ports = rte_eth_dev_count_avail();
printf("size ordered %lld\n", NUM_MBUFS *nb_ports);
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
NUM_MBUFS * nb_ports, MBUF_CACHE_SIZE, 0,
RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (nb_ports < 1)
rte_exit(EXIT_FAILURE, "Error: number of ports must be greater than %d\n", nb_ports);
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
// initialize all ports
RTE_ETH_FOREACH_DEV(portid)
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8"\n",
portid);
if (rte_lcore_count() > 1)
printf("\nWARNING: Too much enabled lcores - "
"App uses only 1 lcore\n");
// call lcore_main on master core only
lcore_main();
return 0;
}
It seems to be a problem of ethernet card with ubuntu 14.04. With ubuntu 16.04 it is working fine.
I've been working from Beejs Network examples, introducing a few customizations. In particular, I'm trying to use a single structure to store the necessary information related to communications/sockets. I think I'm having trouble populating an addrinfo structure and using it with sendto for a UDP socket. Bellow is my code, which compiles fine, but it fails with the message outlined below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Definitions
#define COM_MSG_SIZE 1024
#define COM_HOST_SIZE 128
struct com_socket
{
char *type;
int descriptor;
struct addrinfo addr;
};
void COM_error(char *msg) {
perror(msg);
exit(0);
}
int main()
{
int status;
struct com_socket COM_client;
char addr_str[COM_HOST_SIZE];
// ---------------------------------------------
// Initialize socket
COM_client.type = "UDP";
char *hostname = "192.168.0.110";
char *port_num = "4000";
printf("Creating socket...");
if(strcmp(COM_client.type, "UDP") == 0)
{
COM_client.descriptor = socket(AF_INET, SOCK_DGRAM, 0);
}
// Error check
if(COM_client.descriptor < 0)
{
COM_error(" ERROR opening socket");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Define hints
struct addrinfo hints;
hints.ai_family = AF_INET; // AF_UNSPEC "unspecified" or can use IPv6 = AF_INET6, IPv4 = AF_INET
hints.ai_socktype = SOCK_DGRAM; // Socket type: SOCK_STREAM or SOCK_DGRAM or 0 = auto
hints.ai_flags = AI_CANONNAME;
hints.ai_protocol = 0; // 0 = auto
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_addrlen = 0;
hints.ai_next = NULL;
// Get the linked list of address info
struct addrinfo *host_list;
printf("Building host address list...");
status = getaddrinfo(hostname,port_num,&hints,&host_list);
// returns 0 if succeeds
if (status != 0)
{
COM_error(" ERROR getaddrinfo: %s\n");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Select address
int count = 1;
struct addrinfo *entry;
// Loop through each entry in the "linked list" and pull the necessary one
for (entry = host_list; entry != NULL; entry = entry->ai_next)
{
// Print the list of potential IP addresses
if( NULL == inet_ntop( AF_INET, &((struct sockaddr_in *) entry->ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) )
{
COM_error(" ERROR with inet_ntop\n");
}
printf(" Address entry %d: %s",count,addr_str);
// Update counter
count = count + 1;
// Choose which one to copy
if(strncmp(addr_str,"192.",(size_t) 4) == 0)
{
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf(" <--------- selected* (%s) \n",addr_str);
break;
}
else
{
printf("\n");
}
}
// Clean
freeaddrinfo(host_list);
//-------------------------------------------------------
char *buffer;
char msg[COM_MSG_SIZE];
strncpy(msg,"BEGIN",COM_MSG_SIZE);
printf("ENTER `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
buffer = calloc(COM_MSG_SIZE+1, sizeof(char));
printf("AFTER calloc `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
// Check to see if we were successful
if (buffer == NULL)
{
printf("ERROR Could not allocate required memory\n");
exit(1);
}
// Copy message to buffer
strncpy(buffer,msg,COM_MSG_SIZE);
printf("Message input: %s Message to be sent: %s\n",msg,buffer);
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf("SEND to address (%s) \n",addr_str);
// Send the buffer to the destination address
if(strcmp(COM_client.type, "UDP") == 0)
{
status = sendto(COM_client.descriptor, buffer, strlen(buffer), 0, COM_client.addr.ai_addr, COM_client.addr.ai_addrlen);
// Error check
if (status < 0)
{
COM_error("ERROR could not send message");
}
}
// Free buffer memory
free(buffer);
//---------------------------------------------------------
close(COM_client.descriptor);
return 0;
}
Here is the output showing messages from the print statements as well as the failure
Creating socket... Success
Building host address list... Success
Address entry 1: 192.168.0.110 <--------- selected* (192.168.0.110)
ENTER `COM_msg_send` address length 16
AFTER calloc `COM_msg_send` address length 16
Message input: BEGIN Message to be sent: BEGIN
L1 = 16 L2 = 16
SEND to address (0.0.0.0)
ERROR could not send message: Invalid argument
Showing SEND to address (0.0.0.0), it appears that something is wrong with the address stored in the structure COM_client. Specifically, I believe I'm having trouble with this part
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
As you can see, I've tried various things, all of which fail. I want to continue to use the COM_client structure approach as my intention is to make the code more modular in which I can pass the structure containing all the necessary communication information.
This line
COM_client.addr = *entry;
"tries" to copy a struct addrinfo, which it in fact does, but as it contains pointers and "only" copies the pointers' values. The memory those pointers point to had been allocated by getaddrinfo() and thus will be deallocates by the call to freeaddrinfo() leaving the pointers inside the copy dangle afterwards.
To get around this you need to perform a "deep copy" of a struct addrinfo.
This for example can be done like so:
/* Does a deep copy to where pdst point from where pscr points to.
Returns 0 on success and -1 on error. Sets errno. */
int addrinfo_copy(struct addrinfo * pdst, struct addrinfo * psrc)
{
int result = 0; /* Be optimistic. */
assert(pdst);
assert(psrc);
*pdst = *pscr; /* Copy all. Note that the pointer elements copied
need to be recreated. See below ... */
do
{
pdst->ai_addr = malloc(psrc->ai_addrlen);
if (!pdst->ai_addr)
{
result = -1;
break;
}
memcpy(pdst->ai_addr, psrc->ai_addr, psrc->ai_addrlen);
pdst->ai_canonname = strdup(psrc->ai_canonname); /* Assumes POSIX. */
if (!pdst->ai_canonname)
{
result = -1;
break;
}
} while (0);
return result;
}
To get rid of such a copy you need something like this:
/* Deallocates and sets to a 0/NULL what had been created by
addrinfo_copy(). */
void addrinfo_free(struct addrinfo * p)
{
assert(p);
free(p->ai_addr);
free(p->canonname);
memset(p, 0, sizeof *p); /* In fact not necessary. */
}
Use it like this:
struct addrinfo * entry, * entry_copy;
/* Set entry to something returned by getaddrinfo (). */
...
if (-1 = addrinfo_copy(entry_copy, entry))
{
perror("addrinfo_copy() failed"):
exit(EXIT_FAILURE);
}
/* Deallocate all results returned by getaddrinfo(). */
freeaddrinfo(...);
/* Still use entry_copy here. */
...
/* Clean up. */
addrinfo_free(entry_copy);
As a final note:
If when doing C you observe obscure sudden/unexpected changes in memory content this all most ever dues to having messed up memory management by writing and/or reading to "wrong" memory. This some times happened way long before those changes in memory become obvious and/or in code (seemingly) completely unrelated to where you observe such changes in memory.
This question already has answers here:
C comparing char to "\n" warning: comparison between pointer and integer
(2 answers)
Closed 4 years ago.
I'm currently working with sockets in C, and it seems we can't get the output to buffer correctly. How it works is that a client sends a string to a server in pieces. The server waits until it finds a newline character in the string. Once it has its string, it replaces the newline character with a null terminator, then prints the buffer. The program moves the extra data to the front of the buffer and repeats the process.
Unfortunately, my program does not want to replace the newline with the null terminator.
All attempts to test the code end with the string being outputed in pieces
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#ifndef PORT
#define PORT 30000
#endif
int setup(void) {
int on = 1, status;
struct sockaddr_in self;
int listenfd;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
// Make sure we can reuse the port immediately after the
// server terminates.
status = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
(const char *) &on, sizeof(on));
if(status == -1) {
perror("setsockopt -- REUSEADDR");
}
self.sin_family = AF_INET;
self.sin_addr.s_addr = INADDR_ANY;
self.sin_port = htons(PORT);
memset(&self.sin_zero, 0, sizeof(self.sin_zero)); // Initialize sin_zero to 0
printf("Listening on %d\n", PORT);
if (bind(listenfd, (struct sockaddr *)&self, sizeof(self)) == -1) {
perror("bind"); // probably means port is in use
exit(1);
}
if (listen(listenfd, 5) == -1) {
perror("listen");
exit(1);
}
return listenfd;
}
/*
* Search the first inbuf characters of buf for a network newline ("\r\n").
* Return the location of the '\r' if the network newline is found,
* or -1 otherwise.
* Definitely do not use strchr or any other string function in here. (Why not?)
*/
int find_network_newline(const char *buf, int inbuf) {
int i = 0;
int found = 0;
int location = 0;
while(i < inbuf && found == 0){
if(buf[i] == "\r"){
location = i;
found = 1;
}
i++;
}
if(found = 1){
return location;
}
return -1; // return the location of '\r' if found
}
int main(void) {
int listenfd;
int fd, nbytes;
char buf[30];
int inbuf; // how many bytes currently in buffer?
int room; // how much room left in buffer?
char *after; // pointer to position after the (valid) data in buf
int where; // location of network newline
struct sockaddr_in peer;
socklen_t socklen;
listenfd = setup();
while (1) {
socklen = sizeof(peer);
// Note that we're passing in valid pointers for the second and third
// arguments to accept here, so we can actually store and use client
// information.
if ((fd = accept(listenfd, (struct sockaddr *)&peer, &socklen)) < 0) {
perror("accept");
} else {
printf("New connection on port %d\n", ntohs(peer.sin_port));
// Receive messages
inbuf = 0; // buffer is empty; has no bytes
room = sizeof(buf); // room == capacity of the whole buffer
after = buf; // start writing at beginning of buf
while ((nbytes = read(fd, after, room)) > 0) {
// Step 2: update inbuf (how many bytes were just added?)
inbuf = inbuf + nbytes;
// Step 3: call find_network_newline, store result in variable "where"
where = find_network_newline(buf, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 4: output the full line, not including the "\r\n",
// using print statement below.
// Be sure to put a '\0' in the correct place first;
// otherwise you'll get junk in the output.
// (Replace the "\r\n" with appropriate characters so the
// message prints correctly to stdout.)
buf[where] = "\0";
printf("Next message: %s", buf);
// Note that we could have also used write to avoid having to
// put the '\0' in the buffer. Try using write later!
// Step 5: update inbuf and remove the full line from the buffer
// There might be stuff after the line, so don't just do inbuf = 0
// You want to move the stuff after the full line to the beginning
// of the buffer. A loop can do it, or you can use memmove.
// memmove(destination, source, number_of_bytes)
after += where;
memmove(buf, after, 30);
inbuf = strlen(buf);
}
// Step 6: update room and after, in preparation for the next read
room = 30 - inbuf;
after = buf;
}
close(fd);
}
}
return 0;
}
it is because you are comparing buf[i] to a string not to characther.
if(buf[i] == "\r")
should be
if(buf[i] == '\r')
and
buf[where] = "\0";
should be
buf[where] = '\0';
Try using '\0' instead of "\0", i think it should work.