blob: beb58760c4033038129ecb7afa9def155241108b [file] [log] [blame]
Jens Geyerc1d79432014-04-22 22:52:43 +02001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20using System;
21using System.Net.Security;
22using System.Net.Sockets;
23using System.Security.Authentication;
24using System.Security.Cryptography.X509Certificates;
25
26namespace Thrift.Transport
27{
28 /// <summary>
29 /// SSL Socket Wrapper class
30 /// </summary>
31 public class TTLSSocket : TStreamTransport
32 {
33 /// <summary>
34 /// Internal TCP Client
35 /// </summary>
36 private TcpClient client = null;
37
38 /// <summary>
39 /// The host
40 /// </summary>
41 private string host = null;
42
43 /// <summary>
44 /// The port
45 /// </summary>
46 private int port = 0;
47
48 /// <summary>
49 /// The timeout for the connection
50 /// </summary>
51 private int timeout = 0;
52
53 /// <summary>
54 /// Internal SSL Stream for IO
55 /// </summary>
56 private SslStream secureStream = null;
57
58 /// <summary>
59 /// Defines wheter or not this socket is a server socket<br/>
60 /// This is used for the TLS-authentication
61 /// </summary>
62 private bool isServer = false;
63
64 /// <summary>
65 /// The certificate
66 /// </summary>
67 private X509Certificate certificate = null;
68
69 /// <summary>
70 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
71 /// </summary>
72 /// <param name="client">An already created TCP-client</param>
73 /// <param name="certificate">The certificate.</param>
74 /// <param name="isServer">if set to <c>true</c> [is server].</param>
75 public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false)
76 {
77 this.client = client;
78 this.certificate = certificate;
79 this.isServer = isServer;
80
81 if (IsOpen)
82 {
83 base.inputStream = client.GetStream();
84 base.outputStream = client.GetStream();
85 }
86 }
87
88 /// <summary>
89 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
90 /// </summary>
91 /// <param name="host">The host, where the socket should connect to.</param>
92 /// <param name="port">The port.</param>
93 /// <param name="certificatePath">The certificate path.</param>
94 public TTLSSocket(string host, int port, string certificatePath)
95 : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath))
96 {
97 }
98
99 /// <summary>
100 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
101 /// </summary>
102 /// <param name="host">The host, where the socket should connect to.</param>
103 /// <param name="port">The port.</param>
104 /// <param name="certificate">The certificate.</param>
105 public TTLSSocket(string host, int port, X509Certificate certificate)
106 : this(host, port, 0, certificate)
107 {
108 }
109
110 /// <summary>
111 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
112 /// </summary>
113 /// <param name="host">The host, where the socket should connect to.</param>
114 /// <param name="port">The port.</param>
115 /// <param name="timeout">The timeout.</param>
116 /// <param name="certificate">The certificate.</param>
117 public TTLSSocket(string host, int port, int timeout, X509Certificate certificate)
118 {
119 this.host = host;
120 this.port = port;
121 this.timeout = timeout;
122 this.certificate = certificate;
123
124 InitSocket();
125 }
126
127 /// <summary>
128 /// Creates the TcpClient and sets the timeouts
129 /// </summary>
130 private void InitSocket()
131 {
132 this.client = new TcpClient();
133 client.ReceiveTimeout = client.SendTimeout = timeout;
134 client.Client.NoDelay = true;
135 }
136
137 /// <summary>
138 /// Sets Send / Recv Timeout for IO
139 /// </summary>
140 public int Timeout
141 {
142 set
143 {
144 this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
145 }
146 }
147
148 /// <summary>
149 /// Gets the TCP client.
150 /// </summary>
151 public TcpClient TcpClient
152 {
153 get
154 {
155 return client;
156 }
157 }
158
159 /// <summary>
160 /// Gets the host.
161 /// </summary>
162 public string Host
163 {
164 get
165 {
166 return host;
167 }
168 }
169
170 /// <summary>
171 /// Gets the port.
172 /// </summary>
173 public int Port
174 {
175 get
176 {
177 return port;
178 }
179 }
180
181 /// <summary>
182 /// Gets a value indicating whether TCP Client is Cpen
183 /// </summary>
184 public override bool IsOpen
185 {
186 get
187 {
188 if (this.client == null)
189 {
190 return false;
191 }
192
193 return this.client.Connected;
194 }
195 }
196
197 /// <summary>
198 /// Validates the certificates!<br/>
199 /// </summary>
200 /// <param name="sender">The sender-object.</param>
201 /// <param name="certificate">The used certificate.</param>
202 /// <param name="chain">The certificate chain.</param>
203 /// <param name="sslPolicyErrors">An enum, which lists all the errors from the .NET certificate check.</param>
204 /// <returns></returns>
205 private bool CertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
206 {
207 return (sslValidationErrors == SslPolicyErrors.None);
208 }
209
210 /// <summary>
211 /// Connects to the host and starts the routine, which sets up the TLS
212 /// </summary>
213 public override void Open()
214 {
215 if (IsOpen)
216 {
217 throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
218 }
219
220 if (String.IsNullOrEmpty(host))
221 {
222 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
223 }
224
225 if (port <= 0)
226 {
227 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
228 }
229
230 if (client == null)
231 {
232 InitSocket();
233 }
234
235 client.Connect(host, port);
236
237 setupTLS();
238 }
239
240 /// <summary>
241 /// Creates a TLS-stream and lays it over the existing socket
242 /// </summary>
243 public void setupTLS()
244 {
245 if (isServer)
246 {
247 // Server authentication
248 this.secureStream = new SslStream(this.client.GetStream(), false);
249 this.secureStream.AuthenticateAsServer(this.certificate, false, SslProtocols.Tls, true);
250 }
251 else
252 {
253 // Client authentication
254 X509CertificateCollection validCerts = new X509CertificateCollection();
255 validCerts.Add(certificate);
256
257 this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidator));
258 this.secureStream.AuthenticateAsClient(host, validCerts, SslProtocols.Tls, true);
259 }
260
261 inputStream = this.secureStream;
262 outputStream = this.secureStream;
263 }
264
265 /// <summary>
266 /// Closes the SSL Socket
267 /// </summary>
268 public override void Close()
269 {
270 base.Close();
271 if (this.client != null)
272 {
273 this.client.Close();
274 this.client = null;
275 }
276
277 if (this.secureStream != null)
278 {
279 this.secureStream.Close();
280 this.secureStream = null;
281 }
282 }
283 }
284}