1 ポイント 投稿者 GN⁺ 2024-07-10 | 1件のコメント | WhatsAppで共有
  • PySkyWiFiは、決済前でも開ける航空マイレージアカウントのプロフィール欄を一時ストレージとして使い、長距離フライト中にHTTPリクエストとレスポンスを非常に低速で往復させる実験
  • 初期プロトタイプは、アカウントの名前フィールドを更新・ポーリングしてチャットを作り、その後、地上デーモンがSTOCKPRICE: APPLのようなリクエストを読み取って結果を返す方式へ拡張された
  • 最終構成は、機内のsky proxyとインターネット接続されたground daemonに分かれ、ローカルのcurlリクエストのX-PySkyWiFiヘッダーで対象URLを渡す
  • 転送はHTTPリクエストを小さなチャンクに分割し、DATAACKENDセグメントと6桁のシーケンス番号で順序を合わせる方式
  • 実際の航空会社アカウントに自動リクエストを送り続ける方式は避け、最終テストはGitHub Gistでブログのホームページをcurlで呼び出し、数分後にHTMLレスポンスを受け取るレベルにとどまった

決済前のWi-Fiポータルで見つけた迂回経路

  • 機内Wi-Fiのログインページは決済を要求したが、決済前でも航空マイレージアカウントへのログインは無料で許可されていた
  • プロフィール編集画面の名前フィールドを見て、この値を外部とやり取りする小さな通信チャネルのように使えると考えた
  • 実装言語は最初Goを考えたが、Pythonを使えばツール名をPySkyWiFiにできるためPythonを選んだ
  • ソースコードはPySkyWiFiで公開されている

プロトタイプ1: 名前フィールドで作ったインスタントメッセージング

  • 同じ航空マイレージアカウントに2つのクライアントがログインし、名前フィールドを交互に更新して読み取ることでメッセージをやり取りする
    • 一方が名前をHello how are youに変えると、もう一方がそれを読む
    • 次にIm fine how are youへ変更すると、最初の側が返信を確認する
  • UIで直接名前を変える方式は面倒なので、コマンドラインツールで自動化した
    • ツールはWebサイトにログインし、テストアカウントの名前フィールドをメッセージで更新する
    • 数秒おきに名前フィールドをポーリングし、値が変われば相手の返信として表示する
  • 実際の航空マイレージアカウントにはこれ以上自動データを送らないことにし、名前を高速で約10回更新してすべて成功するかだけ確認した
    • この結果は、そのアカウントがリクエスト速度や回数に制限をかけていない可能性を示している
  • その後の開発は、GitHub Gistsとローカルファイルで同じ原理を再現して進めた
    • 航空マイレージアカウントよりも反復開発が速く簡単だったため

プロトタイプ2: 地上デーモンで外部情報を取得する

  • 次の段階では、地上またはクラウドのインターネット接続されたコンピュータでデーモンを実行する
  • デーモンは航空マイレージアカウントの名前フィールドを継続的にポーリングし、機内から送られた構造化リクエストを探す
    • 例: STOCKPRICE: APPL
    • 例: SCORE: MANUNITED
  • 新しいリクエストを見つけると、デーモンがそれをパースし、関連APIから情報を取得したうえで、航空マイレージアカウント経由で機内側へ返す
  • この段階から、単なるチャットを超えて株価やスポーツのスコアのようなリアルタイム情報の取得が可能になった

sky proxyとground daemonで構成した全体アーキテクチャ

  • PySkyWiFiはHTTPリクエスト全体を航空マイレージアカウントに押し込み、地上コンピュータが実際のHTTPリクエストを実行した後、レスポンスを同じ方法で返送するTCP/IP風プロトコルである
  • 構成要素は2つ
    • sky proxy: 機内のノートPCで動作するプロキシ
    • ground daemon: 自宅またはクラウドのインターネット接続されたコンピュータで動作するデーモン
  • 利用前にまず地上デーモンを起動し、機内でWi-Fiに接続したあとノートPCでsky proxyを起動する
  • ユーザーはcurlのようなツールでローカルプロキシにHTTPリクエストを送る
    • 例: curl localhost:1234 -H "X-PySkyWiFi: example.com"
    • 実際の対象URLはX-PySkyWiFiカスタムヘッダーに入れる
  • 地上デーモンはX-PySkyWiFiヘッダーを取り除き、それを対象Webサイトへのルーティングに使う
    • リクエストボディと他のヘッダーはそのまま転送する
  • すべての処理が終わると通常のインターネットリクエストのようにHTTPレスポンスを受け取れるが、機内ネットワークの状態によってはリクエストが数分間停止し、速度も毎秒数バイト程度になることがある

