dnssd
Version:
Bonjour/Avahi-like service discovery in pure JavaScript
173 lines (139 loc) • 5.2 kB
JavaScript
;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var ValidationError = require('./customError').create('ValidationError');
function isNumeric(value) {
return !Number.isNaN(parseFloat(value)) && Number.isFinite(value);
}
/**
* Exported
*/
var validate = module.exports = {};
/**
* Validates a transport protocol, throws err on invalid input
* @param {string} str
*/
validate.protocol = function protocol(str) {
if (typeof str !== 'string') {
throw new ValidationError('Protocol must be a string, got %s', typeof str === 'undefined' ? 'undefined' : _typeof(str));
}
if (str === '' || str !== '_tcp' && str !== '_udp') {
throw new ValidationError("Protocol must be _tcp or _udp, got '%s'", str);
}
};
/**
* Validates a service name, throws err on invalid input
* @param {string} str
*/
validate.serviceName = function serviceName(str) {
if (typeof str !== 'string') {
throw new ValidationError('Service name must be a string, got %s', typeof str === 'undefined' ? 'undefined' : _typeof(str));
}
if (!str) {
throw new ValidationError("Service name can't be an empty string");
}
if (!/^_/.test(str)) {
throw new ValidationError("Service '%s' must start with '_'", str);
}
// 15 bytes not including the leading underscore
if (Buffer.byteLength(str) > 16) {
throw new ValidationError("Service '%s' is > 15 bytes", str);
}
if (!/^_[A-Za-z0-9]/.test(str) || !/[A-Za-z0-9]*$/.test(str)) {
throw new ValidationError("Service '%s' must start and end with a letter or digit", str);
}
if (!/^_[A-Za-z0-9-]+$/.test(str)) {
throw new ValidationError("Service '%s' should be only letters, digits, and hyphens", str);
}
if (/--/.test(str)) {
throw new ValidationError("Service '%s' must not have consecutive hyphens", str);
}
if (!/[A-Za-z]/.test(str)) {
throw new ValidationError("Service '%s' must have at least 1 letter", str);
}
};
/**
* Validates a dns label, throws err on invalid input
*
* @param {string} str - label to validate
* @param {string} [name] - name of the label (for better error messages)
*/
validate.label = function label(str) {
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'label';
if (typeof str !== 'string') {
throw new ValidationError('%s name must be a string, got %s', name, typeof str === 'undefined' ? 'undefined' : _typeof(str));
}
if (!str) {
throw new ValidationError("%s name can't be an empty string", name);
}
if (/[\x00-\x1F]|\x7F/.test(str)) {
throw new ValidationError("%s name '%s' can't contain control chars", name, str);
}
if (Buffer.byteLength(str) > 63) {
throw new ValidationError('%s must be <= 63 bytes. %s is %d', name, str, Buffer.byteLength(str));
}
};
/**
* Validates a port, throws err on invalid input
*
* @param {integer} num
*/
validate.port = function port(num) {
if (!Number.isInteger(num) || num <= 0 || num > 0xFFFF) {
throw new ValidationError('Port must be an integer between 0 and 65535, got %s', num);
}
};
/**
* Validates rdata for a TXT record, throws err on invalid input
*
* Example of a valid txt object:
* {
* key: 'value',
* buf: Buffer.alloc(123)
* }
*
* @param {object} obj
*/
validate.txt = function txt(obj) {
var sizeTotal = 0;
var keys = new Set();
if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) !== 'object') {
throw new ValidationError('TXT must be an object');
}
// validate each key value pair
Object.keys(obj).forEach(function (key) {
var value = obj[key];
var size = Buffer.byteLength(key);
// keys
if (Buffer.byteLength(key) > 9) {
throw new ValidationError("Key '%s' in TXT is > 9 chars", key);
}
if (!!~key.indexOf('=')) {
throw new ValidationError("Key '%s' in TXT contains a '='", key);
}
if (!/^[ -~]*$/.test(key)) {
throw new ValidationError("Key '%s' in TXT is not printable ascii", key);
}
if (keys.has(key.toLowerCase())) {
throw new ValidationError("Key '%s' in TXT occurs more than once. (case insensitive)", key);
}
keys.add(key.toLowerCase());
// value type
if (typeof value !== 'string' && typeof value !== 'boolean' && !isNumeric(value) && !Buffer.isBuffer(value)) {
throw new ValidationError('TXT values must be a string, buffer, number, or boolean. got %s', typeof value === 'undefined' ? 'undefined' : _typeof(value));
}
// size limits
if (typeof value !== 'boolean') {
size += Buffer.isBuffer(value) ? value.length : Buffer.byteLength(value.toString());
// add 1 for the '=' in 'key=value'
// add 1 for the length byte to be written before 'key=value'
size += 2;
}
sizeTotal += size;
if (size > 255) {
throw new ValidationError('Each key/value in TXT must be < 255 bytes');
}
if (sizeTotal > 1300) {
throw new ValidationError('TXT record is > 1300 bytes.');
}
});
};