/// <summary> /// Adds a new transfer connection from an incoming connection. /// </summary> /// <param name="username">The username of the user from which the connection originated.</param> /// <param name="token">The token with which the firewall was pierced.</param> /// <param name="incomingConnection">The accepted connection.</param> /// <returns>The operation context.</returns> public async Task <(IConnection Connection, int RemoteToken)> AddTransferConnectionAsync(string username, int token, IConnection incomingConnection) { Diagnostic.Debug($"Inbound transfer connection to {username} ({incomingConnection.IPEndPoint}) for token {token} accepted. (type: {incomingConnection.Type}, id: {incomingConnection.Id}"); var connection = ConnectionFactory.GetTransferConnection( incomingConnection.IPEndPoint, SoulseekClient.Options.TransferConnectionOptions, incomingConnection.HandoffTcpClient()); connection.Type = ConnectionTypes.Inbound | ConnectionTypes.Direct; connection.Disconnected += (sender, e) => Diagnostic.Debug($"Transfer connection to {username} ({connection.IPEndPoint}) for token {token} disconnected. (type: {connection.Type}, id: {connection.Id})"); Diagnostic.Debug($"Inbound transfer connection to {username} ({connection.IPEndPoint}) for token {token} handed off. (old: {incomingConnection.Id}, new: {connection.Id})"); int remoteToken; try { var remoteTokenBytes = await connection.ReadAsync(4).ConfigureAwait(false); remoteToken = BitConverter.ToInt32(remoteTokenBytes, 0); } catch (Exception ex) { var msg = $"Failed to establish an inbound transfer connection to {username} ({incomingConnection.IPEndPoint}) for token {token}: {ex.Message}"; Diagnostic.Debug($"{msg} (type: {connection.Type}, id: {connection.Id})"); connection.Dispose(); throw new ConnectionException(msg, ex); } Diagnostic.Debug($"Transfer connection to {username} ({connection.IPEndPoint}) for token {remoteToken} established. (type: {connection.Type}, id: {connection.Id})"); return(connection, remoteToken); }
/// <summary> /// Gets a new transfer connection using the details in the specified <paramref name="connectToPeerResponse"/>, /// pierces the remote peer's firewall, and retrieves the remote token. /// </summary> /// <param name="connectToPeerResponse">The response that solicited the connection.</param> /// <returns>The operation context, including the new connection and the associated remote token.</returns> public async Task <(IConnection Connection, int RemoteToken)> GetTransferConnectionAsync(ConnectToPeerResponse connectToPeerResponse) { Diagnostic.Debug($"Attempting inbound indirect transfer connection to {connectToPeerResponse.Username} ({connectToPeerResponse.IPEndPoint}) for token {connectToPeerResponse.Token}"); var connection = ConnectionFactory.GetTransferConnection( connectToPeerResponse.IPEndPoint, SoulseekClient.Options.TransferConnectionOptions); connection.Type = ConnectionTypes.Inbound | ConnectionTypes.Indirect; connection.Disconnected += (sender, e) => Diagnostic.Debug($"Transfer connection to {connectToPeerResponse.Username} ({connectToPeerResponse.IPEndPoint}) for token {connectToPeerResponse.Token} disconnected. (type: {connection.Type}, id: {connection.Id})"); int remoteToken; try { await connection.ConnectAsync().ConfigureAwait(false); var request = new PierceFirewall(connectToPeerResponse.Token); await connection.WriteAsync(request.ToByteArray()).ConfigureAwait(false); var remoteTokenBytes = await connection.ReadAsync(4).ConfigureAwait(false); remoteToken = BitConverter.ToInt32(remoteTokenBytes, 0); } catch (Exception ex) { var msg = $"Failed to establish an inbound indirect transfer connection to {connectToPeerResponse.Username} ({connectToPeerResponse.IPEndPoint}): {ex.Message}"; Diagnostic.Debug(msg); connection.Dispose(); throw new ConnectionException(msg, ex); } Diagnostic.Debug($"Transfer connection to {connectToPeerResponse.Username} ({connectToPeerResponse.IPEndPoint}) for token {connectToPeerResponse.Token} established. (type: {connection.Type}, id: {connection.Id})"); return(connection, remoteToken); }
private async Task <IConnection> GetTransferConnectionOutboundIndirectAsync(string username, int token, CancellationToken cancellationToken) { Diagnostic.Debug($"Soliciting indirect transfer connection to {username} with token {token}"); var solicitationToken = SoulseekClient.GetNextToken(); try { PendingSolicitationDictionary.TryAdd(solicitationToken, username); await SoulseekClient.ServerConnection .WriteAsync(new ConnectToPeerRequest(solicitationToken, username, Constants.ConnectionType.Transfer).ToByteArray(), cancellationToken) .ConfigureAwait(false); using (var incomingConnection = await SoulseekClient.Waiter .Wait <IConnection>(new WaitKey(Constants.WaitKey.SolicitedPeerConnection, username, solicitationToken), SoulseekClient.Options.TransferConnectionOptions.ConnectTimeout, cancellationToken) .ConfigureAwait(false)) { var connection = ConnectionFactory.GetTransferConnection( incomingConnection.IPEndPoint, SoulseekClient.Options.TransferConnectionOptions, incomingConnection.HandoffTcpClient()); Diagnostic.Debug($"Indirect transfer connection to {username} ({incomingConnection.IPEndPoint}) handed off. (old: {incomingConnection.Id}, new: {connection.Id})"); connection.Type = ConnectionTypes.Outbound | ConnectionTypes.Indirect; connection.Disconnected += (sender, e) => Diagnostic.Debug($"Transfer connection for token {token} ({incomingConnection.IPEndPoint}) disconnected. (type: {connection.Type}, id: {connection.Id})"); Diagnostic.Debug($"Indirect transfer connection for {token} ({connection.IPEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } } catch (Exception ex) { Diagnostic.Debug($"Failed to establish an indirect transfer connection to {username} with token {token}: {ex.Message}"); throw; } finally { PendingSolicitationDictionary.TryRemove(solicitationToken, out var _); } }
private async Task <IConnection> GetTransferConnectionOutboundDirectAsync(IPEndPoint ipEndPoint, int token, CancellationToken cancellationToken) { Diagnostic.Debug($"Attempting direct transfer connection for token {token} to {ipEndPoint}"); var connection = ConnectionFactory.GetTransferConnection(ipEndPoint, SoulseekClient.Options.TransferConnectionOptions); connection.Type = ConnectionTypes.Outbound | ConnectionTypes.Direct; connection.Disconnected += (sender, e) => Diagnostic.Debug($"Transfer connection for token {token} to {ipEndPoint} disconnected. (type: {connection.Type}, id: {connection.Id})"); try { await connection.ConnectAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Diagnostic.Debug($"Failed to establish a direct transfer connection for token {token} to ({ipEndPoint}): {ex.Message}"); connection.Dispose(); throw; } Diagnostic.Debug($"Direct transfer connection for {token} to {connection.IPEndPoint} established. (type: {connection.Type}, id: {connection.Id})"); return(connection); }