mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-26 20:31:11 +08:00
Compare commits
6 Commits
788afb7189
...
3b976c6812
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b976c6812 | ||
![]() |
40269328fb | ||
![]() |
45cbbaf1cf | ||
![]() |
3f542a642c | ||
![]() |
8b4df5f02c | ||
![]() |
4577390130 |
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/AlexxIT/go2rtc/internal/api"
|
||||
"github.com/AlexxIT/go2rtc/internal/app"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
@@ -132,7 +133,8 @@ func apiWS(w http.ResponseWriter, r *http.Request) {
|
||||
if handler := wsHandlers[msg.Type]; handler != nil {
|
||||
go func() {
|
||||
if err = handler(tr, msg); err != nil {
|
||||
tr.Write(&Message{Type: "error", Value: msg.Type + ": " + err.Error()})
|
||||
errMsg := core.StripUserinfo(err.Error())
|
||||
tr.Write(&Message{Type: "error", Value: msg.Type + ": " + errMsg})
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/shell"
|
||||
"github.com/AlexxIT/go2rtc/pkg/yaml"
|
||||
@@ -18,11 +19,16 @@ func LoadConfig(v any) {
|
||||
}
|
||||
}
|
||||
|
||||
var configMu sync.Mutex
|
||||
|
||||
func PatchConfig(path []string, value any) error {
|
||||
if ConfigPath == "" {
|
||||
return errors.New("config file disabled")
|
||||
}
|
||||
|
||||
configMu.Lock()
|
||||
defer configMu.Unlock()
|
||||
|
||||
// empty config is OK
|
||||
b, _ := os.ReadFile(ConfigPath)
|
||||
|
||||
|
@@ -118,3 +118,17 @@ func TestName(t *testing.T) {
|
||||
// stage3
|
||||
_ = prod2.Stop()
|
||||
}
|
||||
|
||||
func TestStripUserinfo(t *testing.T) {
|
||||
s := `streams:
|
||||
test:
|
||||
- ffmpeg:rtsp://username:password@10.1.2.3:554/stream1
|
||||
- ffmpeg:rtsp://10.1.2.3:554/stream1@#video=copy
|
||||
`
|
||||
s = StripUserinfo(s)
|
||||
require.Equal(t, `streams:
|
||||
test:
|
||||
- ffmpeg:rtsp://***@10.1.2.3:554/stream1
|
||||
- ffmpeg:rtsp://10.1.2.3:554/stream1@#video=copy
|
||||
`, s)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -77,3 +78,14 @@ func Caller() string {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
return file + ":" + strconv.Itoa(line)
|
||||
}
|
||||
|
||||
const (
|
||||
unreserved = `A-Za-z0-9-._~`
|
||||
subdelims = `!$&'()*+,;=`
|
||||
userinfo = unreserved + subdelims + `%:`
|
||||
)
|
||||
|
||||
func StripUserinfo(s string) string {
|
||||
sanitizer := regexp.MustCompile(`://[` + userinfo + `]+@`)
|
||||
return sanitizer.ReplaceAllString(s, `://***@`)
|
||||
}
|
||||
|
@@ -71,11 +71,17 @@ type JSONCharacter struct {
|
||||
Event any `json:"ev,omitempty"`
|
||||
}
|
||||
|
||||
// 4.2.1.2 Invalid Setup Codes
|
||||
const insecurePINs = "00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999 12345678 87654321"
|
||||
|
||||
func SanitizePin(pin string) (string, error) {
|
||||
s := strings.ReplaceAll(pin, "-", "")
|
||||
if len(s) != 8 {
|
||||
return "", errors.New("hap: wrong PIN format: " + pin)
|
||||
}
|
||||
if strings.Contains(insecurePINs, s) {
|
||||
return "", errors.New("hap: insecure PIN: " + pin)
|
||||
}
|
||||
// 123-45-678
|
||||
return s[:3] + "-" + s[3:5] + "-" + s[5:], nil
|
||||
}
|
||||
|
@@ -46,6 +46,8 @@ func Marshal(v any) ([]byte, error) {
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return appendSlice(nil, value)
|
||||
case reflect.Struct:
|
||||
return appendStruct(nil, value)
|
||||
}
|
||||
@@ -53,6 +55,23 @@ func Marshal(v any) ([]byte, error) {
|
||||
return nil, errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
// separator the most confusing meaning in the documentation.
|
||||
// It can have a value of 0x00 or 0xFF or even 0x05.
|
||||
const separator = 0xFF
|
||||
|
||||
func appendSlice(b []byte, value reflect.Value) ([]byte, error) {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
var err error
|
||||
if b, err = appendStruct(b, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendStruct(b []byte, value reflect.Value) ([]byte, error) {
|
||||
valueType := value.Type()
|
||||
|
||||
@@ -121,7 +140,7 @@ func appendValue(b []byte, tag byte, value reflect.Value) ([]byte, error) {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, 0, 0)
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
if b, err = appendValue(b, tag, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
@@ -179,64 +198,86 @@ func Unmarshal(data []byte, v any) error {
|
||||
kind = value.Kind()
|
||||
}
|
||||
|
||||
if kind != reflect.Struct {
|
||||
return errors.New("tlv8: not implemented: " + kind.String())
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return unmarshalSlice(data, value)
|
||||
case reflect.Struct:
|
||||
return unmarshalStruct(data, value)
|
||||
}
|
||||
|
||||
return unmarshalStruct(data, value)
|
||||
return errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
var waitSlice bool
|
||||
// unmarshalTLV can return two types of errors:
|
||||
// - critical and then the value of []byte will be nil
|
||||
// - not critical and then []byte will contain the value
|
||||
func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
|
||||
if len(b) < 2 {
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
for len(b) >= 2 {
|
||||
t := b[0]
|
||||
l := int(b[1])
|
||||
t := b[0]
|
||||
l := int(b[1])
|
||||
|
||||
// array item divider
|
||||
if t == 0 && l == 0 {
|
||||
b = b[2:]
|
||||
waitSlice = true
|
||||
continue
|
||||
// array item divider (t == 0x00 || t == 0xFF)
|
||||
if l == 0 {
|
||||
return b[2:], errors.New("tlv8: zero item")
|
||||
}
|
||||
|
||||
var v []byte
|
||||
|
||||
for {
|
||||
if len(b) < 2+l {
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
var v []byte
|
||||
v = append(v, b[2:2+l]...)
|
||||
b = b[2+l:]
|
||||
|
||||
for {
|
||||
if len(b) < 2+l {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
// if size == 255 and same tag - continue read big payload
|
||||
if l < 255 || len(b) < 2 || b[0] != t {
|
||||
break
|
||||
}
|
||||
|
||||
l = int(b[1])
|
||||
}
|
||||
|
||||
tag := strconv.Itoa(int(t))
|
||||
|
||||
valueField, ok := getStructField(value, tag)
|
||||
if !ok {
|
||||
return b, fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
|
||||
if err := unmarshalValue(v, valueField); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func unmarshalSlice(b []byte, value reflect.Value) error {
|
||||
valueIndex := value.Index(growSlice(value))
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, valueIndex); err != nil {
|
||||
if b != nil {
|
||||
valueIndex = value.Index(growSlice(value))
|
||||
continue
|
||||
}
|
||||
|
||||
v = append(v, b[2:2+l]...)
|
||||
b = b[2+l:]
|
||||
|
||||
// if size == 255 and same tag - continue read big payload
|
||||
if l < 255 || len(b) < 2 || b[0] != t {
|
||||
break
|
||||
}
|
||||
|
||||
l = int(b[1])
|
||||
}
|
||||
|
||||
tag := strconv.Itoa(int(t))
|
||||
|
||||
valueField, ok := getStructField(value, tag)
|
||||
if !ok {
|
||||
return fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
|
||||
if waitSlice {
|
||||
if valueField.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("tlv8: should be slice T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
waitSlice = false
|
||||
}
|
||||
|
||||
if err := unmarshalValue(v, valueField); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, value); b == nil && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package tlv8
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -107,3 +108,49 @@ func TestInterface(t *testing.T) {
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
}
|
||||
|
||||
func TestSlice1(t *testing.T) {
|
||||
var v struct {
|
||||
VideoAttrs []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
} `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `030b010280070202380403011e ff00 030b010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v.VideoAttrs, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestSlice2(t *testing.T) {
|
||||
var v []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `010280070202380403011e ff00 010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
|
Reference in New Issue
Block a user