Skip to content

Commit

Permalink
Refactor CreateProxy and DownstreeamDebug
Browse files Browse the repository at this point in the history
  • Loading branch information
tuck1s committed Mar 20, 2020
1 parent edade4a commit 5e8bc9a
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 54 deletions.
45 changes: 14 additions & 31 deletions cmd/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import (
"io/ioutil"
"log"
"os"
"time"

"github.com/tuck1s/go-smtpproxy"
"gopkg.in/natefinch/lumberjack.v2" // timed rotating log handler

smtpproxy "github.com/tuck1s/go-smtpproxy"
)

// myLogger sets up a custom logger, if filename is given, emitting to stdout as well
Expand Down Expand Up @@ -47,52 +45,37 @@ func main() {
log.Println("Starting smtp proxy service on port", *inHostPort)
log.Println("Outgoing host:port set to", *outHostPort)

// Set up parameters that the backend will use
be := smtpproxy.NewBackend(*outHostPort, *verboseOpt, *insecureSkipVerify)
s := smtpproxy.NewServer(be)
s.Addr = *inHostPort
s.ReadTimeout = 60 * time.Second
s.WriteTimeout = 60 * time.Second
var cert, privkey []byte
var err error

// Gather TLS credentials for the proxy server
if *certfile != "" && *privkeyfile != "" {
cert, err := ioutil.ReadFile(*certfile)
cert, err = ioutil.ReadFile(*certfile)
if err != nil {
log.Fatal(err)
}
privkey, err := ioutil.ReadFile(*privkeyfile)
privkey, err = ioutil.ReadFile(*privkeyfile)
if err != nil {
log.Fatal(err)
}
log.Println("Gathered certificate", *certfile, "and key", *privkeyfile)
err = s.ServeTLS(cert, privkey)
if err != nil {
log.Fatal(err)
}
} else {
log.Println("certfile or privkeyfile not specified - proxy will NOT offer STARTTLS to clients")
s.Domain, err = os.Hostname() // This is the fallback in case we have no cert / privkey to give us a Subject
if err != nil {
log.Fatal("Can't read hostname")
}
}

// Logging of downstream (client to proxy server) commands and responses
dbgFile, err := smtpproxy.DownstreamDebug(*downstreamDebug)
if err != nil {
log.Fatal(err)
}
s, _, err := smtpproxy.CreateProxy(*inHostPort, *outHostPort, *verboseOpt, cert, privkey, *insecureSkipVerify, dbgFile)
if err != nil {
log.Fatal(err)
}

log.Println("Proxy will advertise itself as", s.Domain)
log.Println("Verbose SMTP conversation logging:", *verboseOpt)
log.Println("insecure_skip_verify (Skip check of peer cert on upstream side):", *insecureSkipVerify)

// Logging of downstream (client to proxy server) commands and responses
if *downstreamDebug != "" {
dbgFile, err := os.OpenFile(*downstreamDebug, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer dbgFile.Close()
s.Debug = dbgFile
log.Println("Proxy logging SMTP commands, responses and downstream DATA to", dbgFile.Name())
}

// Begin serving requests
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
Expand Down
36 changes: 36 additions & 0 deletions proxy_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,47 @@ import (
"io"
"log"
"net"
"os"
"time"
)

// This file contains functions for an example Proxy app, including
// TLS negotiation, command pass-through, AUTH pass-through.

// CreateProxy sets up a proxy server with provided parameters and options, also returns the backend.
func CreateProxy(inHostPort, outHostPort string, verboseOpt bool, cert, privkey []byte, insecureSkipVerify bool, dbgFile *os.File) (*Server, *ProxyBackend, error) {
// Set up parameters that the backend will use
be := NewBackend(outHostPort, verboseOpt, insecureSkipVerify)
s := NewServer(be)
s.Addr = inHostPort
s.ReadTimeout = 60 * time.Second
s.WriteTimeout = 60 * time.Second
if dbgFile != nil {
s.Debug = dbgFile // Important to write this only if valid
}
var err error
if len(cert) > 0 && len(privkey) > 0 {
err = s.ServeTLS(cert, privkey)
} else {
s.Domain, err = os.Hostname() // This is the fallback in case we have no cert / privkey to give us a Subject
}
return s, be, err
}

// DownstreamDebug creates a debug file, if non-empty filename passed in
func DownstreamDebug(downstreamDebug string) (*os.File, error) {
var dbgFile *os.File
var err error
if downstreamDebug != "" {
dbgFile, err = os.OpenFile(downstreamDebug, os.O_CREATE|os.O_WRONLY, 0644)
if err == nil {
defer dbgFile.Close()
log.Println("Proxy logging SMTP commands, responses and downstream DATA to", dbgFile.Name())
}
}
return dbgFile, err
}

//-----------------------------------------------------------------------------
// Backend handlers

Expand Down
91 changes: 68 additions & 23 deletions proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net/mail"
"net/smtp"
Expand Down Expand Up @@ -247,35 +248,24 @@ func tlsClientConfig(cert []byte, privkey []byte) (*tls.Config, error) {
//-----------------------------------------------------------------------------
// proxy tests

const inHostPort = "localhost:5587"
const outHostPort = ":5588"
const inHostPort = "localhost:5580" // need to specifically have keyword localhost in here for c.Auth to accept nonsecure connections
const outHostPort = ":5581"
const downstreamDebug = "debug_proxy_test2.log"

func TestProxy(t *testing.T) {
rand.Seed(time.Now().UTC().UnixNano())
verboseOpt := true
downstreamDebug := "debug_proxy_test.log"
insecureSkipVerify := true

// Set up parameters that the backend will use, and initialise the proxy server parameters
be := smtpproxy.NewBackend(outHostPort, verboseOpt, insecureSkipVerify)
s := smtpproxy.NewServer(be)
s.Addr = inHostPort
s.ReadTimeout = 60 * time.Second
s.WriteTimeout = 60 * time.Second
var err error

err = s.ServeTLS(localhostCert, localhostKey)
// Logging of downstream (client to proxy server) commands and responses
dbgFile, err := smtpproxy.DownstreamDebug(downstreamDebug)
if err != nil {
t.Error(err)
log.Fatal(err)
}
// Logging of downstream (client to proxy server) commands and responses
if downstreamDebug != "" {
dbgFile, err := os.OpenFile(downstreamDebug, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
t.Error(err)
}
defer dbgFile.Close()
s.Debug = dbgFile
_ = dbgFile

s, be, err := smtpproxy.CreateProxy(inHostPort, outHostPort, verboseOpt, localhostCert, localhostKey, insecureSkipVerify, nil)
if err != nil {
log.Fatal(err)
}

// start the upstream mock SMTP server, which will reply in the channel
Expand Down Expand Up @@ -307,7 +297,7 @@ func sendAndCheckEmails(t *testing.T, inHostPort string, n int, secure string, m
c, err = smtp.Dial(inHostPort)
}
if err != nil {
t.Error(err)
t.Fatalf("Can't connect to proxy: %v\n", err)
}
// EHLO
err = c.Hello("localhost")
Expand Down Expand Up @@ -661,6 +651,61 @@ func RandomRecipient() string {
}

//-----------------------------------------------------------------------------
/*
func TestClientOtherFunctions(t *testing.T) {
// client uses same certs as mock server and proxy, which seems fine for testing purposes
cfg, err := tlsClientConfig(localhostCert, localhostKey)
if err != nil {
t.Error(err)
}
// DialTLS is not used by the proxy app in its current form, but may be useful
smtps := "smtp.gmail.com:465"
c, err := smtpproxy.DialTLS(smtps, cfg)
if err != nil {
t.Error(err)
}
// Greet the endpoint
code, msg, err := c.Hello("there")
if err != nil {
t.Errorf("code %v msg %v err %v\n", code, msg, err)
}
// Check extensions
ok, params := c.Extension("AUTH")
if !ok {
t.Errorf("ok %v, expected %v, params %v\n", ok, true, params)
}
// Close
err = c.Close()
if err != nil {
t.Error(err)
}
}
/*
func TestServerOtherFunctions(t *testing.T) {
verboseOpt := true
insecureSkipVerify := true
// Logging of downstream (client to proxy server) commands and responses
dbgFile, err := smtpproxy.DownstreamDebug(downstreamDebug)
if err != nil {
log.Fatal(err)
}
s, _, err := smtpproxy.CreateProxy("localhost:5586", outHostPort, verboseOpt, localhostCert, localhostKey, insecureSkipVerify, dbgFile)
if err != nil {
log.Fatal(err)
}
// start the proxy
go startProxy(t, s)
time.Sleep(1 * time.Second)
// Test additional server functions not used by the app
f := func(c *smtpproxy.Conn) {
fmt.Printf("%v\n", c)
}
s.ForEachConn(f)
}
*/

0 comments on commit 5e8bc9a

Please sign in to comment.