C unix domain sockets, recvfrom() doesn't set struct sockaddr* src_addr - c

I'm writing an application that listens for UDP packets over a unix domain socket. Consider the following code block.
int sockfd;
struct sockaddr_un servaddr;
sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket() failed");
}
unlink(port);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, port);
if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind() failed");
close(sockfd);
}
int n;
struct sockaddr_un cliaddr;
socklen_t len = sizeof(cliaddr);
discovery_msgs client_message;
bzero(&client_message, sizeof(client_message));
// Wait for a message to be received
n = recvfrom(sock_fd, &client_message, sizeof(client_message), 0,
(struct sockaddr *) &cliaddr, &len);
// At this point n = 560, client_message is filled with the expected data
//len = 0 and cliaddr has no information about the client that sent the data
Now the type of client_message isn't really important, I'm receiving a UDP packet and client_message contains all of the data I expect. The problem begins when I look at cliaddr and len after calling recvfrom. cliaddr is not modified by recvfrom like it normally is with normal network TCP/UDP and len is set to 0 after the call(which means recvfrom wrote no data to &cliaddr). I need the information in cliaddr to be populated with the unix domain path so I can send a response.
What am I doing wrong?

The solution is binding the socket on the client side when using Unix domain sockets. Otherwise the transient pathname created for sending the UDP packet immediately disappears after sendto(), which explains why the client's address information is not available on the server side.
See Stevens Network Programming page 419 or see this for an example client implementation that solves this issue: libpix.org/unp/unixdgcli01_8c_source.html
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_un cliaddr, servaddr;
sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
bzero(&cliaddr, sizeof(cliaddr)); /* bind an address for us */
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, tmpnam(NULL));
Bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
bzero(&servaddr, sizeof(servaddr)); /* fill in server's address */
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
exit(0);
}
Note: unp.h defines Bind() which is simply bind() with some error checking(commonly used throughout Stevens Network Programming). In the same manner, (SA *) is the equivalent to (struct sockaddr *).

Related

Socket Programming (Client - Server UDP)

