課題3:
サンプルプログラム(2)のクライアント(tcpc2.c)とサーバ(tcps2.c)をアドレスファミリ独立となるように書き換えよ.
このプログラムは,以下のようにIPv4に依存した部分が幾つもハードコーティングされている.
- sockaddr_inが使われている.
- hbufがIPv4アドレスの最大文字数長文(INET_ADDRSTRLEN)になっている.
- gethostbynameが使われている.
- socket関数がハードコーティングされたAF_INETを使っている.
- socket関数がSOCK_STREAMにIPPROTO_TCPをハードコーティングしている.
- inet_ntopがハードコーティングされたAF_INETと一緒に使われている.
以上の点に注意して,アドレスファミリ独立のプログラムに書き換えること.
tcpv6c2.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main __P((int, char **));
int
main(argc, argv)
int argc;
char **argv;
{
struct addrinfo hints, *hp;
struct servent *sp;
unsigned long lport;
u_int16_t port;
char *ep;
int dstlen;
size_t l;
int s;
char buf[1024];
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
/* 引数の数をチェック */
if (argc != 3) {
fprintf(stderr, "usage: test host port\n");
exit(1);
/*NOTREACHED*/
}
/* アドレスをバイナリに変換 */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo(argv[1], argv[2], &hints, &hp);
if (!hp) {
fprintf(stderr, "%s: %s\n", argv[1], hstrerror(h_errno));
exit(1);
/*NOTREACHED*/
}
/* ポート番号をバイナリに変換 */
sp = getservbyname(argv[2], "TCP");
if (sp) {
port = sp->s_port & 0xffff;
} else {
ep = NULL;
errno = 0;
lport = strtoul(argv[2], &ep, 10);
if (!*argv[2] || errno || !ep || *ep) {
fprintf(stderr, "%s: no such service\n", argv[2]);
exit(1);
/*NOTREACHED*/
}
if (lport & ~0xffff) {
fprintf(stderr, "%s: out of range\n", argv[2]);
exit(1);
/*NOTREACHED*/
}
port = htons(lport & 0xffff);
}
endservent();
s = socket(hp->ai_family, hp->ai_socktype, hp->ai_protocol);
if (s < 0) {
perror("socket");
exit(1);
/*NOTREACHED*/
}
getnameinfo(hp->ai_addr, hp->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr, "trying %s port %u\n", hbuf, ntohs(port));
if (connect(s, hp->ai_addr, hp->ai_addrlen) < 0) {
perror("connect");
exit(1);
/*NOTREACHED*/
}
while ((l = read(s, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, l);
close(s);
exit(0);
/*NOTREACHED*/
}
tcpv6s2.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
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*/
}
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, 0);
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*/
}