recv & send

This commit is contained in:
wiggin77 2018-11-28 19:00:45 -05:00
commit 3fd8f73c8b
7 changed files with 389 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
mailrelay
test
vendor

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"env": {},
"args": []
}
]
}

71
Gopkg.lock generated Normal file
View File

@ -0,0 +1,71 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:0a2a75a7b0d611bf7ecb4e5a0054242815dc27e857b4b9f8ec62225993fd11b7"
name = "github.com/asaskevich/EventBus"
packages = ["."]
pruneopts = "UT"
revision = "68a521d7cbbb7a859c2608b06342f384b3bd5f5a"
[[projects]]
branch = "master"
digest = "1:9d8902545dea4a3e1440ff60428426de594266b60e2981394540a133128a03ce"
name = "github.com/flashmob/go-guerrilla"
packages = [
".",
"backends",
"log",
"mail",
"response",
]
pruneopts = "UT"
revision = "1c628e503aedc9237ea6a9b13b3d3cdd0aab8f22"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:9e9193aa51197513b3abcb108970d831fbcf40ef96aa845c4f03276e1fa316d2"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "UT"
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
branch = "master"
digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = "UT"
revision = "e657309f52e71501f9934566ac06dc5c2f7f11a1"
[[projects]]
branch = "master"
digest = "1:f343f077a5b0bc3a3788b3a04e24dd417e3e25b2acb529c413e212d2c42416ef"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "UT"
revision = "62eef0e2fa9b2c385f7b2778e763486da6880d37"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/flashmob/go-guerrilla",
"github.com/flashmob/go-guerrilla/backends",
"github.com/flashmob/go-guerrilla/log",
"github.com/flashmob/go-guerrilla/mail",
"github.com/pkg/errors",
]
solver-name = "gps-cdcl"
solver-version = 1

34
Gopkg.toml Normal file
View File

@ -0,0 +1,34 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/flashmob/go-guerrilla"
[prune]
go-tests = true
unused-packages = true

123
client.go Normal file
View File

@ -0,0 +1,123 @@
package main
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/smtp"
"net/textproto"
"github.com/flashmob/go-guerrilla/mail"
"github.com/pkg/errors"
)
type closeable interface {
Close() error
}
// sendMail sends the contents of the envelope to a SMTP server.
func sendMail(e *mail.Envelope, config *mailRelayConfig) error {
server := fmt.Sprintf("%s:%d", config.Server, config.Port)
to := getTo(e)
var msg bytes.Buffer
msg.Write(e.Data.Bytes())
msg.WriteString("\r\n")
fmt.Println("==== Starting email send ====")
defer fmt.Println("==== Finished email send ====")
var err error
var conn *tls.Conn
var client *smtp.Client
var writer io.WriteCloser
tlsconfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: config.Server,
}
if conn, err = tls.Dial("tcp", server, tlsconfig); err != nil {
return errors.Wrap(err, "dial error")
}
if client, err = smtp.NewClient(conn, config.Server); err != nil {
close(conn, "conn")
return errors.Wrap(err, "newclient error")
}
shouldCloseClient := true
defer func(shouldClose *bool) {
if *shouldClose {
close(client, "client")
}
}(&shouldCloseClient)
auth := smtp.PlainAuth("", config.Username, config.Password, config.Server)
if err = client.Auth(auth); err != nil {
return errors.Wrap(err, "auth error")
}
if err = client.Mail(e.MailFrom.String()); err != nil {
return errors.Wrap(err, "mail error")
}
for _, addy := range to {
if err = client.Rcpt(addy); err != nil {
return errors.Wrap(err, "rcpt error")
}
}
if writer, err = client.Data(); err != nil {
return errors.Wrap(err, "data error")
}
_, err = writer.Write(msg.Bytes())
close(writer, "writer")
if err != nil {
return errors.Wrap(err, "write error")
}
if err = client.Quit(); isQuitError(err) {
return errors.Wrap(err, "quit error")
}
// We only need to close client if some other error prevented us
// from getting to `client.Quit`
shouldCloseClient = false
return nil
}
func close(c closeable, what string) {
err := c.Close()
if err != nil {
fmt.Printf("!!!!! Error closing %s: %v\n", what, err)
}
}
func isQuitError(err error) bool {
if err == nil {
return false
}
e, ok := err.(*textproto.Error)
if ok {
// SMTP codes 221 or 250 are acceptable here
if e.Code == 221 || e.Code == 250 {
return false
}
}
return true
}
// getTo returns the array of email addresses in the envelope.
func getTo(e *mail.Envelope) []string {
var ret []string
for _, addy := range e.RcptTo {
ret = append(ret, addy.String())
}
return ret
}
func display(b []byte) {
s := string(b)
fmt.Println("################################")
fmt.Printf("%s\n", s)
fmt.Println("################################")
}

