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