Unsigned int appears as random negative integer after sending with UDP - c

I send a struct with one field (unsigned int) to a network emulator and it outputs the unsigned int but it is incorrect.
If I do NOT re-run the network emulator, if I resend the struct with the same number, it will be the same random integer. If I send that number +1, I get the random integer -1.
If i do re-run the network emulator and resend the struct with the same number, it shows a different random integer.
This is the stuct:
struct pkt_INIT {
unsigned int router_id;
};
This is the code to send that struct:
int sock;
struct sockaddr_in server_addr;
struct hostent *host;
char send_data[1024];
host= (struct hostent *) gethostbyname(emulator_host);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(emulator_port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(server_addr.sin_zero),8);
struct pkt_INIT init;
init.router_id = router_id;
strcpy(send_data,(char *)&init);
sendto(sock, send_data, strlen(send_data), 0,
(struct sockaddr *)&server_addr, sizeof(struct sockaddr));
I don't have any of the code for the network emulator.

Don't use strcpy() to put a binary struct into a binary array. Likewise, do not use strlen() to get the length of a struct. Both functions are designed for strings, and will copy/read bytes until they encounter a null byte. Use memcpy() and sizeof() instead:
memcpy(send_data, &init, sizeof(init));
sendto(sock, send_data, sizeof(init), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
In which case, just get rid of the array and pass the struct directly to sendto() instead:
sendto(sock, (char*)&init, sizeof(init), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
Now, with that said, there is an issue with the router_id. Most network protocols require network byte ordering, so you may or may not need to use htonl(), depending on what the emulator is actually expecting:
init.router_id = htonl(router_id);

You are using: strcpy(send_data,(char *)&init);
Is this really what you intended to do? You are copying a struct variable to an array of chars. Assuming you want just to do byte-by-byte copy this way, there are still problems. init won't have NULL termination. So subsequent call to strlen() is dubious.
update:
Just store -1 in the char array and convert it as integer at the receiver side.
send_data[0]=255; //To store -1
send_data[1]='\0';
Receiver side:
int i;
i = recv_data[0];
'i' will have -1.

Related

C - Create a sockaddr struct

I am trying to create a void mksockaddr(int af, int proto, char addr[], struct sockaddr* dst) that creates a sockaddr structure, here's what I've done:
void sockaddr(int af, int port, char addr[], struct sockaddr* dst) {
if (af == AF_INET) {
struct sockaddr_in s;
s.sin_family = af;
s.sin_port = htons(port);
inet_pton(af, addr, &s.sin_addr);
memcpy(dst, &s, sizeof(s));
} else {
struct sockaddr_in6 s;
s.sin6_family = af;
s.sin6_port = htons(port);
s.sin6_flowinfo = 0;
inet_pton(af, addr, &s.sin6_addr);
memcpy(dst, &s, sizeof(s));
}
}
This seems to be no problem with AF_INET (IPv4), I can bind() without any problem, but when I try to use AF_INET6, bind() give me Invalid argument.
Here's the code I use to bind():
int sock_fd = socket(AF_INET6, SOCK_RAW, proto);
struct sockaddr sin;
sockaddr(AF_INET6, proto, src, &sin);
if(bind(sock_fd, &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket: %s\n", strerror(errno));
exit(1);
} // got Invalid argument
However, I can bind() just fine if I construct a sockaddr_in6 myself:
struct sockaddr_in6 sin;
sin.sin6_port = htons(proto);
sin.sin6_family = AF_INET6;
inet_pton(AF_INET6, src, &sin.sin6_addr);
if(bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket.\n");
exit(1);
} // work just fine
So I cast the sockaddr created by the function back to sockaddr_in6, and I can see that all the fields are same except sin6_scope_id. To my understanding, sin6_scope_id does not matter unless I'm dealing with a link-local IPv6 address.
Am I missing anything here?
From a C perspective, for your code to be certain to work as intended, the caller must pass a valid pointer to the correct structure type in the dst argument. Your example does not do this. Instead, it declares a struct sockaddr, and passes a pointer to that. Type struct sockaddr itself is never meant to be used as the type of an actual object, and it is not large enough for all possible address types. In particular, it is not large enough for an IPv6 address.
On the other hand, POSIX plays a bit more fast and loose than standard C requires for conforming programs. This is especially evident with socket addresses. It defines a type struct sockaddr_storage to serve exactly your purpose: it is large enough and has appropriate alignment to hold the data of any supported socket address type. The docs specifically mention its use in generically supporting both IPv4 and IPv6. POSIX also sanctions casting among different socket address pointer types, although this leads to violations of C's struct aliasing rule.
Thus, I would rewrite your function to use struct sockaddr_storage explicitly, and I would furthermore simplify my code via appropriate casts. Moreover, I would have my function tell me the usable size of the address structure, which encompasses only that portion that is initialized:
void populate_sockaddr(int af, int port, char addr[],
struct sockaddr_storage *dst, socklent_t *addrlen) {
if (af == AF_INET) {
struct sockaddr_in *dst_in4 = (struct sockaddr_in *) dst;
*addrlen = sizeof(*dst_in4);
memset(dst_in4, 0, *addrlen);
dst_in4->sin_family = af;
dst_in4->sin_port = htons(port);
inet_pton(af, addr, &dst_in4->sin_addr);
} else if (af == AF_INET6) {
struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *) dst;
*addrlen = sizeof(*dst_in6);
memset(dst_in6, 0, *addrlen);
dst_in6->sin6_family = af;
dst_in6->sin6_port = htons(port);
// unnecessary because of the memset(): dst_in6->sin6_flowinfo = 0;
inet_pton(af, addr, &dst_in6->sin6_addr);
} // else ...
}
You would then use it like so:
struct sockaddr_strorage addr;
socklen_t addrlen;
populate_sockaddr(af, port, src, &addr, &addrlen);
if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
// ...
}
Note that the cast of &addr to type struct sockaddr * is utterly routine.

IP as number without usual functions?

Context
I would like to know if we can extract the ip information without having to use gethostinfo or getnameinfo etc...
Part of my code
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept( fd , &in_addr , &in_len );
Question
I would like to get the client/incoming ip as a number without the usual formatting (eg 3232238637 instead of 192.168.12.45). Is it even possible ?
The source of those functions is obscure as one would wish, so I cannot figure out.
I read that in_addr (sockaddr) could have the information I need. There is no particular reason but discovering the "inners" of those functions and structures.
Thanks !
For IPv4:
struct sockaddr_in in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin_addr.s_addr contains the client's IP address
// as a 4-byte integer, in network byte order...
For IPv6:
struct sockaddr_in6 in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin6_addr.s6_addr contains the client's IP address
// as a 16-byte array...
If your question is how to "convert" from the binary representation of an IP address to a string "dotted-decimal" representation, rather than use one of the standard functions, then here is an example of how to do it for an IPV4 address ... if not, please clarify your question:
#include <stdio.h>
#include <netdb.h>
int main()
{
struct hostent *he;
long unsigned nbo;
he = gethostbyname("localhost");
nbo = htonl(*((int*)he->h_addr_list[0]));
printf("h_addr: %lx netbyteorder: %lx = %d.%d.%d.%d\n",
*((int*)he->h_addr_list[0]), nbo,
(nbo&0xFF000000)>>24, (nbo&0x00FF0000)>>16, (nbo&0x0000FF00)>>8,
(nbo&0x000000FF) );
}

Segfault when assigning AF_INET to the sin_family attribute of a sockaddr_in structure

Here's my problem:
I'm trying to fill a socket address struct with the appropriate information so that I can use it in a program the handles communication between a server and a client. This is part of the server code. The problem is that it segfaults. When I run gdb, it says that the seg fault occurs when I assign AF_INET to the sin_family attribute for the servaddr struct.
code:
servaddr->sin_family = (short)(AF_INET);
I can't seem to figure out why this occurs.
Here's the full code:
// Function Prototypes
struct sockaddr_in* getServerInfo(char[]);
int main()
{
char hostname[MAXHOSTNAMELEN];
struct sockaddr_in* servaddr = getServerInfo(hostname);
return 0;
} // End main
struct sockaddr_in* getServerInfo(char hostname[])
{
struct sockaddr_in* servaddr = malloc((size_t)sizeof(struct sockaddr_in));
gethostname(hostname, 32);
struct hostent *hostptr;
hostptr = gethostbyname(hostname);
memset((void *) &servaddr, 0, (size_t)sizeof(servaddr));
servaddr->sin_family = (short)(AF_INET);
memcpy((void *)& servaddr->sin_addr, (void *) hostptr->h_addr, hostptr->h_length);
servaddr->sin_port = htons((u_short)8000);
return servaddr;
}
Your bug is here:
memset((void *) &servaddr, 0, (size_t)sizeof(servaddr));
Do this instead:
memset((void *) servaddr, 0, (size_t)sizeof(*servaddr));
Otherwise you're zeroing the pointer for servaddr, (i.e. turning it into NULL). This then explodes when you try and use it.
Similarly you'll need to change your memcpy call.

Is it possible to cast struct to another?

Any one could describe how (struct sockaddr *)&server works here? Is it possible to cast bigger struct to smaller struct?
See these structs:
// IPv4 AF_INET sockets:
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
This is the main program:
int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
server.sin_addr.s_addr = inet_addr("74.125.235.20");
server.sin_family = AF_INET;
server.sin_port = htons( 80 );
//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
{
puts("connect error");
return 1;
}
puts("Connected");
return 0;
}
This is refered as Type Punning. Here, both structures have the same size, so there is no question of struct size. Although you can cast almost anything to anything, doing it with structures is error-prone.
This is C's form of "inheritance" (notice the quotes). This works because C does not care about the underlying data in an address, just what you represent it as.
The function determines what structure it actually is by using the sa_family field, and casting it into the proper sockaddr_in inside the function.
You can cast sockaddr_in to sockaddr, but you cannot usually cast ANY struct to ANY other and assume that things will work properly.
In C, it's possible to cast anything to anything. You could even omit the cast to (struct sockaddr*), and probably just get a compiler warning.

