Skip to content

Instantly share code, notes, and snippets.

@aizatto
Last active December 20, 2015 23:09
Show Gist options
  • Save aizatto/6210353 to your computer and use it in GitHub Desktop.
Save aizatto/6210353 to your computer and use it in GitHub Desktop.
The best way to learn a protocol is to see the traffic between server and client. But all this is hidden from the user, so I had to find out a way get go to print out the traffic for me.
/**
* The best way to learn a protocol is to see the traffic between server and
* client. But all this is hidden from the user, so I had to find out a way
* get go to print out the traffic for me.
*/
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"net"
"net/http"
"net/smtp"
"os"
"strconv"
)
/**
* The only way we can see the traffic between client and server is to proxy
* net.Conn. It is the only replaceable part of the net.smtp that we can change.
*
* We will directly embed the Read and Write methods.
*
* I cannot replace these values with my own structs because they are strictly defined.
* * net/textproto.Conn (also accessile as smtp.Client.Text)
* * net/textproto.Conn.Reader
* * net/textproto.Conn.Reader.R
* * net/textproto.Conn.Writer
* * net/textproto.Conn.Writer.W
*
* The only thing we can proxy is bufio.Reader or bufio.Writer as they take in
* an interface (net.Conn)
*
* We proxy the connection via Go's embedding feature.
* See http://golang.org/doc/effective_go.html#embedding
*/
type ConnProxy struct {
net.Conn
}
func (c *ConnProxy) Read(b []byte) (n int, err error) {
n, err = c.Conn.Read(b)
fmt.Printf("S: %s", b[:n])
return
}
func (c *ConnProxy) Write(b []byte) (n int, err error) {
n, err = c.Conn.Write(b)
fmt.Printf("C: %s", b)
return
}
func main() {
err := connect()
if err != nil {
fmt.Printf("%s\n", err)
os.Exit(1)
}
}
func connect() error {
transport := flag.String("t", "smtp", "transport to test. Either: smtp or http")
flag.Parse()
switch *transport {
case "http":
return httpConnect()
case "smtp":
return smtpConnect()
default:
return errors.New("Unknown transport.")
}
}
func httpConnect() (err error) {
dial := func(network, addr string) (conn net.Conn, err error) {
conn, err = net.Dial(network, addr)
if err != nil {
return
}
bufconn := &ConnProxy{conn}
return bufconn, err
}
transport := &http.Transport{Dial: dial}
client := &http.Client{Transport: transport}
_, err = client.Get("http://example.com")
return err
}
func smtpConnect() (err error) {
mxs, err := net.LookupMX("gmail.com")
if err != nil {
return
}
mx := mxs[0]
host := mx.Host[:len(mx.Host)-1]
port := 25
addr := host + ":" + strconv.Itoa(port)
fmt.Printf("Connecting to %s\n", addr)
conn, err := net.Dial("tcp", addr)
if err != nil {
return
}
conn = &ConnProxy{conn}
client, err := smtp.NewClient(conn, host)
if err != nil {
return
}
if err != nil {
return
}
hostname, err := os.Hostname()
if err != nil {
return
}
err = client.Hello(hostname)
if err != nil {
return
}
err = client.Mail("sender@example.com")
if err != nil {
return
}
err = client.Rcpt("recipient@example.com")
if err != nil {
return
}
err = client.Quit()
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment