UNPKG

hostile

Version:

Simple /etc/hosts manipulation

182 lines (160 loc) 4.65 kB
/*! hostile. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */ var fs = require('fs') var once = require('once') var split = require('split') var through = require('@ljharb/through') var net = require('net') var WINDOWS = process.platform === 'win32' var EOL = WINDOWS ? '\r\n' : '\n' exports.HOSTS = WINDOWS ? 'C:/Windows/System32/drivers/etc/hosts' : '/etc/hosts' /** * Get a list of the lines that make up the filePath. If the * `preserveFormatting` parameter is true, then include comments, blank lines * and other non-host entries in the result. * * @param {boolean} preserveFormatting * @param {function(err, lines)=} cb */ exports.getFile = function (filePath, preserveFormatting, cb) { var lines = [] if (typeof cb !== 'function') { fs.readFileSync(filePath, { encoding: 'utf8' }).split(/\r?\n/).forEach(online) return lines } cb = once(cb) fs.createReadStream(filePath, { encoding: 'utf8' }) .pipe(split()) .pipe(through(online)) .on('close', function () { cb(null, lines) }) .on('error', cb) function online (line) { // Remove all comment text from the line var lineSansComments = line.replace(/#.*/, '') var matches = /^\s*?(.+?)\s+(.+?)\s*$/.exec(lineSansComments) if (matches && matches.length === 3) { // Found a hosts entry var ip = matches[1] var host = matches[2] lines.push([ip, host]) } else { // Found a comment, blank line, or something else if (preserveFormatting) { lines.push(line) } } } } /** * Wrapper of `getFile` for getting a list of lines in the Host file * * @param {boolean} preserveFormatting * @param {function(err, lines)=} cb */ exports.get = function (preserveFormatting, cb) { return exports.getFile(exports.HOSTS, preserveFormatting, cb) } /** * Add a rule to /etc/hosts. If the rule already exists, then this does nothing. * * @param {string} ip * @param {string} host * @param {function(Error)=} cb */ exports.set = function (ip, host, cb) { var didUpdate = false if (typeof cb !== 'function') { return _set(exports.get(true)) } exports.get(true, function (err, lines) { if (err) return cb(err) _set(lines) }) function _set (lines) { // Try to update entry, if host already exists in file lines = lines.map(mapFunc) // If entry did not exist, let's add it if (!didUpdate) { // If the last line is empty, or just whitespace, then insert the new entry // right before it var lastLine = lines[lines.length - 1] if (typeof lastLine === 'string' && /\s*/.test(lastLine)) { lines.splice(lines.length - 1, 0, [ip, host]) } else { lines.push([ip, host]) } } exports.writeFile(lines, cb) } function mapFunc (line) { // replace a line if both hostname and ip version of the address matches if (Array.isArray(line) && line[1] === host && net.isIP(line[0]) === net.isIP(ip)) { line[0] = ip didUpdate = true } return line } } /** * Remove a rule from /etc/hosts. If the rule does not exist, then this does * nothing. * * @param {string} ip * @param {string} host * @param {function(Error)=} cb */ exports.remove = function (ip, host, cb) { if (typeof cb !== 'function') { return _remove(exports.get(true)) } exports.get(true, function (err, lines) { if (err) return cb(err) _remove(lines) }) function _remove (lines) { // Try to remove entry, if it exists lines = lines.filter(filterFunc) return exports.writeFile(lines, cb) } function filterFunc (line) { return !(Array.isArray(line) && line[0] === ip && line[1] === host) } } /** * Write out an array of lines to the host file. Assumes that they're in the * format that `get` returns. * * @param {Array.<string|Array.<string>>} lines * @param {function(Error)=} cb */ exports.writeFile = function (lines, cb) { lines = lines.map(function (line, lineNum) { if (Array.isArray(line)) { line = line[0] + ' ' + line[1] } return line + (lineNum === lines.length - 1 ? '' : EOL) }) if (typeof cb !== 'function') { var stat = fs.statSync(exports.HOSTS) fs.writeFileSync(exports.HOSTS, lines.join(''), { mode: stat.mode }) return true } cb = once(cb) fs.stat(exports.HOSTS, function (err, stat) { if (err) { return cb(err) } var s = fs.createWriteStream(exports.HOSTS, { mode: stat.mode }) s.on('close', cb) s.on('error', cb) lines.forEach(function (data) { s.write(data) }) s.end() }) }