Last active
December 21, 2015 20:17
-
-
Save anoldguy/f5304db2f7d950aa0023 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"errors" | |
"fmt" | |
"log" | |
"net" | |
"strings" | |
) | |
type Job struct { | |
Command string | |
Key string | |
} | |
const KeyNotFound string = "500" | |
const KeyError string = "400" | |
const KeyFound string = "200" | |
func getDomain(email string) (string, error) { | |
domain := "" | |
var err error | |
if !strings.Contains(email, "@") { | |
err = errors.New(fmt.Sprintf("Malformed email address: %s", email)) | |
return domain, err | |
} | |
results := strings.Split(email, "@") | |
domain = results[1] | |
return domain, err | |
} | |
func lookupKey(key string) (bool, error) { | |
found := false | |
mxs, err := net.LookupMX(key) | |
// Hat tip @noahhl | |
found = err == nil && (strings.Contains(mxs[0].Host, "google") || strings.Contains(mxs[0].Host, "gmail")) | |
return found, err | |
} | |
func formatError(s string) string { | |
return fmt.Sprintf("%s %s\n", KeyError, s) | |
} | |
func processJob(job Job) (string, error) { | |
exists := false | |
result := "" | |
var err error | |
switch job.Command { | |
case "get": | |
exists, err = lookupKey(job.Key) | |
if err != nil { | |
result = formatError(err.Error()) | |
return result, err | |
} | |
if exists { | |
result = fmt.Sprintf("%s %s\n", KeyFound, "gmail:") | |
} else { | |
result = fmt.Sprintf("%s %s\n", KeyNotFound, "Domain not hosted by google") | |
} | |
default: | |
err = errors.New(fmt.Sprintf("Command Unimplemented: %s", job.Command)) | |
} | |
return result, err | |
} | |
func parseCommand(line string) (Job, error) { | |
// Based on http://www.postfix.org/transport.5.html, we will get the | |
// entire recipient email for this lookup, so we can split on @ to get | |
// the domain as per https://tools.ietf.org/html/rfc5321#section-4.1.2 | |
job := Job{} | |
var err error | |
fields := strings.Fields(strings.Replace(strings.TrimRight(line, "\n"), "%20", " ", -1)) | |
if len(fields) > 1 { | |
job.Command = fields[0] | |
domain, err := getDomain(fields[1]) | |
if err != nil { | |
return job, err | |
} | |
job.Key = domain | |
} else { | |
err = errors.New("Command Parsing failed; missing command or key") | |
} | |
return job, err | |
} | |
func requestHandler(conn net.Conn) { | |
for { | |
// Read the line off the wire | |
line, err := bufio.NewReader(conn).ReadBytes('\n') | |
if err != nil { | |
return | |
} | |
// Parse the line into a job | |
job, err := parseCommand(string(line)) | |
if err != nil { | |
// If it's not a valid job, bail | |
log.Println(err) | |
conn.Write([]byte(formatError(err.Error()))) | |
} else { | |
// if it is a valid job, let's process it | |
log.Printf("Command: %s, Key: %s", job.Command, job.Key) | |
result, procErr := processJob(job) | |
if procErr != nil { | |
log.Printf("Error processing job %s: %s", job.Command, procErr) | |
} | |
conn.Write([]byte(result)) | |
} | |
} | |
} | |
func main() { | |
psock, err := net.Listen("tcp", ":5000") | |
if err != nil { | |
return | |
} | |
for { | |
conn, err := psock.Accept() | |
if err != nil { | |
return | |
} | |
go requestHandler(conn) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment