| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | using IceRpc.Transports.Tcp.Internal; |
| | 4 | | using System.Net.Security; |
| | 5 | |
|
| | 6 | | namespace IceRpc.Transports.Tcp; |
| | 7 | |
|
| | 8 | | /// <summary>Implements <see cref="IDuplexClientTransport" /> for the tcp transport.</summary> |
| | 9 | | public class TcpClientTransport : IDuplexClientTransport |
| | 10 | | { |
| | 11 | | /// <inheritdoc/> |
| 39 | 12 | | public string Name => TcpName; |
| | 13 | |
|
| | 14 | | private const string SslName = "ssl"; |
| | 15 | | private const string TcpName = "tcp"; |
| | 16 | |
|
| | 17 | | private readonly TcpClientTransportOptions _options; |
| | 18 | |
|
| | 19 | | /// <summary>Constructs a <see cref="TcpClientTransport" />.</summary> |
| | 20 | | public TcpClientTransport() |
| 47 | 21 | | : this(new TcpClientTransportOptions()) |
| 47 | 22 | | { |
| 47 | 23 | | } |
| | 24 | |
|
| | 25 | | /// <summary>Constructs a <see cref="TcpClientTransport" />.</summary> |
| | 26 | | /// <param name="options">The transport options.</param> |
| 466 | 27 | | public TcpClientTransport(TcpClientTransportOptions options) => _options = options; |
| | 28 | |
|
| | 29 | | /// <inheritdoc/> |
| | 30 | | public IDuplexConnection CreateConnection( |
| | 31 | | ServerAddress serverAddress, |
| | 32 | | DuplexConnectionOptions options, |
| | 33 | | SslClientAuthenticationOptions? clientAuthenticationOptions) |
| 280 | 34 | | { |
| 280 | 35 | | if (serverAddress.Transport is string transport && !IsValidTransportName(transport, serverAddress.Protocol)) |
| 6 | 36 | | { |
| 6 | 37 | | throw new NotSupportedException( |
| 6 | 38 | | $"The Tcp client transport does not support server addresses with transport '{transport}'."); |
| | 39 | | } |
| | 40 | |
|
| 274 | 41 | | if (!CheckParams(serverAddress)) |
| 10 | 42 | | { |
| 10 | 43 | | throw new ArgumentException( |
| 10 | 44 | | $"The server address '{serverAddress}' contains parameters that are not valid for the Tcp client transpo |
| 10 | 45 | | nameof(serverAddress)); |
| | 46 | | } |
| | 47 | |
|
| 264 | 48 | | if (serverAddress.Transport is null) |
| 14 | 49 | | { |
| 14 | 50 | | serverAddress = serverAddress with { Transport = Name }; |
| 14 | 51 | | } |
| | 52 | |
|
| 264 | 53 | | SslClientAuthenticationOptions? authenticationOptions = clientAuthenticationOptions?.Clone() ?? |
| 264 | 54 | | (serverAddress.Transport == SslName ? new SslClientAuthenticationOptions() : null); |
| | 55 | |
|
| 264 | 56 | | if (authenticationOptions is not null) |
| 62 | 57 | | { |
| | 58 | | // We are establishing a secure TLS connection. It can rely on system certificates. |
| | 59 | |
|
| 62 | 60 | | authenticationOptions.TargetHost ??= serverAddress.Host; |
| | 61 | |
|
| | 62 | | // Set ApplicationProtocols to "ice" or "icerpc" in the common situation where the application does not |
| | 63 | | // specify any application protocol. This way, a proxy server listening on a port shared by multiple |
| | 64 | | // application protocols can use this ALPN protocol ID to forward all ice/icerpc traffic to an ice/icerpc |
| | 65 | | // back-end server. |
| | 66 | | // We do this only when the port is not the default port for ice or icerpc; when we use the IANA-registered |
| | 67 | | // default port, the server can and should use this port number to identify the application protocol when no |
| | 68 | | // ALPN protocol ID is provided. |
| 62 | 69 | | if (authenticationOptions.ApplicationProtocols is null && |
| 62 | 70 | | serverAddress.Port != serverAddress.Protocol.DefaultPort) |
| 62 | 71 | | { |
| 62 | 72 | | authenticationOptions.ApplicationProtocols = new List<SslApplicationProtocol> |
| 62 | 73 | | { |
| 62 | 74 | | new SslApplicationProtocol(serverAddress.Protocol.Name) |
| 62 | 75 | | }; |
| 62 | 76 | | } |
| 62 | 77 | | } |
| | 78 | |
|
| 264 | 79 | | return new TcpClientConnection( |
| 264 | 80 | | serverAddress, |
| 264 | 81 | | authenticationOptions, |
| 264 | 82 | | options.Pool, |
| 264 | 83 | | options.MinSegmentSize, |
| 264 | 84 | | _options); |
| | 85 | |
|
| | 86 | | static bool CheckParams(ServerAddress serverAddress) |
| 274 | 87 | | { |
| 274 | 88 | | if (serverAddress.Protocol == Protocol.Ice) |
| 26 | 89 | | { |
| 96 | 90 | | foreach (string name in serverAddress.Params.Keys) |
| 10 | 91 | | { |
| 10 | 92 | | switch (name) |
| | 93 | | { |
| | 94 | | case "t": |
| | 95 | | case "z": |
| | 96 | | // we don't check the value since we ignore it |
| 8 | 97 | | break; |
| | 98 | |
|
| | 99 | | default: |
| 2 | 100 | | return false; |
| | 101 | | } |
| 8 | 102 | | } |
| 24 | 103 | | return true; |
| | 104 | | } |
| | 105 | | else |
| 248 | 106 | | { |
| 248 | 107 | | return serverAddress.Params.Count == 0; |
| | 108 | | } |
| 274 | 109 | | } |
| | 110 | |
|
| | 111 | | static bool IsValidTransportName(string transportName, Protocol protocol) => |
| 260 | 112 | | protocol == Protocol.Ice ? transportName is TcpName or SslName : transportName is TcpName; |
| 264 | 113 | | } |
| | 114 | | } |