This repository was archived by the owner on Jan 15, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdevice.go
More file actions
139 lines (128 loc) · 3.49 KB
/
device.go
File metadata and controls
139 lines (128 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2014 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssdp
import (
"bytes"
"errors"
"log"
"net"
"net/http"
"runtime"
)
// A Device represents a SSDP device.
type Device struct {
// ErrorLog specified an optional logger for errors. If it is
// nil, logging goes to os.Stderr via the log package's
// standard logger.
ErrorLog *log.Logger
conn // network connection endpoint
group *net.UDPAddr // group address
unicast func(net.IP) bool // unicast address filter
mifs []net.Interface // multicast network interfaces
}
// ListenDevices listens on the UDP network Listener.Group and
// Listener.Port, and returns a device. If mifs is nil, it tries to
// listen on all available multicast network interfaces.
func (ln *Listener) ListenDevice(mifs []net.Interface) (*Device, error) {
var err error
dev := &Device{}
if dev.conn, dev.group, err = ln.listen(); err != nil {
return nil, err
}
if dev.group.IP.To4() != nil {
dev.unicast = ipv4Unicast
} else {
dev.unicast = ipv6Unicast
}
if dev.mifs, err = joinGroup(dev.conn, dev.group, mifs, dev.unicast); err != nil {
dev.Close()
return nil, err
}
return dev, nil
}
// Serve starts to handle incoming SSDP messages from SSDP control
// points. The handler must not be nil.
func (dev *Device) Serve(hdlr http.Handler) error {
if hdlr == nil {
return errors.New("invalid http handler")
}
b := make([]byte, 1280)
for {
n, path, err := dev.readFrom(b)
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
dev.logf("read failed: %v", err)
continue
}
return err
}
if !path.dst.IP.IsMulticast() {
continue
}
if !path.dst.IP.Equal(dev.group.IP) {
dev.logf("unknown destination address: %v on %v", path.dst, interfaceByIndex(dev.mifs, path.ifIndex).Name)
continue
}
req, err := parseAdvert(b[:n])
if err != nil {
dev.logf("parse advert failed: %v", err)
continue
}
if req.Method != msearchMethod {
continue
}
resp := newResponseWriter(dev.conn, dev.mifs, dev.group, path, req)
go func() {
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
b := make([]byte, size)
b = b[:runtime.Stack(b, false)]
dev.logf("panic serving %v: %v\n%s", resp.path.src, err, b)
}
}()
hdlr.ServeHTTP(resp, req)
}()
}
}
// GroupAddr returns the joined group network address.
func (dev *Device) GroupAddr() *net.UDPAddr {
return dev.group
}
// Close closes the device.
func (dev *Device) Close() error {
for _, ifi := range dev.mifs {
dev.LeaveGroup(&ifi, dev.group)
}
return dev.conn.Close()
}
// Interfaces returns a list of the joined multicast network
// interfaces.
func (dev *Device) Interfaces() []net.Interface {
return dev.mifs
}
// Notify issues a NOTIFY SSDP message. If mifs is nil, it tries to
// use all available multicast network interfaces.
func (dev *Device) Notify(hdr http.Header, mifs []net.Interface) error {
req := newAdvert(notifyMethod, dev.group.String(), hdr)
var buf bytes.Buffer
if err := req.Write(&buf); err != nil {
return err
}
mifs, err := interfaces(mifs, dev.unicast)
if err != nil {
return err
}
if _, err := dev.writeToMulti(buf.Bytes(), dev.group, mifs); err != nil {
return err
}
return nil
}
func (dev *Device) logf(format string, args ...interface{}) {
if dev.ErrorLog != nil {
dev.ErrorLog.Printf(format, args...)
} else {
log.Printf(format, args...)
}
}