/*
GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
import (
"flag"
"fmt"
- "log"
- "net"
- "os"
- "os/signal"
"time"
- "cypherpunks.ru/govpn"
-)
+ "github.com/Sirupsen/logrus"
-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")
- 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")
- 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")
- 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"
+ "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")
+ logLevel = flag.String("log_level", "warning", "Log level")
+ protocol govpn.Protocol
+ err error
+ fields = logrus.Fields{"func": "main"}
+ )
+
flag.Parse()
if *warranty {
fmt.Println(govpn.Warranty)
return
}
- timeout = *timeoutP
- var err error
- log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
+ if *version {
+ fmt.Println(govpn.VersionGet())
+ return
+ }
- if *mtu > govpn.MTUMax {
- log.Fatalln("Maximum allowable MTU is", govpn.MTUMax)
+ logger, err := govpn.NewLogger(*logLevel, *syslog)
+ if err != nil {
+ logrus.WithFields(fields).WithError(err).Fatal("Can't initialize logging")
}
+
if *egdPath != "" {
- log.Println("Using", *egdPath, "EGD")
+ logger.WithField("egd_path", *egdPath).WithFields(fields).Debug("Init EGD")
govpn.EGDInit(*egdPath)
}
- if *proxyAddr != "" {
- *proto = "tcp"
+ if protocol, err = govpn.NewProtocolFromString(*proto); err != nil {
+ logger.WithError(
+ err,
+ ).WithFields(
+ fields,
+ ).WithField(
+ "proto", *proto,
+ ).Fatal("Invalid protocol")
}
- if !(*proto == "udp" || *proto == "tcp") {
- log.Fatalln("Unknown protocol specified")
+
+ if *proxyAddr != "" && protocol == govpn.ProtocolUDP {
+ logrus.WithFields(fields).WithFields(logrus.Fields{
+ "proxy": *proxyAddr,
+ "proto": *proto,
+ }).Fatal("HTTP proxy is supported only in TCP mode")
}
+
if *verifierRaw == "" {
- log.Fatalln("No verifier specified")
+ logger.Fatalln("-verifier is required")
}
verifier, err := govpn.VerifierFromString(*verifierRaw)
if err != nil {
- log.Fatalln(err)
+ logger.WithError(err).Fatal("Invalid -verifier")
}
key, err := govpn.KeyRead(*keyPath)
if err != nil {
- log.Fatalln("Unable to read the key", err)
+ logger.WithError(err).Fatal("Invalid -key")
+ }
+ priv, err := verifier.PasswordApply(key)
+ if err != nil {
+ logger.WithError(err).Fatal("Can't PasswordApply")
}
- priv := verifier.PasswordApply(key)
if *encless {
- if *proto != "tcp" {
- log.Fatalln("Currently encryptionless mode works only with TCP")
+ if protocol != govpn.ProtocolTCP {
+ logger.Fatal("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,
- }
- 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)
+ 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,
+ Up: govpn.RunScriptAction(upPath),
+ Down: govpn.RunScriptAction(downPath),
+ },
+ Protocol: protocol,
+ ProxyAddress: *proxyAddr,
+ ProxyAuthentication: *proxyAuth,
+ RemoteAddress: *remoteAddr,
+ NoReconnect: *noreconnect,
+ }
+ if err = conf.Validate(); err != nil {
+ logger.WithError(err).Fatal("Invalid settings")
+ }
+
+ c, err := client.NewClient(conf, logger, govpn.CatchSignalShutdown())
if err != nil {
- log.Fatalln("Can not listen on TAP interface:", err)
+ logger.WithError(err).Fatal("Can't initialize client")
}
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)
- }
-
- if *syslog {
- govpn.SyslogEnable()
- }
-
- 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)
+ go govpn.StatsProcessor(*stats, c.KnownPeers())
+ }
+
+ go c.MainCycle()
+ if err = <-c.Error; err != nil {
+ logger.WithError(err).Fatal("Fatal error")
}
- govpn.ScriptCall(*downPath, *ifaceName, *remoteAddr)
}