I would like to build a client - server model where, for any number of clients connected to the server, the server should send a broadcast Message. Below is the code which I implemented, it does not throw any error, but I could not see any communication happening between the server and the client. Please let me know, where is the problem in the code.
I would like to see the message passed from the server on the clients system.
/************* UDP SERVER CODE *******************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int udpSocket, nBytes, buflen;
char buffer[1024];
char string[1024];
int portNum;
struct sockaddr_in serverAddr, clientAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size, client_addr_size;
int i;
/*Create UDP socket*/
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
portNum=atoi(argv[1]);
/*Configure settings in address struct*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(5000);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.117");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*Bind socket with address struct*/
int bStatus=bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if (bStatus < 0) {
printf("error binding on server's port number\n");
exit(1);
}
/*Initialize size variable to be used later on*/
addr_size = sizeof serverStorage;
memset(&clientAddr, 0, sizeof(clientAddr));
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(portNum); // this is where client is bound
clientAddr.sin_addr.s_addr = inet_addr("192.168.1.114");
while(1){
sprintf(buffer,"Hello Client");
buflen=strlen(buffer);
int bytesSent = sendto(udpSocket,buffer,buflen,0,(struct sockaddr *)&clientAddr, sizeof(clientAddr));
if (bytesSent < 0) {
printf("Error sending/ communicating with client\n");
exit(1);
}
printf("%s\n",buffer);
/*
// ****************************************************
memset(buffer, 0, 1024);
buflen=65536;
recvfrom(udpSocket,buffer,buflen,0,(struct sockaddr *)&serverStorage, &addr_size);
printf("Received from server: %s\n",buffer);
*/
}
return 0;
}
Below is the client code :
/************* UDP CLIENT CODE *******************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[]){
int clientSocket, portNum, nBytes, buflen;
char buffer[1024];
char string[1024];
struct sockaddr_in serverAddr,clientAddr;
socklen_t addr_size;
/*Create UDP socket*/
clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
portNum = atoi(argv[1]);
/*Configure settings in address struct*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(portNum);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.117");
// memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
memset(&serverAddr,0,sizeof(serverAddr));
// binding
bind(clientSocket, (struct sockaddr *) &clientAddr, sizeof(clientAddr));
/*Initialize size variable to be used later on*/
addr_size = sizeof serverAddr;
while(1){
memset(buffer, 0, 1024);
buflen=65536;
recvfrom(clientSocket,buffer,buflen,0,(struct sockaddr *)&serverAddr, &addr_size);
//recvfrom(clientSocket,buffer,buflen,0,NULL,NULL);
printf("Received from server: %s\n",buffer);
/*
//********************************************************************
sprintf(buffer,"Hello Client");
buflen=strlen(buffer);
sendto(clientSocket,buffer,buflen,0,(struct sockaddr *)&serverAddr,addr_size);
printf("%s\n",buffer);
*/
}
return 0;
}
At a high level there are a few things that need to be addressed. Given the code in the post has a server that initiates communication with the client following line needs to be modified (and related code for these)
On the server side:
sendto(udpSocket,buffer,buflen,0,(struct sockaddr *)&serverStorage,addr_size);
Essentially what's happening here is that udpSocket is bound to serverAddr and we are trying to sendto serverStorage (2nd last argument). The trouble here is that the serverStorage is not bound to anything. Basically sendto has the source right but not the destination.
On the client side:
recvfrom(clientSocket,buffer,buflen,0,(struct sockaddr *)&serverAddr, &addr_size);
The clientSocket is not bound to anything so the system picks a random port where it will listen. In the recvfrom call, 2nd last argument captures the address details of the other side. Whereas in the code you have bound it to server's IP and port, it does not have any effect because it is filled in by the recvfrom call and returned.
To make things work here is what I'd suggest (you can add the details but have worked out the skeleton based on what you posted)
server_initiating_with_client.c
//...
int udpSocket, nBytes, buflen;
char buffer[1024];
int portNum;
struct sockaddr_in serverAddr, clientAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size, client_addr_size;
/*Create UDP socket*/
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
portNum=atoi(argv[1]);
/*Configure settings in address struct*/
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(5000); // Making sure server has a unique port number
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*Bind socket with address struct*/
int bStatus = bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if (bStatus < 0) {
printf("error binding on server's port number\n");
exit(1);
}
/*Initialize size variable to be used later on*/
addr_size = sizeof serverStorage;
/* Because server is initiating the communication here one needs to set the
* clientAddr and needs to know the port number of the client
*/
memset(&clientAddr, 0, sizeof(clientAddr));
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(portNum);
clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
while(1){
sprintf(buffer,"Hello Client");
buflen=strlen(buffer);
int bytesSent = sendto(udpSocket,buffer,buflen, 0,
(struct sockaddr *)&clientAddr,
sizeof(clientAddr));
if (bytesSent < 0) {
printf("Error sending/ communicating with client\n");
exit(1);
}
printf("Done sending %s\n", buffer);
sleep(1);
}
..
and on the client side something along these lines
//..
int clientSocket, portNum, nBytes, buflen;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_in clientAddr;
socklen_t addr_size;
/*Create UDP socket*/
clientSocket = socket(PF_INET, SOCK_DGRAM, 0);
portNum = atoi(argv[1]);
/*Configure settings in address struct*/
memset(&clientAddr, 0, sizeof(clientAddr));
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(portNum);
clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
memset(&serverAddr, 0, sizeof(serverAddr));
/*Bind on client side as well because you are specifically sending something over
* to the client. Usually the server will know where to talk back to the client
* but in your example because of the reversed semantics this will be needed
*/
bind(clientSocket, (struct sockaddr *) &clientAddr, sizeof(clientAddr));
/*Initialize size variable to be used later on*/
addr_size = sizeof serverAddr;
while(1){
memset(buffer, 0, 1024);
buflen=65536;
recvfrom(clientSocket,buffer,buflen,0,(struct sockaddr *)&serverAddr, &addr_size);
printf("Received from server: %s\n",buffer);
sleep(1);
}
return 0;
The code can be simplified if you reverse your comm from client to server, thereby, eliminating the need for a fixed port number on the client side.
just make a few changes in it. Assign a socket length predefined (socklen_t *) structure to typecast length
n = recvfrom(sockfd, (char *)buffer, MAXLINE,MSG_WAITALL, (struct sockaddr *) &servaddr, (socklen_t*)&len);
socklen_t* convert a length into socket length.
Make these changes at both client-side as well as a server-side
.

C: listen() API returning -1

