X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=govpn.go;h=4deab7bc017f261b499998f0f42759b38887a1b5;hb=refs%2Fheads%2Fdevelop;hp=a6db5e34438c27f8d0d13789d7792ec4b6cb42bb;hpb=6ae8a1b4d26b31f08773b4525b01d82efcc93544;p=govpn.git diff --git a/govpn.go b/govpn.go index a6db5e3..4deab7b 100644 --- a/govpn.go +++ b/govpn.go @@ -1,11 +1,10 @@ -/* -govpn -- high-performance secure virtual private network daemon -Copyright (C) 2014 Sergey Matveev +// Simple secure, DPI/censorship-resistant free software VPN daemon. +package govpn -This program is free software: you can redistribute it and/or modify +const ( + Warranty = `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. +the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,254 +12,5 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -package main - -import ( - "encoding/binary" - "encoding/hex" - "flag" - "fmt" - "log" - "net" - "time" - - "code.google.com/p/go.crypto/poly1305" - "code.google.com/p/go.crypto/salsa20" - "github.com/chon219/water" -) - -const ( - NonceSize = 8 - AliveTimeout = time.Second * 90 - KeySize = 32 - // S20BS is Salsa20's internal blocksize in bytes - S20BS = 64 -) - -type Peer struct { - addr *net.UDPAddr - lastPing time.Time - key *[KeySize]byte // encryption key - nonceOur uint64 // nonce for our messages - nonceRecv uint64 // latest received nonce from remote peer -} - -func (p *Peer) IsAlive() bool { - if (p == nil) || (p.lastPing.Add(AliveTimeout).Before(time.Now())) { - return false - } - return true -} - -func (p *Peer) SetAlive() { - p.lastPing = time.Now() -} - -type UDPPkt struct { - addr *net.UDPAddr - size int -} - -var ( - remoteAddr = flag.String("remote", "", "Remote server address") - bindAddr = flag.String("bind", "", "Bind to address") - ifaceName = flag.String("iface", "tap0", "TAP network interface") - keyHex = flag.String("key", "", "Authentication key") - mtu = flag.Int("mtu", 1500, "MTU") - verbose = flag.Bool("v", false, "Increase verbosity") +along with this program. If not, see .` ) - -func main() { - flag.Parse() - log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) - - // Key decoding - if len(*keyHex) != 64 { - panic("Key is required argument (64 hex characters)") - } - keyDecoded, err := hex.DecodeString(*keyHex) - if err != nil { - panic(err) - } - key := new([KeySize]byte) - copy(key[:], keyDecoded) - - // Interface listening - maxIfacePktSize := *mtu - poly1305.TagSize - NonceSize - log.Println("Max MTU", maxIfacePktSize, "on interface", *ifaceName) - iface, err := water.NewTAP(*ifaceName) - if err != nil { - panic(err) - } - ethBuf := make([]byte, maxIfacePktSize) - ethSink := make(chan int) - ethSinkReady := make(chan bool) - go func() { - for { - <- ethSinkReady - n, err := iface.Read(ethBuf) - if err != nil { - panic(err) - } - ethSink <- n - } - }() - ethSinkReady <- true - - // Network address parsing - if (len(*bindAddr) > 1 && len(*remoteAddr) > 1) || - (len(*bindAddr) == 0 && len(*remoteAddr) == 0) { - panic("Either -bind or -remote must be specified only") - } - var conn *net.UDPConn - var remote *net.UDPAddr - serverMode := false - bindTo := "0.0.0.0:0" - - if len(*bindAddr) > 1 { - bindTo = *bindAddr - serverMode = true - } - - bind, err := net.ResolveUDPAddr("udp", bindTo) - if err != nil { - panic(err) - } - conn, err = net.ListenUDP("udp", bind) - if err != nil { - panic(err) - } - - if len(*remoteAddr) > 1 { - remote, err = net.ResolveUDPAddr("udp", *remoteAddr) - if err != nil { - panic(err) - } - } - - udpBuf := make([]byte, *mtu) - udpSink := make(chan UDPPkt) - udpSinkReady := make(chan bool) - go func(conn *net.UDPConn) { - for { - <- udpSinkReady - n, addr, err := conn.ReadFromUDP(udpBuf) - if err != nil { - fmt.Print("B") - } - udpSink <- UDPPkt{addr, n} - } - }(conn) - udpSinkReady <- true - - // Process packets - var udpPkt UDPPkt - var udpPktData []byte - var ethPktSize int - var addr string - var peer Peer - var p *Peer - - states := make(map[string]*Handshake) - nonce := make([]byte, NonceSize) - keyAuth := new([KeySize]byte) - tag := new([poly1305.TagSize]byte) - buf := make([]byte, *mtu+S20BS) - emptyKey := make([]byte, KeySize) - ethPkt := make([]byte, maxIfacePktSize) - udpPktDataBuf := make([]byte, *mtu) - - if !serverMode { - log.Println("starting handshake with", *remoteAddr) - states[remote.String()] = HandshakeStart(conn, remote, key) - } - - for { - select { - case udpPkt = <-udpSink: - copy(udpPktDataBuf, udpBuf[:udpPkt.size]) - udpSinkReady <- true - udpPktData = udpPktDataBuf[:udpPkt.size] - if isValidHandshakePkt(udpPktData) { - addr = udpPkt.addr.String() - state, exists := states[addr] - if serverMode { - if !exists { - state = &Handshake{addr: udpPkt.addr} - states[addr] = state - } - p = state.Server(conn, key, udpPktData) - } else { - if !exists { - fmt.Print("[HS?]") - continue - } - p = state.Client(conn, key, udpPktData) - } - if p != nil { - fmt.Print("[HS-OK]") - peer = *p - delete(states, addr) - } - continue - } - if !peer.IsAlive() { - continue - } - nonceRecv, _ := binary.Uvarint(udpPktData[:8]) - if peer.nonceRecv >= nonceRecv { - fmt.Print("R") - continue - } - copy(buf[:KeySize], emptyKey) - copy(tag[:], udpPktData[udpPkt.size-poly1305.TagSize:]) - copy(buf[S20BS:], udpPktData[NonceSize:udpPkt.size-poly1305.TagSize]) - salsa20.XORKeyStream( - buf[:S20BS+udpPkt.size-poly1305.TagSize], - buf[:S20BS+udpPkt.size-poly1305.TagSize], - udpPktData[:NonceSize], - peer.key, - ) - copy(keyAuth[:], buf[:KeySize]) - if !poly1305.Verify(tag, udpPktData[:udpPkt.size-poly1305.TagSize], keyAuth) { - fmt.Print("T") - continue - } - peer.nonceRecv = nonceRecv - peer.SetAlive() - if _, err := iface.Write(buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]); err != nil { - log.Println("Error writing to iface") - } - if *verbose { - fmt.Print("r") - } - case ethPktSize = <-ethSink: - if ethPktSize > maxIfacePktSize { - panic("Too large packet on interface") - } - if !peer.IsAlive() { - ethSinkReady <- true - continue - } - copy(ethPkt, ethBuf[:ethPktSize]) - ethSinkReady <- true - peer.nonceOur = peer.nonceOur + 2 - binary.PutUvarint(nonce, peer.nonceOur) - copy(buf[:KeySize], emptyKey) - copy(buf[S20BS:], ethPkt[:ethPktSize]) - salsa20.XORKeyStream(buf, buf, nonce, peer.key) - copy(buf[S20BS-NonceSize:S20BS], nonce) - copy(keyAuth[:], buf[:KeySize]) - dataToSend := buf[S20BS-NonceSize : S20BS+ethPktSize] - poly1305.Sum(tag, dataToSend, keyAuth) - if _, err := conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil { - log.Println("Error sending UDP", err) - } - if *verbose { - fmt.Print("w") - } - } - } -}