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