├── .gitignore ├── README ├── logger └── logger.go ├── proxy └── proxy.go ├── rickroll └── rickroll.go └── upsidedown └── upsidedown.go /.gitignore: -------------------------------------------------------------------------------- 1 | syntax:glob 2 | .DS_Store 3 | *.[568ao] 4 | *.ao 5 | *.so 6 | *.pyc 7 | ._* 8 | .nfs.* 9 | [568a].out 10 | *~ 11 | *.orig 12 | *.rej 13 | .*.swp 14 | *.cgo*.go 15 | *.cgo*.c 16 | _cgo_* 17 | _obj 18 | _test 19 | _testmain.go 20 | goinstall.log 21 | *.flv 22 | 23 | syntax:regexp 24 | ^bin/ 25 | ^pkg/ 26 | ^src/cmd/(.*)/6?\1$ 27 | ^.*/core.[0-9]*$ 28 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | proxy - A simple transperent web proxy written in go. 2 | 3 | This was written after my neighbours have taken it a bit too far using 4 | my wireless and I wanted to have a bit of fun. It was inspired by 5 | upside-down-ternet[1]. :-) 6 | 7 | src/proxy is the main proxy library. This repository also includes 8 | multiple simple proxies written using it: 9 | 10 | upsidedown flips every image requested through it. 11 | 12 | logger simply logs every request. 13 | 14 | rickroll takes a path to an flv file, and replaces every flv 15 | request with its contents. i.e. it turns every youtube video 16 | into a rickroll. 17 | 18 | they all listen on port 3128. 19 | 20 | Look at src/logger or src/upsidedown for examples on how to write a 21 | custom proxy. 22 | 23 | To forward all web traffic to the machine running the proxy (in my case on 24 | 192.168.1.11:3128), I use the following iptables commands on the router: 25 | 26 | iptables -t nat -A PREROUTING -s '!' 192.168.1.11 -p tcp --dport 80 -j DNAT --to 192.168.1.11:3128 27 | iptables -t nat -A POSTROUTING -s 192.168.1.0/255.255.255.0 -d 192.168.1.11 -j MASQUERADE 28 | iptables -A FORWARD -s 192.168.1.0/255.255.255.0 -d 192.168.1.11 -p tcp --dport 3128 -j ACCEPT 29 | 30 | Exercises for the reader: inject js into all html pages. 31 | 32 | [1] http://www.ex-parrot.com/pete/upside-down-ternet.html 33 | -------------------------------------------------------------------------------- /logger/logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "log" 6 | "bitbucket.org/saljam/proxy/proxy" 7 | ) 8 | 9 | func logRequest(r *http.Request) *http.Request { 10 | log.Println(r.Method, r.URL, r.UserAgent(), r.Header) 11 | return r 12 | } 13 | 14 | func main() { 15 | p := &proxy.Proxy{ 16 | proxy.ReqManglers{logRequest}, 17 | proxy.ResManglers{}, 18 | } 19 | err := http.ListenAndServe(":3128", p) 20 | if err != nil { 21 | log.Fatal("ListenAndServe: ", err) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /proxy/proxy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Proxy is a simple transperent web proxy written in go. 3 | 4 | Basic usage: 5 | 6 | p := &proxy.Proxy{ 7 | proxy.ReqManglers{logRequest}, 8 | proxy.ResManglers{flipImageRes}, 9 | } 10 | log.Fatal(http.ListenAndServe(":3128", p)) 11 | 12 | Where the members of the manglers are functions which implement the signatures: 13 | 14 | func(*http.Request) *http.Request 15 | func(*http.Response) *http.Response 16 | 17 | */ 18 | package proxy 19 | 20 | import ( 21 | "net/http" 22 | "log" 23 | "io" 24 | ) 25 | 26 | // ReqManglers are a slice of fucntions which modify requests. 27 | // These functions must have the signature: 28 | // func(*http.Request) *http.Request 29 | type ReqManglers []func(*http.Request) *http.Request 30 | 31 | // ResManglers are a slice of fucntions which modify responses. 32 | // These functions must have the signature: 33 | // func(*http.Response) *http.Response 34 | type ResManglers []func(*http.Response) *http.Response 35 | 36 | // Proxy implements http.Handler. 37 | type Proxy struct{ 38 | RequestManglers ReqManglers 39 | ResponseManglers ResManglers 40 | } 41 | 42 | func copyHeader(from, to http.Header) { 43 | for hdr, items := range from { 44 | for _, item := range items { 45 | to.Add(hdr, item) 46 | } 47 | } 48 | } 49 | 50 | // ServeHTTP proxies the request given and writes the response to w. 51 | func (p *Proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { 52 | for _, f := range p.RequestManglers { 53 | req = f(req) 54 | } 55 | 56 | res, err := http.DefaultTransport.RoundTrip(req) 57 | if err != nil { 58 | log.Println("proxy fail:", err) 59 | w.WriteHeader(http.StatusInternalServerError) 60 | return 61 | } 62 | 63 | for _, f := range p.ResponseManglers { 64 | res = f(res) 65 | } 66 | 67 | copyHeader(res.Header, w.Header()) 68 | 69 | w.WriteHeader(res.StatusCode) 70 | 71 | if res.Body != nil { 72 | io.Copy(w, res.Body) 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /rickroll/rickroll.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "net/http" 6 | "log" 7 | "flag" 8 | "bitbucket.org/saljam/proxy/proxy" 9 | ) 10 | 11 | func rickRoll(res *http.Response) *http.Response { 12 | if ct, ok := res.Header["Content-Type"]; !ok || len(ct) == 0{ 13 | return res 14 | } 15 | 16 | if res.Header["Content-Type"][0] == "video/x-flv" { 17 | res.Header.Del("Content-Length") 18 | if file, err := os.Open(*filename); err == nil { 19 | res.Body = file 20 | log.Println("win!") 21 | } else { 22 | log.Println("failed to open file!") 23 | } 24 | } 25 | return res 26 | } 27 | 28 | var filename = flag.String("filename", "", "Path to the replacement .flv file.") 29 | 30 | func main() { 31 | flag.Parse() 32 | 33 | p := &proxy.Proxy{ 34 | proxy.ReqManglers{}, 35 | proxy.ResManglers{rickRoll}, 36 | } 37 | 38 | err := http.ListenAndServe(":3128", p) 39 | if err != nil { 40 | log.Fatal("ListenAndServe: ", err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /upsidedown/upsidedown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "log" 6 | "bitbucket.org/saljam/proxy/proxy" 7 | "bytes" 8 | "image" 9 | "image/jpeg" 10 | "image/gif" 11 | "image/png" 12 | ) 13 | 14 | type imgBuffer struct{ 15 | *bytes.Buffer 16 | } 17 | 18 | func (b imgBuffer) Close() error { 19 | return nil 20 | } 21 | 22 | func flipImage(m image.Image) *image.RGBA { 23 | b := m.Bounds() 24 | s := image.Point{b.Dx(), b.Dy()} 25 | n := image.NewRGBA(b) 26 | for i :=0; i < s.X; i++ { 27 | for j :=0; j < s.Y; j++ { 28 | n.Set(i, j, m.At(i, s.Y-j-1)) 29 | } 30 | } 31 | return n 32 | } 33 | 34 | func flipImageRes(res *http.Response) *http.Response { 35 | switch res.Header["Content-Type"][0] { 36 | case "image/jpeg": 37 | m, err := jpeg.Decode(res.Body) 38 | if err != nil { 39 | log.Println("image error:", err) 40 | return res 41 | } 42 | buf := imgBuffer{bytes.NewBuffer([]byte{})} 43 | n := flipImage(m) 44 | jpeg.Encode(buf, n, nil) 45 | res.Header.Del("Content-Length") 46 | res.Body = buf 47 | case "image/gif": 48 | m, err := gif.Decode(res.Body) 49 | if err != nil { 50 | log.Println("image error:", err) 51 | return res 52 | } 53 | buf := imgBuffer{bytes.NewBuffer([]byte{})} 54 | n := flipImage(m) 55 | png.Encode(buf, n) 56 | res.Header.Del("Content-Length") 57 | res.Header.Del("Content-Type") 58 | res.Header.Add("Content-Type", "image/png") 59 | res.Body = buf 60 | case "image/png": 61 | m, err := png.Decode(res.Body) 62 | if err != nil { 63 | log.Println("image error:", err) 64 | return res 65 | } 66 | buf := imgBuffer{bytes.NewBuffer([]byte{})} 67 | n := flipImage(m) 68 | png.Encode(buf, n) 69 | res.Header.Del("Content-Length") 70 | res.Header.Del("Content-Type") 71 | res.Header.Add("Content-Type", "image/png") 72 | res.Body = buf 73 | } 74 | return res 75 | } 76 | 77 | func logRequest(r *http.Request) *http.Request { 78 | log.Println(r.Method, r.URL, r.UserAgent(), r.Header) 79 | return r 80 | } 81 | 82 | func main() { 83 | p := &proxy.Proxy{ 84 | proxy.ReqManglers{logRequest}, 85 | proxy.ResManglers{flipImageRes}, 86 | } 87 | err := http.ListenAndServe(":3128", p) 88 | if err != nil { 89 | log.Fatal("ListenAndServe: ", err) 90 | } 91 | } 92 | 93 | --------------------------------------------------------------------------------