summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/turn/v2/internal/server/util.go
diff options
context:
space:
mode:
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.go133
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
+}