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 "-". 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 " ". _, err := w.Write([]byte(fmt.Sprintf("%d %s\r\n", code, lines[i]))) if err != nil { return err } return nil }