[インデックス 11167] ファイルの概要
このコミットは、Go言語の実験的なパッケージ exp/proxy を新規に導入するものです。このパッケージは、様々なプロキシ(特にSOCKSv5)を介したネットワーク接続のトンネリングをクライアント側でサポートすることを目的としています。これはAPIの基礎を築くための初期スケッチであり、完全な実装ではありません。
コミット
commit 423a09760bf90fe16dc03251792360daf8a99de0
Author: Adam Langley <agl@golang.org>
Date: Sat Jan 14 10:44:35 2012 -0500
exp/proxy: new package
exp/proxy provides client support for tunneling connections through
various proxies.
This is an initial, incomplete sketch of the code to lay down an
API.
R=golang-dev, r, r, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5490062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/423a09760bf90fe16dc03251792360daf8a99de0
元コミット内容
exp/proxy: new package
exp/proxy provides client support for tunneling connections through
various proxies.
This is an initial, incomplete sketch of the code to lay down an
API.
R=golang-dev, r, r, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5490062
変更の背景
このコミットの背景には、Go言語の標準ライブラリにプロキシ経由でのネットワーク接続機能を追加するというニーズがあります。多くの企業ネットワークや特定の環境では、インターネットへの直接接続が制限されており、HTTPプロキシやSOCKSプロキシなどを介して通信を行う必要があります。この exp/proxy パッケージは、そのような環境下でGoアプリケーションが外部リソースにアクセスできるようにするための基盤を提供することを目的としています。
コミットメッセージにある「initial, incomplete sketch of the code to lay down an API」という記述から、この時点ではまだプロキシ機能の全体像が固まっておらず、まずは基本的なAPI設計とSOCKSv5プロキシの最小限の実装を行うことで、今後の開発の足がかりとしようとしていることが伺えます。
前提知識の解説
このコミットを理解するためには、以下のネットワークおよびGo言語に関する基本的な知識が必要です。
- プロキシ (Proxy): クライアントとサーバーの間に入って通信を中継するサーバーのことです。セキュリティ、匿名性、キャッシュ、アクセス制御などの目的で利用されます。
- HTTPプロキシ: HTTP通信を中継するプロキシ。
- SOCKSプロキシ: TCP/UDP通信を中継する汎用的なプロキシ。SOCKSv5は認証やIPv6、UDPのサポートなど、SOCKSv4よりも高機能です。
- トンネリング (Tunneling): あるプロトコルを別のプロトコルでカプセル化して送信する技術です。プロキシ経由の接続では、クライアントからプロキシへの接続と、プロキシから最終的な宛先への接続という2段階の通信が行われ、プロキシがトンネルの役割を果たします。
net.Connインターフェース: Go言語のnetパッケージで定義されている、ネットワーク接続を表すインターフェースです。Read、Write、Closeなどのメソッドを持ち、TCP/UDP接続などを抽象化します。net.Dial関数:netパッケージの関数で、指定されたネットワーク(例: "tcp")とアドレス(例: "example.com:80")に対してネットワーク接続を確立します。net.SplitHostPort関数: ホスト名とポート番号を分離する関数です。例: "example.com:80" -> ("example.com", "80")。net.ParseIP/net.ParseCIDR関数: IPアドレスやCIDR表記のIPアドレス範囲をパースする関数です。os.Getenv関数: 環境変数の値を取得する関数です。プロキシ設定は通常、HTTP_PROXY,HTTPS_PROXY,NO_PROXYなどの環境変数で指定されます。net/urlパッケージ: URLのパースや操作を行うためのパッケージです。プロキシのアドレスはURL形式で指定されることが多いため、このパッケージが利用されます。- Goの実験的パッケージ (
exp): Go言語の標準ライブラリには、将来的に標準ライブラリに取り込まれる可能性のある実験的な機能がexpディレクトリ以下に置かれることがあります。これらのパッケージはAPIが安定していない可能性があり、変更されることがあります。
技術的詳細
exp/proxy パッケージは、ネットワーク接続を確立するための抽象化として Dialer インターフェースを導入しています。これにより、直接接続、特定のホストをバイパスするプロキシ、SOCKS5プロキシなど、様々な接続方法を統一的に扱うことが可能になります。
主要なコンポーネントは以下の通りです。
-
Dialerインターフェース:type Dialer interface { Dial(network, addr string) (c net.Conn, err error) }これは、
net.Dialと同様に、指定されたネットワークとアドレスに接続を確立するメソッドを持つインターフェースです。プロキシ経由の接続もこのインターフェースを通じて行われます。 -
Directプロキシ:direct.goで定義されており、プロキシを介さずに直接ネットワーク接続を行うDialerの実装です。これは、プロキシが不要な場合や、プロキシ設定のデフォルトとして使用されます。 -
PerHostプロキシ:per_host.goで定義されており、特定のホストやIPアドレス範囲に対しては別のDialer(バイパスプロキシ) を使用し、それ以外の場合はデフォルトのDialerを使用するという、ホストごとのルーティング機能を提供します。これは、NO_PROXY環境変数で指定されるような、プロキシを介さない例外リストを処理するために利用されます。AddFromStringメソッドは、カンマ区切りの文字列からバイパスルール(IPアドレス、CIDR範囲、ドメインゾーン、ホスト名)を解析して追加します。 -
SOCKS5プロキシ:socks5.goで定義されており、SOCKSv5プロトコルを実装したDialerです。ユーザー名とパスワードによる認証もサポートしています。SOCKSv5プロトコルのハンドシェイク(バージョンネゴシエーション、認証方法の選択、認証、接続要求)を実装しています。 -
環境変数からのプロキシ設定 (
FromEnvironment):proxy.goで定義されているFromEnvironment関数は、all_proxyおよびno_proxy環境変数を読み取り、それらの設定に基づいて適切なDialerを構築します。all_proxy: すべての接続に使用するプロキシのURLを指定します。no_proxy: プロキシを介さずに直接接続するホストのリストを指定します。
-
URLからのプロキシ設定 (
FromURL):proxy.goで定義されているFromURL関数は、与えられたURL(例:socks5://user:password@proxy.example.com:1080)からDialerを生成します。RegisterDialerTypeを使用して、新しいプロキシスキーム(例: "http", "https")のハンドラを登録することも可能です。
コアとなるコードの変更箇所
このコミットでは、以下の新しいファイルが追加されています。
src/pkg/exp/proxy/Makefile:exp/proxyパッケージのビルド設定ファイル。src/pkg/exp/proxy/direct.go: 直接接続を行うDirectDialerの実装。src/pkg/exp/proxy/per_host.go: ホストごとのプロキシルーティングを行うPerHostDialerの実装。src/pkg/exp/proxy/per_host_test.go:PerHostDialerのテストコード。src/pkg/exp/proxy/proxy.go:Dialerインターフェースの定義、環境変数からのプロキシ設定読み込み (FromEnvironment)、URLからのDialer生成 (FromURL)、およびDialerタイプの登録機能 (RegisterDialerType) を含む主要なファイル。src/pkg/exp/proxy/proxy_test.go:FromURL関数のテストコード。src/pkg/exp/proxy/socks5.go: SOCKSv5プロトコルを実装したSOCKS5Dialerの実装。
これらのファイルはすべて新規追加であり、既存のコードベースへの変更は含まれていません。
コアとなるコードの解説
src/pkg/exp/proxy/proxy.go
-
Dialerインターフェース: ネットワーク接続を抽象化する中心的なインターフェース。 -
FromEnvironment()関数:func FromEnvironment() Dialer { allProxy := os.Getenv("all_proxy") if len(allProxy) == 0 { return Direct // all_proxyが設定されていなければ直接接続 } proxyURL, err := url.Parse(allProxy) if err != nil { return Direct // URLパースエラーなら直接接続 } proxy, err := FromURL(proxyURL, Direct) // all_proxyで指定されたプロキシを構築 if err != nil { return Direct // プロキシ構築エラーなら直接接続 } noProxy := os.Getenv("no_proxy") if len(noProxy) == 0 { return proxy // no_proxyが設定されていなければ構築したプロキシをそのまま返す } perHost := NewPerHost(proxy, Direct) // no_proxy設定を処理するためPerHostを構築 perHost.AddFromString(noProxy) return perHost }この関数は、環境変数
all_proxyとno_proxyを解析し、それに基づいて適切なDialerを返します。all_proxyが設定されていればそのプロキシを使用し、no_proxyが設定されていればPerHostDialerを使って例外処理を行います。 -
FromURL(u *url.URL, forward Dialer) (Dialer, error)関数: URLスキーム(例: "socks5")に基づいて対応するDialerを生成します。このコミット時点では "socks5" スキームのみが組み込みでサポートされています。RegisterDialerTypeで登録されたカスタムスキームも処理できます。
src/pkg/exp/proxy/direct.go
Direct変数:var Direct = direct{}direct型のゼロ値で初期化されたDirect変数。これはプロキシを介さない直接接続を表すシングルトンとして機能します。func (direct) Dial(network, addr string) (net.Conn, error):net.Dialを直接呼び出すことで、プロキシなしの接続を確立します。
src/pkg/exp/proxy/per_host.go
PerHost構造体:def(デフォルトのDialer) とbypass(バイパス用のDialer) を持ち、さらにバイパス対象のネットワーク、IPアドレス、ゾーン、ホスト名のリストを保持します。NewPerHost(defaultDialer, bypass Dialer) *PerHost:PerHostDialerのコンストラクタ。Dial(network, addr string) (c net.Conn, err error): 接続先のアドレスを解析し、dialerForRequestメソッドを使って適切なDialer(デフォルトかバイパスか) を選択し、そのDialerのDialメソッドを呼び出します。dialerForRequest(host string) Dialer: ホスト名またはIPアドレスがバイパスリストに含まれるかどうかをチェックし、bypassDialerまたはdefDialerを返します。AddFromString(s string): カンマ区切りの文字列からバイパスルールを解析し、AddIP,AddNetwork,AddZone,AddHostメソッドを呼び出して内部リストに追加します。
src/pkg/exp/proxy/socks5.go
SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error): SOCKSv5プロキシのDialerを構築する関数。プロキシのアドレス、認証情報、およびプロキシ自体への接続に使用するforwardDialer(通常はDirect)を受け取ります。socks5構造体: SOCKSv5プロキシの接続情報(ユーザー名、パスワード、プロキシのアドレス、転送用Dialer)を保持します。Dial(network, addr string) (net.Conn, error): SOCKSv5プロトコルに従ってプロキシ経由で接続を確立します。- プロキシへの接続: まず
s.forward.Dialを使ってSOCKSv5プロキシサーバー自体に接続します。 - SOCKSv5ハンドシェイク:
- 認証方法のネゴシエーション: クライアントはサポートする認証方法(認証なし、ユーザー名/パスワード認証など)をプロキシに通知します。
- 認証: プロキシがユーザー名/パスワード認証を要求した場合、クライアントは認証情報を送信します。
- 接続要求: クライアントは、最終的な接続先のアドレスとポート、および接続タイプ(CONNECTコマンド)をプロキシに送信します。
- 応答の処理: プロキシからの応答を読み取り、接続が成功したか、エラーが発生したかを確認します。
- プロキシへの接続: まず
この実装は、SOCKSv5プロトコルの基本的なフロー(RFC 1928)に厳密に従っています。
関連リンク
- Go Change List: https://golang.org/cl/5490062
参考にした情報源リンク
- RFC 1928: SOCKS Protocol Version 5: https://datatracker.ietf.org/doc/html/rfc1928
- Go言語
netパッケージドキュメント: https://pkg.go.dev/net - Go言語
net/urlパッケージドキュメント: https://pkg.go.dev/net/url