ms

package module
v0.3.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2020 License: MIT Imports: 14 Imported by: 0

README

ms

go.dev reference GitHub release (latest SemVer) GitHub Go Report Card

Simple mail service to deliver emails to remote SMTP servers with features:

  • DMIC Signing
  • Message-ID Creation
  • MX Lookups

How to Properly Send Emails for Beginners

To be successfully delivered, an email need to pass 3 spam checks:

Sender Policy Framework (SPF)

SPF checks if your IP address is actually authorized to send emails for your domain.

To pass SPF, you need to have a TXT DNS record for your domain.

Checkout https://dmarcian.com/spf-syntax-table/ for detailed SPF syntax.

A sample record is v=spf1 a mx -all which tells remote SMTP servers to accept the IP addresses have either A, AAA or MX DNS record for the domain.

DomainKeys Identified Mail (DKIM)

DKIM is a bit trickier than SPF. It's basically a public key infrastructure for emails. For this, you'll need a public and private key. You can generate the keys by third party tools like openssl or with crypto packages of Go.

Checkout https://knowledge.ondmarc.redsift.com/en/articles/2141527-generating-1024-bits-dkim-public-and-private-keys-using-openssl-on-a-mac for openssl.

You can use the following code to generate keys in Go:

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"log"
	"os"
)

func main() {
	privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		log.Fatal(err)
	}
	privateKeyData := pem.EncodeToMemory(&pem.Block{
		Type:    "RSA PRIVATE KEY",
		Bytes:   x509.MarshalPKCS1PrivateKey(privateKey),
	})
	err = ioutil.WriteFile("private.key", privateKeyData, os.ModeType)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("private key is saved to private.key")
	publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
	if err != nil {
		log.Fatal(err)
	}
	publicKeyData := pem.EncodeToMemory(&pem.Block{
		Type:    "PUBLIC KEY",
		Bytes:   publicKeyBytes,
	})
	log.Println(string(publicKeyData))
}

Keep private.key file to use with ms later.

Create a DNS TXT record for hostname <dkim-selector>._domainkey.<your-domain.com> with the value v=DKIM1; p=<base64-encoded-public-key>

Replace <dkim-selector> with any identifier you like for the public key, replace <your-domain.com> with your domain, Replace <base64-encoded-public-key> with base64 encoded version of your public key without the whitespace, for example hostname is default._domainkey.example.com and value is v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4O4fVFHCIcBx5TlQHypEmS1efHnhSQ3aUXOTdttHGrVQaVj8/7Mzzc1xKtjl3UO2Y6JU2h6jGBid8umbxQ14PpnfHyX4B7oWlVbm8ipUabRIr1hLRH9BGFOxHYfGjgESx0LdnyWJ6S2OnB7YMlQ/DR2TLArh8hoLCqs1YwNm3QwIDAQAB

Domain-based Message Authentication, Reporting & Conformance (DMARC)

DMARC is a policy based on SPF and DKIM, again works via TXT DNS records. Checkout https://en.wikipedia.org/wiki/DMARC for more info.

A basic TXT DNS record for DMARC looks like: hostname: _dmarc.<your-domain.com>, value: v=DMARC1; p=reject; rua=mailto:admin@<your-domain.com> which tells remote SMTP servers to reject the mails that fails previous spam checks and send reports to admin@<your-domain.com>

Sending Mail via ms

After completing the previous steps, now we're ready to use ms. A sample program looks like: (Be sure you run the program within a host whose IP address passes the SPF check)

package ms_test

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"github.com/cevatbarisyilmaz/ms"
	"io/ioutil"
	"log"
)

