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 (
|
||||
"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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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/"}`)
|
||||
|
|
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 (
|
||||
"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")
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue