Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f196d83a14 | |||
| 9d4f4e1509 | |||
| 7308652f6e | |||
| 870e9c3688 | |||
| 189f142fae | |||
| 6c0918662e | |||
| 2bc01c143a | |||
| f310b85ee6 | |||
| 97fef36f2f |
@@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/mdns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var service = mdns.ServiceHAP
|
||||||
|
|
||||||
|
if len(os.Args) >= 2 {
|
||||||
|
service = os.Args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
onentry := func(entry *mdns.ServiceEntry) bool {
|
||||||
|
log.Printf("name=%s, addr=%s, info=%s\n", entry.Name, entry.Addr(), entry.Info)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(os.Args) >= 3 {
|
||||||
|
host := os.Args[2]
|
||||||
|
|
||||||
|
log.Printf("run discovery service=%s host=%s\n", service, host)
|
||||||
|
|
||||||
|
err = mdns.QueryOrDiscovery(host, service, onentry)
|
||||||
|
} else {
|
||||||
|
log.Printf("run discovery service=%s\n", service)
|
||||||
|
|
||||||
|
err = mdns.Discovery(service, onentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-6
@@ -4,14 +4,15 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/AlexxIT/go2rtc/internal/app"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/AlexxIT/go2rtc/internal/app"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
@@ -30,7 +31,7 @@ func Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default config
|
// default config
|
||||||
cfg.Mod.Listen = ":1984"
|
cfg.Mod.Listen = "0.0.0.0:1984"
|
||||||
|
|
||||||
// load config from YAML
|
// load config from YAML
|
||||||
app.LoadConfig(&cfg)
|
app.LoadConfig(&cfg)
|
||||||
@@ -49,7 +50,7 @@ func Init() {
|
|||||||
HandleFunc("api/exit", exitHandler)
|
HandleFunc("api/exit", exitHandler)
|
||||||
|
|
||||||
// ensure we can listen without errors
|
// ensure we can listen without errors
|
||||||
listener, err := net.Listen("tcp4", cfg.Mod.Listen)
|
listener, err := net.Listen("tcp", cfg.Mod.Listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("[api] listen")
|
log.Fatal().Err(err).Msg("[api] listen")
|
||||||
return
|
return
|
||||||
@@ -87,7 +88,7 @@ func Init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsListener, err := net.Listen("tcp4", cfg.Mod.TLSListen)
|
tlsListener, err := net.Listen("tcp", cfg.Mod.TLSListen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Caller().Send()
|
log.Fatal().Err(err).Caller().Send()
|
||||||
return
|
return
|
||||||
@@ -169,7 +170,7 @@ func middlewareLog(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
func middlewareAuth(username, password string, next http.Handler) http.Handler {
|
func middlewareAuth(username, password string, next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !strings.HasPrefix(r.RemoteAddr, "127.") {
|
if !strings.HasPrefix(r.RemoteAddr, "127.") && !strings.HasPrefix(r.RemoteAddr, "[::1]") {
|
||||||
user, pass, ok := r.BasicAuth()
|
user, pass, ok := r.BasicAuth()
|
||||||
if !ok || user != username || pass != password {
|
if !ok || user != username || pass != password {
|
||||||
w.Header().Set("Www-Authenticate", `Basic realm="go2rtc"`)
|
w.Header().Set("Www-Authenticate", `Basic realm="go2rtc"`)
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "1.6.1"
|
var Version = "1.6.2"
|
||||||
var UserAgent = "go2rtc/" + Version
|
var UserAgent = "go2rtc/" + Version
|
||||||
|
|
||||||
var ConfigPath string
|
var ConfigPath string
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default config
|
// default config
|
||||||
conf.Mod.Listen = ":8554"
|
conf.Mod.Listen = "0.0.0.0:8554"
|
||||||
conf.Mod.DefaultQuery = "video&audio"
|
conf.Mod.DefaultQuery = "video&audio"
|
||||||
|
|
||||||
app.LoadConfig(&conf)
|
app.LoadConfig(&conf)
|
||||||
@@ -45,7 +45,7 @@ func Init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ln, err := net.Listen("tcp4", address)
|
ln, err := net.Listen("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("[rtsp] listen")
|
log.Error().Err(err).Msg("[rtsp] listen")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package srtp
|
package srtp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/internal/app"
|
"github.com/AlexxIT/go2rtc/internal/app"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/srtp"
|
"github.com/AlexxIT/go2rtc/pkg/srtp"
|
||||||
"net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
@@ -14,7 +15,7 @@ func Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default config
|
// default config
|
||||||
cfg.Mod.Listen = ":8443"
|
cfg.Mod.Listen = "0.0.0.0:8443"
|
||||||
|
|
||||||
// load config from YAML
|
// load config from YAML
|
||||||
app.LoadConfig(&cfg)
|
app.LoadConfig(&cfg)
|
||||||
|
|||||||
@@ -57,12 +57,22 @@ func Patch(name string, source string) *Stream {
|
|||||||
if u, err := url.Parse(source); err == nil && u.Scheme == "rtsp" && len(u.Path) > 1 {
|
if u, err := url.Parse(source); err == nil && u.Scheme == "rtsp" && len(u.Path) > 1 {
|
||||||
rtspName := u.Path[1:]
|
rtspName := u.Path[1:]
|
||||||
if stream, ok := streams[rtspName]; ok {
|
if stream, ok := streams[rtspName]; ok {
|
||||||
// link (alias) stream[name] to stream[rtspName]
|
if streams[name] != stream {
|
||||||
streams[name] = stream
|
// link (alias) streams[name] to streams[rtspName]
|
||||||
|
streams[name] = stream
|
||||||
|
}
|
||||||
return stream
|
return stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stream, ok := streams[source]; ok {
|
||||||
|
if name != source {
|
||||||
|
// link (alias) streams[name] to streams[source]
|
||||||
|
streams[name] = stream
|
||||||
|
}
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
// check if src has supported scheme
|
// check if src has supported scheme
|
||||||
if !HasProducer(source) {
|
if !HasProducer(source) {
|
||||||
return nil
|
return nil
|
||||||
@@ -91,7 +101,7 @@ func GetOrPatch(query url.Values) *Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if name param provided
|
// check if name param provided
|
||||||
if name := query.Get("name"); name == "" {
|
if name := query.Get("name"); name != "" {
|
||||||
log.Info().Msgf("[streams] create new stream url=%s", source)
|
log.Info().Msgf("[streams] create new stream url=%s", source)
|
||||||
|
|
||||||
return Patch(name, source)
|
return Patch(name, source)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func Init() {
|
|||||||
} `yaml:"webrtc"`
|
} `yaml:"webrtc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Mod.Listen = ":8555/tcp"
|
cfg.Mod.Listen = "0.0.0.0:8555/tcp"
|
||||||
cfg.Mod.IceServers = []pion.ICEServer{
|
cfg.Mod.IceServers = []pion.ICEServer{
|
||||||
{URLs: []string{"stun:stun.l.google.com:19302"}},
|
{URLs: []string{"stun:stun.l.google.com:19302"}},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (c *Client) Dial() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.conn, err = net.DialTimeout("tcp4", u.Host, Timeout); err != nil {
|
if c.conn, err = net.DialTimeout("tcp", u.Host, Timeout); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-8
@@ -7,6 +7,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/mdns"
|
"github.com/AlexxIT/go2rtc/pkg/mdns"
|
||||||
"github.com/brutella/hap"
|
"github.com/brutella/hap"
|
||||||
@@ -16,12 +23,6 @@ import (
|
|||||||
"github.com/brutella/hap/hkdf"
|
"github.com/brutella/hap/hkdf"
|
||||||
"github.com/brutella/hap/tlv8"
|
"github.com/brutella/hap/tlv8"
|
||||||
"github.com/tadglines/go-pkgs/crypto/srp"
|
"github.com/tadglines/go-pkgs/crypto/srp"
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conn for HomeKit. DevicePublic can be null.
|
// Conn for HomeKit. DevicePublic can be null.
|
||||||
@@ -105,9 +106,16 @@ func (c *Conn) DialAndServe() error {
|
|||||||
return c.Handle()
|
return c.Handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) DeviceHost() string {
|
||||||
|
if i := strings.IndexByte(c.DeviceAddress, ':'); i > 0 {
|
||||||
|
return c.DeviceAddress[:i]
|
||||||
|
}
|
||||||
|
return c.DeviceAddress
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) Dial() error {
|
func (c *Conn) Dial() error {
|
||||||
// update device host before dial
|
// update device address (host and/or port) before dial
|
||||||
_ = mdns.Discovery(mdns.ServiceHAP, func(entry *mdns.ServiceEntry) bool {
|
_ = mdns.QueryOrDiscovery(c.DeviceHost(), mdns.ServiceHAP, func(entry *mdns.ServiceEntry) bool {
|
||||||
if entry.Complete() && entry.Info["id"] == c.DeviceID {
|
if entry.Complete() && entry.Info["id"] == c.DeviceID {
|
||||||
c.DeviceAddress = entry.Addr()
|
c.DeviceAddress = entry.Addr()
|
||||||
return true
|
return true
|
||||||
|
|||||||
+218
-21
@@ -1,18 +1,19 @@
|
|||||||
package mdns
|
package mdns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/miekg/dns"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/miekg/dns" // awesome library for parsing mDNS records
|
||||||
)
|
)
|
||||||
|
|
||||||
const ServiceHAP = "_hap._tcp.local." // HomeKit Accessory Protocol
|
const ServiceHAP = "_hap._tcp.local." // HomeKit Accessory Protocol
|
||||||
|
|
||||||
const requestTimeout = time.Millisecond * 505
|
|
||||||
const responseTimeout = time.Second * 2
|
|
||||||
|
|
||||||
type ServiceEntry struct {
|
type ServiceEntry struct {
|
||||||
Name string
|
Name string
|
||||||
IP net.IP
|
IP net.IP
|
||||||
@@ -28,40 +29,193 @@ func (e *ServiceEntry) Addr() string {
|
|||||||
return fmt.Sprintf("%s:%d", e.IP, e.Port)
|
return fmt.Sprintf("%s:%d", e.IP, e.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Discovery(service string, onentry func(*ServiceEntry) bool) error {
|
var MulticastAddr = &net.UDPAddr{
|
||||||
addr := &net.UDPAddr{
|
IP: net.IP{224, 0, 0, 251},
|
||||||
IP: net.IP{224, 0, 0, 251},
|
Port: 5353,
|
||||||
Port: 5353,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := net.ListenMulticastUDP("udp4", nil, addr)
|
const sendTimeout = time.Millisecond * 505
|
||||||
|
const respTimeout = time.Second * 3
|
||||||
|
|
||||||
|
// BasicDiscovery - default golang Multicast UDP listener.
|
||||||
|
// Does not work well with multiple interfaces.
|
||||||
|
func BasicDiscovery(service string, onentry func(*ServiceEntry) bool) error {
|
||||||
|
conn, err := net.ListenMulticastUDP("udp4", nil, MulticastAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer conn.Close()
|
b := Browser{
|
||||||
|
Service: service,
|
||||||
|
Addr: MulticastAddr,
|
||||||
|
Recv: conn,
|
||||||
|
Sends: []net.PacketConn{conn},
|
||||||
|
RecvTimeout: respTimeout,
|
||||||
|
SendTimeout: sendTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
if err = conn.SetDeadline(time.Now().Add(responseTimeout)); err != nil {
|
defer b.Close()
|
||||||
|
|
||||||
|
return b.Browse(onentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery - better discovery version. Works well with multiple interfaces.
|
||||||
|
func Discovery(service string, onentry func(*ServiceEntry) bool) error {
|
||||||
|
b := Browser{
|
||||||
|
Service: service,
|
||||||
|
Addr: MulticastAddr,
|
||||||
|
RecvTimeout: respTimeout,
|
||||||
|
SendTimeout: sendTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.ListenMulticastUDP(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &dns.Msg{
|
defer b.Close()
|
||||||
Question: []dns.Question{
|
|
||||||
{service, dns.TypePTR, dns.ClassINET},
|
return b.Browse(onentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query - direct Discovery request on device IP-address. Works even over VPN.
|
||||||
|
func Query(host, service string) (entry *ServiceEntry, err error) {
|
||||||
|
conn, err := net.ListenPacket("udp4", ":0") // shouldn't use ":5353"
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
br := Browser{
|
||||||
|
Service: service,
|
||||||
|
Addr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP(host),
|
||||||
|
Port: 5353,
|
||||||
|
},
|
||||||
|
Recv: conn,
|
||||||
|
Sends: []net.PacketConn{conn},
|
||||||
|
SendTimeout: time.Millisecond * 255,
|
||||||
|
RecvTimeout: time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
defer br.Close()
|
||||||
|
|
||||||
|
err = br.Browse(func(en *ServiceEntry) bool {
|
||||||
|
entry = en
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOrDiscovery - useful if we know previous device host and want
|
||||||
|
// to update port or any other information. Will work even over VPN.
|
||||||
|
func QueryOrDiscovery(host, service string, onentry func(*ServiceEntry) bool) error {
|
||||||
|
entry, _ := Query(host, service)
|
||||||
|
if entry != nil && onentry(entry) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return Discovery(service, onentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Browser struct {
|
||||||
|
Service string
|
||||||
|
|
||||||
|
Addr net.Addr
|
||||||
|
Recv net.PacketConn
|
||||||
|
Sends []net.PacketConn
|
||||||
|
|
||||||
|
RecvTimeout time.Duration
|
||||||
|
SendTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenMulticastUDP - creates multiple senders socket (each for IP4 interface).
|
||||||
|
// And one receiver with multicast membership for each sender.
|
||||||
|
// Receiver will get multicast responses on senders requests.
|
||||||
|
func (b *Browser) ListenMulticastUDP() error {
|
||||||
|
// 1. Collect IPv4 interfaces
|
||||||
|
ip4s, err := InterfacesIP4()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 2. Create senders
|
||||||
|
lc1 := net.ListenConfig{
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
// 1. Allow multicast UDP to listen concurrently across multiple listeners
|
||||||
|
_ = SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
b1, err := msg.Pack()
|
for _, ip4 := range ip4s {
|
||||||
|
conn, err := lc1.ListenPacket(ctx, "udp4", ip4.String()+":5353") // same port important
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.Sends = append(b.Sends, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Sends == nil {
|
||||||
|
return errors.New("no interfaces for listen")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create receiver
|
||||||
|
lc2 := net.ListenConfig{
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
// 1. Allow multicast UDP to listen concurrently across multiple listeners
|
||||||
|
_ = SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
// 2. Disable loop responses
|
||||||
|
_ = SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 0)
|
||||||
|
|
||||||
|
// 3. Allow receive multicast responses on all this addresses
|
||||||
|
mreq := &syscall.IPMreq{
|
||||||
|
Multiaddr: [4]byte{224, 0, 0, 251},
|
||||||
|
}
|
||||||
|
_ = SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
|
||||||
|
|
||||||
|
for _, send := range b.Sends {
|
||||||
|
addr := send.LocalAddr().(*net.UDPAddr)
|
||||||
|
mreq.Interface = [4]byte(addr.IP.To4())
|
||||||
|
_ = SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Recv, err = lc2.ListenPacket(ctx, "udp4", "0.0.0.0:5353")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Browser) Browse(onentry func(*ServiceEntry) bool) error {
|
||||||
|
msg := &dns.Msg{
|
||||||
|
Question: []dns.Question{
|
||||||
|
{b.Service, dns.TypePTR, dns.ClassINET},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := msg.Pack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = b.Recv.SetDeadline(time.Now().Add(b.RecvTimeout)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if _, err := conn.WriteToUDP(b1, addr); err != nil {
|
for _, send := range b.Sends {
|
||||||
return
|
if _, err := send.WriteTo(query, b.Addr); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(requestTimeout)
|
time.Sleep(b.SendTimeout)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -71,7 +225,7 @@ func Discovery(service string, onentry func(*ServiceEntry) bool) error {
|
|||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
// in the Hass docker network can receive same msg from different address
|
// in the Hass docker network can receive same msg from different address
|
||||||
n, _, err := conn.ReadFromUDP(b2)
|
n, _, err := b.Recv.ReadFrom(b2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -82,7 +236,7 @@ loop:
|
|||||||
|
|
||||||
ptr := GetPTR(msg)
|
ptr := GetPTR(msg)
|
||||||
|
|
||||||
if !strings.HasSuffix(ptr, service) {
|
if !strings.HasSuffix(ptr, b.Service) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +256,16 @@ loop:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Browser) Close() error {
|
||||||
|
if b.Recv != nil {
|
||||||
|
_ = b.Recv.Close()
|
||||||
|
}
|
||||||
|
for _, send := range b.Sends {
|
||||||
|
_ = send.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetPTR(msg *dns.Msg) string {
|
func GetPTR(msg *dns.Msg) string {
|
||||||
for _, rr := range msg.Answer {
|
for _, rr := range msg.Answer {
|
||||||
if rr, ok := rr.(*dns.PTR); ok {
|
if rr, ok := rr.(*dns.PTR); ok {
|
||||||
@@ -139,3 +303,36 @@ func NewServiceEntry(msg *dns.Msg) *ServiceEntry {
|
|||||||
|
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InterfacesIP4() ([]net.IP, error) {
|
||||||
|
intfs, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ips []net.IP
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for _, intf := range intfs {
|
||||||
|
if intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagLoopback != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := intf.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
if ip := v.IP.To4(); ip != nil {
|
||||||
|
ips = append(ips, ip)
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build darwin || linux
|
||||||
|
|
||||||
|
package mdns
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) {
|
||||||
|
return syscall.SetsockoptInt(int(fd), level, opt, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) (err error) {
|
||||||
|
return syscall.SetsockoptIPMreq(int(fd), level, opt, mreq)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package mdns
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) {
|
||||||
|
return syscall.SetsockoptInt(syscall.Handle(fd), level, opt, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) (err error) {
|
||||||
|
return syscall.SetsockoptIPMreq(syscall.Handle(fd), level, opt, mreq)
|
||||||
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package rtsp
|
package rtsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTimeout(t *testing.T) {
|
func TestTimeout(t *testing.T) {
|
||||||
Timeout = time.Millisecond
|
Timeout = time.Millisecond
|
||||||
|
|
||||||
ln, err := net.Listen("tcp4", "localhost:0")
|
ln, err := net.Listen("tcp", "localhost:0")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
client := NewClient("rtsp://" + ln.Addr().String() + "/stream")
|
client := NewClient("rtsp://" + ln.Addr().String() + "/stream")
|
||||||
@@ -27,7 +28,7 @@ func TestTimeout(t *testing.T) {
|
|||||||
func TestMissedControl(t *testing.T) {
|
func TestMissedControl(t *testing.T) {
|
||||||
Timeout = time.Millisecond
|
Timeout = time.Millisecond
|
||||||
|
|
||||||
ln, err := net.Listen("tcp4", "localhost:0")
|
ln, err := net.Listen("tcp", "localhost:0")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user