mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 20:11:19 -04:00
Validate SDP offers and answers
This commit is contained in:
parent
8e5ea82611
commit
07b5f07452
3 changed files with 56 additions and 13 deletions
|
@ -1,7 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -137,10 +139,17 @@ func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = validateSDP(body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error client SDP: ", err.Error())
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the legacy version
|
// Handle the legacy version
|
||||||
//
|
//
|
||||||
// We support two client message formats. The legacy format is for backwards
|
// We support two client message formats. The legacy format is for backwards
|
||||||
// combatability and relies heavily on HTTP headers and status codes to convey
|
// compatability and relies heavily on HTTP headers and status codes to convey
|
||||||
// information.
|
// information.
|
||||||
isLegacy := false
|
isLegacy := false
|
||||||
if len(body) > 0 && body[0] == '{' {
|
if len(body) > 0 && body[0] == '{' {
|
||||||
|
@ -197,7 +206,7 @@ func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Expects snowflake proxes which have previously successfully received
|
Expects snowflake proxies which have previously successfully received
|
||||||
an offer from proxyHandler to respond with an answer in an HTTP POST,
|
an offer from proxyHandler to respond with an answer in an HTTP POST,
|
||||||
which the broker will pass back to the original client.
|
which the broker will pass back to the original client.
|
||||||
*/
|
*/
|
||||||
|
@ -209,6 +218,13 @@ func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = validateSDP(body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error proxy SDP: ", err.Error())
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
arg := messages.Arg{
|
arg := messages.Arg{
|
||||||
Body: body,
|
Body: body,
|
||||||
RemoteAddr: "",
|
RemoteAddr: "",
|
||||||
|
@ -233,3 +249,12 @@ func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("proxyAnswers unable to write answer response with error: %v", err)
|
log.Printf("proxyAnswers unable to write answer response with error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateSDP(SDP []byte) error {
|
||||||
|
// TODO: more validation likely needed
|
||||||
|
if !bytes.Contains(SDP, []byte("a=candidate")) {
|
||||||
|
return fmt.Errorf("SDP contains no candidate")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -143,10 +145,10 @@ func (c *WebRTCPeer) checkForStaleness(timeout time.Duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connect does the bulk of the work: gather ICE candidates, send the SDP offer to broker,
|
||||||
|
// receive an answer from broker, and wait for data channel to open
|
||||||
func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel) error {
|
func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel) error {
|
||||||
log.Println(c.id, " connecting...")
|
log.Println(c.id, " connecting...")
|
||||||
// TODO: When go-webrtc is more stable, it's possible that a new
|
|
||||||
// PeerConnection won't need to be re-prepared each time.
|
|
||||||
err := c.preparePeerConnection(config)
|
err := c.preparePeerConnection(config)
|
||||||
localDescription := c.pc.LocalDescription()
|
localDescription := c.pc.LocalDescription()
|
||||||
c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{
|
c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{
|
||||||
|
@ -187,7 +189,7 @@ func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
// preparePeerConnection creates a new WebRTC PeerConnection and returns it
|
// preparePeerConnection creates a new WebRTC PeerConnection and returns it
|
||||||
// after ICE candidate gathering is complete..
|
// after non-trickle ICE candidate gathering is complete.
|
||||||
func (c *WebRTCPeer) preparePeerConnection(config *webrtc.Configuration) error {
|
func (c *WebRTCPeer) preparePeerConnection(config *webrtc.Configuration) error {
|
||||||
var err error
|
var err error
|
||||||
s := webrtc.SettingEngine{}
|
s := webrtc.SettingEngine{}
|
||||||
|
@ -240,10 +242,8 @@ func (c *WebRTCPeer) preparePeerConnection(config *webrtc.Configuration) error {
|
||||||
})
|
})
|
||||||
c.transport = dc
|
c.transport = dc
|
||||||
c.open = make(chan struct{})
|
c.open = make(chan struct{})
|
||||||
log.Println("WebRTC: DataChannel created.")
|
log.Println("WebRTC: DataChannel created")
|
||||||
|
|
||||||
// Allow candidates to accumulate until ICEGatheringStateComplete.
|
|
||||||
done := webrtc.GatheringCompletePromise(c.pc)
|
|
||||||
offer, err := c.pc.CreateOffer(nil)
|
offer, err := c.pc.CreateOffer(nil)
|
||||||
// TODO: Potentially timeout and retry if ICE isn't working.
|
// TODO: Potentially timeout and retry if ICE isn't working.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -252,16 +252,23 @@ func (c *WebRTCPeer) preparePeerConnection(config *webrtc.Configuration) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("WebRTC: Created offer")
|
log.Println("WebRTC: Created offer")
|
||||||
|
|
||||||
|
// Allow candidates to accumulate until ICEGatheringStateComplete.
|
||||||
|
done := webrtc.GatheringCompletePromise(c.pc)
|
||||||
|
// Start gathering candidates
|
||||||
err = c.pc.SetLocalDescription(offer)
|
err = c.pc.SetLocalDescription(offer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to prepare offer", err)
|
log.Println("Failed to apply offer", err)
|
||||||
c.pc.Close()
|
c.pc.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("WebRTC: Set local description")
|
log.Println("WebRTC: Set local description")
|
||||||
|
|
||||||
<-done // Wait for ICE candidate gathering to complete.
|
<-done // Wait for ICE candidate gathering to complete.
|
||||||
log.Println("WebRTC: PeerConnection created.")
|
|
||||||
|
if !strings.Contains(c.pc.LocalDescription().SDP, "\na=candidate:") {
|
||||||
|
return fmt.Errorf("SDP offer contains no candidate")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,6 +250,8 @@ func (s *SignalingServer) pollOffer(sid string, proxyType string, acceptedRelayP
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sendAnswer encodes an SDP answer, sends it to the broker
|
||||||
|
// and wait for its response
|
||||||
func (s *SignalingServer) sendAnswer(sid string, pc *webrtc.PeerConnection) error {
|
func (s *SignalingServer) sendAnswer(sid string, pc *webrtc.PeerConnection) error {
|
||||||
ld := pc.LocalDescription()
|
ld := pc.LocalDescription()
|
||||||
if !s.keepLocalAddresses {
|
if !s.keepLocalAddresses {
|
||||||
|
@ -485,6 +487,10 @@ func (sf *SnowflakeProxy) makePeerConnectionFromOffer(sdp *webrtc.SessionDescrip
|
||||||
|
|
||||||
// Wait for ICE candidate gathering to complete
|
// Wait for ICE candidate gathering to complete
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
|
if !strings.Contains(pc.LocalDescription().SDP, "\na=candidate:") {
|
||||||
|
return nil, fmt.Errorf("SDP answer contains no candidate")
|
||||||
|
}
|
||||||
log.Printf("Answer: \n\t%s", strings.ReplaceAll(pc.LocalDescription().SDP, "\n", "\n\t"))
|
log.Printf("Answer: \n\t%s", strings.ReplaceAll(pc.LocalDescription().SDP, "\n", "\n\t"))
|
||||||
|
|
||||||
return pc, nil
|
return pc, nil
|
||||||
|
@ -524,15 +530,16 @@ func (sf *SnowflakeProxy) makeNewPeerConnection(config webrtc.Configuration,
|
||||||
pc.Close()
|
pc.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Println("Probetest: Creating offer")
|
log.Println("Probetest: Created Offer")
|
||||||
|
|
||||||
// As of v3.0.0, pion-webrtc uses trickle ICE by default.
|
// As of v3.0.0, pion-webrtc uses trickle ICE by default.
|
||||||
// We have to wait for candidate gathering to complete
|
// We have to wait for candidate gathering to complete
|
||||||
// before we send the offer
|
// before we send the offer
|
||||||
done := webrtc.GatheringCompletePromise(pc)
|
done := webrtc.GatheringCompletePromise(pc)
|
||||||
|
// start the gathering of ICE candidates
|
||||||
err = pc.SetLocalDescription(offer)
|
err = pc.SetLocalDescription(offer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to prepare offer", err)
|
log.Println("Failed to apply offer", err)
|
||||||
pc.Close()
|
pc.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -540,6 +547,11 @@ func (sf *SnowflakeProxy) makeNewPeerConnection(config webrtc.Configuration,
|
||||||
|
|
||||||
// Wait for ICE candidate gathering to complete
|
// Wait for ICE candidate gathering to complete
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
|
if !strings.Contains(pc.LocalDescription().SDP, "\na=candidate:") {
|
||||||
|
return nil, fmt.Errorf("Probetest SDP offer contains no candidate")
|
||||||
|
}
|
||||||
|
|
||||||
return pc, nil
|
return pc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +713,6 @@ func (sf *SnowflakeProxy) checkNATType(config webrtc.Configuration, probeURL str
|
||||||
log.Printf("Error parsing url: %s", err.Error())
|
log.Printf("Error parsing url: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// create offer used for probetest
|
|
||||||
dataChan := make(chan struct{})
|
dataChan := make(chan struct{})
|
||||||
pc, err := sf.makeNewPeerConnection(config, dataChan)
|
pc, err := sf.makeNewPeerConnection(config, dataChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue