Add RelayURL and AllowedRelayPattern to snowflake signaling

This commit is contained in:
Shelikhoo 2022-04-05 15:03:31 +01:00
parent 38f0e00e5d
commit 613ceaf970
No known key found for this signature in database
GPG key ID: C4D5E79D22B25316
2 changed files with 141 additions and 86 deletions

View file

@ -17,90 +17,103 @@ func TestDecodeProxyPollRequest(t *testing.T) {
clients int clients int
data string data string
err error err error
acceptedRelayPattern string
}{ }{
{ {
//Version 1.0 proxy message //Version 1.0 proxy message
"ymbcCMto7KHNGYlp", sid: "ymbcCMto7KHNGYlp",
"unknown", proxyType: "unknown",
"unknown", natType: "unknown",
0, clients: 0,
`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`, data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`,
nil, err: nil,
}, },
{ {
//Version 1.1 proxy message //Version 1.1 proxy message
"ymbcCMto7KHNGYlp", sid: "ymbcCMto7KHNGYlp",
"standalone", proxyType: "standalone",
"unknown", natType: "unknown",
0, clients: 0,
`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.1","Type":"standalone"}`, data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.1","Type":"standalone"}`,
nil, err: nil,
}, },
{ {
//Version 1.2 proxy message //Version 1.2 proxy message
"ymbcCMto7KHNGYlp", sid: "ymbcCMto7KHNGYlp",
"standalone", proxyType: "standalone",
"restricted", natType: "restricted",
0, clients: 0,
`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted"}`, data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted"}`,
nil, err: nil,
}, },
{ {
//Version 1.2 proxy message with clients //Version 1.2 proxy message with clients
"ymbcCMto7KHNGYlp", sid: "ymbcCMto7KHNGYlp",
"standalone", proxyType: "standalone",
"restricted", natType: "restricted",
24, clients: 24,
`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted","Clients":24}`, data: `{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted","Clients":24}`,
nil, 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: //Version 0.X proxy message:
"", sid: "",
"", proxyType: "",
"", natType: "",
0, clients: 0,
"", data: "",
&json.SyntaxError{}, err: &json.SyntaxError{},
}, },
{ {
"", sid: "",
"", proxyType: "",
"", natType: "",
0, clients: 0,
`{"Sid":"ymbcCMto7KHNGYlp"}`, data: `{"Sid":"ymbcCMto7KHNGYlp"}`,
fmt.Errorf(""), err: fmt.Errorf(""),
}, },
{ {
"", sid: "",
"", proxyType: "",
"", natType: "",
0, clients: 0,
"{}", data: "{}",
fmt.Errorf(""), err: fmt.Errorf(""),
}, },
{ {
"", sid: "",
"", proxyType: "",
"", natType: "",
0, clients: 0,
`{"Version":"1.0"}`, data: `{"Version":"1.0"}`,
fmt.Errorf(""), err: fmt.Errorf(""),
}, },
{ {
"", sid: "",
"", proxyType: "",
"", natType: "",
0, clients: 0,
`{"Version":"2.0"}`, data: `{"Version":"2.0"}`,
fmt.Errorf(""), 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(sid, ShouldResemble, test.sid)
So(proxyType, ShouldResemble, test.proxyType) So(proxyType, ShouldResemble, test.proxyType)
So(natType, ShouldResemble, test.natType) So(natType, ShouldResemble, test.natType)
So(clients, ShouldEqual, test.clients) So(clients, ShouldEqual, test.clients)
So(relayPattern, ShouldResemble, test.acceptedRelayPattern)
So(err, ShouldHaveSameTypeAs, test.err) So(err, ShouldHaveSameTypeAs, test.err)
} }
@ -125,32 +138,40 @@ func TestDecodeProxyPollResponse(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
offer string offer string
data string data string
relayURL string
err error err error
}{ }{
{ {
"fake offer", offer: "fake offer",
`{"Status":"client match","Offer":"fake offer","NAT":"unknown"}`, data: `{"Status":"client match","Offer":"fake offer","NAT":"unknown"}`,
nil, err: nil,
}, },
{ {
"", offer: "fake offer",
`{"Status":"no match"}`, data: `{"Status":"client match","Offer":"fake offer","NAT":"unknown", "RelayURL":"wss://snowflake.torproject.org/proxy"}`,
nil, relayURL: "wss://snowflake.torproject.org/proxy",
err: nil,
}, },
{ {
"", offer: "",
`{"Status":"client match"}`, data: `{"Status":"no match"}`,
fmt.Errorf("no supplied offer"), err: nil,
}, },
{ {
"", offer: "",
`{"Test":"test"}`, data: `{"Status":"client match"}`,
fmt.Errorf(""), 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(err, ShouldHaveSameTypeAs, test.err)
So(offer, ShouldResemble, test.offer) So(offer, ShouldResemble, test.offer)
So(relayURL, ShouldResemble, test.relayURL)
} }
}) })

View file

@ -5,6 +5,7 @@ package messages
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"strings" "strings"
@ -23,6 +24,8 @@ var KnownProxyTypes = map[string]bool{
"iptproxy": true, "iptproxy": true,
} }
var ErrExtraInfo = errors.New("client sent extra info")
/* Version 1.2 specification: /* Version 1.2 specification:
== ProxyPollRequest == == ProxyPollRequest ==
@ -93,22 +96,39 @@ type ProxyPollRequest struct {
Type string Type string
NAT string NAT string
Clients int Clients int
AcceptedRelayPattern string
} }
func EncodeProxyPollRequest(sid string, proxyType string, natType string, clients int) ([]byte, error) { 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{ return json.Marshal(ProxyPollRequest{
Sid: sid, Sid: sid,
Version: version, Version: version,
Type: proxyType, Type: proxyType,
NAT: natType, NAT: natType,
Clients: clients, 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 // Decodes a poll message from a snowflake proxy and returns the
// sid, proxy type, nat type and clients of the proxy on success // sid, proxy type, nat type and clients of the proxy on success
// and an error if it failed // 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 var message ProxyPollRequest
err = json.Unmarshal(data, &message) err = json.Unmarshal(data, &message)
@ -145,21 +165,28 @@ func DecodeProxyPollRequest(data []byte) (sid string, proxyType string, natType
message.Type = ProxyUnknown 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 { type ProxyPollResponse struct {
Status string Status string
Offer string Offer string
NAT string NAT string
RelayURL string
} }
func EncodePollResponse(offer string, success bool, natType string) ([]byte, error) { 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 { if success {
return json.Marshal(ProxyPollResponse{ return json.Marshal(ProxyPollResponse{
Status: "client match", Status: "client match",
Offer: offer, Offer: offer,
NAT: natType, NAT: natType,
RelayURL: relayURL,
}) })
} }
@ -167,23 +194,30 @@ func EncodePollResponse(offer string, success bool, natType string) ([]byte, err
Status: "no match", 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 // 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 // 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 var message ProxyPollResponse
err := json.Unmarshal(data, &message) err := json.Unmarshal(data, &message)
if err != nil { if err != nil {
return "", "", err return "", "", "", err
} }
if message.Status == "" { if message.Status == "" {
return "", "", fmt.Errorf("received invalid data") return "", "", "", fmt.Errorf("received invalid data")
} }
if message.Status == "client match" { if message.Status == "client match" {
if message.Offer == "" { if message.Offer == "" {
return "", "", fmt.Errorf("no supplied offer") return "", "", "", fmt.Errorf("no supplied offer")
} }
} else { } else {
message.Offer = "" message.Offer = ""
@ -194,7 +228,7 @@ func DecodePollResponse(data []byte) (string, string, error) {
natType = "unknown" natType = "unknown"
} }
return message.Offer, natType, nil return message.Offer, natType, message.RelayURL, nil
} }
type ProxyAnswerRequest struct { type ProxyAnswerRequest struct {