dns_worker.js (8995B)
1 /* 2 ***** BEGIN LICENSE BLOCK ***** 3 4 Copyright © 2015 Center for History and New Media 5 George Mason University, Fairfax, Virginia, USA 6 http://zotero.org 7 8 This file is part of Zotero. 9 10 Zotero is free software: you can redistribute it and/or modify 11 it under the terms of the GNU Affero General Public License as published by 12 the Free Software Foundation, either version 3 of the License, or 13 (at your option) any later version. 14 15 Zotero is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 function getIPForLookup(ip) { 27 if (ip.indexOf(".") != -1) { 28 // IPv4 29 x = ip.split(".").reverse().join(".")+".in-addr.arpa"; 30 } else { 31 if (ip.indexOf("%") != -1) ip = ip.substr(0, ip.indexOf("%")); 32 // IPv6 33 var parts = ip.split(":"); 34 x = "ip6.arpa" 35 for (var i = 0; i < parts.length; i++) { 36 var part = parts[i]; 37 for (var j = 0; j < (part.length == 0 ? 4*(9-parts.length) : 4-part.length); j++) x = "0." + x; 38 for (var j = 0; j < part.length; j++) x = part[j] + "." + x; 39 } 40 } 41 return x; 42 } 43 44 function isLocalIP(ip) { 45 return ip.startsWith("169.254.") || ip.startsWith("192.168.") || ip.startsWith("10.") || 46 /^172\.(?:1[6-9]|2[0-9]|3[01])\./.test(ip) || 47 ip.startsWith("fe80:") || ip.startsWith("fd00:") || ip == ""; 48 } 49 50 onmessage = function (e) { 51 var libc, reverseLookup, getIPs, getnameinfo; 52 var sockaddr = new ctypes.StructType("sockaddr"); 53 platform = e.data; 54 55 if (platform == "win") { 56 libc = ctypes.open("Ws2_32.dll"); 57 var addrinfo = new ctypes.StructType("arrinfo"); 58 addrinfo.define([{"ai_flags":ctypes.int}, {"ai_family":ctypes.int}, {"ai_socktype":ctypes.int}, 59 {"ai_protocol":ctypes.int}, {"ai_addrlen":ctypes.int}, {"ai_canonname":ctypes.char.ptr}, 60 {"ai_addr":sockaddr.ptr}, {"ai_next":addrinfo.ptr}]); 61 var gethostname = libc.declare("gethostname", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.size_t); 62 var getaddrinfo = libc.declare("getaddrinfo", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.char.ptr, 63 addrinfo.ptr, addrinfo.ptr.ptr); 64 var freeaddrinfo = libc.declare("freeaddrinfo", ctypes.default_abi, ctypes.void_t, addrinfo.ptr); 65 getnameinfo = libc.declare("getnameinfo", ctypes.default_abi, ctypes.int, sockaddr.ptr, ctypes.int, 66 ctypes.char.ptr, ctypes.int, ctypes.char.ptr, ctypes.int, ctypes.int); 67 getIPs = function () { 68 var buf = new new ctypes.ArrayType(ctypes.char, 1025); 69 var status = gethostname(buf, 1025); 70 if (status != 0) throw new Error("could not get hostname: "+status); 71 72 var ips = []; 73 var out = new addrinfo.ptr(); 74 status = getaddrinfo(buf, null, null, out.address()); 75 if (status != 0) throw new Error("could not get addrinfo: "+status); 76 var rec = out; 77 try { 78 while (!rec.isNull()) { 79 status = getnameinfo(rec.contents.ai_addr, rec.contents.ai_addrlen, buf, 1025, null, 0, 2); 80 if (status != 0) throw new Error("could not get IP address: "+status); 81 var ip = buf.readString(); 82 if (!isLocalIP(ip)) ips.push(ip); 83 rec = rec.contents.ai_next; 84 } 85 } finally { 86 freeaddrinfo(out); 87 } 88 return ips; 89 }; 90 91 var dnsapi = ctypes.open("Dnsapi.dll"); 92 var DNS_RECORD = new ctypes.StructType("DNS_RECORD"); 93 DNS_RECORD.define([{"pNext":DNS_RECORD.ptr}, {"pName":ctypes.char.ptr}, {"wType":ctypes.unsigned_short}, 94 {"wDataLength":ctypes.unsigned_short}, {"DW":ctypes.unsigned_long}, {"dwTtl":ctypes.unsigned_long}, 95 {"dwReserved":ctypes.unsigned_long}, {"pNameHost":ctypes.char.ptr}]); 96 var DnsQuery = dnsapi.declare("DnsQuery_A", ctypes.winapi_abi, ctypes.int, ctypes.char.ptr, ctypes.unsigned_short, 97 ctypes.unsigned_long, ctypes.voidptr_t, DNS_RECORD.ptr, ctypes.voidptr_t); 98 var DnsRecordListFree = dnsapi.declare("DnsRecordListFree", ctypes.winapi_abi, ctypes.void_t, DNS_RECORD.ptr, 99 ctypes.int); 100 reverseLookup = function (ip) { 101 var record = new DNS_RECORD(); 102 var status = DnsQuery(getIPForLookup(ip), 12 /*DNS_TYPE_PTR*/, 32 /*DNS_QUERY_NO_LOCAL_NAME*/, null, record.address(), null); 103 if (status != 0 || record.pNext.isNull()) return null; 104 var retval = record.pNext.contents.pNameHost.readString(); 105 DnsRecordListFree(record.pNext, 1); 106 return retval; 107 }; 108 } else { 109 if (platform == "mac") { 110 libc = ctypes.open("libc.dylib"); 111 } else { 112 var possibleLibcs = [ 113 "libc.so.6", 114 "libc.so.6.1", 115 "libc.so" 116 ]; 117 for(var i = 0; i < possibleLibcs.length; i++) { 118 try { 119 libc = ctypes.open(possibleLibcs[i]); 120 break; 121 } catch(e) {} 122 } 123 } 124 125 var AF_INET = 2, AF_INET6, NI_NUMERICHOST, sockaddr_size, libresolv; 126 if (platform == "linux") { 127 libresolv = ctypes.open("libresolv.so"); 128 sockaddr.define([{"sa_family":ctypes.unsigned_short}]); 129 sockaddrSize = function (x) { return x.sa_family == 10 ? 28 : 16; }; 130 AF_INET6 = 10; 131 NI_NUMERICHOST = 1; 132 } else { 133 libresolv = libc; 134 sockaddr.define([{"sa_len":ctypes.uint8_t}, {"sa_family":ctypes.uint8_t}]); 135 sockaddrSize = function (x) { return x.sa_len; }; 136 AF_INET6 = 30; 137 NI_NUMERICHOST = 2; 138 } 139 140 var ifaddrs = new ctypes.StructType("ifaddrs"); 141 ifaddrs.define([{"ifa_next":ifaddrs.ptr}, {"ifa_name":ctypes.char.ptr}, {"ifa_flags":ctypes.unsigned_int}, 142 {"ifa_addr":sockaddr.ptr}]); 143 var getifaddrs = libc.declare("getifaddrs", ctypes.default_abi, ctypes.int, ifaddrs.ptr.ptr); 144 var freeifaddrs = libc.declare("freeifaddrs", ctypes.default_abi, ctypes.void_t, ifaddrs.ptr); 145 getnameinfo = libc.declare("getnameinfo", ctypes.default_abi, ctypes.int, sockaddr.ptr, ctypes.int, 146 ctypes.char.ptr, ctypes.int, ctypes.char.ptr, ctypes.int, ctypes.int); 147 getIPs = function () { 148 var buf = new new ctypes.ArrayType(ctypes.char, 1025); 149 var out = new ifaddrs.ptr(); 150 var status = getifaddrs(out.address()); 151 if (status != 0) throw new Error("could not get ifaddrs: "+status); 152 var ips = []; 153 var rec = out; 154 try { 155 while (!rec.isNull()) { 156 if (!rec.contents.ifa_name.readString().startsWith("lo")) { 157 var family = rec.contents.ifa_addr.contents.sa_family; 158 if (family == AF_INET || family == AF_INET6) { 159 status = getnameinfo(rec.contents.ifa_addr, sockaddrSize(rec.contents.ifa_addr.contents), 160 buf, 1025, null, 0, NI_NUMERICHOST); 161 if (status != 0) throw new Error("could not get IP address: "+status); 162 var ip = buf.readString(); 163 if (!isLocalIP(ip)) ips.push(ip); 164 } 165 } 166 rec = rec.contents.ifa_next; 167 } 168 } finally { 169 freeifaddrs(out); 170 } 171 return ips; 172 }; 173 174 var res_query; 175 try { 176 res_query = libresolv.declare("res_query", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.int, 177 ctypes.int, ctypes.uint8_t.ptr, ctypes.int); 178 } catch(e) { 179 res_query = libresolv.declare("__res_query", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.int, 180 ctypes.int, ctypes.uint8_t.ptr, ctypes.int); 181 } 182 let response = new new ctypes.ArrayType(ctypes.uint8_t, 1025); 183 var skipName = function(response, offset) { 184 var len = response[offset++]; 185 if ((len & 192) == 192) return offset+1; // compressed 186 while (len != 0) { 187 offset += len; 188 len = response[offset++]; 189 }; 190 return offset; 191 }; 192 var reverseLookup = function(ip) { 193 var len = res_query(getIPForLookup(ip), 1, 12, response, 1025); 194 if (len <= 0) return null; 195 196 var offset = 4; 197 var qdCount = (response[offset++] << 8) + response[offset++]; 198 var anCount = (response[offset++] << 8) + response[offset++]; 199 offset += 4; 200 for (var i=0; i<qdCount; i++) { 201 offset = skipName(response, offset)+4; 202 } 203 var domain = []; 204 if (anCount >= 1) { 205 offset = skipName(response, offset); 206 offset += 8; 207 var rdLength = (response[offset++] << 8) + response[offset++]; // RDLENGTH 208 var endOfData = offset+rdLength; 209 while(offset < endOfData) { 210 if(offset > endOfData) break; 211 var len = response[offset++]; 212 if(offset+len > endOfData) break; 213 var str = ""; 214 for(var i = 0; i < len; i++) { 215 str += String.fromCharCode(response[offset++]); 216 } 217 domain.push(str); 218 } 219 domain.pop(); 220 } 221 return domain.join(".") 222 }; 223 } 224 225 var ips = getIPs(); 226 var hosts = []; 227 for (var i = 0; i < ips.length; i++) { 228 var host = reverseLookup(ips[i]); 229 if(host) hosts.push(host); 230 } 231 232 postMessage(hosts); 233 };