/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; namespace Thrift.Transport { /// /// SSL Server Socket Wrapper Class /// public class TTLSServerSocket : TServerTransport { /// /// Underlying tcp server /// private TcpListener server = null; /// /// The port where the socket listen /// private int port = 0; /// /// Timeout for the created server socket /// private readonly int clientTimeout; /// /// Whether or not to wrap new TSocket connections in buffers /// private bool useBufferedSockets = false; /// /// The servercertificate with the private- and public-key /// private X509Certificate serverCertificate; /// /// The function to validate the client certificate. /// private RemoteCertificateValidationCallback clientCertValidator; /// /// The function to determine which certificate to use. /// private LocalCertificateSelectionCallback localCertificateSelectionCallback; /// /// The SslProtocols value that represents the protocol used for authentication. /// private readonly SslProtocols sslProtocols; /// /// Initializes a new instance of the class. /// /// The port where the server runs. /// The certificate object. public TTLSServerSocket(int port, X509Certificate2 certificate) : this(port, 0, certificate) { } /// /// Initializes a new instance of the class. /// /// The port where the server runs. /// Send/receive timeout. /// The certificate object. public TTLSServerSocket(int port, int clientTimeout, X509Certificate2 certificate) : this(port, clientTimeout, false, certificate) { } /// /// Initializes a new instance of the class. /// /// The port where the server runs. /// Send/receive timeout. /// If set to true [use buffered sockets]. /// The certificate object. /// The certificate validator. /// The callback to select which certificate to use. /// The SslProtocols value that represents the protocol used for authentication. public TTLSServerSocket( int port, int clientTimeout, bool useBufferedSockets, X509Certificate2 certificate, RemoteCertificateValidationCallback clientCertValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+. SslProtocols sslProtocols = SslProtocols.Tls) { if (!certificate.HasPrivateKey) { throw new TTransportException(TTransportException.ExceptionType.Unknown, "Your server-certificate needs to have a private key"); } this.port = port; this.clientTimeout = clientTimeout; this.serverCertificate = certificate; this.useBufferedSockets = useBufferedSockets; this.clientCertValidator = clientCertValidator; this.localCertificateSelectionCallback = localCertificateSelectionCallback; this.sslProtocols = sslProtocols; try { // Create server socket this.server = TSocketVersionizer.CreateTcpListener(this.port); this.server.Server.NoDelay = true; } catch (Exception ex) { server = null; throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex); } } /// /// Starts the server. /// public override void Listen() { // Make sure accept is not blocking if (this.server != null) { try { this.server.Start(); } catch (SocketException sx) { throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx); } } } /// /// Callback for Accept Implementation /// /// /// TTransport-object. /// protected override TTransport AcceptImpl() { if (this.server == null) { throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket."); } try { TcpClient client = this.server.AcceptTcpClient(); client.SendTimeout = client.ReceiveTimeout = this.clientTimeout; //wrap the client in an SSL Socket passing in the SSL cert TTLSSocket socket = new TTLSSocket( client, this.serverCertificate, true, this.clientCertValidator, this.localCertificateSelectionCallback, this.sslProtocols); socket.setupTLS(); if (useBufferedSockets) { TBufferedTransport trans = new TBufferedTransport(socket); return trans; } else { return socket; } } catch (Exception ex) { throw new TTransportException(ex.ToString(), ex); } } /// /// Stops the Server /// public override void Close() { if (this.server != null) { try { this.server.Stop(); } catch (Exception ex) { throw new TTransportException("WARNING: Could not close server socket: " + ex, ex); } this.server = null; } } } }