サンプルコードで理解する
アプリケーションのIPv6対応
2015年2月28日
IPv6普及・高度化推進協議会 IPv4/IPv6共存WG
アプリケーションのIPv6対応検討SWG
渡辺 露文(富士ソフト株式会社)
(2015年3月9日資料更新)
オープンソースカンファレンス 2015 Tokyo/Spring
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
About me
 渡辺 露文 (わたなべ つゆふみ)
富士ソフト株式会社 技術本部技術開発部
ネットワークエキスパート
業務経歴
 1999年 富士ソフトABC株式会社(現 富士ソフト株式会社)入社
 入社後、ISP、データセンター顧客向けシステムなどのシステム開
発・インフラ構築・運用、社内システムのインフラ企画・構築・運
用に従事
 2011年~ 技術調査および社内技術者教育に従事、2014年~ OSS
社内利用管理も兼務
主な社外活動
 IPv6普及・高度化推進協議会
– IPv4/IPv6共存WG アプリケーションのIPv6対応検討SWG
– IPv4/IPv6共存WG IPv6導入に起因する問題検討SWG
 技術評論社 Software Design にて「IPv6化の道も一歩から」連載
(2012年12月号~2014年1月号;共同執筆)
< twatanab@fsi.co.jp >
- 1 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Do you know …
IPv6 ?
- 2 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Do you know …
IPv6 ?
Internet Protocol version 6
インターネットの通信に関する規約(RFC791)
IPネットワークに接続するには1つ以上のIPアドレスが必要
皆さんが馴染んでいるのはIPv4(例:10.1.2.3)
- 3 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
こんなコード書いてないですよね?
 このコード、イケてない…
use IO::Socket::IP;
$host = “198.51.100.1”;
:
my $sock = IO::Socket::IP->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp'
) or die “Error: $!¥n”;
:
- 4 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
他にもイケてないコードがある
 とある Androidプログラミング書籍に
おけるソケット通信のサンプルコード
public class SocketEx…
…
…
private final static String IP=“192.168.11.12”;//★変更必須
- 5 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
他にもイケてないコードがある
 とある Androidプログラミング書籍に
おけるソケット通信のサンプルコード
public class SocketEx…
…
…
private final static String IP=“192.168.11.12”;//★変更必須
良い子はマネしちゃダメ
- 6 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv6対応の話をする前に…
IPアドレスのハードコーディングはNG
ダメ。ゼッタイ。
$host = “www.example.com”
のようにFQDNで接続先を指定する
IPアドレス直書きすると、
アドレス変更時に
 修正が必要
 再テストも必要
- 7 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Internet
ネットワークアクセスの作法=名前解決を使う
 Webアクセスの例
Client
Web Server
www.example.jp
192.0.2.1
権威DNS Server
198.51.100.53
①名前解決問合せ
www.example.jp ?
②アドレス応答
www.example.jp ⇒ 192.0.2.1
③HTTP通信
FQDN
FQDNで接続先を指定し、DNSからアドレス取得
- 8 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
なぜIPアドレス直書きがダメなのか?
アプリケーションは、IPアドレスに依存すべきではない
目的 変更・改修の理由
アプリケーション 機能の
提供
 業務要件の変更
 サービス内容の変更
 ユーザビリティ向上 …,etc.
インフラ 資源の
提供
 資源管理(IPアドレス、サーバリソース
…)
 性能
互いに変更の影響を受けるべきではない
同一システムでも変更・改修の理由・時期は異なる
例)IPアドレスでユーザを識別すべきではない
- 9 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
いまどきのIPv6を知ろう!
- 10 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
実はIPv6を使える環境が増えています①
 最近のOS
Windows Vista以降
Mac OS X
Linux
FreeBSD
…
いずれも
デフォルトで利用可能
- 11 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
実はIPv6を使える環境が増えています②
 インターネット回線
フレッツ光ネクスト
au ひかり
NURO 光
…
利用可能
既存ユーザへの
自動導入も進行中
すでに、ユーザからあなたのサービスにIPv6で
アクセスされようとしている…かもしれない
- 12 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
日本におけるIPv6の普及状況
 フレッツ光ネクストのIPv6普及率(2014年9月)
IPv6普及率:3.9%
フレッツ光ネクスト契約数:15,805,000
 au ひかりのIPv6普及率(2014年9月):99%
今後本格的に普及する前に
IPv6対応を始めたほうが良い
出典:IPv6普及・高度化推進協議会 アクセス網におけるIPv6普及状況調査
http://v6pc.jp/jp/spread/ipv6spread_03.phtml
- 13 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
余談:IPv6でインターネットにアクセスできるかの確認方法
 Webブラウザで http://www.test-ipv6.jp にアクセス
 Webブラウザで http://www.kame.net にアクセス
IPv6でアクセスすると、
亀が踊ります♪
- 14 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv6の背景:IPv4アドレス枯渇
 IPv4アドレスの在庫状況
(地域インターネットレジストリ)
通信事業者、ISP、
データセンター、
クラウド事業者等の
在庫が残るのみ
世界的に足りなくなってきている
- 15 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
もう少しIPv6を知ろう!
- 16 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv4とIPv6とでは何が違うのか?(1)
 アドレス体系が異なる(IPv6のアドレス空間は広大)
例
 IPv4)192.0.2.1
 IPv6完全表記)2001:0db8:0000:0000:0001:0000:0000:0001
 IPv6省略表記)2001:db8::1:0:0:1 (RFC5952準拠)
IPv4アドレス IPv6アドレス
アドレス長 32bit 128bit
文字列
表記
表記法 8bitずつ区切り、
10進数で表記
16bitずつ区切り、
16進数で表記
区切り文字 . (ドット) : (コロン)
文字列長 15文字以内 39文字以内
- 17 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
グローバルスコープ
IPv4とIPv6とでは何が違うのか?(2)
リンクローカルスコープ
リンクローカルアドレス fe80::/10
ユニークローカルアドレス fc00::/7
グローバルユニキャストアドレス 2000::/3
IPv6では1つのNICに複数のアドレスを
有効範囲に応じて割当て、使い分ける
- 18 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv4とIPv6の関係(1)
 先ほど紹介したものの他にも機能的にIPv4と異な
ることがある
IPv4とIPv6は互換性がない
- 19 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv4とIPv6の接続性
Internet
IPv4対応
(IPv6非対応)
システム
IPv4/IPv6
両対応システム
IPv6対応
(IPv4非対応)
システム
(1)
(2)
(3)
IPv4端末
IPv4/IPv6
両対応端末
IPv6端末
IPv4
IPv6
- 20 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv4とIPv6の関係(2)
 両方に対応している環境では(デフォルトで)
IPv6 > IPv4
- 21 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
IPv6に対応しない場合の影響
1. IPv6のみの環境と通信できない
 ビジネス機会を損失する
 システム連携が行えず要件を満たせなくなる
2. 今後、IPv4はサービスレベルが低下していく
 通信事業者等によるCGN(Carrier Grade NAT)導入に
より、遅くなったり、利用できるセッション数が少なく
なったりする可能性がある
IPv6に対応しなきゃ!
- 22 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
WebサービスのIPv6対応
 ネットワークとサーバがIPv6に対応すれば、
IPv6で接続可能
 接続は可能だが…
例えば
システム連携がうまくいかない
想定外の挙動をする
…
アプリケーションのIPv6対応が不可欠!
サービスが正常に動作しない
かもしれない
- 23 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
2. サンプルコードで理解しよう
1. C ソケットサーバ/クライアント
2. PHP ソケットクライアント
3. PHP Webアクセス履歴照会&集計
- 24 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
アプリケーションIPv6対応の基本方針①
IPv6対応 =
IPv4とIPv6の両方で動作する
シングルソースコードで対応
- 25 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
アプリケーションのIPv6対応のポイント
Ethernet
IP(v4/v6)
TCP / UDP
アプリケーション
OS
ミドルウェア/
フレームワーク
アプリケーション
OS
フレームワーク
HTTP/HTTPS
SMTP, SSH,
ソケット通信など
クライアント サーバ
①IPv4/IPv6両対応の
プログラミング言語と実行環境を使う
②通信処理をIPv4/IPv6の
両方に対応させる
③データとしてIPアドレスを
扱う箇所をIPv4/IPv6の
両方に対応させる
- 26 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
ここでいうIPv4/IPv6両対応とは?
IPv4/IPv6両方のアドレスを適切に扱える
IPv4/IPv6両方で通信できる
これらを満たすプログラミング言語、実装環境を利用する
名前解決機構 格納領域
- 27 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample 1
C ソケットサーバ/クライアント
萩野純一郎(itojun)氏の著書「IPv6ネットワーク
プログラミング」に掲載されているソケット
プログラム
詳細は↓
「アプリケーションのIPv6対応ガイドライン基礎編
添付資料」IPv6普及・高度化推進協議会 IPv4/IPv6共存WG
アプリケーションのIPv6対応検討SWG
http://www.v6pc.jp/jp/upload/pdf/socket-sample-20121203.pdf
- 28 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample 1 どんなアプリ?
 クライアント
