I wrote a server in C using the UNIX sockets API for some tests and I ended up with a "weird" problem: The server is blocking connections > IPv4 and it was programmed to only accept IPv6 connections.
Follow the code and a few details below for a better understanding of the problem:
server.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int make_server_socket(void){
int sockfd;
struct addrinfo *res=NULL, hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags=AI_PASSIVE;
hints.ai_family=AF_INET6;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
if(getaddrinfo(NULL, "9009", &hints, &res)!=0){
printf("\n* getaddrinfo() -> failed\n");
exit(1);
}else{
struct addrinfo *it;
for(it=res; it!=NULL; it=it->ai_next){
if(it->ai_family==AF_INET || it->ai_family==AF_INET6){
sockfd=socket(it->ai_family, SOCK_STREAM, IPPROTO_TCP);
if(sockfd<0){
continue;
}else{
if(bind(sockfd, it->ai_addr, it->ai_addrlen)==0){
break;
}
}
close(sockfd);
}
}
freeaddrinfo(res);
}
return sockfd;
}
void show_addr(const struct sockaddr *addr){
char strAddr[64];
if(addr->sa_family==AF_INET){
struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
inet_ntop(AF_INET, &addr4->sin_addr.s_addr,
strAddr, INET_ADDRSTRLEN);
printf("[ipv4 addr] -> %s\n", strAddr);
}else{
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr,
strAddr, INET6_ADDRSTRLEN);
printf("[ipv6 addr] -> %s\n", strAddr);
}
}
int check_addr(const char *buffAddr, char *buffAddrTable[], size_t n){
int rv=0;
struct sockaddr *addr=(struct sockaddr*)buffAddr;
struct sockaddr *addrs=(struct sockaddr*)buffAddrTable;
if(addr->sa_family==AF_INET){
struct sockaddr_in *aux4=(struct sockaddr_in*)buffAddr;
for(size_t i=0; i<n; i++){
struct sockaddr_in *addr4=(struct sockaddr_in*)&addrs[i];
if(addr4->sin_addr.s_addr==aux4->sin_addr.s_addr){
rv=-1;
break;
}else{
rv=0;
}
}
}else if(addr->sa_family==AF_INET6){
struct sockaddr_in6 *aux6=(struct sockaddr_in6*)buffAddr;
for(size_t i=0; i<n; i++){
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)&addrs[i];
int res=memcmp(addr6->sin6_addr.s6_addr,
aux6->sin6_addr.s6_addr, 16);
if(res==0){
rv=-1;
break;
}else{
rv=0;
}
}
}else{
rv=-2;
}
return rv;
}
int main(void){
int sockfd=make_server_socket();
if(sockfd<0){
printf("\n* make_server_socket() -> failed\n");
exit(1);
}
if(listen(sockfd, 10)<0){
printf("\n* listen() -> failed\n");
exit(1);
}
size_t max;
printf("\nEnter the maximum number of connections >");
scanf("%ld", &max);
printf("\n");
int csockfd;
socklen_t socklen;
char buffAddr[1024];
char *buffAddrTable[max];
for(size_t i=0; i<max;){
socklen=sizeof(buffAddr);
csockfd=accept(sockfd, (struct sockaddr*)buffAddr, &socklen);
if(csockfd!=-1){
if(check_addr(buffAddr, buffAddrTable, i)==0){
memcpy(buffAddrTable, buffAddr, socklen);
show_addr((struct sockaddr*)buffAddr);
send(csockfd, "Bonjour!zherkezhi@zherkezhi:~/Documents/C$ nc 127.0.0.1 9009
zherkezhi@zherkezhi:~/Documents/C$ nc ::1 9009
(em outra maquina ligada a rede local)
erika@localhost:~/Desktop$ nc 192.168.50.202 9009
", 9, 0);
i++;
}
close(csockfd);
}
}
printf("\n");
close(csockfd);
return 0;
}
Why I used the char type to handle network addresses ?
Client (netcat):
zherkezhi@zherkezhi:~/Documents/C$ gcc -Wall server.c -o server
zherkezhi@zherkezhi:~/Documents/C$ ./server
Enter the maximum number of connections >3
[ipv6 addr] -> ::ffff:127.0.0.1
[ipv6 addr] -> ::1
[ipv6 addr] -> ::ffff:192.168.50.18
zherkezhi@zherkezhi:~/Documents/C$ #WTF???
Server:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int make_server_socket(void){
int sockfd;
struct addrinfo *res=NULL, hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags=AI_PASSIVE;
hints.ai_family=AF_INET6;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
if(getaddrinfo(NULL, "9009", &hints, &res)!=0){
printf("\n* getaddrinfo() -> failed\n");
exit(1);
}else{
struct addrinfo *it;
for(it=res; it!=NULL; it=it->ai_next){
if(it->ai_family==AF_INET || it->ai_family==AF_INET6){
sockfd=socket(it->ai_family, SOCK_STREAM, IPPROTO_TCP);
if(sockfd<0){
continue;
}else{
if(bind(sockfd, it->ai_addr, it->ai_addrlen)==0){
break;
}
}
close(sockfd);
}
}
freeaddrinfo(res);
}
return sockfd;
}
void show_addr(const struct sockaddr *addr){
char strAddr[64];
if(addr->sa_family==AF_INET){
struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
inet_ntop(AF_INET, &addr4->sin_addr.s_addr,
strAddr, INET_ADDRSTRLEN);
printf("[ipv4 addr] -> %s\n", strAddr);
}else{
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr,
strAddr, INET6_ADDRSTRLEN);
printf("[ipv6 addr] -> %s\n", strAddr);
}
}
int check_addr(const char *buffAddr, char *buffAddrTable[], size_t n){
int rv=0;
struct sockaddr *addr=(struct sockaddr*)buffAddr;
struct sockaddr *addrs=(struct sockaddr*)buffAddrTable;
if(addr->sa_family==AF_INET){
struct sockaddr_in *aux4=(struct sockaddr_in*)buffAddr;
for(size_t i=0; i<n; i++){
struct sockaddr_in *addr4=(struct sockaddr_in*)&addrs[i];
if(addr4->sin_addr.s_addr==aux4->sin_addr.s_addr){
rv=-1;
break;
}else{
rv=0;
}
}
}else if(addr->sa_family==AF_INET6){
struct sockaddr_in6 *aux6=(struct sockaddr_in6*)buffAddr;
for(size_t i=0; i<n; i++){
struct sockaddr_in6 *addr6=(struct sockaddr_in6*)&addrs[i];
int res=memcmp(addr6->sin6_addr.s6_addr,
aux6->sin6_addr.s6_addr, 16);
if(res==0){
rv=-1;
break;
}else{
rv=0;
}
}
}else{
rv=-2;
}
return rv;
}
int main(void){
int sockfd=make_server_socket();
if(sockfd<0){
printf("\n* make_server_socket() -> failed\n");
exit(1);
}
if(listen(sockfd, 10)<0){
printf("\n* listen() -> failed\n");
exit(1);
}
size_t max;
printf("\nEnter the maximum number of connections >");
scanf("%ld", &max);
printf("\n");
int csockfd;
socklen_t socklen;
char buffAddr[1024];
char *buffAddrTable[max];
for(size_t i=0; i<max;){
socklen=sizeof(buffAddr);
csockfd=accept(sockfd, (struct sockaddr*)buffAddr, &socklen);
if(csockfd!=-1){
if(check_addr(buffAddr, buffAddrTable, i)==0){
memcpy(buffAddrTable, buffAddr, socklen);
show_addr((struct sockaddr*)buffAddr);
send(csockfd, "Bonjour!zherkezhi@zherkezhi:~/Documents/C$ nc 127.0.0.1 9009
zherkezhi@zherkezhi:~/Documents/C$ nc ::1 9009
(em outra maquina ligada a rede local)
erika@localhost:~/Desktop$ nc 192.168.50.202 9009
", 9, 0);
i++;
}
close(csockfd);
}
}
printf("\n");
close(csockfd);
return 0;
}
So what can I do to prevent someone from connecting to my server using an IPv4 address when it is configuring to use IPv6 ?
Curiosity : ffff
converted to decimal equals 65535
. 65535
is the number of TCP ports currently in existence (actually they are 65536
, but said 65535
because I did not consider 0
). So, is there any relationship between this value and my problem?