Last active
December 20, 2015 23:09
-
-
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.
This file contains 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
/** | |
* 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