#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/errno.h>

main()
{
	struct sockaddr_in6 sin6;
	struct sockaddr_in sin;
	register int s;
	int len, val;
	char buf[64];

	if ((s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("socket");
		exit(1);
	}
	bzero(&sin6, sizeof(sin6));
	sin6.sin6_len = sizeof(sin6);
	sin6.sin6_family = AF_INET6;
	if (inet_pton(AF_INET6, "::ffff:128.93.8.18", &sin6.sin6_addr) <= 0) {
		fprintf(stderr, "inet_pton failed\n");
		exit(1);
	}
	sin6.sin6_port = htons(25);
	if (connect(s, (struct sockaddr *)&sin6, sin6.sin6_len) < 0) {
		perror("connect");
		exit(1);
	}
	val = 0;
	len = sizeof(int);
	if (getsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM, &val, &len) < 0) {
		perror("getsockopt(IPV6_ADDRFORM)");
		exit(1);
	}
	printf("family = %d\n", val);
	bzero(&sin6, sizeof(sin6));
	len = sizeof(sin6);
	if (getsockname(s, (struct sockaddr *)&sin6, &len) < 0) {
		perror("getsockname");
		exit(1);
	}
	printf("sockname = %s\n",
	       inet_ntop(sin6.sin6_family, &sin6.sin6_addr, buf, 64));
	bzero(&sin6, sizeof(sin6));
	len = sizeof(sin6);
	if (getpeername(s, (struct sockaddr *)&sin6, &len) < 0) {
		perror("getpeername");
		exit(1);
	}
	printf("peername = %s\n",
	       inet_ntop(sin6.sin6_family, &sin6.sin6_addr, buf, 64));
	val = PF_INET;
	len = sizeof(int);
	if (setsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM, &val, len) < 0) {
		perror("setsockopt(IPV6_ADDRFORM)");
		exit(1);
	}
	val = 0;
	len = sizeof(int);
	if (getsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM, &val, &len) < 0) {
		perror("getsockopt(IPV6_ADDRFORM)");
		exit(1);
	}
	printf("new family = %d\n", val);
	bzero(&sin, sizeof(sin));
	len = sizeof(sin);
	if (getsockname(s, (struct sockaddr *)&sin, &len) < 0) {
		perror("getsockname");
		exit(1);
	}
	printf("sockname = %s\n",
	       inet_ntop(sin.sin_family, &sin.sin_addr, buf, 64));
	bzero(&sin, sizeof(sin));
	len = sizeof(sin);
	if (getpeername(s, (struct sockaddr *)&sin, &len) < 0) {
		perror("getpeername");
		exit(1);
	}
	printf("peername = %s\n",
	       inet_ntop(sin.sin_family, &sin.sin_addr, buf, 64));
	val = PF_INET6;
	len = sizeof(int);
	if (setsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM, &val, len) < 0) {
		perror("setsockopt(IPV6_ADDRFORM)");
		exit(1);
	}
	val = 0;
	len = sizeof(int);
	if (getsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM, &val, &len) < 0) {
		perror("getsockopt(IPV6_ADDRFORM)");
		exit(1);
	}
	printf("last family = %d\n", val);
	bzero(&sin6, sizeof(sin6));
	len = sizeof(sin6);
	if (getsockname(s, (struct sockaddr *)&sin6, &len) < 0) {
		perror("getsockname");
		exit(1);
	}
	printf("sockname = %s\n",
	       inet_ntop(sin6.sin6_family, &sin6.sin6_addr, buf, 64));
	bzero(&sin6, sizeof(sin6));
	len = sizeof(sin6);
	if (getpeername(s, (struct sockaddr *)&sin6, &len) < 0) {
		perror("getpeername");
		exit(1);
	}
	printf("peername = %s\n",
	       inet_ntop(sin6.sin6_family, &sin6.sin6_addr, buf, 64));
	(void) close(s);
	exit(0);
}
