|
|
// Copyright 2020, Chef. All rights reserved.
|
|
|
// https://github.com/q191201771/lal
|
|
|
//
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
// that can be found in the License file.
|
|
|
//
|
|
|
// Author: Chef (191201771@qq.com)
|
|
|
|
|
|
package stun
|
|
|
|
|
|
import (
|
|
|
"crypto/rand"
|
|
|
"fmt"
|
|
|
|
|
|
"github.com/q191201771/naza/pkg/bele"
|
|
|
)
|
|
|
|
|
|
type Header struct {
|
|
|
Typ int
|
|
|
Length int
|
|
|
MagicCookie int
|
|
|
TransactionID []byte
|
|
|
}
|
|
|
|
|
|
func PackHeaderTo(out []byte, typ int, length int) error {
|
|
|
if len(out) < minStunMessageSize {
|
|
|
return ErrStun
|
|
|
}
|
|
|
bele.BEPutUint16(out, uint16(typ))
|
|
|
bele.BEPutUint16(out[2:], uint16(length))
|
|
|
bele.BEPutUint32(out[4:], uint32(magicCookieBE))
|
|
|
_, err := rand.Reader.Read(out[8:])
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
func PackBindingRequest() ([]byte, error) {
|
|
|
b := make([]byte, minStunMessageSize)
|
|
|
err := PackHeaderTo(b, typeBindingRequestBE, 0)
|
|
|
return b, err
|
|
|
}
|
|
|
|
|
|
func PackBindingResponse(ip []byte, port int) ([]byte, error) {
|
|
|
b := make([]byte, minStunMessageSize+4+attrTypeXORMappedAddressSize)
|
|
|
err := PackHeaderTo(b, typeBindSuccessResponseBE, 4+attrTypeXORMappedAddressSize)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
bele.BEPutUint16(b[20:], uint16(attrTypeXORMappedAddressBE))
|
|
|
bele.BEPutUint16(b[22:], attrTypeXORMappedAddressSize)
|
|
|
packAttrXORMappedAddressTo(b[24:], ip, port)
|
|
|
return b, nil
|
|
|
}
|
|
|
|
|
|
// @param out 输出参数,需保证len(b)>=8
|
|
|
//
|
|
|
// @return ip 4字节格式
|
|
|
func packAttrXORMappedAddressTo(out []byte, ip []byte, port int) {
|
|
|
bele.BEPutUint32(out, uint32(protocolFamilyIPv4BE))
|
|
|
bele.BEPutUint16(out[2:], uint16(port^(magicCookieBE>>16)))
|
|
|
xor(ip, magicCookie, out[4:])
|
|
|
return
|
|
|
}
|
|
|
|
|
|
//func unpackAttrXORMappedAddress(b []byte) (ip string, port int, err error) {
|
|
|
// if bytes.Compare(b[:2], protocolFamilyIPv4) != 0 {
|
|
|
// return "", 0, ErrStun
|
|
|
// }
|
|
|
//
|
|
|
// port = int(bele.BEUint16(b[2:])) ^ (magicCookieBE >> 16)
|
|
|
//
|
|
|
// ipb := make([]byte, 4)
|
|
|
// xor(b[4:], magicCookie, ipb)
|
|
|
// ip = fmt.Sprintf("%d.%d.%d.%d", ipb[0], ipb[1], ipb[2], ipb[3])
|
|
|
// return
|
|
|
//}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
func UnpackHeader(b []byte) (h Header, err error) {
|
|
|
if len(b) < minStunMessageSize {
|
|
|
return h, ErrStun
|
|
|
}
|
|
|
h.Typ = int(bele.BEUint16(b[:2]))
|
|
|
h.Length = int(bele.BEUint16(b[2:]))
|
|
|
h.MagicCookie = int(bele.BEUint32(b[4:]))
|
|
|
h.TransactionID = b[12:20]
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func UnpackResponseMessage(b []byte) (ip string, port int, err error) {
|
|
|
h, err := UnpackHeader(b)
|
|
|
if err != nil {
|
|
|
return "", 0, err
|
|
|
}
|
|
|
|
|
|
// TODO chef: only impled bind success response
|
|
|
if h.Typ != typeBindSuccessResponseBE {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
if h.MagicCookie != magicCookieBE {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
if len(b) < minStunMessageSize+h.Length {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
// attr list
|
|
|
pos := minStunMessageSize
|
|
|
for {
|
|
|
if len(b[pos:]) < 4 {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
at := int(bele.BEUint16(b[pos : pos+2]))
|
|
|
al := int(bele.BEUint16(b[pos+2 : pos+4]))
|
|
|
pos += 4
|
|
|
if len(b[pos:]) < al {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
if at == attrTypeXORMappedAddressBE || at == attrTypeXORMappedAddress2BE {
|
|
|
ip, port, err = unpackAttrXORMappedAddress(b[pos:])
|
|
|
if err != nil {
|
|
|
return "", 0, err
|
|
|
}
|
|
|
}
|
|
|
if at == attrTypeMappedAddressBE {
|
|
|
ip, port, err = unpackAttrMappedAddress(b[pos:])
|
|
|
if err != nil {
|
|
|
return "", 0, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
pos += al
|
|
|
if pos == minStunMessageSize+h.Length {
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return ip, port, nil
|
|
|
}
|
|
|
|
|
|
func unpackAttrXORMappedAddress(b []byte) (ip string, port int, err error) {
|
|
|
if int(bele.BEUint16(b[:2])) != protocolFamilyIPv4BE {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
port = int(bele.BEUint16(b[2:])) ^ (magicCookieBE >> 16)
|
|
|
|
|
|
ipb := make([]byte, 4)
|
|
|
xor(b[4:], magicCookie, ipb)
|
|
|
ip = fmt.Sprintf("%d.%d.%d.%d", ipb[0], ipb[1], ipb[2], ipb[3])
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func unpackAttrMappedAddress(b []byte) (ip string, port int, err error) {
|
|
|
if int(bele.BEUint16(b[:2])) != protocolFamilyIPv4BE {
|
|
|
return "", 0, ErrStun
|
|
|
}
|
|
|
|
|
|
port = int(bele.BEUint16(b[2:]))
|
|
|
ip = fmt.Sprintf("%d.%d.%d.%d", b[4], b[5], b[6], b[7])
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func xor(a, b, res []byte) {
|
|
|
n := len(a)
|
|
|
if n > len(b) {
|
|
|
n = len(b)
|
|
|
}
|
|
|
for i := 0; i < n; i++ {
|
|
|
res[i] = a[i] ^ b[i]
|
|
|
}
|
|
|
}
|