Files
pmomusic/internal/ssdp/responder.go
Eric Coissac 2e80eb85d2 Changes to be committed:
modified:   cmd/pmomusic/main.go
	new file:   internal/netutils/ip_detect.go
	new file:   internal/netutils/list_all_ip.go
	modified:   internal/soap/soap.go
	modified:   internal/ssdp/responder.go
	modified:   internal/ssdp/ssdp.go
	modified:   internal/upnp/http.go
	new file:   internal/upnp/server.go
	new file:   internal/upnp/xml/AVTransport.xml
	new file:   internal/upnp/xml/ConnectionManager.xml
	new file:   internal/upnp/xml/RenderingControl.xml
2025-06-08 17:41:52 +02:00

101 lines
2.2 KiB
Go

package ssdp
import (
"fmt"
"log"
"net"
"strings"
"time"
)
const (
ssdpAddr = "239.255.255.250:1900"
serviceType = "urn:schemas-upnp-org:device:MediaRenderer:1"
)
// StartSSDPResponder listens for M-SEARCH requests and responds if they match
func StartSSDPResponder(usn, location string) {
addr, err := net.ResolveUDPAddr("udp4", ssdpAddr)
if err != nil {
log.Fatal("Failed to resolve SSDP address:", err)
}
conn, err := net.ListenMulticastUDP("udp4", nil, addr)
if err != nil {
log.Fatal("Failed to listen for SSDP:", err)
}
defer conn.Close()
conn.SetReadBuffer(2048)
buf := make([]byte, 2048)
log.Println("🔍 Listening for M-SEARCH...")
for {
n, src, err := conn.ReadFromUDP(buf)
if err != nil {
log.Println("ReadFromUDP error:", err)
continue
}
data := string(buf[:n])
if strings.HasPrefix(data, "M-SEARCH * HTTP/1.1") {
st := extractHeader(data, "ST")
log.Printf("🔎 M-SEARCH from %s, ST=%s\n", src.String(), st)
if st == "ssdp:all" ||
st == serviceType ||
strings.HasPrefix(st, "urn:schemas-upnp-org:service:") ||
strings.HasPrefix(st, "urn:av-openhome-org:service:") ||
strings.HasPrefix(st, "urn:bubblesoftapps-com:service:") {
go sendSSDPResponse(src, usn, location, st)
}
}
}
}
func sendSSDPResponse(dst *net.UDPAddr, usn, location, st string) {
resp := fmt.Sprintf(
"HTTP/1.1 200 OK\r\n"+
"CACHE-CONTROL: max-age=1800\r\n"+
"DATE: %s\r\n"+
"EXT:\r\n"+
"LOCATION: %s\r\n"+
"SERVER: pmomusic/1.0 UPnP/1.1 DLNARenderer/1.0\r\n"+
"ST: %s\r\n"+
"USN: %s::%s\r\n"+
"\r\n",
time.Now().Format(time.RFC1123),
location,
st,
usn,
st,
)
// envoie UDP
conn, err := net.DialUDP("udp4", nil, dst)
if err != nil {
log.Println("Failed to dial UDP to respond:", err)
return
}
defer conn.Close()
_, err = conn.Write([]byte(resp))
if err != nil {
log.Println("Failed to send SSDP response:", err)
}
}
func extractHeader(data string, key string) string {
lines := strings.Split(data, "\r\n")
key = strings.ToLower(key)
for _, line := range lines {
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 && strings.ToLower(strings.TrimSpace(parts[0])) == key {
return strings.TrimSpace(parts[1])
}
}
return ""
}