I'm trying to code an agnostic echo server, that could accept both IPv4 and IPv6 connection. I'm working with addrinfo structure, set with getaddrinfo.
Ipv4 connect has no problem while I can't get a working ipV6 connection.
I think my problem could be due to a wrong getaddrinfo parameter, but I'm not able to see where I'm going wrong.
Here's my code
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int simpleSocket = 0, simplePort = 0,returnStatus = 0, n;
char buffer[1024] = "";
struct hostent *hostinfo;
struct addrinfo simpleServer, *res;
if (3 != argc) {
fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
exit(1);
}
simplePort = atoi(argv[2]);
memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me
returnStatus = getaddrinfo(argv[1], argv[2], &simpleServer, &res);
simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
char *s = NULL;
switch(res->ai_addr->sa_family) {
case AF_INET: {
struct sockaddr_in *addr_in = (struct sockaddr_in *)res;
s = malloc(INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
break;
}
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res;
s = malloc(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
break;
}
default:
break;
}
fprintf(stdout, "IP address: %s\n", s);
returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
fprintf(stdout, "Type a message \n");
memset(buffer, '\0', strlen(buffer));
fgets(buffer, sizeof(buffer), stdin);
returnStatus = write(simpleSocket, buffer, sizeof(buffer));
memset(&buffer, '\0', sizeof(buffer));
fprintf(stdout, "Waiting server..\n");
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
fprintf(stdout, "Message: %s\n", buffer);
close(simpleSocket);
return 0;
}
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int simpleSocket = 0, simplePort = 0, returnStatus = 0, check = 1, n;
char buffer[1024];
struct addrinfo simpleServer, *res;
if (2 != argc) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
simplePort = atoi(argv[1]);
memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me
getaddrinfo(NULL, argv[1], &simpleServer, &res);
simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
returnStatus =bind(simpleSocket, res->ai_addr, res->ai_addrlen);
returnStatus = listen(simpleSocket, 5);
struct addrinfo clientName = { 0 };
int clientNameLength = sizeof(clientName);
int simpleChildSocket = 0;
while (1) {
while (1) {
simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);
fprintf(stdout,"Waiting..\n");
memset(&buffer, '\0', sizeof(buffer));
returnStatus = read(simpleChildSocket, buffer, sizeof(buffer));
fprintf(stdout, "Message: %s\n", buffer);
write(simpleChildSocket, buffer, sizeof(buffer));
}
}
close(simpleChildSocket);
close(simpleSocket);
return 0;
}
getaddrinfo
to create the socket and bind to an interface. If you want to accept connections using both IPv4 and IPv6 you need two sockets, and bind one to an IPv4 address and the other to an IPv6 address. – GrohclientNameLength
before every call toaccept
, as it may modify the argument. – GrohAF_INET6
) socket and usesetsockopt()
to disable itsIPV6_V6ONLY
option before callingbind()
. The client IP address reported byaccept()
will tell you whether the client is IPv4 or IPv6, so be sure your receivingaddr
buffer is large enough to holdsockaddr_in
andsockaddr_in6
. Best to usesockaddr_storage
as the buffer. – Sidran