Refactor UDP mux to handle STUN only messages

This commit is contained in:
braginini
2022-09-07 18:49:15 +02:00
parent 314f34f916
commit dded91235e
3 changed files with 53 additions and 83 deletions

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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.