blob: 9e1f9f22e7a16fc2bb1aa7f278f57acbea8b5d10 [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{
Jens Geyerd5436f52014-10-03 19:50:38 +020028 /// <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;
Jens Geyerc1d79432014-04-22 22:52:43 +020037
Jens Geyerd5436f52014-10-03 19:50:38 +020038 /// <summary>
39 /// The host
40 /// </summary>
41 private string host = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020042
Jens Geyerd5436f52014-10-03 19:50:38 +020043 /// <summary>
44 /// The port
45 /// </summary>
46 private int port = 0;
Jens Geyerc1d79432014-04-22 22:52:43 +020047
Jens Geyerd5436f52014-10-03 19:50:38 +020048 /// <summary>
49 /// The timeout for the connection
50 /// </summary>
51 private int timeout = 0;
Jens Geyerc1d79432014-04-22 22:52:43 +020052
Jens Geyerd5436f52014-10-03 19:50:38 +020053 /// <summary>
54 /// Internal SSL Stream for IO
55 /// </summary>
56 private SslStream secureStream = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020057
Jens Geyerd5436f52014-10-03 19:50:38 +020058 /// <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;
Jens Geyerc1d79432014-04-22 22:52:43 +020063
Jens Geyerd5436f52014-10-03 19:50:38 +020064 /// <summary>
65 /// The certificate
66 /// </summary>
67 private X509Certificate certificate = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020068
Jens Geyerd5436f52014-10-03 19:50:38 +020069 /// <summary>
70 /// User defined certificate validator.
71 /// </summary>
72 private RemoteCertificateValidationCallback certValidator = null;
Jens Geyer7b11fec2014-06-05 22:03:19 +020073
Jens Geyerd5436f52014-10-03 19:50:38 +020074 /// <summary>
75 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
76 /// </summary>
77 /// <param name="client">An already created TCP-client</param>
78 /// <param name="certificate">The certificate.</param>
79 /// <param name="isServer">if set to <c>true</c> [is server].</param>
80 public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false)
81 {
82 this.client = client;
83 this.certificate = certificate;
84 this.isServer = isServer;
Jens Geyerc1d79432014-04-22 22:52:43 +020085
Jens Geyerd5436f52014-10-03 19:50:38 +020086 if (IsOpen)
87 {
88 base.inputStream = client.GetStream();
89 base.outputStream = client.GetStream();
90 }
91 }
Jens Geyerc1d79432014-04-22 22:52:43 +020092
Jens Geyerd5436f52014-10-03 19:50:38 +020093 /// <summary>
94 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
95 /// </summary>
96 /// <param name="host">The host, where the socket should connect to.</param>
97 /// <param name="port">The port.</param>
98 /// <param name="certificatePath">The certificate path.</param>
99 /// <param name="certValidator">User defined cert validator.</param>
100 public TTLSSocket(string host, int port, string certificatePath, RemoteCertificateValidationCallback certValidator = null)
101 : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator)
102 {
103 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200104
Jens Geyerd5436f52014-10-03 19:50:38 +0200105 /// <summary>
106 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
107 /// </summary>
108 /// <param name="host">The host, where the socket should connect to.</param>
109 /// <param name="port">The port.</param>
110 /// <param name="certificate">The certificate.</param>
111 /// <param name="certValidator">User defined cert validator.</param>
112 public TTLSSocket(string host, int port, X509Certificate certificate, RemoteCertificateValidationCallback certValidator = null)
113 : this(host, port, 0, certificate, certValidator)
114 {
115 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200116
Jens Geyerd5436f52014-10-03 19:50:38 +0200117 /// <summary>
118 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
119 /// </summary>
120 /// <param name="host">The host, where the socket should connect to.</param>
121 /// <param name="port">The port.</param>
122 /// <param name="timeout">The timeout.</param>
123 /// <param name="certificate">The certificate.</param>
124 /// <param name="certValidator">User defined cert validator.</param>
125 public TTLSSocket(string host, int port, int timeout, X509Certificate certificate, RemoteCertificateValidationCallback certValidator = null)
126 {
127 this.host = host;
128 this.port = port;
129 this.timeout = timeout;
130 this.certificate = certificate;
131 this.certValidator = certValidator;
Jens Geyerc1d79432014-04-22 22:52:43 +0200132
Jens Geyerd5436f52014-10-03 19:50:38 +0200133 InitSocket();
134 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200135
Jens Geyerd5436f52014-10-03 19:50:38 +0200136 /// <summary>
137 /// Creates the TcpClient and sets the timeouts
138 /// </summary>
139 private void InitSocket()
140 {
141 this.client = new TcpClient();
142 client.ReceiveTimeout = client.SendTimeout = timeout;
143 client.Client.NoDelay = true;
144 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200145
Jens Geyerd5436f52014-10-03 19:50:38 +0200146 /// <summary>
147 /// Sets Send / Recv Timeout for IO
148 /// </summary>
149 public int Timeout
150 {
151 set
152 {
153 this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
154 }
155 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200156
Jens Geyerd5436f52014-10-03 19:50:38 +0200157 /// <summary>
158 /// Gets the TCP client.
159 /// </summary>
160 public TcpClient TcpClient
161 {
162 get
163 {
164 return client;
165 }
166 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200167
Jens Geyerd5436f52014-10-03 19:50:38 +0200168 /// <summary>
169 /// Gets the host.
170 /// </summary>
171 public string Host
172 {
173 get
174 {
175 return host;
176 }
177 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200178
Jens Geyerd5436f52014-10-03 19:50:38 +0200179 /// <summary>
180 /// Gets the port.
181 /// </summary>
182 public int Port
183 {
184 get
185 {
186 return port;
187 }
188 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200189
Jens Geyerd5436f52014-10-03 19:50:38 +0200190 /// <summary>
191 /// Gets a value indicating whether TCP Client is Cpen
192 /// </summary>
193 public override bool IsOpen
194 {
195 get
196 {
197 if (this.client == null)
198 {
199 return false;
200 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200201
Jens Geyerd5436f52014-10-03 19:50:38 +0200202 return this.client.Connected;
203 }
204 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200205
Jens Geyerd5436f52014-10-03 19:50:38 +0200206 /// <summary>
207 /// Validates the certificates!<br/>
208 /// </summary>
209 /// <param name="sender">The sender-object.</param>
210 /// <param name="certificate">The used certificate.</param>
211 /// <param name="chain">The certificate chain.</param>
212 /// <param name="sslPolicyErrors">An enum, which lists all the errors from the .NET certificate check.</param>
213 /// <returns></returns>
214 private bool CertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
215 {
216 return (sslValidationErrors == SslPolicyErrors.None);
217 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200218
Jens Geyerd5436f52014-10-03 19:50:38 +0200219 /// <summary>
220 /// Connects to the host and starts the routine, which sets up the TLS
221 /// </summary>
222 public override void Open()
223 {
224 if (IsOpen)
225 {
226 throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
227 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200228
Jens Geyerd5436f52014-10-03 19:50:38 +0200229 if (String.IsNullOrEmpty(host))
230 {
231 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
232 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200233
Jens Geyerd5436f52014-10-03 19:50:38 +0200234 if (port <= 0)
235 {
236 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
237 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200238
Jens Geyerd5436f52014-10-03 19:50:38 +0200239 if (client == null)
240 {
241 InitSocket();
242 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200243
Jens Geyerd5436f52014-10-03 19:50:38 +0200244 client.Connect(host, port);
Jens Geyerc1d79432014-04-22 22:52:43 +0200245
Jens Geyerd5436f52014-10-03 19:50:38 +0200246 setupTLS();
247 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200248
Jens Geyerd5436f52014-10-03 19:50:38 +0200249 /// <summary>
250 /// Creates a TLS-stream and lays it over the existing socket
251 /// </summary>
252 public void setupTLS()
253 {
254 if (isServer)
255 {
256 // Server authentication
257 this.secureStream = new SslStream(this.client.GetStream(), false);
258 this.secureStream.AuthenticateAsServer(this.certificate, false, SslProtocols.Tls, true);
259 }
260 else
261 {
262 // Client authentication
263 X509CertificateCollection validCerts = new X509CertificateCollection();
264 validCerts.Add(certificate);
Jens Geyerc1d79432014-04-22 22:52:43 +0200265
Jens Geyerd5436f52014-10-03 19:50:38 +0200266 if (this.certValidator != null)
267 {
268 this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(this.certValidator));
269 }
270 else
271 {
272 this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidator));
273 }
274 this.secureStream.AuthenticateAsClient(host, validCerts, SslProtocols.Tls, true);
275 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200276
Jens Geyerd5436f52014-10-03 19:50:38 +0200277 inputStream = this.secureStream;
278 outputStream = this.secureStream;
279 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200280
Jens Geyerd5436f52014-10-03 19:50:38 +0200281 /// <summary>
282 /// Closes the SSL Socket
283 /// </summary>
284 public override void Close()
285 {
286 base.Close();
287 if (this.client != null)
288 {
289 this.client.Close();
290 this.client = null;
291 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200292
Jens Geyerd5436f52014-10-03 19:50:38 +0200293 if (this.secureStream != null)
294 {
295 this.secureStream.Close();
296 this.secureStream = null;
297 }
298 }
299 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200300}