]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/client/client.go
Use convenient simpler Go 1.9's sync.Map
[govpn.git] / src / cypherpunks.ru / govpn / client / client.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package client
20
21 import (
22         "errors"
23         "fmt"
24         "net"
25         "os"
26         "sync"
27         "time"
28
29         "github.com/agl/ed25519"
30
31         "cypherpunks.ru/govpn"
32 )
33
34 type Protocol int
35
36 const (
37         ProtocolUDP Protocol = iota
38         ProtocolTCP
39 )
40
41 type Configuration struct {
42         PrivateKey          *[ed25519.PrivateKeySize]byte
43         Peer                *govpn.PeerConf
44         Protocol            Protocol
45         InterfaceName       string
46         ProxyAddress        string
47         ProxyAuthentication string
48         RemoteAddress       string
49         UpPath              string
50         DownPath            string
51         StatsAddress        string
52         NoReconnect         bool
53         MTU                 int
54 }
55
56 func (c *Configuration) Validate() error {
57         if c.MTU > govpn.MTUMax {
58                 return fmt.Errorf("Invalid MTU %d, maximum allowable is %d", c.MTU, govpn.MTUMax)
59         }
60         if len(c.RemoteAddress) == 0 {
61                 return errors.New("Missing RemoteAddress")
62         }
63         if len(c.InterfaceName) == 0 {
64                 return errors.New("Missing InterfaceName")
65         }
66         return nil
67 }
68
69 func (c *Configuration) isProxy() bool {
70         return len(c.ProxyAddress) > 0
71 }
72
73 type Client struct {
74         idsCache      *govpn.MACCache
75         tap           *govpn.TAP
76         knownPeers    sync.Map
77         statsPort     net.Listener
78         timeouted     chan struct{}
79         rehandshaking chan struct{}
80         termination   chan struct{}
81         firstUpCall   bool
82         termSignal    chan os.Signal
83         config        Configuration
84
85         // Error channel receives any kind of routine errors
86         Error chan error
87 }
88
89 func (c *Client) MainCycle() {
90         var err error
91         c.tap, err = govpn.TAPListen(c.config.InterfaceName, c.config.MTU)
92         if err != nil {
93                 c.Error <- fmt.Errorf("Can not listen on TUN/TAP interface: %s", err.Error())
94                 return
95         }
96
97         if len(c.config.StatsAddress) > 0 {
98                 c.statsPort, err = net.Listen("tcp", c.config.StatsAddress)
99                 if err != nil {
100                         c.Error <- fmt.Errorf("Can't listen on stats port: %s", err.Error())
101                         return
102                 }
103                 go govpn.StatsProcessor(c.statsPort, &c.knownPeers)
104         }
105
106 MainCycle:
107         for {
108                 c.timeouted = make(chan struct{})
109                 c.rehandshaking = make(chan struct{})
110                 c.termination = make(chan struct{})
111                 switch c.config.Protocol {
112                 case ProtocolUDP:
113                         go c.startUDP()
114                 case ProtocolTCP:
115                         if c.config.isProxy() {
116                                 go c.proxyTCP()
117                         } else {
118                                 go c.startTCP()
119                         }
120                 }
121                 select {
122                 case <-c.termSignal:
123                         govpn.BothPrintf(`[finish remote="%s"]`, c.config.RemoteAddress)
124                         c.termination <- struct{}{}
125                         // empty value signals that everything is fine
126                         c.Error <- nil
127                         break MainCycle
128                 case <-c.timeouted:
129                         if c.config.NoReconnect {
130                                 break MainCycle
131                         }
132                         govpn.BothPrintf(`[sleep seconds="%d"]`, c.config.Peer.Timeout/time.Second)
133                         time.Sleep(c.config.Peer.Timeout)
134                 case <-c.rehandshaking:
135                 }
136                 close(c.timeouted)
137                 close(c.rehandshaking)
138                 close(c.termination)
139         }
140         if _, err = govpn.ScriptCall(
141                 c.config.DownPath,
142                 c.config.InterfaceName,
143                 c.config.RemoteAddress,
144         ); err != nil {
145                 c.Error <- err
146         }
147 }
148
149 func NewClient(conf Configuration, verifier *govpn.Verifier, termSignal chan os.Signal) *Client {
150         client := Client{
151                 idsCache:    govpn.NewMACCache(),
152                 firstUpCall: true,
153                 config:      conf,
154                 termSignal:  termSignal,
155                 Error:       make(chan error, 1),
156         }
157         confs := map[govpn.PeerID]*govpn.PeerConf{*verifier.ID: conf.Peer}
158         client.idsCache.Update(&confs)
159         return &client
160 }