(IPv4/IPv6を問わず)任意の
ホスト、ポートに対してソケット
接続し、サーバのレスポンスを標
準出力へ出力
 サーバ
(IPv4/IPv6を問わず)任意のポートでソケット接続を待ち
受け、接続したクライアントに
“Hello: 接続元IPアドレス”
という文字列を送信
複数のソケットを生成する
デュアルスタック対応
サーバプログラム
サーバ
プロセス
IPv6
IPv4
サーバ
プログラム
起動
接続
接続
IPv6
IPv4
- 29 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
ソケットプログラミングの流れ
- 30 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
コーディングの留意点
 関数、構造体はIPv4/IPv6両対応のものを使用する
アドレス情報は addrinfo 構造体や、それを利用する
getaddrinfo() 関数や getnameinfo() 関数を使用する
 gethostbyname() 関数や getservbyname() 関数はIPv6非対応
getaddrinfo() 関数を使い、addrinfo 構造体のリストの
形で相手先ホストのIPアドレス列を取得する
ソケットアドレスは sockaddr_storage 構造体を使用す
る
- 31 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
クライアントプログラムのポイント
 フォールバック:接続できない場合に別の接続先へ
の接続に切替える動作
接続先アドレス情報をリストで取得し、順にたどる
Client
Web Server
www.example.jp
DNS Server
www.example.jp IN AAAA 2001:db8:100::1
www.example.jp IN A 192.0.2.1
①名前解決問合せ
www.example.jp ?
②AAAA応答 2001:db8:100::1
A応答 192.0.2.1
③HTTP通信(IPv6)
2001:db8:100::1
192.0.2.1
2001:db8:ffff::1
198.51.100.1
④HTTP通信(IPv4)
フォールバック
- 32 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(1)
 クライアントプログラム(抜粋)
