.gitmodules | 7 ++++--- NEWS | 1 - NEWS.RU | 1 - THANKS | 12 +++++------- VERSION | 2 +- doc/download.texi | 14 ++++++++++++++ doc/index.texi | 2 +- doc/installation.texi | 16 +++------------- doc/integrity.texi | 2 +- doc/news.ru.texi | 31 +++++++++++++++++-------------- doc/news.texi | 28 +++++++++++++++------------- doc/sources.texi | 4 ++-- doc/thanks.texi | 12 ++++-------- src/cypherpunks.ru/govpn/aont/aont_test.go | 4 ++-- src/cypherpunks.ru/govpn/aont/oaep.go | 4 ++-- src/cypherpunks.ru/govpn/client/client.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/govpn/client/tcp.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/govpn/client/udp.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/govpn/cmd/govpn-client/main.go | 180 ++++++++++++++++++++++------------------------------- src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go => src/cypherpunks.ru/govpn/client/proxy.go | 31 +++++++++++++++++-------------- src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go | 150 ----------------------------------------------------- src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go | 106 ----------------------------------------------------- src/cypherpunks.ru/govpn/cmd/govpn-server/common.go | 25 +++++++++++++++---------- src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go | 14 +++++++------- src/cypherpunks.ru/govpn/cmd/govpn-server/main.go | 12 ++++++------ src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go | 2 +- src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go | 40 ++++++++++++++++++++-------------------- src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go | 129 ++++++++++++++++++++++++++--------------------------- src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go | 4 ++-- src/cypherpunks.ru/govpn/cnw/cnw.go | 2 +- src/cypherpunks.ru/govpn/cnw/cnw_test.go | 4 ++-- src/cypherpunks.ru/govpn/common.go | 2 +- src/cypherpunks.ru/govpn/conf.go | 4 ++-- src/cypherpunks.ru/govpn/egd.go | 4 ++-- src/cypherpunks.ru/govpn/encless.go | 2 +- src/cypherpunks.ru/govpn/encless_test.go | 4 ++-- src/cypherpunks.ru/govpn/handshake.go | 20 ++++++++++---------- src/cypherpunks.ru/govpn/handshake_test.go | 8 ++++---- src/cypherpunks.ru/govpn/identity.go | 25 +++++++++++++------------ src/cypherpunks.ru/govpn/logger.go | 2 +- src/cypherpunks.ru/govpn/peer.go | 14 ++++++++------ src/cypherpunks.ru/govpn/peer_test.go | 8 ++++---- src/cypherpunks.ru/govpn/stats.go | 2 +- src/cypherpunks.ru/govpn/tap.go | 2 +- src/cypherpunks.ru/govpn/tap_freebsd.go | 2 +- src/cypherpunks.ru/govpn/tap_linux.go | 2 +- src/cypherpunks.ru/govpn/verifier.go | 16 ++++++++-------- utils/makedist.sh | 8 +++++--- utils/news.sh | 33 +++++++++++++++++++++++++++++++++ diff --git a/.gitmodules b/.gitmodules index 82df1f7e8a594fcda48fccdf6d3c937b356e89fb..c837a039e17a5ea45e75cc416ebcd3cbe97f1dda 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,10 @@ 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 -[submodule "src/github.com/go-yaml/yaml"] - path = src/github.com/go-yaml/yaml - url = https://github.com/go-yaml/yaml.git [submodule "src/cypherpunks.ru/balloon"] path = src/cypherpunks.ru/balloon url = git://git.cypherpunks.ru/balloon.git +[submodule "src/gopkg.in/yaml.v2"] + path = src/gopkg.in/yaml.v2 + url = https://github.com/go-yaml/yaml.git + branch = v2 diff --git a/NEWS b/NEWS deleted file mode 120000 index 8be52e420232a063e15ad94bdb5c9e3575056fe7..0000000000000000000000000000000000000000 --- a/NEWS +++ /dev/null @@ -1 +0,0 @@ -doc/news.texi \ No newline at end of file diff --git a/NEWS.RU b/NEWS.RU deleted file mode 120000 index 85f9daf175ebac5a755ebba947addfb79b4e284e..0000000000000000000000000000000000000000 --- a/NEWS.RU +++ /dev/null @@ -1 +0,0 @@ -doc/news.ru.texi \ No newline at end of file diff --git a/THANKS b/THANKS index 38cc203cdb77d4fe31e2ef0e983142e8b4dd26e8..a7fe0af928a49fd277d4e7705927aa906c011744 100644 --- a/THANKS +++ b/THANKS @@ -1,7 +1,5 @@ -* Applied Cryptography (https://www.schneier.com/books/applied_cryptography/) (C) 1996 Bruce Schneier. -* Strong Password-Only Authenticated Key Exchange (http://tnlandforms.us/cns05/speke.pdf) (C) 1996 David P. Jablon. -* Augmented Encrypted Key Exchange (https://www.cs.columbia.edu/~smb/papers/aeke.pdf): a Password-Based Protocol Secure Against Dictionary Attacks and Password File Compromise (C) Steven M. Belloving, Michael Merrit. -* Watson Ladd for suggestion of Elligator (http://elligator.cr.yp.to/) encoding. -* Password Hashing Competition for Argon2 (https://password-hashing.net/#argon2). -* Chaffing and Winnowing: Confidentiality without Encryption (http://people.csail.mit.edu/rivest/chaffing-980701.txt) (C) Ronald L. Rivest -* Zhuoyun Wei for AUR port maintaining and his documentation related fixes. +* Watson Ladd for suggestion of Elligator encoding. +* Zhuoyun Wei for AUR port maintaining and + his documentation related fixes. +* Bruno Clermont for finding serious bugs + and code refactoring diff --git a/VERSION b/VERSION index 4fedf1d20e15761409d6e4e3bf99c0beb499fdfe..0f0fefae5ac96803e7227191df7a6974709eb45d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0 +7.1 diff --git a/doc/download.texi b/doc/download.texi index 31977c66c2bea397f712c3aabd18b5118f1ad55a..60ac685c47490e505c9cacb845f514dad49ea58d 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -5,8 +5,22 @@ You can obtain releases source code prepared tarballs from the links below (or use @url{https://sourceforge.net/projects/govpn/files/, Sourceforge mirror}). Do not forget to check tarball @ref{Integrity, integrity}. +Tarballs include all necessary required libraries: + +@multitable @columnfractions .40 .20 .40 +@headitem Library @tab Platform @tab Licence +@item @code{github.com/agl/ed25519} @tab All @tab BSD 3-Clause +@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause +@item @code{github.com/go-yaml/yaml} @tab All @tab Apache License 2.0 and MIT +@item @code{golang.org/x/crypto} @tab All @tab BSD 3-Clause +@end multitable + @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Version @tab Size @tab Tarball @tab SHA256 checksum + +@item @ref{Release 7.0, 7.0} @tab 287 KiB +@tab @url{download/govpn-7.0.tar.xz, link} @url{download/govpn-7.0.tar.xz.sig, sign} +@tab @code{DF85E42D F1228E8C FA5D582D AEF90553 23033630 E12B0B26 D7DEFBEB B25DBC4C} @item @ref{Release 6.0, 6.0} @tab 310 KiB @tab @url{download/govpn-6.0.tar.xz, link} @url{download/govpn-6.0.tar.xz.sig, sign} diff --git a/doc/index.texi b/doc/index.texi index b962eacf8d83d27661cd3e1e4c0a67c00d6e125a..624abba1fc673dcfcf1708086573e5a026f6811f 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -7,7 +7,7 @@ This manual is for GoVPN -- simple free software virtual private network daemon, aimed to be reviewable, secure, DPI/censorship-resistant, written on Go. -Copyright @copyright{} 2014-2016 @email{stargrave@@stargrave.org, Sergey Matveev} +Copyright @copyright{} 2014-2017 @email{stargrave@@stargrave.org, Sergey Matveev} @quotation Permission is granted to copy, distribute and/or modify this document diff --git a/doc/installation.texi b/doc/installation.texi index 1443d7f95c8f752d3fd5e5d57a2b1694f9c956bb..9abc8229d6a215403f6ef11e9d0fe16ce38f4cc8 100644 --- a/doc/installation.texi +++ b/doc/installation.texi @@ -18,17 +18,7 @@ and GNU ones are fine) is recommended for convenient building. @url{https://www.gnu.org/software/texinfo/, Texinfo} (6.1+ version is recommended) is used for building documentation. Possibly you also need to install TUN/TAP interface utilities (depending on your operating -system): @code{uml-utilities} package in most GNU/Linux distributions. - -Included required libraries: - -@multitable @columnfractions .40 .20 .40 -@headitem Library @tab Platform @tab Licence -@item @code{github.com/agl/ed25519} @tab All @tab BSD 3-Clause -@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause -@item @code{github.com/go-yaml/yaml} @tab All @tab LGPLv3 and MIT -@item @code{golang.org/x/crypto} @tab All @tab BSD 3-Clause -@end multitable +system): @command{uml-utilities} package in most GNU/Linux distributions. Get @ref{Tarballs, the tarball}, check its @ref{Integrity, integrity and authenticity} and run @command{make}. @@ -43,8 +33,8 @@ % tar xf govpn-2.3.tar.xz % make -C govpn-2.3 all @end verbatim -There is @code{install} target respecting @env{DESTDIR}. It will -install binaries, info-documentation and utilities. +There is @command{install} target respecting @env{DESTDIR}. +It will install binaries, info-documentation and utilities. @menu * Prepared tarballs: Tarballs. diff --git a/doc/integrity.texi b/doc/integrity.texi index b9bc1a1e60910a76ae8675f65e5176ca6e5635b5..5fda02f4ee259939ae6b7bc8f75aba38bc548546 100644 --- a/doc/integrity.texi +++ b/doc/integrity.texi @@ -1,7 +1,7 @@ @node Integrity @section Tarballs integrity check -You @strong{have to} verify downloaded archives integrity and check +You @strong{have to} check downloaded archives integrity and verify their signature to be sure that you have got trusted, untampered software. For integrity and authentication of downloaded binaries @url{https://www.gnupg.org/, The GNU Privacy Guard} is used. You must diff --git a/doc/news.ru.texi b/doc/news.ru.texi index 81c3139d90748b163fe4a5e731bf1df1202c0d56..9f7ebf05438a07d4e20052d4e14cc2fdb4a14700 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -1,6 +1,15 @@ @node Новости @section Новости +@node Релиз 7.1 +@subsection Релиз 7.1 +@itemize +@item Исправлена ошибка в коде генерирования и определения идентификации +клиента: параллельно работающие клиенты могут быть неправильно +определены, не давая возможности их подсоединения и позволяя DPI +выявлять GoVPN пакеты. +@end itemize + @node Релиз 7.0 @subsection Релиз 7.0 @itemize @@ -75,8 +84,8 @@ @node Релиз 5.4 @subsection Релиз 5.4 @itemize -@item Добавлена возможность требования @ref{Timesync, синхронизации -времени}. Она добавит временные метки в PRP аутентификацию сообщений +@item Добавлена возможность требования синхронизации +времени. Она добавит временные метки в PRP аутентификацию сообщений рукопожатия, не позволяя повторить ранее перехваченные пакеты и получить ответ от сервера, делая его видимым для DPI. @end itemize @@ -111,15 +120,15 @@ @node Релиз 5.0 @subsection Релиз 5.0 @itemize -@item Новый опциональный @ref{Encless, нешифрованный режим} работы. +@item Новый опциональный нешифрованный режим работы. Технически к исходящим пакетам не применяются функции шифрования, поэтому вас не могут вынудить выдать ключи шифрования или привлечь к ответственности за использования шифрования. -@item @ref{MTU} конфигурируются относительно каждого пользователя отдельно. +@item MTU конфигурируются относительно каждого пользователя отдельно. @item Упрощённая схема дополнения полезной нагрузки, экономия в один байт. @item Возможность указать название TAP интерфейса явно, без использования up-скриптов. -@item @command{govpn-verifier} утилита теперь может использовать @ref{EGD}. +@item @command{govpn-verifier} утилита теперь может использовать EGD. @end itemize @node Релиз 4.2 @@ -152,9 +161,9 @@ @node Релиз 3.5 @subsection Релиз 3.5 @itemize -@item Возможность использовать @ref{Network, TCP} сетевой транспорт. +@item Возможность использовать TCP сетевой транспорт. Сервер можно слушать одновременно на UDP и TCP сокетах. -@item Возможность использовать @ref{Proxy, HTTP прокси} (через CONNECT +@item Возможность использовать HTTP прокси (через CONNECT метод) для доступа к серверу. Сервер может эмулировать поведение HTTP прокси. @item Обновлённая Poly1305 библиотека с исправлениями для ARM. @@ -165,7 +174,7 @@ @node Релиз 3.4 @subsection Релиз 3.4 @itemize -@item Возможность использовать внешний @ref{EGD} совместимый PRNG. +@item Возможность использовать внешний EGD совместимый PRNG. Теперь вы можете использовать GoVPN даже на системах с плохим @file{/dev/random}. @item Опция @option{-noncediff} удалена. Она заменена на хранилище уже @@ -303,9 +312,3 @@ @subsection Релиз 1.1 @itemize @item Поддержка FreeBSD. @end itemize - -@node Релиз 1.0 -@subsection Релиз 1.0 -@itemize -@item Первый стабильный релиз. -@end itemize diff --git a/doc/news.texi b/doc/news.texi index 5a6db8f5113d93524668df0fae901f1c9ff612d7..21104e0006b478d5f6be8ea13aa67e80bc0ad741 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,6 +3,14 @@ @unnumbered News See also this page @ref{Новости, on russian}. +@node Release 7.1 +@section Release 7.1 +@itemize +@item Fixed bug in client's identity generation and detection code: +simultaneous clients may be incorrectly identified, preventing their +connection establishing and allowing DPI to detect GoVPN packets. +@end itemize + @node Release 7.0 @section Release 7.0 @itemize @@ -74,7 +82,7 @@ @node Release 5.4 @section Release 5.4 @itemize -@item Added optional @ref{Timesync, time synchronization} requirement. +@item Added optional time synchronization requirement. It will add timestamps in handshake PRP authentication, disallowing to repeat captured packet and get reply from the server, making it visible to DPI. @@ -107,15 +115,15 @@ @node Release 5.0 @section Release 5.0 @itemize -@item New optional @ref{Encless, encryptionless mode} of operation. +@item New optional encryptionless mode of operation. Technically no encryption functions are applied for outgoing packets, so you can not be forced to reveal your encryption keys or sued for encryption usage. -@item @ref{MTU}s are configured on per-user basis. +@item MTUs are configured on per-user basis. @item Simplified payload padding scheme, saving one byte of data. @item Ability to specify TAP interface name explicitly without any up-scripts for convenience. -@item @command{govpn-verifier} utility also can use @ref{EGD}. +@item @command{govpn-verifier} utility also can use EGD. @end itemize @node Release 4.2 @@ -147,9 +155,9 @@ @node Release 3.5 @section Release 3.5 @itemize -@item Ability to use @ref{Network, TCP} network transport. +@item Ability to use TCP network transport. Server can listen on both UDP and TCP sockets. -@item Ability to use @ref{Proxy, HTTP proxies} (through CONNECT method) +@item Ability to use HTTP proxies (through CONNECT method) for accessing the server. Server can also emulate HTTP proxy behaviour. @item Updated Poly1305 library with ARM-related bugfixes. @item Go 1.5+ version is highly recommended because of performance @@ -159,7 +167,7 @@ @node Release 3.4 @section Release 3.4 @itemize -@item Ability to use external @ref{EGD}-compatible PRNGs. Now you are +@item Ability to use external EGD-compatible PRNGs. Now you are able to use GoVPN even on systems with the bad @file{/dev/random}, providing higher quality entropy from external sources. @item Removed @option{-noncediff} option. It is replaced with in-memory @@ -298,9 +306,3 @@ @section Release 1.1 @itemize @item FreeBSD support. @end itemize - -@node Release 1.0 -@section Release 1.0 -@itemize -@item Initial stable release. -@end itemize diff --git a/doc/sources.texi b/doc/sources.texi index e7a13755b3fc8d9b44911ec4965e651fdbe66428..f3864ef47fef546637e4611d8133c097b0e08331 100644 --- a/doc/sources.texi +++ b/doc/sources.texi @@ -3,8 +3,8 @@ @section Development source code Development source code contains the latest version of the code. It may be buggy. It does not contain compiled documentation and dependent -libraries source code. Because of that, it is not recommended for -porters: use @ref{Tarballs} instead. +libraries source code. Because of that, it is recommended for porters +to use @ref{Tarballs, tarballs} instead. You can obtain it by cloning @url{http://git-scm.com/, Git} @url{http://git.cypherpunks.ru/cgit.cgi/govpn.git/log/, repository} diff --git a/doc/thanks.texi b/doc/thanks.texi index 0a6757cf6069dfecaeaaf5987a8586353705313d..b0426dd907695bb41b3e1e8fcec2fc5917433c99 100644 --- a/doc/thanks.texi +++ b/doc/thanks.texi @@ -4,12 +4,8 @@ Thanks for contributions and suggestions to: @itemize -@item @url{https://www.schneier.com/books/applied_cryptography/, Applied Cryptography} @copyright{} 1996 Bruce Schneier. -@item @url{http://tnlandforms.us/cns05/speke.pdf, Strong Password-Only Authenticated Key Exchange} @copyright{} 1996 David P. Jablon. -@item @url{https://www.cs.columbia.edu/~smb/papers/aeke.pdf, Augmented Encrypted Key Exchange}: a Password-Based Protocol Secure Against Dictionary Attacks and Password File Compromise @copyright{} Steven M. Belloving, Michael Merrit. -@item @email{watsonbladd@@gmail.com, Watson Ladd} for suggestion of @url{http://elligator.cr.yp.to/, Elligator} encoding. -@item @url{https://password-hashing.net/#argon2, Password Hashing Competition for Argon2}. -@item @url{https://crypto.stanford.edu/balloon/, Balloon hashing}. -@item @url{http://people.csail.mit.edu/rivest/chaffing-980701.txt, Chaffing and Winnowing: Confidentiality without Encryption} @copyright{} Ronald L. Rivest -@item @email{wzyboy@@wzyboy.org, Zhuoyun Wei} for @url{https://aur.archlinux.org/packages/govpn/, AUR} port maintaining and his documentation related fixes. +@item Watson Ladd for suggestion of @url{http://elligator.cr.yp.to/, Elligator} encoding. +@item Zhuoyun Wei for @url{https://aur.archlinux.org/packages/govpn/, AUR} port +maintaining and his documentation related fixes. +@item Bruno Clermont for finding serious bugs and code refactoring. @end itemize diff --git a/src/cypherpunks.ru/govpn/aont/aont_test.go b/src/cypherpunks.ru/govpn/aont/aont_test.go index 0ef140c2c407726531a21b951875a4ab0b2b5aa5..b586775bdd8650cf1e0105b6db58f11a2ca02499 100644 --- a/src/cypherpunks.ru/govpn/aont/aont_test.go +++ b/src/cypherpunks.ru/govpn/aont/aont_test.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -27,7 +27,7 @@ "testing/quick" ) var ( - testKey *[16]byte = new([16]byte) + testKey = new([16]byte) ) func init() { diff --git a/src/cypherpunks.ru/govpn/aont/oaep.go b/src/cypherpunks.ru/govpn/aont/oaep.go index 4b780ba427ed2e6f87e8d5724ed2488b4f8e8f17..b648ccb7e26695f2fab2581d2844c1684eb034b7 100644 --- a/src/cypherpunks.ru/govpn/aont/oaep.go +++ b/src/cypherpunks.ru/govpn/aont/oaep.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -48,7 +48,7 @@ RSize = 16 ) var ( - dummyNonce *[16]byte = new([16]byte) + dummyNonce = new([16]byte) ) // Encode the data, produce AONT package. Data size will be larger than diff --git a/src/cypherpunks.ru/govpn/client/client.go b/src/cypherpunks.ru/govpn/client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..8102cc6cdf1430217a27052526496b8c8af446a4 --- /dev/null +++ b/src/cypherpunks.ru/govpn/client/client.go @@ -0,0 +1,160 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2017 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 client + +import ( + "errors" + "fmt" + "net" + "os" + "time" + + "github.com/agl/ed25519" + + "cypherpunks.ru/govpn" +) + +type Protocol int + +const ( + ProtocolUDP Protocol = iota + ProtocolTCP +) + +type Configuration struct { + PrivateKey *[ed25519.PrivateKeySize]byte + Peer *govpn.PeerConf + Protocol Protocol + InterfaceName string + ProxyAddress string + ProxyAuthentication string + RemoteAddress string + UpPath string + DownPath string + StatsAddress string + NoReconnect bool + MTU int +} + +func (c *Configuration) Validate() error { + if c.MTU > govpn.MTUMax { + return fmt.Errorf("Invalid MTU %d, maximum allowable is %d", c.MTU, govpn.MTUMax) + } + if len(c.RemoteAddress) == 0 { + return errors.New("Missing RemoteAddress") + } + if len(c.InterfaceName) == 0 { + return errors.New("Missing InterfaceName") + } + return nil +} + +func (c *Configuration) isProxy() bool { + return len(c.ProxyAddress) > 0 +} + +type Client struct { + idsCache *govpn.MACCache + tap *govpn.TAP + knownPeers govpn.KnownPeers + statsPort net.Listener + timeouted chan struct{} + rehandshaking chan struct{} + termination chan struct{} + firstUpCall bool + termSignal chan os.Signal + config Configuration + + // Error channel receives any kind of routine errors + Error chan error +} + +func (c *Client) MainCycle() { + var err error + c.tap, err = govpn.TAPListen(c.config.InterfaceName, c.config.MTU) + if err != nil { + c.Error <- fmt.Errorf("Can not listen on TUN/TAP interface: %s", err.Error()) + return + } + + if len(c.config.StatsAddress) > 0 { + c.statsPort, err = net.Listen("tcp", c.config.StatsAddress) + if err != nil { + c.Error <- fmt.Errorf("Can't listen on stats port: %s", err.Error()) + return + } + c.knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer)) + go govpn.StatsProcessor(c.statsPort, &c.knownPeers) + } + +MainCycle: + for { + c.timeouted = make(chan struct{}) + c.rehandshaking = make(chan struct{}) + c.termination = make(chan struct{}) + switch c.config.Protocol { + case ProtocolUDP: + go c.startUDP() + case ProtocolTCP: + if c.config.isProxy() { + go c.proxyTCP() + } else { + go c.startTCP() + } + } + select { + case <-c.termSignal: + govpn.BothPrintf(`[finish remote="%s"]`, c.config.RemoteAddress) + c.termination <- struct{}{} + // empty value signals that everything is fine + c.Error <- nil + break MainCycle + case <-c.timeouted: + if c.config.NoReconnect { + break MainCycle + } + govpn.BothPrintf(`[sleep seconds="%d"]`, c.config.Peer.Timeout/time.Second) + time.Sleep(c.config.Peer.Timeout) + case <-c.rehandshaking: + } + close(c.timeouted) + close(c.rehandshaking) + close(c.termination) + } + if _, err = govpn.ScriptCall( + c.config.DownPath, + c.config.InterfaceName, + c.config.RemoteAddress, + ); err != nil { + c.Error <- err + } +} + +func NewClient(conf Configuration, verifier *govpn.Verifier, termSignal chan os.Signal) *Client { + client := Client{ + idsCache: govpn.NewMACCache(), + firstUpCall: true, + config: conf, + termSignal: termSignal, + Error: make(chan error, 1), + } + confs := map[govpn.PeerID]*govpn.PeerConf{*verifier.ID: conf.Peer} + client.idsCache.Update(&confs) + return &client +} diff --git a/src/cypherpunks.ru/govpn/client/tcp.go b/src/cypherpunks.ru/govpn/client/tcp.go new file mode 100644 index 0000000000000000000000000000000000000000..40d81ca818081789c3a9f87ac4e4a2f063c63a76 --- /dev/null +++ b/src/cypherpunks.ru/govpn/client/tcp.go @@ -0,0 +1,160 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2017 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 client + +import ( + "bytes" + "fmt" + "net" + "sync/atomic" + "time" + + "cypherpunks.ru/govpn" +) + +func (c *Client) startTCP() { + remote, err := net.ResolveTCPAddr("tcp", c.config.RemoteAddress) + if err != nil { + c.Error <- fmt.Errorf("Can not resolve remote address: %s", err) + return + } + conn, err := net.DialTCP("tcp", nil, remote) + if err != nil { + c.Error <- fmt.Errorf("Can not connect to address: %s", err) + return + } + govpn.Printf(`[connected remote="%s"]`, c.config.RemoteAddress) + c.handleTCP(conn) +} + +func (c *Client) handleTCP(conn *net.TCPConn) { + hs := govpn.HandshakeStart(c.config.RemoteAddress, conn, c.config.Peer) + buf := make([]byte, 2*(govpn.EnclessEnlargeSize+c.config.MTU)+c.config.MTU) + var n int + var err error + var prev int + var peer *govpn.Peer + var terminator chan struct{} +HandshakeCycle: + for { + select { + case <-c.termination: + break HandshakeCycle + default: + } + if prev == len(buf) { + govpn.Printf(`[packet-timeouted remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break HandshakeCycle + } + + if err = conn.SetReadDeadline(time.Now().Add(c.config.Peer.Timeout)); err != nil { + c.Error <- err + break HandshakeCycle + } + n, err = conn.Read(buf[prev:]) + if err != nil { + govpn.Printf(`[connection-timeouted remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break HandshakeCycle + } + + prev += n + peerID := c.idsCache.Find(buf[:prev]) + if peerID == nil { + continue + } + peer = hs.Client(buf[:prev]) + prev = 0 + if peer == nil { + continue + } + govpn.Printf(`[handshake-completed remote="%s"]`, c.config.RemoteAddress) + c.knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{c.config.RemoteAddress: &peer}) + if c.firstUpCall { + go govpn.ScriptCall(c.config.UpPath, c.config.InterfaceName, c.config.RemoteAddress) + c.firstUpCall = false + } + hs.Zero() + terminator = make(chan struct{}) + go govpn.PeerTapProcessor(peer, c.tap, terminator) + break HandshakeCycle + } + if hs != nil { + hs.Zero() + } + if peer == nil { + return + } + + prev = 0 + var i int +TransportCycle: + for { + select { + case <-c.termination: + break TransportCycle + default: + } + if prev == len(buf) { + govpn.Printf(`[packet-timeouted remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break TransportCycle + } + if err = conn.SetReadDeadline(time.Now().Add(c.config.Peer.Timeout)); err != nil { + c.Error <- err + break TransportCycle + } + n, err = conn.Read(buf[prev:]) + if err != nil { + govpn.Printf(`[connection-timeouted remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break TransportCycle + } + prev += n + CheckMore: + if prev < govpn.MinPktLength { + continue + } + i = bytes.Index(buf[:prev], peer.NonceExpect) + if i == -1 { + continue + } + if !peer.PktProcess(buf[:i+govpn.NonceSize], c.tap, false) { + govpn.Printf(`[packet-unauthenticated remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break TransportCycle + } + if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey { + govpn.Printf(`[rehandshake-required remote="%s"]`, c.config.RemoteAddress) + c.rehandshaking <- struct{}{} + break TransportCycle + } + copy(buf, buf[i+govpn.NonceSize:prev]) + prev = prev - i - govpn.NonceSize + goto CheckMore + } + if terminator != nil { + terminator <- struct{}{} + } + peer.Zero() + if err = conn.Close(); err != nil { + c.Error <- err + } +} diff --git a/src/cypherpunks.ru/govpn/client/udp.go b/src/cypherpunks.ru/govpn/client/udp.go new file mode 100644 index 0000000000000000000000000000000000000000..bb7045ac711778f25e8a9f8d839dea3ae9f11d92 --- /dev/null +++ b/src/cypherpunks.ru/govpn/client/udp.go @@ -0,0 +1,114 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2017 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 client + +import ( + "fmt" + "net" + "sync/atomic" + "time" + + "cypherpunks.ru/govpn" +) + +func (c *Client) startUDP() { + remote, err := net.ResolveUDPAddr("udp", c.config.RemoteAddress) + if err != nil { + c.Error <- fmt.Errorf("Can not resolve remote address: %s", err) + return + } + conn, err := net.DialUDP("udp", nil, remote) + if err != nil { + c.Error <- fmt.Errorf("Can not connect remote address: %s", err) + return + } + govpn.Printf(`[connected remote="%s"]`, c.config.RemoteAddress) + + hs := govpn.HandshakeStart(c.config.RemoteAddress, conn, c.config.Peer) + buf := make([]byte, c.config.MTU*2) + var n int + var timeouts int + var peer *govpn.Peer + var terminator chan struct{} + timeout := int(c.config.Peer.Timeout.Seconds()) +MainCycle: + for { + select { + case <-c.termination: + break MainCycle + default: + } + + if err = conn.SetReadDeadline(time.Now().Add(time.Second)); err != nil { + c.Error <- err + break MainCycle + } + n, err = conn.Read(buf) + if timeouts == timeout { + govpn.Printf(`[connection-timeouted remote="%s"]`, c.config.RemoteAddress) + c.timeouted <- struct{}{} + break + } + if err != nil { + timeouts++ + continue + } + if peer != nil { + if peer.PktProcess(buf[:n], c.tap, true) { + timeouts = 0 + } else { + govpn.Printf(`[packet-unauthenticated remote="%s"]`, c.config.RemoteAddress) + timeouts++ + } + if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey { + govpn.Printf(`[rehandshake-required remote="%s"]`, c.config.RemoteAddress) + c.rehandshaking <- struct{}{} + break MainCycle + } + continue + } + if c.idsCache.Find(buf[:n]) == nil { + govpn.Printf(`[identity-invalid remote="%s"]`, c.config.RemoteAddress) + continue + } + timeouts = 0 + peer = hs.Client(buf[:n]) + if peer == nil { + continue + } + govpn.Printf(`[handshake-completed remote="%s"]`, c.config.RemoteAddress) + c.knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{c.config.RemoteAddress: &peer}) + if c.firstUpCall { + go govpn.ScriptCall(c.config.UpPath, c.config.InterfaceName, c.config.RemoteAddress) + c.firstUpCall = false + } + hs.Zero() + terminator = make(chan struct{}) + go govpn.PeerTapProcessor(peer, c.tap, terminator) + } + if terminator != nil { + terminator <- struct{}{} + } + if hs != nil { + hs.Zero() + } + if err = conn.Close(); err != nil { + c.Error <- err + } +} diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go index 36ff72a113e6ac0db3e809e90af6954fb57f9f6c..8fcd988ecc8c6d1bb8bf3a688482df4751fa8c28 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -23,46 +23,41 @@ import ( "flag" "fmt" "log" - "net" "os" "os/signal" "time" "cypherpunks.ru/govpn" -) - -var ( - remoteAddr = flag.String("remote", "", "Remote server address") - proto = flag.String("proto", "udp", "Protocol to use: udp or tcp") - ifaceName = flag.String("iface", "tap0", "TUN/TAP network interface") - verifierRaw = flag.String("verifier", "", "Verifier") - 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", govpn.MTUDefault, "MTU of TUN/TAP interface") - timeoutP = flag.Int("timeout", 60, "Timeout seconds") - timeSync = flag.Int("timesync", 0, "Time synchronization requirement") - noreconnect = flag.Bool("noreconnect", false, "Disable reconnection after timeout") - noisy = flag.Bool("noise", false, "Enable noise appending") - encless = flag.Bool("encless", false, "Encryptionless mode") - cpr = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate") - egdPath = flag.String("egd", "", "Optional path to EGD socket") - syslog = flag.Bool("syslog", false, "Enable logging to syslog") - version = flag.Bool("version", false, "Print version information") - warranty = flag.Bool("warranty", false, "Print warranty information") - - conf *govpn.PeerConf - tap *govpn.TAP - timeout int - firstUpCall bool = true - knownPeers govpn.KnownPeers - idsCache *govpn.MACCache + "cypherpunks.ru/govpn/client" ) func main() { + var ( + remoteAddr = flag.String("remote", "", "Remote server address") + proto = flag.String("proto", "udp", "Protocol to use: udp or tcp") + ifaceName = flag.String("iface", "tap0", "TUN/TAP network interface") + verifierRaw = flag.String("verifier", "", "Verifier") + 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", govpn.MTUDefault, "MTU of TUN/TAP interface") + timeoutP = flag.Int("timeout", 60, "Timeout seconds") + timeSync = flag.Int("timesync", 0, "Time synchronization requirement") + noreconnect = flag.Bool("noreconnect", false, "Disable reconnection after timeout") + noisy = flag.Bool("noise", false, "Enable noise appending") + encless = flag.Bool("encless", false, "Encryptionless mode") + cpr = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate") + egdPath = flag.String("egd", "", "Optional path to EGD socket") + syslog = flag.Bool("syslog", false, "Enable logging to syslog") + version = flag.Bool("version", false, "Print version information") + warranty = flag.Bool("warranty", false, "Print warranty information") + protocol client.Protocol + err error + ) + flag.Parse() if *warranty { fmt.Println(govpn.Warranty) @@ -72,72 +67,74 @@ if *version { fmt.Println(govpn.VersionGet()) return } - timeout = *timeoutP - var err error log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) - if *mtu > govpn.MTUMax { - log.Fatalln("Maximum allowable MTU is", govpn.MTUMax) - } if *egdPath != "" { log.Println("Using", *egdPath, "EGD") govpn.EGDInit(*egdPath) } - if *proxyAddr != "" { - *proto = "tcp" - } - if !(*proto == "udp" || *proto == "tcp") { + switch *proto { + case "udp": + protocol = client.ProtocolUDP + case "tcp": + protocol = client.ProtocolTCP + default: log.Fatalln("Unknown protocol specified") } + + if *proxyAddr != "" && protocol == client.ProtocolUDP { + log.Fatalln("HTTP proxy is supported only in TCP mode") + } + if *verifierRaw == "" { - log.Fatalln("No verifier specified") + log.Fatalln("-verifier is required") } verifier, err := govpn.VerifierFromString(*verifierRaw) if err != nil { - log.Fatalln(err) + log.Fatalln("Invalid -verifier:", err) } key, err := govpn.KeyRead(*keyPath) if err != nil { - log.Fatalln("Unable to read the key", err) + log.Fatalln("Invalid -key:", err) } priv := verifier.PasswordApply(key) if *encless { - if *proto != "tcp" { + if protocol != client.ProtocolTCP { log.Fatalln("Currently encryptionless mode works only with TCP") } *noisy = true } - conf = &govpn.PeerConf{ - Id: verifier.Id, - Iface: *ifaceName, - MTU: *mtu, - Timeout: time.Second * time.Duration(timeout), - TimeSync: *timeSync, - Noise: *noisy, - CPR: *cpr, - Encless: *encless, - Verifier: verifier, - DSAPriv: priv, + conf := client.Configuration{ + PrivateKey: priv, + Peer: &govpn.PeerConf{ + ID: verifier.ID, + Iface: *ifaceName, + MTU: *mtu, + Timeout: time.Second * time.Duration(*timeoutP), + TimeSync: *timeSync, + Noise: *noisy, + CPR: *cpr, + Encless: *encless, + Verifier: verifier, + DSAPriv: priv, + }, + Protocol: protocol, + InterfaceName: *ifaceName, + ProxyAddress: *proxyAddr, + ProxyAuthentication: *proxyAuth, + RemoteAddress: *remoteAddr, + UpPath: *upPath, + DownPath: *downPath, + StatsAddress: *stats, + NoReconnect: *noreconnect, + MTU: *mtu, } - idsCache = govpn.NewMACCache() - confs := map[govpn.PeerId]*govpn.PeerConf{*verifier.Id: conf} - idsCache.Update(&confs) - log.Println(govpn.VersionGet()) - - tap, err = govpn.TAPListen(*ifaceName, *mtu) - if err != nil { - log.Fatalln("Can not listen on TUN/TAP interface:", err) + if err = conf.Validate(); err != nil { + log.Fatalln("Invalid settings:", err) } - if *stats != "" { - log.Println("Stats are going to listen on", *stats) - statsPort, err := net.Listen("tcp", *stats) - if err != nil { - log.Fatalln("Can not listen on stats port:", err) - } - go govpn.StatsProcessor(statsPort, &knownPeers) - } + log.Println(govpn.VersionGet()) if *syslog { govpn.SyslogEnable() @@ -145,38 +142,9 @@ } termSignal := make(chan os.Signal, 1) signal.Notify(termSignal, os.Interrupt, os.Kill) - -MainCycle: - for { - timeouted := make(chan struct{}) - rehandshaking := make(chan struct{}) - termination := make(chan struct{}) - switch *proto { - case "udp": - go startUDP(timeouted, rehandshaking, termination) - case "tcp": - if *proxyAddr != "" { - go proxyTCP(timeouted, rehandshaking, termination) - } else { - go startTCP(timeouted, rehandshaking, termination) - } - } - select { - case <-termSignal: - govpn.BothPrintf(`[finish remote="%s"]`, *remoteAddr) - termination <- struct{}{} - break MainCycle - case <-timeouted: - if *noreconnect { - break MainCycle - } - govpn.BothPrintf(`[sleep seconds="%d"]`, timeout) - time.Sleep(time.Second * time.Duration(timeout)) - case <-rehandshaking: - } - close(timeouted) - close(rehandshaking) - close(termination) + c := client.NewClient(conf, verifier, termSignal) + go c.MainCycle() + if err = <-c.Error; err != nil { + log.Fatalln(err) } - govpn.ScriptCall(*downPath, *ifaceName, *remoteAddr) } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go b/src/cypherpunks.ru/govpn/client/proxy.go rename from src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go rename to src/cypherpunks.ru/govpn/client/proxy.go index 0a6729c1dd7ca99ae0ce8f66bc4793ace0bd8a3e..be7d1149fa3e4d44e257a195fe92151c11e8ec8d 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go +++ b/src/cypherpunks.ru/govpn/client/proxy.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -16,32 +16,34 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package main +package client import ( "bufio" "encoding/base64" - "log" + "fmt" "net" "net/http" "cypherpunks.ru/govpn" ) -func proxyTCP(timeouted, rehandshaking, termination chan struct{}) { - proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr) +func (c *Client) proxyTCP() { + proxyAddr, err := net.ResolveTCPAddr("tcp", c.config.ProxyAddress) if err != nil { - log.Fatalln("Can not resolve proxy address:", err) + c.Error <- err + return } conn, err := net.DialTCP("tcp", nil, proxyAddr) if err != nil { - log.Fatalln("Can not connect to proxy:", err) + c.Error <- err + return } - req := "CONNECT " + *remoteAddr + " HTTP/1.1\n" - req += "Host: " + *remoteAddr + "\n" - if *proxyAuth != "" { + req := "CONNECT " + c.config.ProxyAddress + " HTTP/1.1\n" + req += "Host: " + c.config.ProxyAddress + "\n" + if c.config.ProxyAuthentication != "" { req += "Proxy-Authorization: Basic " - req += base64.StdEncoding.EncodeToString([]byte(*proxyAuth)) + "\n" + req += base64.StdEncoding.EncodeToString([]byte(c.config.ProxyAuthentication)) + "\n" } req += "\n" conn.Write([]byte(req)) @@ -50,8 +52,9 @@ bufio.NewReader(conn), &http.Request{Method: "CONNECT"}, ) if err != nil || resp.StatusCode != http.StatusOK { - log.Fatalln("Unexpected response from proxy") + c.Error <- fmt.Errorf("Unexpected response from proxy: %s", err.Error()) + return } - govpn.Printf(`[proxy-connected remote="%s" addr="%s"]`, *remoteAddr, *proxyAddr) - go handleTCP(conn, timeouted, rehandshaking, termination) + govpn.Printf(`[proxy-connected remote="%s" addr="%s"]`, c.config.RemoteAddress, *proxyAddr) + go c.handleTCP(conn) } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go deleted file mode 100644 index 57a412d31d687772c691fa2dc07cb7a65ddf8946..0000000000000000000000000000000000000000 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 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 ( - "bytes" - "log" - "net" - "sync/atomic" - "time" - - "cypherpunks.ru/govpn" -) - -func startTCP(timeouted, rehandshaking, termination chan struct{}) { - remote, err := net.ResolveTCPAddr("tcp", *remoteAddr) - if err != nil { - log.Fatalln("Can not resolve remote address:", err) - } - conn, err := net.DialTCP("tcp", nil, remote) - if err != nil { - log.Fatalln("Can not connect to address:", err) - } - govpn.Printf(`[connected remote="%s"]`, *remoteAddr) - handleTCP(conn, timeouted, rehandshaking, termination) -} - -func handleTCP(conn *net.TCPConn, timeouted, rehandshaking, termination chan struct{}) { - hs := govpn.HandshakeStart(*remoteAddr, conn, conf) - buf := make([]byte, 2*(govpn.EnclessEnlargeSize+*mtu)+*mtu) - var n int - var err error - var prev int - var peer *govpn.Peer - var terminator chan struct{} -HandshakeCycle: - for { - select { - case <-termination: - break HandshakeCycle - default: - } - if prev == len(buf) { - govpn.Printf(`[packet-timeouted remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break HandshakeCycle - } - - conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) - n, err = conn.Read(buf[prev:]) - if err != nil { - govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break HandshakeCycle - } - - prev += n - peerId := idsCache.Find(buf[:prev]) - if peerId == nil { - continue - } - peer = hs.Client(buf[:prev]) - prev = 0 - if peer == nil { - continue - } - govpn.Printf(`[handshake-completed remote="%s"]`, *remoteAddr) - knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer}) - if firstUpCall { - go govpn.ScriptCall(*upPath, *ifaceName, *remoteAddr) - firstUpCall = false - } - hs.Zero() - terminator = make(chan struct{}) - go govpn.PeerTapProcessor(peer, tap, terminator) - break HandshakeCycle - } - if hs != nil { - hs.Zero() - } - if peer == nil { - return - } - - prev = 0 - var i int -TransportCycle: - for { - select { - case <-termination: - break TransportCycle - default: - } - if prev == len(buf) { - govpn.Printf(`[packet-timeouted remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break TransportCycle - } - conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) - n, err = conn.Read(buf[prev:]) - if err != nil { - govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break TransportCycle - } - prev += n - CheckMore: - if prev < govpn.MinPktLength { - continue - } - i = bytes.Index(buf[:prev], peer.NonceExpect) - if i == -1 { - continue - } - if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) { - govpn.Printf(`[packet-unauthenticated remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break TransportCycle - } - if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey { - govpn.Printf(`[rehandshake-required remote="%s"]`, *remoteAddr) - rehandshaking <- struct{}{} - break TransportCycle - } - copy(buf, buf[i+govpn.NonceSize:prev]) - prev = prev - i - govpn.NonceSize - goto CheckMore - } - if terminator != nil { - terminator <- struct{}{} - } - peer.Zero() - conn.Close() -} diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go deleted file mode 100644 index 96dbfba418822b9833c40ffd7154070a11c3c8b6..0000000000000000000000000000000000000000 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 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" - "sync/atomic" - "time" - - "cypherpunks.ru/govpn" -) - -func startUDP(timeouted, rehandshaking, termination chan struct{}) { - remote, err := net.ResolveUDPAddr("udp", *remoteAddr) - if err != nil { - log.Fatalln("Can not resolve remote address:", err) - } - conn, err := net.DialUDP("udp", nil, remote) - if err != nil { - log.Fatalln("Can not listen on UDP:", err) - } - govpn.Printf(`[connected remote="%s"]`, *remoteAddr) - - hs := govpn.HandshakeStart(*remoteAddr, conn, conf) - buf := make([]byte, *mtu*2) - var n int - var timeouts int - var peer *govpn.Peer - var terminator chan struct{} -MainCycle: - for { - select { - case <-termination: - break MainCycle - default: - } - - conn.SetReadDeadline(time.Now().Add(time.Second)) - n, err = conn.Read(buf) - if timeouts == timeout { - govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr) - timeouted <- struct{}{} - break - } - if err != nil { - timeouts++ - continue - } - if peer != nil { - if peer.PktProcess(buf[:n], tap, true) { - timeouts = 0 - } else { - govpn.Printf(`[packet-unauthenticated remote="%s"]`, *remoteAddr) - timeouts++ - } - if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey { - govpn.Printf(`[rehandshake-required remote="%s"]`, *remoteAddr) - rehandshaking <- struct{}{} - break MainCycle - } - continue - } - if idsCache.Find(buf[:n]) == nil { - govpn.Printf(`[identity-invalid remote="%s"]`, *remoteAddr) - continue - } - timeouts = 0 - peer = hs.Client(buf[:n]) - if peer == nil { - continue - } - govpn.Printf(`[handshake-completed remote="%s"]`, *remoteAddr) - knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer}) - if firstUpCall { - go govpn.ScriptCall(*upPath, *ifaceName, *remoteAddr) - firstUpCall = false - } - hs.Zero() - terminator = make(chan struct{}) - go govpn.PeerTapProcessor(peer, tap, terminator) - } - if terminator != nil { - terminator <- struct{}{} - } - if hs != nil { - hs.Zero() - } - conn.Close() -} diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go index 7c2dd41e7602a42a7225e8fd74331dcc2df0ffaf..f18d34b420c964a644dcbc4ae76170c1672e8800 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -35,22 +35,27 @@ var ( handshakes map[string]*govpn.Handshake = make(map[string]*govpn.Handshake) hsLock sync.RWMutex - peers map[string]*PeerState = make(map[string]*PeerState) + peers = make(map[string]*PeerState) peersLock sync.RWMutex - peersById map[govpn.PeerId]string = make(map[govpn.PeerId]string) - peersByIdLock sync.RWMutex + peersByID = make(map[govpn.PeerID]string) + peersByIDLock sync.RWMutex knownPeers govpn.KnownPeers kpLock sync.RWMutex ) -func callUp(peerId *govpn.PeerId, remoteAddr string) (string, error) { - ifaceName := confs[*peerId].Iface - if confs[*peerId].Up != "" { - result, err := govpn.ScriptCall(confs[*peerId].Up, ifaceName, remoteAddr) +func callUp(peerID *govpn.PeerID, remoteAddr string) (string, error) { + ifaceName := confs[*peerID].Iface + if confs[*peerID].Up != "" { + result, err := govpn.ScriptCall(confs[*peerID].Up, ifaceName, remoteAddr) if err != nil { - govpn.Printf(`[script-failed bind="%s" path="%s" err="%s"]`, *bindAddr, confs[*peerId].Up, err) + govpn.Printf( + `[script-failed bind="%s" path="%s" err="%s"]`, + *bindAddr, + confs[*peerID].Up, + err, + ) return "", err } if ifaceName == "" { @@ -62,7 +67,7 @@ ifaceName = string(result[:sepIndex]) } } if ifaceName == "" { - govpn.Printf(`[tap-failed bind="%s" peer="%s"]`, *bindAddr, *peerId) + govpn.Printf(`[tap-failed bind="%s" peer="%s"]`, *bindAddr, *peerID) } return ifaceName, nil } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go index ea61c813f22494be3b469b9736f24c99ef436df6..62eca02437fa6135046345d0aa92c974a39c7e30 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -24,7 +24,7 @@ "io/ioutil" "log" "time" - "github.com/go-yaml/yaml" + "gopkg.in/yaml.v2" "cypherpunks.ru/govpn" ) @@ -34,11 +34,11 @@ RefreshRate = time.Minute ) var ( - confs map[govpn.PeerId]*govpn.PeerConf + confs map[govpn.PeerID]*govpn.PeerConf idsCache *govpn.MACCache ) -func confRead() (*map[govpn.PeerId]*govpn.PeerConf, error) { +func confRead() (*map[govpn.PeerID]*govpn.PeerConf, error) { data, err := ioutil.ReadFile(*confPath) if err != nil { return nil, err @@ -49,7 +49,7 @@ if err != nil { return nil, err } - confs := make(map[govpn.PeerId]*govpn.PeerConf, len(*confsRaw)) + confs := make(map[govpn.PeerID]*govpn.PeerConf, len(*confsRaw)) for name, pc := range *confsRaw { verifier, err := govpn.VerifierFromString(pc.VerifierRaw) if err != nil { @@ -67,7 +67,7 @@ pc.MTU = govpn.MTUMax } conf := govpn.PeerConf{ Verifier: verifier, - Id: verifier.Id, + ID: verifier.ID, Name: name, Iface: pc.Iface, MTU: pc.MTU, @@ -82,7 +82,7 @@ if pc.TimeoutInt <= 0 { pc.TimeoutInt = govpn.TimeoutDefault } conf.Timeout = time.Second * time.Duration(pc.TimeoutInt) - confs[*verifier.Id] = &conf + confs[*verifier.ID] = &conf } return &confs, nil } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go index 68a0172f40ca42b2df66fae78a2d414ac4cf45b9..2beb396f6958409a2cf0f27f9e938e6a76558d47 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -108,7 +108,7 @@ case <-termSignal: govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr) for _, ps := range peers { govpn.ScriptCall( - confs[*ps.peer.Id].Down, + confs[*ps.peer.ID].Down, ps.tap.Name, ps.peer.Addr, ) @@ -125,7 +125,7 @@ delete(handshakes, addr) } } peersLock.Lock() - peersByIdLock.Lock() + peersByIDLock.Lock() kpLock.Lock() for addr, ps := range peers { ps.peer.BusyR.Lock() @@ -135,9 +135,9 @@ if needsDeletion { govpn.Printf(`[peer-delete bind="%s" peer="%s"]`, *bindAddr, ps.peer) delete(peers, addr) delete(knownPeers, addr) - delete(peersById, *ps.peer.Id) + delete(peersByID, *ps.peer.ID) go govpn.ScriptCall( - confs[*ps.peer.Id].Down, + confs[*ps.peer.ID].Down, ps.tap.Name, ps.peer.Addr, ) @@ -146,7 +146,7 @@ } } hsLock.Unlock() peersLock.Unlock() - peersByIdLock.Unlock() + peersByIDLock.Unlock() kpLock.Unlock() } } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go index 02c1f0f53addb30708cdace82e756c8edc71ed26..87e3455e13b700d5ce288791f83744816e44d368 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go index bfb4709771e5cdc8680aea9ada107a5bae908c69..c36da3fed3ca02f8610a7ff9040b679f3e4b3981 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -71,16 +71,16 @@ // Either EOFed or timeouted break } prev += n - peerId := idsCache.Find(buf[:prev]) - if peerId == nil { + peerID := idsCache.Find(buf[:prev]) + if peerID == nil { continue } if hs == nil { - conf = confs[*peerId] + conf = confs[*peerID] if conf == nil { govpn.Printf( `[conf-get-failed bind="%s" peer="%s"]`, - *bindAddr, peerId.String(), + *bindAddr, peerID.String(), ) break } @@ -94,11 +94,11 @@ } hs.Zero() govpn.Printf( `[handshake-completed bind="%s" addr="%s" peer="%s"]`, - *bindAddr, addr, peerId.String(), + *bindAddr, addr, peerID.String(), ) - peersByIdLock.RLock() - addrPrev, exists := peersById[*peer.Id] - peersByIdLock.RUnlock() + peersByIDLock.RLock() + addrPrev, exists := peersByID[*peer.ID] + peersByIDLock.RUnlock() if exists { peersLock.Lock() peers[addrPrev].terminator <- struct{}{} @@ -109,22 +109,22 @@ tap: tap, terminator: make(chan struct{}), } go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator) - peersByIdLock.Lock() + peersByIDLock.Lock() kpLock.Lock() delete(peers, addrPrev) delete(knownPeers, addrPrev) peers[addr] = ps knownPeers[addr] = &peer - peersById[*peer.Id] = addr + peersByID[*peer.ID] = addr peersLock.Unlock() - peersByIdLock.Unlock() + peersByIDLock.Unlock() kpLock.Unlock() govpn.Printf( `[rehandshake-completed bind="%s" peer="%s"]`, - *bindAddr, peerId.String(), + *bindAddr, peerID.String(), ) } else { - ifaceName, err := callUp(peer.Id, peer.Addr) + ifaceName, err := callUp(peer.ID, peer.Addr) if err != nil { peer = nil break @@ -133,7 +133,7 @@ tap, err = govpn.TAPListen(ifaceName, peer.MTU) if err != nil { govpn.Printf( `[tap-failed bind="%s" peer="%s" err="%s"]`, - *bindAddr, peerId.String(), err, + *bindAddr, peerID.String(), err, ) peer = nil break @@ -145,15 +145,15 @@ terminator: make(chan struct{}, 1), } go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator) peersLock.Lock() - peersByIdLock.Lock() + peersByIDLock.Lock() kpLock.Lock() peers[addr] = ps - peersById[*peer.Id] = addr + peersByID[*peer.ID] = addr knownPeers[addr] = &peer peersLock.Unlock() - peersByIdLock.Unlock() + peersByIDLock.Unlock() kpLock.Unlock() - govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peerId.String()) + govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peerID.String()) } break } @@ -188,7 +188,7 @@ } if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) { govpn.Printf( `[packet-unauthenticated bind="%s" addr="%s" peer="%s"]`, - *bindAddr, addr, peer.Id.String(), + *bindAddr, addr, peer.ID.String(), ) break } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go index 98993d30a18e5204a2f8c733c9cf8ddeea5d07c9..cdcfb56996cc8492106f3f20797f315d8caa70df 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -36,7 +36,7 @@ } var ( // Buffers for UDP parallel processing - udpBufs chan []byte = make(chan []byte, 1<<8) + udpBufs = make(chan []byte, 1<<8) ) func startUDP() { @@ -61,8 +61,7 @@ var ps *PeerState var hs *govpn.Handshake var addrPrev string var exists bool - var peerId *govpn.PeerId - var peer *govpn.Peer + var peerID *govpn.PeerID var conf *govpn.PeerConf for { buf = <-udpBufs @@ -76,29 +75,54 @@ peersLock.RLock() ps, exists = peers[addr] peersLock.RUnlock() - if !exists { - goto CheckHandshake + if exists { + go func(peer *govpn.Peer, tap *govpn.TAP, buf []byte, n int) { + peer.PktProcess(buf[:n], tap, true) + udpBufs <- buf + }(ps.peer, ps.tap, buf, n) + continue } - go func(ps *govpn.Peer, tap *govpn.TAP, buf []byte, n int) { - peer.PktProcess(buf[:n], tap, true) - udpBufs <- buf - }(ps.peer, ps.tap, buf, n) - continue - CheckHandshake: + hsLock.RLock() hs, exists = handshakes[addr] hsLock.RUnlock() if !exists { - goto CheckID + peerID = idsCache.Find(buf[:n]) + if peerID == nil { + govpn.Printf(`[identity-unknown bind="%s" addr="%s"]`, *bindAddr, addr) + udpBufs <- buf + continue + } + conf = confs[*peerID] + if conf == nil { + govpn.Printf( + `[conf-get-failed bind="%s" peer="%s"]`, + *bindAddr, peerID.String(), + ) + udpBufs <- buf + continue + } + hs := govpn.NewHandshake( + addr, + UDPSender{conn: conn, addr: raddr}, + conf, + ) + hs.Server(buf[:n]) + udpBufs <- buf + hsLock.Lock() + handshakes[addr] = hs + hsLock.Unlock() + continue } - peer = hs.Server(buf[:n]) + + peer := hs.Server(buf[:n]) if peer == nil { - goto Finished + udpBufs <- buf + continue } - govpn.Printf( `[handshake-completed bind="%s" addr="%s" peer="%s"]`, - *bindAddr, addr, peerId.String(), + *bindAddr, addr, peerID.String(), ) hs.Zero() hsLock.Lock() @@ -109,39 +133,39 @@ go func() { udpBufs <- make([]byte, govpn.MTUMax) udpBufs <- make([]byte, govpn.MTUMax) }() - peersByIdLock.RLock() - addrPrev, exists = peersById[*peer.Id] - peersByIdLock.RUnlock() + peersByIDLock.RLock() + addrPrev, exists = peersByID[*peer.ID] + peersByIDLock.RUnlock() if exists { peersLock.Lock() peers[addrPrev].terminator <- struct{}{} - ps = &PeerState{ + psNew := &PeerState{ peer: peer, tap: peers[addrPrev].tap, terminator: make(chan struct{}), } - go func(ps PeerState) { - govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator) + go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) { + govpn.PeerTapProcessor(peer, tap, terminator) <-udpBufs <-udpBufs - }(*ps) - peersByIdLock.Lock() + }(psNew.peer, psNew.tap, psNew.terminator) + peersByIDLock.Lock() kpLock.Lock() delete(peers, addrPrev) delete(knownPeers, addrPrev) - peers[addr] = ps + peers[addr] = psNew knownPeers[addr] = &peer - peersById[*peer.Id] = addr + peersByID[*peer.ID] = addr peersLock.Unlock() - peersByIdLock.Unlock() + peersByIDLock.Unlock() kpLock.Unlock() govpn.Printf( `[rehandshake-completed bind="%s" peer="%s"]`, - *bindAddr, peer.Id.String(), + *bindAddr, peer.ID.String(), ) } else { go func(addr string, peer *govpn.Peer) { - ifaceName, err := callUp(peer.Id, peer.Addr) + ifaceName, err := callUp(peer.ID, peer.Addr) if err != nil { return } @@ -149,57 +173,32 @@ tap, err := govpn.TAPListen(ifaceName, peer.MTU) if err != nil { govpn.Printf( `[tap-failed bind="%s" peer="%s" err="%s"]`, - *bindAddr, peer.Id.String(), err, + *bindAddr, peer.ID.String(), err, ) return } - ps = &PeerState{ + psNew := &PeerState{ peer: peer, tap: tap, terminator: make(chan struct{}), } - go func(ps PeerState) { - govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator) + go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) { + govpn.PeerTapProcessor(peer, tap, terminator) <-udpBufs <-udpBufs - }(*ps) + }(psNew.peer, psNew.tap, psNew.terminator) peersLock.Lock() - peersByIdLock.Lock() + peersByIDLock.Lock() kpLock.Lock() - peers[addr] = ps + peers[addr] = psNew knownPeers[addr] = &peer - peersById[*peer.Id] = addr + peersByID[*peer.ID] = addr peersLock.Unlock() - peersByIdLock.Unlock() + peersByIDLock.Unlock() kpLock.Unlock() - govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peer.Id.String()) + govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peer.ID.String()) }(addr, peer) } - goto Finished - CheckID: - peerId = idsCache.Find(buf[:n]) - if peerId == nil { - govpn.Printf(`[identity-unknown bind="%s" addr="%s"]`, *bindAddr, addr) - goto Finished - } - conf = confs[*peerId] - if conf == nil { - govpn.Printf( - `[conf-get-failed bind="%s" peer="%s"]`, - *bindAddr, peerId.String(), - ) - goto Finished - } - hs = govpn.NewHandshake( - addr, - UDPSender{conn: conn, addr: raddr}, - conf, - ) - hs.Server(buf[:n]) - hsLock.Lock() - handshakes[addr] = hs - hsLock.Unlock() - Finished: udpBufs <- buf } }() diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go index a83f9d15f18f75ea73a0951fd119db26940ba9df..aac36bafb664d5bce876f60bd25a9d105eaa216e 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -62,7 +62,7 @@ id := new([govpn.IDSize]byte) if _, err = io.ReadFull(govpn.Rand, id[:]); err != nil { log.Fatalln(err) } - pid := govpn.PeerId(*id) + pid := govpn.PeerID(*id) v := govpn.VerifierNew(*sOpt, *tOpt, *pOpt, &pid) v.PasswordApply(key) fmt.Println(v.LongForm()) diff --git a/src/cypherpunks.ru/govpn/cnw/cnw.go b/src/cypherpunks.ru/govpn/cnw/cnw.go index c5d41a635afb3440558f19b3c351ccc1035a0229..eaf0958b1cbdbc7d17fd213fd3abde1c81363c02 100644 --- a/src/cypherpunks.ru/govpn/cnw/cnw.go +++ b/src/cypherpunks.ru/govpn/cnw/cnw.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/cnw/cnw_test.go b/src/cypherpunks.ru/govpn/cnw/cnw_test.go index cbb28e98efa1d325c2222ed47b64280ef2881aa1..29e408f42405a6a0936b4e70f4c3bd84057e52f4 100644 --- a/src/cypherpunks.ru/govpn/cnw/cnw_test.go +++ b/src/cypherpunks.ru/govpn/cnw/cnw_test.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -28,7 +28,7 @@ "testing/quick" ) var ( - testKey *[32]byte = new([32]byte) + testKey = new([32]byte) ) func init() { diff --git a/src/cypherpunks.ru/govpn/common.go b/src/cypherpunks.ru/govpn/common.go index 98137c1c3bc4e5e91744c1c8fb22b87c12afb613..030794c726a5d13ba47a97d286ef237c4262519e 100644 --- a/src/cypherpunks.ru/govpn/common.go +++ b/src/cypherpunks.ru/govpn/common.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/conf.go b/src/cypherpunks.ru/govpn/conf.go index c8232d64b047a91644b47111b8cf6db8384613b0..7a9c4b8ce6ba00da4c9bad96e84eeeac10b17ee7 100644 --- a/src/cypherpunks.ru/govpn/conf.go +++ b/src/cypherpunks.ru/govpn/conf.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -25,7 +25,7 @@ "github.com/agl/ed25519" ) type PeerConf struct { - Id *PeerId `yaml:"-"` + ID *PeerID `yaml:"-"` Name string `yaml:"name"` Iface string `yaml:"iface"` MTU int `yaml:"mtu"` diff --git a/src/cypherpunks.ru/govpn/egd.go b/src/cypherpunks.ru/govpn/egd.go index 3d0b7918b26044353c83a408fbdb1b7d4c0750a8..f54c1168d11e0ce4287090e0079678414443316a 100644 --- a/src/cypherpunks.ru/govpn/egd.go +++ b/src/cypherpunks.ru/govpn/egd.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -25,7 +25,7 @@ "net" ) var ( - Rand io.Reader = rand.Reader + Rand = rand.Reader ) type EGDRand string diff --git a/src/cypherpunks.ru/govpn/encless.go b/src/cypherpunks.ru/govpn/encless.go index bd42f1d5729349e1a4c3bf6fe2bb813445ee6249..966a2dde65aac3498de3dddcebd5c8f5b1f7d9fe 100644 --- a/src/cypherpunks.ru/govpn/encless.go +++ b/src/cypherpunks.ru/govpn/encless.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/encless_test.go b/src/cypherpunks.ru/govpn/encless_test.go index fc00d2708d753735bb501e3886c1ee8d4df1cde5..e0c78c4bc6655353150a4b198fb86718850f239c 100644 --- a/src/cypherpunks.ru/govpn/encless_test.go +++ b/src/cypherpunks.ru/govpn/encless_test.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -27,7 +27,7 @@ "testing/quick" ) var ( - testKey *[32]byte = new([32]byte) + testKey = new([32]byte) ) func init() { diff --git a/src/cypherpunks.ru/govpn/handshake.go b/src/cypherpunks.ru/govpn/handshake.go index a3ef2b5cf536397c176678d38c8fcd4fff70d1d8..a824acef902629fcb16515eb8011f6294e00519f 100644 --- a/src/cypherpunks.ru/govpn/handshake.go +++ b/src/cypherpunks.ru/govpn/handshake.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -132,7 +132,7 @@ return &state } // Generate ID tag from client identification and data. -func idTag(id *PeerId, timeSync int, data []byte) []byte { +func idTag(id *PeerID, timeSync int, data []byte) []byte { enc := make([]byte, 8) copy(enc, data) AddTimeSync(timeSync, enc) @@ -141,13 +141,13 @@ if err != nil { panic(err) } mac.Write(enc) - mac.Sum(enc[:0]) - return enc + sum := mac.Sum(nil) + return sum[len(sum)-8:] } // Start handshake's procedure from the client. It is the entry point -// for starting the handshake procedure. // First handshake packet -// will be sent immediately. +// for starting the handshake procedure. +// First handshake packet will be sent immediately. func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { state := NewHandshake(addr, conn, conf) var dhPubRepr *[32]byte @@ -174,7 +174,7 @@ } else { chacha20.XORKeyStream(enc, enc, state.rNonce, state.dsaPubH) } data := append(state.rNonce[8:], enc...) - data = append(data, idTag(state.Conf.Id, state.Conf.TimeSync, state.rNonce[8:])...) + data = append(data, idTag(state.Conf.ID, state.Conf.TimeSync, state.rNonce[8:])...) state.conn.Write(data) return state } @@ -260,7 +260,7 @@ } // Send that to client h.conn.Write(append(encPub, append( - encRs, idTag(h.Conf.Id, h.Conf.TimeSync, encPub)..., + encRs, idTag(h.Conf.ID, h.Conf.TimeSync, encPub)..., )...)) h.LastPing = time.Now() } else @@ -316,7 +316,7 @@ } } else { chacha20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key) } - h.conn.Write(append(enc, idTag(h.Conf.Id, h.Conf.TimeSync, enc)...)) + h.conn.Write(append(enc, idTag(h.Conf.ID, h.Conf.TimeSync, enc)...)) // Switch peer peer := newPeer( @@ -416,7 +416,7 @@ chacha20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key) } // Send that to server - h.conn.Write(append(enc, idTag(h.Conf.Id, h.Conf.TimeSync, enc)...)) + h.conn.Write(append(enc, idTag(h.Conf.ID, h.Conf.TimeSync, enc)...)) h.LastPing = time.Now() } else // ENC(K, R+2, RC) + IDtag diff --git a/src/cypherpunks.ru/govpn/handshake_test.go b/src/cypherpunks.ru/govpn/handshake_test.go index bbe9841b8e2b8fa14d0087a2f8a49b81d88943d0..077146583c654c5fb726aaab70e03430b14cf104 100644 --- a/src/cypherpunks.ru/govpn/handshake_test.go +++ b/src/cypherpunks.ru/govpn/handshake_test.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -24,7 +24,7 @@ ) func TestHandshakeSymmetric(t *testing.T) { // initial values are taken from peer_test.go's init() - v := VerifierNew(1<<10, 1<<4, 1, &testPeerId) + v := VerifierNew(1<<10, 1<<4, 1, &testPeerID) testConf.Verifier = v testConf.DSAPriv = v.PasswordApply("does not matter") hsS := NewHandshake("server", Dummy{&testCt}, testConf) @@ -41,7 +41,7 @@ } func TestHandshakeNoiseSymmetric(t *testing.T) { // initial values are taken from peer_test.go's init() - v := VerifierNew(1<<10, 1<<4, 1, &testPeerId) + v := VerifierNew(1<<10, 1<<4, 1, &testPeerID) testConf.Verifier = v testConf.DSAPriv = v.PasswordApply("does not matter") testConf.Noise = true @@ -59,7 +59,7 @@ testConf.Noise = false } func TestHandshakeEnclessSymmetric(t *testing.T) { // initial values are taken from peer_test.go's init() - v := VerifierNew(1<<10, 1<<4, 1, &testPeerId) + v := VerifierNew(1<<10, 1<<4, 1, &testPeerID) testConf.Verifier = v testConf.DSAPriv = v.PasswordApply("does not matter") testConf.Encless = true diff --git a/src/cypherpunks.ru/govpn/identity.go b/src/cypherpunks.ru/govpn/identity.go index ece2dfcf0bb2131fd4c4a48c8467d39230728837..262bca658be887c4b779dacef6b503ecbaf66b96 100644 --- a/src/cypherpunks.ru/govpn/identity.go +++ b/src/cypherpunks.ru/govpn/identity.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -34,13 +34,13 @@ const ( IDSize = 128 / 8 ) -type PeerId [IDSize]byte +type PeerID [IDSize]byte -func (id PeerId) String() string { +func (id PeerID) String() string { return base64.RawStdEncoding.EncodeToString(id[:]) } -func (id PeerId) MarshalJSON() ([]byte, error) { +func (id PeerID) MarshalJSON() ([]byte, error) { return []byte(`"` + id.String() + `"`), nil } @@ -51,18 +51,18 @@ l sync.Mutex } type MACCache struct { - cache map[PeerId]*MACAndTimeSync + cache map[PeerID]*MACAndTimeSync l sync.RWMutex } func NewMACCache() *MACCache { - return &MACCache{cache: make(map[PeerId]*MACAndTimeSync)} + return &MACCache{cache: make(map[PeerID]*MACAndTimeSync)} } // Remove disappeared keys, add missing ones with initialized MACs. -func (mc *MACCache) Update(peers *map[PeerId]*PeerConf) { +func (mc *MACCache) Update(peers *map[PeerID]*PeerConf) { mc.l.Lock() - for pid, _ := range mc.cache { + for pid := range mc.cache { if _, exists := (*peers)[pid]; !exists { log.Println("Cleaning key:", pid) delete(mc.cache, pid) @@ -101,11 +101,12 @@ // Try to find peer's identity (that equals to MAC) // by taking first blocksize sized bytes from data at the beginning // as plaintext and last bytes as cyphertext. -func (mc *MACCache) Find(data []byte) *PeerId { +func (mc *MACCache) Find(data []byte) *PeerID { if len(data) < 8*2 { return nil } buf := make([]byte, 8) + sum := make([]byte, 32) mc.l.RLock() for pid, mt := range mc.cache { copy(buf, data) @@ -113,10 +114,10 @@ AddTimeSync(mt.ts, buf) mt.l.Lock() mt.mac.Reset() mt.mac.Write(buf) - mt.mac.Sum(buf[:0]) + mt.mac.Sum(sum[:0]) mt.l.Unlock() - if subtle.ConstantTimeCompare(buf, data[len(data)-8:]) == 1 { - ppid := PeerId(pid) + if subtle.ConstantTimeCompare(sum[len(sum)-8:], data[len(data)-8:]) == 1 { + ppid := PeerID(pid) mc.l.RUnlock() return &ppid } diff --git a/src/cypherpunks.ru/govpn/logger.go b/src/cypherpunks.ru/govpn/logger.go index b110ce3e55553fb91c6a8e1d4bbb2fd41f5afadb..48a32356d928b6f07ba461628c868bee90f437d8 100644 --- a/src/cypherpunks.ru/govpn/logger.go +++ b/src/cypherpunks.ru/govpn/logger.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/peer.go b/src/cypherpunks.ru/govpn/peer.go index 22c893c80fb51d3bb0bb1affbfa395e32a137257..e43dc9c75c235f54dcf206209e17b62105cde430 100644 --- a/src/cypherpunks.ru/govpn/peer.go +++ b/src/cypherpunks.ru/govpn/peer.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -56,13 +56,15 @@ mac, err := blake2b.New256(macKey) if err != nil { panic(err) } + sum := make([]byte, mac.Size()) nonces := make(chan *[NonceSize]byte, NonceBucketSize*3) go func() { for { buf := new([NonceSize]byte) binary.BigEndian.PutUint64(buf[:], i) mac.Write(buf[:]) - mac.Sum(buf[:0]) + mac.Sum(sum[0:]) + copy(buf[:], sum) nonces <- buf mac.Reset() i += 2 @@ -86,7 +88,7 @@ HeartbeatSent uint64 // Basic Addr string - Id *PeerId + ID *PeerID Conn io.Writer `json:"-"` // Traffic behaviour @@ -96,7 +98,7 @@ CPRCycle time.Duration `json:"-"` Encless bool MTU int - key *[SSize]byte `json:"-"` + key *[SSize]byte // Timers Timeout time.Duration `json:"-"` @@ -133,7 +135,7 @@ noncesT chan *[NonceSize]byte } func (p *Peer) String() string { - return p.Id.String() + ":" + p.Addr + return p.ID.String() + ":" + p.Addr } // Zero peer's memory state. @@ -183,7 +185,7 @@ } peer := Peer{ Addr: addr, - Id: conf.Id, + ID: conf.ID, Conn: conn, NoiseEnable: noiseEnable, diff --git a/src/cypherpunks.ru/govpn/peer_test.go b/src/cypherpunks.ru/govpn/peer_test.go index 18bd5feada92d165033b0b8da0336b834f08822d..6c16469f18c008298918d43f34e902fd965dacec 100644 --- a/src/cypherpunks.ru/govpn/peer_test.go +++ b/src/cypherpunks.ru/govpn/peer_test.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -28,7 +28,7 @@ var ( testPeer *Peer testPt []byte testCt []byte - testPeerId PeerId + testPeerID PeerID testConf *PeerConf ) @@ -45,9 +45,9 @@ } func init() { id := new([IDSize]byte) - testPeerId = PeerId(*id) + testPeerID = PeerID(*id) testConf = &PeerConf{ - Id: &testPeerId, + ID: &testPeerID, MTU: MTUDefault, Timeout: time.Second * time.Duration(TimeoutDefault), } diff --git a/src/cypherpunks.ru/govpn/stats.go b/src/cypherpunks.ru/govpn/stats.go index 1d956742607071447999665b39bd53a4db1dd2e2..c8ea6223e655912e60e6623344838174a7f6a718 100644 --- a/src/cypherpunks.ru/govpn/stats.go +++ b/src/cypherpunks.ru/govpn/stats.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/tap.go b/src/cypherpunks.ru/govpn/tap.go index 9010c55b6c3a89eb0b224298c74c4aa002c5aa0b..faf88ba9f6b9e1db4359ab792fad59ca5acbf5a5 100644 --- a/src/cypherpunks.ru/govpn/tap.go +++ b/src/cypherpunks.ru/govpn/tap.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 diff --git a/src/cypherpunks.ru/govpn/tap_freebsd.go b/src/cypherpunks.ru/govpn/tap_freebsd.go index 157c583c76f1b5ffd28cb0ac1bf8a1c933474465..03d4554fce9a5973744684646a74645c5d09c772 100644 --- a/src/cypherpunks.ru/govpn/tap_freebsd.go +++ b/src/cypherpunks.ru/govpn/tap_freebsd.go @@ -2,7 +2,7 @@ // +build freebsd /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 Sergey Matveev */ package govpn diff --git a/src/cypherpunks.ru/govpn/tap_linux.go b/src/cypherpunks.ru/govpn/tap_linux.go index 8c5e883760a2bcafda219acc37a39c1a57b2eacd..388853d817e58ef2d2726fff707636a4828d4b00 100644 --- a/src/cypherpunks.ru/govpn/tap_linux.go +++ b/src/cypherpunks.ru/govpn/tap_linux.go @@ -2,7 +2,7 @@ // +build linux /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 Sergey Matveev */ package govpn diff --git a/src/cypherpunks.ru/govpn/verifier.go b/src/cypherpunks.ru/govpn/verifier.go index 832fafe2dacf4981ee3ffc39e20aa5cde77d3a68..8be6ea7ac62c1f25e1d2222d4176c7465b7b1c71 100644 --- a/src/cypherpunks.ru/govpn/verifier.go +++ b/src/cypherpunks.ru/govpn/verifier.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2016 Sergey Matveev +Copyright (C) 2014-2017 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 @@ -45,14 +45,14 @@ type Verifier struct { S int T int P int - Id *PeerId + ID *PeerID Pub *[ed25519.PublicKeySize]byte } // Generate new verifier for given peer, with specified password and // hashing parameters. -func VerifierNew(s, t, p int, id *PeerId) *Verifier { - return &Verifier{S: s, T: t, P: p, Id: id} +func VerifierNew(s, t, p int, id *PeerID) *Verifier { + return &Verifier{S: s, T: t, P: p, ID: id} } func blake2bKeyless() hash.Hash { @@ -66,7 +66,7 @@ // Apply the password: create Ed25519 keypair based on it, save public // key in verifier. func (v *Verifier) PasswordApply(password string) *[ed25519.PrivateKeySize]byte { - r := balloon.H(blake2bKeyless, []byte(password), v.Id[:], v.S, v.T, v.P) + r := balloon.H(blake2bKeyless, []byte(password), v.ID[:], v.S, v.T, v.P) defer SliceZero(r) src := bytes.NewBuffer(r) pub, prv, err := ed25519.GenerateKey(src) @@ -95,8 +95,8 @@ } v := Verifier{S: s, T: t, P: p} id := new([IDSize]byte) copy(id[:], salt) - pid := PeerId(*id) - v.Id = &pid + pid := PeerID(*id) + v.ID = &pid if len(ss) == 5 { pub, err := base64.RawStdEncoding.DecodeString(ss[4]) if err != nil { @@ -113,7 +113,7 @@ // Does not include public key. func (v *Verifier) ShortForm() string { return fmt.Sprintf( "$balloon$s=%d,t=%d,p=%d$%s", - v.S, v.T, v.P, base64.RawStdEncoding.EncodeToString(v.Id[:]), + v.S, v.T, v.P, base64.RawStdEncoding.EncodeToString(v.ID[:]), ) } diff --git a/utils/makedist.sh b/utils/makedist.sh index f890c3a99c0f6e8240e9535b383d938792ffee3e..deff7783b9c02017ea73e84547bfacc5002d987d 100755 --- a/utils/makedist.sh +++ b/utils/makedist.sh @@ -10,7 +10,7 @@ repos=" src/cypherpunks.ru/balloon src/github.com/agl/ed25519 src/github.com/bigeagle/water - src/github.com/go-yaml/yaml + src/gopkg.in/yaml.v2 src/golang.org/x/crypto " for repo in $repos; do @@ -44,11 +44,13 @@ You can obtain releases source code prepared tarballs on @url{http://www.govpn.info/}. EOF make -C doc -rm -r doc/.well-known doc/govpn.html/.well-known +./utils/news.sh +rm -r doc/.well-known doc/govpn.html/.well-known utils/news.sh rm utils/makedist.sh find . -name .git -type d | xargs rm -fr find . -name .gitignore -delete +rm .gitmodules cd .. tar cvf govpn-"$release".tar govpn-"$release" @@ -154,7 +156,7 @@ SHA256 хэш: $hash Идентификатор GPG ключа: 0xF2F59045FFE2F4A1 GoVPN releases Отпечаток: D269 9B73 3C41 2068 D8DA 656E F2F5 9045 FFE2 F4A1 -Пожалуйста все вопросы касающиеся использования GoVPN, отчёты об ошибках +Пожалуйста, все вопросы касающиеся использования GoVPN, отчёты об ошибках и патчи отправляйте в govpn-devel почтовую рассылку: https://lists.cypherpunks.ru/pipermail/govpn-devel/ EOF diff --git a/utils/news.sh b/utils/news.sh new file mode 100755 index 0000000000000000000000000000000000000000..9064312862fe45cd1f9fee26b87aaafceef1153c --- /dev/null +++ b/utils/news.sh @@ -0,0 +1,33 @@ +#!/bin/sh -ex + +texi=`mktemp` + +cat > $texi < $texi <