working prototype

This commit is contained in:
Xavier Henner
2019-08-16 17:23:19 +02:00
parent a82131c8c8
commit 9eff9ce803
23 changed files with 1029 additions and 303 deletions

View File

@@ -6,9 +6,9 @@ import (
"io"
"log"
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/pyke369/golang-support/rcache"
)
@@ -18,8 +18,9 @@ type OpenVpnMgt struct {
port string
m sync.RWMutex
debug bool
VpnRemotes []string `json:"remotes"`
vpnServers map[int]*OpenVpnSrv `json:"sessions"`
VpnRemotes map[string]*[]string `json:"remotes"`
VpnServers map[int]*OpenVpnSrv `json:"sessions"`
LastChange time.Time `json:"last_change"`
}
// NewServer returns a pointer to a new server
@@ -27,7 +28,8 @@ func NewVPNServer(port string, debug bool) *OpenVpnMgt {
return &OpenVpnMgt{
port: port,
debug: debug,
vpnServers: make(map[int]*OpenVpnSrv),
VpnServers: make(map[int]*OpenVpnSrv),
VpnRemotes: make(map[string]*[]string),
}
}
@@ -39,15 +41,18 @@ func (s *OpenVpnMgt) Unlock() {
s.m.Unlock()
}
func (s *OpenVpnMgt) Change() {
s.LastChange = time.Now().Round(time.Second)
}
func (s *OpenVpnMgt) Debug(v ...interface{}) {
if s.debug {
log.Println(v...)
}
}
// Run starts a the server
func (s *OpenVpnMgt) Run() {
// get the endpoint list
if err := s.getServerList(); err != nil {
log.Println(err)
return
}
// Resolve the passed port into an address
addrs, err := net.ResolveTCPAddr("tcp", s.port)
if err != nil {
@@ -73,38 +78,82 @@ func (s *OpenVpnMgt) Run() {
}
}
func (s *OpenVpnMgt) GetSession(remote int) (error, *OpenVpnSrv) {
if vpnServer, ok := s.vpnServers[remote]; ok {
return nil, vpnServer
func (s *OpenVpnMgt) GetSession(pid int) (error, *OpenVpnSrv) {
if openvpn, ok := s.VpnServers[pid]; ok {
return nil, openvpn
}
return errors.New(fmt.Sprintf("unknown session %d", vpnServers)), nil
return errors.New(fmt.Sprintf("unknown session %d", pid)), nil
}
func (s *OpenVpnMgt) SetRemote(server string, remote int) error {
func (s *OpenVpnMgt) Restart(pid int) error {
// check if the session is valid
err, session := s.GetSession(remote)
err, session := s.GetSession(pid)
if err != nil {
return err
}
session.Signal("SIGHUP")
return nil
}
func (s *OpenVpnMgt) Kill(pid int) error {
// check if the session is valid
err, session := s.GetSession(pid)
if err != nil {
return err
}
session.Signal("SIGTERM")
return nil
}
func (s *OpenVpnMgt) SetRemote(server string, pid int) error {
// check if the session is valid
err, session := s.GetSession(pid)
if err != nil {
return err
}
for _, r := range s.VpnRemotes {
if session.Provider == "" {
return errors.New("No server list for this config")
}
if _, ok := s.VpnRemotes[session.Provider]; !ok {
return errors.New("No server list for this provider")
}
for _, r := range *(s.VpnRemotes[session.Provider]) {
if r != server {
continue
}
return session.SetRemote(server)
}
return errors.New(fmt.Sprintf("unknown remote %s", server))
return errors.New(fmt.Sprintf("unknown session %s", server))
}
func (s *OpenVpnMgt) SetPid(openvpn *OpenVpnSrv, pid int) {
s.Lock()
defer s.Unlock()
s.VpnServers[pid] = openvpn
}
func (s *OpenVpnMgt) Remove(openvpn *OpenVpnSrv) {
for pid, v := range s.VpnServers {
if v == openvpn {
delete(s.VpnServers, pid)
}
}
}
// send the version command on all vpn servers. Kind of useless
func (s *OpenVpnMgt) Version() (error, map[int][]string) {
var err error
ret := make(map[int][]string)
for remote, srv := range s.vpnServers {
for pid, srv := range s.VpnServers {
err, msg := srv.Version()
if err == nil {
ret[remote] = msg
ret[pid] = msg
}
}
return err, ret
@@ -113,19 +162,17 @@ func (s *OpenVpnMgt) Version() (error, map[int][]string) {
// main loop for a given openvpn server
func (s *OpenVpnMgt) handleConn(conn net.Conn) {
remote := conn.RemoteAddr().String()
pidRegexp := rcache.Get("^SUCCESS: pid=([0-9]+)$")
// >REMOTE:vpn.example.com,1194,udp
remoteRegexp := rcache.Get("^>REMOTE:(.*),([0-9]*),(.*)$")
defer conn.Close()
vpnServer := NewOpenVpnSrv(conn)
openvpn := NewOpenVpnSrv(conn, s)
defer s.Remove(openvpn)
// most response are multilined, use response to concatenate them
response := []string{}
// remove bogus clients
line, err := vpnServer.session.GetLine()
line, err := openvpn.GetLine()
if err != nil {
log.Println(err)
return
@@ -137,24 +184,27 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
log.Printf("Valid openvpn connected from %s\n", remote)
go session.sendCommand([]string{"pid"})
go openvpn.GetEcho()
go openvpn.GetPid()
s.Change()
defer s.Change()
for {
line, err := session.GetLine()
line, err := openvpn.GetLine()
// manage basic errors
switch {
case err == io.EOF:
log.Println("Reached EOF - close this connection.\n")
log.Println("Reached EOF - close this connection")
return
case err != nil:
log.Println("Error reading line. Got: '"+line+"'\n", err)
log.Printf("Error reading line. Got: '"+line+"'\n", err)
return
}
line = strings.Trim(line, "\n\r ")
if s.debug && strings.Index(line, "password") == -1 {
log.Print(line)
if strings.Index(line, "password") == -1 {
s.Debug(line)
}
// manage exit commands
@@ -165,22 +215,13 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
}
}
// get the PID
match := pidRegexp.FindStringSubmatch(line)
if len(match) == 2 {
pid, _ := strconv.Atoi(match[1])
s.Lock()
s.vpnServers[pid] = vpnServer
s.Unlock()
defer delete(s.vpnServers, pid)
}
// manage all "terminator" lines
for _, terminator := range []string{"END", ">CLIENT:ENV,END", "SUCCESS", "ERROR"} {
if strings.HasPrefix(line, terminator) {
vpnServer.Response(response)
openvpn.Response(append(response, line))
response = nil
line = ""
s.Change()
break
}
}
@@ -188,11 +229,10 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
remoteMatch := remoteRegexp.FindStringSubmatch(line)
switch {
// command successfull, we can ignore
case strings.HasPrefix(line, ">SUCCESS: client-deny command succeeded"):
case strings.HasPrefix(line, ">HOLD"):
go vpnServer.waitForRelase()
go openvpn.waitForRelase()
case len(remoteMatch) > 0:
go vpnServer.ValidRemote(remoteMatch[1], remoteMatch[2], remoteMatch[3])
go openvpn.ValidRemote(remoteMatch[1], remoteMatch[2], remoteMatch[3])
default:
response = append(response, line)
}