IPv6プログラミング

055710C 大城絢子


課題1

tcpv6cはIPv4とIPv6の両方に対応しているにもかかわらず、tcpc(IPv4のみに対応)よりコードが短い。この理由は何故か説明せよ。

 
 tcpc.cは、アドレスとボート番号をそれぞれ別に調べる。よってコードが長くなる。一方でtcpv6c.cは、一緒に調べるので、コードは短くなる。

☆使用している関数☆ 
inetaddr()関数: インターネットホストのアドレスを「数値とドット」記法からバイナリ値へ変換して返す. gethostbyname()関数: 与えられたホスト名に対応する構造体を返す. atoi()関数: nptrによって指示される文字列のはじめの部分をint型に変換する. getservbyname()関数: プロトコルを用いるサービスの名前にマッチする行を/etc/servicesから探し、その行の内容を含めたservent構造体を返す. getaddrinfo()関数: getaddinfo()関数はgetipnodebyname(),Getipnodebyaddr(),getservbyname(),Getservbyport()の4つの関数の機能をまとめて1つのインターフェースにしたものである.なのでgetaddinfo()関数のみでネットワークホストのアドレスを取得でき、さらにポート番号も調べることができる.

課題2

  tcpv6sはAF_INET6でソケットをオープンしているにもかかわらず、IPv4 でも通信できる。この理由は何故か説明せよ。


IPv6アドレス体系の中には、IPv4射影アドレスと呼ばれるIPv6アドレスが存在する。IPv4射影IPv6アドレスとは、IPv6アドレスの上位80bitに0、81〜96bit目に1を入れ、下位32bitにIPv4アドレスを埋め込んだアドレスである。AF_INET6ケットは、IPv6通信とIPv4通信の両方に使用できる。

 IPv4互換IPv6アドレスとは…
下位32bitに 割り当てられているIPv4アドレスを埋め込み、上位のビットは全て0 にする。また、IPv6アドレスは16進表記が基本であるが、この場合には IPv4の10進表記でも良い。

課題3

サンプルプログラム(2)のクライアント(tcpc2.c)とサーバ(tcps2.c)をアドレスファミリ独立となるように書き換えよ。(ヒント:下記参照)

myclient6.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



int main __P((int, char **));

int
main(argc, argv)
int argc;
char **argv;
{
	struct addrinfo info, *res;
	ssize_t l;
	int s;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	char buf[1024];
	char ip[256];
	
	/* 引数の数をチェック */
	if (argc != 3){
		fprintf(stderr, "usage: %s host (port)\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	/*アドレスをバイナリに変更*/
	memset(&info, 0, sizeof(info));
	info.ai_flags    = AI_CANONNAME;
	info.ai_family   = AF_UNSPEC;
	info.ai_socktype = SOCK_STREAM;
	
	if (getaddrinfo(argv[1],argv[2], &info, &res) != 0) {
		perror("getaddrinfo");
		exit(EXIT_FAILURE);       
	}
	
	
	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (s < 0) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	
	getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), sbuf, 
								sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
	fprintf(stderr, "trying %s port %s\n", hbuf, argv[2]);

	if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
		perror("connect");
		exit(EXIT_FAILURE);
	}
	

	while ((l = read(s, buf, sizeof(buf))) > 0)
			write(STDOUT_FILENO, buf, l);
	close(s);
	return EXIT_SUCCESS;

}




myserver6.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main __P((int, char **));

int
		main(argc, argv)
int argc;
char **argv;
{
	struct servent *sp;
	unsigned long lport;
	u_int16_t port;
	char *ep;
	struct sockaddr_in6 serv;
	int servlen;
	struct sockaddr_in6 from;
	socklen_t fromlen;
	int s;
	int ls;
	char hbuf[INET6_ADDRSTRLEN];
	
	if (argc != 2) {
		fprintf(stderr, "usage: test (port)\n");
		exit(1);
		/*NOTREACHED*/
	}
	else{
		sp = getservbyname(argv[1], "tcp");
		if (sp)
				port = sp->s_port & 0xffff;
		else {
			ep =NULL;
			errno = 0;
			lport =strtoul(argv[1], &ep, 10);
			if (!*argv[1] || errno || !ep || *ep) {
				fprintf(stderr, "%s: no such service\n", argv[1]);
				exit(1);
				/*NOTREACHED*/
			}
			if (lport & ~0xffff) {
				fprintf(stderr, "%s: out of range\n", argv[1]);
				exit(1);
				/*NOTREACHED*/
			}
			
			port = htons(lport & 0xffff);
		}
	}
	endservent();
	
	memset(&serv, 0, sizeof(serv));
	serv.sin6_family = AF_INET6;
	/* linux/Solarisでは以下の行は不要 */
	serv.sin6_len = sizeof(struct sockaddr_in6);
	serv.sin6_port = port;
	servlen = sizeof(struct sockaddr_in6);
	
	s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (s < 0) {
		perror("socket");
		exit(1);
		/*NOTREACHED*/
	}
	
	if (bind(s, (struct sockaddr *)&serv, servlen) < 0) {
		perror("bind");
		exit(1);
		/*NOTREACHED*/
	}
	if (listen(s, 5) < 0) {
		perror("listen");
		exit(1);
		/*NOTREACHED*/
	}
	
	while (1) {
		fromlen = sizeof(from);
		ls = accept(s, (struct sockaddr *)&from, &fromlen);
		if (ls < 0)
				continue;
		if (from.sin6_family != AF_INET6 || fromlen != sizeof(struct sockaddr_in6)) {
			exit(1);
			/*NOTREACHED*/
		}
		
		if (inet_ntop(AF_INET6, &from.sin6_addr, hbuf, sizeof(hbuf)) == NULL) {
			exit(1);
			/*NOTREACHED*/
		}
		write(ls, "Hello ",6);
		write(ls, hbuf, strlen(hbuf));
		write(ls, "\n", 1);
		close(ls);
	}
	/*NOTREACHED*/
}






◎IPv4アドレスの時の実行結果

nw0510:/Users/j05010/Desktop>./myclient6 133.13.59.10 2000
trying 133.13.59.10 port 2000
Hello ::ffff:133.13.59.10

◎IPv6アドレスの時の実行結果

nw0510:/Users/j05010/Desktop>./myclient6 2001:02f8:001c:d048:0211:24ff:fe8e:3d47 2000
trying 2001:2f8:1c:d048:211:24ff:fe8e:3d47 port 2000
Hello 2001:2f8:1c:d048:211:24ff:fe8e:3d47