Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions cmd/skywire-cli/commands/dmsg/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,16 @@ func printDmsgSessions(result *visor.DmsgClientSessions) {
}
fmt.Printf("%s (%s)\n", info.Role, info.PK)
fmt.Printf(" Connected sessions: %d\n", info.Count)
for _, s := range info.Servers {
fmt.Printf(" %s\n", s)
// Prefer the enriched session list (server PK + the protocol it was
// reached over); fall back to bare PKs for older visors.
if len(info.Sessions) > 0 {
for _, s := range info.Sessions {
fmt.Printf(" %s %s\n", s.PK, s.Protocol)
}
} else {
for _, s := range info.Servers {
fmt.Printf(" %s\n", s)
}
}
fmt.Println()
}
Expand Down
20 changes: 16 additions & 4 deletions cmd/wasm-visor/selfprovider_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,27 @@ func (s visorSelf) SelfDmsgSessions() []byte {
return nil
}
servers := []cipher.PubKey{}
sessions := []map[string]interface{}{}
for _, cs := range dmsgC.AllSessions() {
servers = append(servers, cs.RemotePK())
// Mirror the native DmsgServerSession shape so the shared Angular UI —
// and the wasm-visor onboarding tour — can show which protocol
// (tcp/ws/wss/webtransport/quic) this browser edge reached each dmsg
// server over. A browser edge dials ws/wss or WebTransport, never tcp.
sessions = append(sessions, map[string]interface{}{
"pk": cs.RemotePK(),
"carrier": cs.Carrier(),
"protocol": cs.Protocol(),
"address": cs.CarrierAddr(),
})
}
out := map[string]interface{}{
"main": map[string]interface{}{
"pk": selfPK,
"role": "main",
"count": len(servers),
"servers": servers,
"pk": selfPK,
"role": "main",
"count": len(servers),
"servers": servers,
"sessions": sessions,
},
}
b, err := json.Marshal(out)
Expand Down
1 change: 1 addition & 0 deletions metrics
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
404 page not found
25 changes: 25 additions & 0 deletions pkg/dmsg/dmsg/carrier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,28 @@ func TestPickCarrier(t *testing.T) {
})
}
}

// TestProtocolLabel locks the human-readable protocol labeling — notably that
// the WebSocket carrier reports wss vs ws by the endpoint scheme, and that an
// empty carrier (an accepted server-side session) reports "accepted".
func TestProtocolLabel(t *testing.T) {
cases := []struct {
carrier string
addr string
want string
}{
{CarrierTCP, "1.2.3.4:8081", "tcp"},
{CarrierWS, "ws://1.2.3.4:8083/dmsg", "ws"},
{CarrierWS, "wss://dmsg.example.com/dmsg", "wss"},
{CarrierWS, "https://dmsg.example.com/dmsg", "wss"},
{CarrierWS, "WSS://UPPER.example.com/dmsg", "wss"},
{CarrierWT, "https://1.2.3.4:8084/dmsg", "webtransport"},
{CarrierQUIC, "1.2.3.4:8085", "quic"},
{"", "", "accepted"},
}
for _, c := range cases {
if got := ProtocolLabel(c.carrier, c.addr); got != c.want {
t.Fatalf("ProtocolLabel(%q,%q) = %q, want %q", c.carrier, c.addr, got, c.want)
}
}
}
38 changes: 38 additions & 0 deletions pkg/dmsg/dmsg/client_sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net"
"strings"

"github.com/hashicorp/yamux"
"github.com/xtaci/smux"
Expand Down Expand Up @@ -209,6 +210,40 @@ func pickCarrier(carriers []string, entry *disc.Entry) (network, addr string) {
return CarrierTCP, entry.Server.Address
}

// ProtocolLabel renders a human-readable label for the protocol a client used
// to reach a dmsg server, given the carrier it dialed and that carrier's
// endpoint address. Every dmsg carrier is Noise-encrypted end to end above the
// byte pipe; the label names the underlying transport, and for the WebSocket
// carrier it distinguishes plain ws:// from TLS-secured wss:// by the endpoint
// scheme. An empty carrier (an accepted, server-side session) renders as
// "accepted".
func ProtocolLabel(carrier, addr string) string {
switch carrier {
case CarrierTCP:
return "tcp"
case CarrierWS:
if isSecureURL(addr) {
return "wss"
}
return "ws"
case CarrierWT:
return "webtransport"
case CarrierQUIC:
return "quic"
case "":
return "accepted"
default:
return carrier
}
}

