1 /*
2  * hunt-proton: AMQP Protocol library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.proton.engine.impl.ssl.SslHandshakeSniffingTransportWrapper;
13 
14 import hunt.proton.engine.impl.HandshakeSniffingTransportWrapper;
15 import hunt.proton.engine.impl.TransportWrapper;
16 import hunt.proton.engine.impl.ssl.SslTransportWrapper;
17 import std.conv:to;
18 import hunt.Exceptions;
19 import hunt.io.ByteBuffer;
20 /**
21  * SslHandshakeSniffingTransportWrapper
22  *
23  */
24 
25 class SslHandshakeSniffingTransportWrapper : HandshakeSniffingTransportWrapper!(SslTransportWrapper, TransportWrapper)
26     , SslTransportWrapper
27 {
28 
29     this(SslTransportWrapper ssl, TransportWrapper plain) {
30         super(ssl, plain);
31     }
32 
33     public string getCipherName()
34     {
35         if(isSecureWrapperSelected())
36         {
37             return _wrapper1.getCipherName();
38         }
39         else
40         {
41             return null;
42         }
43     }
44 
45 
46     public string getProtocolName()
47     {
48         if (isSecureWrapperSelected())
49         {
50             return _wrapper1.getProtocolName();
51         }
52         else
53         {
54             return null;
55         }
56     }
57 
58     override
59     int pending()
60     {
61         return super.pending();
62     }
63 
64     override
65      ByteBuffer head()
66      {
67          return super.head();
68      }
69 
70     override void pop(int bytes)
71     {
72         return super.pop(bytes);
73     }
74 
75     override void close_head()
76     {
77         super.close_head();
78     }
79 
80     private bool isSecureWrapperSelected()
81     {
82         return _selectedTransportWrapper == _wrapper1;
83     }
84 
85     override
86     protected int bufferSize() {
87         // minimum length for determination
88         return 5;
89     }
90 
91     override
92     protected void makeDetermination(byte[] bytesInput)
93     {
94         bool isSecure = checkForSslHandshake(bytesInput);
95         if (isSecure)
96         {
97             _selectedTransportWrapper = _wrapper1;
98         }
99         else
100         {
101             _selectedTransportWrapper = _wrapper2;
102         }
103     }
104 
105     // TODO perhaps the sniffer should save up the bytes from each
106     // input call until it has sufficient bytes to make the determination
107     // and only then pass them to the secure or plain wrapped transport?
108     private bool checkForSslHandshake(byte[] buf)
109     {
110         if (buf.length >= bufferSize())
111         {
112             /*
113              * SSLv2 Client Hello format
114              * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
115              *
116              * Bytes 0-1: RECORD-LENGTH Byte 2: MSG-CLIENT-HELLO (1) Byte 3:
117              * CLIENT-VERSION-MSB Byte 4: CLIENT-VERSION-LSB
118              *
119              * Allowed versions: 2.0 - SSLv2 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS
120              * 1.1 3.3 - TLS 1.2
121              *
122              * The version sent in the Client-Hello is the latest version
123              * supported by the client. NSS may send version 3.x in an SSLv2
124              * header for maximum compatibility.
125              */
126             bool isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
127                     ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2
128                                                      // (v3.1-3.3)
129                     (buf[3] == 2 && buf[4] == 0)); // SSL 2
130 
131             /*
132              * SSLv3/TLS Client Hello format RFC 2246
133              *
134              * Byte 0: ContentType (handshake - 22) Bytes 1-2: ProtocolVersion
135              * {major, minor}
136              *
137              * Allowed versions: 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS 1.1 3.3 -
138              * TLS 1.2
139              */
140             bool isSSL3Handshake = buf[0] == 22 && // handshake
141                     (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2
142                                                   // (v3.1-3.3)
143 
144             return (isSSL2Handshake || isSSL3Handshake);
145         }
146         else
147         {
148             throw new IllegalArgumentException("Too few bytes (" ~ to!string(buf.length) ~ ") to make SSL/plain  determination.");
149         }
150     }
151 
152 }