commit c0b6383f26d2b721ce5896a4c8a52646ea89ba5e Author: Serene Han keroserene+git@gmail.com Date: Thu Jan 21 13:54:43 2016 -0800
Automatic 100% bootstrap using meek-signaling broker works. Minimum viable webrtc pt now exists. (close #1) --- README.md | 113 ++++++++++++++++++++++++++++--------------------- client/meek-webrtc.go | 54 ++++++++--------------- client/snowflake.go | 40 +++++------------ client/torrc | 5 ++- client/torrc-manual | 6 +++ 5 files changed, 103 insertions(+), 115 deletions(-)
diff --git a/README.md b/README.md index 4213ba7..077a900 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,78 @@ A Pluggable Transport using WebRTC
### Status
-- Successfully bootstraps over WebRTC, both directly to a server plugin, - as well as through the browser which proxies WebRTC to websocket. -- Needs work on signaling with the broker. +- Successful automatic bootstraps with a WebRTC transport, + using HTTP signaling (with optional domain fronting) speaking to + a multitude of volunteer "snowflakes". +- Needs a lot more work though.
### Usage
-There are currently two ways to try this: -- Directly to the go-webrtc server plugin. -- Through a browser snowflake proxy. + + +``` +cd client/ +go build +tor -f torrc +``` + +And it will start the client plugin with the following `torrc` +options: +``` +ClientTransportPlugin snowflake exec ./client \ +--url https://snowflake-reg.appspot.com/ \ +--front www.google.com +``` + +It will speak to the Broker, get matched with a "snowflake" browser proxy, +and negotiate a WebRTC PeerConnection. +After that, it should bootstrap to 100%. + +To see logs, do `tail -F snowflake.log` in a second terminal. + +You can modify the `torrc` to use your own broker, +or remove the options entirely which will default to the old copy paste +method (see `torrc-manual`): + +``` +ClientTransportPlugin snowflake exec ./client --meek +``` + +Also, it is possible to connect directly to the go-webrtc server plugin +(skipping all the browser snowflake / broker stuff - see appendix) + +### Building a Snowflake Proxy + +This will only work if there are any browser snowflakes running at all. +To run your own, first make sure coffeescript is installed. +Then, build with: + +``` +cd proxy/ +cake build +``` +(Type `cake` by itself to see possible commands) + +Then, start a local http server in the `proxy/build/` in any way you like. +For instance: + +``` +cd build/ +python -m http.server +``` + +Open a browser tab to `0.0.0.0:8000/snowflake.html`. + +TODO: Turn the snowflake proxy into a more deployable badge. + +### Appendix + +##### -- Testing directly via WebRTC Server --
Using the server plugin uses an HTTP server that simulates the interaction that a client would have with a broker. Using the browser proxy (which will soon be the only way) requires copy and pasting between 3 terminals and a browser tab. -Once a signaling broker is implemented -([issue #1](https://github.com/keroserene/snowflake/issues/1)) -this will become much simpler to use. - -##### -- Via WebRTC Server --
Edit server/torrc and add "-http 127.0.0.1:8080" to the end of the ServerTransportPlugin line: @@ -42,14 +95,6 @@ ClientTransportPlugin line: ClientTransportPlugin snowflake exec ./client -url http://127.0.0.1:8080/ ```
-``` -cd client/ -go build -tor -f torrc -``` - -At this point the tor client should bootstrap to 100%. - ##### -- Via Browser Proxy --
Open up three terminals for the **client:** @@ -60,39 +105,11 @@ B: `cat > signal`
C: `tail -F snowflake.log`
- -To connect through the WebRTC browser proxy, first make sure -coffeescript is installed. Then, build with: -``` -cd proxy/ -cake build -``` - -Then start a local http server in the `proxy/build/` in any way you like. -For instance: - -``` -cd build/ -python -m http.server -``` - -Open a browser tab to `0.0.0.0:8000/snowflake.html`. -Input your desired relay address, or nothing/gibberish, which will cause -snowflake to just use a default relay. +Then, in the browser proxy:
- Look for the offer in terminal C; copy and paste it into the browser. - Copy and paste the answer generated in the browser back to terminal B. - Once WebRTC successfully connects, the browser terminal should turn green. Shortly after, the tor client should bootstrap to 100%.
- -### More - -To try using the Meek signaling channel (which will soon be fully ready), -add the `--meek` flag like so: - -``` -ClientTransportPlugin snowflake exec ./client --meek -``` - More documentation on the way. diff --git a/client/meek-webrtc.go b/client/meek-webrtc.go index 2d68770..369987d 100644 --- a/client/meek-webrtc.go +++ b/client/meek-webrtc.go @@ -16,28 +16,25 @@ import ( type MeekChannel struct { // The Host header to put in the HTTP request (optional and may be // different from the host name in URL). - Host string - Method string - trueURL *url.URL - externalUrl string - transport http.Transport // Used to make all requests. + Host string + url *url.URL + transport http.Transport // Used to make all requests. }
-// Construct a new MeekChannel, where -// |broker| is the URL of the facilitating program which assigns proxies -// to clients, and -// |front| is URL of the front domain. +// Construct a new MeekChannel, where: +// |broker| is the full URL of the facilitating program which assigns proxies +// to clients, and |front| is the option fronting domain. func NewMeekChannel(broker string, front string) *MeekChannel { - targetUrl, err := url.Parse(broker) + targetURL, err := url.Parse(broker) if nil != err { return nil } mc := new(MeekChannel) - mc.Host = front - mc.Method = "POST" - - mc.trueURL = targetUrl - mc.externalUrl = front + "/client" + mc.url = targetURL + if "" != front { // Optional front domain. + mc.Host = mc.url.Host + mc.url.Host = front + }
// We make a copy of DefaultTransport because we want the default Dial // and TLSHandshakeTimeout settings. But we want to disable the default @@ -54,39 +51,24 @@ func NewMeekChannel(broker string, front string) *MeekChannel { func (mc *MeekChannel) Negotiate(offer *webrtc.SessionDescription) ( *webrtc.SessionDescription, error) { data := bytes.NewReader([]byte(offer.Serialize())) - request, err := http.NewRequest(mc.Method, mc.externalUrl, data) + // Suffix with broker's client registration handler. + request, err := http.NewRequest("POST", mc.url.String()+"client", data) if nil != err { return nil, err } - request.Host = mc.trueURL.String() + if "" != mc.Host { // Set true host if necessary. + request.Host = mc.Host + } resp, err := mc.transport.RoundTrip(request) if nil != err { return nil, err } defer resp.Body.Close() - log.Println("MeekChannel Response: ", resp) - + log.Printf("MeekChannel Response:\n%s\n\n", resp) body, err := ioutil.ReadAll(resp.Body) if nil != err { return nil, err } - log.Println("Body: ", string(body)) - answer := webrtc.DeserializeSessionDescription(string(body)) - return answer, nil -} - -// Simple interim non-fronting HTTP POST negotiation, to be removed when more -// general fronting is present. -func sendOfferHTTP(url string, offer *webrtc.SessionDescription) (*webrtc.SessionDescription, error) { - resp, err := http.Post(url, "", bytes.NewBuffer([]byte(offer.Serialize()))) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } answer := webrtc.DeserializeSessionDescription(string(body)) return answer, nil } diff --git a/client/snowflake.go b/client/snowflake.go index ad496f9..83179a3 100644 --- a/client/snowflake.go +++ b/client/snowflake.go @@ -22,18 +22,10 @@ import ( "git.torproject.org/pluggable-transports/goptlib.git" )
-// Hard-coded meek signalling channel for now. -// TODO: expose as param -const ( - // Go fully requires the protocol to make url spec - FRONT_URL = "https://www.google.com" - BROKER_URL = "snowflake-reg.appspot.com" -) - var ptInfo pt.ClientInfo var logFile *os.File -var offerURL string -var meekEnabled bool +var brokerURL string +var frontDomain string
// When a connection handler starts, +1 is written to this channel; when it // ends, -1 is written. @@ -180,9 +172,9 @@ func dialWebRTC(config *webrtc.Configuration, meek *MeekChannel) ( fmt.Fprintln(logFile, "\n"+offer.Serialize()+"\n") log.Printf("----------------") go func() { - if meekEnabled { - log.Println("Sending offer via meek channel...\nTarget URL: ", BROKER_URL, - "\nFront URL: ", FRONT_URL) + if "" != brokerURL { + log.Println("Sending offer via meek channel...\nTarget URL: ", brokerURL, + "\nFront URL: ", frontDomain) answer, err := meek.Negotiate(pc.LocalDescription()) if nil != err { log.Printf("MeekChannel signaling error: %s", err) @@ -190,17 +182,6 @@ func dialWebRTC(config *webrtc.Configuration, meek *MeekChannel) ( if nil == answer { log.Printf("MeekChannel: No answer received.") } else { - log.Println("Recieved answer from Meek channel: \n\n", - answer.Serialize(), "\n") - // TODO: Once this is correct, uncomment and remove copy-paste stuff. - // signalChan <- answer - } - } - if offerURL != "" { - answer, err := sendOfferHTTP(offerURL, offer) - if err != nil { - log.Println(err) - } else { signalChan <- answer } } @@ -214,7 +195,7 @@ func dialWebRTC(config *webrtc.Configuration, meek *MeekChannel) ( pc.Close() return nil, fmt.Errorf("no answer received") } - log.Printf("Received Answer: %s", answer.Serialize()) + log.Printf("Received Answer:\n\n%s\n", answer.Sdp) err = pc.SetRemoteDescription(answer) if err != nil { pc.Close() @@ -257,7 +238,7 @@ func handler(conn *pt.SocksConn) error {
config := webrtc.NewConfiguration( webrtc.OptionIceServer("stun:stun.l.google.com:19302")) - meek := NewMeekChannel(BROKER_URL, FRONT_URL) + meek := NewMeekChannel(brokerURL, frontDomain) remote, err := dialWebRTC(config, meek) if err != nil { conn.Reject() @@ -318,8 +299,8 @@ func readSignalingMessages(f *os.File) { func main() { var err error
- flag.StringVar(&offerURL, "url", "", "do signalling through URL") - flag.BoolVar(&meekEnabled, "meek", false, "use domain fronted signaling") + flag.StringVar(&brokerURL, "url", "", "URL of signaling broker") + flag.StringVar(&frontDomain, "front", "", "front domain") flag.Parse()
logFile, err = os.OpenFile("snowflake.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) @@ -328,10 +309,9 @@ func main() { } defer logFile.Close() log.SetOutput(logFile) - log.Println("starting")
- if offerURL == "" && !meekEnabled { + if "" == brokerURL { log.Println("No HTTP signaling detected. Waiting for a "signal" pipe...") // This FIFO receives signaling messages. err = syscall.Mkfifo("signal", 0600) diff --git a/client/torrc b/client/torrc index 63c88a4..9af942b 100644 --- a/client/torrc +++ b/client/torrc @@ -1,5 +1,8 @@ UseBridges 1 DataDirectory datadir
-ClientTransportPlugin snowflake exec ./client +ClientTransportPlugin snowflake exec ./client \ +-url https://snowflake-reg.appspot.com/ \ +-front www.google.com + Bridge snowflake 0.0.3.0:1 diff --git a/client/torrc-manual b/client/torrc-manual new file mode 100644 index 0000000..014b539 --- /dev/null +++ b/client/torrc-manual @@ -0,0 +1,6 @@ +UseBridges 1 +DataDirectory datadir + +ClientTransportPlugin snowflake exec ./client + +Bridge snowflake 0.0.3.0:1
tor-commits@lists.torproject.org