123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- package ssdp
- import (
- "errors"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "os/exec"
- "runtime"
- "strconv"
- "strings"
- "time"
- ssdp "github.com/koron/go-ssdp"
- "github.com/valyala/fasttemplate"
- )
- type SSDPOption struct {
- URLBase string
- Hostname string
- Vendor string
- VendorURL string
- ModelName string
- ModelDesc string
- Serial string
- UUID string
- }
- type SSDPHost struct {
- ADV *ssdp.Advertiser
- advStarted bool
- SSDPTemplateFile string
- Option *SSDPOption
- quit chan bool
- }
- func NewSSDPHost(outboundIP string, port int, templateFile string, option SSDPOption) (*SSDPHost, error) {
- if runtime.GOOS == "linux" {
- //In case there are more than 1 network interface connect to the same LAN, choose the first one by default
- interfaceName, err := getFirstNetworkInterfaceName()
- if err != nil {
- //Ignore the interface binding
- log.Println("[WARN] No connected network interface. Starting SSDP anyway.")
- } else {
- mainNIC, err := net.InterfaceByName(interfaceName)
- if err != nil {
- log.Println("[WARN] Unable to get interface by name: " + interfaceName + ". Starting SSDP on all IPv4 interfaces.")
- } else {
- ssdp.Interfaces = []net.Interface{*mainNIC}
- }
- }
- }
- ad, err := ssdp.Advertise(
- "upnp:rootdevice", // send as "ST"
- "uuid:"+option.UUID, // send as "USN"
- "http://"+outboundIP+":"+strconv.Itoa(port)+"/ssdp.xml", // send as "LOCATION"
- "arozos/"+outboundIP, // send as "SERVER"
- 30) // send as "maxAge" in "CACHE-CONTROL"
- if err != nil {
- return &SSDPHost{}, err
- }
- return &SSDPHost{
- ADV: ad,
- advStarted: false,
- SSDPTemplateFile: templateFile,
- Option: &option,
- }, nil
- }
- func (a *SSDPHost) Start() {
- //Advertise ssdp
- http.HandleFunc("/ssdp.xml", a.handleSSDP)
- log.Println("Starting SSDP Discovery Service: " + a.Option.URLBase)
- var aliveTick <-chan time.Time
- aliveTick = time.Tick(time.Duration(5) * time.Second)
- quit := make(chan bool)
- a.quit = quit
- a.advStarted = true
- go func(ad *ssdp.Advertiser) {
- for {
- select {
- case <-aliveTick:
- ad.Alive()
- case <-quit:
- ad.Bye()
- ad.Close()
- break
- }
- }
- }(a.ADV)
- }
- func (a *SSDPHost) Close() {
- if a != nil {
- if a.advStarted {
- a.quit <- true
- }
- }
- }
- //Serve the xml file with the given properties
- func (a *SSDPHost) handleSSDP(w http.ResponseWriter, r *http.Request) {
- //Load the ssdp xml from file
- template, err := ioutil.ReadFile(a.SSDPTemplateFile)
- if err != nil {
- w.Write([]byte("SSDP.XML NOT FOUND"))
- return
- }
- t := fasttemplate.New(string(template), "{{", "}}")
- s := t.ExecuteString(map[string]interface{}{
- "urlbase": a.Option.URLBase,
- "hostname": a.Option.Hostname,
- "vendor": a.Option.Vendor,
- "vendorurl": a.Option.VendorURL,
- "modeldesc": a.Option.ModelDesc,
- "modelname": a.Option.ModelName,
- "uuid": a.Option.UUID,
- "serial": a.Option.Serial,
- })
- w.Write([]byte(s))
- }
- //Helper functions
- func getFirstNetworkInterfaceName() (string, error) {
- if runtime.GOOS == "linux" {
- if pkg_exists("ip") {
- //Use the fast method
- cmd := exec.Command("bash", "-c", `ip route | grep default | sed -e "s/^.*dev.//" -e "s/.proto.*//"`)
- out, _ := cmd.CombinedOutput()
- if strings.TrimSpace(string(out)) == "" {
- //No interface found.
- return "", errors.New("No interface found")
- } else {
- return strings.Split(strings.TrimSpace(string(out)), "\n")[0], nil
- }
- } else if pkg_exists("ifconfig") {
- //Guess it from ifconfig list
- cmd := exec.Command("bash", "-c", `ifconfig -a | sed -E 's/[[:space:]:].*//;/^$/d'`)
- out, _ := cmd.CombinedOutput()
- if strings.TrimSpace(string(out)) == "" {
- //No interface found.
- return "", errors.New("No interface found")
- } else {
- return strings.Split(strings.TrimSpace(string(out)), "\n")[0], nil
- }
- }
- }
- return "", errors.New("Not supported platform or missing package")
- }
- func pkg_exists(pkgname string) bool {
- cmd := exec.Command("which", pkgname)
- out, _ := cmd.CombinedOutput()
- if len(string(out)) > 1 {
- return true
- } else {
- return false
- }
- }
|