チャンク、ACK、第2アカウントでHTTPを往復させる

  • sky proxyはcurlリクエストを受け取り、HTTPリクエスト全体をチャンクに分割する
    • 航空マイレージアカウントの名前フィールドに一度に入れられる量が少ないため
  • 各チャンクは最初の航空マイレージアカウントの名前フィールドに順番に書き込まれる
  • ground daemonはアカウントをポーリングし、新しいチャンクを見つけるとそれを読み取り、次のチャンクを送ってよいという受信確認を残す
  • HTTPリクエスト全体が再構成されると、ground daemonがインターネットへ実際のリクエストを送る
  • HTTPレスポンスは逆方向に同じ手順をたどる
    • ground daemonがレスポンスをチャンクに分割して送る
    • 実装を単純にするため、レスポンスチャンクには第2の航空マイレージアカウントを使う
    • sky proxyがチャンクを読み取ってHTTPレスポンスを再構成し、元のcurl呼び出しに返す

トランスポート層とネットワーク層の役割分離

  • PySkyWiFiの通信ロジックはトランスポート層ネットワーク層に分かれている
  • トランスポート層は、どのデータをどの順序で送るかを決める
    • 長いメッセージを小さなチャンクに分ける方法
    • 受信側が次のチャンクを受け取る準備ができたことを知らせる方法
    • TCPに非常に緩く似た役割
  • ネットワーク層は、トランスポート層が決めたデータを実際のストレージに書き込み、読み取る
    • 航空マイレージアカウント、ローカルファイル、Discordプロフィールのようなストレージになりうる
  • この分離により、新しい航空会社のマイレージプラットフォームを接続しやすくなる
    • 新しいネットワーク層だけ実装すれば、トランスポート層はそのまま維持できる
    • テスト用ネットワーク層は航空マイレージアカウントの代わりにローカルファイルを読み書きできる

DATA、ACK、ENDで揃える転送ルール

  • PySkyWiFiの転送接続は2本のパイプで構成される
    • 各クライアントは書き込み用のSENDパイプと読み取り用のRECVパイプを持つ
    • 航空マイレージアカウントをパイプとして使える
  • PSWFクライアントは特定の瞬間に送信または受信のどちらか一方しかできない
    • 送信モードでは相手が送ったデータを見られない
    • 受信モードではデータを送っても相手には見えない
    • TCPのようにいつでも双方向送受信する方式ではない
  • 長いメッセージはDATAACKENDセグメントで送る
    • DATA: 文字D、6桁ゼロ埋めのシーケンス番号、実際のチャンクデータで構成される
    • 例: D000451adline": "Mudslide in Wigan causes m
    • ACK: 文字Aと確認対象の6桁シーケンス番号で構成される
    • 例: A000451
    • END: 文字E1つでメッセージ終了を示す
  • 送信側はDATAを送ったあと、そのシーケンス番号のACKを受け取るまで待つ
    • A000451を受け取ると、000451チャンクが処理されたとみなし、000452チャンクを送る
  • 受信側がENDを見るとメッセージ終了として扱い、送信側と受信側が役割を入れ替える

ネットワーク層実装インターフェース

  • ネットワーク層はデータをストレージに書き込み、読み取るシンプルなインターフェースを提供する
  • 必須操作は2つ
    • send(msg: str): msgをストレージに記録する
    • recv() -> str: ストレージからメッセージを読む
  • 航空マイレージベースの実装では、この2つの操作はアカウントの名前フィールドを書き読みする動作になる
  • 必須属性も2つ
    • sleep_for: RECVパイプをポーリングする間にトランスポート層が待機する秒単位の時間
    • segment_data_size: 1セグメントに入れられる文字数
  • sleep_forはファイルのようなテスト実装では低くできるが、航空マイレージアカウント実装ではリモートサーバーに過剰なリクエストを送らないよう最低でも数秒にすべき
  • segment_data_sizeは使用する航空マイレージアカウントのフィールド最大サイズと同じである必要があり、例では約20文字
  • 任意フックは2つ
    • connect_send(): SENDパイプ初期化時に呼ばれ、航空マイレージ実装ではユーザー名とパスワードでログインしてCookieを取得するために使える
    • connect_recv(): RECVパイプ初期化時に呼ばれる