func Example() {
	const (
		domain            = "yourdomain.com" // Your Domain
		dkimSelector      = "default"        // DKIM Selector To Use
		privateKeyPemFile = "private.key"    // Location of DKIM Private Key File
	)

	// Get the Private Key for DKIM Signatures
	var privateKey *rsa.PrivateKey
	privateKeyData, err := ioutil.ReadFile(privateKeyPemFile)
	if err != nil {
		log.Fatal("reading private key file has failed:", err)
	}
	pemBlock, _ := pem.Decode(privateKeyData)
	if pemBlock == nil {
		log.Fatal("pem block is corrupted")
	}
	privateKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
	if err != nil {
		log.Fatal("parsing der form of private key has failed:", err)
	}
	log.Println("private key is loaded from the pem file")

	// Create the mail service
	mailService := ms.New(domain, dkimSelector, privateKey)

	// Create a new mail
	mail := &ms.Mail{
		Headers: map[string][]byte{
			"From":    []byte("\"Your Domain\" <noreply@yourdomain.com>"),
			"To":      []byte("someuser@anotherdomain.com"),
			"Subject": []byte("Don't Mind Me, Just Testing Some Mail Library"),
		},
		Body: []byte("This mail confirms that you successfully setup ms!"),
	}

	// Send the mail via mail service
	report, err := mailService.Send(mail)
	if err != nil {
        	log.Fatal(err)
	}
	if len(report) != 0 {
		log.Fatal(report)
	}
	log.Println("success")
}

Documentation

Overview

Package ms provides a simple email service to send emails to remote SMTP servers

Example
package main

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"github.com/cevatbarisyilmaz/ms"
	"io/ioutil"
	"log"
)

func main() {
	const (
		domain            = "yourdomain.com" // Your Domain
		dkimSelector      = "default"        // DKIM Selector To Use
		privateKeyPemFile = "private.key"    // Location of DKIM Private Key File
	)

	// Get the Private Key for DKIM Signatures
	var privateKey *rsa.PrivateKey
	privateKeyData, err := ioutil.ReadFile(privateKeyPemFile)
	if err != nil {
		log.Fatal("reading private key file has failed:", err)
	}
	pemBlock, _ := pem.Decode(privateKeyData)
	if pemBlock == nil {
		log.Fatal("pem block is corrupted")
	}
	privateKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
	if err != nil {
		log.Fatal("parsing der form of private key has failed:", err)
	}
	log.Println("private key is loaded from the pem file")

	// Create the mail service
	mailService := ms.New(domain, dkimSelector, privateKey)

	// Create a new mail
	mail := &ms.Mail{
		Headers: map[string][]byte{
			"From":    []byte("\"Your Domain\" <noreply@yourdomain.com>"),
			"To":      []byte("someuser@anotherdomain.com"),
			"Subject": []byte("Don't Mind Me, Just Testing Some Mail Library"),
		},
		Body: []byte("This mail confirms that you successfully setup ms!"),
	}

	// Send the mail via mail service
	report, err := mailService.Send(mail)
	if err != nil {
		log.Fatal(err)
	}
	if len(report) != 0 {
		log.Fatal(report)
	}
	log.Println("success")
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Mail

type Mail struct {
	// From, To, Cc and Bcc headers should start with a capital letter followed by lower cases
	// Such as "From", "To" etc.
	Headers map[string][]byte
	Body    []byte
}

Mail holds mail headers and the body to send

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is used to send mails

func New

func New(domain string, dkimSelector string, dkimSigner crypto.Signer) *Service

New returns a new Service to send emails via domain is the associated domain name with the host dkimSelector is the DKIM selector to use with DKIM signature dkimSigner is the private key belongs to the domain and DKIM selector tuple check out README if you are not sure what DKIM is

func (*Service) Send

func (s *Service) Send(m *Mail) (map[string]error, error)

Send sends the mail to a remote SMTP server Returns (nil, error) if there is a major error that prevented service to send any emails Returns (report, nil) if there was not a major error report is a recipient to error map which shows individual errors for each delivery if exists Recipients are email addresses of To, Cc And Bcc targets without display names such as someuser@somedomain.com a 0 length report and nil error means everything went okay

Directories

Path Synopsis
Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.
Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL