mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
feat: rtsp auth
This commit is contained in:
32
plugin/rtsp/BAD_DEVICE.md
Normal file
32
plugin/rtsp/BAD_DEVICE.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 不规范设备耻辱柱
|
||||
|
||||
## 宇视
|
||||
|
||||
### IPC2A3L-FW-APF60-V1-DT
|
||||
|
||||
sdp 中缺失必填字段 (正确格式c=IN IP4 0.0.0.0)
|
||||
|
||||
> https://tools.ietf.org/html/rfc4566#section-8.2.6
|
||||
|
||||
```sdp
|
||||
v=0
|
||||
o=- 1001 1 IN
|
||||
s=VCP IPC Realtime stream
|
||||
m=video 0 RTP/AVP 105
|
||||
c=IN
|
||||
a=control:rtsp://192.168.0.101/media/video1/video
|
||||
a=rtpmap:105 H264/90000
|
||||
a=fmtp:105 profile-level-id=640032; packetization-mode=1; sprop-parameter-sets=Z2QAMqw7UBIAUdCAAAH0AABhqEI=,aO48sA==
|
||||
a=recvonly
|
||||
m=audio 0 RTP/AVP 0
|
||||
c=IN
|
||||
a=fmtp:0 RTCP=0
|
||||
a=control:rtsp://192.168.0.101/media/video1/audio1
|
||||
a=recvonly
|
||||
m=application 0 RTP/AVP 107
|
||||
c=IN
|
||||
a=control:rtsp://192.168.0.101/media/video1/metadata
|
||||
a=rtpmap:107 vnd.onvif.metadata/90000
|
||||
a=fmtp:107 DecoderTag=h3c-v3 RTCP=0
|
||||
a=recvonly
|
||||
```
|
||||
@@ -24,6 +24,8 @@ var _ = m7s.InstallPlugin[RTSPPlugin](m7s.PluginMeta{
|
||||
|
||||
type RTSPPlugin struct {
|
||||
m7s.Plugin
|
||||
UserName string `desc:"用户名"`
|
||||
Password string `desc:"密码"`
|
||||
UdpPort util.Range[uint16] `default:"20001-30000" desc:"媒体端口范围"` //媒体端口范围
|
||||
udpPorts chan uint16
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ type NetConnection struct {
|
||||
|
||||
// internal
|
||||
|
||||
auth *util.Auth
|
||||
Auth *util.Auth
|
||||
Conn net.Conn
|
||||
keepalive int
|
||||
sequence int
|
||||
@@ -142,10 +142,11 @@ func (c *NetConnection) Connect(remoteURL string) (err error) {
|
||||
}
|
||||
c.Conn = conn
|
||||
c.BufReader = util.NewBufReader(conn)
|
||||
c.URL = rtspURL
|
||||
c.UserAgent = "monibuca" + m7s.Version
|
||||
c.Session = ""
|
||||
c.auth = util.NewAuth(c.URL.User)
|
||||
c.Auth = util.NewAuth(rtspURL.User)
|
||||
c.URL = rtspURL
|
||||
c.URL.User = nil
|
||||
c.SetDescription("remoteAddr", conn.RemoteAddr().String())
|
||||
c.MemoryAllocator = util.NewScalableMemoryAllocator(1 << 12)
|
||||
// c.Backchannel = true
|
||||
@@ -166,7 +167,7 @@ func (c *NetConnection) WriteRequest(req *util.Request) (err error) {
|
||||
// https://github.com/AlexxIT/go2rtc/issues/7
|
||||
req.Header["CSeq"] = []string{strconv.Itoa(c.sequence)}
|
||||
|
||||
c.auth.Write(req)
|
||||
c.Auth.Write(req)
|
||||
|
||||
if c.Session != "" {
|
||||
req.Header.Set("Session", c.Session)
|
||||
|
||||
@@ -30,14 +30,14 @@ func (c *Stream) Do(req *util.Request) (*util.Response, error) {
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
case http.StatusUnauthorized:
|
||||
switch c.auth.Method {
|
||||
switch c.Auth.Method {
|
||||
case util.AuthNone:
|
||||
if c.auth.ReadNone(res) {
|
||||
if c.Auth.ReadNone(res) {
|
||||
return c.Do(req)
|
||||
}
|
||||
return nil, errors.New("user/pass not provided")
|
||||
case util.AuthUnknown:
|
||||
if c.auth.Read(res) {
|
||||
if c.Auth.Read(res) {
|
||||
return c.Do(req)
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -23,7 +23,13 @@ o=- 0 0 IN IP4 0.0.0.0
|
||||
s=-
|
||||
t=0 0`
|
||||
|
||||
var reIN = regexp.MustCompile(`IN\s*\r\n`)
|
||||
|
||||
func UnmarshalSDP(rawSDP []byte) ([]*Media, error) {
|
||||
|
||||
// Fix IN followed by only spaces and newlines
|
||||
rawSDP = reIN.ReplaceAll(rawSDP, []byte("IN IP4 0.0.0.0\r\n"))
|
||||
|
||||
sd := &sdp.SessionDescription{}
|
||||
if err := sd.Unmarshal(rawSDP); err != nil {
|
||||
// fix multiple `s=` https://github.com/AlexxIT/WebRTC/issues/417
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,27 +19,19 @@ type RTSPServer struct {
|
||||
conf *RTSPPlugin
|
||||
}
|
||||
|
||||
func (task *RTSPServer) Start() error {
|
||||
if task.conf.UserName != "" && task.conf.Password != "" {
|
||||
task.Auth = util.NewAuth(url.UserPassword(task.conf.UserName, task.conf.Password))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (task *RTSPServer) Go() (err error) {
|
||||
var receiver *Receiver
|
||||
var sender *Sender
|
||||
var req *util.Request
|
||||
var sendMode bool
|
||||
|
||||
// 添加延迟函数在方法结束时清理资源
|
||||
defer func() {
|
||||
if receiver != nil {
|
||||
receiver.Dispose()
|
||||
}
|
||||
if sender != nil {
|
||||
sender.Dispose()
|
||||
}
|
||||
// 确保任何残留资源被清理
|
||||
if task.NetConnection != nil {
|
||||
task.NetConnection.Dispose()
|
||||
}
|
||||
task.Info("RTSP connection closed and resources cleaned up")
|
||||
}()
|
||||
|
||||
for {
|
||||
req, err = task.ReadRequest()
|
||||
if err != nil {
|
||||
@@ -50,20 +43,21 @@ func (task *RTSPServer) Go() (err error) {
|
||||
task.Logger = task.Logger.With("url", task.URL.String())
|
||||
task.UserAgent = req.Header.Get("User-Agent")
|
||||
task.Info("connect", "userAgent", task.UserAgent)
|
||||
if task.Auth != nil && req.Method != MethodOptions {
|
||||
if !task.Auth.Validate(req) {
|
||||
task.WriteResponse(&util.Response{
|
||||
StatusCode: 401,
|
||||
Status: "Unauthorized",
|
||||
Header: textproto.MIMEHeader{
|
||||
"Www-Authenticate": {`Basic realm="monibuca"`},
|
||||
},
|
||||
Request: req,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if !c.auth.Validate(req) {
|
||||
// res := &tcp.Response{
|
||||
// Status: "401 Unauthorized",
|
||||
// Header: map[string][]string{"Www-Authenticate": {`Basic realm="go2rtc"`}},
|
||||
// Request: req,
|
||||
// }
|
||||
// if err = c.WriteResponse(res); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// continue
|
||||
//}
|
||||
|
||||
// Receiver: OPTIONS > DESCRIBE > SETUP... > PLAY > TEARDOWN
|
||||
// Sender: OPTIONS > ANNOUNCE > SETUP... > RECORD > TEARDOWN
|
||||
switch req.Method {
|
||||
@@ -91,6 +85,7 @@ func (task *RTSPServer) Go() (err error) {
|
||||
}
|
||||
|
||||
receiver = &Receiver{}
|
||||
task.Using(receiver.Dispose)
|
||||
receiver.NetConnection = task.NetConnection
|
||||
if receiver.Publisher, err = task.conf.Publish(task, strings.TrimPrefix(task.URL.Path, "/")); err != nil {
|
||||
receiver = nil
|
||||
@@ -111,6 +106,7 @@ func (task *RTSPServer) Go() (err error) {
|
||||
case MethodDescribe:
|
||||
sendMode = true
|
||||
sender = &Sender{}
|
||||
task.Using(sender.Dispose)
|
||||
sender.NetConnection = task.NetConnection
|
||||
rawQuery := req.URL.RawQuery
|
||||
streamPath := strings.TrimPrefix(task.URL.Path, "/")
|
||||
|
||||
Reference in New Issue
Block a user