├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── example ├── index.js └── server.js ├── index.js ├── package.json ├── packet.js ├── server.js ├── test └── index.js └── types └── index.d.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: song940 2 | patreon: song940 3 | open_collective: song940 4 | custom: https://git.io/fjRcB 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_* 2 | *.log 3 | logs 4 | **/*.backup.* 5 | **/*.back.* 6 | 7 | node_modules 8 | bower_componets 9 | 10 | *.sublime* 11 | 12 | psd 13 | thumb 14 | sketch 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Lsong <song940@gmail.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ntp2 2 | 3 | simple network time protocol implementation for node.js 4 | 5 | ![NPM version](https://img.shields.io/npm/v/ntp2.svg?style=flat) 6 | [![Build Status](https://travis-ci.org/song940/node-ntp.svg?branch=master)](https://travis-ci.org/song940/node-ntp) 7 | 8 | ### Installation 9 | 10 | ```bash 11 | $ npm i ntp2 12 | ``` 13 | 14 | ### Example 15 | 16 | ```js 17 | const ntp = require('ntp2'); 18 | 19 | ntp.time(function(err, response){ 20 | console.log('The network time is :', new Date(response.toMsecs(response.transmitTimestamp))); 21 | }); 22 | ``` 23 | 24 | sntp server 25 | 26 | ```js 27 | const ntp = require('ntp2'); 28 | 29 | const server = ntp.createServer(function(message, response){ 30 | console.log('response message:', message); 31 | response(message); 32 | }).listen(123, function(err){ 33 | console.log('server is running at %s', server.address().port); 34 | }); 35 | ``` 36 | 37 | ### API 38 | 39 | - ntp2.Server() 40 | - ntp2.Client() 41 | - ntp2.createServer() 42 | 43 | ### SPEC 44 | 45 | + https://tools.ietf.org/html/rfc2030 46 | + https://tools.ietf.org/html/rfc4330 47 | 48 | ### Contributing 49 | - Fork this Repo first 50 | - Clone your Repo 51 | - Install dependencies by `$ npm install` 52 | - Checkout a feature branch 53 | - Feel free to add your features 54 | - Make sure your features are fully tested 55 | - Publish your local branch, Open a pull request 56 | - Enjoy hacking <3 57 | 58 | ### MIT license 59 | Copyright (c) 2016 Lsong <song940@gmail.com> 60 | 61 | Permission is hereby granted, free of charge, to any person obtaining a copy 62 | of this software and associated documentation files (the "Software"), to deal 63 | in the Software without restriction, including without limitation the rights 64 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 65 | copies of the Software, and to permit persons to whom the Software is 66 | furnished to do so, subject to the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be included in 69 | all copies or substantial portions of the Software. 70 | 71 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 72 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 73 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 74 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 75 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 76 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 77 | THE SOFTWARE. 78 | 79 | --- 80 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const ntp = require('..'); 2 | 3 | // ntp(function(err, response){ 4 | // if(err) return console.error(err); 5 | // console.log('The network time is :', new Date(response.toMsecs(response.transmitTimestamp))); 6 | // }); 7 | 8 | 9 | (async () => { 10 | const n = ntp(); 11 | const message = await n.time(); 12 | console.log(new Date(message.toMsecs(message.transmitTimestamp))); 13 | })(); -------------------------------------------------------------------------------- /example/server.js: -------------------------------------------------------------------------------- 1 | const ntp = require('..'); 2 | 3 | const server = ntp.createServer(function(message, response){ 4 | console.log('server message:', message); 5 | // By default the current time will be send. Uncomment for custom time 6 | // message.writeMsecs(message.transmitTimestamp, (new Date('December 17, 1995 03:24:00')).getTime()); 7 | response(message); 8 | }).listen(4567, function(err){ 9 | console.log('server is running at %s', server.address().port); 10 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const udp = require('dgram'); 3 | const util = require('util'); 4 | const Packet = require('./packet'); 5 | const EventEmitter = require('events'); 6 | 7 | const sleep = ms => 8 | new Promise(done => setTimeout(done, ms)); 9 | 10 | class NTPTimeoutError extends Error { 11 | name = 'NTP Timeout Error' 12 | } 13 | 14 | /** 15 | * [NTPClient description] 16 | * @docs https://tools.ietf.org/html/rfc2030 17 | */ 18 | function NTP(options, callback) { 19 | if (!(this instanceof NTP)) 20 | return new NTP(options, callback); 21 | EventEmitter.call(this); 22 | if (typeof options === 'function') { 23 | callback = options; 24 | options = {}; 25 | } 26 | const { socketType } = Object.assign(this, { 27 | server: 'pool.ntp.org', 28 | port: 123, 29 | socketType: 'udp4', 30 | }, options); 31 | this.socket = udp.createSocket(socketType); 32 | if (typeof callback === 'function') 33 | this.time(callback); 34 | return this; 35 | }; 36 | 37 | util.inherits(NTP, EventEmitter); 38 | 39 | /** 40 | * [time description] 41 | * @param {Function} callback [description] 42 | * @return {[type]} [description] 43 | */ 44 | NTP.time = function (options, callback) { 45 | return new NTP(options, callback); 46 | }; 47 | 48 | /** 49 | * [time description] 50 | * @param {Function} callback [description] 51 | * @return {[type]} [description] 52 | */ 53 | NTP.prototype.time = function (callback) { 54 | const { server, port, timeout = 400 } = this; 55 | const packet = NTP.createPacket(); 56 | const t = sleep(timeout).then(() => { 57 | throw new NTPTimeoutError(); 58 | }); 59 | const p = new Promise((resolve, reject) => { 60 | this.socket.send(packet, 0, packet.length, port, server, err => { 61 | if (err) return reject(err); 62 | this.socket.once('message', data => { 63 | const message = NTP.parse(data); 64 | resolve(message); 65 | }); 66 | }); 67 | }); 68 | return Promise 69 | .race([p, t]) 70 | .then(res => { 71 | this.socket.close(); 72 | callback && callback(null, res); 73 | return res; 74 | }, err => { 75 | this.socket.close(); 76 | callback && callback(err); 77 | throw err; 78 | }); 79 | }; 80 | 81 | /** 82 | * [createPacket description] 83 | * @return {[type]} [description] 84 | */ 85 | NTP.createPacket = function () { 86 | const packet = new Packet(); 87 | packet.mode = Packet.MODES.CLIENT; 88 | packet.writeMsecs(packet.originateTimestamp, Date.now()); 89 | return packet.toBuffer(); 90 | }; 91 | 92 | /** 93 | * [parse description] 94 | * @param {Function} callback [description] 95 | * @param {[type]} msg [description] 96 | * @return {[type]} [description] 97 | */ 98 | NTP.parse = function (buffer) { 99 | // const SEVENTY_YEARS = 2208988800; 100 | // var secsSince1900 = buffer.readUIntBE(40, 4); 101 | // var epoch = secsSince1900 - SEVENTY_YEARS; 102 | // var date = new Date(0); 103 | // date.setUTCSeconds(epoch); 104 | // return date; 105 | const message = Packet.parse(buffer); 106 | // Timestamp Name ID When Generated 107 | // ------------------------------------------------------------ 108 | // Originate Timestamp T1 time request sent by client 109 | // Receive Timestamp T2 time request received by server 110 | // Transmit Timestamp T3 time reply sent by server 111 | // Destination Timestamp T4 time reply received by client 112 | const T1 = message.toMsecs(message.originateTimestamp); 113 | const T2 = message.toMsecs(message.receiveTimestamp); 114 | const T3 = message.toMsecs(message.transmitTimestamp); 115 | const T4 = message.toMsecs(Date.now()); 116 | // The roundtrip delay d and system clock offset t are defined as: 117 | // - 118 | // d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2 119 | message.d = (T4 - T1) - (T3 - T2); 120 | message.t = ((T2 - T1) + (T3 - T4)) / 2; 121 | return message; 122 | }; 123 | 124 | NTP.Client = NTP; 125 | NTP.Server = require('./server'); 126 | 127 | /** 128 | * [createServer description] 129 | * @return {[type]} [description] 130 | */ 131 | NTP.createServer = function (options) { 132 | return new NTP.Server(options); 133 | }; 134 | 135 | module.exports = NTP; 136 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ntp2", 3 | "version": "0.0.3", 4 | "description": "simple network time protocol implementation for node.js", 5 | "main": "index.js", 6 | "types": "./types/index.d.ts", 7 | "repository": { 8 | "url": "git+ssh://git@github.com/song940/node-ntp.git", 9 | "type": "git" 10 | }, 11 | "author": "Lsong ", 12 | "license": "MIT", 13 | "scripts": { 14 | "start": "node server.js", 15 | "test": "node test" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/song940/node-ntp/issues" 19 | }, 20 | "homepage": "https://github.com/song940/node-ntp#readme", 21 | "directories": { 22 | "example": "example", 23 | "test": "test" 24 | }, 25 | "keywords": [ 26 | "ntp", 27 | "time", 28 | "date" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packet.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | /** 3 | 1 2 3 4 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 5 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 6 | |LI | VN |Mode | Stratum | Poll | Precision | 7 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 8 | | Root Delay | 9 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 10 | | Root Dispersion | 11 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 12 | | Reference Identifier | 13 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 14 | | | 15 | | Reference Timestamp (64) | 16 | | | 17 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 18 | | | 19 | | Originate Timestamp (64) | 20 | | | 21 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 22 | | | 23 | | Receive Timestamp (64) | 24 | | | 25 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | | | 27 | | Transmit Timestamp (64) | 28 | | | 29 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | | Key Identifier (optional) (32) | 31 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | | | 33 | | | 34 | | Message Digest (optional) (128) | 35 | | | 36 | | | 37 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | */ 39 | 40 | /** 41 | * 1900 ~ 1970 42 | * @docs https://tools.ietf.org/html/rfc4330#section-3 43 | */ 44 | const SEVENTY_YEARS = 2208988800; 45 | 46 | /** 47 | * @rfc https://tools.ietf.org/html/rfc4330 48 | */ 49 | class Packet { 50 | constructor(){ 51 | Object.assign(this, { 52 | leapIndicator: 0, 53 | version: 4, 54 | mode: 3, 55 | stratum: 0, 56 | pollInterval: 6, 57 | precision: 236, 58 | referenceIdentifier: Buffer.from([0, 0, 0, 0]), 59 | referenceTimestamp: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), 60 | originateTimestamp: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), 61 | receiveTimestamp: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), 62 | transmitTimestamp: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), 63 | }); 64 | } 65 | static parse(buffer) { 66 | assert.equal(buffer.length, 48, 'Invalid Package'); 67 | const packet = new Packet(); 68 | packet.leapIndicator = (buffer[0] >> 6); 69 | packet.version = (buffer[0] & 0x38) >> 3; 70 | packet.mode = (buffer[0] & 0x7); 71 | packet.stratum = buffer[1]; 72 | packet.pollInterval = buffer[2]; 73 | packet.precision = buffer[3]; 74 | packet.rootDelay = buffer.slice(4, 8); 75 | packet.rootDispersion = buffer.slice(8, 12); 76 | packet.referenceIdentifier = buffer.slice(12, 16); 77 | packet.referenceTimestamp = buffer.slice(16, 24); 78 | packet.originateTimestamp = buffer.slice(24, 32); 79 | packet.receiveTimestamp = buffer.slice(32, 40); 80 | packet.transmitTimestamp = buffer.slice(40, 48) 81 | return packet; 82 | } 83 | toBuffer() { 84 | const buffer = Buffer.alloc(48).fill(0x00); 85 | buffer[0] = 0; // 0b11100011; // LI, Version, Mode 86 | buffer[0] += this.leapIndicator << 6; 87 | buffer[0] += this.version << 3; 88 | buffer[0] += this.mode << 0; 89 | buffer[1] = this.stratum; 90 | buffer[2] = this.pollInterval; 91 | buffer[3] = this.precision; 92 | buffer.writeUInt32BE(this.rootDelay, 4); 93 | buffer.writeUInt32BE(this.rootDispersion, 8); 94 | buffer.writeUInt32BE(this.referenceIdentifier, 12); 95 | this.referenceTimestamp.copy(buffer, 16, 0, 8); 96 | this.originateTimestamp.copy(buffer, 24, 0, 8); 97 | this.receiveTimestamp.copy(buffer, 32, 0, 8); 98 | this.transmitTimestamp.copy(buffer, 40, 0, 8); 99 | return buffer; 100 | } 101 | toJSON() { 102 | const output = Object.assign({}, this); 103 | const { leapIndicator, version } = this; 104 | output.version = version; 105 | output.leapIndicator = { 106 | 0: 'no-warning', 107 | 1: 'last-minute-61', 108 | 2: 'last-minute-59', 109 | 3: 'alarm' 110 | }[leapIndicator]; 111 | const { mode } = this; 112 | switch (mode) { 113 | case 1: output.mode = 'symmetric-active'; break; 114 | case 2: output.mode = 'symmetric-passive'; break; 115 | case 3: output.mode = 'client'; break; 116 | case 4: output.mode = 'server'; break; 117 | case 5: output.mode = 'broadcast'; break; 118 | case 0: 119 | case 6: 120 | case 7: output.mode = 'reserved'; break; 121 | } 122 | const { stratum } = this; 123 | if (stratum === 0) { 124 | output.stratum = 'death'; 125 | } else if (stratum === 1) { 126 | output.stratum = 'primary'; 127 | } else if (stratum <= 15) { 128 | output.stratum = 'secondary'; 129 | } else { 130 | output.stratum = 'reserved'; 131 | } 132 | output.referenceTimestamp = new Date(toMsecs(this.referenceTimestamp)); 133 | output.originateTimestamp = new Date(toMsecs(this.originateTimestamp)); 134 | output.receiveTimestamp = new Date(toMsecs(this.receiveTimestamp)); 135 | output.transmitTimestamp = new Date(toMsecs(this.transmitTimestamp)); 136 | return output; 137 | } 138 | 139 | // 1 2 3 140 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 141 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 142 | // | Seconds | 143 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 144 | // | Seconds Fraction (0-padded) | 145 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 146 | 147 | toMsecs(buffer) { 148 | let seconds = 0; 149 | let fraction = 0; 150 | for (let i = 0; i < 4; ++i) { 151 | seconds = (seconds * 256) + buffer[i]; 152 | } 153 | for (let i = 4; i < 8; ++i) { 154 | fraction = (fraction * 256) + buffer[i]; 155 | } 156 | return ((seconds - SEVENTY_YEARS + (fraction / Math.pow(2, 32))) * 1000); 157 | } 158 | writeMsecs(buffer, ts){ 159 | // const buffer = Buffer.alloc(8); // 64bits 160 | const seconds = Math.floor(ts / 1000) + SEVENTY_YEARS; 161 | const fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32)); 162 | // seconds 163 | buffer[0] = (seconds & 0xFF000000) >> 24; 164 | buffer[1] = (seconds & 0x00FF0000) >> 16; 165 | buffer[2] = (seconds & 0x0000FF00) >> 8; 166 | buffer[3] = (seconds & 0x000000FF); 167 | // fraction 168 | buffer[4] = (fraction & 0xFF000000) >> 24; 169 | buffer[5] = (fraction & 0x00FF0000) >> 16; 170 | buffer[6] = (fraction & 0x0000FF00) >> 8; 171 | buffer[7] = (fraction & 0x000000FF); 172 | return buffer; 173 | } 174 | } 175 | 176 | // Mode Meaning 177 | // ------------------------------------ 178 | // 0 reserved 179 | // 1 symmetric active 180 | // 2 symmetric passive 181 | // 3 client 182 | // 4 server 183 | // 5 broadcast 184 | // 6 reserved for NTP control message 185 | // 7 reserved for private use 186 | Packet.MODES = { 187 | CLIENT: 3, 188 | SERVER: 4, 189 | };; 190 | 191 | module.exports = Packet; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const udp = require('dgram'); 3 | const EventEmitter = require('events'); 4 | const Packet = require('./packet'); 5 | /** 6 | * [NTPServer description] 7 | * @param {[type]} options [description] 8 | */ 9 | class NTPServer extends EventEmitter { 10 | constructor(options, onRequest) { 11 | super(); 12 | if (typeof options === 'function') { 13 | onRequest = options; 14 | options = {}; 15 | } 16 | Object.assign(this, { 17 | port: 123 18 | }, options); 19 | this.socket = udp.createSocket('udp4'); 20 | this.socket.on('message', this.parse.bind(this)); 21 | if (onRequest) this.on('request', onRequest); 22 | return this; 23 | } 24 | listen(port, address) { 25 | this.socket.bind(port || this.port, address); 26 | return this; 27 | } 28 | address() { 29 | return this.socket.address(); 30 | } 31 | send(rinfo, message, callback) { 32 | if (message instanceof Packet) { 33 | message.mode = Packet.MODES.SERVER; // mark mode as server 34 | message = message.toBuffer(); 35 | } 36 | this.socket.send(message, rinfo.port, rinfo.address, callback); 37 | return this; 38 | } 39 | parse(message, rinfo) { 40 | const packet = Packet.parse(message); 41 | packet.stratum = 1; 42 | packet.transmitTimestamp.copy(packet.originateTimestamp); 43 | packet.writeMsecs(packet.receiveTimestamp, Date.now()); 44 | packet.receiveTimestamp.copy(packet.referenceTimestamp); 45 | packet.writeMsecs(packet.transmitTimestamp, Date.now()); 46 | this.emit('request', packet, this.send.bind(this, rinfo)); 47 | return this; 48 | } 49 | } 50 | 51 | 52 | module.exports = NTPServer; -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const Packet = require('../packet'); 3 | 4 | const request = new Packet(); 5 | 6 | request.leapIndicator = 1; 7 | request.mode = Packet.MODES.SERVER; 8 | 9 | const message = request.toBuffer(); 10 | assert.equal(message[0] & 0x7, request.mode); 11 | assert.equal(message[0] >> 6, request.leapIndicator); 12 | assert.equal(((message[0] & 0x38) >> 3), request.version); 13 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "ntp2" { 2 | namespace ntp { 3 | 4 | class NTP { 5 | time(): void; 6 | static time(): void; 7 | static createPacket(): void; 8 | static createServer(options: any): NTPServer; 9 | } 10 | 11 | class NTPServer { 12 | address(): object; 13 | listen(port?: number, address?: string): void; 14 | parse(message: Buffer, rinfo: any): void; 15 | send(rinfo: any, message: any, callback: Function): void; 16 | } 17 | } 18 | } --------------------------------------------------------------------------------