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 );