From 61ce8e723e094e176b1986508c6111a7c938b51f Mon Sep 17 00:00:00 2001 From: Jorropo Date: Tue, 28 May 2019 23:09:53 +0200 Subject: [PATCH 1/3] 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..690f7a8 100644 --- a/websocket.go +++ b/websocket.go @@ -3,6 +3,8 @@ package websocket import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "net" "net/http" @@ -15,8 +17,8 @@ import ( ws "github.com/gorilla/websocket" ma "github.com/multiformats/go-multiaddr" + mafmt "github.com/multiformats/go-multiaddr-fmt" manet "github.com/multiformats/go-multiaddr-net" - mafmt "github.com/whyrusleeping/mafmt" ) // 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 { + wscon, _, err = ws.DefaultDialer.Dial(wsurl, nil) + } else { + 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) + } 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 { From 25ecf69641599a5f64e0fc4a9ef620f8d28bb128 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 29 May 2019 16:25:35 +0200 Subject: [PATCH 2/3] Fixing wss if no system CAs pool were found. If system pool isn't avaible wss can be made but without certificate verification. --- websocket.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/websocket.go b/websocket.go index 690f7a8..e205282 100644 --- a/websocket.go +++ b/websocket.go @@ -78,17 +78,20 @@ func init() { // WebsocketTransport is the actual go-libp2p transport type WebsocketTransport struct { Upgrader *tptu.Upgrader - CertPool *x509.CertPool + CertCfg *tls.Config } func New(u *tptu.Upgrader) *WebsocketTransport { p, err := x509.SystemCertPool() - if err != nil { - p = x509.NewCertPool() + var cfg *tls.Config + if err == nil { + cfg = &tls.Config{RootCAs: p} + } else { + cfg = &tls.Config{RootCAs: nil, InsecureSkipVerify: true} } return &WebsocketTransport{ Upgrader: u, - CertPool: p, + CertCfg: cfg, } } @@ -113,7 +116,6 @@ func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (ma } var wscon *ws.Conn - if raddr.Protocols()[2].Code == WsProtocol.Code { wscon, _, err = ws.DefaultDialer.Dial(wsurl, nil) } else { @@ -127,8 +129,7 @@ func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (ma // 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) + tlsconn, err := tls.Dial("tcp", u.Host, t.CertCfg) if err != nil { return nil, err } From f6d0fbcedd73c05b2a53b045e28c3f166287975c Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 6 Jun 2019 07:55:46 +0200 Subject: [PATCH 3/3] Disabling x509 verification. This was done because of 3 reason : - This is useless for security and was just here to have the same comportement of the browser. - This require some pretty heavy change in libp2p and libp2p-swarm to allow declaration of a protocol requiring dns4|6 addrs not ip. - This save some time by simplyfing testsuite. --- websocket.go | 22 ++++++----------- websocket_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/websocket.go b/websocket.go index e205282..155f9ef 100644 --- a/websocket.go +++ b/websocket.go @@ -4,7 +4,6 @@ package websocket import ( "context" "crypto/tls" - "crypto/x509" "fmt" "net" "net/http" @@ -34,10 +33,10 @@ var WssProtocol = ma.Protocol{ } // WsFmt is multiaddr formatter for WsProtocol -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)), -) +var WsFmt = mafmt.And(mafmt.TCP, mafmt.Or( + mafmt.Base(WsProtocol.Code), + mafmt.Base(WssProtocol.Code), +)) // WsCodec is the multiaddr-net codec definition for the websocket transport var WsCodec = &manet.NetCodec{ @@ -78,20 +77,11 @@ func init() { // WebsocketTransport is the actual go-libp2p transport type WebsocketTransport struct { Upgrader *tptu.Upgrader - CertCfg *tls.Config } func New(u *tptu.Upgrader) *WebsocketTransport { - p, err := x509.SystemCertPool() - var cfg *tls.Config - if err == nil { - cfg = &tls.Config{RootCAs: p} - } else { - cfg = &tls.Config{RootCAs: nil, InsecureSkipVerify: true} - } return &WebsocketTransport{ Upgrader: u, - CertCfg: cfg, } } @@ -109,6 +99,8 @@ func (t *WebsocketTransport) Proxy() bool { return false } +var CertCfg *tls.Config = &tls.Config{RootCAs: nil, InsecureSkipVerify: true} + func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) { wsurl, err := parseMultiaddr(raddr) if err != nil { @@ -129,7 +121,7 @@ func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (ma // your milage may differ "Sec-WebSocket-Extensions": {"permessage-deflate; client_max_window_bits, x-webkit-deflate-frame"}, } - tlsconn, err := tls.Dial("tcp", u.Host, t.CertCfg) + tlsconn, err := tls.Dial("tcp", u.Host, CertCfg) if err != nil { return nil, err } diff --git a/websocket_test.go b/websocket_test.go index 01f344a..a7d8440 100644 --- a/websocket_test.go +++ b/websocket_test.go @@ -3,10 +3,15 @@ package websocket import ( "bytes" "context" + //"crypto/x509" "io" "io/ioutil" "testing" "testing/iotest" + /*"crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" +*/ "github.com/libp2p/go-libp2p-core/sec/insecure" @@ -21,7 +26,7 @@ func TestCanDial(t *testing.T) { if err != nil { t.Fatal(err) } - addrWss, err := ma.NewMultiaddr("/dnsaddr/example.com/tcp/5555/wss") + addrWss, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555/wss") if err != nil { t.Fatal(err) } @@ -216,3 +221,59 @@ func TestWriteZero(t *testing.T) { t.Errorf("expected EOF, got err: %s", err) } } +/* +func TestWebsocketSecureComplete(t *testing.T) { + var cert *[]byte + + cert, _ = &CreateCertificate() + + listen, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0/ws") + if err != nil { + t.Fatal(err) + } + + tpt := &WebsocketTransport{} + l, err := tpt.maListen(zero) + if err != nil { + t.Fatal(err) + } + defer l.Close() + fmt.Printf("%+v\n", l) + + msg := []byte("HELLO WORLD") + + go func() { + c, err := tpt.maDial(context.Background(), l.Multiaddr()) + if err != nil { + t.Error(err) + return + } + + _, err = c.Write(msg) + if err != nil { + t.Error(err) + } + err = c.Close() + if err != nil { + t.Error(err) + } + }() + + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + obr := iotest.OneByteReader(c) + + out, err := ioutil.ReadAll(obr) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(out, msg) { + t.Fatal("got wrong message", out, msg) + } +} +*/