author | Alberto Bertogli
<albertito@blitiri.com.ar> 2015-10-26 00:43:53 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2015-10-26 03:40:32 UTC |
parent | 2c612002a9cacaa8444bd5a1e1a9308a3019659f |
chasquid.go | +74 | -12 |
diff --git a/chasquid.go b/chasquid.go index a3d4058..c6321a4 100644 --- a/chasquid.go +++ b/chasquid.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "crypto/tls" "flag" "fmt" "io" @@ -35,7 +36,31 @@ const ( maxDataSize = 52428800 ) +func getTLSConfig() (*tls.Config, error) { + var err error + conf := &tls.Config{} + + // TODO: Get these from the configuration (we have to support many, not + // just 1 like here). + conf.Certificates = make([]tls.Certificate, 1) + conf.Certificates[0], err = tls.LoadX509KeyPair(".cert.pem", ".key.pem") + if err != nil { + return nil, fmt.Errorf("Error loading client certificate: %v", err) + } + + conf.BuildNameToCertificate() + + return conf, nil +} + func ListenAndServe() { + // Configure TLS. + tlsConfig, err := getTLSConfig() + if err != nil { + glog.Fatalf("Error loading TLS config: %v", err) + } + + // Listen. addr := ":1025" l, err := net.Listen("tcp", addr) if err != nil { @@ -44,6 +69,8 @@ func ListenAndServe() { defer l.Close() glog.Infof("Server listening on %s", addr) + + // Serve. for { conn, err := l.Accept() if err != nil { @@ -51,8 +78,9 @@ func ListenAndServe() { } sc := &Conn{ - netconn: conn, - tc: textproto.NewConn(conn), + netconn: conn, + tc: textproto.NewConn(conn), + tlsConfig: tlsConfig, } go sc.Handle() } @@ -63,7 +91,10 @@ type Conn struct { netconn net.Conn tc *textproto.Conn - // Message data. + // TLS configuration. + tlsConfig *tls.Config + + // Envelope. mail_from string rcpt_to []string data []byte @@ -111,6 +142,8 @@ loop: case "DATA": // DATA handles the whole sequence. code, msg = c.DATA(params, tr) + case "STARTTLS": + code, msg = c.STARTTLS(params, tr) case "QUIT": c.writeResponse(221, "Be seeing you...") break loop @@ -119,11 +152,13 @@ loop: msg = "unknown command" } - tr.LazyPrintf("<- %d %s", code, msg) + if code > 0 { + tr.LazyPrintf("<- %d %s", code, msg) - err = c.writeResponse(code, msg) - if err != nil { - break + err = c.writeResponse(code, msg) + if err != nil { + break + } } } @@ -160,7 +195,7 @@ func (c *Conn) HELP(params string) (code int, msg string) { } func (c *Conn) RSET(params string) (code int, msg string) { - c.resetMessageData() + c.resetEnvelope() msgs := []string{ "Who was that Maud person anyway?", @@ -192,7 +227,7 @@ func (c *Conn) MAIL(params string) (code int, msg string) { return 501, "sender address must contain a domain" } - c.resetMessageData() + c.resetEnvelope() c.mail_from = e.Address return 250, "You feel like you are being watched" } @@ -238,7 +273,7 @@ func (c *Conn) DATA(params string, tr trace.Trace) (code int, msg string) { } // We're going ahead. - err := c.writeResponse(354, "You experience a strange sense of peace") + err := c.writeResponse(354, "You suddenly realize it is unnaturally quiet") if err != nil { return 554, fmt.Sprintf("error writing DATA response: %v", err) } @@ -257,7 +292,7 @@ func (c *Conn) DATA(params string, tr trace.Trace) (code int, msg string) { // It is very important that we reset the envelope before returning, // so clients can send other emails right away without needing to RSET. - c.resetMessageData() + c.resetEnvelope() msgs := []string{ "You offer the Amulet of Yendor to Anhur...", @@ -269,7 +304,34 @@ func (c *Conn) DATA(params string, tr trace.Trace) (code int, msg string) { return 250, msgs[rand.Int()%len(msgs)] } -func (c *Conn) resetMessageData() { +func (c *Conn) STARTTLS(params string, tr trace.Trace) (code int, msg string) { + err := c.writeResponse(220, "You experience a strange sense of peace") + if err != nil { + return 554, fmt.Sprintf("error writing STARTTLS response: %v", err) + } + + tr.LazyPrintf("<- 220 You experience a strange sense of peace") + + client := tls.Server(c.netconn, c.tlsConfig) + err = client.Handshake() + if err != nil { + return 554, fmt.Sprintf("error in client handshake: %v", err) + } + + tr.LazyPrintf("<> ... jump to TLS was successful") + + // Override the connections. We don't need the older ones anymore. + c.netconn = client + c.tc = textproto.NewConn(client) + + // Reset the envelope; clients must start over after switching to TLS. + c.resetEnvelope() + + // 0 indicates not to send back a reply. + return 0, "" +} + +func (c *Conn) resetEnvelope() { c.mail_from = "" c.rcpt_to = nil c.data = nil