ネットワークの基礎となる TCP/IP を理解するために、私はソケットプログラミングによる試行錯誤(Trial and Error)とネットワークトレースのパケット解析を避けては通れないと思っています。
本稿では、ソケットプログラミングをシミュレートする iOS アプリ SocTest を紹介します。
SocTest とは
Developer site より抜粋:
SocTest は、カーネルに対してシステムコールを発行し、インターネットや組織内のイントラネット上の様々なサーバーとソケット通信を行うモバイルアプリです。
SocTest のネットワーク操作は POSIX C API で実装されています。高度に抽象化された API を挟まず、必要以上にエラー判定せず、可能なかぎりカーネルに処理を委ねるように設計されています。
アプリは、App Store で無料で公開されています。
SocTest の使い方
具体例として、Wi-Fi 接続した状態で Safari ブラウザで ifconfig.io へアクセスする操作をシミュレートしてみます。
[参考]
ifconfig.io とは、接続元 IP アドレスを返してくれる WEB サービスです。
この場合、Safari で http://ifconfig.io/ip にアクセスすると、ifconfig.io が接続元であるグローバル IP アドレス「106.171.xx.97」を返します。
使用例①
Safari で実施しているソケット操作は、図に示したシステムコールを順番に発行していると考えられます。
では、SocTest アプリを起動して実際にシミュレートしてみます。
1-1. (事前準備) Address タブで ifconfig.io のアドレスを作成します
1-2. (事前準備) Data タブで HTTP リクエストのデータをバイナリエディタで作成します
HTTP の規約で、末尾は改行コード CRLF が2つ連続している必要があります。
GET /ip HTTP/1.1
Host: ifconfig.io
User-Agent: Mozilla/5.0 (SocTest; iOS 14.4)
1-3. Socket タブでソケットを作成し、ifconfig.io に接続します
1-4. HTTP リクエストを送信します
1-5. HTTP レスポンスを受信します
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2021 07:08:28 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 14
Connection: keep-alive
Set-Cookie: __cfduid=d526f31d8fba79e413589646c00846ad81612505125; expires=Sun, 07-Mar-21 07:08:28 GMT; path=/; domain=.ifconfig.io; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
cf-request-id: 0811f0af920000f8a7b5a18000000001
Report-To: {"max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=dRvaw9S5bWhoae65VS1Spu6gT0I7LC4gdroUsse1iHMn%2By5yKxRkaJFyT%2F8zspLQP4sG64nvciiLQstZ15ILuG0bs204eSI5A%3D%3D"}],"group":"cf-nel"}
NEL: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 61c9b6f8efcaf8a7-NRT
alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
106.171.xx.97
Safari と同様の結果「106.171.xx.97」が得られたことが確認できましたね。
1-6. (後始末) ソケットをクローズします
1-7. (おまけ) ソケットシステムコールの発行内容を確認します
SocTest には、Linux の strace 相当のシステムコールトレース機能が搭載されています。
上記手順でどのようなシステムコールが発行されたのか確認できます。
16:08:18.446 Feb 05, 2021 - Asia/Tokyo (current)
16:08:18.446 Start (pid:2674)
16:08:20.899 [0.000098] socket(PF_INET, SOCK_STREAM, 0) = 3
16:08:24.676 [0.017037] connect(3, {sin_family=AF_INET, sin_port=80, sin_addr="104.21.65.79"}, 16) = 0
16:08:28.611 [0.000234] send(3, "GET /ip HTTP/1.1."..., 84, 0) = 84
16:08:35.069 [0.000066] recv(3, "HTTP/1.1 200 OK.."..., 4096, 0) = 787
16:08:45.045 [0.000150] close(3) = 0
使用例②
iOS では、モバイル通信と Wi-Fi が同時に接続されている状態では、Wi-Fi が優先して使用されます。
今度は、この状態であえてモバイル通信側(4G回線)を使用して ifconfig.io にアクセスできるか試してみます。
では、SocTest アプリを起動して実際にシミュレートしてみます。
2-1. (任意) アプリの動作設定で AUTO MONITORING を有効化します
このパラメータを有効にすると、着信データの有無や接続状態を監視するモードで動作します。
今回は、このモードを使ってみます。
2-2. モバイル通信のインターフェース(自分自身)のアドレスを確認して、ソケットに割り当てます
[参考]
ここでは、未使用の 60000 という適当なポート番号を指定しました。
0 を指定すると接続(connect)時に、カーネルによって自動的に採番されます。
2-3. ソケットを接続し、HTTP リクエスト/レスポンスの送受信を行います
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2021 07:59:24 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 15
Connection: keep-alive
Set-Cookie: __cfduid=de405e0a328b95a506f1099afe86f66cc1612508698; expires=Sun, 07-Mar-21 07:59:24 GMT; path=/; domain=.ifconfig.io; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
cf-request-id: 08129b30e60000d635e4917000000001
Report-To: {"max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=G%2FxZA%2FLfDXM9dmMBsjZvSZ81B2MvsomKic0bDswtuWFZ3SNKEkBIKrAModd2oaTqM2vG4GD35c%2Bbfw2%2BdzWTQet8s4Vk%2BeCS6V4SDA%3D%3D"}],"group":"cf-nel"}
NEL: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 61cac7c7d8aad635-NRT
alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
153.147.xx.155
ifconfig.io によって、モバイル通信のアドレス「153.147.xx.155」が返されました。
つまり、Wi-Fi ではなく、モバイル通信側のネットワークが使用されたことが確認できました。
さいごに
SocTest では、TCP / UDP(ICMP も対応予定)のソケットをサポートしています。
60 個のソケットオプションを使用でき、Keep-Alive や Broadcast などのテストもできたりします。
sendmsg/recvmsg システムコールにより制御メッセージを使用した細かな制御もできたりします。
ネットワークに興味がある方、「ソケットプログラミングは敷居が高い」と敬遠されている方は、SocTest でソケット通信を体験してみてください!
おわり。