updated sys and created publi
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
||||
.idea
|
||||
*.iml
|
||||
npm-debug.log
|
||||
dump.rdb
|
||||
node_modules
|
||||
results.tap
|
||||
results.xml
|
||||
npm-shrinkwrap.json
|
||||
config.json
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
*/*/.DS_Store
|
||||
._*
|
||||
*/._*
|
||||
*/*/._*
|
||||
coverage.*
|
||||
lib-cov
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 0.10
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2012-2014, Eran Hammer and other contributors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
* * *
|
||||
|
||||
The complete list of contributors can be found at: https://github.com/hueniverse/sntp/graphs/contributors
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
test:
|
||||
@node node_modules/lab/bin/lab
|
||||
test-cov:
|
||||
@node node_modules/lab/bin/lab -t 100 -m 3000
|
||||
test-cov-html:
|
||||
@node node_modules/lab/bin/lab -r html -o coverage.html
|
||||
|
||||
.PHONY: test test-cov test-cov-html
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# sntp
|
||||
|
||||
An SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time
|
||||
along with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset
|
||||
to the local time.
|
||||
|
||||
[](http://travis-ci.org/hueniverse/sntp)
|
||||
|
||||
# Usage
|
||||
|
||||
```javascript
|
||||
var Sntp = require('sntp');
|
||||
|
||||
// All options are optional
|
||||
|
||||
var options = {
|
||||
host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org
|
||||
port: 123, // Defaults to 123 (NTP)
|
||||
resolveReference: true, // Default to false (not resolving)
|
||||
timeout: 1000 // Defaults to zero (no timeout)
|
||||
};
|
||||
|
||||
// Request server time
|
||||
|
||||
Sntp.time(options, function (err, time) {
|
||||
|
||||
if (err) {
|
||||
console.log('Failed: ' + err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('Local clock is off by: ' + time.t + ' milliseconds');
|
||||
process.exit(0);
|
||||
});
|
||||
```
|
||||
|
||||
If an application needs to maintain continuous time synchronization, the module provides a stateful method for
|
||||
querying the current offset only when the last one is too old (defaults to daily).
|
||||
|
||||
```javascript
|
||||
// Request offset once
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
console.log(offset); // New (served fresh)
|
||||
|
||||
// Request offset again
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
console.log(offset); // Identical (served from cache)
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
To set a background offset refresh, start the interval and use the provided now() method. If for any reason the
|
||||
client fails to obtain an up-to-date offset, the current system clock is used.
|
||||
|
||||
```javascript
|
||||
var before = Sntp.now(); // System time without offset
|
||||
|
||||
Sntp.start(function () {
|
||||
|
||||
var now = Sntp.now(); // With offset
|
||||
Sntp.stop();
|
||||
});
|
||||
```
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
var Sntp = require('../lib');
|
||||
|
||||
// Request offset once
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
console.log(offset); // New (served fresh)
|
||||
|
||||
// Request offset again
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
console.log(offset); // Identical (served from cache)
|
||||
});
|
||||
});
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
var Sntp = require('../lib');
|
||||
|
||||
// All options are optional
|
||||
|
||||
var options = {
|
||||
host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org
|
||||
port: 123, // Defaults to 123 (NTP)
|
||||
resolveReference: true, // Default to false (not resolving)
|
||||
timeout: 1000 // Defaults to zero (no timeout)
|
||||
};
|
||||
|
||||
// Request server time
|
||||
|
||||
Sntp.time(options, function (err, time) {
|
||||
|
||||
if (err) {
|
||||
console.log('Failed: ' + err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(time);
|
||||
console.log('Local clock is off by: ' + time.t + ' milliseconds');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
module.exports = require('./lib');
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
// Load modules
|
||||
|
||||
var Dgram = require('dgram');
|
||||
var Dns = require('dns');
|
||||
var Hoek = require('hoek');
|
||||
|
||||
|
||||
// Declare internals
|
||||
|
||||
var internals = {};
|
||||
|
||||
|
||||
exports.time = function (options, callback) {
|
||||
|
||||
if (arguments.length !== 2) {
|
||||
callback = arguments[0];
|
||||
options = {};
|
||||
}
|
||||
|
||||
var settings = Hoek.clone(options);
|
||||
settings.host = settings.host || 'pool.ntp.org';
|
||||
settings.port = settings.port || 123;
|
||||
settings.resolveReference = settings.resolveReference || false;
|
||||
|
||||
// Declare variables used by callback
|
||||
|
||||
var timeoutId = 0;
|
||||
var sent = 0;
|
||||
|
||||
// Ensure callback is only called once
|
||||
|
||||
var finish = function (err, result) {
|
||||
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
|
||||
socket.removeAllListeners();
|
||||
socket.once('error', internals.ignore);
|
||||
socket.close();
|
||||
return callback(err, result);
|
||||
};
|
||||
|
||||
finish = Hoek.once(finish);
|
||||
|
||||
// Create UDP socket
|
||||
|
||||
var socket = Dgram.createSocket('udp4');
|
||||
|
||||
socket.once('error', function (err) {
|
||||
|
||||
return finish(err);
|
||||
});
|
||||
|
||||
// Listen to incoming messages
|
||||
|
||||
socket.on('message', function (buffer, rinfo) {
|
||||
|
||||
var received = Date.now();
|
||||
|
||||
var message = new internals.NtpMessage(buffer);
|
||||
if (!message.isValid) {
|
||||
return finish(new Error('Invalid server response'), message);
|
||||
}
|
||||
|
||||
if (message.originateTimestamp !== sent) {
|
||||
return finish(new Error('Wrong originate timestamp'), message);
|
||||
}
|
||||
|
||||
// Timestamp Name ID When Generated
|
||||
// ------------------------------------------------------------
|
||||
// Originate Timestamp T1 time request sent by client
|
||||
// Receive Timestamp T2 time request received by server
|
||||
// Transmit Timestamp T3 time reply sent by server
|
||||
// Destination Timestamp T4 time reply received by client
|
||||
//
|
||||
// The roundtrip delay d and system clock offset t are defined as:
|
||||
//
|
||||
// d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2
|
||||
|
||||
var T1 = message.originateTimestamp;
|
||||
var T2 = message.receiveTimestamp;
|
||||
var T3 = message.transmitTimestamp;
|
||||
var T4 = received;
|
||||
|
||||
message.d = (T4 - T1) - (T3 - T2);
|
||||
message.t = ((T2 - T1) + (T3 - T4)) / 2;
|
||||
message.receivedLocally = received;
|
||||
|
||||
if (!settings.resolveReference ||
|
||||
message.stratum !== 'secondary') {
|
||||
|
||||
return finish(null, message);
|
||||
}
|
||||
|
||||
// Resolve reference IP address
|
||||
|
||||
Dns.reverse(message.referenceId, function (err, domains) {
|
||||
|
||||
if (/* $lab:coverage:off$ */ !err /* $lab:coverage:on$ */) {
|
||||
message.referenceHost = domains[0];
|
||||
}
|
||||
|
||||
return finish(null, message);
|
||||
});
|
||||
});
|
||||
|
||||
// Set timeout
|
||||
|
||||
if (settings.timeout) {
|
||||
timeoutId = setTimeout(function () {
|
||||
|
||||
timeoutId = 0;
|
||||
return finish(new Error('Timeout'));
|
||||
}, settings.timeout);
|
||||
}
|
||||
|
||||
// Construct NTP message
|
||||
|
||||
var message = new Buffer(48);
|
||||
for (var i = 0; i < 48; i++) { // Zero message
|
||||
message[i] = 0;
|
||||
}
|
||||
|
||||
message[0] = (0 << 6) + (4 << 3) + (3 << 0) // Set version number to 4 and Mode to 3 (client)
|
||||
sent = Date.now();
|
||||
internals.fromMsecs(sent, message, 40); // Set transmit timestamp (returns as originate)
|
||||
|
||||
// Send NTP request
|
||||
|
||||
socket.send(message, 0, message.length, settings.port, settings.host, function (err, bytes) {
|
||||
|
||||
if (err ||
|
||||
bytes !== 48) {
|
||||
|
||||
return finish(err || new Error('Could not send entire message'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
internals.NtpMessage = function (buffer) {
|
||||
|
||||
this.isValid = false;
|
||||
|
||||
// Validate
|
||||
|
||||
if (buffer.length !== 48) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leap indicator
|
||||
|
||||
var li = (buffer[0] >> 6);
|
||||
switch (li) {
|
||||
case 0: this.leapIndicator = 'no-warning'; break;
|
||||
case 1: this.leapIndicator = 'last-minute-61'; break;
|
||||
case 2: this.leapIndicator = 'last-minute-59'; break;
|
||||
case 3: this.leapIndicator = 'alarm'; break;
|
||||
}
|
||||
|
||||
// Version
|
||||
|
||||
var vn = ((buffer[0] & 0x38) >> 3);
|
||||
this.version = vn;
|
||||
|
||||
// Mode
|
||||
|
||||
var mode = (buffer[0] & 0x7);
|
||||
switch (mode) {
|
||||
case 1: this.mode = 'symmetric-active'; break;
|
||||
case 2: this.mode = 'symmetric-passive'; break;
|
||||
case 3: this.mode = 'client'; break;
|
||||
case 4: this.mode = 'server'; break;
|
||||
case 5: this.mode = 'broadcast'; break;
|
||||
case 0:
|
||||
case 6:
|
||||
case 7: this.mode = 'reserved'; break;
|
||||
}
|
||||
|
||||
// Stratum
|
||||
|
||||
var stratum = buffer[1];
|
||||
if (stratum === 0) {
|
||||
this.stratum = 'death';
|
||||
}
|
||||
else if (stratum === 1) {
|
||||
this.stratum = 'primary';
|
||||
}
|
||||
else if (stratum <= 15) {
|
||||
this.stratum = 'secondary';
|
||||
}
|
||||
else {
|
||||
this.stratum = 'reserved';
|
||||
}
|
||||
|
||||
// Poll interval (msec)
|
||||
|
||||
this.pollInterval = Math.round(Math.pow(2, buffer[2])) * 1000;
|
||||
|
||||
// Precision (msecs)
|
||||
|
||||
this.precision = Math.pow(2, buffer[3]) * 1000;
|
||||
|
||||
// Root delay (msecs)
|
||||
|
||||
var rootDelay = 256 * (256 * (256 * buffer[4] + buffer[5]) + buffer[6]) + buffer[7];
|
||||
this.rootDelay = 1000 * (rootDelay / 0x10000);
|
||||
|
||||
// Root dispersion (msecs)
|
||||
|
||||
this.rootDispersion = ((buffer[8] << 8) + buffer[9] + ((buffer[10] << 8) + buffer[11]) / Math.pow(2, 16)) * 1000;
|
||||
|
||||
// Reference identifier
|
||||
|
||||
this.referenceId = '';
|
||||
switch (this.stratum) {
|
||||
case 'death':
|
||||
case 'primary':
|
||||
this.referenceId = String.fromCharCode(buffer[12]) + String.fromCharCode(buffer[13]) + String.fromCharCode(buffer[14]) + String.fromCharCode(buffer[15]);
|
||||
break;
|
||||
case 'secondary':
|
||||
this.referenceId = '' + buffer[12] + '.' + buffer[13] + '.' + buffer[14] + '.' + buffer[15];
|
||||
break;
|
||||
}
|
||||
|
||||
// Reference timestamp
|
||||
|
||||
this.referenceTimestamp = internals.toMsecs(buffer, 16);
|
||||
|
||||
// Originate timestamp
|
||||
|
||||
this.originateTimestamp = internals.toMsecs(buffer, 24);
|
||||
|
||||
// Receive timestamp
|
||||
|
||||
this.receiveTimestamp = internals.toMsecs(buffer, 32);
|
||||
|
||||
// Transmit timestamp
|
||||
|
||||
this.transmitTimestamp = internals.toMsecs(buffer, 40);
|
||||
|
||||
// Validate
|
||||
|
||||
if (this.version === 4 &&
|
||||
this.stratum !== 'reserved' &&
|
||||
this.mode === 'server' &&
|
||||
this.originateTimestamp &&
|
||||
this.receiveTimestamp &&
|
||||
this.transmitTimestamp) {
|
||||
|
||||
this.isValid = true;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
internals.toMsecs = function (buffer, offset) {
|
||||
|
||||
var seconds = 0;
|
||||
var fraction = 0;
|
||||
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
seconds = (seconds * 256) + buffer[offset + i];
|
||||
}
|
||||
|
||||
for (i = 4; i < 8; ++i) {
|
||||
fraction = (fraction * 256) + buffer[offset + i];
|
||||
}
|
||||
|
||||
return ((seconds - 2208988800 + (fraction / Math.pow(2, 32))) * 1000);
|
||||
};
|
||||
|
||||
|
||||
internals.fromMsecs = function (ts, buffer, offset) {
|
||||
|
||||
var seconds = Math.floor(ts / 1000) + 2208988800;
|
||||
var fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32));
|
||||
|
||||
buffer[offset + 0] = (seconds & 0xFF000000) >> 24;
|
||||
buffer[offset + 1] = (seconds & 0x00FF0000) >> 16;
|
||||
buffer[offset + 2] = (seconds & 0x0000FF00) >> 8;
|
||||
buffer[offset + 3] = (seconds & 0x000000FF);
|
||||
|
||||
buffer[offset + 4] = (fraction & 0xFF000000) >> 24;
|
||||
buffer[offset + 5] = (fraction & 0x00FF0000) >> 16;
|
||||
buffer[offset + 6] = (fraction & 0x0000FF00) >> 8;
|
||||
buffer[offset + 7] = (fraction & 0x000000FF);
|
||||
};
|
||||
|
||||
|
||||
// Offset singleton
|
||||
|
||||
internals.last = {
|
||||
offset: 0,
|
||||
expires: 0,
|
||||
host: '',
|
||||
port: 0
|
||||
};
|
||||
|
||||
|
||||
exports.offset = function (options, callback) {
|
||||
|
||||
if (arguments.length !== 2) {
|
||||
callback = arguments[0];
|
||||
options = {};
|
||||
}
|
||||
|
||||
var now = Date.now();
|
||||
var clockSyncRefresh = options.clockSyncRefresh || 24 * 60 * 60 * 1000; // Daily
|
||||
|
||||
if (internals.last.offset &&
|
||||
internals.last.host === options.host &&
|
||||
internals.last.port === options.port &&
|
||||
now < internals.last.expires) {
|
||||
|
||||
process.nextTick(function () {
|
||||
|
||||
callback(null, internals.last.offset);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
exports.time(options, function (err, time) {
|
||||
|
||||
if (err) {
|
||||
return callback(err, 0);
|
||||
}
|
||||
|
||||
internals.last = {
|
||||
offset: Math.round(time.t),
|
||||
expires: now + clockSyncRefresh,
|
||||
host: options.host,
|
||||
port: options.port
|
||||
};
|
||||
|
||||
return callback(null, internals.last.offset);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Now singleton
|
||||
|
||||
internals.now = {
|
||||
intervalId: 0
|
||||
};
|
||||
|
||||
|
||||
exports.start = function (options, callback) {
|
||||
|
||||
if (arguments.length !== 2) {
|
||||
callback = arguments[0];
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (internals.now.intervalId) {
|
||||
process.nextTick(function () {
|
||||
|
||||
callback();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
exports.offset(options, function (err, offset) {
|
||||
|
||||
internals.now.intervalId = setInterval(function () {
|
||||
|
||||
exports.offset(options, function () { });
|
||||
}, options.clockSyncRefresh || 24 * 60 * 60 * 1000); // Daily
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports.stop = function () {
|
||||
|
||||
if (!internals.now.intervalId) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(internals.now.intervalId);
|
||||
internals.now.intervalId = 0;
|
||||
};
|
||||
|
||||
|
||||
exports.isLive = function () {
|
||||
|
||||
return !!internals.now.intervalId;
|
||||
};
|
||||
|
||||
|
||||
exports.now = function () {
|
||||
|
||||
var now = Date.now();
|
||||
if (!exports.isLive() ||
|
||||
now >= internals.last.expires) {
|
||||
|
||||
return now;
|
||||
}
|
||||
|
||||
return now + internals.last.offset;
|
||||
};
|
||||
|
||||
|
||||
internals.ignore = function () {
|
||||
|
||||
};
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
{
|
||||
"raw": "sntp@1.x.x",
|
||||
"scope": null,
|
||||
"escapedName": "sntp",
|
||||
"name": "sntp",
|
||||
"rawSpec": "1.x.x",
|
||||
"spec": ">=1.0.0 <2.0.0",
|
||||
"type": "range"
|
||||
},
|
||||
"/mnt/Data/bach/Documents/ola/OLA#4/OLA#4DOC/sys/node_modules/hawk"
|
||||
]
|
||||
],
|
||||
"_from": "sntp@>=1.0.0 <2.0.0",
|
||||
"_id": "sntp@1.0.9",
|
||||
"_inCache": true,
|
||||
"_location": "/sntp",
|
||||
"_npmUser": {
|
||||
"name": "hueniverse",
|
||||
"email": "eran@hueniverse.com"
|
||||
},
|
||||
"_npmVersion": "1.4.23",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"raw": "sntp@1.x.x",
|
||||
"scope": null,
|
||||
"escapedName": "sntp",
|
||||
"name": "sntp",
|
||||
"rawSpec": "1.x.x",
|
||||
"spec": ">=1.0.0 <2.0.0",
|
||||
"type": "range"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/hawk"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
|
||||
"_shasum": "6541184cc90aeea6c6e7b35e2659082443c66198",
|
||||
"_shrinkwrap": null,
|
||||
"_spec": "sntp@1.x.x",
|
||||
"_where": "/mnt/Data/bach/Documents/ola/OLA#4/OLA#4DOC/sys/node_modules/hawk",
|
||||
"author": {
|
||||
"name": "Eran Hammer",
|
||||
"email": "eran@hammer.io",
|
||||
"url": "http://hueniverse.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/hueniverse/sntp/issues"
|
||||
},
|
||||
"contributors": [],
|
||||
"dependencies": {
|
||||
"hoek": "2.x.x"
|
||||
},
|
||||
"description": "SNTP Client",
|
||||
"devDependencies": {
|
||||
"lab": "4.x.x"
|
||||
},
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"shasum": "6541184cc90aeea6c6e7b35e2659082443c66198",
|
||||
"tarball": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"gitHead": "ee2e35284f684609990681734d39010cd356d7da",
|
||||
"homepage": "https://github.com/hueniverse/sntp#readme",
|
||||
"keywords": [
|
||||
"sntp",
|
||||
"ntp",
|
||||
"time"
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD",
|
||||
"url": "http://github.com/hueniverse/sntp/raw/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"main": "index",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "hueniverse",
|
||||
"email": "eran@hueniverse.com"
|
||||
}
|
||||
],
|
||||
"name": "sntp",
|
||||
"optionalDependencies": {},
|
||||
"readme": "# sntp\n\nAn SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time\nalong with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset\nto the local time.\n\n[](http://travis-ci.org/hueniverse/sntp)\n\n# Usage\n\n```javascript\nvar Sntp = require('sntp');\n\n// All options are optional\n\nvar options = {\n host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org\n port: 123, // Defaults to 123 (NTP)\n resolveReference: true, // Default to false (not resolving)\n timeout: 1000 // Defaults to zero (no timeout)\n};\n\n// Request server time\n\nSntp.time(options, function (err, time) {\n\n if (err) {\n console.log('Failed: ' + err.message);\n process.exit(1);\n }\n\n console.log('Local clock is off by: ' + time.t + ' milliseconds');\n process.exit(0);\n});\n```\n\nIf an application needs to maintain continuous time synchronization, the module provides a stateful method for\nquerying the current offset only when the last one is too old (defaults to daily).\n\n```javascript\n// Request offset once\n\nSntp.offset(function (err, offset) {\n\n console.log(offset); // New (served fresh)\n\n // Request offset again\n\n Sntp.offset(function (err, offset) {\n\n console.log(offset); // Identical (served from cache)\n });\n});\n```\n\nTo set a background offset refresh, start the interval and use the provided now() method. If for any reason the\nclient fails to obtain an up-to-date offset, the current system clock is used.\n\n```javascript\nvar before = Sntp.now(); // System time without offset\n\nSntp.start(function () {\n\n var now = Sntp.now(); // With offset\n Sntp.stop();\n});\n```\n\n",
|
||||
"readmeFilename": "README.md",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hueniverse/sntp.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test-cov"
|
||||
},
|
||||
"version": "1.0.9"
|
||||
}
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
// Load modules
|
||||
|
||||
var Dns = require('dns');
|
||||
var Dgram = require('dgram');
|
||||
var Lab = require('lab');
|
||||
var Sntp = require('../lib');
|
||||
|
||||
|
||||
// Declare internals
|
||||
|
||||
var internals = {};
|
||||
|
||||
|
||||
// Test shortcuts
|
||||
|
||||
var lab = exports.lab = Lab.script();
|
||||
var before = lab.before;
|
||||
var after = lab.after;
|
||||
var describe = lab.experiment;
|
||||
var it = lab.test;
|
||||
var expect = Lab.expect;
|
||||
|
||||
|
||||
describe('SNTP', function () {
|
||||
|
||||
describe('#time', function () {
|
||||
|
||||
it('returns consistent result over multiple tries', function (done) {
|
||||
|
||||
Sntp.time(function (err, time) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(time).to.exist;
|
||||
var t1 = time.t;
|
||||
|
||||
Sntp.time(function (err, time) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(time).to.exist;
|
||||
var t2 = time.t;
|
||||
expect(Math.abs(t1 - t2)).is.below(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves reference IP', function (done) {
|
||||
|
||||
Sntp.time({ host: 'ntp.exnet.com', resolveReference: true }, function (err, time) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(time).to.exist;
|
||||
expect(time.referenceHost).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('times out on no response', function (done) {
|
||||
|
||||
Sntp.time({ port: 124, timeout: 100 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time).to.not.exist;
|
||||
expect(err.message).to.equal('Timeout');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('errors on error event', { parallel: false }, function (done) {
|
||||
|
||||
var orig = Dgram.createSocket;
|
||||
Dgram.createSocket = function (type) {
|
||||
|
||||
Dgram.createSocket = orig;
|
||||
var socket = Dgram.createSocket(type);
|
||||
setImmediate(function () { socket.emit('error', new Error('Fake')) });
|
||||
return socket;
|
||||
};
|
||||
|
||||
Sntp.time(function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time).to.not.exist;
|
||||
expect(err.message).to.equal('Fake');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('errors on incorrect sent size', { parallel: false }, function (done) {
|
||||
|
||||
var orig = Dgram.Socket.prototype.send;
|
||||
Dgram.Socket.prototype.send = function (buf, offset, length, port, address, callback) {
|
||||
|
||||
Dgram.Socket.prototype.send = orig;
|
||||
return callback(null, 40);
|
||||
};
|
||||
|
||||
Sntp.time(function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time).to.not.exist;
|
||||
expect(err.message).to.equal('Could not send entire message');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('times out on invalid host', function (done) {
|
||||
|
||||
Sntp.time({ host: 'error', timeout: 10000 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time).to.not.exist;
|
||||
expect(err.message).to.contain('getaddrinfo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad response buffer size', function (done) {
|
||||
|
||||
var server = Dgram.createSocket('udp4');
|
||||
server.on('message', function (message, remote) {
|
||||
var message = new Buffer(10);
|
||||
server.send(message, 0, message.length, remote.port, remote.address, function (err, bytes) {
|
||||
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
server.bind(49123);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Invalid server response');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var messup = function (bytes) {
|
||||
|
||||
var server = Dgram.createSocket('udp4');
|
||||
server.on('message', function (message, remote) {
|
||||
|
||||
var message = new Buffer([
|
||||
0x24, 0x01, 0x00, 0xe3,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x41, 0x43, 0x54, 0x53,
|
||||
0xd4, 0xa8, 0x2d, 0xc7,
|
||||
0x1c, 0x5d, 0x49, 0x1b,
|
||||
0xd4, 0xa8, 0x2d, 0xe6,
|
||||
0x67, 0xef, 0x9d, 0xb2,
|
||||
0xd4, 0xa8, 0x2d, 0xe6,
|
||||
0x71, 0xed, 0xb5, 0xfb,
|
||||
0xd4, 0xa8, 0x2d, 0xe6,
|
||||
0x71, 0xee, 0x6c, 0xc5
|
||||
]);
|
||||
|
||||
for (var i = 0, il = bytes.length; i < il; ++i) {
|
||||
message[bytes[i][0]] = bytes[i][1];
|
||||
}
|
||||
|
||||
server.send(message, 0, message.length, remote.port, remote.address, function (err, bytes) {
|
||||
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
server.bind(49123);
|
||||
};
|
||||
|
||||
it('fails on bad version', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (3 << 3) + (4 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.version).to.equal(3);
|
||||
expect(err.message).to.equal('Invalid server response');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad originateTimestamp', function (done) {
|
||||
|
||||
messup([[24, 0x83], [25, 0xaa], [26, 0x7e], [27, 0x80], [28, 0], [29, 0], [30, 0], [31, 0]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Invalid server response');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad receiveTimestamp', function (done) {
|
||||
|
||||
messup([[32, 0x83], [33, 0xaa], [34, 0x7e], [35, 0x80], [36, 0], [37, 0], [38, 0], [39, 0]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Invalid server response');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad originate timestamp and alarm li', function (done) {
|
||||
|
||||
messup([[0, (3 << 6) + (4 << 3) + (4 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(err.message).to.equal('Wrong originate timestamp');
|
||||
expect(time.leapIndicator).to.equal('alarm');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns time with death stratum and last61 li', function (done) {
|
||||
|
||||
messup([[0, (1 << 6) + (4 << 3) + (4 << 0)], [1, 0]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(time.stratum).to.equal('death');
|
||||
expect(time.leapIndicator).to.equal('last-minute-61');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns time with reserved stratum and last59 li', function (done) {
|
||||
|
||||
messup([[0, (2 << 6) + (4 << 3) + (4 << 0)], [1, 0x1f]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(time.stratum).to.equal('reserved');
|
||||
expect(time.leapIndicator).to.equal('last-minute-59');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad mode (symmetric-active)', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (4 << 3) + (1 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.mode).to.equal('symmetric-active');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad mode (symmetric-passive)', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (4 << 3) + (2 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.mode).to.equal('symmetric-passive');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad mode (client)', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (4 << 3) + (3 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.mode).to.equal('client');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad mode (broadcast)', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (4 << 3) + (5 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.mode).to.equal('broadcast');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on bad mode (reserved)', function (done) {
|
||||
|
||||
messup([[0, (0 << 6) + (4 << 3) + (6 << 0)]]);
|
||||
|
||||
Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(time.mode).to.equal('reserved');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#offset', function () {
|
||||
|
||||
it('gets the current offset', function (done) {
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the current offset from cache', function (done) {
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(0);
|
||||
var offset1 = offset;
|
||||
Sntp.offset({}, function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.equal(offset1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the new offset on different server', function (done) {
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(0);
|
||||
var offset1 = offset;
|
||||
Sntp.offset({ host: 'nist1-sj.ustiming.org' }, function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(offset1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the new offset on different server', function (done) {
|
||||
|
||||
Sntp.offset(function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(0);
|
||||
var offset1 = offset;
|
||||
Sntp.offset({ port: 123 }, function (err, offset) {
|
||||
|
||||
expect(err).to.not.exist;
|
||||
expect(offset).to.not.equal(offset1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('fails getting the current offset on invalid server', function (done) {
|
||||
|
||||
Sntp.offset({ host: 'error' }, function (err, offset) {
|
||||
|
||||
expect(err).to.exist;
|
||||
expect(offset).to.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#now', function () {
|
||||
|
||||
it('starts auto-sync, gets now, then stops', function (done) {
|
||||
|
||||
Sntp.stop();
|
||||
|
||||
var before = Sntp.now();
|
||||
expect(before).to.equal(Date.now());
|
||||
|
||||
Sntp.start(function () {
|
||||
|
||||
var now = Sntp.now();
|
||||
expect(now).to.not.equal(Date.now());
|
||||
Sntp.stop();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('starts twice', function (done) {
|
||||
|
||||
Sntp.start(function () {
|
||||
|
||||
Sntp.start(function () {
|
||||
|
||||
var now = Sntp.now();
|
||||
expect(now).to.not.equal(Date.now());
|
||||
Sntp.stop();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('starts auto-sync, gets now, waits, gets again after timeout', function (done) {
|
||||
|
||||
Sntp.stop();
|
||||
|
||||
var before = Sntp.now();
|
||||
expect(before).to.equal(Date.now());
|
||||
|
||||
Sntp.start({ clockSyncRefresh: 100 }, function () {
|
||||
|
||||
var now = Sntp.now();
|
||||
expect(now).to.not.equal(Date.now());
|
||||
expect(now).to.equal(Sntp.now());
|
||||
|
||||
setTimeout(function () {
|
||||
|
||||
expect(Sntp.now()).to.not.equal(now);
|
||||
Sntp.stop();
|
||||
done();
|
||||
}, 110);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user