@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.
@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.
--- /dev/null
+@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.
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
@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.
* PAKE:: Password Authenticated Key Agreement
* Timeout::
* Network transport::
+* Proxy::
* MTU:: Maximum Transmission Unit
* Stats::
* Noise::
@include pake.texi
@include timeout.texi
@include netproto.texi
+@include proxy.texi
@include mtu.texi
@include stats.texi
@include noise.texi
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")
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")
}
--- /dev/null
+/*
+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
+}
}
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]
+ }
}
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")
)
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")
}
}
go govpn.StatsProcessor(statsPort, &knownPeers)
}
+ if *proxy != "" {
+ go proxyStart(sink)
+ }
log.Println("Server started")
MainCycle:
--- /dev/null
+/*
+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())
+}
)
type TCPSender struct {
- conn *net.TCPConn
+ conn net.Conn
}
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)
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,
+ }
+ }
+}
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 {
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],