~snip~
int
main(int argc, char **argv)
{
struct addrinfo hints, *res, *res0;
ssize_t l;
int s;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
char buf[1024];
int error;
~snip~ アドレス情報は addrinfo 構造体に
格納
- 33 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(2)
 クライアントプログラム(抜粋)その2
~snip~
/* resolve address/port into sockaddr */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(argv[1], argv[2], &hints, &res0);
if (error) {
fprintf(stderr, "%s %s: %s¥n", argv[1], argv[2],
gai_strerror(error));
exit(1);
/*NOTREACHED*/
}
getaddrinfo()で、(接続先)アドレス
情報を取得し、&res0にaddrinfo構造体
リストの先頭へのポインタが入る
- 34 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(3)
 クライアントプログラム(抜粋)その3
/* try all the sockaddrs until connection goes successful */
for (res = res0; res; res = res->ai_next) {
error = getnameinfo(res->ai_addr, res->ai_addrlen,
hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
NI_NUMERICHOST | NI_NUMERICSERV);
if (error) {
fprintf(stderr, "%s %s: %s¥n", argv[1], argv[2],
gai_strerror(error));
continue;
}
fprintf(stderr, "trying %s port %s¥n", hbuf, sbuf);
s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (s < 0)
continue;
addrinfo構造体
リストをたどる
ホスト名を取得
(逆引き)
Socket生成
- 35 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(4)
 クライアントプログラム(抜粋)その4
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
close(s);
s = -1;
continue;
}
while ((l = read(s, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, l);
close(s);
exit(0);
/*NOTREACHED*/
}
fprintf(stderr, "test: no destination to connect to¥n");
exit(1);
/*NOTREACHED*/
}
接続!
- 36 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
クライアントプログラムのポイントおさらい
 フォールバック:接続できない場合に別の接続先へ
の接続に切替える動作 ⇒アドレスはリストで取得
Client
Web Server
www.example.jp
DNS Server
www.example.jp IN AAAA 2001:db8:100::1
www.example.jp IN A 192.0.2.1
①名前解決問合せ
www.example.jp ?
②AAAA応答 2001:db8:100::1
A応答 192.0.2.1
③HTTP通信(IPv6)
2001:db8:100::1
192.0.2.1
2001:db8:ffff::1
198.51.100.1
④HTTP通信(IPv4)
フォールバック
- 37 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
ソケットサーバプログラミング
 アルゴリズムは大きく分けて3種類
手法 利点 欠点
1 IPv4対応とIPv6対応
の2プロセスを並行
動作させる
アプリケーションの構
成はシングルスタック
構成のままでデュアル
スタックに対応できる
複数プロセスで共有
リソースを扱う場合、
プロセス間で並行制
御する必要がある
2 複数のsocketを生成
するデーモンを作る
ひとつのプロセスでマ
ルチプロトコルに対応
できる
プログラムが複雑に
なる
3 inetdから呼び出し可
能なサーバにする
通信部分をinetdが代行
するため、通信のIPv6
化を意識しなくてよい
inetdを必要とする
- 38 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
複数のソケットを生成するデーモンを作る
 接続の待受を並列化する必要がある
1. 単一アドレスを指定
2. Socket生成
3. Socketをアドレスに
bind
4. ポートをlisten
5. 接続待ちループ
① 接続要求受付
② データ読み書き
③ 終了
1. アドレスリストを取得
2. 接続待ち開始ループ(アドレ
スリストを一巡)
3. 接続待ちループ
① Socket生成
② Socketをアドレスにbind
③ ポートをlisten
① 接続先ファイルディスク
リプタ(複数)選択
② 接続要求受付
③ データ読み書き
④ 終了
シングルスタック 手法2
変
更
- 39 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
複数ソケットを生成するデーモンのフロー
1アドレスずつSocketを生成
待受アドレス
リスト指定
Socket生成
bind
listen
START
接続要求
あり?
接続受付
データ読み書き
接続終了
アドレスリスト終了?
No Yes
あり なし
接続先ファイル
ディスクリプタ
(複数)選択
アドレスリストから
1アドレス抽出
- 40 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(5)
 サーバプログラム(抜粋)その1
~Snip~
#define MAXSOCK 20
int
main(int argc, char **argv)
{
struct addrinfo hints, *res, *res0;
int error;
struct sockaddr_storage from;
socklen_t fromlen;
int ls;
int s[MAXSOCK];
int smax;
int sockmax;
fd_set rfd, rfd0;
int n;
int i;
消費されるSocketの最大数を
20と定義している。
システムがサポート
するあらゆるプロト
コルをサポートする
情報保存バッファ
- 41 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(6)
 サーバプログラム(抜粋)その2
char hbuf[NI_MAXHOST];
#ifdef IPV6_V6ONLY
const int on = 1;
#endif
if (argc != 2) {
fprintf(stderr, "usage: test port¥n");
exit(1);
/*NOTREACHED*/
}
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
error = getaddrinfo(NULL, argv[1], &hints, &res0);
IPV6_V6ONLYについては後
述
全てのアドレスファ
ミリを対象とする
OSがListen可能なプロトコル・アドレスのための
addrinfoのリストが生成され、res0 に返される
- 42 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(7)
 サーバプログラム(抜粋)その3
if (error) {
fprintf(stderr, "%s: %s¥n", argv[1],
gai_strerror(error));
exit(1);
/*NOTREACHED*/
}
smax = 0;
sockmax = -1;
for (res = res0; res && smax < MAXSOCK; res = res->ai_next) {
s[smax] = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (s[smax] < 0)
continue;
得られた addrinfo すべて
について socket生成、
bind、listen を行う
- 43 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(8)
 サーバプログラム(抜粋)その4
/* avoid FD_SET overrun */
if (s[smax] >= FD_SETSIZE) {
close(s[smax]);
s[smax] = -1;
continue;
}
#ifdef IPV6_V6ONLY
if (res->ai_family == AF_INET6 &&
setsockopt(s[smax], IPPROTO_IPV6, IPV6_V6ONLY, &on,
sizeof(on)) < 0) {
perror("setsockopt(IPV6_V6ONLY)");
close(s[smax]);
s[smax] = -1;
continue;
}
#endif
- 44 -
IPV6_V6ONLYマクロが扱える環
境では、IPv4マップドアドレスに
起因するセキュリティホールを防
ぐため、ソケットのオプションに
IPV6_V6ONLY を設定する
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(9)
 サーバプログラム(抜粋)その5
if (bind(s[smax], res->ai_addr, res->ai_addrlen) < 0) {
close(s[smax]);
s[smax] = -1;
continue;
}
if (listen(s[smax], 5) < 0) {
close(s[smax]);
s[smax] = -1;
continue;
}
if (s[smax] > sockmax)
sockmax = s[smax];
smax++;
}
- 45 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(10)
 サーバプログラム(抜粋)その6
if (smax == 0) {
fprintf(stderr, "test: no socket to listen to¥n");
exit(1);
/*NOTREACHED*/
}
FD_ZERO(&rfd0);
for (i = 0; i < smax; i++)
FD_SET(s[i], &rfd0);
while (1) {
rfd = rfd0;
n = select(sockmax + 1, &rfd, NULL, NULL, NULL);
if (n < 0) {
perror("select");
exit(1);
/*NOTREACHED*/
}
Select関数で複数デスクリ
プタを待つ
- 46 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(11)
 サーバプログラム(抜粋)その7
for (i = 0; i < smax; i++) {
if (FD_ISSET(s[i], &rfd)) {
fromlen = sizeof(from);
ls = accept(s[i],
(struct sockaddr *)&from,
&fromlen);
if (ls < 0)
continue;
error = getnameinfo(
(struct sockaddr *)&from,
fromlen, hbuf, sizeof(hbuf),
NULL, 0, NI_NUMERICHOST);
if (error) {
exit(1);
/*NOTREACHED*/
}
セットされているファイル
デスクリプタを accept
getnameinfo() で、
接続元の情報を取得
- 47 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample1 コード解説(12)
 サーバプログラム(抜粋)その8
write(ls, "hello ", 6);
write(ls, hbuf, strlen(hbuf));
write(ls, "¥n", 1);
close(ls);
}
}
}
/*NOTREACHED*/
}
接続されたファイルデスク
リプタに write()
- 48 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample 2
ソケットクライアントWebアプリ
(PHP)
- 49 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 どんなアプリ?
 フォームから入力されたホスト、ポートに対してソ
ケット接続を行い、サーバ側の出力をそのまま
Webページに出力
- 50 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
コーディングの留意点
 関数、データ型はIPv4/IPv6両対応のものを使用
する
データ型:文字列型
関数:
 get_dns_record()
– gethostbyname() はIPv6非対応
 gethostbyaddr()
 ライブラリ、フィルタを用いて入力値検証、変換
ライブラリ:Net_IPv6
フィルタ:FILTER_VALIDATE_IP
- 51 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 処理フロー
- 52 -
ユーザ入力値から
接続先アドレス(リスト)を
取得
接続先アドレス(リスト)に、
順にソケットを生成して接続
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 コード解説(1)
<?php
$IS_DEBUG = 0;
$host = filter_input(INPUT_GET, 'host');
$port = filter_input(INPUT_GET, 'port', FILTER_VALIDATE_INT);
if ($host && $port){
$addresses = array();
if ($host_addr = filter_var($host, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV6)){
$addresses[0]['domain'] = AF_INET6;
$addresses[0]['address'] = $host_addr;
} elseif ($host_addr = filter_var($host, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4)){
$addresses[0]['domain'] = AF_INET;
$addresses[0]['address'] = $host_addr;
 フィルタを用いて変数がIPv6/IPv4 アドレスか判断
 アドレスからプロトコルに応じたプロトコルファミリを設定
- 53 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 コード解説(2)
} else {
$host_list = dns_get_record($host);
$size = sizeof($host_list);
for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){
if ($host_list[$loopcnt]['type'] === 'AAAA'){
$addresses[$loopcnt]['domain'] = AF_INET6;
$addresses[$loopcnt]['address']
= $host_list[$loopcnt]['ipv6'];
} else {
$addresses[$loopcnt]['domain'] = AF_INET;
$addresses[$loopcnt]['address']
= $host_list[$loopcnt]['ip'];
}
}
}
$size = sizeof($addresses);
$message = "接続先ホスト名 " . $host . " ポート番号 " . $port .
"<BR>¥n";
ホスト名の場合にはDNSか
らアドレスをリストで取得
 リストの数だけ、アドレスを取得し接続先候補とする
 IPv6 は AAAA レコード、IPv4 は A レコードに格納
gethostbyname()
は、IPv6非対応
- 54 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 コード解説(3)
$connect_flag = 0;
for ($loopcnt = 0; $loopcnt < $size && $connect_flag === 0;
$loopcnt++){
if (($socket = socket_create($addresses[$loopcnt]['domain'],
SOCK_STREAM, SOL_TCP)) === FALSE){
$error_code = socket_last_error();
$error_msg = socket_strerror($error_code);
$message .= "connect to " . $addresses[$loopcnt]['address'] .
"<BR>¥n";
$message .= 'socket create error: [' . $error_code . '] ' .
$error_msg . "<BR>¥n";
} else {
$message .= 'socket connect (' . ($loopcnt +1) . ') : ' .
$addresses[$loopcnt]['address'] . " port: " . $port . "<BR>¥n";
- 55 -
ソケット作る
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 コード解説(4)
if (socket_connect($socket, $addresses[$loopcnt]['address'],
$port)){
$connect_flag = 1;
$response = socket_read($socket, 1024);
$message .= "サーバからのメッセージ:" . '<div style="margin:
10px">' . $response . '</div>' . "<BR>¥n";
} else {
$error_code = socket_last_error();
$error_msg = socket_strerror($error_code);
$message .= 'socket connect error: [' . $error_code . '] ' .
$error_msg . "<BR>¥n";
}
socket_close($socket);
}
}
} else {
$message = "接続先ホスト名 " . $host . " もしくはポート番号 " . $port .
"が入力されていません";
}
?> - 56 -
接続する
切断する
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 コード解説(5)
<html>
<head>
<meta charset="UTF-8">
<title>Socket通信クライアント(デュアルスタック版)</title>
</head>
<body>
<H1>Socket通信クライアント(デュアルスタック版)</H1>
<form action="<?php echo filter_input(INPUT_SERVER, 'PHP_SELF',
FILTER_SANITIZE_URL)?>" method="GET">
接続先ホスト <input type='text' name='host' value='<?php echo
$host; ?>'>
ポート番号 <input type='text' name='port'value='<?php echo
$port; ?>'>
<input type="submit" value="実行する">
</form>
<HR>
<?php echo $message; ?>
</body>
</html>
- 57 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3
アクセス履歴表示・集計Webアプリ
(PHP)
- 58 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
サンプルアプリ3:アクセス履歴照会&集計
(PHP)
- 59 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
データとしてIPアドレスを扱う箇所
入力出力
整列
検索
格納
- 60 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Webフォームからの入力
 入力値の検証
IPアドレスを扱う場合、入力された文字列がIPアドレス
として取りうる値であることを検証
 IPv4アドレス、IPv6アドレス いずれかとして取りうる値
2箇所で実施可能
 ブラウザ側(HTML5のForm Validation等)
 サーバ側
アドレス処理ライブラリ、フィルタを利用すると便利
 例)PHP Net_IPv6::checkIPv6();
(PEARにて提供されるNet_IPv6パッケージに含まれる)
- 61 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
格納、検索、整列、出力のポイント
 IPアドレス型が定義されている場合は、IPアドレス型
を使う
例) PostgreSQLのネットワークアドレス型
 IPアドレス型が定義されていない場合は、文字列型で
完全表記を使う
IPv6完全表記)
2001:0db8:0000:0000:0001:0000:0000:0001
見やすさを求めるときは、省略表記(RFC5952準拠)で出力
 過去に開発されたシステム・ツールでは、RFC5952に準拠しない省
略表記が存在しうるので要注意
省略表記で扱うと、整列、検索時に不具合が出やすい
 既存システムは、格納領域にIPv6アドレスが収まるか
をチェック
- 62 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
PostgreSQLのネットワークアドレス型
- 63 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
コーディングの留意点
 関数、データ型はIPv4/IPv6両対応のものを使用
する
データ型:文字列型
関数:
 get_dns_record()
– gethostbyname() はIPv6非対応
 gethostbyaddr()
 ライブラリ、フィルタを用いて入力値検証、変換
ライブラリ:Net_IPv6
フィルタ:FILTER_VALIDATE_IP
- 64 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample2 処理フロー
 履歴書込み  履歴一覧表示・集計
