From: Sergey Matveev Date: Sun, 23 Aug 2015 14:43:27 +0000 (+0300) Subject: Merge branch 'develop' X-Git-Tag: 3.5^0 X-Git-Url: http://www.git.cypherpunks.ru/?a=commitdiff_plain;h=296f3d2e9197b76209a4e86ed8856aea9195d94c;hp=f9b0c70547abc7275dadb649f35ab3d0964bb575;p=govpn.git Merge branch 'develop' Signed-off-by: Sergey Matveev --- diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f2d6ae4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "src/github.com/bigeagle/water"] + path = src/github.com/bigeagle/water + url = https://github.com/bigeagle/water.git +[submodule "src/github.com/agl/ed25519"] + path = src/github.com/agl/ed25519 + url = https://github.com/agl/ed25519.git +[submodule "src/golang.org/x/crypto"] + path = src/golang.org/x/crypto + url = https://go.googlesource.com/crypto diff --git a/README b/README index 7fdd2e8..6173be4 100644 --- a/README +++ b/README @@ -1,11 +1,14 @@ GoVPN is simple secure free software virtual private network daemon, -aimed to be reviewable, secure, DPI-resistant, written on Go. +aimed to be reviewable, secure, DPI/censorship-resistant, written on Go. -It uses fast PAKE DH A-EKE for mutual strong zero-knowledge peers -authentication. Data transport is encrypted, authenticated, hides -message's length and timestamp. PFS property, resistance to dictionary -attacks, replay attacks. Built-in heartbeating, rehandshaking, real-time -statistics, IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support. +It uses fast strong password authenticated key agreement protocol with +augmented mutual peers authentication (PAKE DH A-EKE). Encrypted, +authenticated data transport that hides message's length and timestamps. +Perfect forward secrecy property. Resistance to: offline dictionary +attacks, replay attacks, client's passwords compromising and dictionary +attacks on the server side. Built-in heartbeating, rehandshaking, +real-time statistics. Ability to work through UDP, TCP and HTTP proxies. +IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support. GoVPN is free software: see the file COPYING for copying conditions. @@ -20,6 +23,6 @@ subscription and archive access information, or send email with the subject "subscribe" to govpn-devel-request@lists.cypherpunks.ru. Development Git source code repository currently is located here: -https://github.com/stargrave/govpn.git +http://git.cypherpunks.ru/cgit.cgi/govpn.git/ For futher information please read either doc/govpn.info or doc/govpn.texi. diff --git a/VERSION b/VERSION index 2f4b607..5a95802 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.4 +3.5 diff --git a/common.mk b/common.mk index b2f01fe..2ae9bb1 100644 --- a/common.mk +++ b/common.mk @@ -1,4 +1,4 @@ -LDFLAGS = -X govpn.Version $(VERSION) +LDFLAGS = -X govpn.Version=$(VERSION) PREFIX ?= /usr/local BINDIR = $(DESTDIR)$(PREFIX)/bin INFODIR = $(DESTDIR)$(PREFIX)/info @@ -7,20 +7,17 @@ DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/govpn all: govpn-client govpn-server govpn-verifier -depends: - $(MAKE) -C src - -govpn-client: depends +govpn-client: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-client -govpn-server: depends +govpn-server: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-server -govpn-verifier: depends +govpn-verifier: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-verifier bench: - cd src/govpn ; GOPATH=$(GOPATH) GOMAXPROC=2 go test -bench . + cd src/govpn ; GOPATH=$(GOPATH) go test -bench . clean: rm -f govpn-client govpn-server govpn-verifier diff --git a/doc/client.texi b/doc/client.texi index e2c26fd..ec816c4 100644 --- a/doc/client.texi +++ b/doc/client.texi @@ -1,11 +1,22 @@ @node Client part @section Client part -Except for common @code{-mtu}, @code{-stats}, @code{-egd} options client -has the following ones: +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. diff --git a/doc/cpr.texi b/doc/cpr.texi index 88339db..cd7b48a 100644 --- a/doc/cpr.texi +++ b/doc/cpr.texi @@ -1,5 +1,5 @@ @node CPR -@section CPR +@section Constant Packet Rate Constant Packet Rate is used to hide fact of underlying payload packets appearance. In this mode daemon inserts necessary dummy packets and diff --git a/doc/developer.texi b/doc/developer.texi index 5f85e00..7ccd927 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -1,6 +1,8 @@ @node Developer manual @unnumbered Developer manual +Pay attention how to get @ref{Development source code}. + @table @asis @item Nonce and identity encryption @url{http://143.53.36.235:8080/tea.htm, XTEA}. @@ -17,9 +19,10 @@ and @url{http://ed25519.cr.yp.to/, Ed25519}. @url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} based on @url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}. @item Packet overhead -26 bytes per packet. +26 bytes per packet. Two more bytes in TCP mode. @item Handshake overhead -4 UDP (2 from client, 2 from server) packets, 264 bytes total payload. +4 UDP (2 from client, 2 from server) packets (round-trips for TCP), +264 bytes total payload (8 bytes more in TCP mode). @item Entropy required 832 bits in average on client, 832 bits in average on server side per handshake. diff --git a/doc/download.texi b/doc/download.texi index 83abb66..124ea63 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -6,44 +6,46 @@ You can obtain releases source code prepared tarballs from the links below: @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Version @tab Size @tab Tarball @tab SHA256 checksum -@item 1.5 @tab 19 KiB -@tab @url{download/govpn-1.5.tar.xz, link} @url{download/govpn-1.5.tar.xz.sig, sign} -@tab @code{715b07d4d1ea4396c3e37014ca65ec3768818423521f3c12e7200b6edca48c31} +@item 3.4 @tab 175 KiB +@tab @url{download/govpn-3.4.tar.xz, link} @url{download/govpn-3.4.tar.xz.sig, sign} +@tab @code{266612a7f8faa6ceb2955ed611c0c21872776306f4eaad5b785145bbb0390c82} -@item 2.0 @tab 31 KiB -@tab @url{download/govpn-2.0.tar.xz, link} @url{download/govpn-2.0.tar.xz.sig, sign} -@tab @code{d43be1248d6a46ba8ca75be2fdab5e3d8b0660fb9df9b6d87cfa3973722b42be} +@item 3.3 @tab 175 KiB +@tab @url{download/govpn-3.3.tar.xz, link} @url{download/govpn-3.3.tar.xz.sig, sign} +@tab @code{1834a057215324f49d6272b2beb89f1532105156f7e853eae855659992ac0c84} -@item 2.2 @tab 32 KiB -@tab @url{download/govpn-2.2.tar.xz, link} @url{download/govpn-2.2.tar.xz.sig, sign} -@tab @code{5745278bce8b9a3bd7ec1636507bbce8c17ba1d79f1568e2f3681b7a90bbe6e1} +@item 3.2 @tab 174 KiB +@tab @url{download/govpn-3.2.tar.xz, link} @url{download/govpn-3.2.tar.xz.sig, sign} +@tab @code{388e98d6adef5ebf3431b0d48419f54d2e2064c657de67e23c669ebcf273126d} -@item 2.3 @tab 34 KiB -@tab @url{download/govpn-2.3.tar.xz, link} @url{download/govpn-2.3.tar.xz.sig, sign} -@tab @code{92986ec6d6da107c6cc1143659e5a154cd19b8f2ede5fa7f5ccc4525ae468e97} +@item 3.1 @tab 54 KiB +@tab @url{download/govpn-3.1.tar.xz, link} @url{download/govpn-3.1.tar.xz.sig, sign} +@tab @code{4034a67eb472e33760ed1783ca871f531c3a6be99b9bd6213f4f83c1147c344b} + +@item 3.0 @tab 53 KiB +@tab @url{download/govpn-3.0.tar.xz, link} @url{download/govpn-3.0.tar.xz.sig, sign} +@tab @code{12579c5c3cccfe73c66b5893335bc70c42d7b13b8e94c7751ec65d421eaff9a5} @item 2.4 @tab 42 KiB @tab @url{download/govpn-2.4.tar.xz, link} @url{download/govpn-2.4.tar.xz.sig, sign} @tab @code{df45225bac2384c5eed73c5cdb05dc3581495e08d365317beb03a2487d46b98c} -@item 3.0 @tab 53 KiB -@tab @url{download/govpn-3.0.tar.xz, link} @url{download/govpn-3.0.tar.xz.sig, sign} -@tab @code{12579c5c3cccfe73c66b5893335bc70c42d7b13b8e94c7751ec65d421eaff9a5} +@item 2.3 @tab 34 KiB +@tab @url{download/govpn-2.3.tar.xz, link} @url{download/govpn-2.3.tar.xz.sig, sign} +@tab @code{92986ec6d6da107c6cc1143659e5a154cd19b8f2ede5fa7f5ccc4525ae468e97} -@item 3.1 @tab 54 KiB -@tab @url{download/govpn-3.1.tar.xz, link} @url{download/govpn-3.1.tar.xz.sig, sign} -@tab @code{4034a67eb472e33760ed1783ca871f531c3a6be99b9bd6213f4f83c1147c344b} +@item 2.2 @tab 32 KiB +@tab @url{download/govpn-2.2.tar.xz, link} @url{download/govpn-2.2.tar.xz.sig, sign} +@tab @code{5745278bce8b9a3bd7ec1636507bbce8c17ba1d79f1568e2f3681b7a90bbe6e1} -@item 3.2 @tab 174 KiB -@tab @url{download/govpn-3.2.tar.xz, link} @url{download/govpn-3.2.tar.xz.sig, sign} -@tab @code{388e98d6adef5ebf3431b0d48419f54d2e2064c657de67e23c669ebcf273126d} +@item 2.0 @tab 31 KiB +@tab @url{download/govpn-2.0.tar.xz, link} @url{download/govpn-2.0.tar.xz.sig, sign} +@tab @code{d43be1248d6a46ba8ca75be2fdab5e3d8b0660fb9df9b6d87cfa3973722b42be} -@item 3.3 @tab 175 KiB -@tab @url{download/govpn-3.3.tar.xz, link} @url{download/govpn-3.3.tar.xz.sig, sign} -@tab @code{1834a057215324f49d6272b2beb89f1532105156f7e853eae855659992ac0c84} +@item 1.5 @tab 19 KiB +@tab @url{download/govpn-1.5.tar.xz, link} @url{download/govpn-1.5.tar.xz.sig, sign} +@tab @code{715b07d4d1ea4396c3e37014ca65ec3768818423521f3c12e7200b6edca48c31} @end multitable Also you can try its @ref{Contacts, .onion} version. -Sourceforge.net also provides mirror for the files above: -@url{http://sourceforge.net/projects/govpn/files/}. diff --git a/doc/egd.texi b/doc/egd.texi index 77d34bf..62d1d87 100644 --- a/doc/egd.texi +++ b/doc/egd.texi @@ -1,5 +1,5 @@ @node EGD -@section EGD +@section Entropy Gathering Daemon Overall security mainly depends on client side: @ref{PAKE, good passphrase} and cryprographically good pseudo random diff --git a/doc/example.texi b/doc/example.texi index 129b10f..a2e4173 100644 --- a/doc/example.texi +++ b/doc/example.texi @@ -17,18 +17,11 @@ is 1432. @strong{Install}. At first you must @ref{Installation, install} this software: download, check the signature, compile. -Do not forget about setting @code{GOMAXPROC} environment variable for -using more than one CPU on both sides: - -@example -% export GOMAXPROC=4 -@end example - @strong{Prepare the server}. Create the new client, named (for example) "Alice": @example -% ./utils/newclient.sh Alice +server% ./utils/newclient.sh Alice Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier @end example @@ -38,11 +31,11 @@ Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier identity: @example -% ./utils/storekey.sh /tmp/passphrase +client% ./utils/storekey.sh /tmp/passphrase Enter passphrase:[my secure passphrase is here] -% govpn-verifier -id 6d4ac605ce8dc37c2f0bf21cb542a713 -key /tmp/passphrase +client% govpn-verifier -id 6d4ac605ce8dc37c2f0bf21cb542a713 -key /tmp/passphrase 562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55 -% rm /tmp/passphrase +client% rm /tmp/passphrase @end example "562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55" -- @@ -51,7 +44,7 @@ this is verifier itself. @strong{Save verifier on server}. @example -% cat > peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier < peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier <server) messages, evens for server(->client) messages. @code{ENCn} is XTEA block cipher algorithm used here as PRP (pseudo -random permutation) to randomize, obfuscate @code{SERIAL}. Plaintext +random permutation function) to obfuscate @code{SERIAL}. Plaintext @code{SERIAL} state is kept in peers internal state, but encrypted before transmission. XTEA is compact and fast enough. Salsa20 is PRF -function and requires much more code to create PRP from it. XTEA's -encryption key is the first 128-bit of Salsa20's output with established -common key and zero nonce (message nonces start from 1). +function and requires much more code to create PRP from it. + +XTEA's encryption key is the first 128-bit of Salsa20's output with +established common key and zero nonce (message nonces start from 1). + +@code{ENC} is Salsa20 stream cipher, with established session @code{KEY} +and obfuscated @code{SERIAL} used as a nonce. First 256 bits of +Salsa20's output is used as Poly1305 authentication key, next 256 bits +are ignored. All remaining output is XORed with the data, encrypting it. -Encrypted @code{SERIAL} is used as a nonce for @code{DATA} encryption: -encryption key is different during each handshake, so (key, nonce) pair -is always used only once. @code{ENC} is Salsa20 cipher, with established -session @code{KEY} and encrypted @code{SERIAL} used as a nonce. -@code{DATA_SIZE} is @emph{uint16} storing length of the @code{DATA}. +@code{DATA_SIZE} is big-endian @emph{uint16} storing length of the +@code{DATA}. @code{NOISE} is optional. It is just some junk data, intended to fill up packet to MTU size. This is useful for concealing payload packets length. @code{AUTH} is Poly1305 authentication function. First 256 bits of -Salsa20 output are used as a one-time key for @code{AUTH}. Next 256 bits -of Salsa20 are ignored. All remaining output is XORed with the data, -encrypting it. +Salsa20 output are used as a one-time key for @code{AUTH}. To prevent replay attacks we must remember received @code{SERIAL}s and if meet one, then drop it. Basically we could just store latest number diff --git a/doc/user.texi b/doc/user.texi index 3702f84..0d237a0 100644 --- a/doc/user.texi +++ b/doc/user.texi @@ -4,16 +4,22 @@ Announcements about updates and new releases can be found in @ref{Contacts}. GoVPN is split into two pieces: client and server. Each of them work on -top of UDP and TAP virtual network interfaces. GoVPN is just a +top of UDP/TCP and TAP virtual network interfaces. GoVPN is just a tunnelling of Ethernet frames, nothing less, nothing more. All you IP-related network management is not touched by VPN at all. You can automate it using up and down shell scripts. +What network performance can user expect? For example single +@emph{Intel i5-2450M 2.5 GHz} core on @emph{FreeBSD 10.2 amd64} +with @emph{Go 1.5} gives 435 Mbps TCP (over UDP) throughput. + @menu * EGD:: Entropy gathering daemon * Identity:: * PAKE:: Password Authenticated Key Agreement * Timeout:: +* Network transport:: +* Proxy:: * MTU:: Maximum Transmission Unit * Stats:: * Noise:: @@ -28,6 +34,8 @@ automate it using up and down shell scripts. @include identity.texi @include pake.texi @include timeout.texi +@include netproto.texi +@include proxy.texi @include mtu.texi @include stats.texi @include noise.texi diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 1fb92d9..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -github.com -golang.org diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 452b23f..0000000 --- a/src/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -GIT ?= git - -all: golang.org/x/crypto github.com/agl/ed25519 github.com/bigeagle/water - -github.com/bigeagle/water: - mkdir -p github.com/bigeagle - $(GIT) clone https://github.com/bigeagle/water.git github.com/bigeagle/water - cd github.com/bigeagle/water && $(GIT) checkout --force 36aebfeb35da4f1f6a975726716c6fc563c5c495 - -github.com/agl/ed25519: - mkdir -p github.com/agl - $(GIT) clone https://github.com/agl/ed25519.git github.com/agl/ed25519 - cd github.com/agl/ed25519 && $(GIT) checkout --force d2b94fd789ea21d12fac1a4443dd3a3f79cda72c - -golang.org/x/crypto: - mkdir -p golang.org/x - $(GIT) clone https://go.googlesource.com/crypto golang.org/x/crypto - cd golang.org/x/crypto && $(GIT) checkout --force 24ffb5feb3312a39054178a4b0a4554fc2201248 diff --git a/src/github.com/agl/ed25519 b/src/github.com/agl/ed25519 new file mode 160000 index 0000000..d2b94fd --- /dev/null +++ b/src/github.com/agl/ed25519 @@ -0,0 +1 @@ +Subproject commit d2b94fd789ea21d12fac1a4443dd3a3f79cda72c diff --git a/src/github.com/bigeagle/water b/src/github.com/bigeagle/water new file mode 160000 index 0000000..36aebfe --- /dev/null +++ b/src/github.com/bigeagle/water @@ -0,0 +1 @@ +Subproject commit 36aebfeb35da4f1f6a975726716c6fc563c5c495 diff --git a/src/golang.org/x/crypto b/src/golang.org/x/crypto new file mode 160000 index 0000000..81bf771 --- /dev/null +++ b/src/golang.org/x/crypto @@ -0,0 +1 @@ +Subproject commit 81bf7719a6b7ce9b665598222362b50122dfc13b diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index 9b65677..7f32b6c 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -21,6 +21,7 @@ package main import ( "flag" + "io" "log" "net" "os" @@ -32,12 +33,15 @@ import ( var ( remoteAddr = flag.String("remote", "", "Remote server address") + proto = flag.String("proto", "udp", "Protocol to use: udp or tcp") ifaceName = flag.String("iface", "tap0", "TAP network interface") IDRaw = flag.String("id", "", "Client identification") keyPath = flag.String("key", "", "Path to passphrase file") 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") @@ -74,17 +78,20 @@ func main() { } govpn.PeersInitDummy(id, conf) - bind, err := net.ResolveUDPAddr("udp", "0.0.0.0:0") - if err != nil { - log.Fatalln("Can not resolve address:", err) - } - conn, err := net.ListenUDP("udp", bind) - if err != nil { - log.Fatalln("Can not listen on UDP:", err) - } - remote, err := net.ResolveUDPAddr("udp", *remoteAddr) - if err != nil { - log.Fatalln("Can not resolve remote address:", err) + var conn io.Writer + var sink chan []byte + var ready chan struct{} + switch *proto { + case "udp": + conn, sink, ready = startUDP() + case "tcp": + if *proxyAddr != "" { + conn, sink, ready = proxyTCP() + } else { + conn, sink, ready = startTCP() + } + default: + log.Fatalln("Unknown protocol specified") } tap, ethSink, ethReady, _, err := govpn.TAPListen( @@ -95,17 +102,16 @@ func main() { if err != nil { log.Fatalln("Can not listen on TAP interface:", err) } - udpSink, udpBuf, udpReady := govpn.ConnListen(conn) timeouts := 0 firstUpCall := true var peer *govpn.Peer var ethPkt []byte - var udpPkt govpn.UDPPkt - var udpPktData []byte - knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{remote.String(): &peer}) + var pkt []byte + knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer}) log.Println(govpn.VersionGet()) + log.Println("Connected to", *proto, *remoteAddr) log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU()) if *stats != "" { log.Println("Stats are going to listen on", *stats) @@ -120,14 +126,14 @@ func main() { signal.Notify(termSignal, os.Interrupt, os.Kill) log.Println("Starting handshake") - handshake := govpn.HandshakeStart(conf, conn, remote) + handshake := govpn.HandshakeStart(*remoteAddr, conn, conf) MainCycle: for { if peer != nil && (peer.BytesIn+peer.BytesOut) > govpn.MaxBytesPerKey { peer.Zero() peer = nil - handshake = govpn.HandshakeStart(conf, conn, remote) + handshake = govpn.HandshakeStart(*remoteAddr, conn, conf) log.Println("Rehandshaking") } select { @@ -140,30 +146,24 @@ MainCycle: } continue } - peer.EthProcess(ethPkt, conn, ethReady) - case udpPkt = <-udpSink: + peer.EthProcess(ethPkt, ethReady) + case pkt = <-sink: timeouts++ if timeouts >= timeout { break MainCycle } - if udpPkt.Addr == nil { - udpReady <- struct{}{} + if pkt == nil { + ready <- struct{}{} continue } - udpPktData = udpBuf[:udpPkt.Size] if peer == nil { - if udpPkt.Addr.String() != remote.String() { - udpReady <- struct{}{} - log.Println("Unknown handshake message") - continue - } - if govpn.IDsCache.Find(udpPktData) == nil { + if govpn.IDsCache.Find(pkt) == nil { log.Println("Invalid identity in handshake packet") - udpReady <- struct{}{} + ready <- struct{}{} continue } - if p := handshake.Client(conn, udpPktData); p != nil { + if p := handshake.Client(pkt); p != nil { log.Println("Handshake completed") if firstUpCall { go govpn.ScriptCall(*upPath, *ifaceName) @@ -173,14 +173,14 @@ MainCycle: handshake.Zero() handshake = nil } - udpReady <- struct{}{} + ready <- struct{}{} continue } if peer == nil { - udpReady <- struct{}{} + ready <- struct{}{} continue } - if peer.UDPProcess(udpPktData, tap, udpReady) { + if peer.PktProcess(pkt, tap, ready) { timeouts = 0 } } diff --git a/src/govpn/cmd/govpn-client/proxy.go b/src/govpn/cmd/govpn-client/proxy.go new file mode 100644 index 0000000..678a5ae --- /dev/null +++ b/src/govpn/cmd/govpn-client/proxy.go @@ -0,0 +1,59 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +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 +} diff --git a/src/govpn/cmd/govpn-client/tcp.go b/src/govpn/cmd/govpn-client/tcp.go new file mode 100644 index 0000000..373789a --- /dev/null +++ b/src/govpn/cmd/govpn-client/tcp.go @@ -0,0 +1,99 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +package main + +import ( + "encoding/binary" + "io" + "log" + "net" + + "govpn" +) + +// TCPSender prepends size prefix to each outgoing packet. +type TCPSender struct { + conn *net.TCPConn +} + +func (c TCPSender) Write(data []byte) (int, error) { + size := make([]byte, 2) + binary.BigEndian.PutUint16(size, uint16(len(data))) + return c.conn.Write(append(size, data...)) +} + +func startTCP() (io.Writer, chan []byte, chan struct{}) { + remote, err := net.ResolveTCPAddr("tcp", *remoteAddr) + if err != nil { + log.Fatalln("Can not resolve remote address:", err) + } + c, err := net.DialTCP("tcp", nil, remote) + conn := TCPSender{c} + if err != nil { + log.Fatalln("Can not connect TCP:", err) + } + sink := make(chan []byte) + ready := make(chan struct{}) + 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 { + sink <- nil + continue + } + sizeNeed = binary.BigEndian.Uint16(sizeBuf) + if int(sizeNeed) > govpn.MTU-2 { + log.Println("Invalid TCP size, skipping") + sizeNbuf = 0 + sink <- nil + 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 <- buf[:sizeNeed] + } +} diff --git a/src/govpn/cmd/govpn-client/udp.go b/src/govpn/cmd/govpn-client/udp.go new file mode 100644 index 0000000..103ed6d --- /dev/null +++ b/src/govpn/cmd/govpn-client/udp.go @@ -0,0 +1,59 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +package main + +import ( + "io" + "log" + "net" + "time" + + "govpn" +) + +func startUDP() (io.Writer, chan []byte, chan struct{}) { + remote, err := net.ResolveUDPAddr("udp", *remoteAddr) + if err != nil { + log.Fatalln("Can not resolve remote address:", err) + } + c, err := net.DialUDP("udp", nil, remote) + conn := io.Writer(c) + if err != nil { + log.Fatalln("Can not listen on UDP:", err) + } + sink := make(chan []byte) + ready := make(chan struct{}) + go func() { + buf := make([]byte, govpn.MTU) + var n int + var err error + for { + <-ready + c.SetReadDeadline(time.Now().Add(time.Second)) + n, err = c.Read(buf) + if err != nil { + sink <- nil + continue + } + sink <- buf[:n] + } + }() + ready <- struct{}{} + return conn, sink, ready +} diff --git a/src/govpn/cmd/govpn-server/main.go b/src/govpn/cmd/govpn-server/main.go index 9fb7ae1..37135b9 100644 --- a/src/govpn/cmd/govpn-server/main.go +++ b/src/govpn/cmd/govpn-server/main.go @@ -22,6 +22,7 @@ package main import ( "bytes" "flag" + "io" "log" "net" "os" @@ -34,12 +35,21 @@ import ( var ( bindAddr = flag.String("bind", "[::]:1194", "Bind to address") + 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") ) +type Pkt struct { + addr string + conn io.Writer + data []byte + ready chan struct{} +} + type PeerReadyEvent struct { peer *govpn.Peer iface string @@ -78,8 +88,8 @@ type EthEvent struct { func main() { flag.Parse() timeout := time.Second * time.Duration(govpn.TimeoutDefault) - var err error log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) + log.Println(govpn.VersionGet()) govpn.MTU = *mtu govpn.PeersInit(*peersPath) @@ -89,15 +99,18 @@ func main() { govpn.EGDInit(*egdPath) } - bind, err := net.ResolveUDPAddr("udp", *bindAddr) - if err != nil { - log.Fatalln("Can not resolve bind address:", err) + sink := make(chan Pkt) + switch *proto { + case "udp": + startUDP(sink) + case "tcp": + startTCP(sink) + case "all": + startUDP(sink) + startTCP(sink) + default: + log.Fatalln("Unknown protocol specified") } - conn, err := net.ListenUDP("udp", bind) - if err != nil { - log.Fatalln("Can listen on UDP:", err) - } - udpSink, udpBuf, udpReady := govpn.ConnListen(conn) termSignal := make(chan os.Signal, 1) signal.Notify(termSignal, os.Interrupt, os.Kill) @@ -105,7 +118,6 @@ func main() { hsHeartbeat := time.Tick(timeout) go func() { <-hsHeartbeat }() - var addr string var state *govpn.Handshake var peerState *PeerState var peer *govpn.Peer @@ -115,15 +127,13 @@ func main() { peerReadySink := make(chan PeerReadyEvent) knownPeers := govpn.KnownPeers(make(map[string]**govpn.Peer)) var peerReady PeerReadyEvent - var udpPkt govpn.UDPPkt - var udpPktData []byte + var pkt Pkt var ethEvent EthEvent var peerId *govpn.PeerId var peerConf *govpn.PeerConf var handshakeProcessForce bool ethSink := make(chan EthEvent) - log.Println(govpn.VersionGet()) log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU()) if *stats != "" { log.Println("Stats are going to listen on", *stats) @@ -133,6 +143,9 @@ func main() { } go govpn.StatsProcessor(statsPort, &knownPeers) } + if *proxy != "" { + go proxyStart(sink) + } log.Println("Server started") MainCycle: @@ -175,15 +188,14 @@ MainCycle: state.peer.Zero() break } - addr = peerReady.peer.Addr.String() state := NewPeerState(peerReady.peer, peerReady.iface) if state == nil { continue } - peers[addr] = state - knownPeers[addr] = &peerReady.peer - states[addr].Zero() - delete(states, addr) + peers[peerReady.peer.Addr] = state + knownPeers[peerReady.peer.Addr] = &peerReady.peer + states[peerReady.peer.Addr].Zero() + delete(states, peerReady.peer.Addr) log.Println("Registered interface", peerReady.iface, "with peer", peer) go func(state *PeerState) { for data := range state.sink { @@ -195,43 +207,43 @@ MainCycle: } }(state) case ethEvent = <-ethSink: - if s, exists := peers[ethEvent.peer.Addr.String()]; !exists || s.peer != ethEvent.peer { + if s, exists := peers[ethEvent.peer.Addr]; !exists || s.peer != ethEvent.peer { continue } - ethEvent.peer.EthProcess(ethEvent.data, conn, ethEvent.ready) - case udpPkt = <-udpSink: - if udpPkt.Addr == nil { - udpReady <- struct{}{} + ethEvent.peer.EthProcess(ethEvent.data, ethEvent.ready) + case pkt = <-sink: + if pkt.data == nil { + pkt.ready <- struct{}{} continue } - udpPktData = udpBuf[:udpPkt.Size] - addr = udpPkt.Addr.String() handshakeProcessForce = false HandshakeProcess: - if _, exists = peers[addr]; handshakeProcessForce || !exists { - peerId = govpn.IDsCache.Find(udpPktData) + if _, exists = peers[pkt.addr]; handshakeProcessForce || !exists { + peerId = govpn.IDsCache.Find(pkt.data) if peerId == nil { - log.Println("Unknown identity from", addr) - udpReady <- struct{}{} + log.Println("Unknown identity from", pkt.addr) + pkt.ready <- struct{}{} continue } peerConf = peerId.Conf() if peerConf == nil { log.Println("Can not get peer configuration", peerId.String()) - udpReady <- struct{}{} + pkt.ready <- struct{}{} continue } - state, exists = states[addr] + state, exists = states[pkt.addr] if !exists { - state = govpn.HandshakeNew(udpPkt.Addr, peerConf) - states[addr] = state + state = govpn.HandshakeNew(pkt.addr, pkt.conn, peerConf) + states[pkt.addr] = state } - peer = state.Server(conn, udpPktData) + peer = state.Server(pkt.data) if peer != nil { log.Println("Peer handshake finished", peer) - if _, exists = peers[addr]; exists { + if _, exists = peers[pkt.addr]; exists { go func() { - peerReadySink <- PeerReadyEvent{peer, peers[addr].tap.Name} + peerReadySink <- PeerReadyEvent{ + peer, peers[pkt.addr].tap.Name, + } }() } else { go func() { @@ -250,18 +262,18 @@ MainCycle: } } if !handshakeProcessForce { - udpReady <- struct{}{} + pkt.ready <- struct{}{} } continue } - peerState, exists = peers[addr] + peerState, exists = peers[pkt.addr] if !exists { - udpReady <- struct{}{} + pkt.ready <- struct{}{} continue } // If it fails during processing, then try to work with it // as with handshake packet - if !peerState.peer.UDPProcess(udpPktData, peerState.tap, udpReady) { + if !peerState.peer.PktProcess(pkt.data, peerState.tap, pkt.ready) { handshakeProcessForce = true goto HandshakeProcess } diff --git a/src/govpn/cmd/govpn-server/proxy.go b/src/govpn/cmd/govpn-server/proxy.go new file mode 100644 index 0000000..f1e8419 --- /dev/null +++ b/src/govpn/cmd/govpn-server/proxy.go @@ -0,0 +1,50 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +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()) +} diff --git a/src/govpn/cmd/govpn-server/tcp.go b/src/govpn/cmd/govpn-server/tcp.go new file mode 100644 index 0000000..42467d6 --- /dev/null +++ b/src/govpn/cmd/govpn-server/tcp.go @@ -0,0 +1,106 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +package main + +import ( + "encoding/binary" + "log" + "net" + + "govpn" +) + +type TCPSender struct { + conn net.Conn +} + +func (c TCPSender) Write(data []byte) (int, error) { + size := make([]byte, 2) + binary.BigEndian.PutUint16(size, uint16(len(data))) + return c.conn.Write(append(size, data...)) +} + +func startTCP(sink chan Pkt) { + bind, err := net.ResolveTCPAddr("tcp", *bindAddr) + if err != nil { + log.Fatalln("Can not resolve bind address:", err) + } + listener, err := net.ListenTCP("tcp", bind) + if err != nil { + log.Fatalln("Can not listen on TCP:", err) + } + log.Println("Listening on TCP", *bindAddr) + go func() { + for { + conn, _ := listener.AcceptTCP() + ready := make(chan struct{}, 1) + 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, + } + } +} diff --git a/src/govpn/cmd/govpn-server/udp.go b/src/govpn/cmd/govpn-server/udp.go new file mode 100644 index 0000000..c2d6e96 --- /dev/null +++ b/src/govpn/cmd/govpn-server/udp.go @@ -0,0 +1,71 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +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 . +*/ + +package main + +import ( + "log" + "net" + "time" + + "govpn" +) + +type UDPSender struct { + conn *net.UDPConn + addr *net.UDPAddr +} + +func (c UDPSender) Write(data []byte) (int, error) { + return c.conn.WriteToUDP(data, c.addr) +} + +func startUDP(sink chan Pkt) { + bind, err := net.ResolveUDPAddr("udp", *bindAddr) + ready := make(chan struct{}) + if err != nil { + log.Fatalln("Can not resolve bind address:", err) + } + lconn, err := net.ListenUDP("udp", bind) + if err != nil { + log.Fatalln("Can not listen on UDP:", err) + } + log.Println("Listening on UDP", *bindAddr) + go func() { + buf := make([]byte, govpn.MTU) + var n int + var raddr *net.UDPAddr + var err error + for { + <-ready + lconn.SetReadDeadline(time.Now().Add(time.Second)) + n, raddr, err = lconn.ReadFromUDP(buf) + if err != nil { + sink <- Pkt{ready: ready} + continue + } + sink <- Pkt{ + raddr.String(), + UDPSender{lconn, raddr}, + buf[:n], + ready, + } + } + }() + ready <- struct{}{} +} diff --git a/src/govpn/handshake.go b/src/govpn/handshake.go index 61b5b4f..3e3d0d6 100644 --- a/src/govpn/handshake.go +++ b/src/govpn/handshake.go @@ -22,8 +22,8 @@ import ( "crypto/rand" "crypto/subtle" "encoding/binary" + "io" "log" - "net" "time" "github.com/agl/ed25519" @@ -40,7 +40,8 @@ const ( ) type Handshake struct { - addr *net.UDPAddr + addr string + conn io.Writer LastPing time.Time Conf *PeerConf dsaPubH *[ed25519.PublicKeySize]byte @@ -133,9 +134,10 @@ func dhKeyGen(priv, pub *[32]byte) *[32]byte { } // Create new handshake state. -func HandshakeNew(addr *net.UDPAddr, conf *PeerConf) *Handshake { +func HandshakeNew(addr string, conn io.Writer, conf *PeerConf) *Handshake { state := Handshake{ addr: addr, + conn: conn, LastPing: time.Now(), Conf: conf, } @@ -157,11 +159,10 @@ func idTag(id *PeerId, data []byte) []byte { } // Start handshake's procedure from the client. It is the entry point -// for starting the handshake procedure. You have to specify outgoing -// conn address, remote's addr address, our own peer configuration. -// First handshake packet will be sent immediately. -func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Handshake { - state := HandshakeNew(addr, conf) +// for starting the handshake procedure. // First handshake packet +// will be sent immediately. +func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { + state := HandshakeNew(addr, conn, conf) var dhPubRepr *[32]byte state.dhPriv, dhPubRepr = dhKeypairGen() @@ -174,17 +175,16 @@ func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Hands salsa20.XORKeyStream(enc, dhPubRepr[:], state.rNonce[:], state.dsaPubH) data := append(state.rNonce[:], enc...) data = append(data, idTag(state.Conf.Id, state.rNonce[:])...) - conn.WriteToUDP(data, addr) + state.conn.Write(data) return state } // Process handshake message on the server side. // This function is intended to be called on server's side. -// Our outgoing conn connection and received data are required. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. -func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { +func (h *Handshake) Server(data []byte) *Peer { // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag if len(data) == 48 && h.rNonce == nil { // Generate DH keypair @@ -222,7 +222,7 @@ func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key) // Send that to client - conn.WriteToUDP(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...), h.addr) + h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...)) h.LastPing = time.Now() } else // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag @@ -249,11 +249,12 @@ func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { // Send final answer to client enc := make([]byte, RSize) salsa20.XORKeyStream(enc, dec[RSize:RSize+RSize], h.rNonceNext(2), h.key) - conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr) + h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) // Switch peer peer := newPeer( h.addr, + h.conn, h.Conf, 0, keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize])) @@ -267,12 +268,10 @@ func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { // Process handshake message on the client side. // This function is intended to be called on client's side. -// Our outgoing conn connection, authentication -// key and received data are required. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. -func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer { +func (h *Handshake) Client(data []byte) *Peer { switch len(data) { case 80: // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag if h.key != nil { @@ -313,7 +312,7 @@ func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer { append(h.sClient[:], sign[:]...)...)...), h.rNonceNext(1), h.key) // Send that to server - conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr) + h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) h.LastPing = time.Now() case 16: // ENC(K, R+2, RC) + IDtag if h.key == nil { @@ -330,7 +329,7 @@ func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer { } // Switch peer - peer := newPeer(h.addr, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:])) + peer := newPeer(h.addr, h.conn, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:])) h.LastPing = time.Now() return peer default: diff --git a/src/govpn/transport.go b/src/govpn/transport.go index 2f5a345..23f04dd 100644 --- a/src/govpn/transport.go +++ b/src/govpn/transport.go @@ -21,7 +21,6 @@ package govpn import ( "encoding/binary" "io" - "net" "time" "golang.org/x/crypto/poly1305" @@ -42,14 +41,10 @@ const ( TimeoutHeartbeat = 4 ) -type UDPPkt struct { - Addr *net.UDPAddr - Size int -} - type Peer struct { - Addr *net.UDPAddr + Addr string Id *PeerId + Conn io.Writer // Traffic behaviour NoiseEnable bool @@ -98,7 +93,7 @@ type Peer struct { } func (p *Peer) String() string { - return p.Id.String() + ":" + p.Addr.String() + return p.Id.String() + ":" + p.Addr } // Zero peer's memory state. @@ -184,35 +179,6 @@ func TAPListen(ifaceName string, timeout time.Duration, cpr int) (*TAP, chan []b return tap, sink, sinkReady, sinkTerminate, nil } -// Create UDP listening goroutine. -// This function takes already listening UDP socket and a buffer where -// all UDP packet data will be saved, channel where information about -// remote address and number of written bytes are stored, and a channel -// used to tell that buffer is ready to be overwritten. -func ConnListen(conn *net.UDPConn) (chan UDPPkt, []byte, chan struct{}) { - buf := make([]byte, MTU) - sink := make(chan UDPPkt) - sinkReady := make(chan struct{}) - go func(conn *net.UDPConn) { - var n int - var addr *net.UDPAddr - var err error - for { - <-sinkReady - conn.SetReadDeadline(time.Now().Add(time.Second)) - n, addr, err = conn.ReadFromUDP(buf) - if err != nil { - // This is needed for ticking the timeouts counter outside - sink <- UDPPkt{nil, 0} - continue - } - sink <- UDPPkt{addr, n} - } - }(conn) - sinkReady <- struct{}{} - return sink, buf, sinkReady -} - func newNonceCipher(key *[32]byte) *xtea.Cipher { nonceKey := make([]byte, 16) salsa20.XORKeyStream( @@ -235,7 +201,7 @@ func cprCycleCalculate(rate int) time.Duration { return time.Second / time.Duration(rate*(1<<10)/MTU) } -func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Peer { +func newPeer(addr string, conn io.Writer, conf *PeerConf, nonce int, key *[SSize]byte) *Peer { now := time.Now() timeout := conf.Timeout cprCycle := cprCycleCalculate(conf.CPR) @@ -248,6 +214,7 @@ func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Pe } peer := Peer{ Addr: addr, + Conn: conn, Timeout: timeout, Established: now, LastPing: now, @@ -270,23 +237,22 @@ func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Pe } // Process incoming UDP packet. -// udpPkt is received data, related to the peer tap interface and // ConnListen'es synchronization channel used to tell him that he is // free to receive new packets. Authenticated and decrypted packets // will be written to the interface immediately (except heartbeat ones). -func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool { - p.size = len(udpPkt) +func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool { + p.size = len(data) copy(p.buf, Emptiness) - copy(p.tag[:], udpPkt[p.size-poly1305.TagSize:]) - copy(p.buf[S20BS:], udpPkt[NonceSize:p.size-poly1305.TagSize]) + copy(p.tag[:], data[p.size-poly1305.TagSize:]) + copy(p.buf[S20BS:], data[NonceSize:p.size-poly1305.TagSize]) salsa20.XORKeyStream( p.buf[:S20BS+p.size-poly1305.TagSize], p.buf[:S20BS+p.size-poly1305.TagSize], - udpPkt[:NonceSize], + data[:NonceSize], p.Key, ) copy(p.keyAuth[:], p.buf[:SSize]) - if !poly1305.Verify(p.tag, udpPkt[:p.size-poly1305.TagSize], p.keyAuth) { + if !poly1305.Verify(p.tag, data[:p.size-poly1305.TagSize], p.keyAuth) { ready <- struct{}{} p.FramesUnauth++ return false @@ -297,7 +263,7 @@ func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) boo // Check from the oldest bucket, as in most cases this will result // in constant time check. // If Bucket0 is filled, then it becomes Bucket1. - p.NonceCipher.Decrypt(p.buf, udpPkt[:NonceSize]) + p.NonceCipher.Decrypt(p.buf, data[:NonceSize]) ready <- struct{}{} p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize]) if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound { @@ -331,25 +297,20 @@ func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) boo return true } -type WriteToUDPer interface { - WriteToUDP([]byte, *net.UDPAddr) (int, error) -} - // Process incoming Ethernet packet. -// ethPkt is received data, conn is our outgoing connection. // ready channel is TAPListen's synchronization channel used to tell him // that he is free to receive new packets. Encrypted and authenticated // packets will be sent to remote Peer side immediately. -func (p *Peer) EthProcess(ethPkt []byte, conn WriteToUDPer, ready chan struct{}) { +func (p *Peer) EthProcess(data []byte, ready chan struct{}) { p.now = time.Now() - p.size = len(ethPkt) + p.size = len(data) // If this heartbeat is necessary if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) { return } copy(p.buf, Emptiness) if p.size > 0 { - copy(p.buf[S20BS+PktSizeSize:], ethPkt) + copy(p.buf[S20BS+PktSizeSize:], data) ready <- struct{}{} binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(p.size)) p.BytesPayloadOut += int64(p.size) @@ -383,5 +344,5 @@ func (p *Peer) EthProcess(ethPkt []byte, conn WriteToUDPer, ready chan struct{}) } } p.LastSent = p.now - conn.WriteToUDP(append(p.frame, p.tag[:]...), p.Addr) + p.Conn.Write(append(p.frame, p.tag[:]...)) } diff --git a/src/govpn/transport_test.go b/src/govpn/transport_test.go index 45f4d01..5809a9f 100644 --- a/src/govpn/transport_test.go +++ b/src/govpn/transport_test.go @@ -1,7 +1,6 @@ package govpn import ( - "net" "testing" "time" ) @@ -10,16 +9,24 @@ var ( peer *Peer plaintext []byte ready chan struct{} - dummy = &Dummy{} ciphertext []byte - addr *net.UDPAddr peerId *PeerId conf *PeerConf ) +type Dummy struct{ + dst *[]byte +} + +func (d Dummy) Write(b []byte) (int, error) { + if d.dst != nil { + *d.dst = b + } + return len(b), nil +} + func init() { MTU = 1500 - addr, _ = net.ResolveUDPAddr("udp", "[::1]:1") peerId, _ = IDDecode("ffffffffffffffffffffffffffffffff") conf = &PeerConf{ Id: peerId, @@ -27,7 +34,7 @@ func init() { NoiseEnable: false, CPR: 0, } - peer = newPeer(addr, conf, 128, new([SSize]byte)) + peer = newPeer("foo", Dummy{&ciphertext}, conf, 128, new([SSize]byte)) plaintext = make([]byte, 789) ready = make(chan struct{}) go func() { @@ -37,33 +44,22 @@ func init() { }() } -type Dummy struct{} - -func (d *Dummy) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { - ciphertext = b - return len(b), nil -} - -func (d *Dummy) Write(p []byte) (n int, err error) { - return len(p), nil -} - func BenchmarkEnc(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { peer.NonceOur = 128 - peer.EthProcess(plaintext, dummy, ready) + peer.EthProcess(plaintext, ready) } } func BenchmarkDec(b *testing.B) { - peer.EthProcess(plaintext, dummy, ready) - peer = newPeer(addr, conf, 128, new([SSize]byte)) + peer.EthProcess(plaintext, ready) + peer = newPeer("foo", Dummy{nil}, conf, 128, new([SSize]byte)) b.ResetTimer() for i := 0; i < b.N; i++ { peer.nonceBucket0 = make(map[uint64]struct{}, 1) peer.nonceBucket1 = make(map[uint64]struct{}, 1) - if !peer.UDPProcess(ciphertext, dummy, ready) { + if !peer.PktProcess(ciphertext, Dummy{nil}, ready) { b.Fail() } } diff --git a/utils/makedist.sh b/utils/makedist.sh index e408142..00ba9ac 100755 --- a/utils/makedist.sh +++ b/utils/makedist.sh @@ -6,8 +6,14 @@ release=$1 [ -n "$release" ] git clone . $tmp/govpn-$release +for repo in src/github.com/bigeagle/water src/github.com/agl/ed25519 src/golang.org/x/crypto; do + git clone $repo $tmp/govpn-$release/$repo +done +cd $tmp/govpn-$release +git checkout $release +git submodule update --init + cat > $tmp/includes < doc/download.texi <