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 }