- 65 -
接続元アドレスを取得
DBにレコード追加
(INSERT)
格納先のDBのデータ型が
文字列の場合は、省略表
記を完全表記に展開
ユーザ入力値から整列
キー、昇順/降順、集計
フラグを取得
ユーザ入力値に従い、DB
から履歴を読み込み、昇
順/降順、集計して表示
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(1)
<?php
require_once 'settings.php';
require_once 'modules.php';
$now = date('Y/m/d H:i:s');
$array_access = array (
'source_addr' => filter_input(INPUT_SERVER, 'REMOTE_ADDR',
FILTER_VALIDATE_IP),
'source_port' => filter_input(INPUT_SERVER, 'REMOTE_PORT',
FILTER_VALIDATE_INT),
'server_addr' => filter_input(INPUT_SERVER, 'SERVER_ADDR',
FILTER_VALIDATE_IP),
'access_time' => $now,
);
$logging = write_history($array_access);
index.php
filter_input() でユーザ入力
値がIPアドレス形式である
ことを検証
関数化したアクセス履歴
書込みを呼び出し
- 66 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(2)
$sort_mode = array (
'key' => filter_input(INPUT_GET, 'sort_key', FILTER_VALIDATE_REGEXP,
array('options' => array('regexp' =>
'/(^access_date$|^source_addr$|^source_port$|^count$)/'))),
'desc' => filter_input(INPUT_GET, 'desc', FILTER_VALIDATE_REGEXP,
array('options' => array('regexp' => '/(^desc$|^asc$)/'))),
'count' => filter_input(INPUT_GET, 'count', FILTER_VALIDATE_INT,
array('options' => array('min_range' => 0, 'max_range' => 1))),
);
if (!$sort_mode['key']){
$sort_mode['key'] = 'access_date';
}
$history = display_history($sort_mode);
?>
~以下、HTML部分につき割愛~
index.php
関数化したアクセス履歴
表示を呼び出し
- 67 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(3)
<?php
// //////////////////////////
// 履歴書込み関数
function write_history ($array_access) {
global $DSN;
// ==========================================
// DBのデータ型がvarcharの場合には、省略表記を完全表記に展開
// ==========================================
if (constant('STORE_TYPE') !== 'INET') {
require_once 'Net/IPv6.php';
if (Net_IPv6::checkIPv6($array_access['source_addr'])) {
$source_addr = Net_IPv6::uncompress($array_access['source_addr'],
TRUE);
} else {
$source_addr = $array_access['source_addr'];
}
}
modules.php
Net_IPv6::checkIPv6() で
変数がIPv6アドレスである
ことを検証
省略表記を完全表記
に展開(第2引数を
TRUEにすることで
完全表記)
STORE_TYPE は、この
プログラムで独自に定義
- 68 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(4)
$query = 'INSERT INTO access_history ( access_date, source_addr,
source_port) VALUES (now(), :ip, :port)';
if ($dbh = new PDO($DSN)) {
$sth = $dbh->prepare($query);
$sth->execute(array(':ip' => $array_access['source_addr'],
':port' => $array_access['source_port']));
$err_code = $sth->errorCode();
if ($err_code === '00000'){
return OK;
} else {
return WRITE_ERROR;
}
} else {
echo "DB connection error";
return OPEN_ERROR;
}
}
modules.php
- 69 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(5)
// /////////////////////////
// 履歴表示&アクセス数集計関数
function display_history ($sort_mode) {
global $DSN;
if ($sort_mode['count']){
// 集計時のクエリ作成
$query = 'SELECT source_addr, count(source_addr) FROM access_history
GROUP BY source_addr';
if ($sort_mode['key'] === 'source_addr'
|| $sort_mode['key'] === 'count' ){
$query .= ' ORDER BY ' . $sort_mode['key'];
if ($sort_mode['desc']) {
$query .= ' ' . $sort_mode['desc'];
}
}
modules.php
整列時の指定は、
通常のSQL(ORDER
BY キー)
- 70 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(6)
} else {
// 履歴一覧時のクエリー作成
$query = 'SELECT * FROM access_history';
if ($sort_mode['key'] && $sort_mode['key'] !== 'count' ){
$query .= ' ORDER BY ' . $sort_mode['key'];
if ($sort_mode['desc']) {
$query .= ' ' . $sort_mode['desc'];
}
$query .= ' NULLS LAST';
}
}
modules.php
整列時の指定は、
通常のSQL(ORDER
BY キー)
- 71 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(7)
// =================================================
// DB接続&クエリ実行
// =================================================
$dbh = new PDO($DSN);
if ($dbh) {
$sth = $dbh->prepare($query);
$sth->execute();
$result = $sth->fetchAll();
$sth->errorCode();
} else {
echo "DB connection error";
}
modules.php
- 72 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(8)
// =================================================
// 出力整形
// =================================================
if (constant('STORE_TYPE') !== 'INET') {
// 文字列で格納されている場合は、省略表記にするためにライブラリを呼び
出す
require_once 'Net/IPv6.php';
}
if ($sort_mode['count']) {
$ret_string = '<H2>アクセス集計</H2><TABLE
border="1"><TR><TH>No.</TH><TH>接続元アドレス</TH><TH>接続回数
</TH></TR>';
$size = sizeof($result);
for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){
modules.php
- 73 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(9)
if (constant('STORE_TYPE') !== 'INET') {
// 文字列で格納されている場合は、省略表記にする
if (Net_IPv6::checkIPv6($result[$loopcnt]['source_addr'])){
$source_addr =
Net_IPv6::compress($result[$loopcnt]['source_addr']);
} else {
$source_addr = $result[$loopcnt]['source_addr'];
}
} else {
$source_addr = $result[$loopcnt]['source_addr'];
}
$ret_string .= "<TR><TD align='right'>" . ($loopcnt +1) .
"</TD><TD>"
. $source_addr . "</TD><TD align='right'>"
. $result[$loopcnt]['count'] . "</TD></TR>¥n";
}
$ret_string .= '</TABLE>';
modules.php
完全表記を
省略表記に
変換
(見やすさ
重視)
- 74 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(10)
} else {
$ret_string = '<H2>アクセス履歴</H2><TABLE
border="1"><TR><TH>No.</TH><TH>接続日時</TH><TH>接続元アドレス</TH><TH>接
続元ポート番号</TH></TR>';
$size = sizeof($result);
for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){
if (constant('STORE_TYPE') !== 'INET') {
// 文字列で格納されている場合は、省略表記にする
if (Net_IPv6::checkIPv6($result[$loopcnt]['source_addr'])){
$source_addr =
Net_IPv6::compress($result[$loopcnt]['source_addr']);
} else {
$source_addr = $result[$loopcnt]['source_addr'];
}
} else {
$source_addr = $result[$loopcnt]['source_addr'];
}
modules.php
完全表記を
省略表記に
変換
(見やすさ
重視)
- 75 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
Sample3 コード解説(10)
$ret_string .= "<TR><TD align='right'>" . ($loopcnt +1)
. "</TD><TD>"
. $result[$loopcnt]['access_date'] . "</TD><TD>"
. $source_addr . "</TD><TD align='right'>"
. $result[$loopcnt]['source_port'] . "</TD></TR>¥n";
}
$ret_string .= '</TABLE>';
}
return $ret_string;
}
?>
modules.php
- 76 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
おわりに
- 77 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
まとめ①
 IPv6を使える環境が増えている
 IPv4とIPv6は互換性なし
 WebサービスのIPv6対応にはアプリケーションの
対応が不可欠
 IPアドレスのハードコーディングは
ダメ。ゼッタイ。
- 78 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
まとめ②
 アプリケーションのIPv6対応の基本方針
IPv6対応=IPv6/IPv4の両方で動作させること
シングルソースコードで対応する
 アプリケーションのIPv6対応のポイント
1. IPv4/IPv6両対応のプログラミング言語と実行環境を使う
2. 通信処理をIPv4/IPv6の両方に対応させる
3. データとしてIPアドレスを扱う箇所をIPv4/IPv6の両方に
対応させる
決して難しくない!
今日から開発するアプリケーションはIPv6に対応させよう!
- 79 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
つづきはWebで(参考文献)
 「アプリケーションのIPv6対応ガイドライン 基礎
編」/IPv6普及・高度化推進協議会 IPv4/IPv6共
存WG アプリケーションのIPv6対応検討SWG
http://www.v6pc.jp/jp/entry/wg/2012/12/ipv610.p
html
 「アプリケーションのIPv6対応ガイドライン Web
