Skip to content

Instantly share code, notes, and snippets.

@kinbei
Created August 13, 2022 07:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kinbei/537f431e3c63b7a3f984231785b6f17c to your computer and use it in GitHub Desktop.
Save kinbei/537f431e3c63b7a3f984231785b6f17c to your computer and use it in GitHub Desktop.
porting-applications-to-ipv6
#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