diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..419e1a8 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 93d3f75..5a92bae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,15 @@ Turbulence ========== -An http proxy for navigating through rough air. +An http/https proxy for navigating through rough air. + +#### Usage + +``` +$ go run *.go +``` + +``` +Prepare for takeoff... +Server started on :25000 +``` diff --git a/connection.go b/connection.go new file mode 100644 index 0000000..87ae58b --- /dev/null +++ b/connection.go @@ -0,0 +1,47 @@ +package main + +import ( + "bufio" + "fmt" + "net" + "net/http" +) + +type connection struct { + incoming net.Conn + outgoing net.Conn + proxy proxy +} + +func (c *connection) Handle() { + reader := bufio.NewReader(c.incoming) + request, err := http.ReadRequest(reader) + if err != nil { + fmt.Println("Could not parse or read request from incoming connection:", err) + return + } + + if request.Method == "CONNECT" { + c.proxy = &httpsProxy{} + } else { + c.proxy = &httpProxy{} + } + + c.proxy.Handle(c, request) +} + +func (c *connection) Close() { + if c.incoming != nil { + c.incoming.Close() + } + + if c.outgoing != nil { + c.outgoing.Close() + } +} + +func NewConnection(incoming net.Conn) *connection { + return &connection{ + incoming: incoming, + } +} diff --git a/http.go b/http.go new file mode 100644 index 0000000..fef1414 --- /dev/null +++ b/http.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "net/http" + "net/http/httputil" +) + +type httpProxy struct{} + +func (hp *httpProxy) Handle(connection *connection, request *http.Request) { + // Connect to outgoing host and write request bytes. + request.RequestURI = "" + client := http.Client{} + response, err := client.Do(request) + if err != nil { + fmt.Println("Error making outgoing request:", err) + return + } + defer response.Body.Close() + + // Dump response struct back into bytes. + responseBytes, err := httputil.DumpResponse(response, true) + if err != nil { + fmt.Println("Error dumping response to write outgoung->incoming:", err) + return + } + + // Write back to incoming connection and finish. + connection.incoming.Write(responseBytes) +} diff --git a/https.go b/https.go new file mode 100644 index 0000000..8f44b1d --- /dev/null +++ b/https.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "io" + "net" + "net/http" +) + +const ConnectionEstablished = "HTTP/1.0 200 Connection established\r\n\r\n" + +type httpsProxy struct{} + +func (hp *httpsProxy) Handle(connection *connection, request *http.Request) { + // Create our outgoing connection. + outgoingHost := request.URL.Host + outgoingConn, err := net.Dial("tcp", outgoingHost) + if err != nil { + fmt.Println("Error opening outgoing connection to", outgoingHost, err) + return + } + connection.outgoing = outgoingConn + + // Signal to the incoming connection that the outgoing connection was established. + _, err = connection.incoming.Write([]byte(ConnectionEstablished)) + if err != nil { + fmt.Println("Could not send Continue response to client: " + err.Error()) + } + + // Spawn incoming->outgoing and outgoing->incoming streams. + signal := make(chan bool) + go streamBytes(connection.incoming, connection.outgoing, signal) + go streamBytes(connection.outgoing, connection.incoming, signal) + + // Wait for either stream to complete and finish. + <-signal +} + +func streamBytes(src net.Conn, dest net.Conn, signal chan bool) { + // Read until EOF or error. + io.Copy(dest, src) + signal <- true +} diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..af6d507 --- /dev/null +++ b/proxy.go @@ -0,0 +1,9 @@ +package main + +import ( + "net/http" +) + +type proxy interface { + Handle(*connection, *http.Request) +} diff --git a/turbulence.go b/turbulence.go index 2aa7d62..fd47770 100644 --- a/turbulence.go +++ b/turbulence.go @@ -2,8 +2,44 @@ package main import ( "fmt" + "net" + "os" ) +func handleConnection(conn net.Conn) { + connection := NewConnection(conn) + defer connection.Close() + connection.Handle() +} + +func acceptedConnsChannel(listener net.Listener) chan net.Conn { + channel := make(chan net.Conn) + go func() { + for { + conn, err := listener.Accept() + if err != nil { + fmt.Println("Could not accept socket:", err) + continue + } + + channel <- conn + } + }() + return channel +} + func main() { - fmt.Println("Prepare for take off...") + fmt.Println("Prepare for takeoff...") + server, err := net.Listen("tcp", ":25000") + if err != nil { + fmt.Println("Could not start server:", err) + os.Exit(1) + } + + fmt.Println("Server started on :25000") + + acceptedConnsChannel := acceptedConnsChannel(server) + for { + go handleConnection(<-acceptedConnsChannel) + } }