1 Star 0 Fork 0

Finn/go-sip-ua

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
client.go 5.88 KB
一键复制 编辑 原始数据 按行查看 历史
Dmitry Kovalev 提交于 2022-12-23 22:28 +08:00 . wrong auth method choice fixed
package auth
import (
"fmt"
"regexp"
"strings"
"github.com/ghettovoice/gosip/sip"
)
// currently only Digest and MD5
type Authorization struct {
realm string
qop string
nonce string
cnonce string
opaque string
algorithm string
username string
password string
uri string
response string
method string
stale string
nc int
ncHex string
domain string
other map[string]string
}
func AuthFromValue(value string) *Authorization {
auth := &Authorization{
algorithm: "MD5",
cnonce: generateNonce(12),
opaque: "",
stale: "",
qop: "",
nc: 0,
ncHex: "00000000",
domain: "",
other: make(map[string]string),
}
re := regexp.MustCompile(`([\w]+)=("([^"]+)"|([\w]+))`)
matches := re.FindAllStringSubmatch(value, -1)
for _, match := range matches {
value2 := strings.Replace(match[2], "\"", "", -1)
switch match[1] {
case "qop":
auth.qop = strings.Split(value2, ",")[0]
case "realm":
auth.realm = value2
case "algorithm":
auth.algorithm = value2
case "opaque":
auth.opaque = value2
case "nonce":
auth.nonce = value2
case "stale":
auth.stale = value2
case "domain":
auth.domain = value2
default:
auth.other[match[1]] = value2
}
}
return auth
}
func (auth *Authorization) SetUsername(username string) *Authorization {
auth.username = username
return auth
}
func (auth *Authorization) SetUri(uri string) *Authorization {
auth.uri = uri
return auth
}
func (auth *Authorization) SetMethod(method string) *Authorization {
auth.method = method
return auth
}
func (auth *Authorization) SetPassword(password string) *Authorization {
auth.password = password
return auth
}
// calculates Authorization response https://www.ietf.org/rfc/rfc2617.txt
func (auth *Authorization) CalcResponse(request sip.Request) *Authorization {
auth.nc += 1
hex := fmt.Sprintf("%x", auth.nc)
ncHex := "00000000"
auth.ncHex = ncHex[:len(ncHex)-len(hex)] + hex
// Nc-value = 8LHEX. Max value = 'FFFFFFFF'.
if temp_nc := int64(auth.nc); temp_nc == 4294967296 {
auth.nc = 1
auth.ncHex = "00000001"
}
// HA1 = MD5(A1) = MD5(username:realm:password).
ha1 := md5Hex(auth.username + ":" + auth.realm + ":" + auth.password)
if auth.qop == "auth" {
// HA2 = MD5(A2) = MD5(method:digestURI).
ha2 := md5Hex(auth.method + ":" + auth.uri)
// Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).
auth.response = md5Hex(ha1 + ":" + auth.nonce + ":" + auth.ncHex + ":" + auth.cnonce + ":auth:" + ha2)
} else if auth.qop == "auth-int" {
// HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody)).
ha2 := md5Hex(auth.method + ":" + auth.uri + ":" + md5Hex(request.Body()))
// Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).
auth.response = md5Hex(ha1 + ":" + auth.nonce + ":" + auth.ncHex + ":" + auth.cnonce + ":auth-int:" + ha2)
} else {
// HA2 = MD5(A2) = MD5(method:digestURI).
ha2 := md5Hex(auth.method + ":" + auth.uri)
// Response = MD5(HA1:nonce:HA2).
auth.response = md5Hex(ha1 + ":" + auth.nonce + ":" + ha2)
}
return auth
}
func (auth *Authorization) String() string {
digest := fmt.Sprintf(
`Digest realm="%s",algorithm=%s,nonce="%s",username="%s",uri="%s",response="%s"`,
auth.realm,
auth.algorithm,
auth.nonce,
auth.username,
auth.uri,
auth.response,
)
if auth.domain != "" {
digest += fmt.Sprintf(`,domain="%s"`, auth.domain)
}
if auth.opaque != "" {
digest += fmt.Sprintf(`,opaque="%s"`, auth.opaque)
}
if auth.qop != "" {
digest += fmt.Sprintf(`,qop="%s"`, auth.qop)
digest += fmt.Sprintf(`,cnonce="%s"`, auth.cnonce)
digest += fmt.Sprintf(`,nc="%s"`, auth.ncHex)
}
if len(auth.stale) > 0 {
digest += fmt.Sprintf(`,stale=%s`, auth.stale)
}
return digest
}
func AuthorizeRequest(request sip.Request, response sip.Response, user, password sip.MaybeString) error {
if user == nil {
return fmt.Errorf("authorize request: user is nil")
}
var authenticateHeaderName, authorizeHeaderName string
if response.StatusCode() == 401 {
// on 401 Unauthorized increase request seq num, add Authorization header and send once again
authenticateHeaderName = "WWW-Authenticate"
authorizeHeaderName = "Authorization"
} else {
// 407 Proxy authentication
authenticateHeaderName = "Proxy-Authenticate"
authorizeHeaderName = "Proxy-Authorization"
}
if hdrs := response.GetHeaders(authenticateHeaderName); len(hdrs) > 0 {
authenticateHeader := hdrs[0].(*sip.GenericHeader)
auth := AuthFromValue(authenticateHeader.Contents).
SetMethod(string(request.Method())).
SetUri(request.Recipient().String()).
SetUsername(user.String())
if password != nil {
auth.SetPassword(password.String())
}
auth.CalcResponse(request)
var authorizationHeader *sip.GenericHeader
hdrs = request.GetHeaders(authorizeHeaderName)
if len(hdrs) > 0 {
authorizationHeader = hdrs[0].(*sip.GenericHeader)
authorizationHeader.Contents = auth.String()
} else {
authorizationHeader = &sip.GenericHeader{
HeaderName: authorizeHeaderName,
Contents: auth.String(),
}
request.AppendHeader(authorizationHeader)
}
} else {
return fmt.Errorf("authorize request: header '%s' not found in response", authenticateHeaderName)
}
if viaHop, ok := request.ViaHop(); ok {
viaHop.Params.Add("branch", sip.String{Str: sip.GenerateBranch()})
}
if cseq, ok := request.CSeq(); ok {
cseq.SeqNo++
}
return nil
}
type Authorizer interface {
AuthorizeRequest(request sip.Request, response sip.Response) error
}
type ClientAuthorizer struct {
user sip.MaybeString
password sip.MaybeString
}
func NewClientAuthorizer(u string, p string) *ClientAuthorizer {
auth := &ClientAuthorizer{
user: sip.String{Str: u},
password: sip.String{Str: p},
}
return auth
}
func (auth *ClientAuthorizer) AuthorizeRequest(request sip.Request, response sip.Response) error {
return AuthorizeRequest(request, response, auth.user, auth.password)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/finntenzor/go-sip-ua.git
git@gitee.com:finntenzor/go-sip-ua.git
finntenzor
go-sip-ua
go-sip-ua
d08fa44809d1

搜索帮助