Milos Milosavljevic Milos Milosavljevic - 21 days ago 13
C Question

UDP server/client in C - sendto error: Address family not supported by protocol

I am writing a simple illustrative UDP server client. Server should, based on the client input calculate client's network byte order, and send it back to client. I've been trying to fix this error for whole day now. Any help would be apprecciated. Here is the server start and output:

./udpserver.out 4545
from.sin_family: 12079
Little Endian

ERROR UDP_SRV_004 - can not send message to client: Address family not supported by protocol


Client start:

./udpclient.out localhost 4545


Server code:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#define BUFFER_SIZE 80

static void error(char *msg){
perror(msg);
exit(1);
}


int main(int argc, char ** argv){

int socketfd, portno, fromlen, n, length;
struct sockaddr_in server, from;
char buffer[BUFFER_SIZE];
char response[BUFFER_SIZE];
const char *le = "Little Endian\n";
const char *be = "Big Endian\n";
const char *unk = "Unknown \n";
int cli_family;

if(argc!=2){
fprintf(stderr,"Error starting the client, please start server as: %s port\n", argv[0]);
exit(0);
}

if((socketfd=socket(AF_INET,SOCK_DGRAM,0))<0)
error("ERROR UDP_SRV_001 - can not create socket");

portno=atoi(argv[1]);

bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=htons(portno);

if(bind(socketfd,(struct sockaddr *)&server,sizeof(server))<0)
error("ERROR UDP_SRV_002 - can not bind server address to a socket file descriptor");

bzero(buffer, BUFFER_SIZE);
bzero(response,BUFFER_SIZE);
n=recvfrom(socketfd,buffer,BUFFER_SIZE-1,0,(struct sockaddr *)&from,&fromlen);
if (n<0)
error("ERROR UDP_SRV_003 - can not receive client's message");

cli_family=from.sin_family;
printf("from.sin_family: %d\n", cli_family);

if (buffer[0] == 1 && buffer[1] == 2)
strcpy(response, be);
else if (buffer[0] == 2 && buffer[1] == 1)
strcpy(response, le);
else
strcpy(response, unk);

length=strlen(response);
printf("%s\n",response);

n=sendto(socketfd,response,length,0,(struct sockaddr *)&from,fromlen);
if(n<0)
error("ERROR UDP_SRV_004 - can not send message to client");


return 0;
}


Client code:

#include <stdio.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 80

static void error(char *msg){
perror(msg);
exit(1);
}

typedef union UNIT{
short s;
char c[sizeof(short)];
}unit;

int main(int argc, char ** argv){

int socketfd, portno, n, length;
struct sockaddr_in server, from;
struct hostent *host;
char buffer[BUFFER_SIZE];
unit un;
un.s=0x0102;

if(argc!=3){
fprintf(stderr,"Error starting the client, please start client as: %s hostname port\n", argv[0]);
exit(0);
}

if((host=gethostbyname(argv[1]))==NULL)
error("ERROR UDP_CLI_001 - no such host defined, please check your /etc/hosts file");

portno=atoi(argv[2]);

if((socketfd=socket(AF_INET,SOCK_DGRAM,0))<0)
error("ERROR UDP_CLI_002 - can not create socket");

bzero((char *)&server,sizeof(server));

server.sin_family=AF_INET;
server.sin_port=htons(portno);
bcopy((char *)host->h_addr,(char *)&server.sin_addr,host->h_length);
length=sizeof(server);

if(sizeof(short)!=2){
exit(0);
}

n=sendto(socketfd,un.c,strlen(un.c),0,(struct sockaddr *)&server,length);
if(n<0)
error("ERROR UDP_CLI_003 - can not send to server");
bzero(buffer,BUFFER_SIZE);
n=recvfrom(socketfd,buffer,BUFFER_SIZE,0,(struct sockaddr *)&from, &length);
if(n<0)
error("ERROR UDP_CLI_004 - can receive from server");
printf("%s",buffer);


return 0;
}

Answer

In the server, you failed to initialize fromlen prior to the call to sendto. As a result, from was not populated.

From the man page:

If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. When src_addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL. 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.

The recvfrom function attempts to read the value of fromlen, but because it is not initialized you invoke undefined behavior.

Initialize fromlen and that will address the issue.

fromlen = sizeof from;
n=recvfrom(socketfd,buffer,BUFFER_SIZE-1,0,(struct sockaddr *)&from,&fromlen);