< Summary

Information
Class: Ice.SSL.TransceiverI
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/SSL/TransceiverI.cs
Tag: 71_18251537082
Line coverage
73%
Covered lines: 170
Uncovered lines: 60
Coverable lines: 230
Total lines: 514
Line coverage: 73.9%
Branch coverage
71%
Covered branches: 69
Total branches: 96
Branch coverage: 71.8%
Method coverage
91%
Covered methods: 21
Total methods: 23
Method coverage: 91.3%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
fd()100%11100%
initialize(...)70%11.171077.27%
closing(...)100%11100%
close()100%22100%
bind()100%210%
destroy()50%22100%
write(...)100%22100%
read(...)100%11100%
startRead(...)33.33%13.33641.18%
finishRead(...)60%13.71066.67%
startWrite(...)50%16850%
finishWrite(...)60%15.081062.96%
protocol()100%11100%
getInfo(...)100%44100%
checkSendSize(...)100%11100%
setBufferSize(...)100%11100%
ToString()100%11100%
toDetailedString()100%210%
.ctor(...)100%22100%
cancelSslHandshake()100%44100%
startAuthenticate(...)60%18.221056.52%
finishAuthenticate()75%4.07483.33%
validationCallback(...)90.91%22.072294.74%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/SSL/TransceiverI.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4using System.Net.Security;
 5using System.Net.Sockets;
 6using System.Security.Authentication;
 7using System.Security.Cryptography.X509Certificates;
 8
 9namespace Ice.SSL;
 10
 11#pragma warning disable CA1001 // _sslStream is disposed by destroy.
 12internal sealed class TransceiverI : Ice.Internal.Transceiver
 13#pragma warning restore CA1001
 14{
 115    public Socket fd() => _delegate.fd();
 16
 17    public int initialize(Ice.Internal.Buffer readBuffer, Ice.Internal.Buffer writeBuffer, ref bool hasMoreData)
 18    {
 119        if (!_isConnected)
 20        {
 121            int status = _delegate.initialize(readBuffer, writeBuffer, ref hasMoreData);
 122            if (status != Ice.Internal.SocketOperation.None)
 23            {
 124                return status;
 25            }
 126            _isConnected = true;
 27        }
 28
 129        Ice.Internal.Network.setBlock(fd(), true); // SSL requires a blocking socket
 30
 131        if (_sslStream == null)
 32        {
 33            try
 34            {
 135                _sslStream = new SslStream(
 136                    new NetworkStream(_delegate.fd(), ownsSocket: false),
 137                    leaveInnerStreamOpen: false);
 138            }
 039            catch (IOException ex)
 40            {
 041                if (Ice.Internal.Network.connectionLost(ex))
 42                {
 043                    throw new Ice.ConnectionLostException(ex);
 44                }
 45                else
 46                {
 047                    throw new Ice.SocketException(ex);
 48                }
 49            }
 150            return Ice.Internal.SocketOperation.Connect;
 51        }
 52
 53        Debug.Assert(_sslStream.IsAuthenticated);
 154        _authenticated = true;
 55
 156        _cipher = _sslStream.CipherAlgorithm.ToString();
 157        _instance.verifyPeer((ConnectionInfo)getInfo(_incoming, _adapterName, connectionId: ""), ToString());
 58
 159        if (_instance.securityTraceLevel() >= 1)
 60        {
 061            _instance.traceStream(_sslStream, ToString());
 62        }
 163        return Ice.Internal.SocketOperation.None;
 64    }
 65
 166    public int closing(bool initiator, Ice.LocalException ex) => _delegate.closing(initiator, ex);
 67
 68    public void close()
 69    {
 170        if (_sslStream != null)
 71        {
 172            cancelSslHandshake();
 173            _sslStream.Dispose(); // Disposing the stream also closes the socket.
 174            _sslStream = null;
 75        }
 76
 177        _delegate.close();
 178    }
 79
 80    public Ice.Internal.EndpointI bind()
 81    {
 82        Debug.Assert(false);
 083        return null;
 84    }
 85
 86    public void destroy()
 87    {
 188        _delegate.destroy();
 189        cancelSslHandshake();
 190        _sslStream?.Dispose();
 191        _sslHandshakeCts.Dispose();
 192    }
 93
 94    public int write(Ice.Internal.Buffer buf) =>
 95        // Force caller to use async write.
 196        buf.b.hasRemaining() ? Ice.Internal.SocketOperation.Write : Ice.Internal.SocketOperation.None;
 97
 98    public int read(Ice.Internal.Buffer buf, ref bool hasMoreData) =>
 99        // Force caller to use async read.
 1100        buf.b.hasRemaining() ? Ice.Internal.SocketOperation.Read : Ice.Internal.SocketOperation.None;
 101
 102    public bool startRead(Ice.Internal.Buffer buf, Ice.Internal.AsyncCallback callback, object state)
 103    {
 1104        if (!_isConnected)
 105        {
 1106            return _delegate.startRead(buf, callback, state);
 107        }
 108
 109        Debug.Assert(_sslStream != null && _sslStream.IsAuthenticated);
 110
 111        try
 112        {
 1113            _readResult = _sslStream.ReadAsync(buf.b.rawBytes(), buf.b.position(), buf.b.remaining());
 1114            _readResult.ContinueWith(
 1115                task => callback(state),
 1116                TaskScheduler.Default);
 1117            return false;
 118        }
 0119        catch (IOException ex)
 120        {
 0121            if (Ice.Internal.Network.connectionLost(ex))
 122            {
 0123                throw new Ice.ConnectionLostException(ex);
 124            }
 0125            if (Ice.Internal.Network.timeout(ex))
 126            {
 0127                throw new Ice.TimeoutException();
 128            }
 0129            throw new Ice.SocketException(ex);
 130        }
 0131        catch (ObjectDisposedException ex)
 132        {
 0133            throw new Ice.ConnectionLostException(ex);
 134        }
 0135        catch (Exception ex)
 136        {
 0137            throw new Ice.SyscallException(ex);
 138        }
 1139    }
 140
 141    public void finishRead(Ice.Internal.Buffer buf)
 142    {
 1143        if (!_isConnected)
 144        {
 1145            _delegate.finishRead(buf);
 1146            return;
 147        }
 1148        else if (_sslStream == null) // Transceiver was closed
 149        {
 0150            _readResult = null;
 0151            return;
 152        }
 153
 154        Debug.Assert(_readResult != null);
 155        try
 156        {
 157            int ret;
 158            try
 159            {
 1160                ret = _readResult.Result;
 1161            }
 1162            catch (AggregateException ex)
 163            {
 1164                throw ex.InnerException;
 165            }
 166
 1167            if (ret == 0)
 168            {
 1169                throw new Ice.ConnectionLostException();
 170            }
 171            Debug.Assert(ret > 0);
 1172            buf.b.position(buf.b.position() + ret);
 1173        }
 1174        catch (Ice.LocalException)
 175        {
 1176            throw;
 177        }
 1178        catch (IOException ex)
 179        {
 1180            if (Ice.Internal.Network.connectionLost(ex))
 181            {
 1182                throw new Ice.ConnectionLostException(ex);
 183            }
 0184            if (Ice.Internal.Network.timeout(ex))
 185            {
 0186                throw new Ice.TimeoutException();
 187            }
 0188            throw new Ice.SocketException(ex);
 189        }
 0190        catch (ObjectDisposedException ex)
 191        {
 0192            throw new Ice.ConnectionLostException(ex);
 193        }
 0194        catch (Exception ex)
 195        {
 0196            throw new Ice.SyscallException(ex);
 197        }
 1198    }
 199
 200    public bool startWrite(Internal.Buffer buf, Internal.AsyncCallback cb, object state, out bool messageWritten)
 201    {
 1202        if (!_isConnected)
 203        {
 1204            return _delegate.startWrite(buf, cb, state, out messageWritten);
 205        }
 206
 207        Debug.Assert(_sslStream != null);
 1208        if (!_authenticated)
 209        {
 1210            messageWritten = false;
 1211            return startAuthenticate(cb, state);
 212        }
 213
 214        try
 215        {
 1216            _writeResult = _sslStream.WriteAsync(buf.b.rawBytes(), buf.b.position(), buf.b.remaining());
 1217            _writeResult.ContinueWith(task => cb(state), TaskScheduler.Default);
 1218            messageWritten = true;
 1219            return false;
 220        }
 0221        catch (IOException ex)
 222        {
 0223            if (Ice.Internal.Network.connectionLost(ex))
 224            {
 0225                throw new Ice.ConnectionLostException(ex);
 226            }
 0227            if (Ice.Internal.Network.timeout(ex))
 228            {
 0229                throw new Ice.TimeoutException();
 230            }
 0231            throw new Ice.SocketException(ex);
 232        }
 0233        catch (ObjectDisposedException ex)
 234        {
 0235            throw new Ice.ConnectionLostException(ex);
 236        }
 0237        catch (Exception ex)
 238        {
 0239            throw new Ice.SyscallException(ex);
 240        }
 1241    }
 242
 243    public void finishWrite(Ice.Internal.Buffer buf)
 244    {
 1245        if (!_isConnected)
 246        {
 1247            _delegate.finishWrite(buf);
 1248            return;
 249        }
 1250        else if (_sslStream == null) // Transceiver was closed
 251        {
 0252            buf.b.position(buf.b.limit()); // Assume all the data was sent for at-most-once semantics.
 0253            _writeResult = null;
 0254            return;
 255        }
 1256        else if (!_authenticated)
 257        {
 1258            finishAuthenticate();
 1259            return;
 260        }
 261
 262        Debug.Assert(_writeResult != null);
 263        try
 264        {
 265            try
 266            {
 1267                _writeResult.Wait();
 1268            }
 1269            catch (AggregateException ex)
 270            {
 1271                throw ex.InnerException;
 272            }
 1273            buf.b.position(buf.b.position() + buf.b.remaining());
 1274        }
 1275        catch (IOException ex)
 276        {
 1277            if (Ice.Internal.Network.connectionLost(ex))
 278            {
 1279                throw new Ice.ConnectionLostException(ex);
 280            }
 0281            if (Ice.Internal.Network.timeout(ex))
 282            {
 0283                throw new Ice.TimeoutException();
 284            }
 0285            throw new Ice.SocketException(ex);
 286        }
 0287        catch (ObjectDisposedException ex)
 288        {
 0289            throw new Ice.ConnectionLostException(ex);
 290        }
 0291        catch (Exception ex)
 292        {
 0293            throw new Ice.SyscallException(ex);
 294        }
 1295    }
 296
 1297    public string protocol() => _delegate.protocol();
 298
 299    public Ice.ConnectionInfo getInfo(bool incoming, string adapterName, string connectionId)
 300    {
 301        Debug.Assert(incoming == _incoming);
 302        // adapterName is the name of the object adapter currently associated with this connection, while _adapterName
 303        // represents the name of the object adapter that created this connection (incoming only).
 304
 1305        return new Ice.SSL.ConnectionInfo(
 1306            _delegate.getInfo(incoming, adapterName, connectionId),
 1307            _cipher,
 1308            _sslStream is SslStream sslStream && sslStream.RemoteCertificate is X509Certificate2 remoteCertificate ?
 1309                [remoteCertificate] : [],
 1310            _verified);
 311    }
 312
 1313    public void checkSendSize(Ice.Internal.Buffer buf) => _delegate.checkSendSize(buf);
 314
 1315    public void setBufferSize(int rcvSize, int sndSize) => _delegate.setBufferSize(rcvSize, sndSize);
 316
 1317    public override string ToString() => _delegate.ToString();
 318
 0319    public string toDetailedString() => _delegate.toDetailedString();
 320
 321    // Only for use by ConnectorI, AcceptorI.
 1322    internal TransceiverI(
 1323        Instance instance,
 1324        Ice.Internal.Transceiver del,
 1325        string hostOrAdapterName,
 1326        bool incoming,
 1327        SslServerAuthenticationOptions serverAuthenticationOptions)
 328    {
 1329        _instance = instance;
 1330        _delegate = del;
 1331        _incoming = incoming;
 1332        if (_incoming)
 333        {
 1334            _adapterName = hostOrAdapterName;
 1335            _serverAuthenticationOptions = serverAuthenticationOptions;
 336        }
 337        else
 338        {
 1339            _host = hostOrAdapterName;
 340            Debug.Assert(_serverAuthenticationOptions is null);
 341        }
 342
 1343        _sslStream = null;
 344
 1345        _verifyPeer = _instance.properties().getIcePropertyAsInt("IceSSL.VerifyPeer");
 1346    }
 347
 348    /// <summary>
 349    /// If the SSL handshake is in progress, cancel it and wait for it to finish. This is used to ensure that the
 350    /// SSLStream is not disposed while the handshake is in progress.
 351    /// </summary>
 352    private void cancelSslHandshake()
 353    {
 1354        if (!_verified && _writeResult is not null)
 355        {
 1356            _sslHandshakeCts.Cancel();
 357            try
 358            {
 1359                _writeResult.Wait();
 1360                _writeResult = null;
 1361            }
 1362            catch
 363            {
 1364            }
 365        }
 366        Debug.Assert(_writeResult is null || _writeResult.IsCompleted);
 1367    }
 368
 369    private bool startAuthenticate(Ice.Internal.AsyncCallback callback, object state)
 370    {
 371        try
 372        {
 1373            if (_incoming)
 374            {
 1375                _writeResult = _sslStream.AuthenticateAsServerAsync(
 1376                    _serverAuthenticationOptions ??
 1377                    _instance.engine().createServerAuthenticationOptions(validationCallback),
 1378                    _sslHandshakeCts.Token);
 379            }
 380            else
 381            {
 1382                _writeResult = _sslStream.AuthenticateAsClientAsync(
 1383                    _instance.initializationData().clientAuthenticationOptions ??
 1384                    _instance.engine().createClientAuthenticationOptions(validationCallback, _host),
 1385                    _sslHandshakeCts.Token);
 386            }
 1387            _writeResult.ContinueWith(
 1388                task => callback(state),
 1389                TaskScheduler.Default);
 1390        }
 0391        catch (IOException ex)
 392        {
 0393            if (Ice.Internal.Network.connectionLost(ex))
 394            {
 395                // This situation occurs when connectToSelf is called; the "remote" end closes the socket immediately.
 0396                throw new ConnectionLostException(ex);
 397            }
 0398            throw new SocketException(ex);
 399        }
 0400        catch (AuthenticationException ex)
 401        {
 0402            throw new SecurityException(
 0403                _errorDescription.Length == 0 ? "SSL authentication failure." : _errorDescription,
 0404                ex);
 405        }
 0406        catch (System.Exception ex)
 407        {
 0408            throw new Ice.SyscallException(ex);
 409        }
 410
 411        Debug.Assert(_writeResult != null);
 1412        return false;
 413    }
 414
 415    private void finishAuthenticate()
 416    {
 417        Debug.Assert(_writeResult != null);
 418        try
 419        {
 420            try
 421            {
 422                // If authentication fails the task throws AuthenticationException.
 1423                _writeResult.Wait();
 1424                _verified = true;
 1425                _cipher = _sslStream.CipherAlgorithm.ToString();
 1426            }
 1427            catch (AggregateException ex)
 428            {
 1429                throw ex.InnerException;
 430            }
 1431        }
 1432        catch (IOException ex)
 433        {
 1434            if (Ice.Internal.Network.connectionLost(ex))
 435            {
 436                // This situation occurs when connectToSelf is called; the "remote" end closes the socket immediately.
 1437                throw new Ice.ConnectionLostException();
 438            }
 0439            throw new Ice.SocketException(ex);
 440        }
 1441        catch (AuthenticationException ex)
 442        {
 1443            throw new SecurityException(
 1444                _errorDescription.Length == 0 ? "SSL authentication failure." : _errorDescription,
 1445                ex);
 446        }
 0447        catch (System.Exception ex)
 448        {
 0449            throw new Ice.SyscallException(ex);
 450        }
 1451    }
 452
 453    private bool validationCallback(
 454        object sender,
 455        X509Certificate certificate,
 456        X509Chain chain,
 457        SslPolicyErrors policyErrors)
 458    {
 1459        int errors = (int)policyErrors;
 1460        int traceLevel = _instance.securityTraceLevel();
 1461        string traceCategory = _instance.securityTraceCategory();
 1462        Ice.Logger logger = _instance.logger();
 1463        string message = "";
 464
 1465        if (_incoming && (errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) != 0 && _verifyPeer <= 1)
 466        {
 467            // The client certificate is optional when IceSSL.VerifyPeer = 1, and not required when IceSSL.VerifyPeer = 
 1468            errors ^= (int)SslPolicyErrors.RemoteCertificateNotAvailable;
 469        }
 470
 1471        if ((errors & (int)SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
 472        {
 1473            message += ": Remote certificate name mismatch";
 474        }
 475
 1476        if ((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) != 0)
 477        {
 1478            message += ": Remote certificate not available";
 479        }
 480
 1481        foreach (X509ChainStatus status in chain?.ChainStatus ?? [])
 482        {
 1483            message += $": {status.StatusInformation}";
 484        }
 485
 1486        if (errors != 0)
 487        {
 1488            _errorDescription =
 1489                message.Length > 0 ? $"SSL authentication failure{message}." : "SSL authentication failure.";
 1490            if (traceLevel >= 1)
 491            {
 0492                logger.trace(traceCategory, _errorDescription);
 493            }
 494        }
 1495        return errors == 0;
 496    }
 497
 1498    private string _errorDescription = "";
 499    private readonly Instance _instance;
 500    private readonly Ice.Internal.Transceiver _delegate;
 1501    private readonly string _host = "";
 1502    private readonly string _adapterName = "";
 503    private readonly bool _incoming;
 504    private SslStream _sslStream;
 505    private readonly int _verifyPeer;
 506    private bool _isConnected;
 507    private bool _authenticated;
 508    private Task _writeResult;
 509    private Task<int> _readResult;
 510    private string _cipher;
 511    private bool _verified;
 512    private readonly SslServerAuthenticationOptions _serverAuthenticationOptions;
 1513    private readonly CancellationTokenSource _sslHandshakeCts = new();
 514}