|
@@ -1,12 +1,12 @@
|
1
|
1
|
module stratos.upnp;
|
2
|
2
|
|
3
|
|
-/+
|
|
3
|
+import vibe.core.log;
|
|
4
|
+
|
4
|
5
|
/// UPnP handler.
|
5
|
6
|
class UPnP
|
6
|
7
|
{
|
7
|
8
|
import std.datetime : SysTime;
|
8
|
9
|
import std.uuid : UUID;
|
9
|
|
- import vibe.core.log;
|
10
|
10
|
import vibe.core.net;
|
11
|
11
|
import vibe.core.sync : ManualEvent, createManualEvent;
|
12
|
12
|
|
|
@@ -20,6 +20,8 @@ class UPnP
|
20
|
20
|
SysTime expires;
|
21
|
21
|
string serviceType;
|
22
|
22
|
string serviceVersion;
|
|
23
|
+ string nodeName;
|
|
24
|
+ string[] ips;
|
23
|
25
|
|
24
|
26
|
ulong bootID;
|
25
|
27
|
ulong configID;
|
|
@@ -102,17 +104,17 @@ class UPnP
|
102
|
104
|
|
103
|
105
|
private void broadcast(string destIP, string[] myIPs)
|
104
|
106
|
{
|
105
|
|
- import scriptlike.core : interp;
|
|
107
|
+ import scriptlike.core : interp, _interp_text;
|
|
108
|
+ import std.algorithm.iteration : joiner;
|
106
|
109
|
import std.conv : to;
|
107
|
110
|
import std.socket : Socket;
|
108
|
111
|
import std.string : replace;
|
109
|
|
- import std.system : os;
|
110
|
112
|
import std.uuid : randomUUID;
|
111
|
113
|
import stratos.osinfo;
|
112
|
114
|
|
113
|
115
|
if (myIPs.length == 0) return;
|
114
|
116
|
|
115
|
|
- auto name = Socket.hostname;
|
|
117
|
+ auto name = Socket.hostName;
|
116
|
118
|
auto configID = 1;
|
117
|
119
|
auto msgNumber = 1;
|
118
|
120
|
// So, this is an interesting problem.
|
|
@@ -120,7 +122,8 @@ class UPnP
|
120
|
122
|
// For instance, you might have code that should only run on Linux with kernel 2.6 or higher.
|
121
|
123
|
// We want the kernel name. Or maybe the GLIBC version is the issue. Or maybe you want it
|
122
|
124
|
// to run on Ubuntu 16.04 or higher, or SLES 11.x, or something like that.
|
123
|
|
- auto osVersion = "4";
|
|
125
|
+ enum stratosVersion = "0.0.1";
|
|
126
|
+ auto os = osInfo;
|
124
|
127
|
|
125
|
128
|
auto packet = mixin(interp!`NOTIFY * HTTP/1.1
|
126
|
129
|
HOST: ${destIP}:1900
|
|
@@ -128,7 +131,7 @@ CACHE-CONTROL: max-age=3610
|
128
|
131
|
LOCATION: https://stratos.ikeran.org/upnp
|
129
|
132
|
NT: urn:stratos.ikeran.org:service:stratos:${stratosVersion}
|
130
|
133
|
NTS: keep-alive
|
131
|
|
-SERVER: ${os().to!string}/${osVersion} UPnP/1.1 stratos/${stratosVersion}
|
|
134
|
+SERVER: ${os.family}/${os.versionID} UPnP/1.1 stratos/${stratosVersion}
|
132
|
135
|
USN: ${randomUUID()}
|
133
|
136
|
BOOTID.UPNP.ORG: ${msgNumber}
|
134
|
137
|
CONFIGID.UPNP.ORG: ${configID}
|
|
@@ -143,28 +146,31 @@ STRATOS-NODENAME: ${name}
|
143
|
146
|
|
144
|
147
|
private void receivedUPnPPacket(string text)
|
145
|
148
|
{
|
146
|
|
- import core.time : seconds;
|
147
|
|
- import stratos.util : splitOnce;
|
148
|
|
- import std.algorithm.iteration : splitter, chunkBy, map;
|
149
|
|
- import std.conv : to;
|
150
|
|
- import std.range : drop;
|
151
|
|
- import std.string : strip, startsWith;
|
152
|
|
- import std.typecons : tuple;
|
|
149
|
+ import std.algorithm.iteration : splitter;
|
|
150
|
+ import std.string : strip;
|
153
|
151
|
|
154
|
152
|
string operation;
|
155
|
153
|
SSDPService service;
|
156
|
|
- foreach (line; lines)
|
|
154
|
+ foreach (line; text.splitter("\n"))
|
157
|
155
|
{
|
158
|
|
- readHeader(service, line);
|
|
156
|
+ readHeader(service, line.strip);
|
159
|
157
|
}
|
160
|
158
|
}
|
161
|
159
|
}
|
162
|
160
|
|
163
|
161
|
private void readHeader(ref UPnP.SSDPService service, string line)
|
164
|
162
|
{
|
|
163
|
+ import core.time : seconds;
|
|
164
|
+ import std.algorithm.iteration : splitter, filter;
|
|
165
|
+ import std.array : array;
|
|
166
|
+ import std.conv : to;
|
|
167
|
+ import std.string : strip;
|
|
168
|
+ import stratos.util : splitOnce;
|
|
169
|
+
|
165
|
170
|
auto tag = line.splitter(":").front;
|
166
|
171
|
auto value = line[tag.length + 1 .. $].strip;
|
167
|
172
|
tag = tag.strip;
|
|
173
|
+ string operation;
|
168
|
174
|
switch (tag)
|
169
|
175
|
{
|
170
|
176
|
case "NT":
|
|
@@ -184,7 +190,7 @@ private void readHeader(ref UPnP.SSDPService service, string line)
|
184
|
190
|
case "SERVER":
|
185
|
191
|
auto bits = value
|
186
|
192
|
.splitter(" ")
|
187
|
|
- .where(x => x.length)
|
|
193
|
+ .filter!(x => x.length)
|
188
|
194
|
.array;
|
189
|
195
|
auto osInfo = bits[0];
|
190
|
196
|
auto serviceVersionInfo = bits[$-1].splitOnce("/");
|
|
@@ -257,5 +263,4 @@ string[] getIPAddresses()
|
257
|
263
|
}
|
258
|
264
|
freeifaddrs(gotten);
|
259
|
265
|
return addresses;
|
260
|
|
-}
|
261
|
|
-+/
|
|
266
|
+}
|