├── .gitignore ├── README.md ├── client └── client.go └── server └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Newmarket 2 | ========= 3 | 4 | Wrap a TCP connection over two HTTP connections like so: 5 | 6 | ![](http://i.imgur.com/V1kKCb1.png) 7 | 8 | To a firewall the two connections looks like two (correctly formatted) HTTP connections that are slowly POSTing and GETing data from a HTTP url. 9 | 10 | ##Setup 11 | Make sure you have GoLang 1.1 or above installed then clone the git. 12 | 13 | ###Server 14 | First go into the server directory 15 | `cd server` 16 | 17 | Fetch the things that are needed to run this first: 18 | 19 | `go get` 20 | 21 | then build it 22 | 23 | `go build` 24 | 25 | You can then run it, If you want to change the port that it listens on, set the PORT env var by doing (in a normal bash shell) `export port=80` 26 | 27 | then run the server program 28 | 29 | `./server` 30 | 31 | ###Client 32 | 33 | First go into the server directory 34 | `cd client` 35 | 36 | Fetch the things that are needed to run this first: 37 | 38 | `go get` 39 | 40 | then build it 41 | 42 | `go build` 43 | 44 | Then you can look into the usage of the client by running `./client --help` or `client.exe --help` 45 | 46 | You will get this in return 47 | 48 | ``` 49 | NAME: 50 | Newmarket Client - A Client to the Newmarket HTTP Tunnel server 51 | 52 | USAGE: 53 | Newmarket Client [global options] command [command options] [arguments...] 54 | 55 | VERSION: 56 | 0.9 57 | 58 | COMMANDS: 59 | help, h Shows a list of commands or help for one command 60 | 61 | GLOBAL OPTIONS: 62 | --url, -u 'http://localhost:3000' the URL of the Newmarket server 63 | --port, -p '3001' The port you want to listen on 64 | --version, -v print the version 65 | --help, -h show help 66 | ``` 67 | 68 | The two things you will want to change here is the `--url` var to point to your remote server that you will be tunneling to. 69 | 70 | Example usage is 71 | 72 | `client --url http://test.example.com` 73 | 74 | then connect on the forwarded port (default is 3001) 75 | 76 | `ssh -p 3001 localhost` 77 | 78 | Auth like normal and now you are inside your remote server! 79 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "net" 8 | "net/http" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | url := flag.String("url", "http://localhost:3000", "the URL of the Newmarket server") 14 | port := flag.Int("port", 3001, "The port you want to listen on") 15 | bindlocal := flag.Bool("bindlocal", true, "enable to bind only on 127.0.0.1") 16 | flag.Parse() 17 | StartTunnel(*url, int64(*port), *bindlocal) 18 | } 19 | 20 | func StartTunnel(URL string, Port int64, BindLocal bool) { 21 | fmt.Printf("The settings are \n\nURL:%s\nListening Port:%s\n", URL, Port) 22 | // First, Lets see if we can bind that port. 23 | var bindaddr string = "0.0.0.0" 24 | 25 | if BindLocal { 26 | bindaddr = "127.0.0.1" 27 | } 28 | 29 | listener, e := net.Listen("tcp", fmt.Sprintf("%s:%d", bindaddr, Port)) 30 | if e != nil { 31 | fmt.Errorf("Cannot bind to port %s:%d", bindaddr, Port) 32 | return 33 | } 34 | fmt.Printf("Bound to %s:%d waiting for a connection to proceed\n", bindaddr, Port) 35 | for { 36 | conn, err := listener.Accept() 37 | if err != nil { 38 | fmt.Errorf("Error accept incoming connection: %s", err.Error()) 39 | return 40 | } 41 | go HandleTunConnection(conn, URL, Port) 42 | } 43 | 44 | } 45 | 46 | func HandleTunConnection(conn net.Conn, URL string, Port int64) { 47 | fmt.Println("Getting a session ID from server...\n") 48 | r, e := http.Get(URL + "/init") 49 | if e != nil { 50 | fmt.Errorf("Unable to get a session!") 51 | conn.Close() 52 | return 53 | } 54 | sessdata, e := ioutil.ReadAll(r.Body) 55 | if e != nil { 56 | fmt.Errorf("Unable to read from the connection to get a session!") 57 | conn.Close() 58 | return 59 | } 60 | sessiontoken := string(sessdata) 61 | fmt.Println("Session tokean obtained:", sessiontoken) 62 | // Okay so we now have our session token. 63 | go DialUpWards(URL, sessiontoken, conn) 64 | go DialDownWards(URL+"/session/"+sessiontoken, conn) 65 | } 66 | 67 | func DialUpWards(URL string, sessiontoken string, conn net.Conn) { 68 | // because go can't do what I am about to do I am going to 69 | // #yolo my own HTTP code fora bit :v 70 | URL = strings.Replace(URL, "http://", "", 1) 71 | // WARNING THIS CODE WONT HELP YOU AGAINST SUB FOLDER URLS 72 | // NEED TO BE REFACTORED OR HELL, JUST DONE PROPERALLY AND NOT 73 | // INSANE LIKE THIS ONE IS. 74 | conn2, err := net.Dial("tcp", URL) 75 | defer conn.Close() 76 | defer conn2.Close() 77 | if err != nil { 78 | fmt.Errorf("Woah wtf, I tried to dial up and I got a error! %s", err) 79 | return 80 | } 81 | HTTPRequest := fmt.Sprintf("POST /session/%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: Newmarket\r\nContent-Length: 9999999\r\n\r\n", sessiontoken, URL) 82 | conn2.Write([]byte(HTTPRequest)) 83 | buf := make([]byte, 25565) 84 | for { 85 | read, e := conn.Read(buf) 86 | if e != nil { 87 | fmt.Errorf("Upstream broke down for reason %s", e.Error()) 88 | return 89 | } 90 | _, e = conn2.Write(buf[0:read]) 91 | if e != nil { 92 | fmt.Errorf("Tried to write data to remotesocket and it broke: %s", e.Error()) 93 | return 94 | } 95 | } 96 | } 97 | 98 | func DialDownWards(URL string, conn net.Conn) { 99 | r, e := http.Get(URL) 100 | defer conn.close() 101 | if e != nil { 102 | fmt.Errorf("Woah wtf, I tried to dial down and I got a error! %s", e.Error()) 103 | return 104 | } 105 | buf := make([]byte, 25565) 106 | for { 107 | read, e := r.Body.Read(buf) 108 | if e != nil { 109 | fmt.Errorf("Downstream broke down for reason %s", e.Error()) 110 | return 111 | } 112 | _, e = conn.Write(buf[0:read]) 113 | if e != nil { 114 | fmt.Errorf("Tried to write data to localsocket and it broke: %s", e.Error()) 115 | return 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/codegangsta/martini" 7 | "github.com/nu7hatch/gouuid" 8 | "net" 9 | "net/http" 10 | ) 11 | 12 | type ConnectionSession struct { 13 | Token string 14 | UpChan chan []byte 15 | DownChan chan []byte 16 | } 17 | 18 | var Sessions = make([]ConnectionSession, 0) 19 | var FwdHost string = "localhost:22" 20 | 21 | func main() { 22 | FwdHostFlag := flag.String("fwdhost", "localhost:22", "host:port as the destination for clients connecting to newmarket.") 23 | flag.Parse() 24 | 25 | FwdHost = *FwdHostFlag 26 | 27 | // Now that the TCP waiter is setup. lets start the HTTP server 28 | m := martini.Classic() 29 | // m.Map(Sessions) 30 | m.Get("/", Welcome) 31 | m.Get("/init", StartSession) 32 | m.Get("/session/:id", DownLink) 33 | m.Post("/session/:id", UpLink) 34 | m.Run() 35 | } 36 | 37 | func DownLink(rw http.ResponseWriter, req *http.Request, prams martini.Params) { 38 | SessionIDString := fmt.Sprintf("%s", prams["id"]) 39 | if !DoesSessionExist(SessionIDString) { 40 | http.Error(rw, "That session does not exist.", http.StatusBadRequest) 41 | return 42 | } 43 | // This one is where it does down 44 | SessionObj := GetSessionObject(SessionIDString) 45 | for data := range SessionObj.DownChan { 46 | _, e := rw.Write(data) 47 | if e != nil { 48 | fmt.Fprint(rw, "Connection dead :(") 49 | return 50 | } 51 | if f, ok := rw.(http.Flusher); ok { 52 | f.Flush() 53 | } 54 | } 55 | } 56 | 57 | func UpLink(rw http.ResponseWriter, req *http.Request, prams martini.Params) { 58 | SessionIDString := fmt.Sprintf("%s", prams["id"]) 59 | if !DoesSessionExist(SessionIDString) { 60 | http.Error(rw, "That session does not exist.", http.StatusBadRequest) 61 | return 62 | } 63 | // This one is where it does up 64 | SessionObj := GetSessionObject(SessionIDString) 65 | b := make([]byte, 25565) 66 | SessionObj.UpChan <- b 67 | for { 68 | n, e := req.Body.Read(b) 69 | if e != nil { 70 | fmt.Println("Uplink down. All is lost") 71 | return 72 | } 73 | SessionObj.UpChan <- b[0:n] 74 | } 75 | } 76 | 77 | func GetSessionObject(sessionID string) (Output ConnectionSession) { 78 | for _, Sess := range Sessions { 79 | if Sess.Token == sessionID { 80 | return Sess 81 | } 82 | } 83 | // Basically this should never happen, I'm not sure how to return nil either so 84 | // I will have to return what ever the hell "Output" is at this point. 85 | return Output 86 | } 87 | 88 | func DoesSessionExist(sessionID string) bool { 89 | for _, Sess := range Sessions { 90 | if Sess.Token == sessionID { 91 | return true 92 | } 93 | } 94 | return false 95 | } 96 | 97 | func StartSession(rw http.ResponseWriter, req *http.Request) string { 98 | // Now we need to make a new session and store it in a KV DB 99 | UpChan := make(chan []byte) 100 | DownChan := make(chan []byte) 101 | u, _ := uuid.NewV4() 102 | ustr := fmt.Sprintf("%s", u) 103 | WorkingSession := ConnectionSession{ 104 | Token: ustr, 105 | UpChan: UpChan, 106 | DownChan: DownChan, 107 | } 108 | go TCPSocket(WorkingSession) 109 | Sessions = append(Sessions, WorkingSession) 110 | return ustr 111 | } 112 | 113 | func UpPoll(conn net.Conn, UpChan chan []byte) { 114 | for { 115 | conn.Write(<-UpChan) 116 | } 117 | } 118 | 119 | func DownPoll(conn net.Conn, DownChan chan []byte) { 120 | for { 121 | buf := make([]byte, 25565) 122 | n, err := conn.Read(buf) 123 | if err != nil { 124 | fmt.Errorf("Could not Read!!! %s", err.Error()) 125 | break 126 | } else { 127 | DownChan <- buf[:n] 128 | } 129 | } 130 | } 131 | 132 | func TCPSocket(Session ConnectionSession) { 133 | <-Session.UpChan 134 | UpChan := Session.UpChan 135 | DownChan := Session.DownChan 136 | 137 | // This blocks the first "contact" 138 | // and awakes the server up from its terrifying slumber 139 | conn, err := net.Dial("tcp", FwdHost) 140 | if err != nil { 141 | fmt.Errorf("Could not dial SSH on the localhost, this is a srs issue. %s", err.Error()) 142 | return 143 | } 144 | go UpPoll(conn, UpChan) 145 | go DownPoll(conn, DownChan) 146 | } 147 | 148 | func Welcome(rw http.ResponseWriter, req *http.Request) string { 149 | return "Newmarket" 150 | } 151 | --------------------------------------------------------------------------------