]>
Commit | Line | Data |
---|---|---|
aebc4d36 JH |
1 | package hotline |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "encoding/binary" | |
6 | "errors" | |
a2ef262a | 7 | "fmt" |
d4c152a4 | 8 | "io" |
aebc4d36 JH |
9 | ) |
10 | ||
a2ef262a JH |
11 | // Hotline handshake process |
12 | // | |
aebc4d36 JH |
13 | // After establishing TCP connection, both client and server start the handshake process |
14 | // in order to confirm that each of them comply with requirements of the other. | |
15 | // The information provided in this initial data exchange identifies protocols, | |
16 | // and their versions, used in the communication. In the case where, after inspection, | |
17 | // the capabilities of one of the subjects do not comply with the requirements of the other, | |
18 | // the connection is dropped. | |
19 | // | |
20 | // The following information is sent to the server: | |
21 | // Description Size Data Note | |
d9bc63a1 JH |
22 | // Protocol Type 4 TRTP 0x54525450 |
23 | // Sub-protocol Type 4 HOTL User defined | |
aebc4d36 JH |
24 | // VERSION 2 1 Currently 1 |
25 | // Sub-version 2 2 User defined | |
26 | // | |
27 | // The server replies with the following: | |
28 | // Description Size Data Note | |
d9bc63a1 | 29 | // Protocol Type 4 TRTP |
aebc4d36 | 30 | // Error code 4 Error code returned by the server (0 = no error) |
a2ef262a JH |
31 | |
32 | type handshake struct { | |
33 | Protocol [4]byte // Must be 0x54525450 TRTP | |
34 | SubProtocol [4]byte // Must be 0x484F544C HOTL | |
35 | Version [2]byte // Always 1 (?) | |
36 | SubVersion [2]byte // Always 2 (?) | |
37 | } | |
38 | ||
39 | // Write implements the io.Writer interface for handshake. | |
40 | func (h *handshake) Write(p []byte) (n int, err error) { | |
41 | if len(p) != handshakeSize { | |
42 | return 0, errors.New("invalid handshake size") | |
d4c152a4 JH |
43 | } |
44 | ||
a2ef262a JH |
45 | _ = binary.Read(bytes.NewBuffer(p), binary.BigEndian, h) |
46 | ||
47 | return len(p), nil | |
48 | } | |
49 | ||
50 | // Valid checks if the handshake contains valid protocol and sub-protocol IDs. | |
51 | func (h *handshake) Valid() bool { | |
52 | return h.Protocol == trtp && h.SubProtocol == hotl | |
53 | } | |
54 | ||
55 | var ( | |
d9bc63a1 | 56 | // trtp represents the Protocol Type "TRTP" in hex |
a2ef262a JH |
57 | trtp = [4]byte{0x54, 0x52, 0x54, 0x50} |
58 | ||
d9bc63a1 | 59 | // hotl represents the Sub-protocol Type "HOTL" in hex |
a2ef262a JH |
60 | hotl = [4]byte{0x48, 0x4F, 0x54, 0x4C} |
61 | ||
62 | // handshakeResponse represents the server's response after a successful handshake | |
63 | // Response with "TRTP" and no error code | |
64 | handshakeResponse = [8]byte{0x54, 0x52, 0x54, 0x50, 0x00, 0x00, 0x00, 0x00} | |
65 | ) | |
66 | ||
67 | const handshakeSize = 12 | |
68 | ||
69 | // performHandshake performs the handshake process. | |
70 | func performHandshake(rw io.ReadWriter) error { | |
aebc4d36 | 71 | var h handshake |
a2ef262a JH |
72 | |
73 | // Copy exactly handshakeSize bytes from rw to handshake | |
74 | if _, err := io.CopyN(&h, rw, handshakeSize); err != nil { | |
fd740bc4 | 75 | return fmt.Errorf("read handshake: %w", err) |
a2ef262a | 76 | } |
a2ef262a JH |
77 | if !h.Valid() { |
78 | return errors.New("invalid protocol or sub-protocol in handshake") | |
aebc4d36 JH |
79 | } |
80 | ||
a2ef262a | 81 | if _, err := rw.Write(handshakeResponse[:]); err != nil { |
fd740bc4 | 82 | return fmt.Errorf("send handshake response: %w", err) |
aebc4d36 JH |
83 | } |
84 | ||
a2ef262a | 85 | return nil |
aebc4d36 | 86 | } |