begin meek-webrtc signalling channel for client (issue #1)

This commit is contained in:
Serene Han 2016-01-15 10:26:16 -08:00
parent bc1e147ca2
commit b4934f3979
2 changed files with 124 additions and 7 deletions

84
client/meek-webrtc.go Normal file
View file

@ -0,0 +1,84 @@
// Exchange WebRTC SessionDescriptions over meek.
// Much of this source is extracted from meek-client.go.
package main
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
"github.com/keroserene/go-webrtc"
)
// RequestInfo encapsulates all the configuration used for a requestresponse
// roundtrip, including variables that may come from SOCKS args or from the
// command line.
type RequestInfo struct {
// What to put in the X-Session-ID header.
// SessionID string
// The URL to request.
URL *url.URL
// The Host header to put in the HTTP request (optional and may be
// different from the host name in URL).
Host string
}
func NewRequestInfo(meekUrl string, front string) *RequestInfo {
info := new(RequestInfo)
requestUrl, err := url.Parse(meekUrl)
if nil != err {
return nil
}
info.URL = requestUrl
info.Host = info.URL.Host
info.URL.Host = front
return info
}
// Meek Signalling Channel
type MeekChannel struct {
info *RequestInfo
// Used to make all requests.
transport http.Transport
}
func NewMeekChannel(info *RequestInfo) *MeekChannel {
m := new(MeekChannel)
// We make a copy of DefaultTransport because we want the default Dial
// and TLSHandshakeTimeout settings. But we want to disable the default
// ProxyFromEnvironment setting. Proxy is overridden below if
// options.ProxyURL is set.
m.transport = *http.DefaultTransport.(*http.Transport)
m.transport.Proxy = nil
m.info = info
return m
}
// Do an HTTP roundtrip using the payload data in buf.
func (m *MeekChannel) roundTripHTTP(buf []byte) (*http.Response, error) {
req, err := http.NewRequest("POST", m.info.URL.String(), bytes.NewReader(buf))
if nil != err {
return nil, err
}
if "" != m.info.Host {
req.Host = m.info.Host
}
// req.Header.Set("X-Session-Id", m.info.SessionID)
return m.transport.RoundTrip(req)
}
// Send an SDP offer to the meek facilitator, and wait for an SDP answer from
// the assigned proxy in the response.
func (m *MeekChannel) Negotiate(offer *webrtc.SessionDescription) (
*webrtc.SessionDescription, error) {
buf := []byte(offer.Serialize())
resp, err := m.roundTripHTTP(buf)
if nil != err {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
answer := webrtc.DeserializeSessionDescription(string(body))
return answer, nil
}

View file

@ -1,3 +1,6 @@
// Client transport plugin for the snowflake pluggable transport.
//
// TODO: Use meek for signalling.
package main package main
import ( import (
@ -18,6 +21,13 @@ import (
"git.torproject.org/pluggable-transports/goptlib.git" "git.torproject.org/pluggable-transports/goptlib.git"
) )
// Hard-coded meek signalling channel for now.
// TODO: expose as param
const (
MEEK_URL = "not implemented yet"
FRONT_DOMAIN = "www.google.com"
)
var ptInfo pt.ClientInfo var ptInfo pt.ClientInfo
var logFile *os.File var logFile *os.File
@ -89,8 +99,10 @@ func (c *webRTCConn) SetWriteDeadline(t time.Time) error {
return fmt.Errorf("SetWriteDeadline not implemented") return fmt.Errorf("SetWriteDeadline not implemented")
} }
func dialWebRTC(config *webrtc.Configuration) (*webRTCConn, error) { func dialWebRTC(config *webrtc.Configuration, meek *MeekChannel) (
blobChan := make(chan string) *webRTCConn, error) {
offerChan := make(chan *webrtc.SessionDescription)
errChan := make(chan error) errChan := make(chan error)
openChan := make(chan bool) openChan := make(chan bool)
@ -123,7 +135,7 @@ func dialWebRTC(config *webrtc.Configuration) (*webRTCConn, error) {
// TODO: This may soon be deprecated, consider OnIceGatheringStateChange. // TODO: This may soon be deprecated, consider OnIceGatheringStateChange.
pc.OnIceComplete = func() { pc.OnIceComplete = func() {
log.Printf("OnIceComplete") log.Printf("OnIceComplete")
blobChan <- pc.LocalDescription().Serialize() offerChan <- pc.LocalDescription()
} }
pc.OnDataChannel = func(channel *data.Channel) { pc.OnDataChannel = func(channel *data.Channel) {
log.Println("OnDataChannel") log.Println("OnDataChannel")
@ -162,10 +174,22 @@ func dialWebRTC(config *webrtc.Configuration) (*webRTCConn, error) {
case err := <-errChan: case err := <-errChan:
pc.Close() pc.Close()
return nil, err return nil, err
case offer := <-blobChan: case offer := <-offerChan:
log.Printf("----------------") log.Printf("----------------")
fmt.Fprintln(logFile, "\n"+offer+"\n") fmt.Fprintln(logFile, "\n"+offer.Serialize()+"\n")
log.Printf("----------------") log.Printf("----------------")
go func() {
log.Printf("Sending offer via meek...")
answer, err := meek.Negotiate(pc.LocalDescription())
if nil != err {
log.Printf("Signalling error: %s", err)
}
if nil == answer {
log.Printf("No answer received from meek channel.")
} else {
signalChan <- answer
}
}()
} }
log.Printf("waiting for answer") log.Printf("waiting for answer")
@ -184,6 +208,7 @@ func dialWebRTC(config *webrtc.Configuration) (*webRTCConn, error) {
// Wait until data channel is open; otherwise for example sends may get // Wait until data channel is open; otherwise for example sends may get
// lost. // lost.
// TODO: Buffering *should* work though.
_, ok = <-openChan _, ok = <-openChan
if !ok { if !ok {
pc.Close() pc.Close()
@ -215,8 +240,16 @@ func handler(conn *pt.SocksConn) error {
}() }()
defer conn.Close() defer conn.Close()
config := webrtc.NewConfiguration(webrtc.OptionIceServer("stun:stun.l.google.com:19302")) // go func() {
remote, err := dialWebRTC(config) // }()
// Prepare meek signalling channel.
info := NewRequestInfo(MEEK_URL, FRONT_DOMAIN)
meek := NewMeekChannel(info)
config := webrtc.NewConfiguration(
webrtc.OptionIceServer("stun:stun.l.google.com:19302"))
// remote, err := dialWebRTC(config, nil)
remote, err := dialWebRTC(config, meek)
if err != nil { if err != nil {
conn.Reject() conn.Reject()
return err return err