base26エンコーディングと帯域幅改善のアイデア

  • 航空マイレージのHTMLフォームは、名前にアルファベット以外の文字を許可しない可能性がある
    • Stephenは許可されるかもしれない
    • GET /data?id=5は拒否されるかもしれない
  • これを避けるには、ネットワーク層がセグメントを航空マイレージアカウントへ書き込む前にbase26でエンコードすればよい
    • base26は文字列をAからZまでの文字だけで表現する
    • バイト列を大きな数値に変換し、その後26進数のようにAZで表現する
    • 例: b26_encode("Hello world")CZEZINADXFFTZEIDPKMを返す
  • トランスポート層はこのエンコーディングを知る必要がない
    • ネットワーク層がバイト列をbase26に変換して記録し、読み取るときに再びバイト列へ戻す
  • base26は文字列をかなり長くしてしまい、帯域幅を下げる
    • 大文字と小文字の両方を使うbase52を使えばより短くできるかもしれないが、これはバージョン2の改善項目として残した
  • 帯域幅はセグメントサイズを大きくすることで増やせる
    • 1フィールドに20文字しか保存できなくても、20文字フィールドを5個同時に使えば、1リクエストで100文字セグメントを送れる
    • 受信側は5個のフィールドを1リクエストで読み取り、再結合できる

HTTP CONNECTが残した改善余地と最終テスト

  • PySkyWiFiは手動でHTTPリクエストをやり取りする代わりに、HTTP CONNECT requestsを使えばより良いトンネルを作れる
  • CONNECTは大半のHTTPプロキシが使う方式で、これを使えばPySkyWiFiはシステムレベルのプロキシのように動作し、ブラウザのリクエストも処理できる
  • CONNECTを使うと、PySkyWiFiが対象Webサイトと直接TLS接続をネゴシエートするため、航空マイレージアカウントを通過するトラフィックは暗号化される
  • 欠点は実装作業がはるかに増えること
  • 最終テストではGitHub Gistを通じてデータをトンネリングし、curlでブログのホームページを読み込み、数分後にHTMLレスポンスを受け取った

