public async Task <CQConnection> ConnectTo(string hostport) { var parts = hostport.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); var hostName = parts[0]; var portNumber = parts.Length > 1 ? int.Parse(parts[1]) : 18593; var connection = await CQConnection.Create(_crypt, hostName, portNumber, _rsa, _onStatusChange); ValidateWhitelistAndSetAlias(connection); lock (_connections) { // NOTE: prior connections to same peer are not removed, by design _connections.Add(connection); } try { NewConnection?.Invoke(this, new CQConnectionEventArgs { Connection = connection }); } catch (Exception ex) { ex.Log(); } $"Connected to [{connection}]".Log(); return(connection); }
/// <summary> /// Used to create a CQConnection not-yet-connected to a remote. /// </summary> /// <returns> /// The returned <see cref="CQConnection"/> is valid for all uses. /// </returns> public static async Task <CQConnection> Create( Crypt crypt, string hostName, int portNumber, RSA rsa, Action <string> onStatusChange) { var connection = new CQConnection(crypt, hostName, portNumber, onStatusChange); $"Establishing connection to [{hostName}:{portNumber}]..".Log(); var tcpClient = new TcpClient(); tcpClient.NoDelay = true; tcpClient.ReceiveBufferSize = 16; await tcpClient.ConnectAsync(hostName, portNumber); connection._tcpClient = tcpClient; $"Initiating conversation with [{hostName}:{portNumber}]..".Log(); await connection.InitiateConversation(rsa); // NOTE: the returned connection is valid for all uses. ha // thumbprint, alias, and is actively sending/receiving data.. return(connection); }
private void ValidateWhitelistAndSetAlias(CQConnection connection) { $"Checking whitelist for {connection}".Log(); // check thumbprint against whitelist, if not in whitelist then // force a disconnect if (_whitelist.TryGetAlias(connection.Peer.Thumbprint, out string alias)) { connection.Peer.Alias = alias; _onStatusChange($"{connection} whitelisted as '{connection.Peer.Alias}' ({connection.Peer.Thumbprint})".Log()); } else { connection.Peer.Alias = connection.Peer.Thumbprint; var err = $@"Rejecting {connection.Peer.Thumbprint} -- not authorized. Use `/WHITELIST <thumbprint>` and `/BAN <thumbprint>` to authorized/deauthorize.".Log(); _onStatusChange(err); throw new Exception(err); } }
/// <summary> /// Used to create a CQConnection for a TcpClient accepted by a listener. /// </summary> /// <param name="tcpClient"></param> /// <returns></returns> public static async Task <CQConnection> Create( Crypt crypt, TcpClient tcpClient, RSA rsa, Action <string> onStatusChange) { var ipEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint; var hostName = ipEndPoint.Address.ToString(); // TODO: reverse dns? var portNumber = ipEndPoint.Port; var connection = new CQConnection(crypt, hostName, portNumber, onStatusChange); connection._tcpClient = tcpClient; $"Accepting conversation with [{hostName}:{portNumber}]..".Log(); await connection.AcceptConversation(rsa); // NOTE: the returned connection is valid for all uses. ha // thumbprint, alias, and is actively sending/receiving data.. return(connection); }
public async Task OnConnectionAccepted(TcpClient tcpClient) { var connection = await CQConnection.Create( _crypt, tcpClient, _rsa, _onStatusChange); connection.ConversationEnded += (s, e) => { lock (_connections) { if (_connections.Remove(connection)) { $"Removed {connection}".Log(); } } }; ValidateWhitelistAndSetAlias(connection); $"Accepted [{connection}]".Log(); lock (_connections) { _connections.Add(connection); try { NewConnection?.Invoke(this, new CQConnectionEventArgs { Connection = connection }); } catch (Exception ex) { ex.Log(); } } }