cassandra-driver
Version:
DataStax Node.js Driver for Apache Cassandra
248 lines (232 loc) • 6.95 kB
JavaScript
/*
* 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;