Forward bridge fingerprint

gitlab 28651
This commit is contained in:
Arlo Breault 2022-03-08 16:27:52 -05:00
parent 281d917beb
commit b563141c6a
7 changed files with 41 additions and 11 deletions

View file

@ -139,10 +139,11 @@ func (ctx *BrokerContext) AddSnowflake(id string, proxyType string, natType stri
return snowflake return snowflake
} }
// Client offer contains an SDP and the NAT type of the client // Client offer contains an SDP, bridge fingerprint and the NAT type of the client
type ClientOffer struct { type ClientOffer struct {
natType string natType string
sdp []byte sdp []byte
fingerprint string
} }
func main() { func main() {

View file

@ -132,6 +132,7 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
offer := &ClientOffer{ offer := &ClientOffer{
natType: req.NAT, natType: req.NAT,
sdp: []byte(req.Offer), sdp: []byte(req.Offer),
fingerprint: req.Fingerprint,
} }
// Only hand out known restricted snowflakes to unrestricted clients // Only hand out known restricted snowflakes to unrestricted clients

View file

@ -43,6 +43,7 @@ type BrokerChannel struct {
keepLocalAddresses bool keepLocalAddresses bool
natType string natType string
lock sync.Mutex lock sync.Mutex
BridgeFingerprint string
} }
// We make a copy of DefaultTransport because we want the default Dial // We make a copy of DefaultTransport because we want the default Dial
@ -92,6 +93,7 @@ func newBrokerChannelFromConfig(config ClientConfig) (*BrokerChannel, error) {
Rendezvous: rendezvous, Rendezvous: rendezvous,
keepLocalAddresses: config.KeepLocalAddresses, keepLocalAddresses: config.KeepLocalAddresses,
natType: nat.NATUnknown, natType: nat.NATUnknown,
BridgeFingerprint: config.BridgeFingerprint,
}, nil }, nil
} }
@ -118,6 +120,7 @@ func (bc *BrokerChannel) Negotiate(offer *webrtc.SessionDescription) (
req := &messages.ClientPollRequest{ req := &messages.ClientPollRequest{
Offer: offerSDP, Offer: offerSDP,
NAT: bc.natType, NAT: bc.natType,
Fingerprint: bc.BridgeFingerprint,
} }
encReq, err := req.EncodeClientPollRequest() encReq, err := req.EncodeClientPollRequest()
bc.lock.Unlock() bc.lock.Unlock()

View file

@ -103,6 +103,9 @@ type ClientConfig struct {
// UTLSRemoveSNI is the flag to control whether SNI should be removed from Client Hello // UTLSRemoveSNI is the flag to control whether SNI should be removed from Client Hello
// when uTLS is used. // when uTLS is used.
UTLSRemoveSNI bool UTLSRemoveSNI bool
// BridgeFingerprint is the fingerprint of the bridge that the client will eventually
// connect to, as specified in the Bridge line of the torrc.
BridgeFingerprint string
} }
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple // NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple

View file

@ -95,6 +95,9 @@ func socksAcceptLoop(ln *pt.SocksListener, config sf.ClientConfig, shutdown chan
if arg, ok := conn.Req.Args.Get("utls-imitate"); ok { if arg, ok := conn.Req.Args.Get("utls-imitate"); ok {
config.UTLSClientID = arg config.UTLSClientID = arg
} }
if arg, ok := conn.Req.Args.Get("fingerprint"); ok {
config.BridgeFingerprint = arg
}
transport, err := sf.NewSnowflakeClient(config) transport, err := sf.NewSnowflakeClient(config)
if err != nil { if err != nil {
conn.Reject() conn.Reject()

View file

@ -3,6 +3,6 @@ DataDirectory datadir
ClientTransportPlugin snowflake exec ./client -log snowflake.log ClientTransportPlugin snowflake exec ./client -log snowflake.log
Bridge snowflake 192.0.2.3:1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 Bridge snowflake 192.0.2.3:1 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
SocksPort auto SocksPort auto

View file

@ -29,10 +29,13 @@ each encoded in JSON format
{ {
offer: <sdp offer> offer: <sdp offer>
[nat: (unknown|restricted|unrestricted)] [nat: (unknown|restricted|unrestricted)]
[fingerprint: <fingerprint string>]
} }
The NAT field is optional, and if it is missing a The NAT field is optional, and if it is missing a
value of "unknown" will be assumed. value of "unknown" will be assumed. The fingerprint
is also optional and, if absent, will be assigned the
fingerprint of the default bridge.
== ClientPollResponse == == ClientPollResponse ==
<poll response> := <poll response> :=
@ -49,13 +52,25 @@ for the error.
*/ */
// The bridge fingerprint to assume, for client poll requests that do not
// specify a fingerprint. Before #28651, there was only one bridge with one
// fingerprint, which all clients expected to be connected to implicitly.
// If a client is old enough that it does not specify a fingerprint, this is
// the fingerprint it expects. Clients that do set a fingerprint in the
// SOCKS params will also be assumed to want to connect to the default bridge.
const defaultBridgeFingerprint = "2B280B23E1107BB62ABFC40DDCC8824814F80A72"
type ClientPollRequest struct { type ClientPollRequest struct {
Offer string `json:"offer"` Offer string `json:"offer"`
NAT string `json:"nat"` NAT string `json:"nat"`
Fingerprint string `json:"fingerprint"`
} }
// Encodes a poll message from a snowflake client // Encodes a poll message from a snowflake client
func (req *ClientPollRequest) EncodeClientPollRequest() ([]byte, error) { func (req *ClientPollRequest) EncodeClientPollRequest() ([]byte, error) {
if req.Fingerprint == "" {
req.Fingerprint = defaultBridgeFingerprint
}
body, err := json.Marshal(req) body, err := json.Marshal(req)
if err != nil { if err != nil {
return nil, err return nil, err
@ -87,6 +102,10 @@ func DecodeClientPollRequest(data []byte) (*ClientPollRequest, error) {
return nil, fmt.Errorf("no supplied offer") return nil, fmt.Errorf("no supplied offer")
} }
if message.Fingerprint == "" {
message.Fingerprint = defaultBridgeFingerprint
}
switch message.NAT { switch message.NAT {
case "": case "":
message.NAT = nat.NATUnknown message.NAT = nat.NATUnknown