First DGRAM Has No IP - c

I have an odd problem with a UDP server I'm working on. The very first udp packet received has no information on the source of the packet. Subsequent udp packets all appear to be fine and correctly display the ip address from which the packet was received. I have no clue what is causing this behavior, probably some stupid mistake, or some obscure bug. I'm using on a Linux machine running Debian.
fd_set master;
fd_set read_fds;
int fdmax;
int i;
int bytes_sent;
int bytes_recv;
socklen_t addr_len;
struct sockaddr_storage their_addr;
// provides users information needed to connect
serv_info *server_info;
server_info = (serv_info*) serv_config;
// Create UDP listener socket
int info_sock = createDGRAMSocket(NULL, server_info->port, 1);
char buffer[1024];
int len;
int send_response;
FD_SET(info_sock, &master);
fdmax = info_sock;
bytes_recv = recvfrom(i, buffer, sizeof(buffer), 0, (struct sockaddr *)&their_addr, &addr_len);
printf("Info started \n");
while (running) {
read_fds = master;
select(fdmax+1, &read_fds, NULL, NULL, NULL);
for (i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) {
bytes_recv = recvfrom(i, buffer, sizeof(buffer), 0, (struct sockaddr *)&their_addr, &addr_len);
printf("length %u: %s\n", bytes_recv, buffer);
send_response = 0;
switch (buffer[0]) {
// Handle different packet types
}
struct sockaddr_in *sin = (struct sockaddr_in *)&their_addr;
unsigned char *ip = (unsigned char *)&sin->sin_addr.s_addr;
printf("IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
if (send_response) {
bytes_sent = sendto(info_sock, buffer, len, 0, (struct sockaddr *)&their_addr, sizeof(struct sockaddr_storage));
if (bytes_sent < 0) {
printf("[ERROR] Packet Send Failed %d (%s) %d\n", bytes_sent, buffer, len);
}
}
}
}
};
close(info_sock);

You need to initialize addr_len to sizeof(their_addr). According to the man page:
The argument addrlen is a value-result argument, which the caller should initialize
before the call to the size of the buffer associated with src_addr, and
modified on return to indicate the actual size of the source address. The returned
address is truncated if the buffer provided is too small; in this case, addrlen will
return a value greater than was supplied to the call.
Since you aren't initializing addr_len it seems to be taking on a value of 0 (this is highly undefined behavior). In this case, recvfrom() will not fill in the their_addr buffer, but as the man page indicates addr_len will return a value greater than was supplied to the call. So after the first call addr_len is taking on a value that is allowing the next calls to recvfrom() to properly fill in the their_addr buffer. Relying on this is unsafe though.

Related

UNIX domain datagram socket blocks on recvfrom() with select()