Send string with binary null '\0'

Hi I have programmed linux daemon who sends files in udp packets.
problem is that in string "abc\0asdf" it sends only abc not null character and asdf (all characters after null symbol),
there is udp client code, which send packets:
int sock;
struct sockaddr_in server_addr;
struct hostent *host;
host= (struct hostent *) gethostbyname((char *)ip);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
memset(server_addr.sin_zero,0,8);
and code which send buffer:
if (sendto(sock, buf, sizeof buf, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1)
in serverside I need to receive binary buffer:
defining socket code:
int sock;
int addr_len, bytes_read;
struct sockaddr_in server_addr , client_addr;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
//bzero(&(server_addr.sin_zero),8);
memset(server_addr.sin_zero,0,8);
if (bind(sock,(struct sockaddr *)&server_addr,
sizeof(struct sockaddr)) == -1){
perror("Bind");
exit(1);
}
addr_len = sizeof(struct sockaddr);
diep("sendto()");
and receive buffer (in big loop):
bytes_read = recvfrom(sock,buf,sizeof (buf),0,
(struct sockaddr *)&client_addr, &addr_len);
does anyone know why I didn't receive full buffer?
By looking at the comments, the error is most likely that you treat the received buffer as a string.
If you want to print/output the buffer, you need to convert the null character into something else first.
You should use a for loop to print your received buffer instead of printf:
for (int i=0; i<bytes_read; i++)
printf("%c",buf[i]);
This is incorrect (formatting changed so it fits on a screen for me):
if (sendto(sock,
buf,
sizeof buf,
0,
(struct sockaddr *)&server_addr,
sizeof(struct sockaddr))==-1)
You want sizeof(server_addr) as the length. This will be larger than sizeof(struct sockaddr).
Also, from the manpage:
Return Value
On success, these calls return the number of characters sent. On error, -1 is returned, and errno is set appropriately.
You haven't accounted for the case where it returns some value less than sizeof(buf). Not sure how that can happen but it seems to be something to handle.
My comment on the overall approach is similar to what #jgauffin says. buf is just bytes. It's only a convention for C strings that '\0' terminates them, not a requirement. Typically when using binary byte buffers you also track the size. You're just assuming that all of sizeof(buf) will be used which doesn't make sense. (Suggestion: Perhaps part of your sendto payload should include the size of the message that follows?)

Resources