Skip to content

Commit

Permalink
Add access restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
macbookpro committed Jan 12, 2019
1 parent 99fa3f7 commit ae5d79e
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 28 deletions.
5 changes: 4 additions & 1 deletion easydarwin.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ timeout=28800
; 是否使能gop cache。如果使能,服务器会缓存最后一个I帧以及其后的非I帧,以提高播放速度。但是可能在高并发的情况下带来内存压力。
gop_cache_enable=1

; 是否使能向服务器推流或者从服务器播放时验证用户名密码
authorization_enable=0

; 是否使能推送的同事进行本地存储,使能后则可以进行录像查询与回放。
save_stream_to_local=1
save_stream_to_local=0

;easydarwin使用ffmpeg工具来进行存储。这里表示ffmpeg的可执行程序的路径
ffmpeg_path=/Users/ze/Downloads/ffmpeg-20180719-9cb3d8f-macos64-shared/bin/ffmpeg
Expand Down
144 changes: 117 additions & 27 deletions rtsp/rtsp-session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rtsp
import (
"bufio"
"bytes"
"crypto/md5"
"encoding/binary"
"fmt"
"io"
Expand All @@ -16,6 +17,8 @@ import (
"sync"
"time"

"github.com/EasyDarwin/EasyDarwin/models"
"github.com/penggy/EasyGoLib/db"
"github.com/penggy/EasyGoLib/utils"

"github.com/teris-io/shortid"
Expand Down Expand Up @@ -99,6 +102,9 @@ type Session struct {
SDPRaw string
SDPMap map[string]*SDPInfo

authorizationEnable bool
nonce string

AControl string
VControl string
ACodec string
Expand Down Expand Up @@ -133,17 +139,17 @@ func NewSession(server *Server, conn net.Conn) *Session {
networkBuffer := utils.Conf().Section("rtsp").Key("network_buffer").MustInt(204800)
timeoutMillis := utils.Conf().Section("rtsp").Key("timeout").MustInt(0)
timeoutTCPConn := &RichConn{conn, time.Duration(timeoutMillis) * time.Millisecond}

authorizationEnable := utils.Conf().Section("rtsp").Key("authorization_enable").MustInt(0)
session := &Session{
ID: shortid.MustGenerate(),
Server: server,
Conn: timeoutTCPConn,
connRW: bufio.NewReadWriter(bufio.NewReaderSize(timeoutTCPConn, networkBuffer), bufio.NewWriterSize(timeoutTCPConn, networkBuffer)),
StartAt: time.Now(),
Timeout: utils.Conf().Section("rtsp").Key("timeout").MustInt(0),

RTPHandles: make([]func(*RTPPack), 0),
StopHandles: make([]func(), 0),
ID: shortid.MustGenerate(),
Server: server,
Conn: timeoutTCPConn,
connRW: bufio.NewReadWriter(bufio.NewReaderSize(timeoutTCPConn, networkBuffer), bufio.NewWriterSize(timeoutTCPConn, networkBuffer)),
StartAt: time.Now(),
Timeout: utils.Conf().Section("rtsp").Key("timeout").MustInt(0),
authorizationEnable: authorizationEnable != 0,
RTPHandles: make([]func(*RTPPack), 0),
StopHandles: make([]func(), 0),
}

session.logger = log.New(os.Stdout, fmt.Sprintf("[%s]", session.ID), log.LstdFlags|log.Lshortfile)
Expand Down Expand Up @@ -273,6 +279,68 @@ func (session *Session) Start() {
}
}

func CheckAuth(authLine string, method string, sessionNonce string) error {
realmRex := regexp.MustCompile(`realm="(.*?)"`)
nonceRex := regexp.MustCompile(`nonce="(.*?)"`)
usernameRex := regexp.MustCompile(`username="(.*?)"`)
responseRex := regexp.MustCompile(`response="(.*?)"`)
uriRex := regexp.MustCompile(`uri="(.*?)"`)

realm := ""
nonce := ""
username := ""
response := ""
uri := ""
result1 := realmRex.FindStringSubmatch(authLine)
if len(result1) == 2 {
realm = result1[1]
} else {
return fmt.Errorf("CheckAuth error : no realm found")
}
result1 = nonceRex.FindStringSubmatch(authLine)
if len(result1) == 2 {
nonce = result1[1]
} else {
return fmt.Errorf("CheckAuth error : no nonce found")
}
if sessionNonce != nonce {
return fmt.Errorf("CheckAuth error : sessionNonce not same as nonce")
}

result1 = usernameRex.FindStringSubmatch(authLine)
if len(result1) == 2 {
username = result1[1]
} else {
return fmt.Errorf("CheckAuth error : username not found")
}

result1 = responseRex.FindStringSubmatch(authLine)
if len(result1) == 2 {
response = result1[1]
} else {
return fmt.Errorf("CheckAuth error : response not found")
}

result1 = uriRex.FindStringSubmatch(authLine)
if len(result1) == 2 {
uri = result1[1]
} else {
return fmt.Errorf("CheckAuth error : uri not found")
}
var user models.User
err := db.SQLite.Where("Username = ?", username).First(&user).Error
if err != nil {
return fmt.Errorf("CheckAuth error : user not exists")
}
md5UserRealmPwd := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", username, realm, user.Password))))
md5MethodURL := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s", method, uri))))
myResponse := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", md5UserRealmPwd, nonce, md5MethodURL))))
if myResponse != response {
return fmt.Errorf("CheckAuth error : response not equal")
}
return nil
}

func (session *Session) handleRequest(req *Request) {
//if session.Timeout > 0 {
// session.Conn.SetDeadline(time.Now().Add(time.Duration(session.Timeout) * time.Second))
Expand Down Expand Up @@ -306,11 +374,33 @@ func (session *Session) handleRequest(req *Request) {
return
}
}
if res.StatusCode != 200 {
if res.StatusCode != 200 && res.StatusCode != 401 {
logger.Printf("Response request error[%d]. stop session.", res.StatusCode)
session.Stop()
}
}()
if req.Method != "OPTIONS" {
if session.authorizationEnable {
authLine := req.Header["Authorization"]
authFailed := true
if authLine != "" {
err := CheckAuth(authLine, req.Method, session.nonce)
if err == nil {
authFailed = false
} else {
logger.Printf("%v", err)
}
}
if authFailed {
res.StatusCode = 401
res.Status = "Unauthorized"
nonce := fmt.Sprintf("%x", md5.Sum([]byte(shortid.MustGenerate())))
session.nonce = nonce
res.Header["WWW-Authenticate"] = fmt.Sprintf(`Digest realm="EasyDarwin", nonce="%s", algorithm="MD5"`, nonce)
return
}
}
}
switch req.Method {
case "OPTIONS":
res.Header["Public"] = "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD"
Expand Down Expand Up @@ -393,23 +483,11 @@ func (session *Session) handleRequest(req *Request) {
}
setupPath := setupUrl.String()

// 播放器可能直接从SETUP来,不用DESCRIBE(比如可能事先已经获取过了)
// error status. SETUP without ANNOUNCE or DESCRIBE.
if session.Pusher == nil {
session.Path = setupUrl.Path
pusher := session.Server.GetPusher(session.Path)
if pusher == nil {
res.StatusCode = 404
res.Status = "NOT FOUND"
return
}
session.Type = SESSEION_TYPE_PLAYER
session.Player = NewPlayer(session, pusher)
session.Pusher = pusher
session.AControl = pusher.AControl()
session.VControl = pusher.VControl()
session.ACodec = pusher.ACodec()
session.VCodec = pusher.VCodec()
session.Conn.timeout = 0
res.StatusCode = 500
res.Status = "Error Status"
return
}
//setupPath = setupPath[strings.LastIndex(setupPath, "/")+1:]
vPath := ""
Expand Down Expand Up @@ -539,8 +617,20 @@ func (session *Session) handleRequest(req *Request) {
}
res.Header["Transport"] = ts
case "PLAY":
// error status. PLAY without ANNOUNCE or DESCRIBE.
if session.Pusher == nil {
res.StatusCode = 500
res.Status = "Error Status"
return
}
res.Header["Range"] = req.Header["Range"]
case "RECORD":
// error status. RECORD without ANNOUNCE or DESCRIBE.
if session.Pusher == nil {
res.StatusCode = 500
res.Status = "Error Status"
return
}
}
}

Expand Down

0 comments on commit ae5d79e

Please sign in to comment.