1 module ws.network.upnp;
2 
3 
4 version(WsUpnp){
5 
6 
7 	import
8 		ws.exception,
9 		ws.string,
10 		ws.io,
11 		ws.sys.library,
12 		ws.time,
13 		std.socket,
14 		core.sys.windows.winsock2;
15 
16 	//pragma(lib, "miniupnpc");
17 
18 	__gshared:
19 
20 
21 	class upnp {
22 
23 		enum: long {
24 			tcp = 1,
25 			udp = 1<<1
26 		}
27 
28 		static UPNPUrls urls;
29 		static IGDdatas data;
30 		static UPNPDev* devlist;
31 		static string addrInternal;
32 		static string addrExternal;
33 
34 		version(Windows)
35 			static bool wsaInitialized;
36 
37 		static void init(){
38 			
39 			version(Windows)
40 				if(!wsaInitialized){
41 					WSADATA wd;
42 					int val = WSAStartup(0x2020, &wd);
43 					wsaInitialized = true;
44 					if(val)
45 						exception("Unable to initialize socket library " ~ tostring(val));
46 				}
47 			
48 			FreeUPNPUrls(&urls);
49 			
50 			char[64] lanaddr;
51 			int r;
52 			int error = 0;
53 			devlist = upnpDiscover(2000, null, null, 0/*sameport*/, 0, &error);
54 			if(!devlist)
55 				exception("upnpDiscover() error: " ~ error.tostring());
56 			r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr.ptr, lanaddr.sizeof);
57 			if(!r || r != 1)
58 				exception(tostring("Could not find valid Internet Gateway Device (Error %)", r));
59 			addrInternal = lanaddr.tostring();
60 			char[40] externalIPAddress;
61 
62 			UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype.ptr, externalIPAddress.ptr);
63 			if(!externalIPAddress[0])
64 				throw new Exception("GetExternalIPAddress failed.");
65 			addrExternal = externalIPAddress.tostring();
66 		}
67 
68 
69 		static class forward {
70 			long external;
71 			long protocol;
72 
73 			double timeout;
74 
75 			@property bool valid(){
76 				return timeout > now;
77 			}
78 			
79 			this(long portIntern, long portExtern, long p){
80 				if(p & tcp) addPortMapping("TCP", portIntern, portExtern);
81 				if(p & udp) addPortMapping("UDP", portIntern, portExtern);
82 				timeout = now+7200;
83 			}
84 			
85 			void disable(){
86 				if(protocol & tcp) removeMapping("TCP", external);
87 				if(protocol & udp) removeMapping("UDP", external);
88 			}
89 			
90 		}
91 
92 
93 		static ~this(){
94 			FreeUPNPUrls(&urls);
95 		}
96 		
97 
98 		private:
99 		
100 			static void removeMapping(string pr, long e){
101 				int r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype.ptr, tostring(e).toStringz(), pr.toStringz(), null);
102 				writeln("UPNP_DeletePortMapping() returned : %", r);
103 			}
104 		
105 			static void addPortMapping(string pr, long i, long e, bool updated = false){
106 				char[40] intClient;
107 				char[6] intPort;
108 				char[16] duration;
109 				int r = UPNP_AddPortMapping(
110 					urls.controlURL, data.first.servicetype.ptr,
111 					tostring(e).toStringz(), tostring(i).toStringz(), addrInternal.toStringz(),
112 					null, pr.toStringz(), null, "7200"
113 				);
114 				if(r != UPNPCOMMAND_SUCCESS){
115 					if(updated){
116 						throw new Exception(tostring("AddPortMapping failed: % (%)", r, strupnperror(r)));
117 					}else{
118 						init();
119 						addPortMapping(pr, i, e, true);
120 					}
121 				}
122 				/+
123 				r = UPNP_GetSpecificPortMappingEntry(
124 					urls.controlURL, data.first.servicetype.ptr, tostring(e).toStringz,
125 					tostring(i).toStringz, intClient.ptr, intPort.ptr, null/*desc*/, null/*enabled*/, duration.ptr
126 				);
127 				if(r != UPNPCOMMAND_SUCCESS)
128 					//throw new Exception(tostring("GetSpecificPortMappingEntry failed: % (%)", r, strupnperror(r)));
129 					writeln("GetSpecificPortMappingEntry failed: % (%)", r, strupnperror(r));
130 				+/
131 			}
132 
133 	}
134 
135 
136 	extern(C):
137 
138 		const static auto MINIUPNPC_URL_MAXSIZE = 128;
139 		const static auto UPNPCOMMAND_SUCCESS = 0;
140 
141 		struct UPNPUrls {
142 			char* controlURL;
143 			char* ipcondescURL;
144 			char* controlURL_CIF;
145 			char* controlURL_6FC;
146 			char* rootdescURL;
147 		}
148 		
149 		struct IGDdatas_service {
150 			char[MINIUPNPC_URL_MAXSIZE] controlurl;
151 			char[MINIUPNPC_URL_MAXSIZE] eventsuburl;
152 			char[MINIUPNPC_URL_MAXSIZE] scpdurl;
153 			char[MINIUPNPC_URL_MAXSIZE] servicetype;
154 		}
155 			
156 		struct IGDdatas {
157 			char[MINIUPNPC_URL_MAXSIZE] cureltname;
158 			char[MINIUPNPC_URL_MAXSIZE] urlbase;
159 			char[MINIUPNPC_URL_MAXSIZE] presentationurl;
160 			int level;
161 			IGDdatas_service CIF;
162 			IGDdatas_service first;
163 			IGDdatas_service second;
164 			IGDdatas_service IPv6FC;
165 			IGDdatas_service tmp;
166 		}
167 		
168 		struct UPNPDev {
169 			UPNPDev* pNext;
170 			char * descURL;
171 			char * st;
172 			uint scope_id;
173 			char[2] buffer;
174 		}
175 		
176 		
177 		mixin library!(
178 			"miniupnpc", "miniupnpc",
179 			"strupnperror",
180 				"const(char*) function(int)",
181 			"upnpDiscover",
182 				"UPNPDev* function(int, const char*, const char*, int, int, int*)",
183 			"UPNP_GetValidIGD",
184 				"int function(UPNPDev*, UPNPUrls*, IGDdatas*, char*, int)",
185 			"UPNP_GetExternalIPAddress",
186 				"int function(const char*, const char*, char*)",
187 			"UPNP_AddPortMapping",
188 				"int function(const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*)",
189 			"UPNP_GetSpecificPortMappingEntry",
190 				"int function(const char*, const char*, const char*, const char*, char*, char*, char*, char*, char*)",
191 			"UPNP_DeletePortMapping",
192 				"int function(const char*, const char*, const char*, const char*, const char*)",
193 			"FreeUPNPUrls",
194 				"void function(UPNPUrls*)"
195 			
196 		);
197 
198 }