[インデックス 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
: 直接接続を行うDirect
Dialerの実装。src/pkg/exp/proxy/per_host.go
: ホストごとのプロキシルーティングを行うPerHost
Dialerの実装。src/pkg/exp/proxy/per_host_test.go
:PerHost
Dialerのテストコード。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プロトコルを実装したSOCKS5
Dialerの実装。
これらのファイルはすべて新規追加であり、既存のコードベースへの変更は含まれていません。
コアとなるコードの解説
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
が設定されていればPerHost
Dialerを使って例外処理を行います。 -
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
:PerHost
Dialerのコンストラクタ。Dial(network, addr string) (c net.Conn, err error)
: 接続先のアドレスを解析し、dialerForRequest
メソッドを使って適切なDialer
(デフォルトかバイパスか) を選択し、そのDialer
のDial
メソッドを呼び出します。dialerForRequest(host string) Dialer
: ホスト名またはIPアドレスがバイパスリストに含まれるかどうかをチェックし、bypass
Dialerまたはdef
Dialerを返します。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
を構築する関数。プロキシのアドレス、認証情報、およびプロキシ自体への接続に使用するforward
Dialer(通常は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