git » chasquid » commit 1ee6bf5

Initial commit

author Alberto Bertogli
2015-10-25 22:38:17 UTC
committer Alberto Bertogli
2015-10-26 03:40:26 UTC

Initial commit

Extremely basic and hacky chasquid.go.
Hard-codes ports and hostname.

"Implements" EHLO, HELO, HELP and QUIT.

.gitignore +12 -0
chasquid.go +187 -0

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..09b424f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+
+# Ignore anything beginning with a dot: these are usually temporary or
+# unimportant.
+.*
+
+# Exceptions to the rules above: files we care about that would otherwise be
+# excluded.
+!.gitignore
+
+# The binary.
+chasquid
+
diff --git a/chasquid.go b/chasquid.go
new file mode 100644
index 0000000..b6a9188
--- /dev/null
+++ b/chasquid.go
@@ -0,0 +1,187 @@
+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
+}