mirror of
				https://github.com/aler9/gortsplib
				synced 2025-11-01 02:52:36 +08:00 
			
		
		
		
	 f283abc2e7
			
		
	
	f283abc2e7
	
	
	
		
			
			(https://github.com/bluenviron/mediamtx/issues/3116) This fixes authentication issues with some TP-LINK cameras.
		
			
				
	
	
		
			187 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package headers contains various RTSP headers.
 | |
| package headers
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/bluenviron/gortsplib/v4/pkg/base"
 | |
| )
 | |
| 
 | |
| // AuthMethod is an authentication method.
 | |
| type AuthMethod int
 | |
| 
 | |
| // authentication methods.
 | |
| const (
 | |
| 	AuthMethodBasic AuthMethod = iota
 | |
| 	AuthMethodDigest
 | |
| )
 | |
| 
 | |
| // AuthAlgorithm is a digest algorithm.
 | |
| type AuthAlgorithm int
 | |
| 
 | |
| // digest algorithms.
 | |
| const (
 | |
| 	AuthAlgorithmMD5 AuthAlgorithm = iota
 | |
| 	AuthAlgorithmSHA256
 | |
| )
 | |
| 
 | |
| func parseAuthAlgorithm(v string) (AuthAlgorithm, error) {
 | |
| 	switch {
 | |
| 	case strings.ToLower(v) == "md5":
 | |
| 		return AuthAlgorithmMD5, nil
 | |
| 
 | |
| 	case strings.ToLower(v) == "sha-256":
 | |
| 		return AuthAlgorithmSHA256, nil
 | |
| 
 | |
| 	default:
 | |
| 		return 0, fmt.Errorf("unrecognized algorithm: %v", v)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Authenticate is a WWW-Authenticate header.
 | |
| type Authenticate struct {
 | |
| 	// authentication method
 | |
| 	Method AuthMethod
 | |
| 
 | |
| 	// realm
 | |
| 	Realm string
 | |
| 
 | |
| 	//
 | |
| 	// Digest authentication fields
 | |
| 	//
 | |
| 
 | |
| 	// nonce
 | |
| 	Nonce string
 | |
| 
 | |
| 	// opaque
 | |
| 	Opaque *string
 | |
| 
 | |
| 	// stale
 | |
| 	Stale *string
 | |
| 
 | |
| 	// algorithm
 | |
| 	Algorithm *AuthAlgorithm
 | |
| }
 | |
| 
 | |
| // Unmarshal decodes a WWW-Authenticate header.
 | |
| func (h *Authenticate) Unmarshal(v base.HeaderValue) error {
 | |
| 	if len(v) == 0 {
 | |
| 		return fmt.Errorf("value not provided")
 | |
| 	}
 | |
| 
 | |
| 	if len(v) > 1 {
 | |
| 		return fmt.Errorf("value provided multiple times (%v)", v)
 | |
| 	}
 | |
| 
 | |
| 	v0 := v[0]
 | |
| 
 | |
| 	i := strings.IndexByte(v0, ' ')
 | |
| 	if i < 0 {
 | |
| 		return fmt.Errorf("unable to split between method and keys (%v)", v0)
 | |
| 	}
 | |
| 	method, v0 := v0[:i], v0[i+1:]
 | |
| 
 | |
| 	switch method {
 | |
| 	case "Basic":
 | |
| 		h.Method = AuthMethodBasic
 | |
| 
 | |
| 	case "Digest":
 | |
| 		h.Method = AuthMethodDigest
 | |
| 
 | |
| 	default:
 | |
| 		return fmt.Errorf("invalid method (%s)", method)
 | |
| 	}
 | |
| 
 | |
| 	if h.Method == AuthMethodBasic {
 | |
| 		kvs, err := keyValParse(v0, ',')
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		realmReceived := false
 | |
| 
 | |
| 		for k, rv := range kvs {
 | |
| 			v := rv
 | |
| 
 | |
| 			if k == "realm" {
 | |
| 				h.Realm = v
 | |
| 				realmReceived = true
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !realmReceived {
 | |
| 			return fmt.Errorf("realm is missing")
 | |
| 		}
 | |
| 	} else { // digest
 | |
| 		kvs, err := keyValParse(v0, ',')
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		realmReceived := false
 | |
| 		nonceReceived := false
 | |
| 
 | |
| 		for k, rv := range kvs {
 | |
| 			v := rv
 | |
| 
 | |
| 			switch k {
 | |
| 			case "realm":
 | |
| 				h.Realm = v
 | |
| 				realmReceived = true
 | |
| 
 | |
| 			case "nonce":
 | |
| 				h.Nonce = v
 | |
| 				nonceReceived = true
 | |
| 
 | |
| 			case "opaque":
 | |
| 				h.Opaque = &v
 | |
| 
 | |
| 			case "stale":
 | |
| 				h.Stale = &v
 | |
| 
 | |
| 			case "algorithm":
 | |
| 				a, err := parseAuthAlgorithm(v)
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				h.Algorithm = &a
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !realmReceived || !nonceReceived {
 | |
| 			return fmt.Errorf("one or more digest fields are missing")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Marshal encodes a WWW-Authenticate header.
 | |
| func (h Authenticate) Marshal() base.HeaderValue {
 | |
| 	if h.Method == AuthMethodBasic {
 | |
| 		return base.HeaderValue{"Basic " +
 | |
| 			"realm=\"" + h.Realm + "\""}
 | |
| 	}
 | |
| 
 | |
| 	ret := "Digest realm=\"" + h.Realm + "\", nonce=\"" + h.Nonce + "\""
 | |
| 
 | |
| 	if h.Opaque != nil {
 | |
| 		ret += ", opaque=\"" + *h.Opaque + "\""
 | |
| 	}
 | |
| 
 | |
| 	if h.Stale != nil {
 | |
| 		ret += ", stale=\"" + *h.Stale + "\""
 | |
| 	}
 | |
| 
 | |
| 	if h.Algorithm != nil {
 | |
| 		if *h.Algorithm == AuthAlgorithmMD5 {
 | |
| 			ret += ", algorithm=\"MD5\""
 | |
| 		} else {
 | |
| 			ret += ", algorithm=\"SHA-256\""
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return base.HeaderValue{ret}
 | |
| }
 |