Created
August 13, 2022 07:58
-
-
Save kinbei/537f431e3c63b7a3f984231785b6f17c to your computer and use it in GitHub Desktop.
porting-applications-to-ipv6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netdb.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> // for close | |
#include <arpa/inet.h> // for inet_ntop | |
#include <errno.h> // for errno | |
#define BACKLOG 10 | |
#ifdef _DEBUG | |
#define LOG(fmt, ...) fprintf(stdout, "%s - "fmt"\n", __FUNCTION__, ##__VA_ARGS__) | |
#define LOG_ERR(fmt, ...) fprintf(stderr, "%s - "fmt"\n", __FUNCTION__, ##__VA_ARGS__) | |
#define LOG_ERRNO(fmt, ...) LOG_ERR(fmt" : %s", ##__VA_ARGS__, strerror(errno)) | |
#else | |
#define LOG(fmt, ...) fprintf(stdout, fmt"\n", ##__VA_ARGS__) | |
#define LOG_ERR(fmt, ...) fprintf(stderr, fmt"\n", ##__VA_ARGS__) | |
#define LOG_ERRNO(fmt, ...) LOG_ERR(fmt" : %s", ##__VA_ARGS__, strerror(errno)) | |
#endif | |
int get_addrinfo_family(const char *family) { | |
if(strcmp(family, "unspec") == 0) | |
return AF_UNSPEC; | |
if(strcmp(family, "ipv4") == 0) | |
return AF_INET; | |
if(strcmp(family, "ipv6") == 0) | |
return AF_INET6; | |
return 0; | |
} | |
const char* get_family_str(int af) | |
{ | |
switch (af) { | |
case AF_INET: | |
return "AF_INET"; | |
case AF_INET6: | |
return "AF_INET6"; | |
default: | |
return "<unknown address family>"; | |
} | |
} | |
int print_protocol(int protocol) { | |
struct protoent *pe = getprotobynumber(protocol); | |
if(pe == NULL) { | |
LOG_ERRNO("getprotobynumber failed"); | |
return -1; | |
} | |
LOG("protocol(%s|%d)", pe->p_name, protocol); | |
return 0; | |
} | |
int print_nameinfo(struct sockaddr *addr, socklen_t addrlen) { | |
char host[NI_MAXHOST]; | |
char serv[NI_MAXSERV]; | |
int ret = getnameinfo(addr, addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV); | |
if( ret != 0 ) { | |
LOG_ERR("getnameinfo() failed : %s", gai_strerror(ret)); | |
return -1; | |
} | |
LOG("host(%s) serv(%s)", host, serv); | |
return 0; | |
} | |
void print_address_ntop(struct sockaddr *addr) { | |
char str[INET6_ADDRSTRLEN]; | |
const char *src = NULL; | |
switch(addr->sa_family) { | |
case AF_INET: | |
src = (const char *)&((struct sockaddr_in*)addr)->sin_addr; | |
break; | |
case AF_INET6: | |
src = (const char *)&((struct sockaddr_in6*)addr)->sin6_addr; | |
break; | |
default: | |
LOG_ERR("unknown address family(%d)", addr->sa_family); | |
break; | |
} | |
if(inet_ntop(addr->sa_family, src, str, sizeof(str)) == NULL) { | |
LOG_ERRNO("inet_ntop failed"); | |
} | |
else { | |
LOG("address is [%s]|family[%s]", str, get_family_str(addr->sa_family)); | |
} | |
} | |
/* | |
On success, a file descriptor for the new socket is returned. | |
On error, -1 is returned. | |
*/ | |
int listen_server(const char *host, const char *service, int family) { | |
struct addrinfo hints; | |
struct addrinfo *res, *p; | |
int sfd = -1; | |
memset(&hints, 0, sizeof(hints)); | |
hints.ai_family = family; | |
hints.ai_socktype = SOCK_STREAM; /* Stream socket */ | |
hints.ai_protocol = IPPROTO_TCP; /* TCP protocol */ | |
int ret = getaddrinfo(host, service, &hints, &res); | |
if( ret != 0 ) { | |
LOG_ERR("getaddrinfo() failed : %s", gai_strerror(ret)); | |
return -1; | |
} | |
/* | |
Try open socket with each address getaddrinfo returned, | |
until we get a valid listening socket. | |
*/ | |
for(p = res; p != NULL; p = p->ai_next) { | |
print_address_ntop(p->ai_addr); | |
print_protocol(p->ai_protocol); | |
print_nameinfo(p->ai_addr, p->ai_addrlen); | |
if( (sfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 ) { | |
LOG_ERRNO("socket() failed"); | |
continue; | |
} | |
int on = 1; | |
if( setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0 ) | |
{ | |
LOG_ERRNO("setsockopt() - SO_REUSEADDR failed"); | |
goto _failed_listen_socket; | |
} | |
if( bind(sfd, p->ai_addr, p->ai_addrlen) == -1 ) { | |
LOG_ERRNO("bind() failed :"); | |
goto _failed_listen_socket; | |
} | |
break; | |
_failed_listen_socket: | |
close(sfd); | |
continue; | |
} | |
freeaddrinfo(res); | |
if(p == NULL) { | |
LOG_ERR("Could not bind"); | |
return -1; | |
} | |
if( listen(sfd, BACKLOG) == -1 ) { | |
LOG_ERRNO("listen failed"); | |
close(sfd); | |
return -1; | |
} | |
return sfd; | |
} | |
int main(int argc, char *argv[]) { | |
if(argc < 4) { | |
LOG_ERR("usage: %s [host] [service] [family]", argv[0]); | |
return EXIT_FAILURE; | |
} | |
char *host = (strcmp(argv[1], "") == 0) ? NULL : argv[1]; | |
char *service = argv[2]; | |
char *family = argv[3]; | |
int listen_sfd = listen_server(host, service, get_addrinfo_family(family)); | |
if(listen_sfd == -1) { | |
return EXIT_FAILURE; | |
} | |
LOG("Listen socket : %d", listen_sfd); | |
int client_sfd = -1; | |
struct sockaddr_storage addr; | |
socklen_t addrlen = sizeof(addr); | |
if( (client_sfd = accept(listen_sfd, (struct sockaddr *)&addr, &addrlen)) == -1 ) { | |
LOG_ERRNO("accept() failed"); | |
goto _failed_accept_socket; | |
} | |
LOG("Client address: "); | |
print_nameinfo((struct sockaddr *)&addr, addrlen); | |
close(client_sfd); | |
close(listen_sfd); | |
return EXIT_SUCCESS; | |
_failed_accept_socket: | |
close(listen_sfd); | |
return EXIT_FAILURE; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment