From d7da7b53c12d1bf3e1c8b6c73ce8ac69fb540198 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Tue, 28 May 2019 23:09:53 +0200 Subject: [PATCH] Added wss --- addrs.go | 13 ++++-- addrs_test.go | 13 ++++++ go.mod | 1 + go.sum | 4 ++ websocket.go | 114 ++++++++++++++++++++++++++++++++++------------ websocket_test.go | 22 +++++++++ 6 files changed, 135 insertions(+), 32 deletions(-) diff --git a/addrs.go b/addrs.go index e5dbc46..ce96930 100644 --- a/addrs.go +++ b/addrs.go @@ -64,10 +64,17 @@ func ParseWebsocketNetAddr(a net.Addr) (ma.Multiaddr, error) { } func parseMultiaddr(a ma.Multiaddr) (string, error) { - _, host, err := manet.DialArgs(a) + p := a.Protocols() + host, err := a.ValueForProtocol(p[0].Code) if err != nil { return "", err } - - return "ws://" + host, nil + if p[0].Code == ma.P_IP6 { + host = "[" + host + "]" + } + port, err := a.ValueForProtocol(ma.P_TCP) + if err != nil { + return "", err + } + return p[2].Name + "://" + host + ":" + port, nil } diff --git a/addrs_test.go b/addrs_test.go index d962760..12430b1 100644 --- a/addrs_test.go +++ b/addrs_test.go @@ -20,6 +20,19 @@ func TestMultiaddrParsing(t *testing.T) { if wsaddr != "ws://127.0.0.1:5555" { t.Fatalf("expected ws://127.0.0.1:5555, got %s", wsaddr) } + + addr, err = ma.NewMultiaddr("/dnsaddr/example.com/tcp/5555/wss") + if err != nil { + t.Fatal(err) + } + + wsaddr, err = parseMultiaddr(addr) + if err != nil { + t.Fatal(err) + } + if wsaddr != "wss://example.com:5555" { + t.Fatalf("expected wss://example.com:5555, got %s", wsaddr) + } } type httpAddr struct { diff --git a/go.mod b/go.mod index b25b7df..72687ff 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/libp2p/go-libp2p-testing v0.0.3 github.com/libp2p/go-libp2p-transport-upgrader v0.1.1 github.com/multiformats/go-multiaddr v0.0.4 + github.com/multiformats/go-multiaddr-fmt v0.0.1 github.com/multiformats/go-multiaddr-net v0.0.1 github.com/whyrusleeping/mafmt v1.2.8 ) diff --git a/go.sum b/go.sum index ad6d7b5..5e04908 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,10 @@ github.com/multiformats/go-multiaddr v0.0.4 h1:WgMSI84/eRLdbptXMkMWDXPjPq7SPLIgG github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr-dns v0.0.1 h1:jQt9c6tDSdQLIlBo4tXYx7QUHCPjxsB1zXcag/2S7zc= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2 h1:/Bbsgsy3R6e3jf2qBahzNHzww6usYaZ0NhNH3sqdFS8= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-fmt v0.0.1 h1:5YjeOIzbX8OTKVaN72aOzGIYW7PnrZrnkDyOfAWRSMA= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= github.com/multiformats/go-multiaddr-net v0.0.1 h1:76O59E3FavvHqNg7jvzWzsPSW5JSi/ek0E4eiDVbg9g= github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= diff --git a/websocket.go b/websocket.go index 972681a..e85552f 100644 --- a/websocket.go +++ b/websocket.go @@ -7,6 +7,8 @@ import ( "net" "net/http" "net/url" + "crypto/tls" + "crypto/x509" "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/transport" @@ -16,7 +18,7 @@ import ( ws "github.com/gorilla/websocket" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr-net" - mafmt "github.com/whyrusleeping/mafmt" + mafmt "github.com/multiformats/go-multiaddr-fmt" ) // WsProtocol is the multiaddr protocol definition for this transport. @@ -25,9 +27,17 @@ var WsProtocol = ma.Protocol{ Name: "ws", VCode: ma.CodeToVarint(477), } +var WssProtocol = ma.Protocol{ + Code: 478, + Name: "wss", + VCode: ma.CodeToVarint(478), +} // WsFmt is multiaddr formatter for WsProtocol -var WsFmt = mafmt.And(mafmt.TCP, mafmt.Base(WsProtocol.Code)) +var WsFmt = mafmt.Or( + mafmt.And(mafmt.TCP, mafmt.Base(WsProtocol.Code)), + mafmt.And(mafmt.And(mafmt.DNS, mafmt.Base(ma.P_TCP)), mafmt.Base(WssProtocol.Code)), +) // WsCodec is the multiaddr-net codec definition for the websocket transport var WsCodec = &manet.NetCodec{ @@ -36,6 +46,12 @@ var WsCodec = &manet.NetCodec{ ConvertMultiaddr: ConvertWebsocketMultiaddrToNetAddr, ParseNetAddr: ParseWebsocketNetAddr, } +var WssCodec = &manet.NetCodec{ + NetAddrNetworks: []string{"websocket+tls"}, + ProtocolName: "wss", + ConvertMultiaddr: ConvertWebsocketMultiaddrToNetAddr, + ParseNetAddr: ParseWebsocketNetAddr, +} // Default gorilla upgrader var upgrader = ws.Upgrader{ @@ -50,17 +66,30 @@ func init() { if err != nil { panic(fmt.Errorf("error registering websocket protocol: %s", err)) } + err = ma.AddProtocol(WssProtocol) + if err != nil { + panic(fmt.Errorf("error registering websocket+tls protocol: %s", err)) + } manet.RegisterNetCodec(WsCodec) + manet.RegisterNetCodec(WssCodec) } // WebsocketTransport is the actual go-libp2p transport type WebsocketTransport struct { Upgrader *tptu.Upgrader + CertPool *x509.CertPool } func New(u *tptu.Upgrader) *WebsocketTransport { - return &WebsocketTransport{u} + p, err := x509.SystemCertPool() + if err != nil { + p = x509.NewCertPool() + } + return &WebsocketTransport{ + Upgrader: u, + CertPool: p, + } } var _ transport.Transport = (*WebsocketTransport)(nil) @@ -70,7 +99,7 @@ func (t *WebsocketTransport) CanDial(a ma.Multiaddr) bool { } func (t *WebsocketTransport) Protocols() []int { - return []int{WsProtocol.Code} + return []int{WsProtocol.Code, WssProtocol.Code} } func (t *WebsocketTransport) Proxy() bool { @@ -83,7 +112,28 @@ func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (ma return nil, err } - wscon, _, err := ws.DefaultDialer.Dial(wsurl, nil) + var wscon *ws.Conn + + if raddr.Protocols()[2].Code == WsProtocol.Code { + u, err := url.Parse(wsurl) + if err != nil { + return nil, err + } + + wsHeaders := http.Header{ + "Origin": {u.Host}, + // your milage may differ + "Sec-WebSocket-Extensions": {"permessage-deflate; client_max_window_bits, x-webkit-deflate-frame"}, + } + cfg := &tls.Config{RootCAs: t.CertPool} + tlsconn, err := tls.Dial("tcp", u.Host, cfg) + if err != nil { + return nil, err + } + wscon, _, err = ws.NewClient(tlsconn, u, wsHeaders, 1024, 1024) + } else { + wscon, _, err = ws.DefaultDialer.Dial(wsurl, nil) + } if err != nil { return nil, err } @@ -104,32 +154,38 @@ func (t *WebsocketTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p pee return t.Upgrader.UpgradeOutbound(ctx, t, macon, p) } -func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) { - lnet, lnaddr, err := manet.DialArgs(a) - if err != nil { - return nil, err - } - - nl, err := net.Listen(lnet, lnaddr) - if err != nil { - return nil, err - } - - u, err := url.Parse("http://" + nl.Addr().String()) - if err != nil { - nl.Close() - return nil, err - } +var listenWss = fmt.Errorf("Unable to listen wss, you should use a reverse proxy like nginx or apache.") - malist, err := t.wrapListener(nl, u) - if err != nil { - nl.Close() - return nil, err +func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) { + if a.Protocols()[2].Code == WsProtocol.Code { + lnet, lnaddr, err := manet.DialArgs(a) + if err != nil { + return nil, err + } + + nl, err := net.Listen(lnet, lnaddr) + if err != nil { + return nil, err + } + + u, err := url.Parse("http://" + nl.Addr().String()) + if err != nil { + nl.Close() + return nil, err + } + + malist, err := t.wrapListener(nl, u) + if err != nil { + nl.Close() + return nil, err + } + + go malist.serve() + + return malist, nil + } else { + return nil, listenWss } - - go malist.serve() - - return malist, nil } func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) { diff --git a/websocket_test.go b/websocket_test.go index 4f397c8..01f344a 100644 --- a/websocket_test.go +++ b/websocket_test.go @@ -21,6 +21,10 @@ func TestCanDial(t *testing.T) { if err != nil { t.Fatal(err) } + addrWss, err := ma.NewMultiaddr("/dnsaddr/example.com/tcp/5555/wss") + if err != nil { + t.Fatal(err) + } addrTCP, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555") if err != nil { @@ -29,11 +33,15 @@ func TestCanDial(t *testing.T) { d := &WebsocketTransport{} matchTrue := d.CanDial(addrWs) + matchTrueWss := d.CanDial(addrWss) matchFalse := d.CanDial(addrTCP) if !matchTrue { t.Fatal("expected to match websocket maddr, but did not") } + if !matchTrueWss { + t.Fatal("expected to match websocket+tls maddr, but did not") + } if matchFalse { t.Fatal("expected to not match tcp maddr, but did") @@ -54,6 +62,20 @@ func TestWebsocketTransport(t *testing.T) { ttransport.SubtestTransport(t, ta, tb, zero, "peerA") } +func TestWebsocketTransport6(t *testing.T) { + ta := New(&tptu.Upgrader{ + Secure: insecure.New("peerA"), + Muxer: new(mplex.Transport), + }) + tb := New(&tptu.Upgrader{ + Secure: insecure.New("peerB"), + Muxer: new(mplex.Transport), + }) + + zero := "/ip6/::1/tcp/0/ws" + ttransport.SubtestTransport(t, ta, tb, zero, "peerA") +} + func TestWebsocketListen(t *testing.T) { zero, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0/ws") if err != nil {