This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a change to branch main in repository pluggable-transports/snowflake.
from f38c91f Don't use entropy for test new 3d4f294 Add Bridge List Definition new 0822c5f Add Bridge List Holder new 5578b4d Add Bridge List Holder Test new 38f0e00 Add Domain Name Matcher new 613ceaf Add RelayURL and AllowedRelayPattern to snowflake signaling new 863a829 Add RelayURL support in proxy new d5a87c3 Guard Proxy Relay URL Acceptance with Pattern Check new 5d7a376 Add Relay Info Forwarding for Snowflake new c7549d8 Update default snowflake server address new 50c0d64 Add Detailed Error Output for proxyPolls, proxyAnswers new c961b07 Add Detailed Error Output for datachannelHandler new 02c6f76 Add support for specifying bridge list file new b09a2e0 Add Relay URL Check in Snowflake Proxy new 2ebdc89 Add Allowed Relay Hostname Pattern Indication new b18a943 Add Broker Allowed Relay Pattern Indication Rejection for Proxy new 3ebb5a4 Show relay URL when connecting to relay new 6e8fbe5 Rejection reason feedback new 1b48ee1 Add test for proxy poll with Relay URL new b391d98 Add Proxy Relay URL Support Counting Metrics Output new 7caab01 Fixed desynchronized comment and behavior for log interval new b78eb74 Add Proxy Relay URL Rejection Metrics new dd61e2b Add Proxy Relay URL Metrics Collection new f789dce Represent Bridge Fingerprint As String new c5e5b45 Update message protocol version to 1.3 for RelayURL new 8ab4565 Disallow unknown bridge list file field new 8ba8917 Add document for LoadBridgeInfo input new a4bbb72 Fix not zero metrics for 1.3 values new 0ae4d82 Move ErrExtraInfo to ipc.go new e5b799d Update documents for broker messages new ddf7202 Restrict Allowed Relay to Tor Pool by default new 97dea53 Update Relay Pattern format to include dollar sign
The 31 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "add" were already present in the repository and have only been added to this reference.
Summary of changes: broker/bridge-list.go | 94 ++++++++++++++++ broker/bridge-list_test.go | 64 +++++++++++ broker/broker.go | 53 ++++++++- broker/http.go | 4 +- broker/ipc.go | 54 ++++++++- broker/metrics.go | 47 +++++++- broker/snowflake-broker_test.go | 17 ++- common/bridgefingerprint/fingerprint.go | 30 +++++ common/messages/client.go | 5 +- common/messages/ipc.go | 1 + common/messages/messages_test.go | 191 ++++++++++++++++++++------------ common/messages/proxy.go | 88 +++++++++++---- common/namematcher/matcher.go | 31 ++++++ common/namematcher/matcher_test.go | 55 +++++++++ doc/broker-spec.txt | 28 ++++- proxy/lib/proxy-go_test.go | 4 +- proxy/lib/snowflake.go | 67 ++++++++--- proxy/main.go | 5 + 18 files changed, 708 insertions(+), 130 deletions(-) create mode 100644 broker/bridge-list.go create mode 100644 broker/bridge-list_test.go create mode 100644 common/bridgefingerprint/fingerprint.go create mode 100644 common/namematcher/matcher.go create mode 100644 common/namematcher/matcher_test.go
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 3d4f294241c662872ec75a5adcf8928faec60e5e Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon Mar 28 17:17:10 2022 +0100
Add Bridge List Definition --- broker/bridge-list.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/broker/bridge-list.go b/broker/bridge-list.go new file mode 100644 index 0000000..3913a2e --- /dev/null +++ b/broker/bridge-list.go @@ -0,0 +1,18 @@ +package main + +import "sync" + +type bridgeListHolder struct { + bridgeInfo map[[20]byte]BridgeInfo + accessBridgeInfo sync.RWMutex +} + +type BridgeListHolder interface { + GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) +} + +type BridgeInfo struct { + DisplayName string `json:"displayName"` + WebSocketAddress string `json:"webSocketAddress"` + Fingerprint string `json:"fingerprint"` +}
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 0822c5f87b29aee159645af25d7c97c61f539315 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Tue Mar 29 13:29:48 2022 +0100
Add Bridge List Holder --- broker/bridge-list.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/broker/bridge-list.go b/broker/bridge-list.go index 3913a2e..e77db65 100644 --- a/broker/bridge-list.go +++ b/broker/bridge-list.go @@ -1,6 +1,20 @@ package main
-import "sync" +import ( + "bufio" + "encoding/hex" + "encoding/json" + "errors" + "io" + "sync" +) + +var ErrBridgeNotFound = errors.New("bridge not found") +var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid") + +func NewBridgeListHolder() BridgeListHolderFileBased { + return &bridgeListHolder{} +}
type bridgeListHolder struct { bridgeInfo map[[20]byte]BridgeInfo @@ -11,8 +25,45 @@ type BridgeListHolder interface { GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) }
+type BridgeListHolderFileBased interface { + BridgeListHolder + LoadBridgeInfo(reader io.Reader) error +} + type BridgeInfo struct { DisplayName string `json:"displayName"` WebSocketAddress string `json:"webSocketAddress"` Fingerprint string `json:"fingerprint"` } + +func (h *bridgeListHolder) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { + h.accessBridgeInfo.RLock() + defer h.accessBridgeInfo.RUnlock() + if bridgeInfo, ok := h.bridgeInfo[fingerprint]; ok { + return bridgeInfo, nil + } + return BridgeInfo{}, ErrBridgeNotFound +} + +func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error { + bridgeInfoMap := map[[20]byte]BridgeInfo{} + inputScanner := bufio.NewScanner(reader) + for inputScanner.Scan() { + inputLine := inputScanner.Bytes() + bridgeInfo := BridgeInfo{} + if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil { + return err + } + var bridgeHash [20]byte + if n, err := hex.Decode(bridgeHash[:], []byte(bridgeInfo.Fingerprint)); err != nil { + return err + } else if n != 20 { + return ErrBridgeFingerprintInvalid + } + bridgeInfoMap[bridgeHash] = bridgeInfo + } + h.accessBridgeInfo.Lock() + defer h.accessBridgeInfo.Unlock() + h.bridgeInfo = bridgeInfoMap + return nil +}
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 5578b4dd76339cbf76ee8fced30d08e02a424d3b Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Tue Mar 29 14:06:05 2022 +0100
Add Bridge List Holder Test --- broker/bridge-list_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+)
diff --git a/broker/bridge-list_test.go b/broker/bridge-list_test.go new file mode 100644 index 0000000..73da43c --- /dev/null +++ b/broker/bridge-list_test.go @@ -0,0 +1,59 @@ +package main + +import ( + "bytes" + "encoding/hex" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +const DefaultBridges = `{"displayName":"default", "webSocketAddress":"wss://snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80A72"} +` + +const ImaginaryBridges = `{"displayName":"default", "webSocketAddress":"wss://snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80A72"} +{"displayName":"imaginary-1", "webSocketAddress":"wss://imaginary-1-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B00"} +{"displayName":"imaginary-2", "webSocketAddress":"wss://imaginary-2-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B01"} +{"displayName":"imaginary-3", "webSocketAddress":"wss://imaginary-3-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B02"} +{"displayName":"imaginary-4", "webSocketAddress":"wss://imaginary-4-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B03"} +{"displayName":"imaginary-5", "webSocketAddress":"wss://imaginary-5-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B04"} +{"displayName":"imaginary-6", "webSocketAddress":"wss://imaginary-6-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B05"} +{"displayName":"imaginary-7", "webSocketAddress":"wss://imaginary-7-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B06"} +{"displayName":"imaginary-8", "webSocketAddress":"wss://imaginary-8-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B07"} +{"displayName":"imaginary-9", "webSocketAddress":"wss://imaginary-9-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B08"} +{"displayName":"imaginary-10", "webSocketAddress":"wss://imaginary-10-snowflake.torproject.org", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80B09"} +` + +func TestBridgeLoad(t *testing.T) { + Convey("load default list", t, func() { + bridgeList := NewBridgeListHolder() + So(bridgeList.LoadBridgeInfo(bytes.NewReader([]byte(DefaultBridges))), ShouldBeNil) + { + bridgeFingerprint := [20]byte{} + { + n, err := hex.Decode(bridgeFingerprint[:], []byte("2B280B23E1107BB62ABFC40DDCC8824814F80A72")) + So(n, ShouldEqual, 20) + So(err, ShouldBeNil) + } + bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) + So(err, ShouldBeNil) + So(bridgeInfo.DisplayName, ShouldEqual, "default") + So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org") + } + }) + Convey("load imaginary list", t, func() { + bridgeList := NewBridgeListHolder() + So(bridgeList.LoadBridgeInfo(bytes.NewReader([]byte(ImaginaryBridges))), ShouldBeNil) + { + bridgeFingerprint := [20]byte{} + { + n, err := hex.Decode(bridgeFingerprint[:], []byte("2B280B23E1107BB62ABFC40DDCC8824814F80B07")) + So(n, ShouldEqual, 20) + So(err, ShouldBeNil) + } + bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) + So(err, ShouldBeNil) + So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8") + So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org") + } + }) +}
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 38f0e00e5d576bbb6ee78cc14ba07003c40d6091 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Mar 30 16:42:59 2022 +0100
Add Domain Name Matcher
Design difference from original vision: Skipped FQDN step to make it more generalized https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowf... --- common/namematcher/matcher.go | 26 ++++++++++++++++++ common/namematcher/matcher_test.go | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+)
diff --git a/common/namematcher/matcher.go b/common/namematcher/matcher.go new file mode 100644 index 0000000..57f9c56 --- /dev/null +++ b/common/namematcher/matcher.go @@ -0,0 +1,26 @@ +package namematcher + +import "strings" + +func NewNameMatcher(rule string) NameMatcher { + return NameMatcher{suffix: strings.TrimPrefix(rule, "^"), exact: strings.HasPrefix(rule, "^")} +} + +type NameMatcher struct { + exact bool + suffix string +} + +func (m *NameMatcher) IsSupersetOf(matcher NameMatcher) bool { + if m.exact { + return matcher.exact && m.suffix == matcher.suffix + } + return strings.HasSuffix(matcher.suffix, m.suffix) +} + +func (m *NameMatcher) IsMember(s string) bool { + if m.exact { + return s == m.suffix + } + return strings.HasSuffix(s, m.suffix) +} diff --git a/common/namematcher/matcher_test.go b/common/namematcher/matcher_test.go new file mode 100644 index 0000000..8d92614 --- /dev/null +++ b/common/namematcher/matcher_test.go @@ -0,0 +1,55 @@ +package namematcher + +import "testing" + +import . "github.com/smartystreets/goconvey/convey" + +func TestMatchMember(t *testing.T) { + testingVector := []struct { + matcher string + target string + expects bool + }{ + {matcher: "", target: "", expects: true}, + {matcher: "^snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, + {matcher: "^snowflake.torproject.net", target: "faketorproject.net", expects: false}, + {matcher: "snowflake.torproject.net", target: "faketorproject.net", expects: false}, + {matcher: "snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "imaginary-01-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "imaginary-aaa-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "imaginary-aaa-snowflake.faketorproject.net", expects: false}, + } + for _, v := range testingVector { + t.Run(v.matcher+"<>"+v.target, func(t *testing.T) { + Convey("test", t, func() { + matcher := NewNameMatcher(v.matcher) + So(matcher.IsMember(v.target), ShouldEqual, v.expects) + }) + }) + } +} + +func TestMatchSubset(t *testing.T) { + testingVector := []struct { + matcher string + target string + expects bool + }{ + {matcher: "", target: "", expects: true}, + {matcher: "^snowflake.torproject.net", target: "^snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "^snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "testing-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "^testing-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net", target: "", expects: false}, + } + for _, v := range testingVector { + t.Run(v.matcher+"<>"+v.target, func(t *testing.T) { + Convey("test", t, func() { + matcher := NewNameMatcher(v.matcher) + target := NewNameMatcher(v.target) + So(matcher.IsSupersetOf(target), ShouldEqual, v.expects) + }) + }) + } +}
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 613ceaf9709e7170cc95c065cbd3ea8a205a04b2 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Tue Apr 5 15:03:31 2022 +0100
Add RelayURL and AllowedRelayPattern to snowflake signaling --- common/messages/messages_test.go | 163 ++++++++++++++++++++++----------------- common/messages/proxy.go | 64 +++++++++++---- 2 files changed, 141 insertions(+), 86 deletions(-)
diff --git a/common/messages/messages_test.go b/common/messages/messages_test.go index dd1f4fb..d1a5e96 100644 --- a/common/messages/messages_test.go +++ b/common/messages/messages_test.go @@ -17,90 +17,103 @@ func TestDecodeProxyPollRequest(t *testing.T) { clients int data string err error + + acceptedRelayPattern string }{ { //Version 1.0 proxy message - "ymbcCMto7KHNGYlp", - "unknown", - "unknown", - 0, - `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`, - nil, + sid: "ymbcCMto7KHNGYlp", + proxyType: "unknown", + natType: "unknown", + clients: 0, + data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`, + err: nil, }, { //Version 1.1 proxy message - "ymbcCMto7KHNGYlp", - "standalone", - "unknown", - 0, - `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.1","Type":"standalone"}`, - nil, + sid: "ymbcCMto7KHNGYlp", + proxyType: "standalone", + natType: "unknown", + clients: 0, + data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.1","Type":"standalone"}`, + err: nil, }, { //Version 1.2 proxy message - "ymbcCMto7KHNGYlp", - "standalone", - "restricted", - 0, - `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted"}`, - nil, + sid: "ymbcCMto7KHNGYlp", + proxyType: "standalone", + natType: "restricted", + clients: 0, + data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted"}`, + err: nil, }, { //Version 1.2 proxy message with clients - "ymbcCMto7KHNGYlp", - "standalone", - "restricted", - 24, - `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted","Clients":24}`, - nil, + sid: "ymbcCMto7KHNGYlp", + proxyType: "standalone", + natType: "restricted", + clients: 24, + data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted","Clients":24}`, + err: nil, + }, + { + //Version 1.3 proxy message with clients and proxyURL + sid: "ymbcCMto7KHNGYlp", + proxyType: "standalone", + natType: "restricted", + clients: 24, + acceptedRelayPattern: "snowfalke.torproject.org", + data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted","Clients":24, "AcceptedRelayPattern":"snowfalke.torproject.org"}`, + err: nil, }, { //Version 0.X proxy message: - "", - "", - "", - 0, - "", - &json.SyntaxError{}, + sid: "", + proxyType: "", + natType: "", + clients: 0, + data: "", + err: &json.SyntaxError{}, }, { - "", - "", - "", - 0, - `{"Sid":"ymbcCMto7KHNGYlp"}`, - fmt.Errorf(""), + sid: "", + proxyType: "", + natType: "", + clients: 0, + data: `{"Sid":"ymbcCMto7KHNGYlp"}`, + err: fmt.Errorf(""), }, { - "", - "", - "", - 0, - "{}", - fmt.Errorf(""), + sid: "", + proxyType: "", + natType: "", + clients: 0, + data: "{}", + err: fmt.Errorf(""), }, { - "", - "", - "", - 0, - `{"Version":"1.0"}`, - fmt.Errorf(""), + sid: "", + proxyType: "", + natType: "", + clients: 0, + data: `{"Version":"1.0"}`, + err: fmt.Errorf(""), }, { - "", - "", - "", - 0, - `{"Version":"2.0"}`, - fmt.Errorf(""), + sid: "", + proxyType: "", + natType: "", + clients: 0, + data: `{"Version":"2.0"}`, + err: fmt.Errorf(""), }, } { - sid, proxyType, natType, clients, err := DecodeProxyPollRequest([]byte(test.data)) + sid, proxyType, natType, clients, relayPattern, err := DecodeProxyPollRequestWithRelayPrefix([]byte(test.data)) So(sid, ShouldResemble, test.sid) So(proxyType, ShouldResemble, test.proxyType) So(natType, ShouldResemble, test.natType) So(clients, ShouldEqual, test.clients) + So(relayPattern, ShouldResemble, test.acceptedRelayPattern) So(err, ShouldHaveSameTypeAs, test.err) }
@@ -123,34 +136,42 @@ func TestEncodeProxyPollRequests(t *testing.T) { func TestDecodeProxyPollResponse(t *testing.T) { Convey("Context", t, func() { for _, test := range []struct { - offer string - data string - err error + offer string + data string + relayURL string + err error }{ { - "fake offer", - `{"Status":"client match","Offer":"fake offer","NAT":"unknown"}`, - nil, + offer: "fake offer", + data: `{"Status":"client match","Offer":"fake offer","NAT":"unknown"}`, + err: nil, }, { - "", - `{"Status":"no match"}`, - nil, + offer: "fake offer", + data: `{"Status":"client match","Offer":"fake offer","NAT":"unknown", "RelayURL":"wss://snowflake.torproject.org/proxy"}`, + relayURL: "wss://snowflake.torproject.org/proxy", + err: nil, }, { - "", - `{"Status":"client match"}`, - fmt.Errorf("no supplied offer"), + offer: "", + data: `{"Status":"no match"}`, + err: nil, }, { - "", - `{"Test":"test"}`, - fmt.Errorf(""), + offer: "", + data: `{"Status":"client match"}`, + err: fmt.Errorf("no supplied offer"), + }, + { + offer: "", + data: `{"Test":"test"}`, + err: fmt.Errorf(""), }, } { - offer, _, err := DecodePollResponse([]byte(test.data)) + offer, _, relayURL, err := DecodePollResponseWithRelayURL([]byte(test.data)) So(err, ShouldHaveSameTypeAs, test.err) So(offer, ShouldResemble, test.offer) + So(relayURL, ShouldResemble, test.relayURL) }
}) diff --git a/common/messages/proxy.go b/common/messages/proxy.go index dcfe0ab..d18a7c3 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -5,6 +5,7 @@ package messages
import ( "encoding/json" + "errors" "fmt" "strings"
@@ -23,6 +24,8 @@ var KnownProxyTypes = map[string]bool{ "iptproxy": true, }
+var ErrExtraInfo = errors.New("client sent extra info") + /* Version 1.2 specification:
== ProxyPollRequest == @@ -93,22 +96,39 @@ type ProxyPollRequest struct { Type string NAT string Clients int + + AcceptedRelayPattern string }
func EncodeProxyPollRequest(sid string, proxyType string, natType string, clients int) ([]byte, error) { + return EncodeProxyPollRequestWithRelayPrefix(sid, proxyType, natType, clients, "") +} + +func EncodeProxyPollRequestWithRelayPrefix(sid string, proxyType string, natType string, clients int, relayPattern string) ([]byte, error) { return json.Marshal(ProxyPollRequest{ - Sid: sid, - Version: version, - Type: proxyType, - NAT: natType, - Clients: clients, + Sid: sid, + Version: version, + Type: proxyType, + NAT: natType, + Clients: clients, + AcceptedRelayPattern: relayPattern, }) }
+func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType string, clients int, err error) { + var relayPrefix string + sid, proxyType, natType, clients, relayPrefix, err = DecodeProxyPollRequestWithRelayPrefix(data) + if relayPrefix != "" { + return "", "", "", 0, ErrExtraInfo + } + return +} + // Decodes a poll message from a snowflake proxy and returns the // sid, proxy type, nat type and clients of the proxy on success // and an error if it failed -func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType string, clients int, err error) { +func DecodeProxyPollRequestWithRelayPrefix(data []byte) ( + sid string, proxyType string, natType string, clients int, relayPrefix string, err error) { var message ProxyPollRequest
err = json.Unmarshal(data, &message) @@ -145,21 +165,28 @@ func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType message.Type = ProxyUnknown }
- return message.Sid, message.Type, message.NAT, message.Clients, nil + return message.Sid, message.Type, message.NAT, message.Clients, message.AcceptedRelayPattern, nil }
type ProxyPollResponse struct { Status string Offer string NAT string + + RelayURL string }
func EncodePollResponse(offer string, success bool, natType string) ([]byte, error) { + return EncodePollResponseWithRelayURL(offer, success, natType, "") +} + +func EncodePollResponseWithRelayURL(offer string, success bool, natType, relayURL string) ([]byte, error) { if success { return json.Marshal(ProxyPollResponse{ - Status: "client match", - Offer: offer, - NAT: natType, + Status: "client match", + Offer: offer, + NAT: natType, + RelayURL: relayURL, })
} @@ -167,23 +194,30 @@ func EncodePollResponse(offer string, success bool, natType string) ([]byte, err Status: "no match", }) } +func DecodePollResponse(data []byte) (string, string, error) { + offer, natType, relayURL, err := DecodePollResponseWithRelayURL(data) + if relayURL != "" { + return "", "", ErrExtraInfo + } + return offer, natType, err +}
// Decodes a poll response from the broker and returns an offer and the client's NAT type // If there is a client match, the returned offer string will be non-empty -func DecodePollResponse(data []byte) (string, string, error) { +func DecodePollResponseWithRelayURL(data []byte) (string, string, string, error) { var message ProxyPollResponse
err := json.Unmarshal(data, &message) if err != nil { - return "", "", err + return "", "", "", err } if message.Status == "" { - return "", "", fmt.Errorf("received invalid data") + return "", "", "", fmt.Errorf("received invalid data") }
if message.Status == "client match" { if message.Offer == "" { - return "", "", fmt.Errorf("no supplied offer") + return "", "", "", fmt.Errorf("no supplied offer") } } else { message.Offer = "" @@ -194,7 +228,7 @@ func DecodePollResponse(data []byte) (string, string, error) { natType = "unknown" }
- return message.Offer, natType, nil + return message.Offer, natType, message.RelayURL, nil }
type ProxyAnswerRequest struct {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 863a8296e85ae467aa3855ab85f6f990f9cb40e5 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Thu Apr 7 21:32:55 2022 +0100
Add RelayURL support in proxy --- proxy/lib/proxy-go_test.go | 4 ++-- proxy/lib/snowflake.go | 43 +++++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/proxy/lib/proxy-go_test.go b/proxy/lib/proxy-go_test.go index f4cbfbf..b5ff86c 100644 --- a/proxy/lib/proxy-go_test.go +++ b/proxy/lib/proxy-go_test.go @@ -365,7 +365,7 @@ func TestBrokerInteractions(t *testing.T) { b, }
- sdp := broker.pollOffer(sampleOffer, DefaultProxyType, nil) + sdp, _ := broker.pollOffer(sampleOffer, DefaultProxyType, "", nil) expectedSDP, _ := strconv.Unquote(sampleSDP) So(sdp.SDP, ShouldResemble, expectedSDP) }) @@ -379,7 +379,7 @@ func TestBrokerInteractions(t *testing.T) { b, }
- sdp := broker.pollOffer(sampleOffer, DefaultProxyType, nil) + sdp, _ := broker.pollOffer(sampleOffer, DefaultProxyType, "", nil) So(sdp, ShouldBeNil) }) Convey("sends answer to broker", func() { diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index c508447..83e4cd9 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -112,6 +112,12 @@ type SnowflakeProxy struct { KeepLocalAddresses bool // RelayURL is the URL of the Snowflake server that all traffic will be relayed to RelayURL string + // RelayDomainNamePattern is the pattern specify allowed domain name for relay + // If the pattern starts with ^ then an exact match is required. + // The rest of pattern is the suffix of domain name. + // There is no look ahead assertion when matching domain name suffix, + // thus the string prepend the suffix does not need to be empty or ends with a dot. + RelayDomainNamePattern string // NATProbeURL is the URL of the probe service we use for NAT checks NATProbeURL string // NATTypeMeasurementInterval is time before NAT type is retested @@ -188,7 +194,7 @@ func (s *SignalingServer) Post(path string, payload io.Reader) ([]byte, error) { return limitedRead(resp.Body, readLimit) }
-func (s *SignalingServer) pollOffer(sid string, proxyType string, shutdown chan struct{}) *webrtc.SessionDescription { +func (s *SignalingServer) pollOffer(sid string, proxyType string, acceptedRelayPattern string, shutdown chan struct{}) (*webrtc.SessionDescription, string) { brokerPath := s.url.ResolveReference(&url.URL{Path: "proxy"})
ticker := time.NewTicker(pollInterval) @@ -198,38 +204,38 @@ func (s *SignalingServer) pollOffer(sid string, proxyType string, shutdown chan for ; true; <-ticker.C { select { case <-shutdown: - return nil + return nil, "" default: numClients := int((tokens.count() / 8) * 8) // Round down to 8 currentNATTypeLoaded := getCurrentNATType() body, err := messages.EncodeProxyPollRequest(sid, proxyType, currentNATTypeLoaded, numClients) if err != nil { log.Printf("Error encoding poll message: %s", err.Error()) - return nil + return nil, "" } resp, err := s.Post(brokerPath.String(), bytes.NewBuffer(body)) if err != nil { log.Printf("error polling broker: %s", err.Error()) }
- offer, _, err := messages.DecodePollResponse(resp) + offer, _, relayURL, err := messages.DecodePollResponseWithRelayURL(resp) if err != nil { log.Printf("Error reading broker response: %s", err.Error()) log.Printf("body: %s", resp) - return nil + return nil, "" } if offer != "" { offer, err := util.DeserializeSessionDescription(offer) if err != nil { log.Printf("Error processing session description: %s", err.Error()) - return nil + return nil, "" } - return offer + return offer, relayURL
} } } - return nil + return nil, "" }
func (s *SignalingServer) sendAnswer(sid string, pc *webrtc.PeerConnection) error { @@ -295,11 +301,14 @@ func copyLoop(c1 io.ReadWriteCloser, c2 io.ReadWriteCloser, shutdown chan struct // conn.RemoteAddr() inside this function, as a workaround for a hang that // otherwise occurs inside of conn.pc.RemoteDescription() (called by // RemoteAddr). https://bugs.torproject.org/18628#comment:8 -func (sf *SnowflakeProxy) datachannelHandler(conn *webRTCConn, remoteAddr net.Addr) { +func (sf *SnowflakeProxy) datachannelHandler(conn *webRTCConn, remoteAddr net.Addr, relayURL string) { defer conn.Close() defer tokens.ret()
- u, err := url.Parse(sf.RelayURL) + if relayURL == "" { + relayURL = sf.RelayURL + } + u, err := url.Parse(relayURL) if err != nil { log.Fatalf("invalid relay url: %s", err) } @@ -326,6 +335,15 @@ func (sf *SnowflakeProxy) datachannelHandler(conn *webRTCConn, remoteAddr net.Ad log.Printf("datachannelHandler ends") }
+type dataChannelHandlerWithRelayURL struct { + RelayURL string + sf *SnowflakeProxy +} + +func (d dataChannelHandlerWithRelayURL) datachannelHandler(conn *webRTCConn, remoteAddr net.Addr) { + d.sf.datachannelHandler(conn, remoteAddr, d.RelayURL) +} + // Create a PeerConnection from an SDP offer. Blocks until the gathering of ICE // candidates is complete and the answer is available in LocalDescription. // Installs an OnDataChannel callback that creates a webRTCConn and passes it to @@ -470,14 +488,15 @@ func (sf *SnowflakeProxy) makeNewPeerConnection(config webrtc.Configuration, }
func (sf *SnowflakeProxy) runSession(sid string) { - offer := broker.pollOffer(sid, sf.ProxyType, sf.shutdown) + offer, relayURL := broker.pollOffer(sid, sf.ProxyType, sf.RelayDomainNamePattern, sf.shutdown) if offer == nil { log.Printf("bad offer from broker") tokens.ret() return } dataChan := make(chan struct{}) - pc, err := sf.makePeerConnectionFromOffer(offer, config, dataChan, sf.datachannelHandler) + dataChannelAdaptor := dataChannelHandlerWithRelayURL{RelayURL: relayURL, sf: sf} + pc, err := sf.makePeerConnectionFromOffer(offer, config, dataChan, dataChannelAdaptor.datachannelHandler) if err != nil { log.Printf("error making WebRTC connection: %s", err) tokens.ret()
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit d5a87c3c02ea673d397e3cb8f945f2f0f0e05a76 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Fri Apr 8 15:14:38 2022 +0100
Guard Proxy Relay URL Acceptance with Pattern Check --- proxy/lib/snowflake.go | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index 83e4cd9..b2a2be1 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -30,6 +30,7 @@ import ( "crypto/rand" "encoding/base64" "fmt" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/namematcher" "io" "io/ioutil" "log" @@ -494,6 +495,12 @@ func (sf *SnowflakeProxy) runSession(sid string) { tokens.ret() return } + matcher := namematcher.NewNameMatcher(sf.RelayDomainNamePattern) + if relayURL != "" && !matcher.IsMember(relayURL) { + log.Printf("bad offer from broker: rejected Relay URL") + tokens.ret() + return + } dataChan := make(chan struct{}) dataChannelAdaptor := dataChannelHandlerWithRelayURL{RelayURL: relayURL, sf: sf} pc, err := sf.makePeerConnectionFromOffer(offer, config, dataChan, dataChannelAdaptor.datachannelHandler)
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 5d7a3766d6f8af0a3adc24b19aaa30747b49c847 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon Apr 11 14:24:49 2022 +0100
Add Relay Info Forwarding for Snowflake --- broker/broker.go | 15 +++++++++++++++ broker/ipc.go | 12 +++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/broker/broker.go b/broker/broker.go index 10129d7..692cea4 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -36,6 +36,13 @@ type BrokerContext struct { snowflakeLock sync.Mutex proxyPolls chan *ProxyPoll metrics *Metrics + + bridgeList BridgeListHolderFileBased + allowedRelayPattern string +} + +func (ctx *BrokerContext) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { + return ctx.bridgeList.GetBridgeInfo(fingerprint) }
func NewBrokerContext(metricsLogger *log.Logger) *BrokerContext { @@ -139,6 +146,14 @@ func (ctx *BrokerContext) AddSnowflake(id string, proxyType string, natType stri return snowflake }
+func (ctx *BrokerContext) InstallBridgeListProfile(reader io.Reader, relayPattern string) error { + if err := ctx.bridgeList.LoadBridgeInfo(reader); err != nil { + return err + } + ctx.allowedRelayPattern = relayPattern + return nil +} + // Client offer contains an SDP, bridge fingerprint and the NAT type of the client type ClientOffer struct { natType string diff --git a/broker/ipc.go b/broker/ipc.go index e11a33c..e559c2a 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -102,7 +102,13 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { }
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc() - b, err = messages.EncodePollResponse(string(offer.sdp), true, offer.natType) + var relayURL string + if info, err := i.ctx.bridgeList.GetBridgeInfo(offer.fingerprint); err != nil { + return err + } else { + relayURL = info.WebSocketAddress + } + b, err = messages.EncodePollResponseWithRelayURL(string(offer.sdp), true, offer.natType, relayURL) if err != nil { return messages.ErrInternal } @@ -141,6 +147,10 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error { } copy(offer.fingerprint[:], fingerprint)
+ if _, err := i.ctx.GetBridgeInfo(offer.fingerprint); err != nil { + return err + } + // Only hand out known restricted snowflakes to unrestricted clients var snowflakeHeap *SnowflakeHeap if offer.natType == NATUnrestricted {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit c7549d886eb84ef0fb31bbdced6de3bb00818a4e Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon Apr 11 16:29:08 2022 +0100
Update default snowflake server address
Change snowflake broker test for updated address
Amend DefaultBridges Value
Add Default Fingerprint Info for Snowflake --- broker/broker.go | 8 ++++++++ broker/ipc.go | 3 ++- broker/snowflake-broker_test.go | 13 +++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/broker/broker.go b/broker/broker.go index 692cea4..d9e8dea 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -6,6 +6,7 @@ SessionDescriptions in order to negotiate a WebRTC connection. package main
import ( + "bytes" "container/heap" "crypto/tls" "flag" @@ -60,12 +61,19 @@ func NewBrokerContext(metricsLogger *log.Logger) *BrokerContext { panic("Failed to create metrics") }
+ bridgeListHolder := NewBridgeListHolder() + + const DefaultBridges = `{"displayName":"default", "webSocketAddress":"wss://snowflake.torproject.net/", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80A72"} +` + bridgeListHolder.LoadBridgeInfo(bytes.NewReader([]byte(DefaultBridges))) + return &BrokerContext{ snowflakes: snowflakes, restrictedSnowflakes: rSnowflakes, idToSnowflake: make(map[string]*Snowflake), proxyPolls: make(chan *ProxyPoll), metrics: metrics, + bridgeList: bridgeListHolder, } }
diff --git a/broker/ipc.go b/broker/ipc.go index e559c2a..780a9a5 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -66,7 +66,8 @@ func (i *IPC) Debug(_ interface{}, response *string) error { }
func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { - sid, proxyType, natType, clients, err := messages.DecodeProxyPollRequest(arg.Body) + sid, proxyType, natType, clients, relayPattern, err := messages.DecodeProxyPollRequestWithRelayPrefix(arg.Body) + _ = relayPattern if err != nil { return messages.ErrBadRequest } diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index f7850f8..fdd1114 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -3,6 +3,7 @@ package main import ( "bytes" "container/heap" + "encoding/hex" "io" "io/ioutil" "log" @@ -36,6 +37,10 @@ func decodeAMPArmorToString(r io.Reader) (string, error) {
func TestBroker(t *testing.T) {
+ defaultBridgeValue, _ := hex.DecodeString("2B280B23E1107BB62ABFC40DDCC8824814F80A72") + var defaultBridge [20]byte + copy(defaultBridge[:], defaultBridgeValue) + Convey("Context", t, func() { ctx := NewBrokerContext(NullLogger()) i := &IPC{ctx} @@ -253,10 +258,10 @@ func TestBroker(t *testing.T) { // Pass a fake client offer to this proxy p := <-ctx.proxyPolls So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp") - p.offerChannel <- &ClientOffer{sdp: []byte("fake offer")} + p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge} <-done So(w.Code, ShouldEqual, http.StatusOK) - So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":""}`) + So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`) })
Convey("return empty 200 OK when no client offer is available.", func() { @@ -269,7 +274,7 @@ func TestBroker(t *testing.T) { // nil means timeout p.offerChannel <- nil <-done - So(w.Body.String(), ShouldEqual, `{"Status":"no match","Offer":"","NAT":""}`) + So(w.Body.String(), ShouldEqual, `{"Status":"no match","Offer":"","NAT":"","RelayURL":""}`) So(w.Code, ShouldEqual, http.StatusOK) }) }) @@ -412,7 +417,7 @@ func TestBroker(t *testing.T) {
<-polled So(wP.Code, ShouldEqual, http.StatusOK) - So(wP.Body.String(), ShouldResemble, `{"Status":"client match","Offer":"fake","NAT":"unknown"}`) + So(wP.Body.String(), ShouldResemble, `{"Status":"client match","Offer":"fake","NAT":"unknown","RelayURL":"wss://snowflake.torproject.net/"}`) So(ctx.idToSnowflake["ymbcCMto7KHNGYlp"], ShouldNotBeNil) // Follow up with the answer request afterwards wA := httptest.NewRecorder()
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 50c0d64e108a56dc42623f2c430cda790ed887c6 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon Apr 11 16:30:45 2022 +0100
Add Detailed Error Output for proxyPolls, proxyAnswers --- broker/http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/broker/http.go b/broker/http.go index 3b0ba1f..2f81f1d 100644 --- a/broker/http.go +++ b/broker/http.go @@ -94,7 +94,7 @@ For snowflake proxies to request a client from the Broker. func proxyPolls(i *IPC, w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) if err != nil { - log.Println("Invalid data.") + log.Println("Invalid data.", err.Error()) w.WriteHeader(http.StatusBadRequest) return } @@ -204,7 +204,7 @@ which the broker will pass back to the original client. func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) if err != nil { - log.Println("Invalid data.") + log.Println("Invalid data.", err.Error()) w.WriteHeader(http.StatusBadRequest) return }
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit c961b07459cd6fcc9ba40631e41a2108baf554d7 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Apr 13 13:42:06 2022 +0100
Add Detailed Error Output for datachannelHandler --- proxy/lib/snowflake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index b2a2be1..7e06c0f 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -326,7 +326,7 @@ func (sf *SnowflakeProxy) datachannelHandler(conn *webRTCConn, remoteAddr net.Ad
ws, _, err := websocket.DefaultDialer.Dial(u.String(), nil) if err != nil { - log.Printf("error dialing relay: %s", err) + log.Printf("error dialing relay: %s = %s", u.String(), err) return } wsConn := websocketconn.New(ws)
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 02c6f764c9f94cbbc7ef482d6db43fcb0e794996 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Apr 13 14:19:27 2022 +0100
Add support for specifying bridge list file --- broker/broker.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/broker/broker.go b/broker/broker.go index d9e8dea..476bc81 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -176,6 +176,7 @@ func main() { var addr string var geoipDatabase string var geoip6Database string + var bridgeListFilePath, allowedRelayPattern string var disableTLS bool var certFilename, keyFilename string var disableGeoip bool @@ -190,6 +191,8 @@ func main() { flag.StringVar(&addr, "addr", ":443", "address to listen on") flag.StringVar(&geoipDatabase, "geoipdb", "/usr/share/tor/geoip", "path to correctly formatted geoip database mapping IPv4 address ranges to country codes") flag.StringVar(&geoip6Database, "geoip6db", "/usr/share/tor/geoip6", "path to correctly formatted geoip database mapping IPv6 address ranges to country codes") + flag.StringVar(&bridgeListFilePath, "bridge-list-path", "", "file path for bridgeListFile") + flag.StringVar(&allowedRelayPattern, "allowed-relay-pattern", "", "allowed pattern for relay host name") flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS") flag.BoolVar(&disableGeoip, "disable-geoip", false, "don't use geoip for stats collection") flag.StringVar(&metricsFilename, "metrics-log", "", "path to metrics logging output") @@ -222,6 +225,17 @@ func main() {
ctx := NewBrokerContext(metricsLogger)
+ if bridgeListFilePath != "" { + bridgeListFile, err := os.Open(bridgeListFilePath) + if err != nil { + log.Fatal(err.Error()) + } + err = ctx.InstallBridgeListProfile(bridgeListFile, allowedRelayPattern) + if err != nil { + log.Fatal(err.Error()) + } + } + if !disableGeoip { err = ctx.metrics.LoadGeoipDatabases(geoipDatabase, geoip6Database) if err != nil {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit b09a2e09b3e8abadac5f5b96662864eec4ebd597 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Apr 13 16:20:52 2022 +0100
Add Relay URL Check in Snowflake Proxy --- proxy/lib/snowflake.go | 9 ++++++++- proxy/main.go | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index 7e06c0f..7dbc976 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -119,6 +119,7 @@ type SnowflakeProxy struct { // There is no look ahead assertion when matching domain name suffix, // thus the string prepend the suffix does not need to be empty or ends with a dot. RelayDomainNamePattern string + AllowNonTLSRelay bool // NATProbeURL is the URL of the probe service we use for NAT checks NATProbeURL string // NATTypeMeasurementInterval is time before NAT type is retested @@ -496,7 +497,13 @@ func (sf *SnowflakeProxy) runSession(sid string) { return } matcher := namematcher.NewNameMatcher(sf.RelayDomainNamePattern) - if relayURL != "" && !matcher.IsMember(relayURL) { + parsedRelayURL, err := url.Parse(relayURL) + if err != nil { + log.Printf("bad offer from broker: bad Relay URL %v", err.Error()) + tokens.ret() + return + } + if relayURL != "" && (!matcher.IsMember(parsedRelayURL.Hostname()) || (!sf.AllowNonTLSRelay && parsedRelayURL.Scheme != "wss")) { log.Printf("bad offer from broker: rejected Relay URL") tokens.ret() return diff --git a/proxy/main.go b/proxy/main.go index 7d025ea..305d0b0 100644 --- a/proxy/main.go +++ b/proxy/main.go @@ -21,6 +21,8 @@ func main() { unsafeLogging := flag.Bool("unsafe-logging", false, "prevent logs from being scrubbed") keepLocalAddresses := flag.Bool("keep-local-addresses", false, "keep local LAN address ICE candidates") relayURL := flag.String("relay", sf.DefaultRelayURL, "websocket relay URL") + allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "", "a pattern to specify allowed hostname pattern for relay URL.") + allowNonTLSRelay := flag.Bool("allow-non-tls-relay", false, "allow relay without tls encryption") NATTypeMeasurementInterval := flag.Duration("nat-retest-interval", time.Hour*24, "the time interval in second before NAT type is retested, 0s disables retest. Valid time units are "s", "m", "h". ") SummaryInterval := flag.Duration("summary-interval", time.Hour, @@ -40,6 +42,9 @@ func main() {
NATTypeMeasurementInterval: *NATTypeMeasurementInterval, EventDispatcher: eventLogger, + + RelayDomainNamePattern: *allowedRelayHostNamePattern, + AllowNonTLSRelay: *allowNonTLSRelay, }
var logOutput io.Writer = os.Stderr
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 2ebdc89c42dfb1331dd172282b4c2192bfbb4acc Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Apr 13 17:51:17 2022 +0100
Add Allowed Relay Hostname Pattern Indication --- broker/ipc.go | 3 ++- common/messages/messages_test.go | 2 +- common/messages/proxy.go | 16 ++++++++++------ proxy/lib/snowflake.go | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/broker/ipc.go b/broker/ipc.go index 780a9a5..fbaed48 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -66,8 +66,9 @@ func (i *IPC) Debug(_ interface{}, response *string) error { }
func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { - sid, proxyType, natType, clients, relayPattern, err := messages.DecodeProxyPollRequestWithRelayPrefix(arg.Body) + sid, proxyType, natType, clients, relayPattern, relayPatternSupported, err := messages.DecodeProxyPollRequestWithRelayPrefix(arg.Body) _ = relayPattern + _ = relayPatternSupported if err != nil { return messages.ErrBadRequest } diff --git a/common/messages/messages_test.go b/common/messages/messages_test.go index d1a5e96..017e959 100644 --- a/common/messages/messages_test.go +++ b/common/messages/messages_test.go @@ -108,7 +108,7 @@ func TestDecodeProxyPollRequest(t *testing.T) { err: fmt.Errorf(""), }, } { - sid, proxyType, natType, clients, relayPattern, err := DecodeProxyPollRequestWithRelayPrefix([]byte(test.data)) + sid, proxyType, natType, clients, relayPattern, _, err := DecodeProxyPollRequestWithRelayPrefix([]byte(test.data)) So(sid, ShouldResemble, test.sid) So(proxyType, ShouldResemble, test.proxyType) So(natType, ShouldResemble, test.natType) diff --git a/common/messages/proxy.go b/common/messages/proxy.go index d18a7c3..19cf6a3 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -97,7 +97,7 @@ type ProxyPollRequest struct { NAT string Clients int
- AcceptedRelayPattern string + AcceptedRelayPattern *string }
func EncodeProxyPollRequest(sid string, proxyType string, natType string, clients int) ([]byte, error) { @@ -111,13 +111,13 @@ func EncodeProxyPollRequestWithRelayPrefix(sid string, proxyType string, natType Type: proxyType, NAT: natType, Clients: clients, - AcceptedRelayPattern: relayPattern, + AcceptedRelayPattern: &relayPattern, }) }
func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType string, clients int, err error) { var relayPrefix string - sid, proxyType, natType, clients, relayPrefix, err = DecodeProxyPollRequestWithRelayPrefix(data) + sid, proxyType, natType, clients, relayPrefix, _, err = DecodeProxyPollRequestWithRelayPrefix(data) if relayPrefix != "" { return "", "", "", 0, ErrExtraInfo } @@ -128,7 +128,7 @@ func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType // sid, proxy type, nat type and clients of the proxy on success // and an error if it failed func DecodeProxyPollRequestWithRelayPrefix(data []byte) ( - sid string, proxyType string, natType string, clients int, relayPrefix string, err error) { + sid string, proxyType string, natType string, clients int, relayPrefix string, relayPrefixAware bool, err error) { var message ProxyPollRequest
err = json.Unmarshal(data, &message) @@ -164,8 +164,12 @@ func DecodeProxyPollRequestWithRelayPrefix(data []byte) ( if !KnownProxyTypes[message.Type] { message.Type = ProxyUnknown } - - return message.Sid, message.Type, message.NAT, message.Clients, message.AcceptedRelayPattern, nil + var acceptedRelayPattern = "" + if message.AcceptedRelayPattern != nil { + acceptedRelayPattern = *message.AcceptedRelayPattern + } + return message.Sid, message.Type, message.NAT, message.Clients, + acceptedRelayPattern, message.AcceptedRelayPattern != nil, nil }
type ProxyPollResponse struct { diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index 7dbc976..a60b5ab 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -210,7 +210,7 @@ func (s *SignalingServer) pollOffer(sid string, proxyType string, acceptedRelayP default: numClients := int((tokens.count() / 8) * 8) // Round down to 8 currentNATTypeLoaded := getCurrentNATType() - body, err := messages.EncodeProxyPollRequest(sid, proxyType, currentNATTypeLoaded, numClients) + body, err := messages.EncodeProxyPollRequestWithRelayPrefix(sid, proxyType, currentNATTypeLoaded, numClients, acceptedRelayPattern) if err != nil { log.Printf("Error encoding poll message: %s", err.Error()) return nil, ""
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit b18a9431b26af8c4e3c908f6e34c2340fc4911bc Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Thu Apr 14 11:15:35 2022 +0100
Add Broker Allowed Relay Pattern Indication Rejection for Proxy --- broker/broker.go | 23 ++++++++++++++++++----- broker/ipc.go | 6 ++++-- 2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/broker/broker.go b/broker/broker.go index 476bc81..8ca0120 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -20,6 +20,7 @@ import ( "syscall" "time"
+ "git.torproject.org/pluggable-transports/snowflake.git/v2/common/namematcher" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/safelog" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -38,8 +39,9 @@ type BrokerContext struct { proxyPolls chan *ProxyPoll metrics *Metrics
- bridgeList BridgeListHolderFileBased - allowedRelayPattern string + bridgeList BridgeListHolderFileBased + allowedRelayPattern string + presumedPatternForLegacyClient string }
func (ctx *BrokerContext) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { @@ -154,14 +156,24 @@ func (ctx *BrokerContext) AddSnowflake(id string, proxyType string, natType stri return snowflake }
-func (ctx *BrokerContext) InstallBridgeListProfile(reader io.Reader, relayPattern string) error { +func (ctx *BrokerContext) InstallBridgeListProfile(reader io.Reader, relayPattern, presumedPatternForLegacyClient string) error { if err := ctx.bridgeList.LoadBridgeInfo(reader); err != nil { return err } ctx.allowedRelayPattern = relayPattern + ctx.presumedPatternForLegacyClient = presumedPatternForLegacyClient return nil }
+func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bool) bool { + if nonSupported { + pattern = ctx.presumedPatternForLegacyClient + } + proxyPattern := namematcher.NewNameMatcher(pattern) + brokerPattern := namematcher.NewNameMatcher(ctx.allowedRelayPattern) + return proxyPattern.IsSupersetOf(brokerPattern) +} + // Client offer contains an SDP, bridge fingerprint and the NAT type of the client type ClientOffer struct { natType string @@ -176,7 +188,7 @@ func main() { var addr string var geoipDatabase string var geoip6Database string - var bridgeListFilePath, allowedRelayPattern string + var bridgeListFilePath, allowedRelayPattern, presumedPatternForLegacyClient string var disableTLS bool var certFilename, keyFilename string var disableGeoip bool @@ -193,6 +205,7 @@ func main() { flag.StringVar(&geoip6Database, "geoip6db", "/usr/share/tor/geoip6", "path to correctly formatted geoip database mapping IPv6 address ranges to country codes") flag.StringVar(&bridgeListFilePath, "bridge-list-path", "", "file path for bridgeListFile") flag.StringVar(&allowedRelayPattern, "allowed-relay-pattern", "", "allowed pattern for relay host name") + flag.StringVar(&presumedPatternForLegacyClient, "default-relay-pattern", "", "presumed pattern for legacy client") flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS") flag.BoolVar(&disableGeoip, "disable-geoip", false, "don't use geoip for stats collection") flag.StringVar(&metricsFilename, "metrics-log", "", "path to metrics logging output") @@ -230,7 +243,7 @@ func main() { if err != nil { log.Fatal(err.Error()) } - err = ctx.InstallBridgeListProfile(bridgeListFile, allowedRelayPattern) + err = ctx.InstallBridgeListProfile(bridgeListFile, allowedRelayPattern, presumedPatternForLegacyClient) if err != nil { log.Fatal(err.Error()) } diff --git a/broker/ipc.go b/broker/ipc.go index fbaed48..97f26ef 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -67,12 +67,14 @@ func (i *IPC) Debug(_ interface{}, response *string) error {
func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { sid, proxyType, natType, clients, relayPattern, relayPatternSupported, err := messages.DecodeProxyPollRequestWithRelayPrefix(arg.Body) - _ = relayPattern - _ = relayPatternSupported if err != nil { return messages.ErrBadRequest }
+ if !i.ctx.CheckProxyRelayPattern(relayPattern, !relayPatternSupported) { + return fmt.Errorf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest) + } + // Log geoip stats remoteIP, _, err := net.SplitHostPort(arg.RemoteAddr) if err != nil {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 3ebb5a4186581784a2d2ada6a5ce1c703030c3bb Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Thu Apr 21 12:00:15 2022 +0100
Show relay URL when connecting to relay --- proxy/lib/snowflake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index a60b5ab..2770aa4 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -331,7 +331,7 @@ func (sf *SnowflakeProxy) datachannelHandler(conn *webRTCConn, remoteAddr net.Ad return } wsConn := websocketconn.New(ws) - log.Printf("connected to relay") + log.Printf("connected to relay: %v", relayURL) defer wsConn.Close() copyLoop(conn, wsConn, sf.shutdown) log.Printf("datachannelHandler ends")
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 6e8fbe54eeebc0ffbf84d4dd82e3e9a87d7729c4 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Fri Apr 29 17:12:59 2022 +0100
Rejection reason feedback --- broker/ipc.go | 10 ++++++++-- common/messages/proxy.go | 12 ++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/broker/ipc.go b/broker/ipc.go index 97f26ef..5a93585 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -72,7 +72,13 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { }
if !i.ctx.CheckProxyRelayPattern(relayPattern, !relayPatternSupported) { - return fmt.Errorf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest) + log.Printf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest) + b, err := messages.EncodePollResponseWithRelayURL("", false, "", "", "incorrect relay pattern") + *response = b + if err != nil { + return messages.ErrInternal + } + return nil }
// Log geoip stats @@ -112,7 +118,7 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { } else { relayURL = info.WebSocketAddress } - b, err = messages.EncodePollResponseWithRelayURL(string(offer.sdp), true, offer.natType, relayURL) + b, err = messages.EncodePollResponseWithRelayURL(string(offer.sdp), true, offer.natType, relayURL, "") if err != nil { return messages.ErrInternal } diff --git a/common/messages/proxy.go b/common/messages/proxy.go index 19cf6a3..6ea2c8a 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -181,10 +181,10 @@ type ProxyPollResponse struct { }
func EncodePollResponse(offer string, success bool, natType string) ([]byte, error) { - return EncodePollResponseWithRelayURL(offer, success, natType, "") + return EncodePollResponseWithRelayURL(offer, success, natType, "", "no match") }
-func EncodePollResponseWithRelayURL(offer string, success bool, natType, relayURL string) ([]byte, error) { +func EncodePollResponseWithRelayURL(offer string, success bool, natType, relayURL, failReason string) ([]byte, error) { if success { return json.Marshal(ProxyPollResponse{ Status: "client match", @@ -195,7 +195,7 @@ func EncodePollResponseWithRelayURL(offer string, success bool, natType, relayUR
} return json.Marshal(ProxyPollResponse{ - Status: "no match", + Status: failReason, }) } func DecodePollResponse(data []byte) (string, string, error) { @@ -219,12 +219,16 @@ func DecodePollResponseWithRelayURL(data []byte) (string, string, string, error) return "", "", "", fmt.Errorf("received invalid data") }
+ err = nil if message.Status == "client match" { if message.Offer == "" { return "", "", "", fmt.Errorf("no supplied offer") } } else { message.Offer = "" + if message.Status != "no match" { + err = errors.New(message.Status) + } }
natType := message.NAT @@ -232,7 +236,7 @@ func DecodePollResponseWithRelayURL(data []byte) (string, string, string, error) natType = "unknown" }
- return message.Offer, natType, message.RelayURL, nil + return message.Offer, natType, message.RelayURL, err }
type ProxyAnswerRequest struct {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 1b48ee14f47f6b5ac4b061c2bd50aaf58b2fff4f Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon May 2 13:24:39 2022 +0100
Add test for proxy poll with Relay URL --- common/messages/messages_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/common/messages/messages_test.go b/common/messages/messages_test.go index 017e959..2fc76df 100644 --- a/common/messages/messages_test.go +++ b/common/messages/messages_test.go @@ -194,6 +194,34 @@ func TestEncodeProxyPollResponse(t *testing.T) { So(err, ShouldEqual, nil) }) } + +func TestEncodeProxyPollResponseWithProxyURL(t *testing.T) { + Convey("Context", t, func() { + b, err := EncodePollResponseWithRelayURL("fake offer", true, "restricted", "wss://test/", "") + So(err, ShouldBeNil) + offer, natType, err := DecodePollResponse(b) + So(err, ShouldNotBeNil) + + offer, natType, relay, err := DecodePollResponseWithRelayURL(b) + So(offer, ShouldEqual, "fake offer") + So(natType, ShouldEqual, "restricted") + So(relay, ShouldEqual, "wss://test/") + So(err, ShouldBeNil) + + b, err = EncodePollResponse("", false, "unknown") + So(err, ShouldBeNil) + offer, natType, relay, err = DecodePollResponseWithRelayURL(b) + So(offer, ShouldEqual, "") + So(natType, ShouldEqual, "unknown") + So(err, ShouldBeNil) + + b, err = EncodePollResponseWithRelayURL("fake offer", false, "restricted", "wss://test/", "test error reason") + So(err, ShouldBeNil) + offer, natType, relay, err = DecodePollResponseWithRelayURL(b) + So(err, ShouldNotBeNil) + So(err.Error(), ShouldContainSubstring, "test error reason") + }) +} func TestDecodeProxyAnswerRequest(t *testing.T) { Convey("Context", t, func() { for _, test := range []struct {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit b391d986799459a843e1596c0ffe60c46e2e4d25 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon May 2 14:03:32 2022 +0100
Add Proxy Relay URL Support Counting Metrics Output --- broker/metrics.go | 28 ++++++++++++++++++++++++++++ broker/snowflake-broker_test.go | 4 ++-- 2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/broker/metrics.go b/broker/metrics.go index c642045..5d95cb4 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -49,6 +49,9 @@ type Metrics struct { clientUnrestrictedDeniedCount uint clientProxyMatchCount uint
+ proxyPollWithRelayURLExtension uint + proxyPollWithoutRelayURLExtension uint + // synchronization for access to snowflake metrics lock sync.Mutex
@@ -189,6 +192,8 @@ func (m *Metrics) printMetrics() { } m.logger.Println("snowflake-ips-total", total) m.logger.Println("snowflake-idle-count", binCount(m.proxyIdleCount)) + m.logger.Println("snowflake-proxy-poll-with-relay-url-count", binCount(m.proxyPollWithRelayURLExtension)) + m.logger.Println("snowflake-proxy-poll-without-relay-url-count", binCount(m.proxyPollWithoutRelayURLExtension)) m.logger.Println("client-denied-count", binCount(m.clientDeniedCount)) m.logger.Println("client-restricted-denied-count", binCount(m.clientRestrictedDeniedCount)) m.logger.Println("client-unrestricted-denied-count", binCount(m.clientUnrestrictedDeniedCount)) @@ -227,6 +232,9 @@ type PromMetrics struct { ProxyPollTotal *RoundedCounterVec ClientPollTotal *RoundedCounterVec AvailableProxies *prometheus.GaugeVec + + ProxyPollWithRelayURLExtensionTotal *RoundedCounterVec + ProxyPollWithoutRelayURLExtensionTotal *RoundedCounterVec }
// Initialize metrics for prometheus exporter @@ -262,6 +270,24 @@ func initPrometheus() *PromMetrics { []string{"nat", "status"}, )
+ promMetrics.ProxyPollWithRelayURLExtensionTotal = NewRoundedCounterVec( + prometheus.CounterOpts{ + Namespace: prometheusNamespace, + Name: "rounded_proxy_poll_with_relay_url_extension_total", + Help: "The number of snowflake proxy polls with Relay URL Extension, rounded up to a multiple of 8", + }, + []string{"nat", "status"}, + ) + + promMetrics.ProxyPollWithoutRelayURLExtensionTotal = NewRoundedCounterVec( + prometheus.CounterOpts{ + Namespace: prometheusNamespace, + Name: "rounded_proxy_poll_without_relay_url_extension_total", + Help: "The number of snowflake proxy polls without Relay URL Extension, rounded up to a multiple of 8", + }, + []string{"nat", "status"}, + ) + promMetrics.ClientPollTotal = NewRoundedCounterVec( prometheus.CounterOpts{ Namespace: prometheusNamespace, @@ -275,6 +301,8 @@ func initPrometheus() *PromMetrics { promMetrics.registry.MustRegister( promMetrics.ClientPollTotal, promMetrics.ProxyPollTotal, promMetrics.ProxyTotal, promMetrics.AvailableProxies, + promMetrics.ProxyPollWithRelayURLExtensionTotal, + promMetrics.ProxyPollWithoutRelayURLExtensionTotal, )
return promMetrics diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index fdd1114..6a3ba62 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -560,7 +560,7 @@ func TestMetrics(t *testing.T) { So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-standalone 1\n") So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-badge 1\n") So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-webext 1\n") - So(metricsStr, ShouldEndWith, "\nsnowflake-ips-total 4\nsnowflake-idle-count 8\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1\n") + So(metricsStr, ShouldEndWith, "\nsnowflake-ips-total 4\nsnowflake-idle-count 8\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 8\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1\n") })
//Test addition of client failures @@ -584,7 +584,7 @@ func TestMetrics(t *testing.T) { So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-standalone 0\n") So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-badge 0\n") So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-webext 0\n") - So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-total 0\nsnowflake-idle-count 0\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0\n") + So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-total 0\nsnowflake-idle-count 0\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 0\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0\n") }) //Test addition of client matches Convey("for client-proxy match", func() {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 7caab017850fdf7cb79e93b60ab9bd6baa28b027 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon May 2 14:15:41 2022 +0100
Fixed desynchronized comment and behavior for log interval
In 64ce7dff1b38ecda027d67c8ba54d8290755afa0, the log interval is modified while the comment is left unchanged. --- broker/metrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/broker/metrics.go b/broker/metrics.go index 5d95cb4..fbf3452 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -166,7 +166,7 @@ func NewMetrics(metricsLogger *log.Logger) (*Metrics, error) { m.logger = metricsLogger m.promMetrics = initPrometheus()
- // Write to log file every hour with updated metrics + // Write to log file every day with updated metrics go m.logMetrics()
return m, nil
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit b78eb74e42e58827ac579e05049f4d9f5cd6f23a Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon May 2 16:13:43 2022 +0100
Add Proxy Relay URL Rejection Metrics --- broker/metrics.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/broker/metrics.go b/broker/metrics.go index fbf3452..d9f8123 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -49,8 +49,9 @@ type Metrics struct { clientUnrestrictedDeniedCount uint clientProxyMatchCount uint
- proxyPollWithRelayURLExtension uint - proxyPollWithoutRelayURLExtension uint + proxyPollWithRelayURLExtension uint + proxyPollWithoutRelayURLExtension uint + proxyPollRejectedWithRelayURLExtension uint
// synchronization for access to snowflake metrics lock sync.Mutex @@ -194,6 +195,7 @@ func (m *Metrics) printMetrics() { m.logger.Println("snowflake-idle-count", binCount(m.proxyIdleCount)) m.logger.Println("snowflake-proxy-poll-with-relay-url-count", binCount(m.proxyPollWithRelayURLExtension)) m.logger.Println("snowflake-proxy-poll-without-relay-url-count", binCount(m.proxyPollWithoutRelayURLExtension)) + m.logger.Println("snowflake-proxy-rejected-for-relay-url-count", binCount(m.proxyPollRejectedWithRelayURLExtension)) m.logger.Println("client-denied-count", binCount(m.clientDeniedCount)) m.logger.Println("client-restricted-denied-count", binCount(m.clientRestrictedDeniedCount)) m.logger.Println("client-unrestricted-denied-count", binCount(m.clientUnrestrictedDeniedCount)) @@ -235,6 +237,8 @@ type PromMetrics struct {
ProxyPollWithRelayURLExtensionTotal *RoundedCounterVec ProxyPollWithoutRelayURLExtensionTotal *RoundedCounterVec + + ProxyPollRejectedForRelayURLExtensionTotal *RoundedCounterVec }
// Initialize metrics for prometheus exporter @@ -276,7 +280,7 @@ func initPrometheus() *PromMetrics { Name: "rounded_proxy_poll_with_relay_url_extension_total", Help: "The number of snowflake proxy polls with Relay URL Extension, rounded up to a multiple of 8", }, - []string{"nat", "status"}, + []string{"nat"}, )
promMetrics.ProxyPollWithoutRelayURLExtensionTotal = NewRoundedCounterVec( @@ -285,7 +289,16 @@ func initPrometheus() *PromMetrics { Name: "rounded_proxy_poll_without_relay_url_extension_total", Help: "The number of snowflake proxy polls without Relay URL Extension, rounded up to a multiple of 8", }, - []string{"nat", "status"}, + []string{"nat"}, + ) + + promMetrics.ProxyPollRejectedForRelayURLExtensionTotal = NewRoundedCounterVec( + prometheus.CounterOpts{ + Namespace: prometheusNamespace, + Name: "rounded_proxy_poll_rejected_relay_url_extension_total", + Help: "The number of snowflake proxy polls rejected by Relay URL Extension, rounded up to a multiple of 8", + }, + []string{"nat"}, )
promMetrics.ClientPollTotal = NewRoundedCounterVec(
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit dd61e2be0f65aed72b0740aa22debab7246ebc48 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Mon May 2 16:19:27 2022 +0100
Add Proxy Relay URL Metrics Collection --- broker/ipc.go | 17 +++++++++++++++++ broker/metrics.go | 1 + broker/snowflake-broker_test.go | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/broker/ipc.go b/broker/ipc.go index 5a93585..c86d1a7 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -71,7 +71,24 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error { return messages.ErrBadRequest }
+ if !relayPatternSupported { + i.ctx.metrics.lock.Lock() + i.ctx.metrics.proxyPollWithoutRelayURLExtension++ + i.ctx.metrics.promMetrics.ProxyPollWithoutRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType}).Inc() + i.ctx.metrics.lock.Unlock() + } else { + i.ctx.metrics.lock.Lock() + i.ctx.metrics.proxyPollWithRelayURLExtension++ + i.ctx.metrics.promMetrics.ProxyPollWithRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType}).Inc() + i.ctx.metrics.lock.Unlock() + } + if !i.ctx.CheckProxyRelayPattern(relayPattern, !relayPatternSupported) { + i.ctx.metrics.lock.Lock() + i.ctx.metrics.proxyPollRejectedWithRelayURLExtension++ + i.ctx.metrics.promMetrics.ProxyPollRejectedForRelayURLExtensionTotal.With(prometheus.Labels{"nat": natType}).Inc() + i.ctx.metrics.lock.Unlock() + log.Printf("bad request: rejected relay pattern from proxy = %v", messages.ErrBadRequest) b, err := messages.EncodePollResponseWithRelayURL("", false, "", "", "incorrect relay pattern") *response = b diff --git a/broker/metrics.go b/broker/metrics.go index d9f8123..eecc137 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -316,6 +316,7 @@ func initPrometheus() *PromMetrics { promMetrics.ProxyTotal, promMetrics.AvailableProxies, promMetrics.ProxyPollWithRelayURLExtensionTotal, promMetrics.ProxyPollWithoutRelayURLExtensionTotal, + promMetrics.ProxyPollRejectedForRelayURLExtensionTotal, )
return promMetrics diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index 6a3ba62..aee8578 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -560,7 +560,7 @@ func TestMetrics(t *testing.T) { So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-standalone 1\n") So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-badge 1\n") So(metricsStr, ShouldContainSubstring, "\nsnowflake-ips-webext 1\n") - So(metricsStr, ShouldEndWith, "\nsnowflake-ips-total 4\nsnowflake-idle-count 8\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 8\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1\n") + So(metricsStr, ShouldEndWith, "\nsnowflake-ips-total 4\nsnowflake-idle-count 8\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 8\nsnowflake-proxy-rejected-for-relay-url-count 0\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1\n") })
//Test addition of client failures @@ -584,7 +584,7 @@ func TestMetrics(t *testing.T) { So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-standalone 0\n") So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-badge 0\n") So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-webext 0\n") - So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-total 0\nsnowflake-idle-count 0\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 0\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0\n") + So(buf.String(), ShouldContainSubstring, "\nsnowflake-ips-total 0\nsnowflake-idle-count 0\nsnowflake-proxy-poll-with-relay-url-count 0\nsnowflake-proxy-poll-without-relay-url-count 0\nsnowflake-proxy-rejected-for-relay-url-count 0\nclient-denied-count 0\nclient-restricted-denied-count 0\nclient-unrestricted-denied-count 0\nclient-snowflake-match-count 0\nsnowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0\n") }) //Test addition of client matches Convey("for client-proxy match", func() {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit f789dce6d2b5e6e7d02eef6b168c31ed2ddd149e Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Tue May 17 15:53:15 2022 +0100
Represent Bridge Fingerprint As String --- broker/bridge-list.go | 22 +++++++++++----------- broker/bridge-list_test.go | 9 +++++++-- broker/broker.go | 5 +++-- broker/ipc.go | 17 ++++++++++++++--- broker/snowflake-broker_test.go | 2 +- common/bridgefingerprint/fingerprint.go | 30 ++++++++++++++++++++++++++++++ common/messages/client.go | 5 +++-- 7 files changed, 69 insertions(+), 21 deletions(-)
diff --git a/broker/bridge-list.go b/broker/bridge-list.go index e77db65..ca2c041 100644 --- a/broker/bridge-list.go +++ b/broker/bridge-list.go @@ -2,27 +2,26 @@ package main
import ( "bufio" - "encoding/hex" "encoding/json" "errors" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" "io" "sync" )
var ErrBridgeNotFound = errors.New("bridge not found") -var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid")
func NewBridgeListHolder() BridgeListHolderFileBased { return &bridgeListHolder{} }
type bridgeListHolder struct { - bridgeInfo map[[20]byte]BridgeInfo + bridgeInfo map[bridgefingerprint.Fingerprint]BridgeInfo accessBridgeInfo sync.RWMutex }
type BridgeListHolder interface { - GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) + GetBridgeInfo(bridgefingerprint.Fingerprint) (BridgeInfo, error) }
type BridgeListHolderFileBased interface { @@ -36,7 +35,7 @@ type BridgeInfo struct { Fingerprint string `json:"fingerprint"` }
-func (h *bridgeListHolder) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { +func (h *bridgeListHolder) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) { h.accessBridgeInfo.RLock() defer h.accessBridgeInfo.RUnlock() if bridgeInfo, ok := h.bridgeInfo[fingerprint]; ok { @@ -46,7 +45,7 @@ func (h *bridgeListHolder) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, erro }
func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error { - bridgeInfoMap := map[[20]byte]BridgeInfo{} + bridgeInfoMap := map[bridgefingerprint.Fingerprint]BridgeInfo{} inputScanner := bufio.NewScanner(reader) for inputScanner.Scan() { inputLine := inputScanner.Bytes() @@ -54,13 +53,14 @@ func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error { if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil { return err } - var bridgeHash [20]byte - if n, err := hex.Decode(bridgeHash[:], []byte(bridgeInfo.Fingerprint)); err != nil { + + var bridgeFingerprint bridgefingerprint.Fingerprint + var err error + if bridgeFingerprint, err = bridgefingerprint.FingerprintFromHexString(bridgeInfo.Fingerprint); err != nil { return err - } else if n != 20 { - return ErrBridgeFingerprintInvalid } - bridgeInfoMap[bridgeHash] = bridgeInfo + + bridgeInfoMap[bridgeFingerprint] = bridgeInfo } h.accessBridgeInfo.Lock() defer h.accessBridgeInfo.Unlock() diff --git a/broker/bridge-list_test.go b/broker/bridge-list_test.go index 73da43c..4b53821 100644 --- a/broker/bridge-list_test.go +++ b/broker/bridge-list_test.go @@ -3,6 +3,7 @@ package main import ( "bytes" "encoding/hex" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" . "github.com/smartystreets/goconvey/convey" "testing" ) @@ -34,7 +35,9 @@ func TestBridgeLoad(t *testing.T) { So(n, ShouldEqual, 20) So(err, ShouldBeNil) } - bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) + Fingerprint, err := bridgefingerprint.FingerprintFromBytes(bridgeFingerprint[:]) + So(err, ShouldBeNil) + bridgeInfo, err := bridgeList.GetBridgeInfo(Fingerprint) So(err, ShouldBeNil) So(bridgeInfo.DisplayName, ShouldEqual, "default") So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org") @@ -50,7 +53,9 @@ func TestBridgeLoad(t *testing.T) { So(n, ShouldEqual, 20) So(err, ShouldBeNil) } - bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) + Fingerprint, err := bridgefingerprint.FingerprintFromBytes(bridgeFingerprint[:]) + So(err, ShouldBeNil) + bridgeInfo, err := bridgeList.GetBridgeInfo(Fingerprint) So(err, ShouldBeNil) So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8") So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org") diff --git a/broker/broker.go b/broker/broker.go index 8ca0120..9162370 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -10,6 +10,7 @@ import ( "container/heap" "crypto/tls" "flag" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" "io" "log" "net/http" @@ -44,7 +45,7 @@ type BrokerContext struct { presumedPatternForLegacyClient string }
-func (ctx *BrokerContext) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { +func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) { return ctx.bridgeList.GetBridgeInfo(fingerprint) }
@@ -178,7 +179,7 @@ func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bo type ClientOffer struct { natType string sdp []byte - fingerprint [20]byte + fingerprint []byte }
func main() { diff --git a/broker/ipc.go b/broker/ipc.go index c86d1a7..f5d4747 100644 --- a/broker/ipc.go +++ b/broker/ipc.go @@ -4,6 +4,7 @@ import ( "container/heap" "encoding/hex" "fmt" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" "log" "net" "time" @@ -130,7 +131,11 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc() var relayURL string - if info, err := i.ctx.bridgeList.GetBridgeInfo(offer.fingerprint); err != nil { + bridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(offer.fingerprint) + if err != nil { + return messages.ErrBadRequest + } + if info, err := i.ctx.bridgeList.GetBridgeInfo(bridgeFingerprint); err != nil { return err } else { relayURL = info.WebSocketAddress @@ -172,12 +177,18 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error { if err != nil { return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response) } - copy(offer.fingerprint[:], fingerprint)
- if _, err := i.ctx.GetBridgeInfo(offer.fingerprint); err != nil { + BridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(fingerprint) + if err != nil { + return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response) + } + + if _, err := i.ctx.GetBridgeInfo(BridgeFingerprint); err != nil { return err }
+ offer.fingerprint = BridgeFingerprint.ToBytes() + // Only hand out known restricted snowflakes to unrestricted clients var snowflakeHeap *SnowflakeHeap if offer.natType == NATUnrestricted { diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index aee8578..a72f3ac 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -258,7 +258,7 @@ func TestBroker(t *testing.T) { // Pass a fake client offer to this proxy p := <-ctx.proxyPolls So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp") - p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge} + p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge[:]} <-done So(w.Code, ShouldEqual, http.StatusOK) So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`) diff --git a/common/bridgefingerprint/fingerprint.go b/common/bridgefingerprint/fingerprint.go new file mode 100644 index 0000000..1a89773 --- /dev/null +++ b/common/bridgefingerprint/fingerprint.go @@ -0,0 +1,30 @@ +package bridgefingerprint + +import ( + "encoding/hex" + "errors" +) + +type Fingerprint string + +var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid") + +func FingerprintFromBytes(bytes []byte) (Fingerprint, error) { + n := len(bytes) + if n != 20 && n != 32 { + return Fingerprint(""), ErrBridgeFingerprintInvalid + } + return Fingerprint(bytes), nil +} + +func FingerprintFromHexString(hexString string) (Fingerprint, error) { + decoded, err := hex.DecodeString(hexString) + if err != nil { + return "", err + } + return FingerprintFromBytes(decoded) +} + +func (f Fingerprint) ToBytes() []byte { + return []byte(f) +} diff --git a/common/messages/client.go b/common/messages/client.go index 96f8ed8..af63e08 100644 --- a/common/messages/client.go +++ b/common/messages/client.go @@ -5,9 +5,9 @@ package messages
import ( "bytes" - "encoding/hex" "encoding/json" "fmt" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/nat" ) @@ -106,7 +106,8 @@ func DecodeClientPollRequest(data []byte) (*ClientPollRequest, error) { if message.Fingerprint == "" { message.Fingerprint = defaultBridgeFingerprint } - if hex.DecodedLen(len(message.Fingerprint)) != 20 { + + if _, err := bridgefingerprint.FingerprintFromHexString(message.Fingerprint); err != nil { return nil, fmt.Errorf("cannot decode fingerprint") }
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit c5e5b45b062098389c22710745ae2f7372d299e4 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Tue May 17 17:44:37 2022 +0100
Update message protocol version to 1.3 for RelayURL --- common/messages/proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/common/messages/proxy.go b/common/messages/proxy.go index 6ea2c8a..aa55c22 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -13,7 +13,7 @@ import ( )
const ( - version = "1.2" + version = "1.3" ProxyUnknown = "unknown" )
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 8ab45651d094de98ff48c5900e6de50a74a0f867 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed May 18 12:19:21 2022 +0100
Disallow unknown bridge list file field --- broker/bridge-list.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/broker/bridge-list.go b/broker/bridge-list.go index ca2c041..8a80f6a 100644 --- a/broker/bridge-list.go +++ b/broker/bridge-list.go @@ -2,11 +2,13 @@ package main
import ( "bufio" + "bytes" "encoding/json" "errors" - "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" "io" "sync" + + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint" )
var ErrBridgeNotFound = errors.New("bridge not found") @@ -50,7 +52,9 @@ func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error { for inputScanner.Scan() { inputLine := inputScanner.Bytes() bridgeInfo := BridgeInfo{} - if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil { + decoder := json.NewDecoder(bytes.NewReader(inputLine)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&bridgeInfo); err != nil { return err }
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 8ba89179f1f862a4957ed28a88b6a08167b653e9 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed May 18 14:11:35 2022 +0100
Add document for LoadBridgeInfo input --- broker/bridge-list.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/broker/bridge-list.go b/broker/bridge-list.go index 8a80f6a..4433a12 100644 --- a/broker/bridge-list.go +++ b/broker/bridge-list.go @@ -1,3 +1,24 @@ +/* (*BridgeListHolderFileBased).LoadBridgeInfo loads a Snowflake Server bridge info description file, + its format is as follows: + + This file should be in newline-delimited JSON format(https://jsonlines.org/). + For each line, the format of json data should be in the format of: + {"displayName":"default", "webSocketAddress":"wss://snowflake.torproject.net/", "fingerprint":"2B280B23E1107BB62ABFC40DDCC8824814F80A72"} + + displayName:string is the name of this bridge. This value is not currently used programmatically. + + webSocketAddress:string is the WebSocket URL of this bridge. + This will be the address proxy used to connect to this snowflake server. + + fingerprint:string is the identifier of the bridge. + This will be used by a client to identify the bridge it wishes to connect to. + + The existence of ANY other fields is NOT permitted. + + The file will be considered invalid if there is at least one invalid json record. + In this case, an error will be returned, and none of the records will be loaded. +*/ + package main
import (
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit a4bbb728e611dbf5bd8e2021e8b1e654923e5c1d Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed May 18 15:52:46 2022 +0100
Fix not zero metrics for 1.3 values --- broker/metrics.go | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/broker/metrics.go b/broker/metrics.go index eecc137..639d505 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -212,6 +212,9 @@ func (m *Metrics) zeroMetrics() { m.clientDeniedCount = 0 m.clientRestrictedDeniedCount = 0 m.clientUnrestrictedDeniedCount = 0 + m.proxyPollRejectedWithRelayURLExtension = 0 + m.proxyPollWithRelayURLExtension = 0 + m.proxyPollWithoutRelayURLExtension = 0 m.clientProxyMatchCount = 0 m.countryStats.counts = make(map[string]int) for pType := range m.countryStats.proxies {
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 0ae4d821f0c8440a3d1d9771f16912ef897754be Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed May 18 16:19:38 2022 +0100
Move ErrExtraInfo to ipc.go --- common/messages/ipc.go | 1 + common/messages/proxy.go | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/common/messages/ipc.go b/common/messages/ipc.go index 13e096f..3250742 100644 --- a/common/messages/ipc.go +++ b/common/messages/ipc.go @@ -12,6 +12,7 @@ type Arg struct { var ( ErrBadRequest = errors.New("bad request") ErrInternal = errors.New("internal error") + ErrExtraInfo = errors.New("client sent extra info")
StrTimedOut = "timed out waiting for answer!" StrNoProxies = "no snowflake proxies currently available" diff --git a/common/messages/proxy.go b/common/messages/proxy.go index aa55c22..41af4bf 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -24,8 +24,6 @@ var KnownProxyTypes = map[string]bool{ "iptproxy": true, }
-var ErrExtraInfo = errors.New("client sent extra info") - /* Version 1.2 specification:
== ProxyPollRequest ==
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit e5b799d618fc9a1669626c6ca1a6e759192640b2 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed May 18 16:49:19 2022 +0100
Update documents for broker messages --- common/messages/proxy.go | 12 +++++++----- doc/broker-spec.txt | 28 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/common/messages/proxy.go b/common/messages/proxy.go index 41af4bf..b135c34 100644 --- a/common/messages/proxy.go +++ b/common/messages/proxy.go @@ -24,15 +24,16 @@ var KnownProxyTypes = map[string]bool{ "iptproxy": true, }
-/* Version 1.2 specification: +/* Version 1.3 specification:
== ProxyPollRequest == { Sid: [generated session id of proxy], - Version: 1.2, + Version: 1.3, Type: ["badge"|"webext"|"standalone"], NAT: ["unknown"|"restricted"|"unrestricted"], - Clients: [number of current clients, rounded down to multiples of 8] + Clients: [number of current clients, rounded down to multiples of 8], + AcceptedRelayPattern: [a pattern representing accepted set of relay domains] }
== ProxyPollResponse == @@ -44,7 +45,8 @@ HTTP 200 OK type: offer, sdp: [WebRTC SDP] }, - NAT: ["unknown"|"restricted"|"unrestricted"] + NAT: ["unknown"|"restricted"|"unrestricted"], + RelayURL: [the WebSocket URL proxy should connect to relay Snowflake traffic] }
2) If a client is not matched: @@ -60,7 +62,7 @@ HTTP 400 BadRequest == ProxyAnswerRequest == { Sid: [generated session id of proxy], - Version: 1.2, + Version: 1.3, Answer: { type: answer, diff --git a/doc/broker-spec.txt b/doc/broker-spec.txt index f25be79..b138605 100644 --- a/doc/broker-spec.txt +++ b/doc/broker-spec.txt @@ -100,6 +100,24 @@ Metrics data from the Snowflake broker can be retrieved by sending an HTTP GET r A count of the total number of unique IP addresses of snowflake proxies that have an unknown NAT type.
+ "snowflake-proxy-poll-with-relay-url-count" NUM NL + [At most once.] + + A count of snowflake proxy polls with relay url extension present. + This means this proxy understands relay url, and is sending its + allowed prefix. + "snowflake-proxy-poll-without-relay-url-count" NUM NL + [At most once.] + + A count of snowflake proxy polls with relay url extension absent. + This means this proxy is not yet updated. + "snowflake-proxy-rejected-for-relay-url-count" NUM NL + [At most once.] + + A count of snowflake proxy polls with relay url extension rejected + based on broker's relay url extension policy. + This means an incompatible allowed relay pattern is included in the + proxy poll message. 2. Broker messaging specification and endpoints
The broker facilitates the connection of snowflake clients and snowflake proxies @@ -177,10 +195,11 @@ POST /proxy HTTP
{ Sid: [generated session id of proxy], - Version: 1.1, + Version: 1.3, Type: ["badge"|"webext"|"standalone"|"mobile"], NAT: ["unknown"|"restricted"|"unrestricted"], - Clients: [number of current clients, rounded down to multiples of 8] + Clients: [number of current clients, rounded down to multiples of 8], + AcceptedRelayPattern: [a pattern representing accepted set of relay domains] } ```
@@ -195,7 +214,8 @@ HTTP 200 OK { type: offer, sdp: [WebRTC SDP] - } + }, + RelayURL: [the WebSocket URL proxy should connect to relay Snowflake traffic] } ```
@@ -220,7 +240,7 @@ POST /answer HTTP
{ Sid: [generated session id of proxy], - Version: 1.1, + Version: 1.3, Answer: { type: answer,
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit ddf72025d199db0ebd265f4b0ccc11dc243d88f9 Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Jun 1 14:39:53 2022 +0100
Restrict Allowed Relay to Tor Pool by default --- proxy/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxy/main.go b/proxy/main.go index 305d0b0..63ed5c7 100644 --- a/proxy/main.go +++ b/proxy/main.go @@ -21,7 +21,7 @@ func main() { unsafeLogging := flag.Bool("unsafe-logging", false, "prevent logs from being scrubbed") keepLocalAddresses := flag.Bool("keep-local-addresses", false, "keep local LAN address ICE candidates") relayURL := flag.String("relay", sf.DefaultRelayURL, "websocket relay URL") - allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "", "a pattern to specify allowed hostname pattern for relay URL.") + allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "snowflake.torproject.net", "a pattern to specify allowed hostname pattern for relay URL.") allowNonTLSRelay := flag.Bool("allow-non-tls-relay", false, "allow relay without tls encryption") NATTypeMeasurementInterval := flag.Duration("nat-retest-interval", time.Hour*24, "the time interval in second before NAT type is retested, 0s disables retest. Valid time units are "s", "m", "h". ")
This is an automated email from the git hooks/post-receive script.
shelikhoo pushed a commit to branch main in repository pluggable-transports/snowflake.
commit 97dea533da7b6b3b2b1dfbffe7dca3a8350fab0b Author: Shelikhoo xiaokangwang@outlook.com AuthorDate: Wed Jun 15 13:20:58 2022 +0100
Update Relay Pattern format to include dollar sign --- common/namematcher/matcher.go | 5 +++++ common/namematcher/matcher_test.go | 26 +++++++++++++------------- proxy/lib/snowflake.go | 6 +++++- proxy/main.go | 2 +- 4 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/common/namematcher/matcher.go b/common/namematcher/matcher.go index 57f9c56..afcdbff 100644 --- a/common/namematcher/matcher.go +++ b/common/namematcher/matcher.go @@ -3,9 +3,14 @@ package namematcher import "strings"
func NewNameMatcher(rule string) NameMatcher { + rule = strings.TrimSuffix(rule, "$") return NameMatcher{suffix: strings.TrimPrefix(rule, "^"), exact: strings.HasPrefix(rule, "^")} }
+func IsValidRule(rule string) bool { + return strings.HasSuffix(rule, "$") +} + type NameMatcher struct { exact bool suffix string diff --git a/common/namematcher/matcher_test.go b/common/namematcher/matcher_test.go index 8d92614..08d089c 100644 --- a/common/namematcher/matcher_test.go +++ b/common/namematcher/matcher_test.go @@ -11,13 +11,13 @@ func TestMatchMember(t *testing.T) { expects bool }{ {matcher: "", target: "", expects: true}, - {matcher: "^snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, - {matcher: "^snowflake.torproject.net", target: "faketorproject.net", expects: false}, - {matcher: "snowflake.torproject.net", target: "faketorproject.net", expects: false}, - {matcher: "snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "imaginary-01-snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "imaginary-aaa-snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "imaginary-aaa-snowflake.faketorproject.net", expects: false}, + {matcher: "^snowflake.torproject.net$", target: "snowflake.torproject.net", expects: true}, + {matcher: "^snowflake.torproject.net$", target: "faketorproject.net", expects: false}, + {matcher: "snowflake.torproject.net$", target: "faketorproject.net", expects: false}, + {matcher: "snowflake.torproject.net$", target: "snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net$", target: "imaginary-01-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net$", target: "imaginary-aaa-snowflake.torproject.net", expects: true}, + {matcher: "snowflake.torproject.net$", target: "imaginary-aaa-snowflake.faketorproject.net", expects: false}, } for _, v := range testingVector { t.Run(v.matcher+"<>"+v.target, func(t *testing.T) { @@ -36,12 +36,12 @@ func TestMatchSubset(t *testing.T) { expects bool }{ {matcher: "", target: "", expects: true}, - {matcher: "^snowflake.torproject.net", target: "^snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "^snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "testing-snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "^testing-snowflake.torproject.net", expects: true}, - {matcher: "snowflake.torproject.net", target: "", expects: false}, + {matcher: "^snowflake.torproject.net$", target: "^snowflake.torproject.net$", expects: true}, + {matcher: "snowflake.torproject.net$", target: "^snowflake.torproject.net$", expects: true}, + {matcher: "snowflake.torproject.net$", target: "snowflake.torproject.net$", expects: true}, + {matcher: "snowflake.torproject.net$", target: "testing-snowflake.torproject.net$", expects: true}, + {matcher: "snowflake.torproject.net$", target: "^testing-snowflake.torproject.net$", expects: true}, + {matcher: "snowflake.torproject.net$", target: "", expects: false}, } for _, v := range testingVector { t.Run(v.matcher+"<>"+v.target, func(t *testing.T) { diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index 2770aa4..34f8abe 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -30,7 +30,6 @@ import ( "crypto/rand" "encoding/base64" "fmt" - "git.torproject.org/pluggable-transports/snowflake.git/v2/common/namematcher" "io" "io/ioutil" "log" @@ -43,6 +42,7 @@ import (
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/event" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/messages" + "git.torproject.org/pluggable-transports/snowflake.git/v2/common/namematcher" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/task" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/util" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/websocketconn" @@ -582,6 +582,10 @@ func (sf *SnowflakeProxy) Start() error { return fmt.Errorf("invalid relay url: %s", err) }
+ if !namematcher.IsValidRule(sf.RelayDomainNamePattern) { + return fmt.Errorf("invalid relay domain name pattern") + } + config = webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { diff --git a/proxy/main.go b/proxy/main.go index 63ed5c7..c42852e 100644 --- a/proxy/main.go +++ b/proxy/main.go @@ -21,7 +21,7 @@ func main() { unsafeLogging := flag.Bool("unsafe-logging", false, "prevent logs from being scrubbed") keepLocalAddresses := flag.Bool("keep-local-addresses", false, "keep local LAN address ICE candidates") relayURL := flag.String("relay", sf.DefaultRelayURL, "websocket relay URL") - allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "snowflake.torproject.net", "a pattern to specify allowed hostname pattern for relay URL.") + allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "snowflake.torproject.net$", "a pattern to specify allowed hostname pattern for relay URL.") allowNonTLSRelay := flag.Bool("allow-non-tls-relay", false, "allow relay without tls encryption") NATTypeMeasurementInterval := flag.Duration("nat-retest-interval", time.Hour*24, "the time interval in second before NAT type is retested, 0s disables retest. Valid time units are "s", "m", "h". ")
tor-commits@lists.torproject.org