From dded91235e9c20a3e04e4bc7483c279465f0357a Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 7 Sep 2022 18:49:15 +0200 Subject: [PATCH] Refactor UDP mux to handle STUN only messages --- iface/bind.go | 44 ++++++++++++++------------------ iface/udp_mux.go | 51 ++++++++++++++------------------------ iface/udp_mux_universal.go | 41 +++++++++++------------------- 3 files changed, 53 insertions(+), 83 deletions(-) diff --git a/iface/bind.go b/iface/bind.go index 7520ff36e..7ae68aa55 100644 --- a/iface/bind.go +++ b/iface/bind.go @@ -13,7 +13,7 @@ import ( ) type BindMux interface { - HandlePacket(p []byte, n int, addr net.Addr) error + HandleSTUNMessage(msg *stun.Message, addr net.Addr) error Type() string } @@ -96,6 +96,17 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) { return conn, uaddr.Port, nil } +func parseStunMessage(raw []byte) (*stun.Message, error) { + msg := &stun.Message{ + Raw: append([]byte{}, raw...), + } + if err := msg.Decode(); err != nil { + return nil, err + } + + return msg, nil +} + func (b *ICEBind) makeReceiveIPv4(c net.PacketConn) conn.ReceiveFunc { return func(buff []byte) (int, conn.Endpoint, error) { n, endpoint, err := c.ReadFrom(buff) @@ -114,37 +125,20 @@ func (b *ICEBind) makeReceiveIPv4(c net.PacketConn) conn.ReceiveFunc { Zone: e.Addr().Zone(), }), nil } + + msg, err := parseStunMessage(buff[:n]) + if err != nil { + return 0, nil, err + } + b.mu.Lock() - - /* msg := &stun.Message{ - Raw: append([]byte{}, buff[:n]...), - } - if err := msg.Decode(); err != nil { - return 0, nil, err - } - strAttrs := []string{} - for _, attribute := range msg.Attributes { - strAttrs = append(strAttrs, attribute.String()) - } - - xorMapped := "EMPTY" - _, err = msg.Get(stun.AttrXORMappedAddress) - if err == nil { - var addr stun.XORMappedAddress - if err := addr.GetFrom(msg); err == nil { - xorMapped = addr.String() - } - } - - log.Printf("endpoint %s XORMAPPED %s mux type %s msg type %s, attributes %s", endpoint.String(), xorMapped, bindMux.Type(), msg.Type.String(), strings.Join(strAttrs[:], ";")) - */ if _, ok := b.endpointMap[e.String()]; !ok { b.endpointMap[e.String()] = c log.Infof("added endpoint %s", e.String()) } b.mu.Unlock() - err = b.udpMux.HandlePacket(buff, n, endpoint) + err = b.udpMux.HandleSTUNMessage(msg, endpoint) if err != nil { return 0, nil, err } diff --git a/iface/udp_mux.go b/iface/udp_mux.go index 112331fe1..5fd512121 100644 --- a/iface/udp_mux.go +++ b/iface/udp_mux.go @@ -72,7 +72,7 @@ func (m *UDPMuxDefault) Type() string { return "HOST" } -func (m *UDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error { +func (m *UDPMuxDefault) HandleSTUNMessage(msg *stun.Message, addr net.Addr) error { udpAddr, ok := addr.(*net.UDPAddr) if !ok { @@ -89,45 +89,32 @@ func (m *UDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error { } m.addressMapMu.Unlock() - // If we haven't seen this address before but is a STUN packet lookup by ufrag - if stun.IsMessage(p[:20]) { - // This block is needed to discover Peer Reflexive Candidates for which we don't know the Endpoint upfront. - // However, we can take a username attribute from the STUN message which contains ufrag. - // We can use ufrag to identify the destination conn to route packet to. - msg := &stun.Message{ - Raw: append([]byte{}, p[:n]...), - } + // This block is needed to discover Peer Reflexive Candidates for which we don't know the Endpoint upfront. + // However, we can take a username attribute from the STUN message which contains ufrag. + // We can use ufrag to identify the destination conn to route packet to. - if err := msg.Decode(); err != nil { - log.Warnf("Failed to handle decode ICE from %s: %v\n", addr.String(), err) - return err - } + attr, stunAttrErr := msg.Get(stun.AttrUsername) + if stunAttrErr == nil { + ufrag := strings.Split(string(attr), ":")[0] - attr, stunAttrErr := msg.Get(stun.AttrUsername) - if stunAttrErr == nil { - ufrag := strings.Split(string(attr), ":")[0] - - m.mu.Lock() - if destinationConn, ok := m.conns[ufrag]; ok { - exists := false - for _, conn := range destinationConnList { - if conn.params.Key == destinationConn.params.Key { - exists = true - break - } - } - if !exists { - destinationConnList = append(destinationConnList, destinationConn) + m.mu.Lock() + if destinationConn, ok := m.conns[ufrag]; ok { + exists := false + for _, conn := range destinationConnList { + if conn.params.Key == destinationConn.params.Key { + exists = true + break } } - m.mu.Unlock() - } else { - //log.Warnf("No Username attribute in STUN message from %s\n", addr.String()) + if !exists { + destinationConnList = append(destinationConnList, destinationConn) + } } + m.mu.Unlock() } for _, conn := range destinationConnList { - if err := conn.writePacket(p[:n], udpAddr); err != nil { + if err := conn.writePacket(msg.Raw, udpAddr); err != nil { log.Errorf("could not write packet: %v", err) } } diff --git a/iface/udp_mux_universal.go b/iface/udp_mux_universal.go index f24f66253..f9c51bb9d 100644 --- a/iface/udp_mux_universal.go +++ b/iface/udp_mux_universal.go @@ -79,34 +79,23 @@ func (m *UniversalUDPMuxDefault) Type() string { return "SRFLX" } -func (m *UniversalUDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error { - if stun.IsMessage(p[:20]) { - msg := &stun.Message{ - Raw: append([]byte{}, p[:n]...), - } +func (m *UniversalUDPMuxDefault) HandleSTUNMessage(msg *stun.Message, addr net.Addr) error { - if err := msg.Decode(); err != nil { - log.Warnf("Failed to handle decode ICE from %s: %v\n", addr.String(), err) - // todo proper error - return nil - } - - udpAddr, ok := addr.(*net.UDPAddr) - if !ok { - // message about this err will be logged in the UDPMux - return nil - } - - if m.isXORMappedResponse(msg, udpAddr.String()) { - err := m.handleXORMappedResponse(udpAddr, msg) - if err != nil { - log.Debugf("%w: %v", errors.New("failed to get XOR-MAPPED-ADDRESS response"), err) - return nil - } - return nil - } + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + // message about this err will be logged in the UDPMux + return nil } - return m.UDPMuxDefault.HandlePacket(p, n, addr) + + if m.isXORMappedResponse(msg, udpAddr.String()) { + err := m.handleXORMappedResponse(udpAddr, msg) + if err != nil { + log.Debugf("%w: %v", errors.New("failed to get XOR-MAPPED-ADDRESS response"), err) + return nil + } + return nil + } + return m.UDPMuxDefault.HandleSTUNMessage(msg, addr) } // isXORMappedResponse indicates whether the message is a XORMappedAddress and is coming from the known STUN server.