70
main.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/signal"
)
type loggerLevels struct {
Debug *log.Logger
Error *log.Logger
}
type mailRelayConfig struct {
Server string `json:"server"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
}
// Logger provides application logging.
var Logger loggerLevels
func main() {
Logger.Debug = log.New(os.Stdout, "debug: ", log.Ldate|log.Ltime|log.Lshortfile)
Logger.Error = log.New(os.Stderr, "error: ", log.Ldate|log.Ltime|log.Lshortfile)
var configFile string
flag.StringVar(&configFile, "config", "/etc/mailrelay.json", "specifies JSON config file")
flag.Parse()
appConfig, err := loadConfig(configFile)
if err != nil {
Logger.Error.Fatalf("loading config: %v", err)
}
err = Start(appConfig)
if err != nil {
Logger.Error.Fatalf("starting server: %v", err)
}
// Wait for SIGINT
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Kill)
// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
os.Exit(0)
}
func loadConfig(path string) (*mailRelayConfig, error) {
var cfg mailRelayConfig
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
parser := json.NewDecoder(file)
if err := parser.Decode(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}

71
server.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"fmt"
guerrilla "github.com/flashmob/go-guerrilla"
"github.com/flashmob/go-guerrilla/backends"
"github.com/flashmob/go-guerrilla/log"
"github.com/flashmob/go-guerrilla/mail"
)
// Start starts the server.
func Start(appConfig *mailRelayConfig) (err error) {
cfg := &guerrilla.AppConfig{LogFile: log.OutputStdout.String(), AllowedHosts: []string{"warpmail.net"}}
sc := guerrilla.ServerConfig{
ListenInterface: "0.0.0.0:2525",
IsEnabled: true,
}
cfg.Servers = append(cfg.Servers, sc)
bcfg := backends.BackendConfig{
"save_workers_size": 3,
"save_process": "HeadersParser|Header|Hasher|Debugger|MailRelay",
"log_received_mails": true,
"primary_mail_host": "homeoffice.com",
"username": appConfig.Username,
"password": appConfig.Password,
"server": appConfig.Server,
"port": appConfig.Port,
}
cfg.BackendConfig = bcfg
d := guerrilla.Daemon{Config: cfg}
d.AddProcessor("MailRelay", mailRelayProcessor)
return d.Start()
}
// mailRelayProcessor decorator relays emails to another SMTP server.
var mailRelayProcessor = func() backends.Decorator {
config := &mailRelayConfig{}
initFunc := backends.InitializeWith(func(backendConfig backends.BackendConfig) error {
configType := backends.BaseConfig(&mailRelayConfig{})
bcfg, err := backends.Svc.ExtractConfig(backendConfig, configType)
if err != nil {
return err
}
config = bcfg.(*mailRelayConfig)
return nil
})
backends.Svc.AddInitializer(initFunc)
return func(p backends.Processor) backends.Processor {
return backends.ProcessWith(
func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) {
if task == backends.TaskSaveMail {
err := sendMail(e, config)
if err != nil {
fmt.Printf("!!! %v\n", err)
return backends.NewResult(fmt.Sprintf("554 Error: %s", err)), err
}
return p.Process(e, task)
}
return p.Process(e, task)
},
)
}
}