ntpclient 1.6.3 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -8
- package/dist/cjs/index.test.d.ts +0 -1
- package/dist/cjs/index.test.js +0 -60
- package/dist/esm/index.test.d.ts +0 -1
- package/dist/esm/index.test.js +0 -51
- package/src/cli.ts +0 -42
- package/src/index.test.ts +0 -70
- package/src/index.ts +0 -126
package/package.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"author": "Florian Imdahl <git@ffflorian.de>",
|
|
3
3
|
"bin": "dist/cjs/cli.js",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"commander": "12.
|
|
5
|
+
"commander": "12.1.0"
|
|
6
6
|
},
|
|
7
7
|
"description": "A TypeScript implementation of the NTP Client Protocol",
|
|
8
8
|
"devDependencies": {
|
|
9
|
-
"rimraf": "5.0.
|
|
10
|
-
"typescript": "5.4.
|
|
11
|
-
"vitest": "1.
|
|
9
|
+
"rimraf": "5.0.7",
|
|
10
|
+
"typescript": "5.4.5",
|
|
11
|
+
"vitest": "1.6.0"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">= 18.0"
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
"src"
|
|
23
|
+
"dist"
|
|
25
24
|
],
|
|
26
25
|
"license": "GPL-3.0",
|
|
27
26
|
"main": "dist/cjs/index.js",
|
|
@@ -39,6 +38,6 @@
|
|
|
39
38
|
"test": "vitest run"
|
|
40
39
|
},
|
|
41
40
|
"type": "module",
|
|
42
|
-
"version": "1.6.
|
|
43
|
-
"gitHead": "
|
|
41
|
+
"version": "1.6.4",
|
|
42
|
+
"gitHead": "28c184f53a87d8eb082cc27c923ef7b352bbe875"
|
|
44
43
|
}
|
package/dist/cjs/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/cjs/index.test.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
/* eslint-disable no-magic-numbers */
|
|
11
|
-
import { assert, expect, describe, test, beforeEach, vi, afterEach } from 'vitest';
|
|
12
|
-
import { NTPClient } from './index.js';
|
|
13
|
-
const SECOND_IN_MILLIS = 1000;
|
|
14
|
-
const replyTimeout = 10 * SECOND_IN_MILLIS;
|
|
15
|
-
describe('NTP', () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
vi.mock('dgram', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
-
const actual = (yield vi.importActual('dgram'));
|
|
19
|
-
const socket = actual.createSocket('udp4');
|
|
20
|
-
return Object.assign(Object.assign({}, actual), { send: () => {
|
|
21
|
-
socket.emit('message', Buffer.from([
|
|
22
|
-
0x1c, 0x02, 0x03, 0xe8, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x05, 0x12, 0xc0, 0x35, 0x67, 0x6c, 0xe9,
|
|
23
|
-
0x03, 0x76, 0xff, 0xff, 0x97, 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x03,
|
|
24
|
-
0x78, 0x0f, 0xe9, 0xd6, 0x4e, 0x10, 0xe9, 0x03, 0x78, 0x0f, 0xea, 0x03, 0x93, 0xf5,
|
|
25
|
-
]));
|
|
26
|
-
} });
|
|
27
|
-
}));
|
|
28
|
-
});
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
vi.resetAllMocks();
|
|
31
|
-
});
|
|
32
|
-
test('returns the current time', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
-
const ntpClient = new NTPClient({
|
|
34
|
-
replyTimeout,
|
|
35
|
-
});
|
|
36
|
-
const data = yield ntpClient.getNetworkTime();
|
|
37
|
-
expect(data).toEqual(expect.any(Date));
|
|
38
|
-
}), replyTimeout);
|
|
39
|
-
test('works with another NTP server', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
-
const ntpClient = new NTPClient({
|
|
41
|
-
replyTimeout,
|
|
42
|
-
server: '0.pool.ntp.org',
|
|
43
|
-
});
|
|
44
|
-
const data = yield ntpClient.getNetworkTime();
|
|
45
|
-
expect(data).toEqual(expect.any(Date));
|
|
46
|
-
}), replyTimeout);
|
|
47
|
-
test("doesn't work with an invalid NTP server", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
-
try {
|
|
49
|
-
const ntpClient = new NTPClient({
|
|
50
|
-
replyTimeout: SECOND_IN_MILLIS,
|
|
51
|
-
server: 'google.com',
|
|
52
|
-
});
|
|
53
|
-
yield ntpClient.getNetworkTime();
|
|
54
|
-
assert.fail();
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
expect(error.message).toContain('Timeout');
|
|
58
|
-
}
|
|
59
|
-
}));
|
|
60
|
-
});
|
package/dist/esm/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/esm/index.test.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-magic-numbers */
|
|
2
|
-
import { assert, expect, describe, test, beforeEach, vi, afterEach } from 'vitest';
|
|
3
|
-
import { NTPClient } from './index.js';
|
|
4
|
-
const SECOND_IN_MILLIS = 1000;
|
|
5
|
-
const replyTimeout = 10 * SECOND_IN_MILLIS;
|
|
6
|
-
describe('NTP', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
vi.mock('dgram', async () => {
|
|
9
|
-
const actual = (await vi.importActual('dgram'));
|
|
10
|
-
const socket = actual.createSocket('udp4');
|
|
11
|
-
return Object.assign(Object.assign({}, actual), { send: () => {
|
|
12
|
-
socket.emit('message', Buffer.from([
|
|
13
|
-
0x1c, 0x02, 0x03, 0xe8, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x05, 0x12, 0xc0, 0x35, 0x67, 0x6c, 0xe9,
|
|
14
|
-
0x03, 0x76, 0xff, 0xff, 0x97, 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x03,
|
|
15
|
-
0x78, 0x0f, 0xe9, 0xd6, 0x4e, 0x10, 0xe9, 0x03, 0x78, 0x0f, 0xea, 0x03, 0x93, 0xf5,
|
|
16
|
-
]));
|
|
17
|
-
} });
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
vi.resetAllMocks();
|
|
22
|
-
});
|
|
23
|
-
test('returns the current time', async () => {
|
|
24
|
-
const ntpClient = new NTPClient({
|
|
25
|
-
replyTimeout,
|
|
26
|
-
});
|
|
27
|
-
const data = await ntpClient.getNetworkTime();
|
|
28
|
-
expect(data).toEqual(expect.any(Date));
|
|
29
|
-
}, replyTimeout);
|
|
30
|
-
test('works with another NTP server', async () => {
|
|
31
|
-
const ntpClient = new NTPClient({
|
|
32
|
-
replyTimeout,
|
|
33
|
-
server: '0.pool.ntp.org',
|
|
34
|
-
});
|
|
35
|
-
const data = await ntpClient.getNetworkTime();
|
|
36
|
-
expect(data).toEqual(expect.any(Date));
|
|
37
|
-
}, replyTimeout);
|
|
38
|
-
test("doesn't work with an invalid NTP server", async () => {
|
|
39
|
-
try {
|
|
40
|
-
const ntpClient = new NTPClient({
|
|
41
|
-
replyTimeout: SECOND_IN_MILLIS,
|
|
42
|
-
server: 'google.com',
|
|
43
|
-
});
|
|
44
|
-
await ntpClient.getNetworkTime();
|
|
45
|
-
assert.fail();
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
expect(error.message).toContain('Timeout');
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
});
|
package/src/cli.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import {program as commander} from 'commander';
|
|
4
|
-
import {createRequire} from 'module';
|
|
5
|
-
const require = createRequire(import.meta.url);
|
|
6
|
-
|
|
7
|
-
import {NTPClient} from './index.js';
|
|
8
|
-
|
|
9
|
-
interface PackageJson {
|
|
10
|
-
bin: Record<string, string>;
|
|
11
|
-
description: string;
|
|
12
|
-
version: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const {bin, description, version}: PackageJson = require('../package.json');
|
|
16
|
-
|
|
17
|
-
commander
|
|
18
|
-
.name(Object.keys(bin)[0])
|
|
19
|
-
.version(version)
|
|
20
|
-
.description(description)
|
|
21
|
-
.option('-s, --server <host>', 'Specify a custom NTP server')
|
|
22
|
-
.option('-p, --port <number>', 'Specify a custom NTP port')
|
|
23
|
-
.option('-t, --timeout <number>', 'Specify the timeout in milliseconds')
|
|
24
|
-
.parse(process.argv);
|
|
25
|
-
|
|
26
|
-
const commanderOptions = commander.opts();
|
|
27
|
-
|
|
28
|
-
void (async () => {
|
|
29
|
-
try {
|
|
30
|
-
const date = await new NTPClient({
|
|
31
|
-
...(commanderOptions.server && {server: commanderOptions.server}),
|
|
32
|
-
...(commanderOptions.port && {port: commanderOptions.port}),
|
|
33
|
-
...(commanderOptions.timeout && {replyTimeout: commanderOptions.timeout}),
|
|
34
|
-
}).getNetworkTime();
|
|
35
|
-
|
|
36
|
-
console.info(date.toString());
|
|
37
|
-
process.exit();
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(error);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
})();
|
package/src/index.test.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-magic-numbers */
|
|
2
|
-
import {assert, expect, describe, test, beforeEach, vi, afterEach} from 'vitest';
|
|
3
|
-
import {NTPClient} from './index.js';
|
|
4
|
-
import type dgram from 'dgram';
|
|
5
|
-
|
|
6
|
-
const SECOND_IN_MILLIS = 1000;
|
|
7
|
-
const replyTimeout = 10 * SECOND_IN_MILLIS;
|
|
8
|
-
|
|
9
|
-
describe('NTP', () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
vi.mock('dgram', async () => {
|
|
12
|
-
const actual = (await vi.importActual('dgram')) as typeof dgram;
|
|
13
|
-
const socket = actual.createSocket('udp4');
|
|
14
|
-
return {
|
|
15
|
-
...actual,
|
|
16
|
-
send: () => {
|
|
17
|
-
socket.emit(
|
|
18
|
-
'message',
|
|
19
|
-
Buffer.from([
|
|
20
|
-
0x1c, 0x02, 0x03, 0xe8, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x05, 0x12, 0xc0, 0x35, 0x67, 0x6c, 0xe9,
|
|
21
|
-
0x03, 0x76, 0xff, 0xff, 0x97, 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x03,
|
|
22
|
-
0x78, 0x0f, 0xe9, 0xd6, 0x4e, 0x10, 0xe9, 0x03, 0x78, 0x0f, 0xea, 0x03, 0x93, 0xf5,
|
|
23
|
-
])
|
|
24
|
-
);
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
vi.resetAllMocks();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test(
|
|
34
|
-
'returns the current time',
|
|
35
|
-
async () => {
|
|
36
|
-
const ntpClient = new NTPClient({
|
|
37
|
-
replyTimeout,
|
|
38
|
-
});
|
|
39
|
-
const data = await ntpClient.getNetworkTime();
|
|
40
|
-
expect(data).toEqual(expect.any(Date));
|
|
41
|
-
},
|
|
42
|
-
replyTimeout
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
test(
|
|
46
|
-
'works with another NTP server',
|
|
47
|
-
async () => {
|
|
48
|
-
const ntpClient = new NTPClient({
|
|
49
|
-
replyTimeout,
|
|
50
|
-
server: '0.pool.ntp.org',
|
|
51
|
-
});
|
|
52
|
-
const data = await ntpClient.getNetworkTime();
|
|
53
|
-
expect(data).toEqual(expect.any(Date));
|
|
54
|
-
},
|
|
55
|
-
replyTimeout
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
test("doesn't work with an invalid NTP server", async () => {
|
|
59
|
-
try {
|
|
60
|
-
const ntpClient = new NTPClient({
|
|
61
|
-
replyTimeout: SECOND_IN_MILLIS,
|
|
62
|
-
server: 'google.com',
|
|
63
|
-
});
|
|
64
|
-
await ntpClient.getNetworkTime();
|
|
65
|
-
assert.fail();
|
|
66
|
-
} catch (error) {
|
|
67
|
-
expect((error as Error).message).toContain('Timeout');
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import * as dgram from 'dgram';
|
|
2
|
-
|
|
3
|
-
const TEN_SECONDS_IN_MILLIS = 10_000;
|
|
4
|
-
|
|
5
|
-
export interface NTPConfig {
|
|
6
|
-
/** Remote NTP Server port number */
|
|
7
|
-
port?: number;
|
|
8
|
-
/** Amount of acceptable time to await for a response from the remote server. */
|
|
9
|
-
replyTimeout?: number;
|
|
10
|
-
/** IP/Hostname of the remote NTP Server */
|
|
11
|
-
server?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const defaultConfig: Required<NTPConfig> = {
|
|
15
|
-
port: 123,
|
|
16
|
-
replyTimeout: TEN_SECONDS_IN_MILLIS,
|
|
17
|
-
server: 'pool.ntp.org',
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export class NTPClient {
|
|
21
|
-
private readonly config: Required<NTPConfig>;
|
|
22
|
-
|
|
23
|
-
constructor(server?: string, port?: number, timeout?: number);
|
|
24
|
-
constructor(config?: NTPConfig);
|
|
25
|
-
constructor(configOrServer?: string | NTPConfig, port?: number, replyTimeout?: number) {
|
|
26
|
-
this.config = defaultConfig;
|
|
27
|
-
|
|
28
|
-
if (typeof configOrServer === 'string') {
|
|
29
|
-
this.config.server = configOrServer;
|
|
30
|
-
} else {
|
|
31
|
-
this.config = {
|
|
32
|
-
...this.config,
|
|
33
|
-
...configOrServer,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
if (port) {
|
|
37
|
-
this.config.port = port;
|
|
38
|
-
}
|
|
39
|
-
if (replyTimeout) {
|
|
40
|
-
this.config.replyTimeout = replyTimeout;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Fetches the current NTP Time from the given server and port. */
|
|
45
|
-
public getNetworkTime(ntpReplyTimeout?: number): Promise<Date> {
|
|
46
|
-
if (ntpReplyTimeout) {
|
|
47
|
-
this.config.replyTimeout = ntpReplyTimeout;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
const client = dgram.createSocket('udp4');
|
|
52
|
-
const ntpData = Buffer.alloc(48);
|
|
53
|
-
|
|
54
|
-
// RFC 2030 -> LI = 0 (no warning, 2 bits), VN = 3 (IPv4 only, 3 bits), Mode = 3 (Client Mode, 3 bits) -> 1 byte
|
|
55
|
-
// -> rtol(LI, 6) ^ rotl(VN, 3) ^ rotl(Mode, 0)
|
|
56
|
-
// -> = 0x00 ^ 0x18 ^ 0x03
|
|
57
|
-
ntpData[0] = 0x1b;
|
|
58
|
-
|
|
59
|
-
const timeout = setTimeout(() => {
|
|
60
|
-
client.close();
|
|
61
|
-
reject(new Error('Timeout waiting for NTP response.'));
|
|
62
|
-
errorFired = true;
|
|
63
|
-
}, this.config.replyTimeout);
|
|
64
|
-
|
|
65
|
-
/*
|
|
66
|
-
* Some errors can happen before/after send() or cause send() to break.
|
|
67
|
-
* Some errors will also be given to send()
|
|
68
|
-
* NOTE: the error rejection is not generalised, as the client has to
|
|
69
|
-
* lose the connection also, apparently.
|
|
70
|
-
*/
|
|
71
|
-
let errorFired = false;
|
|
72
|
-
|
|
73
|
-
client.on('error', err => {
|
|
74
|
-
if (errorFired) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
errorFired = true;
|
|
79
|
-
clearTimeout(timeout);
|
|
80
|
-
|
|
81
|
-
return reject(err);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
client.send(ntpData, 0, ntpData.length, this.config.port, this.config.server, err => {
|
|
85
|
-
if (err) {
|
|
86
|
-
if (errorFired) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
clearTimeout(timeout);
|
|
90
|
-
errorFired = true;
|
|
91
|
-
client.close();
|
|
92
|
-
return reject(err);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
client.once('message', msg => {
|
|
96
|
-
clearTimeout(timeout);
|
|
97
|
-
client.close();
|
|
98
|
-
|
|
99
|
-
// Offset to get to the "Transmit Timestamp" field (time at which the reply
|
|
100
|
-
// departed the server for the client, in 64-bit timestamp format.
|
|
101
|
-
const offsetTransmitTime = 40;
|
|
102
|
-
let intpart = 0;
|
|
103
|
-
let fractpart = 0;
|
|
104
|
-
|
|
105
|
-
// Get the seconds part
|
|
106
|
-
for (let index = 0; index <= 3; index++) {
|
|
107
|
-
intpart = 256 * intpart + msg[offsetTransmitTime + index];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Get the seconds fraction
|
|
111
|
-
for (let index = 4; index <= 7; index++) {
|
|
112
|
-
fractpart = 256 * fractpart + msg[offsetTransmitTime + index];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100_000_000;
|
|
116
|
-
|
|
117
|
-
// **UTC** time
|
|
118
|
-
const date = new Date('Jan 01 1900 GMT');
|
|
119
|
-
date.setUTCMilliseconds(date.getUTCMilliseconds() + milliseconds);
|
|
120
|
-
|
|
121
|
-
return resolve(date);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|