simple_nts_client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +17 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +57 -0
- data/Rakefile +10 -0
- data/exe/simple_nts_client +8 -0
- data/lib/nts/cli.rb +83 -0
- data/lib/nts/ntske/client.rb +100 -0
- data/lib/nts/ntske/message/aead_algorithm_negotiation.rb +47 -0
- data/lib/nts/ntske/message/cookie.rb +33 -0
- data/lib/nts/ntske/message/end_of_message.rb +30 -0
- data/lib/nts/ntske/message/error_record.rb +39 -0
- data/lib/nts/ntske/message/nts_next_protocol_negotiation.rb +35 -0
- data/lib/nts/ntske/message/ntsv4_port_negotiation.rb +36 -0
- data/lib/nts/ntske/message/ntsv4_server_negotiation.rb +36 -0
- data/lib/nts/ntske/message/warning_record.rb +34 -0
- data/lib/nts/ntske/message.rb +107 -0
- data/lib/nts/ntske.rb +4 -0
- data/lib/nts/sntp/client.rb +160 -0
- data/lib/nts/sntp/extension/nts_authenticator.rb +53 -0
- data/lib/nts/sntp/extension/nts_cookie.rb +34 -0
- data/lib/nts/sntp/extension/unique_identifier.rb +36 -0
- data/lib/nts/sntp/extension/unknown_extension.rb +36 -0
- data/lib/nts/sntp/extension.rb +38 -0
- data/lib/nts/sntp/message.rb +119 -0
- data/lib/nts/sntp.rb +4 -0
- data/lib/nts/version.rb +5 -0
- data/lib/nts.rb +12 -0
- data/simple_nts_client.gemspec +27 -0
- data/spec/aead_algorithm_negotiation_spec.rb +28 -0
- data/spec/cookie_spec.rb +36 -0
- data/spec/end_of_message_spec.rb +26 -0
- data/spec/error_record_spec.rb +28 -0
- data/spec/extension_spec.rb +28 -0
- data/spec/nts_authenticator_spec.rb +64 -0
- data/spec/nts_cookie_spec.rb +36 -0
- data/spec/nts_next_protocol_negotiation_spec.rb +28 -0
- data/spec/ntsv4_port_negotiation_spec.rb +28 -0
- data/spec/ntsv4_server_negotiation_spec.rb +28 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/unique_identifier_spec.rb +36 -0
- data/spec/unknown_extension_spec.rb +46 -0
- data/spec/warning_record_spec.rb +28 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fc37d0334da875bdc71e5089fc001e0ce29772ab238db3ca93f750f25579d417
|
4
|
+
data.tar.gz: c432c7f707cdbe678df8181881900525d39fa6e502190924fb3b6025ad5ca63e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 054d1484e8d6c17b1da9f183edd9c18c79fe83b86747555774a551b6791460a67c9c6aff5dff5ef5b9bda1a47e8613e58ceec5dd2ae3848253dc8718973bcbb2
|
7
|
+
data.tar.gz: ee2d152f81b731ab6e9213acb11b8e7af43366cb6565a3369000efbb8f8d005f2f46dbdde95708fa3c1b7e26917a94cc79eafb62834cbaab9042535645ef3503
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
|
4
|
+
Style/Documentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Style/NumericLiterals:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/BlockDelimiters:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Metrics/AbcSize:
|
14
|
+
Max: 30
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Max: 30
|
18
|
+
|
19
|
+
Naming/UncommunicativeMethodParamName:
|
20
|
+
MinNameLength: 1
|
21
|
+
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Exclude:
|
24
|
+
- 'spec/*.rb'
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Tomoya Kuwayama
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# simple_nts_client
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/simple_nts_client.svg)](https://badge.fury.io/rb/simple_nts_client)
|
4
|
+
[![Build Status](https://travis-ci.org/thekuwayama/simple_nts_client.svg?branch=master)](https://travis-ci.org/thekuwayama/simple_nts_client)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/7b34a4868f1e297af084/maintainability)](https://codeclimate.com/github/thekuwayama/simple_nts_client/maintainability)
|
6
|
+
|
7
|
+
simple\_nts\_client is CLI that is simple NTS(Network Time Security) Client implementation.
|
8
|
+
This CLI prints the now timestamp got with NTS.
|
9
|
+
Current implementation is based on:
|
10
|
+
|
11
|
+
* [draft-ietf-ntp-using-nts-for-ntp-20](https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20)
|
12
|
+
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
The gem is available at [rubygems.org](https://rubygems.org/gems/simple_nts_client). You can install it the following:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
$ gem install simple_nts_client
|
20
|
+
```
|
21
|
+
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ simple_nts_client --help
|
27
|
+
Usage: simple_nts_client [options]
|
28
|
+
-s, --server VALUE NTS-KE server name (default time.cloudflare.com)
|
29
|
+
-p, --port VALUE NTS-KE port number (default 1234)
|
30
|
+
-v, --verbose verbose mode (default false)
|
31
|
+
```
|
32
|
+
|
33
|
+
You can run it the following:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
$ simple_nts_client
|
37
|
+
2019-07-20 06:00:00 +0900
|
38
|
+
```
|
39
|
+
|
40
|
+
If you need to access other NTS-KE server or port, you can run it the following:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
$ simple_nts_client --server YOURSERVER --port YOURPORT
|
44
|
+
2019-07-20 06:00:00 +0900
|
45
|
+
```
|
46
|
+
|
47
|
+
|
48
|
+
## How "simple" client?
|
49
|
+
|
50
|
+
* The CLI supports only `AEAD_AES_SIV_CMAC_256` as AEAD algorithms to protect the NTPv4 packet.
|
51
|
+
* The CLI sends the first one of the received cookies via the response of the New Cookie for NTPv4 record and discards cookies that didn't send.
|
52
|
+
* The CLI just prints the timestamp adjusted by calculated system clock offset.
|
53
|
+
|
54
|
+
|
55
|
+
## License
|
56
|
+
|
57
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/nts/cli.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Nts
|
6
|
+
class CLI
|
7
|
+
# rubocop: disable Metrics/MethodLength
|
8
|
+
def parse_options(argv = ARGV)
|
9
|
+
op = OptionParser.new
|
10
|
+
|
11
|
+
# default value
|
12
|
+
opts = {
|
13
|
+
server: 'time.cloudflare.com',
|
14
|
+
port: 1234,
|
15
|
+
verbose: false
|
16
|
+
}
|
17
|
+
|
18
|
+
op.on(
|
19
|
+
'-s',
|
20
|
+
'--server VALUE',
|
21
|
+
"NTS-KE server name (default #{opts[:server]})"
|
22
|
+
) do |v|
|
23
|
+
opts[:server] = v
|
24
|
+
end
|
25
|
+
|
26
|
+
op.on(
|
27
|
+
'-p',
|
28
|
+
'--port VALUE',
|
29
|
+
"NTS-KE port number (default #{opts[:port]})"
|
30
|
+
) do |v|
|
31
|
+
opts[:port] = v
|
32
|
+
end
|
33
|
+
|
34
|
+
op.on(
|
35
|
+
'-v',
|
36
|
+
'--verbose',
|
37
|
+
"verbose mode (default #{opts[:verbose]})"
|
38
|
+
) do |v|
|
39
|
+
opts[:verbose] = v
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
op.parse(argv)
|
44
|
+
rescue OptionParser::InvalidOption => e
|
45
|
+
puts op.to_s
|
46
|
+
puts "error: #{e.message}"
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
|
50
|
+
opts
|
51
|
+
end
|
52
|
+
# rubocop: enable Metrics/MethodLength
|
53
|
+
|
54
|
+
def run
|
55
|
+
opts = parse_options
|
56
|
+
|
57
|
+
# NTS-KE
|
58
|
+
ntske_client = Ntske::Client.new(opts[:server], opts[:port])
|
59
|
+
hostname, ntp_port, cookies, c2s_key, s2c_key = ntske_client.key_establish
|
60
|
+
hostname ||= opts[:server]
|
61
|
+
ntp_port ||= 123
|
62
|
+
|
63
|
+
# verbose mode
|
64
|
+
if opts[:verbose]
|
65
|
+
puts "# NTPv4 Server => #{hostname}"
|
66
|
+
puts "# NTPv4 Port => #{ntp_port}"
|
67
|
+
puts "# Cookie for NTPv4(hex) => #{cookies.first.unpack1('H*')}"
|
68
|
+
puts "# client-to-server(hex) => #{c2s_key.unpack1('H*')}"
|
69
|
+
puts "# server-to-client(hex) => #{s2c_key.unpack1('H*')}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# NTS protected NTP
|
73
|
+
ntp_client = Nts::Sntp::Client.new(
|
74
|
+
hostname,
|
75
|
+
ntp_port,
|
76
|
+
cookies.first,
|
77
|
+
c2s_key,
|
78
|
+
s2c_key
|
79
|
+
)
|
80
|
+
puts ntp_client.what_time
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Nts
|
5
|
+
module Ntske
|
6
|
+
class Client
|
7
|
+
ALPN = 'ntske/1'
|
8
|
+
private_constant :ALPN
|
9
|
+
|
10
|
+
KE_LABEL = 'EXPORTER-network-time-security/1'
|
11
|
+
private_constant :KE_LABEL
|
12
|
+
|
13
|
+
# @param hostname [String]
|
14
|
+
# @param port [Integer]
|
15
|
+
def initialize(hostname, port)
|
16
|
+
@hostname = hostname
|
17
|
+
@port = port
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String | nil] NTPv4 Server
|
21
|
+
# @return [Integer | nil] NTPv4 Port
|
22
|
+
# @return [Array of String] Cookies for NTPv4
|
23
|
+
# @return [String] C2S key
|
24
|
+
# @return [String] S2C key
|
25
|
+
# rubocop: disable Metrics/AbcSize
|
26
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
27
|
+
# rubocop: disable Metrics/MethodLength
|
28
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
29
|
+
def key_establish
|
30
|
+
sock = TCPSocket.new(@hostname, @port)
|
31
|
+
client = TTTLS13::Client.new(sock, @hostname, alpn: [ALPN])
|
32
|
+
client.connect
|
33
|
+
req = [
|
34
|
+
NtsNextProtocolNegotiation.new,
|
35
|
+
AeadAlgorithmNegotiation.new([AeadAlgorithm::AEAD_AES_SIV_CMAC_256]),
|
36
|
+
EndOfMessage.new
|
37
|
+
]
|
38
|
+
client.write(req.map(&:serialize))
|
39
|
+
res = []
|
40
|
+
buffer = ''
|
41
|
+
loop do
|
42
|
+
read, = IO.select([sock], nil, nil, 1)
|
43
|
+
if read.nil?
|
44
|
+
warn 'Timeout: receiving for NTS-KE messages'
|
45
|
+
exit 1
|
46
|
+
else
|
47
|
+
r, b = Ntske.response_deserialize(buffer + client.read)
|
48
|
+
res += r
|
49
|
+
buffer = b
|
50
|
+
end
|
51
|
+
|
52
|
+
# Error
|
53
|
+
er = res.find { |m| m.is_a?(ErrorRecord) }
|
54
|
+
raise "received Error(#{er.error_code.unpack1('n')})" unless er.nil?
|
55
|
+
|
56
|
+
# Warning
|
57
|
+
wr = res.find { |m| m.is_a?(WarningRecord) }
|
58
|
+
raise "received Warning(#{wr.warning_code})" unless wr.nil?
|
59
|
+
|
60
|
+
# End of Message
|
61
|
+
break if res.last&.is_a?(EndOfMessage) &&
|
62
|
+
res.count { |m| m.is_a?(EndOfMessage) } == 1 &&
|
63
|
+
buffer.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Next Protocol Negotiation
|
67
|
+
npn = res.select { |m| m.is_a?(NtsNextProtocolNegotiation) }
|
68
|
+
raise Exception if npn.nil? || npn.length != 1
|
69
|
+
|
70
|
+
# NTPv4 Server
|
71
|
+
server = res.find { |m| m.is_a?(Ntsv4ServerNegotiation) }&.server
|
72
|
+
|
73
|
+
# NTPv4 Port
|
74
|
+
port = res.find { |m| m.is_a?(Ntsv4PortNegotiation) }&.port
|
75
|
+
|
76
|
+
# Cookies for NTPv4
|
77
|
+
cookies = res.select { |m| m.is_a?(Cookie) }&.map(&:cookie)
|
78
|
+
raise Exception if cookies.empty?
|
79
|
+
|
80
|
+
# AEAD algorithm => C2S, S2C key
|
81
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-5.1
|
82
|
+
alg = res.find { |m| m.is_a?(AeadAlgorithmNegotiation) }&.algorithms
|
83
|
+
&.first
|
84
|
+
raise Exception if alg.nil?
|
85
|
+
|
86
|
+
key_len = 32 # only support AEAD_AES_SIV_CMAC_256
|
87
|
+
# NTPv4 context | AEAD Algorithm | C2S / S2C
|
88
|
+
# 0x00 0x00 | [refer IANA] | 0x00 / 0x01
|
89
|
+
c2s_key = client.exporter(KE_LABEL, "\x00\x00" + alg + "\x00", key_len)
|
90
|
+
s2c_key = client.exporter(KE_LABEL, "\x00\x00" + alg + "\x01", key_len)
|
91
|
+
|
92
|
+
[server, port, cookies, c2s_key, s2c_key]
|
93
|
+
end
|
94
|
+
# rubocop: enable Metrics/AbcSize
|
95
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
96
|
+
# rubocop: enable Metrics/MethodLength
|
97
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Nts
|
5
|
+
module Ntske
|
6
|
+
# https://www.iana.org/assignments/aead-parameters/aead-parameters.xhtml#aead-parameters-2
|
7
|
+
module AeadAlgorithm
|
8
|
+
AEAD_AES_SIV_CMAC_256 = "\x00\x0F"
|
9
|
+
end
|
10
|
+
|
11
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.5
|
12
|
+
class AeadAlgorithmNegotiation < Record
|
13
|
+
attr_reader :algorithms
|
14
|
+
|
15
|
+
# @param algorithms [Array of Nts::Ntske::AeadAlgorithm constants]
|
16
|
+
# @param c [Boolean]
|
17
|
+
#
|
18
|
+
# example:
|
19
|
+
# AeadAlgorithmNegotiation.new([
|
20
|
+
# AeadAlgorithm::AEAD_AES_SIV_CMAC_256
|
21
|
+
# ])
|
22
|
+
def initialize(algorithms, c = false)
|
23
|
+
super(c, RecordType::AEAD_ALGORITHM_NEGOTIATION)
|
24
|
+
@algorithms = algorithms
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param s [String]
|
28
|
+
# @param c [Boolean]
|
29
|
+
#
|
30
|
+
# @raise [Exception]
|
31
|
+
#
|
32
|
+
# @return [Nts::Ntske::AeadAlgorithmNegotiation]
|
33
|
+
def self.deserialize(s, c)
|
34
|
+
raise Exception unless (s.length % 2).zero?
|
35
|
+
|
36
|
+
AeadAlgorithmNegotiation.new(s.scan(/.{2}/), c)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @return [String]
|
42
|
+
def serialize_body
|
43
|
+
@algorithms.join
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.6
|
6
|
+
class Cookie < Record
|
7
|
+
attr_reader :cookie
|
8
|
+
|
9
|
+
# @param cookie [String]
|
10
|
+
# @param c [Boolean]
|
11
|
+
def initialize(cookie, c = false)
|
12
|
+
super(c, RecordType::NEW_COOKIE_FOR_NTPV4)
|
13
|
+
|
14
|
+
@cookie = cookie
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param s [String]
|
18
|
+
# @param c [Boolean]
|
19
|
+
#
|
20
|
+
# @return [Nts::Ntske::Cookie]
|
21
|
+
def self.deserialize(s, c)
|
22
|
+
Cookie.new(s, c)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def serialize_body
|
29
|
+
@cookie
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.1
|
6
|
+
class EndOfMessage < Record
|
7
|
+
def initialize
|
8
|
+
super(true, RecordType::END_OF_MESSAGE)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param s [String]
|
12
|
+
#
|
13
|
+
# @raise [Exception]
|
14
|
+
#
|
15
|
+
# @return [Nts::Ntske::EndOfMessage]
|
16
|
+
def self.deserialize(s)
|
17
|
+
raise Exception unless s.empty?
|
18
|
+
|
19
|
+
EndOfMessage.new
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
def serialize_body
|
26
|
+
''
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.3
|
6
|
+
module ErrorCode
|
7
|
+
UNRECOGNIZED_CRITICAL_RECORD = "\x00\x00"
|
8
|
+
BAD_REQUEST = "\x00\x01"
|
9
|
+
end
|
10
|
+
|
11
|
+
class ErrorRecord < Record
|
12
|
+
attr_reader :error_code
|
13
|
+
|
14
|
+
# @param error_code [Nts::Ntske::ErrorCode constant]
|
15
|
+
def initialize(error_code)
|
16
|
+
super(true, RecordType::ERROR)
|
17
|
+
@error_code = error_code
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param s [String]
|
21
|
+
#
|
22
|
+
# @raise [Exception]
|
23
|
+
#
|
24
|
+
# @return [Nts::Ntske::ErrorRecord]
|
25
|
+
def self.deserialize(s)
|
26
|
+
raise Exception unless s.length == 2
|
27
|
+
|
28
|
+
ErrorRecord.new(s)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# @return [String]
|
34
|
+
def serialize_body
|
35
|
+
@error_code
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Nts
|
5
|
+
module Ntske
|
6
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.2
|
7
|
+
class NtsNextProtocolNegotiation < Record
|
8
|
+
attr_reader :next_protocol
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super(false, RecordType::NTS_NEXT_PROTOCOL_NEGOTIATION)
|
12
|
+
@next_protocol = "\x00\x00" # Protocol ID 0 (NTPv4)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param s [String]
|
16
|
+
#
|
17
|
+
# @raise [Exception]
|
18
|
+
#
|
19
|
+
# @return [Nts::Ntske::NtsNextProtocolNegotiation]
|
20
|
+
def self.deserialize(s)
|
21
|
+
# only support NTPv4
|
22
|
+
raise Exception unless s == "\x00\x00"
|
23
|
+
|
24
|
+
NtsNextProtocolNegotiation.new
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @return [String]
|
30
|
+
def serialize_body
|
31
|
+
@next_protocol
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.8
|
6
|
+
class Ntsv4PortNegotiation < Record
|
7
|
+
attr_reader :port
|
8
|
+
|
9
|
+
# @param port [Integer]
|
10
|
+
# @param c [Boolean]
|
11
|
+
def initialize(port, c = false)
|
12
|
+
super(c, RecordType::NTPV4_PORT_NEGOTIATION)
|
13
|
+
@port = port
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param s [String]
|
17
|
+
# @param c [Boolean]
|
18
|
+
#
|
19
|
+
# @raise [Exception]
|
20
|
+
#
|
21
|
+
# @return [Nts::Ntske::Ntsv4PortNegotiation]
|
22
|
+
def self.deserialize(s, c)
|
23
|
+
raise Exception unless s.length == 2
|
24
|
+
|
25
|
+
Ntsv4PortNegotiation.new(s.unpack1('n'), c)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
def serialize_body
|
32
|
+
[@port].pack('n1')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.7
|
6
|
+
class Ntsv4ServerNegotiation < Record
|
7
|
+
attr_reader :server
|
8
|
+
|
9
|
+
# @param server [String]
|
10
|
+
# @param c [Boolean]
|
11
|
+
def initialize(server, c = false)
|
12
|
+
super(c, RecordType::NTPV4_SERVER_NEGOTIATION)
|
13
|
+
@server = server
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param s [String]
|
17
|
+
# @param c [Boolean]
|
18
|
+
#
|
19
|
+
# @raise [Exception]
|
20
|
+
#
|
21
|
+
# @return [Nts::Ntske::Ntsv4ServerNegotiation]
|
22
|
+
def self.deserialize(s, c)
|
23
|
+
raise Exception if s.nil? || s.empty?
|
24
|
+
|
25
|
+
Ntsv4ServerNegotiation.new(s, c)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
def serialize_body
|
32
|
+
@server
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nts
|
4
|
+
module Ntske
|
5
|
+
# https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-20#section-4.1.4
|
6
|
+
class WarningRecord < Record
|
7
|
+
attr_reader :warning_code
|
8
|
+
|
9
|
+
# @param warning_code [Integer]
|
10
|
+
def initialize(warning_code)
|
11
|
+
super(true, RecordType::WARNING)
|
12
|
+
@warning_code = warning_code
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param s [String]
|
16
|
+
#
|
17
|
+
# @raise [Exception]
|
18
|
+
#
|
19
|
+
# @return [Nts::Ntske:WarningRecord]
|
20
|
+
def self.deserialize(s)
|
21
|
+
raise Exception unless s.length == 2
|
22
|
+
|
23
|
+
WarningRecord.new(s.unpack1('n'))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
def serialize_body
|
30
|
+
[@warning_code].pack('n1')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|