package main import ( "bufio" "errors" "fmt" "net" "strconv" "sync" "github.com/pyke369/golang-support/rcache" ) type OpenVpnPassword struct { User string Pass string } type OpenVpnSrv struct { Remote string `json:"active-vpn"` Status string `json:"status"` Provider string `json:"provider"` Identifier string `json:"identifier"` chanHold chan bool chanPass chan OpenVpnPassword m sync.RWMutex ret chan []string buf *bufio.ReadWriter mgt *OpenVpnMgt hold bool authCache *OpenVpnPassword } func (v *OpenVpnSrv) Lock() { v.m.Lock() } func (v *OpenVpnSrv) Unlock() { v.m.Unlock() } func NewOpenVpnSrv(conn net.Conn, mgt *OpenVpnMgt) *OpenVpnSrv { return &OpenVpnSrv{ buf: bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)), chanHold: make(chan bool), chanPass: make(chan OpenVpnPassword), ret: make(chan []string), mgt: mgt, hold: false, } } // send a command to the server. Set the channel to receive the response func (v *OpenVpnSrv) sendCommand(msg []string) (error, []string) { v.Lock() for _, line := range msg { if _, err := v.buf.WriteString(line + "\r\n"); err != nil { v.Unlock() return err, nil } } if err := v.buf.Flush(); err != nil { return err, nil } v.Unlock() // wait for the response ret := <-v.ret return nil, ret } func (v *OpenVpnSrv) Signal(signal string) error { for _, valid := range []string{"SIGHUP", "SIGTERM"} { if signal == valid { err, _ := v.sendCommand([]string{fmt.Sprintf("signal %s", signal)}) return err } } return errors.New("unknown signal") } func (v *OpenVpnSrv) GetPid() error { err, infos := v.sendCommand([]string{"pid"}) if err != nil { v.mgt.Debug(err) return err } pidRegexp := rcache.Get("^SUCCESS: pid=([0-9]+)$") for _, line := range infos { match := pidRegexp.FindStringSubmatch(line) if len(match) == 0 { continue } if len(match) == 2 { pid, err := strconv.Atoi(match[1]) if err == nil { v.mgt.SetPid(v, pid) v.mgt.Debug("Found PID", pid) } return err } } v.mgt.Debug("Can't find PID") return errors.New("Can't find PID") } func (v *OpenVpnSrv) GetEcho() { err, infos := v.sendCommand([]string{"echo all"}) if err != nil { return } echoRegexp := rcache.Get("^[0-9]+,([A-Za-z0-9-]*):([A-Za-z0-9-]*)$") for _, line := range infos { match := echoRegexp.FindStringSubmatch(line) if len(match) == 0 { continue } switch match[1] { case "vpnidentifier": v.Identifier = match[2] case "vpnprovider": if err := v.mgt.getServerList(match[2]); err != nil { v.mgt.Debug(err) continue } v.Provider = match[2] } } } func (v *OpenVpnSrv) Response(response []string) { v.Lock() v.ret <- response v.Unlock() } func (v *OpenVpnSrv) GetLine() (string, error) { return v.buf.ReadString('\n') } func (v *OpenVpnSrv) ValidRemote(server, port, proto string) { if v.Remote != "" { v.sendCommand([]string{fmt.Sprintf("remote MOD %s %s %s", v.Remote, port, proto)}) v.Status = "Connected" return } v.Remote = server v.sendCommand([]string{"remote ACCEPT"}) } func (v *OpenVpnSrv) Version() (error, []string) { return v.sendCommand([]string{"version"}) } func (v *OpenVpnSrv) SetRemote(server string) error { // already the active server, do nothing if v.Remote == server { return nil } if v.Remote != "" { v.Remote = server v.Signal("SIGHUP") v.Status = "Reloaded" } v.Remote = server // release Hold if necessary v.ReleaseHold() return nil } func (v *OpenVpnSrv) NeedPassword(line string) { v.mgt.Debug(line) v.Status = "Need Password" switch line { case ">PASSWORD:Need 'Auth' username/password": v.Status = "Need Password" case ">PASSWORD:Verification Failed: 'Auth'": v.authCache = nil v.Status = "Auth Failed" return } if v.authCache == nil { ident := <-v.chanPass v.authCache = &ident } switch line { case ">PASSWORD:Need 'Auth' username/password": v.sendCommand([]string{fmt.Sprintf("username \"Auth\" %s", v.authCache.User)}) v.sendCommand([]string{fmt.Sprintf("password \"Auth\" %s", v.authCache.Pass)}) } } func (v *OpenVpnSrv) AuthUserPass(user, pass string) { auth := OpenVpnPassword{user, pass} v.authCache = &auth if v.Status == "Need Password" { v.Status = "Authenticate" v.chanPass <- auth } } func (v *OpenVpnSrv) waitForRelase() { v.Status = "Hold" if v.hold { return } v.hold = true <-v.chanHold v.sendCommand([]string{"hold release"}) v.sendCommand([]string{"hold off"}) } func (v *OpenVpnSrv) ReleaseHold() { if !v.hold { return } v.hold = false v.chanHold <- true v.Status = "Waiting for connexion" }