Git Product home page Git Product logo

smtpd's People

Contributors

aluvare avatar awoodbeck avatar axllent avatar bhorvath avatar blueimp avatar egonelbre avatar ggilley avatar legionus avatar mhale avatar motienko avatar pierremarc avatar prologic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

smtpd's Issues

Parse DATA error?

How to send DATA end?
https://github.com/mhale/smtpd/blob/master/smtpd.go#L252

$ nc localhost 2525
220 localhost MyServerApp SMTP Service ready
EHLO hello server
250 localhost greets hello server
MAIL FROM:<[email protected]>
250 Ok
RCPT TO:<[email protected]>
250 Ok
DATA
354 Start mail input; end with <CR><LF>.<CR><LF>
From:[email protected]
To:[email protected]
Subject:Test

<h1>Test</h1>

\r\n.\r\n

.\r\n

but nothing.

My locale

LANG=ru_RU.UTF-8
LC_CTYPE=ru_RU.UTF-8
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=

From and To addresses with leading spaces

We found that two of our systems are unable to send emails to our service that uses smtpd as the smtp handler. Tracking it down to the errors for 'invalid FROM parameter' and 'invalid TO parameter'. Found that updating the regex to allow for a single space handles this and then trimmed the addresses as they were added. I was hoping this could be evaluated and added so that we don't have to use a custom fork. Had issues making a PR so adding the code changes here. Thank you.

26:rcptToRE = regexp.MustCompile(`[Tt][Oo]:\s?<(.+)>`)
27:mailFromRE = regexp.MustCompile(`[Ff][Rr][Oo][Mm]:\s?<(.*)>(\s(.*))?`) // Delivery Status Notifications are sent with "MAIL FROM:<>"
294:from = strings.TrimSpace(match[1])
334:to = append(to, strings.TrimSpace(match[1]))

tls support

is there tls support planned or can you provide guidance how to implement it?

Ability to set maximum recipients

Hi there.

Currently there is a maximum of 100 recipients hardcoded. Whilst I understand that this should be a default, the RFC states that an SMTP server must handle a minimum of 100 recipients, not the maximum. I use your awesome package in Mailpit (email / SMTP testing tool) and it would be extremely helpful if your module could allow a custom max to be set.

Thank you.

How to gracefully shutdown smtpd?

You mentioned that you modeled it after the built-in http server. It has a Shutdown method for gracefully shutting down the service. How do I do the equivalent with smtpd? Thanks!

[Question] Suitable for receiving incoming mail from the wider web?

I apologise in advance for the non technical question, but i am attempting to set up a "simple" (ha) service to receive incoming mail (*@domain.com) and dump it into an sql database.

This looked ideal, but my lack of understanding exactly how email works has me stumped.
I have built the example used in the readme, but listening on port 25 and using fmt.Printf so i can see the output in the terminal. I uploaded the binary to a test server, ssh in and ran it sudo ./go-email.

I can connect with telnet from my local machine telnet 195.201.233.74 25 and it connect fine.
I have added A and MX records on a subdomain (gomail.fl9.uk), and checked with mxtoolbox, and it seems happy:
https://mxtoolbox.com/SuperTool.aspx?action=mx%3agomail.fl9.uk&run=toolpage#

Connecting to 195.201.233.74

220 ubuntu-2gb-fsn1-1 MyServerApp ESMTP Service ready [742 ms]
EHLO keeper-us-east-1c.mxtoolbox.com
250-ubuntu-2gb-fsn1-1 greets keeper-us-east-1c.mxtoolbox.com
250-SIZE 0
250 ENHANCEDSTATUSCODES [696 ms]
MAIL FROM:<[email protected]>
250 2.1.0 Ok [703 ms]
RCPT TO:<[email protected]>
250 2.1.5 Ok [703 ms]

LookupServer 4500ms

I can send emails from my gmail, to [email protected], with no bounces or other errors.
However, i am not getting any output in the terminal thats running the program.

Have i completely misunderstood how this works?

how to get the body of the email?

great work! i've managed to get everything set up but dunno how to get the body of the msg. I know how to get the subject but how do i get the body?

    msg, _ := mail.ReadMessage(bytes.NewReader(data))
    subject := msg.Header.Get("Subject")
    log.Printf("Received mail from %s for %s with subject %s", from, to[0], subject)
    return nil

Example not working

Hi,

I tried to use your simple example in readme.md
But calling mail.ReadMessage actually raises an error malformed MIME header: missing colon: "..."

func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
	msg, err := mail.ReadMessage(bytes.NewReader(data))
	if err != nil {
		log.Print(err)
		return nil
	}
        return nil
}

It seems that the mail package is not able to parse the RFC 2821 Received header properly. Any idea how to fix this?

Save state after authentication

Hello! Thanks for creating this library, it's really useful and I love the simplicity πŸ‘Œ I'm looking at the example for authentication that you give in the readme...

func authHandler(remoteAddr net.Addr, mechanism string, username []byte, password []byte, shared []byte) (bool, error) {
    return string(username) == "valid" && string(password) == "password", nil
}

Is there a way to store some kind of state at authentication time that can later be used in a mail handler? That way you could, for instance, only allow sending emails that are from that user's account.

If not, do you have any ideas for how to implement this? I'd be really happy to help with an implementation 😁

