forked from prestonTao/upnp
/
SearchGatewayMsg.go
129 lines (116 loc) · 3.39 KB
/
SearchGatewayMsg.go
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
package upnp
import (
"log"
"fmt"
"net"
"strings"
)
type Gateway struct {
GatewayName string //Gateway Name
Host string //Gateway ip and port
DeviceDescUrl string //Gateway device description path
Cache string //cache
ST string
USN string
deviceType string //Urn device "urn: schemas-upnp-org: service: WANIPConnection: 1"
ControlURL string //Device port mapping request path
ServiceType string //Upnp services provide the type of service
}
type SearchGateway struct {
searchMessage string
upnp *Upnp
}
func (this *SearchGateway) Send() (bool, error) {
this.buildRequest()
result, err := this.SendMessage()
if err != nil {
return false, err
}
if result == "" {
//Overtime
this.upnp.Active = false
return false, nil
}
this.resolve(result)
this.upnp.Gateway.ServiceType = "urn:schemas-upnp-org:service:WANIPConnection:1"
this.upnp.Active = true
return true, nil
}
func (this *SearchGateway) SendMessage() (result string, err error) {
//Send broadcast messages to bring the port, formats such as: "239.255.255.250:1900"
var conn *net.UDPConn
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic err: %s", r)
}
}()
//go func(conn *net.UDPConn) {
// defer func() {
// if r := recover(); r != nil {
// log.Printf("panic in timeout conn err: %s", err)
// }
// }()
// //Timeout to 3 seconds
// time.Sleep(time.Second * 3)
// if err := conn.Close(); err != nil {
// log.Printf("conn close err: %s", err)
// }
//}(conn)
remotAddr, err := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
if err != nil {
return "", fmt.Errorf("Multicast address format is incorrect err: %s", err)
}
locaAddr, err := net.ResolveUDPAddr("udp", this.upnp.LocalHost+":0")
if err != nil {
return "", fmt.Errorf("Local IP address is incorrent err: %s", err)
}
conn, err = net.ListenUDP("udp", locaAddr)
if err != nil {
return "", fmt.Errorf("Listening udp error err: %s", err)
}
defer func(conn net.Conn) {
if err := conn.Close(); err != nil {
log.Printf("conn close err: %s", err)
}
}(conn)
_, err = conn.WriteToUDP([]byte(this.searchMessage), remotAddr)
if err != nil {
return "", fmt.Errorf("Sent to a multicast address err: %s", err)
}
buf := make([]byte, 1024)
n, _, err := conn.ReadFromUDP(buf)
if err != nil {
return "", fmt.Errorf("Error message received from a multicast address")
}
return string(buf[:n]), nil
}
func (this *SearchGateway) buildRequest() {
this.searchMessage = "M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
"MAN: \"ssdp:discover\"\r\n" + "MX: 3\r\n\r\n"
}
func (this *SearchGateway) resolve(result string) {
this.upnp.Gateway = &Gateway{}
lines := strings.Split(result, "\r\n")
for _, line := range lines {
//According to a first colon into two strings
nameValues := strings.SplitAfterN(line, ":", 2)
if len(nameValues) < 2 {
continue
}
switch strings.ToUpper(strings.Trim(strings.Split(nameValues[0], ":")[0], " ")) {
case "ST":
this.upnp.Gateway.ST = nameValues[1]
case "CACHE-CONTROL":
this.upnp.Gateway.Cache = nameValues[1]
case "LOCATION":
urls := strings.Split(strings.Split(nameValues[1], "//")[1], "/")
this.upnp.Gateway.Host = urls[0]
this.upnp.Gateway.DeviceDescUrl = "/" + urls[1]
case "SERVER":
this.upnp.Gateway.GatewayName = nameValues[1]
default:
}
}
}