cosullivan / SmtpServer

A SMTP Server component written in C#
MIT License
676 stars 160 forks source link

Best way to block connections? #140

Closed large closed 8 months ago

large commented 3 years ago

Hi @cosullivan!

I wonder what is the best approach to block connections?

Already I have a list of IPs I want to block.
This list is checked in the "SessionCreated" event.

My approach is to throw an exception for the IPs that match:

           string ipSource = endpoint.Address.ToString();

            if(ipSource == "127.0.0.1")
                throw new Exception("IP is blocked");

This will trigger an "SessionFault" event and the socket is still "connected" as I can see it?
Are there any better approach?


Edit later in the day: I tested the same in your examples and the NetworkClient is not disposed after an Exception.

Check out ListenAsync() https://github.com/cosullivan/SmtpServer/blob/fe827eea98540e1df55c4f39a460eeaf4c99cf04/Src/SmtpServer/SmtpServer.cs#L114-L140

The exception handling after a OnSessionFaulted() here should could be handled like this:

                    catch (Exception ex)
                    {
                        OnSessionFaulted(new SessionFaultedEventArgs(sessionContext, ex));
                        if(sessionContext.NetworkClient != null)
                            sessionContext.NetworkClient.Dispose();
                    }

This will be equal to how the SmtpSession-class handle the NetworkClient.

large commented 3 years ago

(I opened the issue again if you wanted to add any comments on this 👍 )

cosullivan commented 3 years ago

Ok, thanks for finding that.. I will have a look in more detail later today.

FYI, I have been working on v8 (only Beta at the moment) and there are some API changes but for the most part the changes were made to take advantage of the latest .NET functionality and reduce allocations and increase performance.

https://github.com/cosullivan/SmtpServer/blob/develop/Version8.md

cosullivan commented 3 years ago

What is the desired result for blocking an IP? Do you want to return a standard SMTP error code back to the client or do you just want to block their connection from actually occurring?

If you want to return an SMTP status code to tell the client that their IP is blocked then either the SessionCreated event or the IMailboxFilter are the two places for this.

However, if you want to filter out IP addresses to stop them event creating a session then you could use a custom endpoint listener;

https://github.com/cosullivan/SmtpServer/blob/master/Src/SampleApp/Examples/CustomEndpointListenerExample.cs#L48

In thinking about it, it's probably a good function to add to the default endpoint listener, ie, the ability to support a hook to allow IP address filtering.

large commented 3 years ago

I really just want a silent disconnect of ips in the list, so your suggestion here is best 👍 Already using your custom endpointlistener so it was simple to implemented, tested and it works.

When return of null, the sessionfaulted is still triggered, is there a better way to end silent?

Sudo code:

    public sealed class CustomEndpointListener : IEndpointListener
    {
        readonly IEndpointListener _endpointListener;

        public CustomEndpointListener(IEndpointListener endpointListener)
        {
            _endpointListener = endpointListener;
        }

        public void Dispose()
        {
            _endpointListener.Dispose();
        }

        public async Task<INetworkStream> GetStreamAsync(ISessionContext context, CancellationToken cancellationToken)
        {
            var stream = await _endpointListener.GetStreamAsync(context, cancellationToken);
            var endpoint = (IPEndPoint)context.Properties[EndpointListener.RemoteEndPointKey];
            string ipSource = endpoint.Address.ToString();

            if(ipSource == "127.0.0.1")
            {
                    stream.Dispose();
                    return null;
            }
            return new CustomStream(stream, ipSource, ipPort);
        }
    }
cosullivan commented 9 months ago

Hi,

There are two ways you can do this now.

Firstly, you can listen for the SessionCreated event on the SmtpServer instance and check the IP address from there. The default EndpointListener will add the IP address to the session context (check out the SessionTracingExample). If you get an IP address that you want to block, you can simply throw an exception here and the client will be disconnected and closed.

The second option is similar to before whereby you can create a custom endpoint listener. From within the custom endpoint listener, get the underlying Pipe and you can dispose of that and return null.

Thanks, Cain.