]> Cypherpunks.ru repositories - govpn.git/commitdiff
Ability to use HTTP proxies for accessing server
authorSergey Matveev <stargrave@stargrave.org>
Sun, 23 Aug 2015 09:56:00 +0000 (12:56 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 23 Aug 2015 11:58:49 +0000 (14:58 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
13 files changed:
doc/client.texi
doc/overview.texi
doc/proxy.texi [new file with mode: 0644]
doc/server.texi
doc/todo.texi
doc/user.texi
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-client/proxy.go [new file with mode: 0644]
src/govpn/cmd/govpn-client/tcp.go
src/govpn/cmd/govpn-server/main.go
src/govpn/cmd/govpn-server/proxy.go [new file with mode: 0644]
src/govpn/cmd/govpn-server/tcp.go
src/govpn/cmd/govpn-server/udp.go

index d224fff281e69cc7eebc88b6e2eb7d1d5accc143..ec816c48f72d32fb76d28bca32cb94f05619a557 100644 (file)
@@ -1,11 +1,22 @@
 @node Client part
 @section Client part
 
-Except for common @code{-proto}, @code{-mtu}, @code{-stats}, @code{-egd}
+Except for common @code{-mtu}, @code{-stats}, @code{-egd}
 options client has the following ones:
 
 @table @code
 
+@item -proto
+@ref{Network transport} to use. Can be either @emph{udp} or @emph{tcp}.
+
+@item -proxy
+Use specified @emph{host:port} @ref{Proxy} server for accessing remote
+server.
+
+@item -proxy-auth
+Optional @emph{user:password} for HTTP Basic authorization on proxy
+server.
+
 @item -remote
 Address (@code{host:port} format) of remote server we need to connect to.
 
index 502422ade039f0a6126feb85fa83f53594979763..9f7846ee65b8e2ae07e830e405a0a3ecb1d0f714 100644 (file)
@@ -46,6 +46,7 @@ Copylefted free software: licensed under
 @item
 Works with @url{https://en.wikipedia.org/wiki/TAP_(network_driver), TAP}
 network interfaces on top of either UDP or TCP entirely.
+@item Ability to use HTTP proxies to access TCP server.
 @item
 @url{https://www.gnu.org/, GNU}/Linux and
 @url{http://www.freebsd.org/, FreeBSD} support.
diff --git a/doc/proxy.texi b/doc/proxy.texi
new file mode 100644 (file)
index 0000000..e5f5b80
--- /dev/null
@@ -0,0 +1,16 @@
+@node Proxy
+@section Proxy
+
+You can proxy your requests through HTTP using CONNECT method. This can
+help if you are only allowed to access outside world through HTTP proxy
+server.
+
+Server has @emph{-proxy} option allowing to listen on specified port and
+accept HTTP request. All of them will be treated as a CONNECT method
+switching to raw TCP mode. You can make POST request and server will
+anyway switch to raw TCP mode. You are not forced to use this option:
+any external HTTP proxy server can be used.
+
+Client has @emph{-proxy} option forcing it to connect to proxy and send
+CONNECT method. Optionally it can be authenticated on it using
+@emph{-proxy-auth} HTTP Basic method.
index 989a8a8a8f8e17f0964a7e15a36d82aed7d3fa4f..e86d86a3b5155f3f8430553084f61e35b1299683 100644 (file)
@@ -5,10 +5,19 @@ Except for common @code{-mtu}, @code{-stats}, @code{-egd} options server
 has the following ones:
 
 @table @code
+
+@item -proto
+@ref{Network transport} to use. Can be @emph{udp}, @emph{tcp} or @emph{all}.
+
 @item -bind
 Address (@code{host:port} format) we must bind to.
+
 @item -peers
 Path to the directory containing peers information, database.
+
+@item -proxy
+Start trivial HTTP @ref{Proxy} server on specified @emph{host:port}.
+
 @end table
 
 Peers directory must contain subdirectories with the names of client's
index 93b95f11d10150646a4caaa11cb87b30278be906..f8064cfa8902dc4bb01dd2e4c54f0541e63a49b0 100644 (file)
@@ -4,7 +4,6 @@
 @itemize
 @item Ability to tunnel only specified TCP connections, without full
   featured VPN solution. Similar to ssh -R.
-@item Ability to work over HTTP, WebSockets and something similar.
 @item Ability to noise handshake packets sizes.
 @item Increase performance.
 @item Randomize ports usage.
index 8959c74c7bafff5e6623d4b1f75bc137a3365cb4..413982279bdc87a83b0e02f31c921e64f1ed3112 100644 (file)
@@ -19,6 +19,7 @@ with @emph{Go 1.5} gives 435 Mbps TCP throughput.
 * PAKE:: Password Authenticated Key Agreement
 * Timeout::
 * Network transport::
+* Proxy::
 * MTU:: Maximum Transmission Unit
 * Stats::
 * Noise::
@@ -34,6 +35,7 @@ with @emph{Go 1.5} gives 435 Mbps TCP throughput.
 @include pake.texi
 @include timeout.texi
 @include netproto.texi
+@include proxy.texi
 @include mtu.texi
 @include stats.texi
 @include noise.texi
index 00d8f6abdd57c966cbe5ea39049211a34d38f795..7f32b6c1a5af998b003c2ad45caef7b97a22d5bb 100644 (file)
@@ -40,6 +40,8 @@ var (
        upPath     = flag.String("up", "", "Path to up-script")
        downPath   = flag.String("down", "", "Path to down-script")
        stats      = flag.String("stats", "", "Enable stats retrieving on host:port")
+       proxyAddr  = flag.String("proxy", "", "Use HTTP proxy on host:port")
+       proxyAuth  = flag.String("proxy-auth", "", "user:password Basic proxy auth")
        mtu        = flag.Int("mtu", 1452, "MTU for outgoing packets")
        timeoutP   = flag.Int("timeout", 60, "Timeout seconds")
        noisy      = flag.Bool("noise", false, "Enable noise appending")
@@ -83,7 +85,11 @@ func main() {
        case "udp":
                conn, sink, ready = startUDP()
        case "tcp":
-               conn, sink, ready = startTCP()
+               if *proxyAddr != "" {
+                       conn, sink, ready = proxyTCP()
+               } else {
+                       conn, sink, ready = startTCP()
+               }
        default:
                log.Fatalln("Unknown protocol specified")
        }
diff --git a/src/govpn/cmd/govpn-client/proxy.go b/src/govpn/cmd/govpn-client/proxy.go
new file mode 100644 (file)
index 0000000..678a5ae
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+       "bufio"
+       "encoding/base64"
+       "io"
+       "log"
+       "net"
+       "net/http"
+)
+
+func proxyTCP() (io.Writer, chan []byte, chan struct{}) {
+       proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr)
+       if err != nil {
+               log.Fatalln("Can not resolve proxy address:", err)
+       }
+       conn, err := net.DialTCP("tcp", nil, proxyAddr)
+       if err != nil {
+               log.Fatalln("Can not connect to proxy:", err)
+       }
+       req := "CONNECT " + *remoteAddr + " HTTP/1.1\n"
+       req += "Host: " + *remoteAddr + "\n"
+       if *proxyAuth != "" {
+               req += "Proxy-Authorization: Basic "
+               req += base64.StdEncoding.EncodeToString([]byte(*proxyAuth)) + "\n"
+       }
+       req += "\n"
+       conn.Write([]byte(req))
+       resp, err := http.ReadResponse(
+               bufio.NewReader(conn),
+               &http.Request{Method: "CONNECT"},
+       )
+       if err != nil || resp.StatusCode != http.StatusOK {
+               log.Fatalln("Unexpected response from proxy")
+       }
+       sink := make(chan []byte)
+       ready := make(chan struct{})
+       go handleTCP(conn, sink, ready)
+       go func() { ready <- struct{}{} }()
+       return TCPSender{conn}, sink, ready
+}
index aa868da3d861a7c87e86c3cca1716518e9b31936..373789af8be5f9c7f64ecf8df320c04bef20528c 100644 (file)
@@ -50,46 +50,50 @@ func startTCP() (io.Writer, chan []byte, chan struct{}) {
        }
        sink := make(chan []byte)
        ready := make(chan struct{})
-       go func() {
-               var err error
-               var n int
-               var sizeNbuf int
-               sizeBuf := make([]byte, 2)
-               var sizeNeed uint16
-               var bufN uint16
-               buf := make([]byte, govpn.MTU)
-               for {
-                       <-ready
+       go handleTCP(c, sink, ready)
+       go func() { ready <- struct{}{} }()
+       return conn, sink, ready
+}
+
+func handleTCP(conn *net.TCPConn, sink chan []byte, ready chan struct{}) {
+       var err error
+       var n int
+       var sizeNbuf int
+       sizeBuf := make([]byte, 2)
+       var sizeNeed uint16
+       var bufN uint16
+       buf := make([]byte, govpn.MTU)
+       for {
+               <-ready
+               if sizeNbuf != 2 {
+                       n, err = conn.Read(sizeBuf[sizeNbuf:2])
+                       if err != nil {
+                               break
+                       }
+                       sizeNbuf += n
                        if sizeNbuf != 2 {
-                               n, err = c.Read(sizeBuf[sizeNbuf:2])
-                               if err != nil {
-                                       break
-                               }
-                               sizeNbuf += n
-                               if sizeNbuf == 2 {
-                                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
-                                       if sizeNeed > uint16(govpn.MTU)-2 {
-                                               log.Println("Invalid TCP size, skipping")
-                                               sizeNbuf = 0
-                                               sink <- nil
-                                               continue
-                                       }
-                                       bufN = 0
-                               }
+                               sink <- nil
+                               continue
                        }
-               ReadMore:
-                       if sizeNeed != bufN {
-                               n, err = c.Read(buf[bufN:sizeNeed])
-                               if err != nil {
-                                       break
-                               }
-                               bufN += uint16(n)
-                               goto ReadMore
+                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
+                       if int(sizeNeed) > govpn.MTU-2 {
+                               log.Println("Invalid TCP size, skipping")
+                               sizeNbuf = 0
+                               sink <- nil
+                               continue
                        }
-                       sizeNbuf = 0
-                       sink <- buf[:sizeNeed]
+                       bufN = 0
                }
-       }()
-       go func() { ready <- struct{}{} }()
-       return conn, sink, ready
+       ReadMore:
+               if sizeNeed != bufN {
+                       n, err = conn.Read(buf[bufN:sizeNeed])
+                       if err != nil {
+                               break
+                       }
+                       bufN += uint16(n)
+                       goto ReadMore
+               }
+               sizeNbuf = 0
+               sink <- buf[:sizeNeed]
+       }
 }
index 664e04d1b72a509721dd8c6bed52add868538876..37135b9155d09df6314df3db11a7ec1d8ed43602 100644 (file)
@@ -38,6 +38,7 @@ var (
        proto     = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
        peersPath = flag.String("peers", "peers", "Path to peers keys directory")
        stats     = flag.String("stats", "", "Enable stats retrieving on host:port")
+       proxy     = flag.String("proxy", "", "Enable HTTP proxy on host:port")
        mtu       = flag.Int("mtu", 1452, "MTU for outgoing packets")
        egdPath   = flag.String("egd", "", "Optional path to EGD socket")
 )
@@ -101,12 +102,12 @@ func main() {
        sink := make(chan Pkt)
        switch *proto {
        case "udp":
-               startUDP(&sink)
+               startUDP(sink)
        case "tcp":
-               startTCP(&sink)
+               startTCP(sink)
        case "all":
-               startUDP(&sink)
-               startTCP(&sink)
+               startUDP(sink)
+               startTCP(sink)
        default:
                log.Fatalln("Unknown protocol specified")
        }
@@ -142,6 +143,9 @@ func main() {
                }
                go govpn.StatsProcessor(statsPort, &knownPeers)
        }
+       if *proxy != "" {
+               go proxyStart(sink)
+       }
        log.Println("Server started")
 
 MainCycle:
diff --git a/src/govpn/cmd/govpn-server/proxy.go b/src/govpn/cmd/govpn-server/proxy.go
new file mode 100644 (file)
index 0000000..f1e8419
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+       "log"
+       "net/http"
+)
+
+type proxyHandler struct {
+       sink chan Pkt
+}
+
+func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       conn, _, err := w.(http.Hijacker).Hijack()
+       if err != nil {
+               log.Println("Hijacking failed:", err.Error())
+               return
+       }
+       conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
+       ready := make(chan struct{}, 1)
+       go handleTCP(conn, p.sink, ready)
+       ready <- struct{}{}
+
+}
+
+func proxyStart(sink chan Pkt) {
+       log.Println("HTTP proxy listening on:", *proxy)
+       s := &http.Server{
+               Addr:    *proxy,
+               Handler: proxyHandler{sink},
+       }
+       log.Println("HTTP proxy result:", s.ListenAndServe())
+}
index c7b1788e9e405170ab51dfed58ea0dc5a28bd5b3..42467d644ec98feac3bddcdbb379e38667314a09 100644 (file)
@@ -27,7 +27,7 @@ import (
 )
 
 type TCPSender struct {
-       conn *net.TCPConn
+       conn net.Conn
 }
 
 func (c TCPSender) Write(data []byte) (int, error) {
@@ -36,7 +36,7 @@ func (c TCPSender) Write(data []byte) (int, error) {
        return c.conn.Write(append(size, data...))
 }
 
-func startTCP(sink *chan Pkt) {
+func startTCP(sink chan Pkt) {
        bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
        if err != nil {
                log.Fatalln("Can not resolve bind address:", err)
@@ -50,53 +50,57 @@ func startTCP(sink *chan Pkt) {
                for {
                        conn, _ := listener.AcceptTCP()
                        ready := make(chan struct{}, 1)
-                       go func(conn *net.TCPConn, ready chan struct{}) {
-                               addr := conn.RemoteAddr().String()
-                               var err error
-                               var n int
-                               var sizeNbuf int
-                               sizeBuf := make([]byte, 2)
-                               var sizeNeed uint16
-                               var bufN uint16
-                               buf := make([]byte, govpn.MTU)
-                               for {
-                                       <-ready
-                                       if sizeNbuf != 2 {
-                                               n, err = conn.Read(sizeBuf[sizeNbuf:2])
-                                               if err != nil {
-                                                       break
-                                               }
-                                               sizeNbuf += n
-                                               if sizeNbuf == 2 {
-                                                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
-                                                       if sizeNeed > uint16(govpn.MTU)-2 {
-                                                               log.Println("Invalid TCP size, skipping")
-                                                               sizeNbuf = 0
-                                                               *sink <- Pkt{ready: ready}
-                                                               continue
-                                                       }
-                                                       bufN = 0
-                                               }
-                                       }
-                               ReadMore:
-                                       if sizeNeed != bufN {
-                                               n, err = conn.Read(buf[bufN:sizeNeed])
-                                               if err != nil {
-                                                       break
-                                               }
-                                               bufN += uint16(n)
-                                               goto ReadMore
-                                       }
-                                       sizeNbuf = 0
-                                       *sink <- Pkt{
-                                               addr,
-                                               TCPSender{conn},
-                                               buf[:sizeNeed],
-                                               ready,
-                                       }
-                               }
-                       }(conn, ready)
+                       go handleTCP(conn, sink, ready)
                        ready <- struct{}{}
                }
        }()
 }
+
+func handleTCP(conn net.Conn, sink chan Pkt, ready chan struct{}) {
+       addr := conn.RemoteAddr().String()
+       var err error
+       var n int
+       var sizeNbuf int
+       sizeBuf := make([]byte, 2)
+       var sizeNeed uint16
+       var bufN uint16
+       buf := make([]byte, govpn.MTU)
+       for {
+               <-ready
+               if sizeNbuf != 2 {
+                       n, err = conn.Read(sizeBuf[sizeNbuf:2])
+                       if err != nil {
+                               break
+                       }
+                       sizeNbuf += n
+                       if sizeNbuf != 2 {
+                               sink <- Pkt{ready: ready}
+                               continue
+                       }
+                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
+                       if int(sizeNeed) > govpn.MTU-2 {
+                               log.Println("Invalid TCP size, skipping")
+                               sizeNbuf = 0
+                               sink <- Pkt{ready: ready}
+                               continue
+                       }
+                       bufN = 0
+               }
+       ReadMore:
+               if sizeNeed != bufN {
+                       n, err = conn.Read(buf[bufN:sizeNeed])
+                       if err != nil {
+                               break
+                       }
+                       bufN += uint16(n)
+                       goto ReadMore
+               }
+               sizeNbuf = 0
+               sink <- Pkt{
+                       addr,
+                       TCPSender{conn},
+                       buf[:sizeNeed],
+                       ready,
+               }
+       }
+}
index 26b28a835e70c56b48487871475879ea260f70b1..c2d6e96c2fe732c7064629e3bb02289701976c71 100644 (file)
@@ -35,7 +35,7 @@ func (c UDPSender) Write(data []byte) (int, error) {
        return c.conn.WriteToUDP(data, c.addr)
 }
 
-func startUDP(sink *chan Pkt) {
+func startUDP(sink chan Pkt) {
        bind, err := net.ResolveUDPAddr("udp", *bindAddr)
        ready := make(chan struct{})
        if err != nil {
@@ -56,10 +56,10 @@ func startUDP(sink *chan Pkt) {
                        lconn.SetReadDeadline(time.Now().Add(time.Second))
                        n, raddr, err = lconn.ReadFromUDP(buf)
                        if err != nil {
-                               *sink <- Pkt{ready: ready}
+                               sink <- Pkt{ready: ready}
                                continue
                        }
-                       *sink <- Pkt{
+                       sink <- Pkt{
                                raddr.String(),
                                UDPSender{lconn, raddr},
                                buf[:n],