アプリ編(案)」/IPv6普及・高度化推進協議会
IPv4/IPv6共存WG アプリケーションのIPv6対応
検討SWG
http://www.v6pc.jp/jp/entry/wg/2014/06/ipv6web
.phtml
- 80 -
Copyright ©2015 FUJISOFT INCORPORATED, All rights reserved.
ご清聴いただき、
ありがとうございました
- 81 -

20150228 OSC2015 Tokyo/Spring サンプルコードで理解するアプリケーションのIPv6対応

  • 1.
  • 2.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. About me  渡辺 露文 (わたなべ つゆふみ) 富士ソフト株式会社 技術本部技術開発部 ネットワークエキスパート 業務経歴  1999年 富士ソフトABC株式会社(現 富士ソフト株式会社)入社  入社後、ISP、データセンター顧客向けシステムなどのシステム開 発・インフラ構築・運用、社内システムのインフラ企画・構築・運 用に従事  2011年~ 技術調査および社内技術者教育に従事、2014年~ OSS 社内利用管理も兼務 主な社外活動  IPv6普及・高度化推進協議会 – IPv4/IPv6共存WG アプリケーションのIPv6対応検討SWG – IPv4/IPv6共存WG IPv6導入に起因する問題検討SWG  技術評論社 Software Design にて「IPv6化の道も一歩から」連載 (2012年12月号~2014年1月号;共同執筆) < twatanab@fsi.co.jp > - 1 -
  • 3.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Do you know … IPv6 ? - 2 -
  • 4.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Do you know … IPv6 ? Internet Protocol version 6 インターネットの通信に関する規約(RFC791) IPネットワークに接続するには1つ以上のIPアドレスが必要 皆さんが馴染んでいるのはIPv4(例:10.1.2.3) - 3 -
  • 5.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. こんなコード書いてないですよね?  このコード、イケてない… use IO::Socket::IP; $host = “198.51.100.1”; : my $sock = IO::Socket::IP->new( PeerAddr => $host, PeerPort => $port, Proto => 'tcp' ) or die “Error: $!¥n”; : - 4 -
  • 6.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 他にもイケてないコードがある  とある Androidプログラミング書籍に おけるソケット通信のサンプルコード public class SocketEx… … … private final static String IP=“192.168.11.12”;//★変更必須 - 5 -
  • 7.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 他にもイケてないコードがある  とある Androidプログラミング書籍に おけるソケット通信のサンプルコード public class SocketEx… … … private final static String IP=“192.168.11.12”;//★変更必須 良い子はマネしちゃダメ - 6 -
  • 8.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv6対応の話をする前に… IPアドレスのハードコーディングはNG ダメ。ゼッタイ。 $host = “www.example.com” のようにFQDNで接続先を指定する IPアドレス直書きすると、 アドレス変更時に  修正が必要  再テストも必要 - 7 -
  • 9.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Internet ネットワークアクセスの作法=名前解決を使う  Webアクセスの例 Client Web Server www.example.jp 192.0.2.1 権威DNS Server 198.51.100.53 ①名前解決問合せ www.example.jp ? ②アドレス応答 www.example.jp ⇒ 192.0.2.1 ③HTTP通信 FQDN FQDNで接続先を指定し、DNSからアドレス取得 - 8 -
  • 10.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. なぜIPアドレス直書きがダメなのか? アプリケーションは、IPアドレスに依存すべきではない 目的 変更・改修の理由 アプリケーション 機能の 提供  業務要件の変更  サービス内容の変更  ユーザビリティ向上 …,etc. インフラ 資源の 提供  資源管理(IPアドレス、サーバリソース …)  性能 互いに変更の影響を受けるべきではない 同一システムでも変更・改修の理由・時期は異なる 例)IPアドレスでユーザを識別すべきではない - 9 -
  • 11.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. いまどきのIPv6を知ろう! - 10 -
  • 12.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 実はIPv6を使える環境が増えています①  最近のOS Windows Vista以降 Mac OS X Linux FreeBSD … いずれも デフォルトで利用可能 - 11 -
  • 13.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 実はIPv6を使える環境が増えています②  インターネット回線 フレッツ光ネクスト au ひかり NURO 光 … 利用可能 既存ユーザへの 自動導入も進行中 すでに、ユーザからあなたのサービスにIPv6で アクセスされようとしている…かもしれない - 12 -
  • 14.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 日本におけるIPv6の普及状況  フレッツ光ネクストのIPv6普及率(2014年9月) IPv6普及率:3.9% フレッツ光ネクスト契約数:15,805,000  au ひかりのIPv6普及率(2014年9月):99% 今後本格的に普及する前に IPv6対応を始めたほうが良い 出典:IPv6普及・高度化推進協議会 アクセス網におけるIPv6普及状況調査 http://v6pc.jp/jp/spread/ipv6spread_03.phtml - 13 -
  • 15.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 余談:IPv6でインターネットにアクセスできるかの確認方法  Webブラウザで http://www.test-ipv6.jp にアクセス  Webブラウザで http://www.kame.net にアクセス IPv6でアクセスすると、 亀が踊ります♪ - 14 -
  • 16.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv6の背景:IPv4アドレス枯渇  IPv4アドレスの在庫状況 (地域インターネットレジストリ) 通信事業者、ISP、 データセンター、 クラウド事業者等の 在庫が残るのみ 世界的に足りなくなってきている - 15 -
  • 17.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. もう少しIPv6を知ろう! - 16 -
  • 18.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv4とIPv6とでは何が違うのか?(1)  アドレス体系が異なる(IPv6のアドレス空間は広大) 例  IPv4)192.0.2.1  IPv6完全表記)2001:0db8:0000:0000:0001:0000:0000:0001  IPv6省略表記)2001:db8::1:0:0:1 (RFC5952準拠) IPv4アドレス IPv6アドレス アドレス長 32bit 128bit 文字列 表記 表記法 8bitずつ区切り、 10進数で表記 16bitずつ区切り、 16進数で表記 区切り文字 . (ドット) : (コロン) 文字列長 15文字以内 39文字以内 - 17 -
  • 19.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. グローバルスコープ IPv4とIPv6とでは何が違うのか?(2) リンクローカルスコープ リンクローカルアドレス fe80::/10 ユニークローカルアドレス fc00::/7 グローバルユニキャストアドレス 2000::/3 IPv6では1つのNICに複数のアドレスを 有効範囲に応じて割当て、使い分ける - 18 -
  • 20.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv4とIPv6の関係(1)  先ほど紹介したものの他にも機能的にIPv4と異な ることがある IPv4とIPv6は互換性がない - 19 -
  • 21.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv4とIPv6の接続性 Internet IPv4対応 (IPv6非対応) システム IPv4/IPv6 両対応システム IPv6対応 (IPv4非対応) システム (1) (2) (3) IPv4端末 IPv4/IPv6 両対応端末 IPv6端末 IPv4 IPv6 - 20 -
  • 22.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv4とIPv6の関係(2)  両方に対応している環境では(デフォルトで) IPv6 > IPv4 - 21 -
  • 23.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. IPv6に対応しない場合の影響 1. IPv6のみの環境と通信できない  ビジネス機会を損失する  システム連携が行えず要件を満たせなくなる 2. 今後、IPv4はサービスレベルが低下していく  通信事業者等によるCGN(Carrier Grade NAT)導入に より、遅くなったり、利用できるセッション数が少なく なったりする可能性がある IPv6に対応しなきゃ! - 22 -
  • 24.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. WebサービスのIPv6対応  ネットワークとサーバがIPv6に対応すれば、 IPv6で接続可能  接続は可能だが… 例えば システム連携がうまくいかない 想定外の挙動をする … アプリケーションのIPv6対応が不可欠! サービスが正常に動作しない かもしれない - 23 -
  • 25.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 2. サンプルコードで理解しよう 1. C ソケットサーバ/クライアント 2. PHP ソケットクライアント 3. PHP Webアクセス履歴照会&集計 - 24 -
  • 26.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. アプリケーションIPv6対応の基本方針① IPv6対応 = IPv4とIPv6の両方で動作する シングルソースコードで対応 - 25 -
  • 27.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. アプリケーションのIPv6対応のポイント Ethernet IP(v4/v6) TCP / UDP アプリケーション OS ミドルウェア/ フレームワーク アプリケーション OS フレームワーク HTTP/HTTPS SMTP, SSH, ソケット通信など クライアント サーバ ①IPv4/IPv6両対応の プログラミング言語と実行環境を使う ②通信処理をIPv4/IPv6の 両方に対応させる ③データとしてIPアドレスを 扱う箇所をIPv4/IPv6の 両方に対応させる - 26 -
  • 28.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. ここでいうIPv4/IPv6両対応とは? IPv4/IPv6両方のアドレスを適切に扱える IPv4/IPv6両方で通信できる これらを満たすプログラミング言語、実装環境を利用する 名前解決機構 格納領域 - 27 -
  • 29.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample 1 C ソケットサーバ/クライアント 萩野純一郎(itojun)氏の著書「IPv6ネットワーク プログラミング」に掲載されているソケット プログラム 詳細は↓ 「アプリケーションのIPv6対応ガイドライン基礎編 添付資料」IPv6普及・高度化推進協議会 IPv4/IPv6共存WG アプリケーションのIPv6対応検討SWG http://www.v6pc.jp/jp/upload/pdf/socket-sample-20121203.pdf - 28 -
  • 30.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample 1 どんなアプリ?  クライアント (IPv4/IPv6を問わず)任意の ホスト、ポートに対してソケット 接続し、サーバのレスポンスを標 準出力へ出力  サーバ (IPv4/IPv6を問わず)任意のポートでソケット接続を待ち 受け、接続したクライアントに “Hello: 接続元IPアドレス” という文字列を送信 複数のソケットを生成する デュアルスタック対応 サーバプログラム サーバ プロセス IPv6 IPv4 サーバ プログラム 起動 接続 接続 IPv6 IPv4 - 29 -
  • 31.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. ソケットプログラミングの流れ - 30 -
  • 32.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. コーディングの留意点  関数、構造体はIPv4/IPv6両対応のものを使用する アドレス情報は addrinfo 構造体や、それを利用する getaddrinfo() 関数や getnameinfo() 関数を使用する  gethostbyname() 関数や getservbyname() 関数はIPv6非対応 getaddrinfo() 関数を使い、addrinfo 構造体のリストの 形で相手先ホストのIPアドレス列を取得する ソケットアドレスは sockaddr_storage 構造体を使用す る - 31 -
  • 33.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. クライアントプログラムのポイント  フォールバック:接続できない場合に別の接続先へ の接続に切替える動作 接続先アドレス情報をリストで取得し、順にたどる Client Web Server www.example.jp DNS Server www.example.jp IN AAAA 2001:db8:100::1 www.example.jp IN A 192.0.2.1 ①名前解決問合せ www.example.jp ? ②AAAA応答 2001:db8:100::1 A応答 192.0.2.1 ③HTTP通信(IPv6) 2001:db8:100::1 192.0.2.1 2001:db8:ffff::1 198.51.100.1 ④HTTP通信(IPv4) フォールバック - 32 -
  • 34.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(1)  クライアントプログラム(抜粋) ~snip~ int main(int argc, char **argv) { struct addrinfo hints, *res, *res0; ssize_t l; int s; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char buf[1024]; int error; ~snip~ アドレス情報は addrinfo 構造体に 格納 - 33 -
  • 35.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(2)  クライアントプログラム(抜粋)その2 ~snip~ /* resolve address/port into sockaddr */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(argv[1], argv[2], &hints, &res0); if (error) { fprintf(stderr, "%s %s: %s¥n", argv[1], argv[2], gai_strerror(error)); exit(1); /*NOTREACHED*/ } getaddrinfo()で、(接続先)アドレス 情報を取得し、&res0にaddrinfo構造体 リストの先頭へのポインタが入る - 34 -
  • 36.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(3)  クライアントプログラム(抜粋)その3 /* try all the sockaddrs until connection goes successful */ for (res = res0; res; res = res->ai_next) { error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (error) { fprintf(stderr, "%s %s: %s¥n", argv[1], argv[2], gai_strerror(error)); continue; } fprintf(stderr, "trying %s port %s¥n", hbuf, sbuf); s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) continue; addrinfo構造体 リストをたどる ホスト名を取得 (逆引き) Socket生成 - 35 -
  • 37.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(4)  クライアントプログラム(抜粋)その4 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { close(s); s = -1; continue; } while ((l = read(s, buf, sizeof(buf))) > 0) write(STDOUT_FILENO, buf, l); close(s); exit(0); /*NOTREACHED*/ } fprintf(stderr, "test: no destination to connect to¥n"); exit(1); /*NOTREACHED*/ } 接続! - 36 -
  • 38.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. クライアントプログラムのポイントおさらい  フォールバック:接続できない場合に別の接続先へ の接続に切替える動作 ⇒アドレスはリストで取得 Client Web Server www.example.jp DNS Server www.example.jp IN AAAA 2001:db8:100::1 www.example.jp IN A 192.0.2.1 ①名前解決問合せ www.example.jp ? ②AAAA応答 2001:db8:100::1 A応答 192.0.2.1 ③HTTP通信(IPv6) 2001:db8:100::1 192.0.2.1 2001:db8:ffff::1 198.51.100.1 ④HTTP通信(IPv4) フォールバック - 37 -
  • 39.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. ソケットサーバプログラミング  アルゴリズムは大きく分けて3種類 手法 利点 欠点 1 IPv4対応とIPv6対応 の2プロセスを並行 動作させる アプリケーションの構 成はシングルスタック 構成のままでデュアル スタックに対応できる 複数プロセスで共有 リソースを扱う場合、 プロセス間で並行制 御する必要がある 2 複数のsocketを生成 するデーモンを作る ひとつのプロセスでマ ルチプロトコルに対応 できる プログラムが複雑に なる 3 inetdから呼び出し可 能なサーバにする 通信部分をinetdが代行 するため、通信のIPv6 化を意識しなくてよい inetdを必要とする - 38 -
  • 40.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 複数のソケットを生成するデーモンを作る  接続の待受を並列化する必要がある 1. 単一アドレスを指定 2. Socket生成 3. Socketをアドレスに bind 4. ポートをlisten 5. 接続待ちループ ① 接続要求受付 ② データ読み書き ③ 終了 1. アドレスリストを取得 2. 接続待ち開始ループ(アドレ スリストを一巡) 3. 接続待ちループ ① Socket生成 ② Socketをアドレスにbind ③ ポートをlisten ① 接続先ファイルディスク リプタ(複数)選択 ② 接続要求受付 ③ データ読み書き ④ 終了 シングルスタック 手法2 変 更 - 39 -
  • 41.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 複数ソケットを生成するデーモンのフロー 1アドレスずつSocketを生成 待受アドレス リスト指定 Socket生成 bind listen START 接続要求 あり? 接続受付 データ読み書き 接続終了 アドレスリスト終了? No Yes あり なし 接続先ファイル ディスクリプタ (複数)選択 アドレスリストから 1アドレス抽出 - 40 -
  • 42.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(5)  サーバプログラム(抜粋)その1 ~Snip~ #define MAXSOCK 20 int main(int argc, char **argv) { struct addrinfo hints, *res, *res0; int error; struct sockaddr_storage from; socklen_t fromlen; int ls; int s[MAXSOCK]; int smax; int sockmax; fd_set rfd, rfd0; int n; int i; 消費されるSocketの最大数を 20と定義している。 システムがサポート するあらゆるプロト コルをサポートする 情報保存バッファ - 41 -
  • 43.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(6)  サーバプログラム(抜粋)その2 char hbuf[NI_MAXHOST]; #ifdef IPV6_V6ONLY const int on = 1; #endif if (argc != 2) { fprintf(stderr, "usage: test port¥n"); exit(1); /*NOTREACHED*/ } memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; error = getaddrinfo(NULL, argv[1], &hints, &res0); IPV6_V6ONLYについては後 述 全てのアドレスファ ミリを対象とする OSがListen可能なプロトコル・アドレスのための addrinfoのリストが生成され、res0 に返される - 42 -
  • 44.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(7)  サーバプログラム(抜粋)その3 if (error) { fprintf(stderr, "%s: %s¥n", argv[1], gai_strerror(error)); exit(1); /*NOTREACHED*/ } smax = 0; sockmax = -1; for (res = res0; res && smax < MAXSOCK; res = res->ai_next) { s[smax] = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s[smax] < 0) continue; 得られた addrinfo すべて について socket生成、 bind、listen を行う - 43 -
  • 45.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(8)  サーバプログラム(抜粋)その4 /* avoid FD_SET overrun */ if (s[smax] >= FD_SETSIZE) { close(s[smax]); s[smax] = -1; continue; } #ifdef IPV6_V6ONLY if (res->ai_family == AF_INET6 && setsockopt(s[smax], IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { perror("setsockopt(IPV6_V6ONLY)"); close(s[smax]); s[smax] = -1; continue; } #endif - 44 - IPV6_V6ONLYマクロが扱える環 境では、IPv4マップドアドレスに 起因するセキュリティホールを防 ぐため、ソケットのオプションに IPV6_V6ONLY を設定する
  • 46.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(9)  サーバプログラム(抜粋)その5 if (bind(s[smax], res->ai_addr, res->ai_addrlen) < 0) { close(s[smax]); s[smax] = -1; continue; } if (listen(s[smax], 5) < 0) { close(s[smax]); s[smax] = -1; continue; } if (s[smax] > sockmax) sockmax = s[smax]; smax++; } - 45 -
  • 47.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(10)  サーバプログラム(抜粋)その6 if (smax == 0) { fprintf(stderr, "test: no socket to listen to¥n"); exit(1); /*NOTREACHED*/ } FD_ZERO(&rfd0); for (i = 0; i < smax; i++) FD_SET(s[i], &rfd0); while (1) { rfd = rfd0; n = select(sockmax + 1, &rfd, NULL, NULL, NULL); if (n < 0) { perror("select"); exit(1); /*NOTREACHED*/ } Select関数で複数デスクリ プタを待つ - 46 -
  • 48.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(11)  サーバプログラム(抜粋)その7 for (i = 0; i < smax; i++) { if (FD_ISSET(s[i], &rfd)) { fromlen = sizeof(from); ls = accept(s[i], (struct sockaddr *)&from, &fromlen); if (ls < 0) continue; error = getnameinfo( (struct sockaddr *)&from, fromlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); if (error) { exit(1); /*NOTREACHED*/ } セットされているファイル デスクリプタを accept getnameinfo() で、 接続元の情報を取得 - 47 -
  • 49.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample1 コード解説(12)  サーバプログラム(抜粋)その8 write(ls, "hello ", 6); write(ls, hbuf, strlen(hbuf)); write(ls, "¥n", 1); close(ls); } } } /*NOTREACHED*/ } 接続されたファイルデスク リプタに write() - 48 -
  • 50.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample 2 ソケットクライアントWebアプリ (PHP) - 49 -
  • 51.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 どんなアプリ?  フォームから入力されたホスト、ポートに対してソ ケット接続を行い、サーバ側の出力をそのまま Webページに出力 - 50 -
  • 52.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. コーディングの留意点  関数、データ型はIPv4/IPv6両対応のものを使用 する データ型:文字列型 関数:  get_dns_record() – gethostbyname() はIPv6非対応  gethostbyaddr()  ライブラリ、フィルタを用いて入力値検証、変換 ライブラリ:Net_IPv6 フィルタ:FILTER_VALIDATE_IP - 51 -
  • 53.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 処理フロー - 52 - ユーザ入力値から 接続先アドレス(リスト)を 取得 接続先アドレス(リスト)に、 順にソケットを生成して接続
  • 54.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 コード解説(1) <?php $IS_DEBUG = 0; $host = filter_input(INPUT_GET, 'host'); $port = filter_input(INPUT_GET, 'port', FILTER_VALIDATE_INT); if ($host && $port){ $addresses = array(); if ($host_addr = filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){ $addresses[0]['domain'] = AF_INET6; $addresses[0]['address'] = $host_addr; } elseif ($host_addr = filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){ $addresses[0]['domain'] = AF_INET; $addresses[0]['address'] = $host_addr;  フィルタを用いて変数がIPv6/IPv4 アドレスか判断  アドレスからプロトコルに応じたプロトコルファミリを設定 - 53 -
  • 55.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 コード解説(2) } else { $host_list = dns_get_record($host); $size = sizeof($host_list); for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){ if ($host_list[$loopcnt]['type'] === 'AAAA'){ $addresses[$loopcnt]['domain'] = AF_INET6; $addresses[$loopcnt]['address'] = $host_list[$loopcnt]['ipv6']; } else { $addresses[$loopcnt]['domain'] = AF_INET; $addresses[$loopcnt]['address'] = $host_list[$loopcnt]['ip']; } } } $size = sizeof($addresses); $message = "接続先ホスト名 " . $host . " ポート番号 " . $port . "<BR>¥n"; ホスト名の場合にはDNSか らアドレスをリストで取得  リストの数だけ、アドレスを取得し接続先候補とする  IPv6 は AAAA レコード、IPv4 は A レコードに格納 gethostbyname() は、IPv6非対応 - 54 -
  • 56.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 コード解説(3) $connect_flag = 0; for ($loopcnt = 0; $loopcnt < $size && $connect_flag === 0; $loopcnt++){ if (($socket = socket_create($addresses[$loopcnt]['domain'], SOCK_STREAM, SOL_TCP)) === FALSE){ $error_code = socket_last_error(); $error_msg = socket_strerror($error_code); $message .= "connect to " . $addresses[$loopcnt]['address'] . "<BR>¥n"; $message .= 'socket create error: [' . $error_code . '] ' . $error_msg . "<BR>¥n"; } else { $message .= 'socket connect (' . ($loopcnt +1) . ') : ' . $addresses[$loopcnt]['address'] . " port: " . $port . "<BR>¥n"; - 55 - ソケット作る
  • 57.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 コード解説(4) if (socket_connect($socket, $addresses[$loopcnt]['address'], $port)){ $connect_flag = 1; $response = socket_read($socket, 1024); $message .= "サーバからのメッセージ:" . '<div style="margin: 10px">' . $response . '</div>' . "<BR>¥n"; } else { $error_code = socket_last_error(); $error_msg = socket_strerror($error_code); $message .= 'socket connect error: [' . $error_code . '] ' . $error_msg . "<BR>¥n"; } socket_close($socket); } } } else { $message = "接続先ホスト名 " . $host . " もしくはポート番号 " . $port . "が入力されていません"; } ?> - 56 - 接続する 切断する
  • 58.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 コード解説(5) <html> <head> <meta charset="UTF-8"> <title>Socket通信クライアント(デュアルスタック版)</title> </head> <body> <H1>Socket通信クライアント(デュアルスタック版)</H1> <form action="<?php echo filter_input(INPUT_SERVER, 'PHP_SELF', FILTER_SANITIZE_URL)?>" method="GET"> 接続先ホスト <input type='text' name='host' value='<?php echo $host; ?>'> ポート番号 <input type='text' name='port'value='<?php echo $port; ?>'> <input type="submit" value="実行する"> </form> <HR> <?php echo $message; ?> </body> </html> - 57 -
  • 59.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 アクセス履歴表示・集計Webアプリ (PHP) - 58 -
  • 60.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. サンプルアプリ3:アクセス履歴照会&集計 (PHP) - 59 -
  • 61.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. データとしてIPアドレスを扱う箇所 入力出力 整列 検索 格納 - 60 -
  • 62.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Webフォームからの入力  入力値の検証 IPアドレスを扱う場合、入力された文字列がIPアドレス として取りうる値であることを検証  IPv4アドレス、IPv6アドレス いずれかとして取りうる値 2箇所で実施可能  ブラウザ側(HTML5のForm Validation等)  サーバ側 アドレス処理ライブラリ、フィルタを利用すると便利  例)PHP Net_IPv6::checkIPv6(); (PEARにて提供されるNet_IPv6パッケージに含まれる) - 61 -
  • 63.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. 格納、検索、整列、出力のポイント  IPアドレス型が定義されている場合は、IPアドレス型 を使う 例) PostgreSQLのネットワークアドレス型  IPアドレス型が定義されていない場合は、文字列型で 完全表記を使う IPv6完全表記) 2001:0db8:0000:0000:0001:0000:0000:0001 見やすさを求めるときは、省略表記(RFC5952準拠)で出力  過去に開発されたシステム・ツールでは、RFC5952に準拠しない省 略表記が存在しうるので要注意 省略表記で扱うと、整列、検索時に不具合が出やすい  既存システムは、格納領域にIPv6アドレスが収まるか をチェック - 62 -
  • 64.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. PostgreSQLのネットワークアドレス型 - 63 -
  • 65.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. コーディングの留意点  関数、データ型はIPv4/IPv6両対応のものを使用 する データ型:文字列型 関数:  get_dns_record() – gethostbyname() はIPv6非対応  gethostbyaddr()  ライブラリ、フィルタを用いて入力値検証、変換 ライブラリ:Net_IPv6 フィルタ:FILTER_VALIDATE_IP - 64 -
  • 66.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample2 処理フロー  履歴書込み  履歴一覧表示・集計 - 65 - 接続元アドレスを取得 DBにレコード追加 (INSERT) 格納先のDBのデータ型が 文字列の場合は、省略表 記を完全表記に展開 ユーザ入力値から整列 キー、昇順/降順、集計 フラグを取得 ユーザ入力値に従い、DB から履歴を読み込み、昇 順/降順、集計して表示
  • 67.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(1) <?php require_once 'settings.php'; require_once 'modules.php'; $now = date('Y/m/d H:i:s'); $array_access = array ( 'source_addr' => filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP), 'source_port' => filter_input(INPUT_SERVER, 'REMOTE_PORT', FILTER_VALIDATE_INT), 'server_addr' => filter_input(INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP), 'access_time' => $now, ); $logging = write_history($array_access); index.php filter_input() でユーザ入力 値がIPアドレス形式である ことを検証 関数化したアクセス履歴 書込みを呼び出し - 66 -
  • 68.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(2) $sort_mode = array ( 'key' => filter_input(INPUT_GET, 'sort_key', FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/(^access_date$|^source_addr$|^source_port$|^count$)/'))), 'desc' => filter_input(INPUT_GET, 'desc', FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/(^desc$|^asc$)/'))), 'count' => filter_input(INPUT_GET, 'count', FILTER_VALIDATE_INT, array('options' => array('min_range' => 0, 'max_range' => 1))), ); if (!$sort_mode['key']){ $sort_mode['key'] = 'access_date'; } $history = display_history($sort_mode); ?> ~以下、HTML部分につき割愛~ index.php 関数化したアクセス履歴 表示を呼び出し - 67 -
  • 69.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(3) <?php // ////////////////////////// // 履歴書込み関数 function write_history ($array_access) { global $DSN; // ========================================== // DBのデータ型がvarcharの場合には、省略表記を完全表記に展開 // ========================================== if (constant('STORE_TYPE') !== 'INET') { require_once 'Net/IPv6.php'; if (Net_IPv6::checkIPv6($array_access['source_addr'])) { $source_addr = Net_IPv6::uncompress($array_access['source_addr'], TRUE); } else { $source_addr = $array_access['source_addr']; } } modules.php Net_IPv6::checkIPv6() で 変数がIPv6アドレスである ことを検証 省略表記を完全表記 に展開(第2引数を TRUEにすることで 完全表記) STORE_TYPE は、この プログラムで独自に定義 - 68 -
  • 70.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(4) $query = 'INSERT INTO access_history ( access_date, source_addr, source_port) VALUES (now(), :ip, :port)'; if ($dbh = new PDO($DSN)) { $sth = $dbh->prepare($query); $sth->execute(array(':ip' => $array_access['source_addr'], ':port' => $array_access['source_port'])); $err_code = $sth->errorCode(); if ($err_code === '00000'){ return OK; } else { return WRITE_ERROR; } } else { echo "DB connection error"; return OPEN_ERROR; } } modules.php - 69 -
  • 71.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(5) // ///////////////////////// // 履歴表示&アクセス数集計関数 function display_history ($sort_mode) { global $DSN; if ($sort_mode['count']){ // 集計時のクエリ作成 $query = 'SELECT source_addr, count(source_addr) FROM access_history GROUP BY source_addr'; if ($sort_mode['key'] === 'source_addr' || $sort_mode['key'] === 'count' ){ $query .= ' ORDER BY ' . $sort_mode['key']; if ($sort_mode['desc']) { $query .= ' ' . $sort_mode['desc']; } } modules.php 整列時の指定は、 通常のSQL(ORDER BY キー) - 70 -
  • 72.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(6) } else { // 履歴一覧時のクエリー作成 $query = 'SELECT * FROM access_history'; if ($sort_mode['key'] && $sort_mode['key'] !== 'count' ){ $query .= ' ORDER BY ' . $sort_mode['key']; if ($sort_mode['desc']) { $query .= ' ' . $sort_mode['desc']; } $query .= ' NULLS LAST'; } } modules.php 整列時の指定は、 通常のSQL(ORDER BY キー) - 71 -
  • 73.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(7) // ================================================= // DB接続&クエリ実行 // ================================================= $dbh = new PDO($DSN); if ($dbh) { $sth = $dbh->prepare($query); $sth->execute(); $result = $sth->fetchAll(); $sth->errorCode(); } else { echo "DB connection error"; } modules.php - 72 -
  • 74.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(8) // ================================================= // 出力整形 // ================================================= if (constant('STORE_TYPE') !== 'INET') { // 文字列で格納されている場合は、省略表記にするためにライブラリを呼び 出す require_once 'Net/IPv6.php'; } if ($sort_mode['count']) { $ret_string = '<H2>アクセス集計</H2><TABLE border="1"><TR><TH>No.</TH><TH>接続元アドレス</TH><TH>接続回数 </TH></TR>'; $size = sizeof($result); for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){ modules.php - 73 -
  • 75.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(9) if (constant('STORE_TYPE') !== 'INET') { // 文字列で格納されている場合は、省略表記にする if (Net_IPv6::checkIPv6($result[$loopcnt]['source_addr'])){ $source_addr = Net_IPv6::compress($result[$loopcnt]['source_addr']); } else { $source_addr = $result[$loopcnt]['source_addr']; } } else { $source_addr = $result[$loopcnt]['source_addr']; } $ret_string .= "<TR><TD align='right'>" . ($loopcnt +1) . "</TD><TD>" . $source_addr . "</TD><TD align='right'>" . $result[$loopcnt]['count'] . "</TD></TR>¥n"; } $ret_string .= '</TABLE>'; modules.php 完全表記を 省略表記に 変換 (見やすさ 重視) - 74 -
  • 76.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(10) } else { $ret_string = '<H2>アクセス履歴</H2><TABLE border="1"><TR><TH>No.</TH><TH>接続日時</TH><TH>接続元アドレス</TH><TH>接 続元ポート番号</TH></TR>'; $size = sizeof($result); for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){ if (constant('STORE_TYPE') !== 'INET') { // 文字列で格納されている場合は、省略表記にする if (Net_IPv6::checkIPv6($result[$loopcnt]['source_addr'])){ $source_addr = Net_IPv6::compress($result[$loopcnt]['source_addr']); } else { $source_addr = $result[$loopcnt]['source_addr']; } } else { $source_addr = $result[$loopcnt]['source_addr']; } modules.php 完全表記を 省略表記に 変換 (見やすさ 重視) - 75 -
  • 77.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. Sample3 コード解説(10) $ret_string .= "<TR><TD align='right'>" . ($loopcnt +1) . "</TD><TD>" . $result[$loopcnt]['access_date'] . "</TD><TD>" . $source_addr . "</TD><TD align='right'>" . $result[$loopcnt]['source_port'] . "</TD></TR>¥n"; } $ret_string .= '</TABLE>'; } return $ret_string; } ?> modules.php - 76 -
  • 78.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. おわりに - 77 -
  • 79.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. まとめ①  IPv6を使える環境が増えている  IPv4とIPv6は互換性なし  WebサービスのIPv6対応にはアプリケーションの 対応が不可欠  IPアドレスのハードコーディングは ダメ。ゼッタイ。 - 78 -
  • 80.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. まとめ②  アプリケーションのIPv6対応の基本方針 IPv6対応=IPv6/IPv4の両方で動作させること シングルソースコードで対応する  アプリケーションのIPv6対応のポイント 1. IPv4/IPv6両対応のプログラミング言語と実行環境を使う 2. 通信処理をIPv4/IPv6の両方に対応させる 3. データとしてIPアドレスを扱う箇所をIPv4/IPv6の両方に 対応させる 決して難しくない! 今日から開発するアプリケーションはIPv6に対応させよう! - 79 -
  • 81.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. つづきはWebで(参考文献)  「アプリケーションのIPv6対応ガイドライン 基礎 編」/IPv6普及・高度化推進協議会 IPv4/IPv6共 存WG アプリケーションのIPv6対応検討SWG http://www.v6pc.jp/jp/entry/wg/2012/12/ipv610.p html  「アプリケーションのIPv6対応ガイドライン Web アプリ編(案)」/IPv6普及・高度化推進協議会 IPv4/IPv6共存WG アプリケーションのIPv6対応 検討SWG http://www.v6pc.jp/jp/entry/wg/2014/06/ipv6web .phtml - 80 -
  • 82.
    Copyright ©2015 FUJISOFTINCORPORATED, All rights reserved. ご清聴いただき、 ありがとうございました - 81 -