diff options
Diffstat (limited to 'vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/client.go')
-rw-r--r-- | vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/client.go | 303 |
1 files changed, 0 insertions, 303 deletions
diff --git a/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/client.go b/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/client.go deleted file mode 100644 index 7768004..0000000 --- a/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/client.go +++ /dev/null @@ -1,303 +0,0 @@ -package openvpn - -import ( - "bytes" - "fmt" - "io" - "net" - "strconv" - "time" - - "github.com/apparentlymart/go-openvpn-mgmt/demux" -) - -var newline = []byte{'\n'} -var successPrefix = []byte("SUCCESS: ") -var errorPrefix = []byte("ERROR: ") -var endMessage = []byte("END") - -type MgmtClient struct { - wr io.Writer - replies <-chan []byte -} - -// NewClient creates a new MgmtClient that communicates via the given -// io.ReadWriter and emits events on the given channel. -// -// eventCh should be a buffered channel with a sufficient buffer depth -// such that it cannot be filled under the expected event volume. Event -// volume depends on which events are enabled and how they are configured; -// some of the event-enabling functions have further discussion how frequently -// events are likely to be emitted, but the caller should also factor in -// how long its own event *processing* will take, since slow event -// processing will create back-pressure that could cause this buffer to -// fill faster. -// -// It probably goes without saying given the previous paragraph, but the -// caller *must* constantly read events from eventCh to avoid its buffer -// becoming full. Events and replies are received on the same channel -// from OpenVPN, so if writing to eventCh blocks then this will also block -// responses from the client's various command methods. -// -// eventCh will be closed to signal the closing of the client connection, -// whether due to graceful shutdown or to an error. In the case of error, -// a FatalEvent will be emitted on the channel as the last event before it -// is closed. Connection errors may also concurrently surface as error -// responses from the client's various command methods, should an error -// occur while we await a reply. -func NewClient(conn io.ReadWriter, eventCh chan<- Event) *MgmtClient { - replyCh := make(chan []byte) - rawEventCh := make(chan []byte) // not buffered because eventCh should be - - go demux.Demultiplex(conn, replyCh, rawEventCh) - - // Get raw events and upgrade them into proper event types before - // passing them on to the caller's event channel. - go func() { - for raw := range rawEventCh { - eventCh <- upgradeEvent(raw) - } - close(eventCh) - }() - - return &MgmtClient{ - // replyCh acts as the reader for our ReadWriter, so we only - // need to retain the io.Writer for it, so we can send commands. - wr: conn, - replies: replyCh, - } -} - -// Dial is a convenience wrapper around NewClient that handles the common -// case of opening an TCP/IP socket to an OpenVPN management port and creating -// a client for it. -// -// See the NewClient docs for discussion about the requirements for eventCh. -// -// OpenVPN will create a suitable management port if launched with the -// following command line option: -// -// --management <ipaddr> <port> -// -// Address may an IPv4 address, an IPv6 address, or a hostname that resolves -// to either of these, followed by a colon and then a port number. -// -// When running on Unix systems it's possible to instead connect to a Unix -// domain socket. To do this, pass an absolute path to the socket as -// the target address, having run OpenVPN with the following options: -// -// --management /path/to/socket unix -// -func Dial(addr string, eventCh chan<- Event) (*MgmtClient, error) { - proto := "tcp" - if len(addr) > 0 && addr[0] == '/' { - proto = "unix" - } - conn, err := net.Dial(proto, addr) - if err != nil { - return nil, err - } - - return NewClient(conn, eventCh), nil -} - -// HoldRelease instructs OpenVPN to release any management hold preventing -// it from proceeding, but to retain the state of the hold flag such that -// the daemon will hold again if it needs to reconnect for any reason. -// -// OpenVPN can be instructed to activate a management hold on startup by -// running it with the following option: -// -// --management-hold -// -// Instructing OpenVPN to hold gives your client a chance to connect and -// do any necessary configuration before a connection proceeds, thus avoiding -// the problem of missed events. -// -// When OpenVPN begins holding, or when a new management client connects while -// a hold is already in effect, a HoldEvent will be emitted on the event -// channel. -func (c *MgmtClient) HoldRelease() error { - _, err := c.simpleCommand("hold release") - return err -} - -// SetStateEvents either enables or disables asynchronous events for changes -// in the OpenVPN connection state. -// -// When enabled, a StateEvent will be emitted from the event channel each -// time the connection state changes. See StateEvent for more information -// on the event structure. -func (c *MgmtClient) SetStateEvents(on bool) error { - var err error - if on { - _, err = c.simpleCommand("state on") - } else { - _, err = c.simpleCommand("state off") - } - return err -} - -// SetEchoEvents either enables or disables asynchronous events for "echo" -// commands sent from a remote server to our managed OpenVPN client. -// -// When enabled, an EchoEvent will be emitted from the event channel each -// time the server sends an echo command. See EchoEvent for more information. -func (c *MgmtClient) SetEchoEvents(on bool) error { - var err error - if on { - _, err = c.simpleCommand("echo on") - } else { - _, err = c.simpleCommand("echo off") - } - return err -} - -// SetByteCountEvents either enables or disables ongoing asynchronous events -// for information on OpenVPN bandwidth usage. -// -// When enabled, a ByteCountEvent will be emitted at given time interval, -// (which may only be whole seconds) describing how many bytes have been -// transferred in each direction See ByteCountEvent for more information. -// -// Set the time interval to zero in order to disable byte count events. -func (c *MgmtClient) SetByteCountEvents(interval time.Duration) error { - msg := fmt.Sprintf("bytecount %d", int(interval.Seconds())) - _, err := c.simpleCommand(msg) - return err -} - -// SendSignal sends a signal to the OpenVPN process via the management -// channel. In effect this causes the OpenVPN process to send a signal to -// itself on our behalf. -// -// OpenVPN accepts a subset of the usual UNIX signal names, including -// "SIGHUP", "SIGTERM", "SIGUSR1" and "SIGUSR2". See the OpenVPN manual -// page for the meaning of each. -// -// Behavior is undefined if the given signal name is not entirely uppercase -// letters. In particular, including newlines in the string is likely to -// cause very unpredictable behavior. -func (c *MgmtClient) SendSignal(name string) error { - msg := fmt.Sprintf("signal %q", name) - _, err := c.simpleCommand(msg) - return err -} - -// LatestState retrieves the most recent StateEvent from the server. This -// can either be used to poll the state or it can be used to determine the -// initial state after calling SetStateEvents(true) but before the first -// state event is delivered. -func (c *MgmtClient) LatestState() (*StateEvent, error) { - err := c.sendCommand([]byte("state")) - if err != nil { - return nil, err - } - - payload, err := c.readCommandResponsePayload() - if err != nil { - return nil, err - } - - if len(payload) != 1 { - return nil, fmt.Errorf("Malformed OpenVPN 'state' response") - } - - return &StateEvent{ - body: payload[0], - }, nil -} - -// Pid retrieves the process id of the connected OpenVPN process. -func (c *MgmtClient) Pid() (int, error) { - raw, err := c.simpleCommand("pid") - if err != nil { - return 0, err - } - - if !bytes.HasPrefix(raw, []byte("pid=")) { - return 0, fmt.Errorf("malformed response from OpenVPN") - } - - pid, err := strconv.Atoi(string(raw[4:])) - if err != nil { - return 0, fmt.Errorf("error parsing pid from OpenVPN: %s", err) - } - - return pid, nil -} - -func (c *MgmtClient) sendCommand(cmd []byte) error { - _, err := c.wr.Write(cmd) - if err != nil { - return err - } - _, err = c.wr.Write(newline) - return err -} - -// sendCommandPayload can be called after sendCommand for -// commands that expect a multi-line input payload. -// -// The buffer given in 'payload' *must* end with a newline, -// or else the protocol will be broken. -func (c *MgmtClient) sendCommandPayload(payload []byte) error { - _, err := c.wr.Write(payload) - if err != nil { - return err - } - _, err = c.wr.Write(endMessage) - if err != nil { - return err - } - _, err = c.wr.Write(newline) - return err -} - -func (c *MgmtClient) readCommandResult() ([]byte, error) { - reply, ok := <-c.replies - if !ok { - return nil, fmt.Errorf("connection closed while awaiting result") - } - - if bytes.HasPrefix(reply, successPrefix) { - result := reply[len(successPrefix):] - return result, nil - } - - if bytes.HasPrefix(reply, errorPrefix) { - message := reply[len(errorPrefix):] - return nil, ErrorFromServer(message) - } - - return nil, fmt.Errorf("malformed result message") -} - -func (c *MgmtClient) readCommandResponsePayload() ([][]byte, error) { - lines := make([][]byte, 0, 10) - - for { - line, ok := <-c.replies - if !ok { - // We'll give the caller whatever we got before the connection - // closed, in case it's useful for debugging. - return lines, fmt.Errorf("connection closed before END recieved") - } - - if bytes.Equal(line, endMessage) { - break - } - - lines = append(lines, line) - } - - return lines, nil -} - -func (c *MgmtClient) simpleCommand(cmd string) ([]byte, error) { - err := c.sendCommand([]byte(cmd)) - if err != nil { - return nil, err - } - return c.readCommandResult() -} |