I am using UNIX domain datagram sockets to send records from multiple clients to a single server in a multithreaded program. Everything is done within one process; I'm sending records from multiple threads to a single thread that acts as the server. All threads are assigned to separate cores using their affinity masks.
My problem is when I use select() to retrieve records from client sockets that have records in the socket buffer. I am using the same basic setup I used with a single client socket (and it worked in that context), but now it hangs (apparently it blocks) when I call recvfrom. That's surprising because the select() function has already identified the socket as available for reading.
int select_clientsockets(int64_t srvrfd, int64_t * claddr, int fds_array[], int fd_count, void * recvbuf){
int fds_ready;
int abc;
int64_t cli_addr;
FD_ZERO(&fdset);
FD_SET(0,&fdset);
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
fds_ready = select(3, &fdset, NULL, NULL, 0);
for (int i = 0; i < fd_count; i++){
fds_array[i] = 0;
if (FD_ISSET(i, &fdset)) {
fds_array[i] = 1;
cli_addr = claddr[i];
server_receive(srvrfd, recvbuf, 720, cli_addr);}
}
return 0;
}
The select function calls server_receive on clients where select says data are available:
int64_t server_receive(int64_t sfd, void * buf, int64_t msgLen, int64_t claddr)
{
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
int numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) claddr, len);
if (numBytes == -1)
return 0;
return numBytes;
}
The client socket address is taken from the 3-element array "claddr" (for 3 client sockets) where the corresponding position for each client socket is filled in when the socket is created. At socket creation I also call FD_SET to set the client address into the fd_set. I think I should get the client socket address from fd_set instead, BUT they're both the same pointer value so I don't know why that would make a difference. For internet domain datagram sockets we can use getpeername() but I don't know if there is an analogous function for UNIX domain sockets -- or even if that's the problem.
Thanks very much for any help with this.
UPDATE:
Client fds are added to the global fdset struct on socket creation:
int64_t * create_socket_client(struct sockaddr_un claddr, int64_t retvals[])
{
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
retvals[0] = 0;
retvals[1] = 0;
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1)
return retvals;
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/ud_ucase_cl.%ld", (long) getpid());
FD_SET(sfd,&fdset);
retvals[0] = sfd;
retvals[1] = (int64_t)&claddr;
return retvals;
}
FD_ZERO(&fdset);
FD_SET(0,&fdset);
socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
fds_ready = select(3, &fdset, NULL, NULL, 0);
for (int i = 0; i < fd_count; i++){
fds_array[i] = 0;
if (FD_ISSET(i, &fdset)) {
Your code empties fdset then adds only 0 to fdset. So when you call select and pass it fdset, you are asking it only to check socket 0 for readiness.
You later check if sockets 0 to one less than fd_count are in fdset, but only zero could possibly be because it's the only one you asked about.
Where is the list of sockets you want to check for readiness?

Why does recvfrom() for a UNIX domain datagram socket return Invalid Argument?

I'm working on a program that uses UNIX domain datagram sockes for IPC. Half the time it works fine, but the other half it returns "Invalid argument". I've confirmed that this shouldn't be due to a socket path reuse error, SO_REUSEADDR is set on the socket and the error occurs even when any residual file in the socket path has been deleted. Why does this happen? Here's the code:
Server
/* receive data from the client and return a structure containing operation information and argument */
int handle_input(int server_socket, module *module_registry) {
input client_input; // operation and argument sent from client
struct sockaddr *client_address;
unsigned int cl_address_len;
if(recvfrom(server_socket, &client_input, sizeof(input), 0, client_address, &cl_address_len) < 0) {
fprintf(stderr, "failed to receive from datagram socket\n");
perror("guru meditation");
return 0;
}
/* parse input ... */
}
Client
int main(int argc, char **argv) {
/* parse input ... */
/* initialize client socket and addressing information */
if((client_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "failed to bind client socket\n");
exit(1);
}
/* initialize server socket addressing information */
memset(&server_addr, 0, sizeof(struct sockaddr_un));
server_addr.sun_family = AF_UNIX;
snprintf(server_addr.sun_path, sizeof server_addr.sun_path, "%s", SOCK_PATH);
if(sendto(client_socket, &client_input, sizeof(input), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un))
fprintf(stderr, "failed to send message to daemon\n");
exit(1);
}
exit(0);
}
Half the time it works fine, but the other half it returns "Invalid argument"
That is because cl_address_len needs to be initialized with the size of your address buffer but it contains an indeterminate value.
You need to make the following changes:
struct sockaddr_un client_address;
socklen_t cl_address_len = sizeof client_address;
if(recvfrom(server_socket, &client_input, sizeof(input), 0, (struct sockaddr*)&client_address, &cl_address_len) < 0) {

C sockaddr function call for sendto?

I'm currently setting up a UDP socket for a school assignment but I can't figure out how to properly send sockaddr as a parameter into a function. The input arguments for SendData is defined the same way they are in the sendto function.
void SendData(int fileDescriptor, const struct sockaddr_in *destAddrUdp, unsigned char buffer[MAXMSG])
{
/* crc */
int strlength = strlen(buffer);
unsigned char SCRC = CRC(buffer, strlength-1);
buffer[strlength-1] = SCRC;
buffer[strlength] = '\0';
/* send */
if (sendto(fileDescriptor, buffer, strlen(buffer), 0, (struct sockaddr*) &destAddrUdp,sizeof(destAddrUdp)) < 0) {
fprintf(stderr, "Could not send data\n");
}
} `
When the below code is called from main it works however when it's called from SendData the return value from sendto is -1.
if (sendto(fileDescriptor, buffer, strlen(buffer), 0, (struct sockaddr*) &destAddrUdp,sizeof(destAddrUdp)) < 0) {
fprintf(stderr, "Could not send data\n");
}
If the program is compiled I get warning: passing argument 5 of ‘recvfrom’ from incompatible pointer type. on the sendto functioncall in SendData.
The function call for SendData in my main program is: SendData(sockfd, (struct sockaddr *) &destAddrUdp, buffer);
The main program in case it is relevant:
unsigned char SCRC;
unsigned char strlength;
unsigned char buffer[MAXMSG-5];
unsigned char header[MAXMSG];
struct rtp_struct *sendstruct;
/* option 2 */
struct sockaddr_in destAddrUdp;
int sockfd;
char dstHost[15];
printf("input host IP:\n>");
fgets(dstHost, 15, stdin);
/* Create socket */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
fprintf(stderr, "Can't create UDP socket\n");
/* set configurations */
memset (&destAddrUdp, 0, sizeof(destAddrUdp)); //set zero
destAddrUdp.sin_family = AF_INET; //internet use
destAddrUdp.sin_addr.s_addr = inet_addr(dstHost); //set so anyone can connect
destAddrUdp.sin_port = htons(dstUdpPort); //set the port to use
/* Generate CRC table */
GenerateCRCTable();
/* get string and send */
while(1) {
printf("\n>");
fgets(buffer, MAXMSG, stdin);
if(strncmp(buffer,"quit\n",MAXMSG) != 0){ //if read string == quit, see else
strlength = strlen(buffer);
SCRC = CRC(buffer, strlength-1);
buffer[strlength-1] = SCRC;
buffer[strlength] = '\0';
SendData(sockfd, (struct sockaddr *) &destAddrUdp, buffer);
/*if (sendto(sockfd, buffer, strlength, 0, (struct sockaddr *) &destAddrUdp,sizeof(destAddrUdp)) < 0){
fprintf(stderr, "Could not send data\n");}*/
}
else {
close(sockfd);
exit(EXIT_SUCCESS);
}
}
}
In your SendData function, the parameter destAddrUdp has type const struct sockaddr_in *. When you then make this call:
sendto(fileDescriptor, buffer, strlen(buffer), 0,
(struct sockaddr*) &destAddrUdp, sizeof(destAddrUdp)
The expression &destAddrUdp has type const struct sockaddr_in **, so the pointer type is incompatible. Also, sizeof(destAddrUdp) is returning the size of a pointer, not the size of the struct.
You failed to account for the differing types of destAddrUdp in SendData and main. The correct call to sendto would be:
sendto(fileDescriptor, buffer, strlen(buffer), 0,
(struct sockaddr*) destAddrUdp, sizeof(*destAddrUdp)
The problem is the sizeof(destAddrUdp) and the (struct sockaddr*) &destAddrUdp.
In your main, destAddrUdp is an struct sockaddr_in, in your function however, its a sockaddr_in* so you can't use them equally. First, sizeof(destAddrUdp) in your function will give you the size of a pointer, second, &destAddrUdp in your function will give you a destAddrUdp**. Thats not what you want.
Try sendto(fileDescriptor, buffer, strlen(buffer), 0, (struct sockaddr*) destAddrUdp,sizeof(*destAddrUdp))

read() from UDP socket drops data and blocks unexpectedly in C

I'm trying to read data from a UDP socket, but after reading the first 255 bytes, read() seems to drop the rest of the data on the socket and block until another data-gram comes in.
Here's the network code I'm using:
int sock;
struct sockaddr_in remote_addr, self_addr;
uint8_t network_init(uint16_t port)
{
memset((char *) &remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr("192.168.1.22");
remote_addr.sin_port = htons(3001);
memset((char *) &self_addr, 0, sizeof(self_addr));
self_addr.sin_family = AF_INET;
self_addr.sin_addr.s_addr = INADDR_ANY;
self_addr.sin_port = htons(3001);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf(stderr, "Could not create socket.");
return 1;
}
else if (bind(sock, (struct sockaddr *) &self_addr, sizeof(self_addr)) != 0)
{
fprintf(stderr, "Could not bind to socket.");
return 1;
}
return 0;
}
void network_send(uint8_t *data, uint8_t len)
{
sendto(sock, data, len, 0, (struct sockaddr *) &remote_addr, sizeof(remote_addr));
}
void read_data()
{
int len = 0;
ioctl(sock, FIONREAD, &len);
// We have data
if (len > 0)
{
char *buffer = (char *) malloc(256);
uint8_t buflen;
printf("==== %d | Data:\n", len);
while (len > 0)
{
buflen = min(255, len);
len = len - buflen;
buffer[buflen] = '\0';
printf("len: %d, buflen: %d,\n",len, buflen);
read(sock, buffer, buflen);
printf("%s\n", buffer);
}
printf("\n");
}
}
Here's the command I'm using to send data:
echo -n '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567' | nc -u localhost 3001
And here's the output:
==== 257 | Data:
len: 2, buflen: 255,
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
len: 0, buflen: 2,
^C
Also, after performing this read, ioctl(sock, FIONREAD, &len); produces a length result of 0. My suspicion is that for some reason, read() is clearing out the rest of the data before it has a chance to be read, but I can't seem to find any reference to this behaviour in any documentation.
I'm developing on an Ubuntu linux machine (x86_64).
With UDP sockets, each call to read() reads a whole datagram out of the kernel. If the read buffer isn't big enough for the entire datagram, the rest of it will be discarded. It's not like a stream socket, where you can keep calling until you get everything.
Since FIONREAD tells you the number of bytes in the message, you should use that as the size to malloc() rather than using 256:
if (len > 0) {
char *buffer = malloc(len);
...
P.S. Do I cast the result of malloc?

Simple messaging application...getting errno 14: bad address

I am writing a simple messaging application in C using sockets. When I use function recvfrom, it returns -1 and sets errno = 14 which is Bad address (which I am printing at the end).
The strange thing is that it still reads from the socket and gets the correct message. That is, the application is working perfectly and as expected except for that error.
My question is this: Why do you think I am getting this error? I cannot think of any reason. I was using inet_pton to set peer->sin_addr but I was getting the same error.
// socket file descriptor to send data through
int recv_sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
// fill in the peer's address, loopback in this case
struct sockaddr_in *peer = malloc(sizeof(struct sockaddr_in));
peer->sin_family = AF_INET;
peer->sin_port = htons(11110);
char *new = &(peer->sin_addr);
new[0] = 127;
new[1] = 0;
new[2] = 0;
new[3] = 1;
for (int i = 0; i < 8; i++) {
peer->sin_zero[i] = NULL;
}
bind(recv_sock_fd, peer, sizeof(struct sockaddr_in));
// check to see if the socket has any data...code removed
char buff[32] = {0};
errno = 0;
int bytes_received = recvfrom(recv_sock_fd, buff, sizeof(buff), NULL, (struct sockaddr *)peer, sizeof(struct sockaddr_in));
printf("Bytes recieved: %d: %d : %s\n", bytes_received, errno, strerror(errno));
Look at the signature of recvfrom(2):
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Last argument is an address, while you are giving it a plain integer.
Then you're building of the IP address is wrong. Do use inet_pton(3), that's what it's for. Also check the return value of the bind(2), it's surely failing now.

Resources