(as a side-note, docs for this library can't be viewed at pkg.go.dev/github.com/mhale/smtpd because you don't have a license, you may want to create a simple LICENSE file)

manage smtp response

For the moment, if I'm not wrong, I have no way to answer a different SMTP response according to my code.

For example I want to implement a rate-limiting and so want to answer SMTP Error 451 if exceeded.

Possibility to publish a version

Nice job for the implementation! I was wondering, how about this implementation could be versioned and published so it could be used as a library? I'm using the GitHub link right now for importing, but the version is 0.0.0 and could be prone to new changes if any.

The version I retrieve from my go.mod file with the unclear versioning is: v0.0.0-20210322105601-438c8edb069c.

smtpd as MTA?

could you please write very basic example of MTA?

Fix go.mod

Hello! I really want to use this package in my project, but there is one major problem: it's impossible to use this package in go.mod file in my project because of package and module different names ("github.com/mhale/smtpd" and "github.com/prologic/smtpd" respectively), so go.mod can't find module to use. Would be awesome if you fixed this!

Add AUTH support

Please add support for authentication. It will be very useful to have a callback to check the username and password.

sending Email

how i can use it for send email for other server like @gmail.com

`net.LookupAddr` can take so long that clients run into a timeout

The net.LookupAddr function is used at a few places in the code, for example right after accepting a connection. If that function does not terminate quickly big response times could arise that risk timeouts or major slowdowns.

I suggest to remove the lookup because it seems this is only used for diagnostics and the reverse lookup is flaky anyway. Please be aware that even if instead a very short timeout is added this can easily pile up to a major slowdown if called hundreds of times, say in a CI test. Even when the lookup is working as intended this can be a relatively slow process that may involve several DNS lookups.

Background

I am using this trough axllent/mailpit for local development and CI. In my Gitlab CI setup several services are running that try to connect via SMTP. For some reason in my particular environment net.LookupAddr takes 10s (returns an error). Unfortunately that does not only slow down things a lot but also breaks several timeouts.

Arguably there is something off with the reverse lookup in my environment but because the lookup is such an unreliable thing to do I would use that opportunity to get rid of it 😒 In case you are interested, I think it is a bug somewhere in Docker, musl or Go (what a line up…), but it is hard to get a small reproducible setup of this, currently I can reproduce that only through jobs in a Gitlab runner…

It was a quite a journey to spot this issue, but I was able to reproduce the 10s spent between the initial TCP SYN and the first message (220 host Mailpit ESMTP Service ready) by isolating the net.LookupAddr call that was made, and all of that in CI jobs, what a day πŸ˜„. Hope that saves someone else's day.

Return Message ID in SMTP response after message

Currently after the DATA command is issued the response always returns as 250 2.0.0 Ok: queued, some systems rely on receiving the SMTP Message ID in the SMTP response for logging purposes.

It would be good if either a custom function could be passed in for generating the SMTP Message ID, or if the Handle function could return a Message ID that will be used in the response.

Ideally the response would be formatted as 250 2.0.0 Ok: queued as <message-id>

On shutdown, smtpd waits for one more message before shutting down

Hello, I'm looking at implementing a graceful shutdown but can't quite get it to work. Here's my code that starts the SMTP listener:

func listenEmail(ctx context.Context, port string) {
    srv := &smtpd.Server{
        Addr: "0.0.0.0:" + port,
        Handler: mailHandler,
        Appname: "...",
        Hostname: "...",
    }

    go func() {
        <- ctx.Done()

        log.Println("We should stop the SMTP server now...")
        srv.Close()
        log.Println("We closed it")
    }()

    err := srv.ListenAndServe()
    if err != nil {
        log.Fatal("Unable to start smtp server: ", err)
        os.Exit(1)
    }

    log.Println("listenEmail goroutine quits")
}

Not shown here, but on Ctrl+C, ctx gets cancelled, so the inner goroutine runs and calls srv.Close(). When I run this, the "We closed it" message from the inner goroutine gets printed, but the final "listenEmail goroutine quits" does not. I assume that's because ListenAndServe still keeps blocking.

I was looking at the smtpd source code:

// Serve creates a new SMTP session after a network connection is established.
func (srv *Server) Serve(ln net.Listener) error {
	if atomic.LoadInt32(&srv.inShutdown) != 0 {
		return ErrServerClosed
	}

	defer ln.Close()
	for {

		// if we are shutting down, don't accept new connections
		select {
		case <-srv.getShutdownChan():
			return ErrServerClosed
		default:
		}

		conn, err := ln.Accept()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				continue
			}
			return err
		}

		session := srv.newSession(conn)
		atomic.AddInt32(&srv.openSessions, 1)
		go session.serve()
	}
}

To me it looks like it checks the shutdown condition at the start of the for loop, and then blocks at ln.Accept(). So, I'm guessing, it needs to receive one extra email for the loop to advance and the Serve call to return. Does that make sense? I'm a Go novice and am stepping on rakes and making wrong guesses left and right :-)

I also run a HTTP listener (http.Server) in a similar way, and it is able to finish up immediately, without waiting for one extra HTTP request. Perhaps there's a viable way to make smtpd work the same?

Originally posted by @cuu508 in #27 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.