mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
For the DJI M30, there is a bug where empty NALU packets with a size of zero are causing issues with HLS streaming. This bug leads to random unpublish events due to the SRS disconnecting the connection for the HLS module when it fails to handle empty NALU packets. To address this bug, we have patched the system to ignore any empty NALU packets with a size of zero. Additionally, we have created a tool in the srs-bench to replay pcapng files captured by tcpdump or Wireshark. We have also added utest using mprotect and asan to detect any memory corruption. It is important to note that this bug has been fixed in versions 4.0.2716477f31004
and 5.0.170939f6b484b
. This patch specifically addresses the issue in SRS 6.0. Please be aware that there is another commit related to this bug that partially fixes the issue but still leaves a small problem for asan to detect memory corruption. This commit,577cd299e1
, only ignores empty NALU packets but still reads beyond the memory. --------- Co-authored-by: chundonglinlin <chundonglinlin@163.com>
236 lines
7.7 KiB
Go
236 lines
7.7 KiB
Go
// Copyright 2012 Google, Inc. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree.
|
|
|
|
package gopacket
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
|
|
//
|
|
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
|
|
// can't use a byte slice. The two obvious choices are to use a string or a
|
|
// byte array. Strings work great, but string creation requires memory
|
|
// allocation, which can be slow. Arrays work great, but have a fixed size. We
|
|
// originally used the former, now we've switched to the latter. Use of a fixed
|
|
// byte-array doubles the speed of constructing a flow (due to not needing to
|
|
// allocate). This is a huge increase... too much for us to pass up.
|
|
//
|
|
// The end result of this, though, is that an endpoint/flow can't be created
|
|
// using more than MaxEndpointSize bytes per address.
|
|
const MaxEndpointSize = 16
|
|
|
|
// Endpoint is the set of bytes used to address packets at various layers.
|
|
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
|
|
// Endpoints are usable as map keys.
|
|
type Endpoint struct {
|
|
typ EndpointType
|
|
len int
|
|
raw [MaxEndpointSize]byte
|
|
}
|
|
|
|
// EndpointType returns the endpoint type associated with this endpoint.
|
|
func (a Endpoint) EndpointType() EndpointType { return a.typ }
|
|
|
|
// Raw returns the raw bytes of this endpoint. These aren't human-readable
|
|
// most of the time, but they are faster than calling String.
|
|
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
|
|
|
|
// LessThan provides a stable ordering for all endpoints. It sorts first based
|
|
// on the EndpointType of an endpoint, then based on the raw bytes of that
|
|
// endpoint.
|
|
//
|
|
// For some endpoints, the actual comparison may not make sense, however this
|
|
// ordering does provide useful information for most Endpoint types.
|
|
// Ordering is based first on endpoint type, then on raw endpoint bytes.
|
|
// Endpoint bytes are sorted lexicographically.
|
|
func (a Endpoint) LessThan(b Endpoint) bool {
|
|
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
|
|
}
|
|
|
|
// fnvHash is used by our FastHash functions, and implements the FNV hash
|
|
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
|
|
// See http://isthe.com/chongo/tech/comp/fnv/.
|
|
func fnvHash(s []byte) (h uint64) {
|
|
h = fnvBasis
|
|
for i := 0; i < len(s); i++ {
|
|
h ^= uint64(s[i])
|
|
h *= fnvPrime
|
|
}
|
|
return
|
|
}
|
|
|
|
const fnvBasis = 14695981039346656037
|
|
const fnvPrime = 1099511628211
|
|
|
|
// FastHash provides a quick hashing function for an endpoint, useful if you'd
|
|
// like to split up endpoints by modulos or other load-balancing techniques.
|
|
// It uses a variant of Fowler-Noll-Vo hashing.
|
|
//
|
|
// The output of FastHash is not guaranteed to remain the same through future
|
|
// code revisions, so should not be used to key values in persistent storage.
|
|
func (a Endpoint) FastHash() (h uint64) {
|
|
h = fnvHash(a.raw[:a.len])
|
|
h ^= uint64(a.typ)
|
|
h *= fnvPrime
|
|
return
|
|
}
|
|
|
|
// NewEndpoint creates a new Endpoint object.
|
|
//
|
|
// The size of raw must be less than MaxEndpointSize, otherwise this function
|
|
// will panic.
|
|
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
|
|
e.len = len(raw)
|
|
if e.len > MaxEndpointSize {
|
|
panic("raw byte length greater than MaxEndpointSize")
|
|
}
|
|
e.typ = typ
|
|
copy(e.raw[:], raw)
|
|
return
|
|
}
|
|
|
|
// EndpointTypeMetadata is used to register a new endpoint type.
|
|
type EndpointTypeMetadata struct {
|
|
// Name is the string returned by an EndpointType's String function.
|
|
Name string
|
|
// Formatter is called from an Endpoint's String function to format the raw
|
|
// bytes in an Endpoint into a human-readable string.
|
|
Formatter func([]byte) string
|
|
}
|
|
|
|
// EndpointType is the type of a gopacket Endpoint. This type determines how
|
|
// the bytes stored in the endpoint should be interpreted.
|
|
type EndpointType int64
|
|
|
|
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
|
|
|
|
// RegisterEndpointType creates a new EndpointType and registers it globally.
|
|
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
|
|
// reserved for gopacket's use.
|
|
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
|
|
t := EndpointType(num)
|
|
if _, ok := endpointTypes[t]; ok {
|
|
panic("Endpoint type number already in use")
|
|
}
|
|
endpointTypes[t] = meta
|
|
return t
|
|
}
|
|
|
|
func (e EndpointType) String() string {
|
|
if t, ok := endpointTypes[e]; ok {
|
|
return t.Name
|
|
}
|
|
return strconv.Itoa(int(e))
|
|
}
|
|
|
|
func (a Endpoint) String() string {
|
|
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
|
|
return t.Formatter(a.raw[:a.len])
|
|
}
|
|
return fmt.Sprintf("%v:%v", a.typ, a.raw)
|
|
}
|
|
|
|
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
|
|
// Flows are usable as map keys.
|
|
type Flow struct {
|
|
typ EndpointType
|
|
slen, dlen int
|
|
src, dst [MaxEndpointSize]byte
|
|
}
|
|
|
|
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
|
|
// The endpoints must have the same EndpointType, or this function will return
|
|
// an error.
|
|
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
|
|
if src.typ != dst.typ {
|
|
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
|
|
return
|
|
}
|
|
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
|
|
}
|
|
|
|
// FastHash provides a quick hashing function for a flow, useful if you'd
|
|
// like to split up flows by modulos or other load-balancing techniques.
|
|
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
|
|
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
|
|
// B->A.
|
|
//
|
|
// The output of FastHash is not guaranteed to remain the same through future
|
|
// code revisions, so should not be used to key values in persistent storage.
|
|
func (f Flow) FastHash() (h uint64) {
|
|
// This combination must be commutative. We don't use ^, since that would
|
|
// give the same hash for all A->A flows.
|
|
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
|
|
h ^= uint64(f.typ)
|
|
h *= fnvPrime
|
|
return
|
|
}
|
|
|
|
// String returns a human-readable representation of this flow, in the form
|
|
// "Src->Dst"
|
|
func (f Flow) String() string {
|
|
s, d := f.Endpoints()
|
|
return fmt.Sprintf("%v->%v", s, d)
|
|
}
|
|
|
|
// EndpointType returns the EndpointType for this Flow.
|
|
func (f Flow) EndpointType() EndpointType {
|
|
return f.typ
|
|
}
|
|
|
|
// Endpoints returns the two Endpoints for this flow.
|
|
func (f Flow) Endpoints() (src, dst Endpoint) {
|
|
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
|
|
}
|
|
|
|
// Src returns the source Endpoint for this flow.
|
|
func (f Flow) Src() (src Endpoint) {
|
|
src, _ = f.Endpoints()
|
|
return
|
|
}
|
|
|
|
// Dst returns the destination Endpoint for this flow.
|
|
func (f Flow) Dst() (dst Endpoint) {
|
|
_, dst = f.Endpoints()
|
|
return
|
|
}
|
|
|
|
// Reverse returns a new flow with endpoints reversed.
|
|
func (f Flow) Reverse() Flow {
|
|
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
|
|
}
|
|
|
|
// NewFlow creates a new flow.
|
|
//
|
|
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
|
|
// panic.
|
|
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
|
|
f.slen = len(src)
|
|
f.dlen = len(dst)
|
|
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
|
|
panic("flow raw byte length greater than MaxEndpointSize")
|
|
}
|
|
f.typ = t
|
|
copy(f.src[:], src)
|
|
copy(f.dst[:], dst)
|
|
return
|
|
}
|
|
|
|
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
|
|
// that are specified incorrectly during creation.
|
|
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
|
|
return fmt.Sprintf("%v", b)
|
|
}})
|
|
|
|
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
|
|
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
|
|
|
|
// InvalidFlow is a singleton Flow of type EndpointInvalid.
|
|
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)
|