1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-17 14:37:02 +00:00
Files
go-chasquid-smtp/chasquid.go
Alberto Bertogli 1ee6bf563e Initial commit
Extremely basic and hacky chasquid.go.
Hard-codes ports and hostname.

"Implements" EHLO, HELO, HELP and QUIT.
2015-10-26 03:40:26 +00:00

188 lines
3.6 KiB
Go

package main
import (
"bytes"
"flag"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/textproto"
"strings"
"github.com/golang/glog"
"golang.org/x/net/trace"
)
func main() {
flag.Parse()
monAddr := ":1099"
glog.Infof("Monitoring HTTP server listening on %s", monAddr)
go http.ListenAndServe(monAddr, nil)
ListenAndServe()
}
const (
hostname = "charqui.com.ar"
)
func ListenAndServe() {
addr := ":1025"
l, err := net.Listen("tcp", addr)
if err != nil {
glog.Fatalf("Error listening: %v", err)
}
defer l.Close()
glog.Infof("Server listening on %s", addr)
for {
conn, err := l.Accept()
if err != nil {
glog.Fatalf("Error accepting: %v", err)
}
sc := &Conn{
netconn: conn,
tc: textproto.NewConn(conn),
}
go sc.Handle()
}
}
type Conn struct {
netconn net.Conn
tc *textproto.Conn
}
func (c *Conn) Handle() {
defer c.netconn.Close()
tr := trace.New("SMTP", "connection")
defer tr.Finish()
c.tc.PrintfLine("220 %s ESMTP charquid", hostname)
var cmd, params string
var err error
loop:
for {
cmd, params, err = c.readCommand()
if err != nil {
c.tc.PrintfLine("554 error reading command: %v", err)
break
}
tr.LazyPrintf("-> %s %s", cmd, params)
var code int
var msg string
switch cmd {
case "HELO":
code, msg = c.HELO(params)
case "EHLO":
code, msg = c.EHLO(params)
case "HELP":
code, msg = c.HELP(params)
case "QUIT":
c.writeResponse(221, "Be seeing you...")
break loop
default:
code = 500
msg = "unknown command"
}
tr.LazyPrintf("<- %d", code)
err = c.writeResponse(code, msg)
if err != nil {
break
}
}
if err != nil {
tr.LazyPrintf("exiting with error: %v", err)
}
}
func (c *Conn) HELO(params string) (code int, msg string) {
types := []string{
"general store", "used armor dealership", "second-hand bookstore",
"liquor emporium", "antique weapons outlet", "delicatessen",
"jewelers", "quality apparel and accessories", "hardware",
"rare books", "lighting store"}
t := types[rand.Int()%len(types)]
msg = fmt.Sprintf("Hello my friend, welcome to chasqui's %s!", t)
return 250, msg
}
func (c *Conn) EHLO(params string) (code int, msg string) {
buf := bytes.NewBuffer(nil)
fmt.Fprintf(buf, hostname+" - Your hour of destiny has come.\n")
fmt.Fprintf(buf, "8BITMIME\n")
fmt.Fprintf(buf, "PIPELINING\n")
fmt.Fprintf(buf, "SIZE 52428800\n")
fmt.Fprintf(buf, "STARTTLS\n")
fmt.Fprintf(buf, "HELP\n")
return 250, buf.String()
}
func (c *Conn) HELP(params string) (code int, msg string) {
return 214, "hoy por ti, mañana por mi"
}
func (c *Conn) NOOP(params string) (code int, msg string) {
return 250, "noooooooooooooooooooop"
}
func (c *Conn) readCommand() (cmd, params string, err error) {
var msg string
msg, err = c.tc.ReadLine()
if err != nil {
return "", "", err
}
sp := strings.SplitN(msg, " ", 2)
cmd = strings.ToUpper(sp[0])
if len(sp) > 1 {
params = sp[1]
}
return cmd, params, err
}
func (c *Conn) writeResponse(code int, msg string) error {
defer c.tc.W.Flush()
return writeResponse(c.tc.W, code, msg)
}
// writeResponse writes a multi-line response to the given writer.
// This is the writing version of textproto.Reader.ReadResponse().
func writeResponse(w io.Writer, code int, msg string) error {
var i int
lines := strings.Split(msg, "\n")
// The first N-1 lines use "<code>-<text>".
for i = 0; i < len(lines)-2; i++ {
_, err := w.Write([]byte(fmt.Sprintf("%d-%s\r\n", code, lines[i])))
if err != nil {
return err
}
}
// The last line uses "<code> <text>".
_, err := w.Write([]byte(fmt.Sprintf("%d %s\r\n", code, lines[i])))
if err != nil {
return err
}
return nil
}