/// <summary> /// Handles embedded messages from the server. /// </summary> /// <param name="message">The message.</param> public async void HandleEmbeddedMessage(byte[] message) { var code = MessageCode.Distributed.Unknown; try { var embeddedMessage = EmbeddedMessage.FromByteArray(message); code = embeddedMessage.DistributedCode; var distributedMessage = embeddedMessage.DistributedMessage; switch (code) { case MessageCode.Distributed.SearchRequest: // receiving a SearchRequest/3 from the server as an embedded message indicates that we are // operating as a branch root on the distributed network. SoulseekClient.DistributedConnectionManager.PromoteToBranchRoot(); var searchRequest = DistributedSearchRequest.FromByteArray(distributedMessage); _ = SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(message).ConfigureAwait(false); await SoulseekClient.SearchResponder.TryRespondAsync(searchRequest.Username, searchRequest.Token, searchRequest.Query).ConfigureAwait(false); break; default: Diagnostic.Debug($"Unhandled embedded message: {code}; {message.Length} bytes"); break; } } catch (Exception ex) { Diagnostic.Warning($"Error handling embedded message: {code}; {ex.Message}", ex); } }
private static void ControlTime() { var controls = actions.Where(t => t.Run()).ToList(); do { foreach (var action in controls) { action.UpdateTime(); var date = DateTime.Now; try { action.action(); Diagnostic.Debug(LOG, null, $"Performed {action.name} action in {(DateTime.Now - date).TotalSeconds} seconds. next round will be {action.Next}."); } catch (ThreadAbortException ex) { var track = Diagnostic.TrackObject(action); Diagnostic.Error(LOG, track, $"Thread aborted, the {action.name} creates exception."); Diagnostic.Error(LOG, ex); } catch (Exception ex) { var track = Diagnostic.TrackObject(action); Diagnostic.Error(LOG, track, $"Error to execute {action.name} action in the thread."); Diagnostic.Error(LOG, ex); } } Thread.Sleep(60000); // 1 minute } while (true); }
/// <summary> /// Handles incoming messages from distributed children. /// </summary> /// <param name="sender">The child <see cref="IMessageConnection"/> from which the message originated.</param> /// <param name="message">The message.</param> public async void HandleChildMessageRead(object sender, byte[] message) { var connection = (IMessageConnection)sender; var code = new MessageReader <MessageCode.Distributed>(message).ReadCode(); Diagnostic.Debug($"Distributed child message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})"); try { switch (code) { case MessageCode.Distributed.ChildDepth: break; case MessageCode.Distributed.Ping: var pingResponse = new DistributedPingResponse(SoulseekClient.GetNextToken()); await connection.WriteAsync(pingResponse).ConfigureAwait(false); break; default: Diagnostic.Debug($"Unhandled distributed child message: {code} from {connection.Username} ({connection.IPEndPoint}); {message.Length} bytes"); break; } } catch (Exception ex) { Diagnostic.Warning($"Error handling distributed child message: {code} from {connection.Username} ({connection.IPEndPoint}); {ex.Message}", ex); } }
/// <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); }
/// <summary> /// Handles outgoing messages, post send. /// </summary> /// <param name="sender">The <see cref="IMessageConnection"/> instance to which the message was sent.</param> /// <param name="args">The message event args.</param> public void HandleMessageWritten(object sender, MessageEventArgs args) { var connection = (IMessageConnection)sender; var code = new MessageReader <MessageCode.Peer>(args.Message).ReadCode(); Diagnostic.Debug($"Peer message sent: {code} ({connection.IPEndPoint}) (id: {connection.Id})"); }
private async Task <IMessageConnection> GetMessageConnectionOutboundDirectAsync(string username, IPEndPoint ipEndPoint, CancellationToken cancellationToken) { Diagnostic.Debug($"Attempting direct message connection to {username} ({ipEndPoint})"); var connection = ConnectionFactory.GetMessageConnection( username, ipEndPoint, SoulseekClient.Options.PeerConnectionOptions); connection.Type = ConnectionTypes.Outbound | ConnectionTypes.Direct; connection.MessageRead += SoulseekClient.PeerMessageHandler.HandleMessageRead; connection.MessageReceived += SoulseekClient.PeerMessageHandler.HandleMessageReceived; connection.MessageWritten += SoulseekClient.PeerMessageHandler.HandleMessageWritten; try { await connection.ConnectAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Diagnostic.Debug($"Failed to establish a direct message connection to {username} ({ipEndPoint}): {ex.Message}"); connection.Dispose(); throw; } Diagnostic.Debug($"Direct message connection to {username} ({ipEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); }
/// <summary> /// Handles outgoing messages to distributed children, post send. /// </summary> /// <param name="sender">The child <see cref="IMessageConnection"/> instance to which the message was sent.</param> /// <param name="args">The message event args.</param> public void HandleChildMessageWritten(object sender, MessageEventArgs args) { var connection = (IMessageConnection)sender; var code = new MessageReader <MessageCode.Distributed>(args.Message).ReadCode(); Diagnostic.Debug($"Distributed child message sent: {code} to {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})"); }
/// <summary> /// Returns an existing, or gets a new connection using the details in the specified /// <paramref name="connectToPeerResponse"/> and pierces the remote peer's firewall. /// </summary> /// <remarks> /// This method will be invoked from <see cref="Messaging.Handlers.ServerMessageHandler"/> upon receipt of an /// unsolicited <see cref="ConnectToPeerResponse"/> of type 'P' only. This connection should only be initiated if /// there is no existing connection; superceding should be avoided if possible. /// </remarks> /// <param name="connectToPeerResponse">The response that solicited the connection.</param> /// <returns>The operation context, including the new or updated connection.</returns> public async Task <IMessageConnection> GetOrAddMessageConnectionAsync(ConnectToPeerResponse connectToPeerResponse) { bool cached = true; var r = connectToPeerResponse; try { var connection = await MessageConnectionDictionary.GetOrAdd( r.Username, key => new Lazy <Task <IMessageConnection> >(() => GetConnection())).Value.ConfigureAwait(false); if (cached) { Diagnostic.Debug($"Retrieved cached message connection to {r.Username} ({r.IPEndPoint})"); } return(connection); } catch (Exception ex) { var msg = $"Failed to establish an inbound indirect message connection to {r.Username} ({r.IPEndPoint}): {ex.Message}"; Diagnostic.Debug(msg); Diagnostic.Debug($"Purging message connection cache of failed connection to {r.Username} ({r.IPEndPoint})."); MessageConnectionDictionary.TryRemove(r.Username, out _); throw new ConnectionException(msg, ex); } async Task <IMessageConnection> GetConnection() { cached = false; Diagnostic.Debug($"Attempting inbound indirect message connection to {r.Username} ({r.IPEndPoint}) for token {r.Token}"); var connection = ConnectionFactory.GetMessageConnection( r.Username, r.IPEndPoint, SoulseekClient.Options.PeerConnectionOptions); connection.Type = ConnectionTypes.Inbound | ConnectionTypes.Indirect; connection.MessageRead += SoulseekClient.PeerMessageHandler.HandleMessageRead; connection.MessageReceived += SoulseekClient.PeerMessageHandler.HandleMessageReceived; connection.Disconnected += MessageConnection_Disconnected; try { await connection.ConnectAsync().ConfigureAwait(false); var request = new PierceFirewall(r.Token).ToByteArray(); await connection.WriteAsync(request).ConfigureAwait(false); } catch { connection.Dispose(); throw; } Diagnostic.Debug($"Message connection to {r.Username} ({r.IPEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } }
/// <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 to the specified <paramref name="username"/> using the specified <paramref name="token"/>. /// </summary> /// <remarks>A direct connection is attempted first, and, if unsuccessful, an indirect connection is attempted.</remarks> /// <param name="username">The username of the user to which to connect.</param> /// <param name="ipEndPoint">The remote IP endpoint of the connection.</param> /// <param name="token">The token with which to initialize the connection.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>The operation context, including the new connection.</returns> public async Task <IConnection> GetTransferConnectionAsync(string username, IPEndPoint ipEndPoint, int token, CancellationToken cancellationToken) { using (var directCts = new CancellationTokenSource()) using (var directLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, directCts.Token)) using (var indirectCts = new CancellationTokenSource()) using (var indirectLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, indirectCts.Token)) { Diagnostic.Debug($"Attempting simultaneous direct and indirect transfer connections to {username} ({ipEndPoint})"); var direct = GetTransferConnectionOutboundDirectAsync(ipEndPoint, token, directLinkedCts.Token); var indirect = GetTransferConnectionOutboundIndirectAsync(username, token, indirectLinkedCts.Token); var tasks = new[] { direct, indirect }.ToList(); Task <IConnection> task; do { task = await Task.WhenAny(tasks).ConfigureAwait(false); tasks.Remove(task); }while (task.Status != TaskStatus.RanToCompletion && tasks.Count > 0); if (task.Status != TaskStatus.RanToCompletion) { var msg = $"Failed to establish a direct or indirect transfer connection to {username} ({ipEndPoint})"; Diagnostic.Debug(msg); throw new ConnectionException(msg); } var connection = await task.ConfigureAwait(false); var isDirect = task == direct; Diagnostic.Debug($"{(isDirect ? "Direct" : "Indirect")} transfer connection to {username} ({ipEndPoint}) established first, attempting to cancel {(isDirect ? "indirect" : "direct")} connection."); (isDirect ? indirectCts : directCts).Cancel(); try { if (isDirect) { var request = new PeerInit(SoulseekClient.Username, Constants.ConnectionType.Transfer, token).ToByteArray(); await connection.WriteAsync(request, cancellationToken).ConfigureAwait(false); } await connection.WriteAsync(BitConverter.GetBytes(token), cancellationToken).ConfigureAwait(false); } catch (Exception ex) { var msg = $"Failed to negotiate transfer connection to {username} ({ipEndPoint}): {ex.Message}"; Diagnostic.Debug($"{msg} (type: {connection.Type}, id: {connection.Id})"); connection.Dispose(); throw new ConnectionException(msg, ex); } Diagnostic.Debug($"Transfer connection to {username} ({ipEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } }
public static void Delete(FileInfo file) { System.IO.File.Delete(file.FullName); if (R.DebugMode) { Diagnostic.Debug(LOG, null, "File deleted: {0}", file.FullName); } }
private void TryRemoveMessageConnectionRecord(IMessageConnection connection) { if (MessageConnectionDictionary.TryRemove(connection.Username, out _)) { Diagnostic.Debug($"Removed message connection record for {connection.Key.Username} ({connection.IPEndPoint}) (type: {connection.Type}, id: {connection.Id})"); } Diagnostic.Debug($"Message connection cache now contains {MessageConnectionDictionary.Count} connections."); }
private void MessageConnection_Disconnected(object sender, ConnectionDisconnectedEventArgs e) { var connection = (IMessageConnection)sender; Diagnostic.Debug($"Message connection to {connection.Username} ({connection.IPEndPoint}) disconnected. (type: {connection.Type}, id: {connection.Id})"); TryRemoveMessageConnectionRecord(connection); connection.Dispose(); }
public static void Delete(FileInfo[] files) { foreach (var file in files) { System.IO.File.Delete(file.FullName); } if (R.DebugMode && files.Length > 0) { var track = Diagnostic.TrackMessages(Array.ConvertAll(files, t => t.FullName)); Diagnostic.Debug(LOG, null, "Deleted {0} files", files.Length); } }
private async Task <bool> TrySendSearchResults(string username, int token, string query) { if (SoulseekClient.Options.SearchResponseResolver == default) { return(false); } SearchResponse searchResponse = null; try { searchResponse = await SoulseekClient.Options.SearchResponseResolver(username, token, SearchQuery.FromText(query)).ConfigureAwait(false); } catch (Exception ex) { Diagnostic.Warning($"Error resolving search response for query '{query}' requested by {username} with token {token}: {ex.Message}", ex); return(false); } if (searchResponse == null) { return(false); } if (searchResponse.FileCount <= 0) { return(false); } try { Diagnostic.Debug($"Resolved {searchResponse.FileCount} files for query '{query}' with token {token} from {username}"); var endpoint = await SoulseekClient.GetUserEndPointAsync(username).ConfigureAwait(false); var peerConnection = await SoulseekClient.PeerConnectionManager.GetOrAddMessageConnectionAsync(username, endpoint, CancellationToken.None).ConfigureAwait(false); await peerConnection.WriteAsync(searchResponse.ToByteArray()).ConfigureAwait(false); Diagnostic.Debug($"Sent response containing {searchResponse.FileCount} files to {username} for query '{query}' with token {token}"); return(true); } catch (Exception ex) { Diagnostic.Debug($"Failed to send search response for {query} to {username}: {ex.Message}", ex); } return(false); }
/// <summary> /// Adds a new message connection from an incoming connection. /// </summary> /// <remarks> /// This method will be invoked from <see cref="ListenerHandler"/> upon receipt of an incoming unsolicited message /// only. Because this connection is fully established by the time it is passed to this method, it must supercede any /// cached connection, as it will be the most recently established connection as tracked by the remote user. /// </remarks> /// <param name="username">The username of the user from which the connection originated.</param> /// <param name="incomingConnection">The the accepted connection.</param> /// <returns>The operation context.</returns> public async Task AddMessageConnectionAsync(string username, IConnection incomingConnection) { var c = incomingConnection; try { await MessageConnectionDictionary.AddOrUpdate( username, new Lazy <Task <IMessageConnection> >(() => GetConnection()), (key, cachedConnectionRecord) => new Lazy <Task <IMessageConnection> >(() => GetConnection(cachedConnectionRecord))).Value.ConfigureAwait(false); } catch (Exception ex) { var msg = $"Failed to establish an inbound message connection to {username} ({c.IPEndPoint}): {ex.Message}"; Diagnostic.Debug($"{msg} (type: {c.Type}, id: {c.Id})"); Diagnostic.Debug($"Purging message connection cache of failed connection to {username} ({c.IPEndPoint})."); MessageConnectionDictionary.TryRemove(username, out _); throw new ConnectionException(msg, ex); } async Task <IMessageConnection> GetConnection(Lazy <Task <IMessageConnection> > cachedConnectionRecord = null) { Diagnostic.Debug($"Inbound message connection to {username} ({c.IPEndPoint}) accepted. (type: {c.Type}, id: {c.Id})"); var connection = ConnectionFactory.GetMessageConnection( username, c.IPEndPoint, SoulseekClient.Options.PeerConnectionOptions, c.HandoffTcpClient()); connection.Type = ConnectionTypes.Inbound | ConnectionTypes.Direct; connection.MessageRead += SoulseekClient.PeerMessageHandler.HandleMessageRead; connection.Disconnected += MessageConnection_Disconnected; Diagnostic.Debug($"Inbound message connection to {username} ({connection.IPEndPoint}) handed off. (old: {c.Id}, new: {connection.Id})"); if (cachedConnectionRecord != null) { var cachedConnection = await cachedConnectionRecord.Value.ConfigureAwait(false); cachedConnection.Disconnected -= MessageConnection_Disconnected; Diagnostic.Debug($"Superceding cached message connection to {username} ({cachedConnection.IPEndPoint}) (old: {cachedConnection.Id}, new: {connection.Id}"); } connection.StartReadingContinuously(); Diagnostic.Debug($"Message connection to {username} ({connection.IPEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } }
public string GetPassword() { try { return(k.Security.Decrypt(EPassword, User)); }catch (Exception ex) { var track = Diagnostic.TrackObject(this); Diagnostic.Debug(this, track, $"The invalid epassword, may be you added password before add user"); Diagnostic.Error(LOG, ex); throw new KException(LOG, E.Message.CredPasswordError_0); throw ex; } }
/// <summary> /// Awaits an incoming transfer connection from the specified <paramref name="username"/> for the specified /// <paramref name="filename"/> and <paramref name="remoteToken"/>. /// </summary> /// <remarks> /// After this method is invoked, a <see cref="TransferResponse"/> message with the <paramref name="remoteToken"/> /// must be sent to the <paramref name="username"/> via a message connection to signal the remote peer to initate the connection. /// </remarks> /// <param name="username">The username of the user from which the connection is expected.</param> /// <param name="filename">The filename associated with the expected transfer.</param> /// <param name="remoteToken">The remote token associated with the expected transfer.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>The operation context, including the established connection.</returns> public async Task <IConnection> AwaitTransferConnectionAsync(string username, string filename, int remoteToken, CancellationToken cancellationToken) { using (var directCts = new CancellationTokenSource()) using (var directLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, directCts.Token)) using (var indirectCts = new CancellationTokenSource()) using (var indirectLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, indirectCts.Token)) { Diagnostic.Debug($"Waiting for a direct or indirect connection from {username} with remote token {remoteToken} for {filename}"); // completed in ServerMessageHandler upon receipt of a ConnectToPeerResponse. var indirect = SoulseekClient.Waiter.Wait <IConnection>( key: new WaitKey(Constants.WaitKey.IndirectTransfer, username, filename, remoteToken), timeout: SoulseekClient.Options.TransferConnectionOptions.ConnectTimeout, cancellationToken: indirectLinkedCts.Token); // completed in AddTransferConnectionAsync when handling the incoming connection within ListenerHandler. var direct = SoulseekClient.Waiter.Wait <IConnection>( key: new WaitKey(Constants.WaitKey.DirectTransfer, username, remoteToken), timeout: SoulseekClient.Options.TransferConnectionOptions.ConnectTimeout, cancellationToken: directLinkedCts.Token); var tasks = new[] { direct, indirect }.ToList(); Task <IConnection> task; do { task = await Task.WhenAny(tasks).ConfigureAwait(false); tasks.Remove(task); }while (task.Status != TaskStatus.RanToCompletion && tasks.Count > 0); if (task.Status != TaskStatus.RanToCompletion) { var msg = $"Failed to establish a direct or indirect transfer connection to {username} with remote token {remoteToken} for {filename}"; Diagnostic.Debug(msg); throw new ConnectionException(msg); } var connection = await task.ConfigureAwait(false); var isDirect = task == direct; Diagnostic.Debug($"{(isDirect ? "Direct" : "Indirect")} transfer connection to {username} ({connection.IPEndPoint}) with remote token {remoteToken} for {filename} established first, attempting to cancel {(isDirect ? "indirect" : "direct")} connection."); (isDirect ? indirectCts : directCts).Cancel(); Diagnostic.Debug($"Transfer connection to {username} ({connection.IPEndPoint}) with remote token {remoteToken} for {filename} established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } }
private async Task <IMessageConnection> GetMessageConnectionOutboundIndirectAsync(string username, CancellationToken cancellationToken) { var solicitationToken = SoulseekClient.GetNextToken(); Diagnostic.Debug($"Soliciting indirect message connection to {username} with token {solicitationToken}"); try { PendingSolicitationDictionary.TryAdd(solicitationToken, username); await SoulseekClient.ServerConnection .WriteAsync(new ConnectToPeerRequest(solicitationToken, username, Constants.ConnectionType.Peer), cancellationToken) .ConfigureAwait(false); using var incomingConnection = await SoulseekClient.Waiter .Wait <IConnection>(new WaitKey (Constants.WaitKey.SolicitedPeerConnection, username, solicitationToken), SoulseekClient.Options.PeerConnectionOptions.ConnectTimeout, cancellationToken) .ConfigureAwait(false); var connection = ConnectionFactory.GetMessageConnection( username, incomingConnection.IPEndPoint, SoulseekClient.Options.PeerConnectionOptions, incomingConnection.HandoffTcpClient()); Diagnostic.Debug($"Indirect message connection to {username} ({incomingConnection.IPEndPoint}) handed off. (old: {incomingConnection.Id}, new: {connection.Id})"); connection.Type = ConnectionTypes.Outbound | ConnectionTypes.Indirect; connection.MessageRead += SoulseekClient.PeerMessageHandler.HandleMessageRead; connection.MessageReceived += SoulseekClient.PeerMessageHandler.HandleMessageReceived; connection.MessageWritten += SoulseekClient.PeerMessageHandler.HandleMessageWritten; connection.Disconnected += MessageConnectionProvisional_Disconnected; Diagnostic.Debug($"Indirect message connection to {username} ({connection.IPEndPoint}) established. (type: {connection.Type}, id: {connection.Id})"); return(connection); } catch (Exception ex) { Diagnostic.Debug($"Failed to establish an indirect message connection to {username} with token {solicitationToken}: {ex.Message}"); throw; } finally { PendingSolicitationDictionary.TryRemove(solicitationToken, out _); } }
internal static void End() { try { if (thread != null && thread.IsAlive) { thread.Abort(); } Diagnostic.Debug(LOG, null, $"Control thread finished"); } catch (Exception ex) { var track = Diagnostic.TrackObject(actions); Diagnostic.Error(LOG, track, $"Fatal control thread error trying to terminate thread."); Diagnostic.Error(LOG, ex); } }
/// <summary> /// Gets an existing message connection to the specified <paramref name="username"/>, if one exists. /// </summary> /// <param name="username">The username of the user for which to retrieve the cached connection.</param> /// <returns>The operation context, including the cached connection, or null if one does not exist.</returns> public async Task <IMessageConnection> GetCachedMessageConnectionAsync(string username) { try { if (MessageConnectionDictionary.TryGetValue(username, out var cached)) { var connection = await cached.Value.ConfigureAwait(false); Diagnostic.Debug($"Retrieved cached message connection to {connection.Username} ({connection.IPEndPoint}) (type: {connection.Type}, id: {connection.Id})"); return(connection); } } catch (Exception ex) { Diagnostic.Debug($"Failed to retrieve cached message connection to {username}: {ex.Message}"); } return(null); }
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 _); } }
internal static void Start() { try { if (actions == null) { actions = new List <ThreadStruct>(); } thread = new Thread(new ThreadStart(ControlTime)); thread.Start(); Diagnostic.Debug(LOG, null, $"Control thread started"); } catch (Exception ex) { var track = Diagnostic.TrackObject(actions); Diagnostic.Error(LOG, track, $"Fatal control thread error."); Diagnostic.Error(LOG, ex); } }
/// <summary> /// Sends the pending response matching the specified <paramref name="responseToken"/>, if one exists. /// </summary> /// <remarks> /// This overload is called by the listener when an incoming connection is established with a pierce firewall token, /// and if that token doesn't match a pending solicitation, and if the token matches a cached search response. In this case, /// the connection is retrieved from the cache and used to send the response. /// </remarks> /// <param name="responseToken">The token matching the pending response to send.</param> /// <returns>The operation context, including a value indicating whether a response was successfully sent.</returns> public async Task <bool> TryRespondAsync(int responseToken) { if (SoulseekClient.Options.SearchResponseCache != default) { bool cached; (string Username, int Token, string Query, SearchResponse SearchResponse)record; try { cached = SoulseekClient.Options.SearchResponseCache.TryRemove(responseToken, out record); } catch (Exception ex) { Diagnostic.Warning($"Error retrieving cached search response {responseToken}: {ex.Message}", ex); return(false); } if (cached) { var(username, token, query, searchResponse) = record; try { var peerConnection = await SoulseekClient.PeerConnectionManager.GetCachedMessageConnectionAsync(username).ConfigureAwait(false); await peerConnection.WriteAsync(searchResponse.ToByteArray()).ConfigureAwait(false); Diagnostic.Debug($"Sent cached response {responseToken} containing {searchResponse.FileCount + searchResponse.LockedFileCount} files to {username} for query '{query}' with token {token}"); ResponseDelivered?.Invoke(this, new SearchRequestResponseEventArgs(username, token, query, searchResponse)); return(true); } catch (Exception ex) { Diagnostic.Debug($"Failed to send cached search response {responseToken} to {username} for query '{query}' with token {token}: {ex.Message}", ex); ResponseDeliveryFailed?.Invoke(this, new SearchRequestResponseEventArgs(username, token, query, searchResponse)); } } } return(false); }
public async void HandleMessage(object sender, byte[] message) { var connection = (IMessageConnection)sender; var code = new MessageReader <MessageCode.Distributed>(message).ReadCode(); Diagnostic.Debug($"Distributed message received: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port})"); try { switch (code) { default: Diagnostic.Debug($"Unhandled distributed message: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port}); {message.Length} bytes"); break; } } catch (Exception ex) { Diagnostic.Warning($"Error handling distributed message: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port}); {ex.Message}", ex); } }
public static void ZipFiles(string[] files, string destination) { using (var zip = new ZipFile()) { foreach (var file in files) { if (System.IO.File.Exists(file)) { zip.AddFile(file); } zip.Save(destination); } if (R.DebugMode && files.Length > 0) { var track = Diagnostic.TrackMessages(files); Diagnostic.Debug(typeof(File).Name, track, "Compacted {0} files to {1}", files.Length, destination); } } }
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); }
public static void Add(Action staticMethod, string description, int minutes) { if (actions == null) { actions = new List <ThreadStruct>(); } var name = staticMethod.Method.DeclaringType.FullName; var thread = new ThreadStruct(staticMethod, description, minutes); var track = Diagnostic.TrackObject(thread); if (!actions.Where(t => t.name == name).Any()) { actions.Add(thread); Diagnostic.Debug(LOG, track, $"Added {thread.name} action in the thread. Details: {thread.description}."); } else { var index = actions.FindIndex(t => t.name == name); actions[index] = thread; Diagnostic.Debug(LOG, track, $"Updated {thread.name} action in the thread. Details: {thread.description}."); } }
/// <summary> /// Discards the cached response matching the specified <paramref name="responseToken"/>, if one exists. /// </summary> /// <param name="responseToken">The token matching the cached response to discard.</param> /// <returns>A value indicating whether the cached response was discarded.</returns> public bool TryDiscard(int responseToken) { if (SoulseekClient.Options.SearchResponseCache != default) { try { if (SoulseekClient.Options.SearchResponseCache.TryRemove(responseToken, out var response)) { var(username, token, query, searchResponse) = response; Diagnostic.Debug($"Discarded cached search response {responseToken} to {username} for query '{query}' with token {token}"); ResponseDeliveryFailed?.Invoke(this, new SearchRequestResponseEventArgs(username, token, query, searchResponse)); return(true); } } catch (Exception ex) { Diagnostic.Warning($"Error removing cached search response {responseToken}: {ex.Message}", ex); return(false); } } return(false); }
/// <summary> /// Handles outgoing messages, post send. /// </summary> /// <param name="sender">The <see cref="IMessageConnection"/> instance to which the message was sent.</param> /// <param name="args">The message event args.</param> public void HandleMessageWritten(object sender, MessageEventArgs args) { var code = new MessageReader <MessageCode.Distributed>(args.Message).ReadCode(); Diagnostic.Debug($"Distributed message sent: {code}"); }