// isSecureURL reports whether addr is a TLS-secured websocket / https endpoint
// (wss:// or https://), used to distinguish wss from plain ws.
func isSecureURL(addr string) bool {
a := strings.ToLower(strings.TrimSpace(addr))
return strings.HasPrefix(a, "wss://") || strings.HasPrefix(a, "https://")
}

func (ce *Client) dialSession(ctx context.Context, entry *disc.Entry) (cs ClientSession, err error) {
ce.log.WithField("remote_pk", entry.Static).Debug("Dialing session...")

Expand Down Expand Up @@ -355,7 +390,10 @@ func (ce *Client) dialSession(ctx context.Context, entry *disc.Entry) (cs Client
// identity-checked so it cannot evict this live replacement.
// Record the carrier actually used (after any WT→WS / *→TCP fallback above)
// so Client.UpgradeBrowserSessions can later converge wss → WebTransport.
// carrierAddr is the endpoint that carrier dialed, so callers can tell the
// exact protocol used to reach this server (and ws:// from wss://).
dSes.carrier = network
dSes.carrierAddr = dialAddr

ce.sessionsMx.Lock()
if ce.closed {
Expand Down
21 changes: 21 additions & 0 deletions pkg/dmsg/dmsg/session_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ type SessionCommon struct {
// wss converge to WebTransport: see Client.UpgradeBrowserSessions.
carrier string

// carrierAddr is the endpoint the carrier actually dialed: host:port for
// tcp/quic, the ws(s):// URL for ws, the https:// URL for WebTransport.
// Empty for accepted (server-side) sessions. Together with carrier it
// tells you the exact protocol a client used to reach its dmsg server
// (and, for ws, whether it was plain ws:// or secured wss://).
carrierAddr string

netConn net.Conn // underlying net.Conn (TCP connection to the dmsg server)
// ys *yamux.Session
// ss *smux.Session
Expand Down Expand Up @@ -238,6 +245,20 @@ func (sc *SessionCommon) LocalPK() cipher.PubKey { return sc.entity.pk }
// RemotePK returns the remote public key of the session.
func (sc *SessionCommon) RemotePK() cipher.PubKey { return sc.rPK }

// Carrier reports how this client session's byte pipe to the dmsg server was
// dialed: one of CarrierTCP / CarrierWS / CarrierWT / CarrierQUIC. It is empty
// for accepted (server-side) sessions. Use Protocol for a human-readable label
// that also distinguishes ws from wss.
func (sc *SessionCommon) Carrier() string { return sc.carrier }

// CarrierAddr reports the endpoint this session's carrier dialed. Empty for
// accepted (server-side) sessions.
func (sc *SessionCommon) CarrierAddr() string { return sc.carrierAddr }

// Protocol renders a human-readable label for the protocol this session used
// to reach its dmsg server, distinguishing plain ws:// from secured wss://.
func (sc *SessionCommon) Protocol() string { return ProtocolLabel(sc.carrier, sc.carrierAddr) }

// LocalTCPAddr returns the local address of the underlying TCP connection.
func (sc *SessionCommon) LocalTCPAddr() net.Addr { return sc.netConn.LocalAddr() }

Expand Down
17 changes: 17 additions & 0 deletions pkg/visor/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,23 @@ type DmsgClientSessionInfo struct {
Role string `json:"role"` // "main" | "route_setup" | "transport_setup"
Count int `json:"count"`
Servers []cipher.PubKey `json:"servers"`
// Sessions carries the same servers plus the protocol each one was reached
// over (tcp / ws / wss / webtransport / quic). Parallel to Servers, sorted
// the same way, so existing Servers consumers keep working.
Sessions []DmsgServerSession `json:"sessions,omitempty"`
}

// DmsgServerSession is one active dmsg-server session and the protocol the
// local client used to reach it.
type DmsgServerSession struct {
PK cipher.PubKey `json:"pk"`
// Carrier is the raw dmsg carrier: tcp | ws | wt | quic.
Carrier string `json:"carrier"`
// Protocol is a human-readable label distinguishing ws from wss:
// tcp | ws | wss | webtransport | quic.
Protocol string `json:"protocol"`
// Address is the endpoint the carrier dialed (host:port or ws(s)://…).
Address string `json:"address,omitempty"`
}

// DmsgHTTPRequest represents an HTTP request to be made over dmsg
Expand Down
67 changes: 39 additions & 28 deletions pkg/visor/api_dmsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,12 +639,13 @@ func (v *Visor) DmsgSessions() (*DmsgClientSessions, error) {
out := &DmsgClientSessions{}

if v.dmsgC != nil {
servers := dmsgClientServerPKs(v.dmsgC)
servers, sessions := dmsgClientServerSessions(v.dmsgC)
out.Main = &DmsgClientSessionInfo{
PK: v.conf.PK,
Role: "main",
Count: len(servers),
Servers: servers,
PK: v.conf.PK,
Role: "main",
Count: len(servers),
Servers: servers,
Sessions: sessions,
}
}

Expand All @@ -654,39 +655,49 @@ func (v *Visor) DmsgSessions() (*DmsgClientSessions, error) {
v.initLock.Unlock()

if rsn != nil && rsn.DmsgClient() != nil {
servers := dmsgClientServerPKs(rsn.DmsgClient())
servers, sessions := dmsgClientServerSessions(rsn.DmsgClient())
out.RouteSetup = &DmsgClientSessionInfo{
PK: rsn.PK(),
Role: "route_setup",
Count: len(servers),
Servers: servers,
PK: rsn.PK(),
Role: "route_setup",
Count: len(servers),
Servers: servers,
Sessions: sessions,
}
}
if tps != nil && tps.DmsgClient() != nil {
servers := dmsgClientServerPKs(tps.DmsgClient())
servers, sessions := dmsgClientServerSessions(tps.DmsgClient())
out.TransportSetup = &DmsgClientSessionInfo{
PK: tps.PK(),
Role: "transport_setup",
Count: len(servers),
Servers: servers,
PK: tps.PK(),
Role: "transport_setup",
Count: len(servers),
Servers: servers,
Sessions: sessions,
}
}
return out, nil
}

// dmsgClientServerPKs returns the PKs of the dmsg servers the given client
// currently has an active session with. Sorted by PK for stable output.
func dmsgClientServerPKs(c *dmsg.Client) []cipher.PubKey {
strs := c.ConnectedServersPK()
out := make([]cipher.PubKey, 0, len(strs))
for _, s := range strs {
var pk cipher.PubKey
if err := pk.Set(s); err == nil {
out = append(out, pk)
}
}
sort.Slice(out, func(i, j int) bool { return out[i].String() < out[j].String() })
return out
// dmsgClientServerSessions returns the dmsg servers the given client currently
// has an active session with, both as bare PKs (Servers, kept for existing
// consumers) and enriched with the protocol each was reached over (Sessions).
// Both slices are sorted by server PK for stable output.
func dmsgClientServerSessions(c *dmsg.Client) ([]cipher.PubKey, []DmsgServerSession) {
all := c.AllSessions()
sessions := make([]DmsgServerSession, 0, len(all))
for _, s := range all {
sessions = append(sessions, DmsgServerSession{
PK: s.RemotePK(),
Carrier: s.Carrier(),
Protocol: s.Protocol(),
Address: s.CarrierAddr(),
})
}
sort.Slice(sessions, func(i, j int) bool { return sessions[i].PK.String() < sessions[j].PK.String() })
pks := make([]cipher.PubKey, len(sessions))
for i, s := range sessions {
pks[i] = s.PK
}
return pks, sessions
}

// DmsgProbe checks whether a remote PK is reachable on a given dmsg port
Expand Down
Binary file modified wasm-visor
Binary file not shown.
Loading