diff options
Diffstat (limited to 'vendor/github.com/pion/turn/v2/internal/server/util.go')
-rw-r--r-- | vendor/github.com/pion/turn/v2/internal/server/util.go | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/vendor/github.com/pion/turn/v2/internal/server/util.go b/vendor/github.com/pion/turn/v2/internal/server/util.go new file mode 100644 index 0000000..c6baffb --- /dev/null +++ b/vendor/github.com/pion/turn/v2/internal/server/util.go @@ -0,0 +1,133 @@ +package server + +import ( + "crypto/md5" //nolint:gosec,gci + "fmt" + "io" + "math/rand" + "net" + "strconv" + "time" + + "github.com/pion/stun" + "github.com/pion/turn/v2/internal/proto" +) + +const ( + maximumAllocationLifetime = time.Hour // https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 seconds recommendation + nonceLifetime = time.Hour // https://tools.ietf.org/html/rfc5766#section-4 + +) + +func randSeq(n int) string { + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] //nolint:gosec + } + return string(b) +} + +func buildNonce() (string, error) { + /* #nosec */ + h := md5.New() + if _, err := io.WriteString(h, strconv.FormatInt(time.Now().Unix(), 10)); err != nil { + return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err) + } + if _, err := io.WriteString(h, strconv.FormatInt(rand.Int63(), 10)); err != nil { //nolint:gosec + return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err) + } + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error { + msg, err := stun.Build(attrs...) + if err != nil { + return err + } + _, err = conn.WriteTo(msg.Raw, dst) + return err +} + +// Send a STUN packet and return the original error to the caller +func buildAndSendErr(conn net.PacketConn, dst net.Addr, err error, attrs ...stun.Setter) error { + if sendErr := buildAndSend(conn, dst, attrs...); sendErr != nil { + err = fmt.Errorf("%w %v %v", errFailedToSendError, sendErr, err) + } + return err +} + +func buildMsg(transactionID [stun.TransactionIDSize]byte, msgType stun.MessageType, additional ...stun.Setter) []stun.Setter { + return append([]stun.Setter{&stun.Message{TransactionID: transactionID}, msgType}, additional...) +} + +func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method) (stun.MessageIntegrity, bool, error) { + respondWithNonce := func(responseCode stun.ErrorCode) (stun.MessageIntegrity, bool, error) { + nonce, err := buildNonce() + if err != nil { + return nil, false, err + } + + // Nonce has already been taken + if _, keyCollision := r.Nonces.LoadOrStore(nonce, time.Now()); keyCollision { + return nil, false, errDuplicatedNonce + } + + return nil, false, buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, + stun.NewType(callingMethod, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: responseCode}, + stun.NewNonce(nonce), + stun.NewRealm(r.Realm), + )...) + } + + if !m.Contains(stun.AttrMessageIntegrity) { + return respondWithNonce(stun.CodeUnauthorized) + } + + nonceAttr := &stun.Nonce{} + usernameAttr := &stun.Username{} + realmAttr := &stun.Realm{} + badRequestMsg := buildMsg(m.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}) + + if err := nonceAttr.GetFrom(m); err != nil { + return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + } + + // Assert Nonce exists and is not expired + nonceCreationTime, ok := r.Nonces.Load(string(*nonceAttr)) + if !ok || time.Since(nonceCreationTime.(time.Time)) >= nonceLifetime { + r.Nonces.Delete(nonceAttr) + return respondWithNonce(stun.CodeStaleNonce) + } + + if err := realmAttr.GetFrom(m); err != nil { + return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + } else if err := usernameAttr.GetFrom(m); err != nil { + return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + } + + ourKey, ok := r.AuthHandler(usernameAttr.String(), realmAttr.String(), r.SrcAddr) + if !ok { + return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), badRequestMsg...) + } + + if err := stun.MessageIntegrity(ourKey).Check(m); err != nil { + return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + } + + return stun.MessageIntegrity(ourKey), true, nil +} + +func allocationLifeTime(m *stun.Message) time.Duration { + lifetimeDuration := proto.DefaultLifetime + + var lifetime proto.Lifetime + if err := lifetime.GetFrom(m); err == nil { + if lifetime.Duration < maximumAllocationLifetime { + lifetimeDuration = lifetime.Duration + } + } + + return lifetimeDuration +} |