UNPKG

cassandra-driver

Version:

DataStax Node.js Driver for Apache Cassandra

248 lines (232 loc) 6.95 kB
/* * Copyright DataStax, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; const utils = require('../utils'); /** @module types */ /** * Creates a new instance of InetAddress * @class * @classdesc Represents an v4 or v6 Internet Protocol (IP) address. * @param {Buffer} buffer * @constructor */ function InetAddress(buffer) { if (!(buffer instanceof Buffer) || (buffer.length !== 4 && buffer.length !== 16)) { throw new TypeError('The ip address must contain 4 or 16 bytes'); } /** * Immutable buffer that represents the IP address * @type Array */ this.buffer = buffer; /** * Returns the length of the underlying buffer * @type Number */ this.length = buffer.length; /** * Returns the Ip version (4 or 6) * @type Number */ this.version = buffer.length === 4 ? 4 : 6; } /** * Parses the string representation and returns an Ip address * @param {String} value */ InetAddress.fromString = function (value) { if (!value) { return new InetAddress(utils.allocBufferFromArray([0, 0, 0, 0])); } const ipv4Pattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; const ipv6Pattern = /^[\da-f:.]+$/i; let parts; if (ipv4Pattern.test(value)) { parts = value.split('.'); return new InetAddress(utils.allocBufferFromArray(parts)); } if (!ipv6Pattern.test(value)) { throw new TypeError('Value could not be parsed as InetAddress: ' + value); } parts = value.split(':'); if (parts.length < 3) { throw new TypeError('Value could not be parsed as InetAddress: ' + value); } const buffer = utils.allocBufferUnsafe(16); let filling = 8 - parts.length + 1; let applied = false; let offset = 0; const embeddedIp4 = ipv4Pattern.test(parts[parts.length - 1]); if (embeddedIp4) { // Its IPv6 address with an embedded IPv4 address: // subtract 1 from the potential empty filling as ip4 contains 4 bytes instead of 2 of a ipv6 section filling -= 1; } function writeItem(uIntValue) { buffer.writeUInt8(+uIntValue, offset++); } for (let i = 0; i < parts.length; i++) { const item = parts[i]; if (item) { if (embeddedIp4 && i === parts.length - 1) { item.split('.').forEach(writeItem); break; } buffer.writeUInt16BE(parseInt(item, 16), offset); offset = offset + 2; continue; } //its an empty string if (applied) { //there could be 2 occurrences of empty string filling = 1; } applied = true; for (let j = 0; j < filling; j++) { buffer[offset++] = 0; buffer[offset++] = 0; } } if (embeddedIp4 && !isValidIPv4Mapped(buffer)) { throw new TypeError('Only IPv4-Mapped IPv6 addresses are allowed as IPv6 address with embedded IPv4 address'); } return new InetAddress(buffer); }; /** * Compares 2 addresses and returns true if the underlying bytes are the same * @param {InetAddress} other * @returns {Boolean} */ InetAddress.prototype.equals = function (other) { if (!(other instanceof InetAddress)) { return false; } return (this.buffer.length === other.buffer.length && this.buffer.toString('hex') === other.buffer.toString('hex')); }; /** * Returns the underlying buffer * @returns {Buffer} */ InetAddress.prototype.getBuffer = function () { return this.buffer; }; /** * Provide the name of the constructor and the string representation * @returns {string} */ InetAddress.prototype.inspect = function () { return this.constructor.name + ': ' + this.toString(); }; /** * Returns the string representation of the IP address. * <p>For v4 IP addresses, a string in the form of d.d.d.d is returned.</p> * <p> * For v6 IP addresses, a string in the form of x:x:x:x:x:x:x:x is returned, where the 'x's are the hexadecimal * values of the eight 16-bit pieces of the address, according to rfc5952. * In cases where there is more than one field of only zeros, it can be shortened. For example, 2001:0db8:0:0:0:1:0:1 * will be expressed as 2001:0db8::1:0:1. * </p> * @param {String} [encoding] * @returns {String} */ InetAddress.prototype.toString = function (encoding) { if (encoding === 'hex') { //backward compatibility: behave in the same way as the buffer return this.buffer.toString('hex'); } if (this.buffer.length === 4) { return ( this.buffer[0] + '.' + this.buffer[1] + '.' + this.buffer[2] + '.' + this.buffer[3] ); } let start = -1; const longest = { length: 0, start: -1}; function checkLongest (i) { if (start >= 0) { //close the group const length = i - start; if (length > longest.length) { longest.length = length; longest.start = start; start = -1; } } } //get the longest 16-bit group of zeros for (let i = 0; i < this.buffer.length; i = i + 2) { if (this.buffer[i] === 0 && this.buffer[i + 1] === 0) { //its a group of zeros if (start < 0) { start = i; } // at the end of the buffer, make a final call to checkLongest. if(i === this.buffer.length - 2) { checkLongest(i+2); } continue; } //its a group of non-zeros checkLongest(i); } let address = ''; for (let h = 0; h < this.buffer.length; h = h + 2) { if (h === longest.start) { address += ':'; continue; } if (h < (longest.start + longest.length) && h > longest.start) { //its a group of zeros continue; } if (address.length > 0) { address += ':'; } address += ((this.buffer[h] << 8) | this.buffer[h+1]).toString(16); } if (address.charAt(address.length-1) === ':') { address += ':'; } return address; }; /** * Returns the string representation. * Method used by the native JSON.stringify() to serialize this instance. */ InetAddress.prototype.toJSON = function () { return this.toString(); }; /** * Validates for a IPv4-Mapped IPv6 according to https://tools.ietf.org/html/rfc4291#section-2.5.5 * @private * @param {Buffer} buffer */ function isValidIPv4Mapped(buffer) { // check the form // | 80 bits | 16 | 32 bits // +----------------+----+------------- // |0000........0000|FFFF| IPv4 address for (let i = 0; i < buffer.length - 6; i++) { if (buffer[i] !== 0) { return false; } } return !(buffer[10] !== 255 || buffer[11] !== 255); } module.exports = InetAddress;