1件のコメント

 
GN⁺ 2024-07-10
Hacker News の意見
  • 何十年も前、配偶者が Google Voice のSMSをGmailの受信トレイから直接読んで返信する形で使っていたが、携帯電話が嫌いで、無制限3G付きの古い Kindle Keyboard を持ち歩いていた。
    Kindleの簡易ブラウザで低スペック向けGmail画面を開けたので、月額料金のないSMS端末のように使えた。問題は新着SMS通知だけだった。
    そこで脱獄してホーム画面にカウンターを付けようとしたが、Amazonアカウントで変更できるKindle端末名の横にカウンターを置けばよいと思いついた。
    自宅サーバーからブラウザ自動化でAmazonにログインし、端末名を My Kindle (x) に変えさせた。xは未読のGoogle Voice SMS数で、Kindleは1分以内にホーム画面の名前を更新し、数年間うまく動いた。

    • 3G Kindleの AT&T の請求額はものすごかったと記憶している。
      数バイト削るだけで100万ドルくらい節約できるレベルだったと思う。
    • 旅行に行く同僚にKindle Keyboardを貸したら、メキシコで盗まれた。
      当時はほぼ最古級の動作するKindleだったはずなので、泥棒は単にバッグの中にあったものを持っていっただけだと思う。
      その後フリーマーケットで20ドルのKindle Keyboardをまた見つけたが、6カ月後にバッテリーが死んだ。まだ本体は持っているので、交換バッテリーの値段が気になる。
    • あの 無料3G は本当にすごかった。
      ブラウザでGmailをかろうじて使えたし、複数のzmachine ROMから選んでテキストゲームを遊べるKindle向けのZorkサイトも作った。
      いくつかのニュースサイトで紹介され、ある程度反響もあった。
    • 自分もこれをやっていた。
      Kindle名を変えるような賢い統合はなかったが、無制限3G付きのKeyboard KindleでしばらくSMSを送っていた。
    • スマートフォンを買う前は、長距離ドライブで Kindle Keyboardを使って道案内していた。
      Google MapsのWebサイトをかろうじて動かせた。
  • 以前、イタリアからギリシャへ向かう約20時間のフェリーに乗ったとき、衛星インターネットベースの有料Wi-Fiがあったが、支払う気にはならなかった。
    決済のためにStripeへのアクセスは開いていて、調べてみるとstripe.comの開発者ドキュメントなど全ページにアクセスできたので、画像を繰り返しダウンロードして帯域を浪費し始めた。
    そのうち、Stripeが動作するには Fastly CDN へのアクセスが必要で、RedditもFastlyを使っていることを思い出し、Stripeに接続したあとHTTPの Host ヘッダーを reddit.com に変えるとRedditを見られた。
    画像は i.redd.it がFastlyではないので無理だったが、/etc/hostsold.reddit.com をStripeのFastly IPに向け、恐ろしいTLSエラーを無視するとログインもできた。

    • そのようにドキュメントを数GBもダウンロードして被害を与える相手は、そのサービスにお金を払って使っている 機内の乗客たちである可能性が高い。
      接続が遅くなって二度と買わなくなれば提供会社も被害を受けるし、Stripeのアップロード帯域コストも増やす可能性がある。
      私は周囲の人に悪影響を与えないよう、セルラー回線でもダウンロードを避けるほうだ。
    • そういう行動で正確に何を得るのか分からない。
      ブロックされた接続でどこまでアクセスできるか探る知的実験は理解できるが、わざと帯域を浪費するのは、公衆トイレの蛇口を開けっぱなしにして水を捨てたり、便器を詰まらせてあふれさせたりするのに近い。
    • それは ドメインフロンティング(domain fronting) で、今ではすべての大手CDNでブロックされている。
      https://en.wikipedia.org/wiki/Domain_fronting
    • 次は有料サービスを厚かましく提供したという理由でホテルの水道を出しっぱなしにして、教訓を与えたんだろうね。
    • なぜ、お金を払いたくないサービスに対する最初の本能が 機能を悪用したり低下させたりする方向だったのか気になる。
  • 数週間前、ショッピングモールで子どもたちを教室に送っていき、無料Wi-Fiを使おうとしたところ、ノートPCのログインは成功したがインターネットは使えないと表示された。
    デフォルトゲートウェイを確認するとCiscoモデムにつながっており、診断ツールと接続機器一覧が見えたが、インターネット接続はオフになっていた。
    携帯電話でそのモデルを検索すると、管理者アカウントはシリアル番号で、パスワードは空欄にする方式だと書かれており、例のパターンもあった。接続機器のうち1つの名前がまさにそのパターンのように見えた。
    そのままログインして インターネットボタンをオンにすると、15秒後に緑色になり、機器のパスワードも新しく設定した。

    • 家族を訪ねたとき、隣人の保護されていないWi-Fiに接続して会社のVPNに入ろうとしたことがある。
      VPNがつながらなかったので、ルーターに簡単に入れたことから MTU設定を変更したところ、すぐ正常に動いた。
  • こういう ハッキング は本来の意味そのままで格好よく、好きだ。
    ただ最近確認したところ、skywifiではグローバルDNSは支払いなしでも普通に名前解決できるようで、自分が乗った飛行機では単なる一般的な iodineトンネル も動いたのではないかと思う。
    https://github.com/yarrick/iodine

    • 数年前、誰でも接続できる公開 iodineサービス を運用していた。
      悪用報告を避けるため、サーバーの外向きトラフィックはTorを経由させ、どの列車・ホテル・空港ネットワークで動作するかのログも集め、他のトンネリングプロトコルも提供していた。
      しばらくは面白いプロジェクトで、動かし続けておけばよかった。
      最近は一部のDNSサーバー、特にGoogleがDNSリクエストの大文字小文字をランダム化して特定のエンコーディングをひどく壊すため、DNSトンネリングは少し難しくなっている。
    • 6年前、別の航空会社のWi-Fiで iodine が動作した。
      アイデアと原理は面白いが、速度のせいで実戦ではほとんど使い物にならない。UDPの上にメッセンジャー程度を載せるなら別かもしれないが ;-)
    • ファイアウォールでDNSが許可されているなら、DNSプロトコル自体はいじらずに WireGuardをUDP/53 で使えばいいのでは?
  • 「ヘッドホンの充電を忘れて、Limp Bizkit がノートPCのスピーカーから流れ始めた。幸い、機内のほかの人たちは気にしていないようだったので、みんなで楽しく盛り上がった。」
    こういう人たちは基本的な公共のマナーが欠けている
    周囲の多くの人は間違いなく気にしていただろうが、余計な騒音を押しつけるなと言うには礼儀正しすぎる人たちだった可能性が高い

    • 著者が「これは風刺です」と書いたら、風刺が台無しになる
    • 私はジョークとして読んだ
      こういう基本マナーのない人があまりに多いので笑える冗談だと思ったし、少なくとも著者が実際にそういう人ではないだろう
    • ジョークとして面白くはなかったが、記事のテーマには合っている
      無料インターネットの可能性を実行可能なチャンスとして見るハッカー像を示しつつ、後半では実際に航空会社のシステムを悪用する人間ではないという形で距離を置いているので、冒頭のトーンは文体上の仕掛けに見える
    • 人間たちが「ユーモア」と呼んでいるものらしい
    • ほかの人が言うように、冗談であってほしい
      そうでなければ、ものすごく無礼で冷淡な行為だ
  • 飛行機に乗るといつも疲れてしまい、目を閉じて終わるまで待つ以外に何もできないのは自分だけなのか気になる
    ノートPCや本を取り出すことを考えるだけで疲れるし、騒音も多く、ノイズキャンセリングヘッドホンもないので集中しづらい
    家を出てから電車・トラム・保安検査を経て少なくとも2時間は過ごした状態で、空港の不健康な食べ物も助けにならない
    天気も半年は暑すぎるか寒すぎるし、10kgのバックパックのせいでずっと汗をかいて、結局機内では何かをする気分が残らない

    • 成人のADHDがあるが、職場では集中するのがより難しい一方、飛行機やほかの雑然とした場所ではむしろレーザーのように集中できる
      直感に反するが、多くのADHD脳はこう動くと聞いたことがあり、飛行機で過ごす時間が一番生産的だ
    • 以前はノートPCを取り出して何かしようとしていたが、シートピッチが狭くなってからはタブレットより大きい物を出すのが本当に難しくなった
      膝の上に載せるしかなかったり、前の人が座席を倒すと画面が割れる危険があり、数年前に同僚が実際にそうなった
      最近はただ iPad とノイズキャンセリングヘッドホンを取り出して、映画やテレビ番組を見る
    • 自分だけではない
      自分の体もまったく同じ反応をするし、空気中の酸素量の低下と関係があると思う
      騒音の問題ではなく空気の問題で、ノイズキャンセリングヘッドホンは本当に効果が高い
      ただ、まったく影響を受けずに飛行機で有用な仕事をする人たちもいる
    • 私は正反対だ
      どれほど疲れていても、睡眠不足でも、飛行機では眠れもしないし疲れもしないので、大西洋横断便がさらに苦痛で、同行者が8時間ずつ眠っているとからかわれているような気分にすらなる
    • 自分だけではない
      飛行中は頭がひどくぼんやりして、着陸するまで目を閉じているし、本を読んだり仕事をしようとしても5〜10分後には脳がシャットダウンしてしまう
      不思議なことに、機内食は毎回お腹の張りを起こすので、着陸直後に薬が必要になる
      興味深いことに、ビジネスクラスに2回乗ったときは、脳がシャットダウンする前にある程度読書できた
      長距離列車では進行方向を向いて座っていると実際に仕事がしやすいが、列車がかなり混んでくるとまた脳がシャットダウンする
  • 株価、試合のスコア、天気のようなものを送ってくれる話を見て、以前 Google がテキストメッセージサービスで提供していた機能を思い出した
    初めてスマートフォンを買う前に本当によく使っていて、W[ZIP code] を 46645(GOOGL) に送ると天気をSMSで返してくれた
    stock:[symbol] も使えたし、今では忘れてしまったいろいろな機能があった
    もちろん Google は終了したが、続いている間はかなり素晴らしかった

    • PHPを触っていた頃、歌詞の一部から曲を探してくれる SongBuddy というTwitterボットを作った
      当時Twitterが対応していたSMS DMで歌詞を数行送ると、Googleで " lyrics" を検索し、結果をパースしてアーティスト名と曲名をSMSで返す仕組みだった
      うまく動いたわけではないが、かなり誇らしかった
    • 私が使っている為替換算サイトは教授が1人で運営していて、おそらく大学の階段下に隠してある Pentium III のコンピュータで動いているはずなのに、何十年も生き続けていて、一生続きそうだ
    • Twilio と AWS インスタンスで SMS↔ChatGPT インターフェースを作った
      おかげで携帯型衛星通信機 inReach Mini 2 から地球上のどこでも ChatGPT に質問でき、Death Valley のど真ん中でも聞けるのはかなり良い
    • フィーチャーフォンでそのサービスをいつも使っていた
      Google の検索語を入力するのに時間がかかり、結果が届くまで1〜2分かかることもあったが、ないよりはましだった
      主に行きたい場所の住所を検索して TomTom に入力するのに使っていて、時代は大きく変わった
    • それがいつのことだったのか気になる
      1995年ごろ、Ericsson の子会社である通信研究会社で働いていたときにこうしたアイデアを思いつき、代表例はフライト状況の更新だった
      数字キーパッドで1文字を入力するには何度も押さなければならないP2Pメッセージングがそこまで人気になるとは誰も想像していなかったので、初期のWebでデータが増えていく状況では、情報サービスが最も良い活用法に見えた
      Teletext もまだ存在していて、それをスクレイピングする別プロジェクトもあった
      だが、SMSにアクセスできた私たちと一般の人々が結局それを何に使ったのかを考えると興味深い
  • 著者が抽象的な共有編集フィールド上のTCPにかなり近いところまで行っている点が興味深い
    それ自体でも非常にクールなツールになり得る
    両側で共有リソースに読み書きする方法だけを高レベルに設計すれば、残りはすべて処理して SOCKS プロキシとして提供するプロキシを想像できる

    • 有料の Boingo 空港ホットスポットで無料インターネットを使うために、DNS上のTCP/IP はすでに実装されたことがあると認識している
      結局、無料Wi-Fiが標準になって大した話ではなくなり、今度は機内無料Wi-Fiの戦いに移ってきた
      いずれ機内Wi-Fiもどこでも無料になるだろうし、すでに複数の航空会社ではそうなっている
    • 自国の通信事業者がデータ制限付きプランで「WhatsApp無制限」を宣伝し始めて以来、何年もこのことを考えてきた
      テキストだけで画像や動画は無理だろうが、一般的なWebブラウジングには十分だったはずだ
  • 数年前、うちの大学の学部長が「ゲームのせいで出席に影響が出ている」といった趣旨のことを言い、夜10時以降はLANネットワークを遮断することにした
    実装方法は、IPアドレスで行き来するすべてのトラフィックをブロックするというものだったが、TCP自体はブロックしておらず、当時すでにそれほど新しくはなかったIPv6のことも完全に忘れていた
    そこでIPv6で動作する簡単なP2Pチャットアプリを作った。唯一の問題は、友人たちにIPv6アドレスを共有し、連絡先として保持してもらう必要があることだった
    うまく動いたが、後になって、ネットワーク外のコンピューターへトンネリングするほうがずっと簡単だと分かった

  • これは、役に立たないソフトウェアをもっと書こうという記事と、それに続くHNでの議論を思い出させる、ほぼ完璧な例のように思える
    人生を変えるものか? おそらく違う
    作って探求する過程は楽しかったか? ほぼ間違いなくそうだ
    私たちはこういうものをもっと作るべきだ
    https://ntietz.com/blog/write-more-useless-software/
    https://news.ycombinator.com/item?id=37911900