I'm using below code to create a server program in c. The code is taken from here
#include <Winsock.h>
#include <string.h>
#include <ws2tcpip.h>
#include<conio.h>
int main(){
int welcomeSocket, newSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
welcomeSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(7891);
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Bind the address struct to the socket ----*/
bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
/*---- Listen on the socket, with 5 max connection requests queued ----*/
if(listen(welcomeSocket,5)==0)
printf("Listening\n");
else
printf("Error %d\n",listen(welcomeSocket,5));
/*---- Accept call creates a new socket for the incoming connection ----*/
addr_size = sizeof serverStorage;
newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
/*---- Send message to the socket of the incoming connection ----*/
strcpy(buffer,"Hello World\n");
send(newSocket,buffer,13,0);
getch();
return 0;
}
I've changed the code little bit to make it work in dev-c but in the output it's printing error i.e it's executing the else condition. Anyone have any idea why? And how to debug this?
I've tried changing the port no. It didn't work.
On Windows, you have to initialize the networking subsystem first by calling WSAStartup, before you can make any calls to socket/network-related functions.
WSADATA wsaData;
int wsaRc = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (wsaRc != 0) {
fprintf(stderr, "WSAStartup failed with error: %d\n", wsaRc);
return 1;
}
/* Socket related functions callable from here on */
The example-code you've linked was written for UNIX systems, which do not have that API-requirement.
Please note, that you also
[...] must call the WSACleanup function for every successful
time the WSAStartup function is called.
(From the WSAStartup documentation).

UDP packets not delivered - recvfrom() never returns

I have setup a UDP receiver, as:
int rx_socket;
struct sockaddr_in my_addr;
struct sockaddr_in rem_addr;
socklen_t addrlen = sizeof(rem_addr);
rx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9900);
rc = bind(rx_socket, (struct sockaddr *) &(my_addr), sizeof(my_addr));
if (!rc) {
printf("BIND SUCCESSFULL\n");
}
char buf[250];
while(1) {
printf("WAITING\n");
recvfrom(rx_socket, buf, sizeof(buf), 0, (struct sockaddr *)&rem_addr, &addrlen);
printf("RECEIVED\n");
}
The recvfrom() never returns. I have done some Wireshark analysis, and it indicates the packets are there:
Summary:
User Datagram Protocol, Src Port: 57506 (57506), Dst Port: iua (9900)
Checksum: 0x14a2 [validation disabled]
Data (8 bytes)
Any help will be appreciated.
EDIT:
An interesting observation is that the source, which is a DSP fails to send packets, i.e., sendto() returns -1, until I ping to it, from destination. Right after the ping, the source can start transmitting packets.
EDIT 2:
Here is the sender's code:
int fd;
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9900);
inet_aton("10.0.201.102", &(my_addr.sin_addr));
char buf[250];
for (;;) {
int bytesSent = sendto(fd, buf, 8, 0,
(struct sockaddr *) &(my_addr), sizeof(my_addr));
printf("sent: %d bytes\n", bytesSent);
sleep(1000);
}
So the problem turned out to be with virtualbox. My sender is on host, but receiver is on a virtual machine. If I run the receiver on the host as well, UDP packets are being received.

Packet received in NIC not read via socket

I have opened an UDP socket to listen to incoming packet. I could see in the wireshark log the packets reaching the NIC. But the same is not available when reading via Socket. The 'netsatat ' command shows the port number is listened for the any incoming UDP messages. The socket reader keeps on waiting . I have checked using Java and C, in Linux(ubuntu) environment. I can see the Identification value for received IPV4 packet is 0. Is this value can play any role for a socket to read the data ?
My C code for reading the socket
int sock, n, nr;
socklen_t fromlen;
struct sockaddr_in server;
struct sockaddr_in from;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
printf("Can not create socket in server\n");
memset(&server, 0, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(5555);
server.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
fromlen = sizeof(struct sockaddr_in);
while(1) {
printf("Waiting to receive\n");
n = recvfrom(sock, &nr, sizeof(nr), 0, (struct sockaddr *) &from, &fromlen);
printf("I have received");
}
Please find the wireshark trace http://imgur.com/Au9BeS1
This is a problem:
if(bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
fromlen = sizeof(struct sockaddr_in);
This will only set fromlen is the bind call fails. And since fromlen is not properly initialized, it will contain a seemingly random value that is not valid for recvfrom.

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