diff options
Diffstat (limited to 'vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/server.go')
-rw-r--r-- | vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/server.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/server.go b/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/server.go new file mode 100644 index 0000000..d7defd4 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-openvpn-mgmt/openvpn/server.go @@ -0,0 +1,172 @@ +package openvpn + +import ( + "net" + "time" +) + +// MgmtListener accepts incoming connections from OpenVPN. +// +// The primary way to instantiate this type is via the function Listen. +// See its documentation for more information. +type MgmtListener struct { + l net.Listener +} + +// NewMgmtListener constructs a MgmtListener from an already-established +// net.Listener. In most cases it will be more convenient to use +// the function Listen. +func NewMgmtListener(l net.Listener) *MgmtListener { + return &MgmtListener{l} +} + +// Listen opens a listen port and awaits incoming connections from OpenVPN +// processes. +// +// OpenVPN will behave in this manner when launched with the following options: +// +// --management ipaddr port --management-client +// +// Note that in this case the terminology is slightly confusing, since from +// the standpoint of TCP/IP it is OpenVPN that is the client and our program +// that is the server, but once the connection is established the channel +// is indistinguishable from the situation where OpenVPN exposed a management +// *server* and we connected to it. Thus we still refer to our program as +// the "client" and OpenVPN as the "server" once the connection is established. +// +// When running on Unix systems it's possible to instead listen on a Unix +// domain socket. To do this, pass an absolute path to the socket as +// the listen address, and then run OpenVPN with the following options: +// +// --management /path/to/socket unix --management-client +// +func Listen(laddr string) (*MgmtListener, error) { + proto := "tcp" + if len(laddr) > 0 && laddr[0] == '/' { + proto = "unix" + } + listener, err := net.Listen(proto, laddr) + if err != nil { + return nil, err + } + + return NewMgmtListener(listener), nil +} + +// Accept waits for and returns the next connection. +func (l *MgmtListener) Accept() (*IncomingConn, error) { + conn, err := l.l.Accept() + if err != nil { + return nil, err + } + + return &IncomingConn{conn}, nil +} + +// Close closes the listener. Any blocked Accept operations +// will be blocked and each will return an error. +func (l *MgmtListener) Close() error { + return l.l.Close() +} + +// Addr returns the listener's network address. +func (l *MgmtListener) Addr() net.Addr { + return l.l.Addr() +} + +// Serve will await new connections and call the given handler +// for each. +// +// Serve does not return unless the listen port is closed; a non-nil +// error is always returned. +func (l *MgmtListener) Serve(handler IncomingConnHandler) error { + defer l.Close() + + var tempDelay time.Duration + + for { + incoming, err := l.Accept() + if err != nil { + if ne, ok := err.(net.Error); ok && ne.Temporary() { + if tempDelay == 0 { + tempDelay = 5 * time.Millisecond + } else { + tempDelay *= 2 + } + if max := 1 * time.Second; tempDelay > max { + tempDelay = max + } + + // Wait a while before we try again. + time.Sleep(tempDelay) + continue + } else { + // Listen socket is permanently closed or errored, + // so it's time for us to exit. + return err + } + } + + // always reset our retry delay once we successfully read + tempDelay = 0 + + go handler.ServeOpenVPNMgmt(*incoming) + } +} + +type IncomingConn struct { + conn net.Conn +} + +// Open initiates communication with the connected OpenVPN process, +// and establishes the channel on which events will be delivered. +// +// See the documentation for NewClient for discussion about the requirements +// for eventCh. +func (ic IncomingConn) Open(eventCh chan<- Event) *MgmtClient { + return NewClient(ic.conn, eventCh) +} + +// Close abruptly closes the socket connected to the OpenVPN process. +// +// This is a rather abrasive way to close the channel, intended for rejecting +// unwanted incoming clients that may or may not speak the OpenVPN protocol. +// +// Once communication is accepted and established, it is generally better +// to close the connection gracefully using commands on the client returned +// from Open. +func (ic IncomingConn) Close() error { + return ic.conn.Close() +} + +type IncomingConnHandler interface { + ServeOpenVPNMgmt(IncomingConn) +} + +// IncomingConnHandlerFunc is an adapter to allow the use of ordinary +// functions as connection handlers. +// +// Given a function with the appropriate signature, IncomingConnHandlerFunc(f) +// is an IncomingConnHandler that calls f. +type IncomingConnHandlerFunc func(IncomingConn) + +func (f IncomingConnHandlerFunc) ServeOpenVPNMgmt(i IncomingConn) { + f(i) +} + +// ListenAndServe creates a MgmtListener for the given listen address +// and then calls AcceptAndServe on it. +// +// This is just a convenience wrapper. See the AcceptAndServe method for +// more details. Just as with AcceptAndServe, this function does not return +// except on error; in addition to the error cases handled by AcceptAndServe, +// this function may also fail if the listen socket cannot be established +// in the first place. +func ListenAndServe(laddr string, handler IncomingConnHandler) error { + listener, err := Listen(laddr) + if err != nil { + return err + } + + return listener.Serve(handler) +} |