X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=src%2Fgovpn%2Fcmd%2Fgovpn-client%2Fmain.go;h=c4414050937f20e9740a3233d6e2d0c1fcac193e;hb=cf77814e44502cf140f063b022b662d7cc7be66d;hp=f49edf8de8c05be0a87c7633e18aaace92e5eeea;hpb=e0b2dac4d0732860cf41d8ffa4ddbeeb3fa04696;p=govpn.git diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index f49edf8..c441405 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2015 Sergey Matveev +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 @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -// Simple secure free software virtual private network daemon client. +// Simple secure, DPI/censorship-resistant free software VPN daemon client. package main import ( @@ -31,83 +31,80 @@ import ( ) var ( - remoteAddr = flag.String("remote", "", "Remote server address") - 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") - mtu = flag.Int("mtu", 1452, "MTU for outgoing packets") - nonceDiff = flag.Int("noncediff", 1, "Allow nonce difference") - timeoutP = flag.Int("timeout", 60, "Timeout seconds") - noisy = flag.Bool("noise", false, "Enable noise appending") - cpr = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate") + 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") + 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 TAP interface") + timeoutP = flag.Int("timeout", 60, "Timeout seconds") + 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") + + conf *govpn.PeerConf + tap *govpn.TAP + timeout int + firstUpCall bool = true + knownPeers govpn.KnownPeers + idsCache *govpn.CipherCache ) func main() { flag.Parse() - timeout := *timeoutP + timeout = *timeoutP var err error log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) - govpn.MTU = *mtu - - id := govpn.IDDecode(*IDRaw) - if id == nil { - panic("ID is not specified") + if *mtu > govpn.MTUMax { + log.Fatalln("Maximum allowable MTU is", govpn.MTUMax) } - - pub, priv := govpn.NewVerifier(id, govpn.StringFromFile(*keyPath)) - conf := &govpn.PeerConf{ - Id: id, - Timeout: time.Second * time.Duration(timeout), - Noncediff: *nonceDiff, - NoiseEnable: *noisy, - CPR: *cpr, - DSAPub: pub, - DSAPriv: priv, + if *egdPath != "" { + log.Println("Using", *egdPath, "EGD") + govpn.EGDInit(*egdPath) } - govpn.PeersInitDummy(id, conf) - bind, err := net.ResolveUDPAddr("udp", "0.0.0.0:0") + verifier, err := govpn.VerifierFromString(*verifierRaw) if err != nil { - panic(err) + log.Fatalln(err) } - conn, err := net.ListenUDP("udp", bind) - if err != nil { - panic(err) + priv := verifier.PasswordApply(govpn.StringFromFile(*keyPath)) + if *encless { + if *proto != "tcp" { + log.Fatalln("Currently encryptionless mode works only with TCP") + } + *noisy = true } - remote, err := net.ResolveUDPAddr("udp", *remoteAddr) - if err != nil { - panic(err) + conf = &govpn.PeerConf{ + Id: verifier.Id, + Iface: *ifaceName, + MTU: *mtu, + Timeout: time.Second * time.Duration(timeout), + Noise: *noisy, + CPR: *cpr, + Encless: *encless, + Verifier: verifier, + DSAPriv: priv, } + idsCache = govpn.NewCipherCache([]govpn.PeerId{*verifier.Id}) + log.Println(govpn.VersionGet()) - tap, ethSink, ethReady, _, err := govpn.TAPListen( - *ifaceName, - time.Second*time.Duration(timeout), - *cpr, - ) + tap, err = govpn.TAPListen(*ifaceName, *mtu) if err != nil { - panic(err) + 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}) - log.Println(govpn.VersionGet()) - log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU()) if *stats != "" { log.Println("Stats are going to listen on", *stats) statsPort, err := net.Listen("tcp", *stats) if err != nil { - panic(err) + log.Fatalln("Can not listen on stats port:", err) } go govpn.StatsProcessor(statsPort, &knownPeers) } @@ -115,71 +112,38 @@ func main() { termSignal := make(chan os.Signal, 1) signal.Notify(termSignal, os.Interrupt, os.Kill) - log.Println("Starting handshake") - handshake := govpn.HandshakeStart(conf, conn, remote) - MainCycle: for { - if peer != nil && (peer.BytesIn+peer.BytesOut) > govpn.MaxBytesPerKey { - peer.Zero() - peer = nil - handshake = govpn.HandshakeStart(conf, conn, remote) - log.Println("Rehandshaking") + timeouted := make(chan struct{}) + rehandshaking := make(chan struct{}) + termination := make(chan struct{}) + if *proxyAddr != "" { + *proto = "tcp" + } + switch *proto { + case "udp": + go startUDP(timeouted, rehandshaking, termination) + case "tcp": + if *proxyAddr != "" { + go proxyTCP(timeouted, rehandshaking, termination) + } else { + go startTCP(timeouted, rehandshaking, termination) + } + default: + log.Fatalln("Unknown protocol specified") } select { case <-termSignal: + log.Fatalln("Finishing") + termination <- struct{}{} break MainCycle - case ethPkt = <-ethSink: - if peer == nil { - if len(ethPkt) > 0 { - ethReady <- struct{}{} - } - continue - } - peer.EthProcess(ethPkt, conn, ethReady) - case udpPkt = <-udpSink: - timeouts++ - if timeouts >= timeout { - break MainCycle - } - if udpPkt.Addr == nil { - udpReady <- 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 { - log.Println("Invalid identity in handshake packet") - udpReady <- struct{}{} - continue - } - if p := handshake.Client(conn, udpPktData); p != nil { - log.Println("Handshake completed") - if firstUpCall { - go govpn.ScriptCall(*upPath, *ifaceName) - firstUpCall = false - } - peer = p - handshake.Zero() - handshake = nil - } - udpReady <- struct{}{} - continue - } - if peer == nil { - udpReady <- struct{}{} - continue - } - if peer.UDPProcess(udpPktData, tap, udpReady) { - timeouts = 0 - } + case <-timeouted: + break MainCycle + case <-rehandshaking: } + close(timeouted) + close(rehandshaking) + close(termination) } govpn.ScriptCall(*downPath, *ifaceName) }