From b524cb77dcdac078a5bbd6fe4d37b6793030e75e Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Wed, 29 Apr 2026 06:37:20 +0200 Subject: [PATCH] Address review on relay server IP signaling Mark relayServerAddress and relayServerIP as optional in the signal proto, return the relay instance address and IP atomically from Manager.RelayInstanceAddress to avoid divergence across reconnections, and split the relay client constructor into NewClient and NewClientWithServerIP. Rename fallback terminology to server IP throughout. --- client/internal/peer/handshaker.go | 6 +- client/internal/peer/status.go | 2 +- client/internal/peer/worker_relay.go | 14 ++- relay/test/benchmark_test.go | 4 +- relay/testec2/relay.go | 5 +- shared/relay/client/client.go | 42 +++++---- ...llback_test.go => client_serverip_test.go} | 28 +++--- shared/relay/client/client_test.go | 39 ++++---- shared/relay/client/manager.go | 39 ++++---- ...lback_test.go => manager_serverip_test.go} | 21 ++--- shared/relay/client/manager_test.go | 6 +- shared/relay/client/picker.go | 3 +- shared/signal/client/client.go | 31 +++---- shared/signal/proto/signalexchange.pb.go | 89 ++++++++++--------- shared/signal/proto/signalexchange.proto | 4 +- 15 files changed, 162 insertions(+), 171 deletions(-) rename shared/relay/client/{client_fallback_test.go => client_serverip_test.go} (88%) rename shared/relay/client/{manager_fallback_test.go => manager_serverip_test.go} (83%) diff --git a/client/internal/peer/handshaker.go b/client/internal/peer/handshaker.go index c8dc7c905..1d44096b6 100644 --- a/client/internal/peer/handshaker.go +++ b/client/internal/peer/handshaker.go @@ -42,7 +42,7 @@ type OfferAnswer struct { // relay server address RelaySrvAddress string // RelaySrvIP is the IP the remote peer is connected to on its - // relay server. Used as a fallback dial target if DNS for RelaySrvAddress + // relay server. Used as a dial target if DNS for RelaySrvAddress // fails. Zero value if the peer did not advertise an IP. RelaySrvIP netip.Addr // SessionID is the unique identifier of the session, used to discard old messages @@ -222,9 +222,9 @@ func (h *Handshaker) buildOfferAnswer() OfferAnswer { answer.SessionID = &sid } - if addr, err := h.relay.RelayInstanceAddress(); err == nil { + if addr, ip, err := h.relay.RelayInstanceAddress(); err == nil { answer.RelaySrvAddress = addr - answer.RelaySrvIP = h.relay.RelayInstanceIP() + answer.RelaySrvIP = ip } return answer diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go index abedc208e..7bd19b0e1 100644 --- a/client/internal/peer/status.go +++ b/client/internal/peer/status.go @@ -919,7 +919,7 @@ func (d *Status) GetRelayStates() []relay.ProbeResult { // if the server connection is not established then we will use the general address // in case of connection we will use the instance specific address - instanceAddr, err := d.relayMgr.RelayInstanceAddress() + instanceAddr, _, err := d.relayMgr.RelayInstanceAddress() if err != nil { // TODO add their status for _, r := range d.relayMgr.ServerURLs() { diff --git a/client/internal/peer/worker_relay.go b/client/internal/peer/worker_relay.go index e8c1d68cd..0402992c9 100644 --- a/client/internal/peer/worker_relay.go +++ b/client/internal/peer/worker_relay.go @@ -54,19 +54,19 @@ func (w *WorkerRelay) OnNewOffer(remoteOfferAnswer *OfferAnswer) { w.relaySupportedOnRemotePeer.Store(true) // the relayManager will return with error in case if the connection has lost with relay server - currentRelayAddress, err := w.relayManager.RelayInstanceAddress() + currentRelayAddress, _, err := w.relayManager.RelayInstanceAddress() if err != nil { w.log.Errorf("failed to handle new offer: %s", err) return } srv := w.preferredRelayServer(currentRelayAddress, remoteOfferAnswer.RelaySrvAddress) - var fallbackIP netip.Addr + var serverIP netip.Addr if srv == remoteOfferAnswer.RelaySrvAddress { - fallbackIP = remoteOfferAnswer.RelaySrvIP + serverIP = remoteOfferAnswer.RelaySrvIP } - relayedConn, err := w.relayManager.OpenConn(w.peerCtx, srv, w.config.Key, fallbackIP) + relayedConn, err := w.relayManager.OpenConn(w.peerCtx, srv, w.config.Key, serverIP) if err != nil { if errors.Is(err, relayClient.ErrConnAlreadyExists) { w.log.Debugf("handled offer by reusing existing relay connection") @@ -95,14 +95,10 @@ func (w *WorkerRelay) OnNewOffer(remoteOfferAnswer *OfferAnswer) { }) } -func (w *WorkerRelay) RelayInstanceAddress() (string, error) { +func (w *WorkerRelay) RelayInstanceAddress() (string, netip.Addr, error) { return w.relayManager.RelayInstanceAddress() } -func (w *WorkerRelay) RelayInstanceIP() netip.Addr { - return w.relayManager.RelayInstanceIP() -} - func (w *WorkerRelay) IsRelayConnectionSupportedWithPeer() bool { return w.relaySupportedOnRemotePeer.Load() && w.RelayIsSupportedLocally() } diff --git a/relay/test/benchmark_test.go b/relay/test/benchmark_test.go index 117a1e500..9dd189776 100644 --- a/relay/test/benchmark_test.go +++ b/relay/test/benchmark_test.go @@ -102,7 +102,7 @@ func transfer(t *testing.T, testData []byte, peerPairs int) { clientsSender := make([]*client.Client, peerPairs) for i := 0; i < cap(clientsSender); i++ { - c := client.NewClient(serverConnURL, netip.Addr{}, hmacTokenStore, "sender-"+fmt.Sprint(i), iface.DefaultMTU) + c := client.NewClient(serverConnURL, hmacTokenStore, "sender-"+fmt.Sprint(i), iface.DefaultMTU) err := c.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -112,7 +112,7 @@ func transfer(t *testing.T, testData []byte, peerPairs int) { clientsReceiver := make([]*client.Client, peerPairs) for i := 0; i < cap(clientsReceiver); i++ { - c := client.NewClient(serverConnURL, netip.Addr{}, hmacTokenStore, "receiver-"+fmt.Sprint(i), iface.DefaultMTU) + c := client.NewClient(serverConnURL, hmacTokenStore, "receiver-"+fmt.Sprint(i), iface.DefaultMTU) err := c.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) diff --git a/relay/testec2/relay.go b/relay/testec2/relay.go index 6b0c1cba6..e6924061f 100644 --- a/relay/testec2/relay.go +++ b/relay/testec2/relay.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "net" - "net/netip" "sync" "time" @@ -72,7 +71,7 @@ func prepareConnsSender(serverConnURL string, peerPairs int) []net.Conn { ctx := context.Background() clientsSender := make([]*client.Client, peerPairs) for i := 0; i < cap(clientsSender); i++ { - c := client.NewClient(serverConnURL, netip.Addr{}, hmacTokenStore, "sender-"+fmt.Sprint(i), iface.DefaultMTU) + c := client.NewClient(serverConnURL, hmacTokenStore, "sender-"+fmt.Sprint(i), iface.DefaultMTU) if err := c.Connect(ctx); err != nil { log.Fatalf("failed to connect to server: %s", err) } @@ -158,7 +157,7 @@ func runReader(conn net.Conn) time.Duration { func prepareConnsReceiver(serverConnURL string, peerPairs int) []net.Conn { clientsReceiver := make([]*client.Client, peerPairs) for i := 0; i < cap(clientsReceiver); i++ { - c := client.NewClient(serverConnURL, netip.Addr{}, hmacTokenStore, "receiver-"+fmt.Sprint(i), iface.DefaultMTU) + c := client.NewClient(serverConnURL, hmacTokenStore, "receiver-"+fmt.Sprint(i), iface.DefaultMTU) err := c.Connect(context.Background()) if err != nil { log.Fatalf("failed to connect to server: %s", err) diff --git a/shared/relay/client/client.go b/shared/relay/client/client.go index 069665a32..e4b586ce6 100644 --- a/shared/relay/client/client.go +++ b/shared/relay/client/client.go @@ -150,7 +150,7 @@ func (cc *connContainer) close() { type Client struct { log *log.Entry connectionURL string - fallbackIP netip.Addr + serverIP netip.Addr authTokenStore *auth.TokenStore hashedID messages.PeerID @@ -175,16 +175,21 @@ type Client struct { } // NewClient creates a new client for the relay server. The client is not connected to the server until the Connect -// is called. fallbackIP, when valid, is used as a dial-time fallback if the FQDN-based dial fails. TLS -// verification still uses the FQDN from serverURL via SNI. -func NewClient(serverURL string, fallbackIP netip.Addr, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client { +// is called. +func NewClient(serverURL string, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client { + return NewClientWithServerIP(serverURL, netip.Addr{}, authTokenStore, peerID, mtu) +} + +// NewClientWithServerIP creates a new client for the relay server with a known server IP. serverIP, when valid, is +// used as a dial target if the FQDN-based dial fails. TLS verification still uses the FQDN from serverURL via SNI. +func NewClientWithServerIP(serverURL string, serverIP netip.Addr, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client { hashedID := messages.HashID(peerID) relayLog := log.WithFields(log.Fields{"relay": serverURL}) c := &Client{ log: relayLog, connectionURL: serverURL, - fallbackIP: fallbackIP, + serverIP: serverIP, authTokenStore: authTokenStore, hashedID: hashedID, mtu: mtu, @@ -378,11 +383,11 @@ func (c *Client) connect(ctx context.Context) (*RelayAddr, error) { rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, c.connectionURL, dialers...) conn, err := rd.Dial(ctx) if err != nil { - fallbackConn, fbErr := c.dialFallback(ctx, dialers) - if fbErr != nil { - return nil, fmt.Errorf("primary dial: %w; fallback dial: %w", err, fbErr) + directConn, dErr := c.dialDirect(ctx, dialers) + if dErr != nil { + return nil, fmt.Errorf("dial via FQDN: %w; dial via server IP: %w", err, dErr) } - conn = fallbackConn + conn = directConn } c.relayConn = conn @@ -398,22 +403,21 @@ func (c *Client) connect(ctx context.Context) (*RelayAddr, error) { return instanceURL, nil } -// dialFallback retries the dial against c.fallbackIP, preserving the -// original FQDN as the TLS ServerName for SNI. Returns an error if no -// fallback IP is configured or if the substituted URL is malformed. -func (c *Client) dialFallback(ctx context.Context, dialers []dialer.DialeFn) (net.Conn, error) { - if !c.fallbackIP.IsValid() || c.fallbackIP.IsUnspecified() { - return nil, errors.New("no usable fallback IP configured") +// dialDirect retries the dial against c.serverIP, preserving the original FQDN as the TLS ServerName for SNI. +// Returns an error if no usable server IP is configured or if the substituted URL is malformed. +func (c *Client) dialDirect(ctx context.Context, dialers []dialer.DialeFn) (net.Conn, error) { + if !c.serverIP.IsValid() || c.serverIP.IsUnspecified() { + return nil, errors.New("no usable server IP configured") } - fallbackURL, serverName, err := substituteHost(c.connectionURL, c.fallbackIP) + directURL, serverName, err := substituteHost(c.connectionURL, c.serverIP) if err != nil { return nil, fmt.Errorf("substitute host: %w", err) } - c.log.Infof("primary dial failed, retrying via fallback IP %s (SNI=%s)", c.fallbackIP, serverName) + c.log.Infof("FQDN dial failed, retrying via server IP %s (SNI=%s)", c.serverIP, serverName) - rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, fallbackURL, dialers...). + rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, directURL, dialers...). WithServerName(serverName) return rd.Dial(ctx) } @@ -431,7 +435,7 @@ func substituteHost(serverURL string, ip netip.Addr) (string, string, error) { return "", "", fmt.Errorf("invalid relay URL %q", serverURL) } if !ip.IsValid() { - return "", "", errors.New("invalid fallback IP") + return "", "", errors.New("invalid server IP") } origHost := u.Hostname() if _, err := netip.ParseAddr(origHost); err == nil { diff --git a/shared/relay/client/client_fallback_test.go b/shared/relay/client/client_serverip_test.go similarity index 88% rename from shared/relay/client/client_fallback_test.go rename to shared/relay/client/client_serverip_test.go index 677fc5189..7e699e37d 100644 --- a/shared/relay/client/client_fallback_test.go +++ b/shared/relay/client/client_serverip_test.go @@ -15,10 +15,10 @@ import ( "github.com/netbirdio/netbird/shared/relay/auth/allow" ) -// TestClient_FallbackIPRecoversFromUnresolvableFQDN verifies that when the +// TestClient_ServerIPRecoversFromUnresolvableFQDN verifies that when the // primary FQDN-based dial fails (unresolvable .invalid host), Connect -// recovers via the fallback IP and SNI still uses the FQDN. -func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) { +// recovers via the server IP and SNI still uses the FQDN. +func TestClient_ServerIPRecoversFromUnresolvableFQDN(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() @@ -49,19 +49,19 @@ func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) { t.Fatalf("server failed to start: %s", err) } - t.Run("no fallback IP, primary fails", func(t *testing.T) { - c := NewClient(srvCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice-nofallback", iface.DefaultMTU) + t.Run("no server IP, primary fails", func(t *testing.T) { + c := NewClient(srvCfg.ExposedAddress, hmacTokenStore, "alice-noip", iface.DefaultMTU) err := c.Connect(ctx) if err == nil { _ = c.Close() - t.Fatalf("expected connect to fail without fallback IP, got nil") + t.Fatalf("expected connect to fail without server IP, got nil") } }) - t.Run("fallback IP recovers", func(t *testing.T) { - c := NewClient(srvCfg.ExposedAddress, netip.MustParseAddr("127.0.0.1"), hmacTokenStore, "alice-fallback", iface.DefaultMTU) + t.Run("server IP recovers", func(t *testing.T) { + c := NewClientWithServerIP(srvCfg.ExposedAddress, netip.MustParseAddr("127.0.0.1"), hmacTokenStore, "alice-with-ip", iface.DefaultMTU) if err := c.Connect(ctx); err != nil { - t.Fatalf("connect with fallback IP: %s", err) + t.Fatalf("connect with server IP: %s", err) } t.Cleanup(func() { _ = c.Close() }) @@ -77,7 +77,7 @@ func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) { // TestClient_ConnectedIPAfterFQDNDial verifies ConnectedIP returns the // resolved IP after a successful FQDN-based dial. The underlying socket's // RemoteAddr must be exposed through the dialer wrappers; if it returns -// the dial-time URL instead, ConnectedIP returns empty and the fallback +// the dial-time URL instead, ConnectedIP returns empty and the dial // IP we advertise to peers is empty too. func TestClient_ConnectedIPAfterFQDNDial(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) @@ -105,7 +105,7 @@ func TestClient_ConnectedIPAfterFQDNDial(t *testing.T) { t.Fatalf("server failed to start: %s", err) } - c := NewClient(srvCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice-fqdn", iface.DefaultMTU) + c := NewClient(srvCfg.ExposedAddress, hmacTokenStore, "alice-fqdn", iface.DefaultMTU) if err := c.Connect(ctx); err != nil { t.Fatalf("connect: %s", err) } @@ -141,7 +141,7 @@ func TestSubstituteHost(t *testing.T) { wantServerName: "relay.example.com", }, { - name: "ipv6 fallback bracketed", + name: "ipv6 server IP bracketed", serverURL: "rels://relay.example.com:443", ip: "2001:db8::1", wantURL: "rels://[2001:db8::1]:443", @@ -169,7 +169,7 @@ func TestSubstituteHost(t *testing.T) { wantServerName: "", }, { - name: "ipv6 fallback no port", + name: "ipv6 server IP no port", serverURL: "rels://relay.example.com", ip: "2001:db8::1", wantURL: "rels://[2001:db8::1]", @@ -215,7 +215,7 @@ func TestSubstituteHost(t *testing.T) { } func TestClient_ConnectedIPEmptyWhenNotConnected(t *testing.T) { - c := NewClient("rel://example.invalid:80", netip.Addr{}, hmacTokenStore, "x", iface.DefaultMTU) + c := NewClient("rel://example.invalid:80", hmacTokenStore, "x", iface.DefaultMTU) if got := c.ConnectedIP(); got.IsValid() { t.Fatalf("ConnectedIP on disconnected client = %q, want zero", got) } diff --git a/shared/relay/client/client_test.go b/shared/relay/client/client_test.go index 8043252e9..9820d642f 100644 --- a/shared/relay/client/client_test.go +++ b/shared/relay/client/client_test.go @@ -3,7 +3,6 @@ package client import ( "context" "net" - "net/netip" "os" "testing" "time" @@ -69,7 +68,7 @@ func TestClient(t *testing.T) { t.Fatalf("failed to start server: %s", err) } t.Log("alice connecting to server") - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -77,7 +76,7 @@ func TestClient(t *testing.T) { defer clientAlice.Close() t.Log("placeholder connecting to server") - clientPlaceHolder := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "clientPlaceHolder", iface.DefaultMTU) + clientPlaceHolder := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "clientPlaceHolder", iface.DefaultMTU) err = clientPlaceHolder.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -85,7 +84,7 @@ func TestClient(t *testing.T) { defer clientPlaceHolder.Close() t.Log("Bob connecting to server") - clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU) + clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU) err = clientBob.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -145,7 +144,7 @@ func TestRegistration(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { _ = srv.Shutdown(ctx) @@ -185,7 +184,7 @@ func TestRegistrationTimeout(t *testing.T) { _ = fakeTCPListener.Close() }(fakeTCPListener) - clientAlice := NewClient("127.0.0.1:50201", netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient("127.0.0.1:50201", hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err == nil { t.Errorf("failed to connect to server: %s", err) @@ -228,7 +227,7 @@ func TestEcho(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -240,7 +239,7 @@ func TestEcho(t *testing.T) { } }() - clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idBob, iface.DefaultMTU) + clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idBob, iface.DefaultMTU) err = clientBob.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -320,7 +319,7 @@ func TestBindToUnavailabePeer(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Errorf("failed to connect to server: %s", err) @@ -368,13 +367,13 @@ func TestBindReconnect(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) } - clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU) + clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU) err = clientBob.Connect(ctx) if err != nil { t.Errorf("failed to connect to server: %s", err) @@ -396,7 +395,7 @@ func TestBindReconnect(t *testing.T) { t.Errorf("failed to close client: %s", err) } - clientAlice = NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice = NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Errorf("failed to connect to server: %s", err) @@ -471,13 +470,13 @@ func TestCloseConn(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - bob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU) + bob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU) err = bob.Connect(ctx) if err != nil { t.Errorf("failed to connect to server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Errorf("failed to connect to server: %s", err) @@ -535,13 +534,13 @@ func TestCloseRelayConn(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - bob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU) + bob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU) err = bob.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -591,7 +590,7 @@ func TestCloseByServer(t *testing.T) { idAlice := "alice" log.Debugf("connect by alice") - relayClient := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU) + relayClient := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU) if err = relayClient.Connect(ctx); err != nil { log.Fatalf("failed to connect to server: %s", err) } @@ -649,7 +648,7 @@ func TestCloseByClient(t *testing.T) { idAlice := "alice" log.Debugf("connect by alice") - relayClient := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU) + relayClient := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU) err = relayClient.Connect(ctx) if err != nil { log.Fatalf("failed to connect to server: %s", err) @@ -702,7 +701,7 @@ func TestCloseNotDrainedChannel(t *testing.T) { t.Fatalf("failed to start server: %s", err) } - clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU) + clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU) err = clientAlice.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) @@ -714,7 +713,7 @@ func TestCloseNotDrainedChannel(t *testing.T) { } }() - clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idBob, iface.DefaultMTU) + clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idBob, iface.DefaultMTU) err = clientBob.Connect(ctx) if err != nil { t.Fatalf("failed to connect to server: %s", err) diff --git a/shared/relay/client/manager.go b/shared/relay/client/manager.go index e1732258a..6a219914f 100644 --- a/shared/relay/client/manager.go +++ b/shared/relay/client/manager.go @@ -132,9 +132,9 @@ func (m *Manager) Serve() error { // established via the relay server. If the peer is on a different relay server, the manager will establish a new // connection to the relay server. It returns back with a net.Conn what represent the remote peer connection. // -// fallbackIP, when valid and serverAddress is foreign, is used as a dial-time fallback if the FQDN-based -// dial fails. Ignored for the local home-server path. TLS verification still uses the FQDN via SNI. -func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, fallbackIP netip.Addr) (net.Conn, error) { +// serverIP, when valid and serverAddress is foreign, is used as a dial target if the FQDN-based dial fails. +// Ignored for the local home-server path. TLS verification still uses the FQDN via SNI. +func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, serverIP netip.Addr) (net.Conn, error) { m.relayClientMu.RLock() defer m.relayClientMu.RUnlock() @@ -155,7 +155,7 @@ func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, f netConn, err = m.relayClient.OpenConn(ctx, peerKey) } else { log.Debugf("open peer connection via foreign server: %s", serverAddress) - netConn, err = m.openConnVia(ctx, serverAddress, peerKey, fallbackIP) + netConn, err = m.openConnVia(ctx, serverAddress, peerKey, serverIP) } if err != nil { return nil, err @@ -207,29 +207,22 @@ func (m *Manager) AddCloseListener(serverAddress string, onClosedListener OnServ return nil } -// RelayInstanceAddress returns the address of the permanent relay server. It could change if the network connection is -// lost. This address will be sent to the target peer to choose the common relay server for the communication. -func (m *Manager) RelayInstanceAddress() (string, error) { +// RelayInstanceAddress returns the address and resolved IP of the permanent relay server. It could change if the +// network connection is lost. The address is sent to the target peer to choose the common relay server for the +// communication; the IP is sent alongside so remote peers can dial directly without their own DNS lookup. Both +// values are read under the same lock so they cannot diverge across a reconnection. +func (m *Manager) RelayInstanceAddress() (string, netip.Addr, error) { m.relayClientMu.RLock() defer m.relayClientMu.RUnlock() if m.relayClient == nil { - return "", ErrRelayClientNotConnected + return "", netip.Addr{}, ErrRelayClientNotConnected } - return m.relayClient.ServerInstanceURL() -} - -// RelayInstanceIP returns the IP address of the live home relay connection. -// Zero value if not connected. Sent alongside RelayInstanceAddress so remote -// peers can dial directly without their own DNS lookup. -func (m *Manager) RelayInstanceIP() netip.Addr { - m.relayClientMu.RLock() - defer m.relayClientMu.RUnlock() - - if m.relayClient == nil { - return netip.Addr{} + addr, err := m.relayClient.ServerInstanceURL() + if err != nil { + return "", netip.Addr{}, err } - return m.relayClient.ConnectedIP() + return addr, m.relayClient.ConnectedIP(), nil } // ServerURLs returns the addresses of the relay servers. @@ -253,7 +246,7 @@ func (m *Manager) UpdateToken(token *relayAuth.Token) error { return m.tokenStore.UpdateToken(token) } -func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string, fallbackIP netip.Addr) (net.Conn, error) { +func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string, serverIP netip.Addr) (net.Conn, error) { // check if already has a connection to the desired relay server m.relayClientsMutex.RLock() rt, ok := m.relayClients[serverAddress] @@ -288,7 +281,7 @@ func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string m.relayClients[serverAddress] = rt m.relayClientsMutex.Unlock() - relayClient := NewClient(serverAddress, fallbackIP, m.tokenStore, m.peerID, m.mtu) + relayClient := NewClientWithServerIP(serverAddress, serverIP, m.tokenStore, m.peerID, m.mtu) err := relayClient.Connect(m.ctx) if err != nil { rt.err = err diff --git a/shared/relay/client/manager_fallback_test.go b/shared/relay/client/manager_serverip_test.go similarity index 83% rename from shared/relay/client/manager_fallback_test.go rename to shared/relay/client/manager_serverip_test.go index 97d4d6185..a4b91b1a0 100644 --- a/shared/relay/client/manager_fallback_test.go +++ b/shared/relay/client/manager_serverip_test.go @@ -10,12 +10,12 @@ import ( "github.com/netbirdio/netbird/relay/server" ) -// TestManager_ForeignRelayFallbackIP exercises the foreign-relay path +// TestManager_ForeignRelayServerIP exercises the foreign-relay path // end-to-end through Manager.OpenConn. Alice and Bob register on different // relay servers; Alice dials Bob's foreign relay using an unresolvable -// FQDN. Without a fallback IP the dial fails; with Bob's advertised IP it +// FQDN. Without a server IP the dial fails; with Bob's advertised IP it // recovers and a payload round-trips between the peers. -func TestManager_ForeignRelayFallbackIP(t *testing.T) { +func TestManager_ForeignRelayServerIP(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() @@ -66,14 +66,11 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) { t.Fatalf("bob manager serve: %s", err) } - // Bob's real relay URL (what mgrBob.RelayInstanceAddress returns). - bobRealAddr, err := mgrBob.RelayInstanceAddress() + // Bob's real relay URL and the IP that would ride along in signal as relayServerIP. + bobRealAddr, bobAdvertisedIP, err := mgrBob.RelayInstanceAddress() if err != nil { t.Fatalf("bob relay address: %s", err) } - // What Bob's RelayInstanceIP() reports — this is the field that - // would ride along in signal as relayServerIP. - bobAdvertisedIP := mgrBob.RelayInstanceIP() if !bobAdvertisedIP.IsValid() { t.Fatalf("expected valid RelayInstanceIP for bob, got zero") } @@ -84,16 +81,16 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) { t.Fatalf("broken FQDN must differ from bob's real address (%s)", bobRealAddr) } - t.Run("no fallback IP, dial fails", func(t *testing.T) { + t.Run("no server IP, dial fails", func(t *testing.T) { dialCtx, dialCancel := context.WithTimeout(ctx, 5*time.Second) defer dialCancel() _, err := mgrAlice.OpenConn(dialCtx, brokenFQDN, "bob", netip.Addr{}) if err == nil { - t.Fatalf("expected OpenConn to fail without fallback IP, got success") + t.Fatalf("expected OpenConn to fail without server IP, got success") } }) - t.Run("fallback IP recovers", func(t *testing.T) { + t.Run("server IP recovers", func(t *testing.T) { // Bob waits for Alice's incoming peer connection on his side. bobSideCh := make(chan error, 1) go func() { @@ -117,7 +114,7 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) { aliceConn, err := mgrAlice.OpenConn(ctx, brokenFQDN, "bob", bobAdvertisedIP) if err != nil { - t.Fatalf("alice OpenConn with fallback IP: %s", err) + t.Fatalf("alice OpenConn with server IP: %s", err) } t.Cleanup(func() { _ = aliceConn.Close() }) diff --git a/shared/relay/client/manager_test.go b/shared/relay/client/manager_test.go index 754dde4ac..9e964f688 100644 --- a/shared/relay/client/manager_test.go +++ b/shared/relay/client/manager_test.go @@ -2,8 +2,8 @@ package client import ( "context" - "net/netip" "fmt" + "net/netip" "testing" "time" @@ -102,7 +102,7 @@ func TestForeignConn(t *testing.T) { if err := clientBob.Serve(); err != nil { t.Fatalf("failed to serve manager: %s", err) } - bobsSrvAddr, err := clientBob.RelayInstanceAddress() + bobsSrvAddr, _, err := clientBob.RelayInstanceAddress() if err != nil { t.Fatalf("failed to get relay address: %s", err) } @@ -368,7 +368,7 @@ func TestAutoReconnect(t *testing.T) { if err != nil { t.Fatalf("failed to serve manager: %s", err) } - ra, err := clientAlice.RelayInstanceAddress() + ra, _, err := clientAlice.RelayInstanceAddress() if err != nil { t.Errorf("failed to get relay address: %s", err) } diff --git a/shared/relay/client/picker.go b/shared/relay/client/picker.go index e096a7a38..39d0ba072 100644 --- a/shared/relay/client/picker.go +++ b/shared/relay/client/picker.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net/netip" "sync/atomic" "time" @@ -70,7 +69,7 @@ func (sp *ServerPicker) PickServer(parentCtx context.Context) (*Client, error) { func (sp *ServerPicker) startConnection(ctx context.Context, resultChan chan connResult, url string) { log.Infof("try to connecting to relay server: %s", url) - relayClient := NewClient(url, netip.Addr{}, sp.TokenStore, sp.PeerID, sp.MTU) + relayClient := NewClient(url, sp.TokenStore, sp.PeerID, sp.MTU) err := relayClient.Connect(ctx) resultChan <- connResult{ RelayClient: relayClient, diff --git a/shared/signal/client/client.go b/shared/signal/client/client.go index 8e5eb6b5a..9dc6ccd37 100644 --- a/shared/signal/client/client.go +++ b/shared/signal/client/client.go @@ -72,25 +72,26 @@ func UnMarshalCredential(msg *proto.Message) (*Credential, error) { // MarshalCredential marshal a Credential instance and returns a Message object func MarshalCredential(myKey wgtypes.Key, remoteKey string, p CredentialPayload) (*proto.Message, error) { - var relayIPBytes []byte + body := &proto.Body{ + Type: p.Type, + Payload: fmt.Sprintf("%s:%s", p.Credential.UFrag, p.Credential.Pwd), + WgListenPort: uint32(p.WgListenPort), + NetBirdVersion: version.NetbirdVersion(), + RosenpassConfig: &proto.RosenpassConfig{ + RosenpassPubKey: p.RosenpassPubKey, + RosenpassServerAddr: p.RosenpassAddr, + }, + SessionId: p.SessionID, + } + if p.RelaySrvAddress != "" { + body.RelayServerAddress = &p.RelaySrvAddress + } if p.RelaySrvIP.IsValid() { - relayIPBytes = p.RelaySrvIP.Unmap().AsSlice() + body.RelayServerIP = p.RelaySrvIP.Unmap().AsSlice() } return &proto.Message{ Key: myKey.PublicKey().String(), RemoteKey: remoteKey, - Body: &proto.Body{ - Type: p.Type, - Payload: fmt.Sprintf("%s:%s", p.Credential.UFrag, p.Credential.Pwd), - WgListenPort: uint32(p.WgListenPort), - NetBirdVersion: version.NetbirdVersion(), - RosenpassConfig: &proto.RosenpassConfig{ - RosenpassPubKey: p.RosenpassPubKey, - RosenpassServerAddr: p.RosenpassAddr, - }, - RelayServerAddress: p.RelaySrvAddress, - RelayServerIP: relayIPBytes, - SessionId: p.SessionID, - }, + Body: body, }, nil } diff --git a/shared/signal/proto/signalexchange.pb.go b/shared/signal/proto/signalexchange.pb.go index f7fe24bf8..9afa5d714 100644 --- a/shared/signal/proto/signalexchange.pb.go +++ b/shared/signal/proto/signalexchange.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.12 +// protoc v7.34.1 // source: signalexchange.proto package proto @@ -229,13 +229,13 @@ type Body struct { // RosenpassConfig is a Rosenpass config of the remote peer our peer tries to connect to RosenpassConfig *RosenpassConfig `protobuf:"bytes,7,opt,name=rosenpassConfig,proto3" json:"rosenpassConfig,omitempty"` // relayServerAddress is url of the relay server - RelayServerAddress string `protobuf:"bytes,8,opt,name=relayServerAddress,proto3" json:"relayServerAddress,omitempty"` - SessionId []byte `protobuf:"bytes,10,opt,name=sessionId,proto3,oneof" json:"sessionId,omitempty"` + RelayServerAddress *string `protobuf:"bytes,8,opt,name=relayServerAddress,proto3,oneof" json:"relayServerAddress,omitempty"` + SessionId []byte `protobuf:"bytes,10,opt,name=sessionId,proto3,oneof" json:"sessionId,omitempty"` // relayServerIP is the IP the sender is connected to on its relay server, // encoded as 4 bytes (IPv4) or 16 bytes (IPv6). Receivers may use it as a // fallback dial target when DNS resolution of relayServerAddress fails. // SNI/TLS verification still uses relayServerAddress. - RelayServerIP []byte `protobuf:"bytes,11,opt,name=relayServerIP,proto3" json:"relayServerIP,omitempty"` + RelayServerIP []byte `protobuf:"bytes,11,opt,name=relayServerIP,proto3,oneof" json:"relayServerIP,omitempty"` } func (x *Body) Reset() { @@ -320,8 +320,8 @@ func (x *Body) GetRosenpassConfig() *RosenpassConfig { } func (x *Body) GetRelayServerAddress() string { - if x != nil { - return x.RelayServerAddress + if x != nil && x.RelayServerAddress != nil { + return *x.RelayServerAddress } return "" } @@ -463,7 +463,7 @@ var file_signalexchange_proto_rawDesc = []byte{ 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x90, 0x04, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2d, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xc3, 0x04, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, @@ -483,43 +483,46 @@ var file_signalexchange_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x33, 0x0a, 0x12, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x50, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x50, 0x22, - 0x43, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x46, 0x46, 0x45, 0x52, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x53, 0x57, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0d, - 0x0a, 0x09, 0x43, 0x41, 0x4e, 0x44, 0x49, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, - 0x04, 0x4d, 0x4f, 0x44, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x4f, 0x5f, 0x49, 0x44, - 0x4c, 0x45, 0x10, 0x05, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x2e, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, - 0x12, 0x1b, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x48, 0x00, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x22, 0x6d, 0x0a, 0x0f, 0x52, 0x6f, 0x73, 0x65, - 0x6e, 0x70, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, 0x0f, 0x72, - 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, - 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x32, 0xb9, 0x01, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x4c, 0x0a, 0x04, 0x53, 0x65, - 0x6e, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x2e, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x29, + 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x50, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x49, 0x50, 0x88, 0x01, 0x01, 0x22, 0x43, 0x0a, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x46, 0x46, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x41, 0x4e, 0x53, 0x57, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x4e, 0x44, + 0x49, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x44, 0x45, 0x10, + 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x4f, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x05, 0x42, 0x15, + 0x0a, 0x13, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x49, 0x50, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x2e, 0x0a, 0x04, 0x4d, + 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x22, 0x6d, 0x0a, 0x0f, 0x52, + 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, + 0x0a, 0x0f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, + 0x73, 0x73, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, + 0x6e, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x32, 0xb9, 0x01, 0x0a, 0x0e, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x4c, 0x0a, + 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x20, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x20, + 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/shared/signal/proto/signalexchange.proto b/shared/signal/proto/signalexchange.proto index 06817e658..96a4001e3 100644 --- a/shared/signal/proto/signalexchange.proto +++ b/shared/signal/proto/signalexchange.proto @@ -63,7 +63,7 @@ message Body { RosenpassConfig rosenpassConfig = 7; // relayServerAddress is url of the relay server - string relayServerAddress = 8; + optional string relayServerAddress = 8; reserved 9; @@ -73,7 +73,7 @@ message Body { // encoded as 4 bytes (IPv4) or 16 bytes (IPv6). Receivers may use it as a // fallback dial target when DNS resolution of relayServerAddress fails. // SNI/TLS verification still uses relayServerAddress. - bytes relayServerIP = 11; + optional bytes relayServerIP = 11; } // Mode indicates a connection mode