package main import ( "bufio" "errors" "fmt" "io" "log" "net" "strings" "sync" ) // Server represents the server type OpenVpnMgt struct { Port string buf *bufio.ReadWriter connected bool m sync.RWMutex ret chan []string } // NewServer returns a pointer to a new server func NewVPNServer(port string) *OpenVpnMgt { return &OpenVpnMgt{ Port: port, ret: make(chan []string), } } // Run starts a the server func (s *OpenVpnMgt) Run() { // Resolve the passed port into an address addrs, err := net.ResolveTCPAddr("tcp", s.Port) if err != nil { return } // start listening to client connections listener, err := net.ListenTCP("tcp", addrs) if err != nil { fmt.Println(err) } // Infinite loop since we dont want the server to shut down for { // Accept the incomming connections conn, err := listener.Accept() if err != nil { // continue accepting connection even if an error occurs (if error occurs dont shut down) continue } // run it as a go routine to allow multiple clients to connect at the same time go s.handleConn(conn) } } func (s *OpenVpnMgt) sendCommand(msg []string) (error, []string) { if !s.connected { return errors.New("No openvpn server present"), nil } for _, line := range msg { if _, err := s.buf.WriteString(line + "\r\n"); err != nil { return err, nil } } if err := s.buf.Flush(); err != nil { return err, nil } // wait for the response ret := <-s.ret return nil, ret } func (s *OpenVpnMgt) Help() (error, []string) { err, msg := s.sendCommand([]string{"help"}) if err != nil { return err, nil } return nil, msg } func (s *OpenVpnMgt) Version() (error, []string) { err, msg := s.sendCommand([]string{"version"}) if err != nil { return err, nil } return nil, msg } func (s *OpenVpnMgt) ClientDisconnect(line string) { msg := <-s.ret log.Println(msg) } func (s *OpenVpnMgt) ClientConnect(line string) { msg := <-s.ret log.Println(msg) } func (s *OpenVpnMgt) handleConn(conn net.Conn) { defer conn.Close() // we don't want multiple connexions, only one openvpn server at a time s.m.Lock() if s.connected { conn.Write([]byte("Sorry, only one server allowed\n")) s.m.Unlock() return } s.connected = true s.m.Unlock() // we store the buffer pointer in the struct, to be accessed from other methods s.buf = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) // most response are multilined, use response to concatenate them response := []string{} for { line, err := s.buf.ReadString('\n') // manage basic errors switch { case err == io.EOF: log.Println("Reached EOF - close this connection.\n") s.connected = false return case err != nil: log.Println("Error reading line. Got: '"+line+"'\n", err) s.connected = false return } line = strings.Trim(line, "\n\r ") switch { // a new openvpn server is connected case strings.HasPrefix(line, ">INFO"): // new bloc for a disconnect event. // We start the receiving handler, which will wait for the Channel message case strings.HasPrefix(line, ">CLIENT:DISCONNECT"): go s.ClientDisconnect(line) // new bloc for a connect event. // We start the receiving handler, which will wait for the Channel message case strings.HasPrefix(line, ">CLIENT:CONNECT"): go s.ClientConnect(line) // write the cumulated lines into the channel to the current handler case strings.HasPrefix(line, "END") || strings.HasPrefix(line, ">CLIENT:ENV,END"): s.ret <- response response = nil default: response = append(response, line) } log.Print(line) } }