mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
Represent Bridge Fingerprint As String
This commit is contained in:
parent
dd61e2be0f
commit
f789dce6d2
7 changed files with 69 additions and 21 deletions
|
@ -2,27 +2,26 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrBridgeNotFound = errors.New("bridge not found")
|
var ErrBridgeNotFound = errors.New("bridge not found")
|
||||||
var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid")
|
|
||||||
|
|
||||||
func NewBridgeListHolder() BridgeListHolderFileBased {
|
func NewBridgeListHolder() BridgeListHolderFileBased {
|
||||||
return &bridgeListHolder{}
|
return &bridgeListHolder{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type bridgeListHolder struct {
|
type bridgeListHolder struct {
|
||||||
bridgeInfo map[[20]byte]BridgeInfo
|
bridgeInfo map[bridgefingerprint.Fingerprint]BridgeInfo
|
||||||
accessBridgeInfo sync.RWMutex
|
accessBridgeInfo sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type BridgeListHolder interface {
|
type BridgeListHolder interface {
|
||||||
GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error)
|
GetBridgeInfo(bridgefingerprint.Fingerprint) (BridgeInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BridgeListHolderFileBased interface {
|
type BridgeListHolderFileBased interface {
|
||||||
|
@ -36,7 +35,7 @@ type BridgeInfo struct {
|
||||||
Fingerprint string `json:"fingerprint"`
|
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()
|
h.accessBridgeInfo.RLock()
|
||||||
defer h.accessBridgeInfo.RUnlock()
|
defer h.accessBridgeInfo.RUnlock()
|
||||||
if bridgeInfo, ok := h.bridgeInfo[fingerprint]; ok {
|
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 {
|
func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error {
|
||||||
bridgeInfoMap := map[[20]byte]BridgeInfo{}
|
bridgeInfoMap := map[bridgefingerprint.Fingerprint]BridgeInfo{}
|
||||||
inputScanner := bufio.NewScanner(reader)
|
inputScanner := bufio.NewScanner(reader)
|
||||||
for inputScanner.Scan() {
|
for inputScanner.Scan() {
|
||||||
inputLine := inputScanner.Bytes()
|
inputLine := inputScanner.Bytes()
|
||||||
|
@ -54,13 +53,14 @@ func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error {
|
||||||
if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil {
|
if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil {
|
||||||
return err
|
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
|
return err
|
||||||
} else if n != 20 {
|
|
||||||
return ErrBridgeFingerprintInvalid
|
|
||||||
}
|
}
|
||||||
bridgeInfoMap[bridgeHash] = bridgeInfo
|
|
||||||
|
bridgeInfoMap[bridgeFingerprint] = bridgeInfo
|
||||||
}
|
}
|
||||||
h.accessBridgeInfo.Lock()
|
h.accessBridgeInfo.Lock()
|
||||||
defer h.accessBridgeInfo.Unlock()
|
defer h.accessBridgeInfo.Unlock()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +35,9 @@ func TestBridgeLoad(t *testing.T) {
|
||||||
So(n, ShouldEqual, 20)
|
So(n, ShouldEqual, 20)
|
||||||
So(err, ShouldBeNil)
|
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(err, ShouldBeNil)
|
||||||
So(bridgeInfo.DisplayName, ShouldEqual, "default")
|
So(bridgeInfo.DisplayName, ShouldEqual, "default")
|
||||||
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org")
|
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org")
|
||||||
|
@ -50,7 +53,9 @@ func TestBridgeLoad(t *testing.T) {
|
||||||
So(n, ShouldEqual, 20)
|
So(n, ShouldEqual, 20)
|
||||||
So(err, ShouldBeNil)
|
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(err, ShouldBeNil)
|
||||||
So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8")
|
So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8")
|
||||||
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org")
|
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org")
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -44,7 +45,7 @@ type BrokerContext struct {
|
||||||
presumedPatternForLegacyClient string
|
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)
|
return ctx.bridgeList.GetBridgeInfo(fingerprint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bo
|
||||||
type ClientOffer struct {
|
type ClientOffer struct {
|
||||||
natType string
|
natType string
|
||||||
sdp []byte
|
sdp []byte
|
||||||
fingerprint [20]byte
|
fingerprint []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"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()
|
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc()
|
||||||
var relayURL string
|
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
|
return err
|
||||||
} else {
|
} else {
|
||||||
relayURL = info.WebSocketAddress
|
relayURL = info.WebSocketAddress
|
||||||
|
@ -172,12 +177,18 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offer.fingerprint = BridgeFingerprint.ToBytes()
|
||||||
|
|
||||||
// Only hand out known restricted snowflakes to unrestricted clients
|
// Only hand out known restricted snowflakes to unrestricted clients
|
||||||
var snowflakeHeap *SnowflakeHeap
|
var snowflakeHeap *SnowflakeHeap
|
||||||
if offer.natType == NATUnrestricted {
|
if offer.natType == NATUnrestricted {
|
||||||
|
|
|
@ -258,7 +258,7 @@ func TestBroker(t *testing.T) {
|
||||||
// Pass a fake client offer to this proxy
|
// Pass a fake client offer to this proxy
|
||||||
p := <-ctx.proxyPolls
|
p := <-ctx.proxyPolls
|
||||||
So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp")
|
So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp")
|
||||||
p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge}
|
p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge[:]}
|
||||||
<-done
|
<-done
|
||||||
So(w.Code, ShouldEqual, http.StatusOK)
|
So(w.Code, ShouldEqual, http.StatusOK)
|
||||||
So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`)
|
So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`)
|
||||||
|
|
30
common/bridgefingerprint/fingerprint.go
Normal file
30
common/bridgefingerprint/fingerprint.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -5,9 +5,9 @@ package messages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
|
||||||
|
|
||||||
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/nat"
|
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/nat"
|
||||||
)
|
)
|
||||||
|
@ -106,7 +106,8 @@ func DecodeClientPollRequest(data []byte) (*ClientPollRequest, error) {
|
||||||
if message.Fingerprint == "" {
|
if message.Fingerprint == "" {
|
||||||
message.Fingerprint = defaultBridgeFingerprint
|
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")
|
return nil, fmt.Errorf("cannot decode fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue