ip_converter.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.IpConverter = void 0;
  4. const pvtsutils_1 = require("pvtsutils");
  5. class IpConverter {
  6. static isIPv4(ip) {
  7. return /^(\d{1,3}\.){3}\d{1,3}$/.test(ip);
  8. }
  9. static parseIPv4(ip) {
  10. const parts = ip.split(".");
  11. if (parts.length !== 4) {
  12. throw new Error("Invalid IPv4 address");
  13. }
  14. return parts.map((part) => {
  15. const num = parseInt(part, 10);
  16. if (isNaN(num) || num < 0 || num > 255) {
  17. throw new Error("Invalid IPv4 address part");
  18. }
  19. return num;
  20. });
  21. }
  22. static parseIPv6(ip) {
  23. const expandedIP = this.expandIPv6(ip);
  24. const parts = expandedIP.split(":");
  25. if (parts.length !== 8) {
  26. throw new Error("Invalid IPv6 address");
  27. }
  28. return parts.reduce((bytes, part) => {
  29. const num = parseInt(part, 16);
  30. if (isNaN(num) || num < 0 || num > 0xffff) {
  31. throw new Error("Invalid IPv6 address part");
  32. }
  33. bytes.push((num >> 8) & 0xff);
  34. bytes.push(num & 0xff);
  35. return bytes;
  36. }, []);
  37. }
  38. static expandIPv6(ip) {
  39. if (!ip.includes("::")) {
  40. return ip;
  41. }
  42. const parts = ip.split("::");
  43. if (parts.length > 2) {
  44. throw new Error("Invalid IPv6 address");
  45. }
  46. const left = parts[0] ? parts[0].split(":") : [];
  47. const right = parts[1] ? parts[1].split(":") : [];
  48. const missing = 8 - (left.length + right.length);
  49. if (missing < 0) {
  50. throw new Error("Invalid IPv6 address");
  51. }
  52. return [...left, ...Array(missing).fill("0"), ...right].join(":");
  53. }
  54. static formatIPv6(bytes) {
  55. const parts = [];
  56. for (let i = 0; i < 16; i += 2) {
  57. parts.push(((bytes[i] << 8) | bytes[i + 1]).toString(16));
  58. }
  59. return this.compressIPv6(parts.join(":"));
  60. }
  61. static compressIPv6(ip) {
  62. const parts = ip.split(":");
  63. let longestZeroStart = -1;
  64. let longestZeroLength = 0;
  65. let currentZeroStart = -1;
  66. let currentZeroLength = 0;
  67. for (let i = 0; i < parts.length; i++) {
  68. if (parts[i] === "0") {
  69. if (currentZeroStart === -1) {
  70. currentZeroStart = i;
  71. }
  72. currentZeroLength++;
  73. }
  74. else {
  75. if (currentZeroLength > longestZeroLength) {
  76. longestZeroStart = currentZeroStart;
  77. longestZeroLength = currentZeroLength;
  78. }
  79. currentZeroStart = -1;
  80. currentZeroLength = 0;
  81. }
  82. }
  83. if (currentZeroLength > longestZeroLength) {
  84. longestZeroStart = currentZeroStart;
  85. longestZeroLength = currentZeroLength;
  86. }
  87. if (longestZeroLength > 1) {
  88. const before = parts.slice(0, longestZeroStart).join(":");
  89. const after = parts.slice(longestZeroStart + longestZeroLength).join(":");
  90. return `${before}::${after}`;
  91. }
  92. return ip;
  93. }
  94. static parseCIDR(text) {
  95. const [addr, prefixStr] = text.split("/");
  96. const prefix = parseInt(prefixStr, 10);
  97. if (this.isIPv4(addr)) {
  98. if (prefix < 0 || prefix > 32) {
  99. throw new Error("Invalid IPv4 prefix length");
  100. }
  101. return [this.parseIPv4(addr), prefix];
  102. }
  103. else {
  104. if (prefix < 0 || prefix > 128) {
  105. throw new Error("Invalid IPv6 prefix length");
  106. }
  107. return [this.parseIPv6(addr), prefix];
  108. }
  109. }
  110. static decodeIP(value) {
  111. if (value.length === 64 && parseInt(value, 16) === 0) {
  112. return "::/0";
  113. }
  114. if (value.length !== 16) {
  115. return value;
  116. }
  117. const mask = parseInt(value.slice(8), 16)
  118. .toString(2)
  119. .split("")
  120. .reduce((a, k) => a + +k, 0);
  121. let ip = value.slice(0, 8).replace(/(.{2})/g, (match) => `${parseInt(match, 16)}.`);
  122. ip = ip.slice(0, -1);
  123. return `${ip}/${mask}`;
  124. }
  125. static toString(buf) {
  126. const uint8 = new Uint8Array(buf);
  127. if (uint8.length === 4) {
  128. return Array.from(uint8).join(".");
  129. }
  130. if (uint8.length === 16) {
  131. return this.formatIPv6(uint8);
  132. }
  133. if (uint8.length === 8 || uint8.length === 32) {
  134. const half = uint8.length / 2;
  135. const addrBytes = uint8.slice(0, half);
  136. const maskBytes = uint8.slice(half);
  137. const isAllZeros = uint8.every((byte) => byte === 0);
  138. if (isAllZeros) {
  139. return uint8.length === 8 ? "0.0.0.0/0" : "::/0";
  140. }
  141. const prefixLen = maskBytes.reduce((a, b) => a + (b.toString(2).match(/1/g) || []).length, 0);
  142. if (uint8.length === 8) {
  143. const addrStr = Array.from(addrBytes).join(".");
  144. return `${addrStr}/${prefixLen}`;
  145. }
  146. else {
  147. const addrStr = this.formatIPv6(addrBytes);
  148. return `${addrStr}/${prefixLen}`;
  149. }
  150. }
  151. return this.decodeIP(pvtsutils_1.Convert.ToHex(buf));
  152. }
  153. static fromString(text) {
  154. if (text.includes("/")) {
  155. const [addr, prefix] = this.parseCIDR(text);
  156. const maskBytes = new Uint8Array(addr.length);
  157. let bitsLeft = prefix;
  158. for (let i = 0; i < maskBytes.length; i++) {
  159. if (bitsLeft >= 8) {
  160. maskBytes[i] = 0xff;
  161. bitsLeft -= 8;
  162. }
  163. else if (bitsLeft > 0) {
  164. maskBytes[i] = 0xff << (8 - bitsLeft);
  165. bitsLeft = 0;
  166. }
  167. }
  168. const out = new Uint8Array(addr.length * 2);
  169. out.set(addr, 0);
  170. out.set(maskBytes, addr.length);
  171. return out.buffer;
  172. }
  173. const bytes = this.isIPv4(text) ? this.parseIPv4(text) : this.parseIPv6(text);
  174. return new Uint8Array(bytes).buffer;
  175. }
  176. }
  177. exports.IpConverter = IpConverter;