mirror of https://0xacab.org/leap/bitmask-vpn
[pkg] vendor update
parent
5d89055928
commit
505316ad1a
|
@ -1,5 +1,5 @@
|
|||
client/client
|
||||
server/server
|
||||
/obfsvpn-client
|
||||
obfsvpn-client
|
||||
*.swp
|
||||
*.swo
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
stages:
|
||||
- test
|
||||
- validate
|
||||
- integration-test
|
||||
|
||||
test:
|
||||
image: golang:alpine
|
||||
|
@ -19,7 +18,7 @@ validate:
|
|||
image: golang:alpine
|
||||
stage: test
|
||||
script: |
|
||||
apk add build-base git jq curl
|
||||
apk add build-base git
|
||||
go version
|
||||
go env
|
||||
|
||||
|
@ -32,20 +31,10 @@ validate:
|
|||
gofmt -s -l . && [ -z "$(gofmt -s -l .)" ]
|
||||
|
||||
# See: https://staticcheck.io/docs/checks
|
||||
staticcheck -checks inherit,ST1016,ST1020,ST1021,ST1022,ST1023 ./...
|
||||
staticcheck -checks inherit,ST1000,ST1016,ST1020,ST1021,ST1022,ST1023 ./...
|
||||
# gosec does not handle modules correctly.
|
||||
# See: https://github.com/securego/gosec/issues/622
|
||||
gosec ./...
|
||||
make check-yawning-obfs4
|
||||
|
||||
go mod tidy
|
||||
git diff --exit-code -- go.mod go.sum
|
||||
|
||||
integration-test:
|
||||
image: debian
|
||||
stage: test
|
||||
script:
|
||||
- apt-get -q update && env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends docker docker.io make
|
||||
- make integration
|
||||
tags:
|
||||
- linux
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
OBFS4_ENDPOINT ?=
|
||||
OBFS4_CERT ?=
|
||||
TAG ?= 0.1
|
||||
OBFSVPN_IMAGE_NAME ?= leap/obfsvpn-test-server
|
||||
OBFSVPN_CLIENT_IMAGE_NAME ?= leap/obfsvpn-test-client
|
||||
|
||||
certs:
|
||||
#curl -k https://black.riseup.net/ca.crt > /tmp/ca.crt
|
||||
curl -k https://black.riseup.net/ca.crt > /tmp/ca.crt
|
||||
curl -k https://api.black.riseup.net/3/cert > /tmp/cert.pem
|
||||
|
||||
build-client:
|
||||
go get ./...
|
||||
CGO_ENABLED=0 go build -o obfsvpn-client ./cmd/client/
|
||||
go build -o obfsvpn-client ./cmd/client/
|
||||
|
||||
run-client:
|
||||
./obfsvpn-client -c ${OBFS4_CERT}
|
||||
|
@ -46,23 +43,3 @@ build-android:
|
|||
test:
|
||||
go test ./...
|
||||
|
||||
check-yawning-obfs4:
|
||||
./scripts/check-obfs4-ver.sh
|
||||
|
||||
obfsvpn-server-container:
|
||||
docker build -t ${OBFSVPN_IMAGE_NAME}:${TAG} -f images/obfsvpn/Dockerfile \
|
||||
. --no-cache
|
||||
|
||||
obfsvpn-client-container:
|
||||
docker build -t ${OBFSVPN_CLIENT_IMAGE_NAME}:${TAG} \
|
||||
-f images/obfsvpn-client/Dockerfile \
|
||||
. --no-cache
|
||||
|
||||
integration: obfsvpn-server-container obfsvpn-client-container
|
||||
docker network create test-net
|
||||
docker run -d --rm --privileged -p 4430:4430/tcp \
|
||||
-v obfsvpn_data:/opt/Dockovpn_data:Z -e OBFS4_HOST=0.0.0.0 \
|
||||
--name obfsvpn-server --net test-net ${OBFSVPN_IMAGE_NAME}:${TAG}
|
||||
docker run --rm --privileged -v obfsvpn_data:/vpn:Z --net test-net \
|
||||
--name obfsvpn-client ${OBFSVPN_CLIENT_IMAGE_NAME}:${TAG}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ var (
|
|||
|
||||
type Client struct {
|
||||
kcp bool
|
||||
SocksAddr string
|
||||
socksAddr string
|
||||
obfs4Cert string
|
||||
server *socks5.Server
|
||||
EventLogger EventLogger
|
||||
|
@ -35,14 +35,14 @@ type EventLogger interface {
|
|||
func NewClient(kcp bool, socksAddr, obfs4Cert string) *Client {
|
||||
return &Client{
|
||||
kcp: kcp,
|
||||
SocksAddr: socksAddr,
|
||||
socksAddr: socksAddr,
|
||||
obfs4Cert: obfs4Cert,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Start() (bool, error) {
|
||||
defer func() {
|
||||
c.log("STOPPED", "")
|
||||
c.log("STOPPED", "", nil)
|
||||
}()
|
||||
|
||||
if c.IsStarted() {
|
||||
|
@ -51,7 +51,7 @@ func (c *Client) Start() (bool, error) {
|
|||
}
|
||||
|
||||
c.server = &socks5.Server{
|
||||
Addr: c.SocksAddr,
|
||||
Addr: c.socksAddr,
|
||||
BindIP: "127.0.0.1",
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ func (c *Client) Start() (bool, error) {
|
|||
|
||||
c.server.Dial = dialer.Dial
|
||||
|
||||
c.log("RUNNING", "[+] Starting socks5 proxy at %s\n", c.SocksAddr)
|
||||
c.log("RUNNING", "[+] Starting socks5 proxy at %s\n", c.socksAddr)
|
||||
if err := c.server.ListenAndServe(); err != nil {
|
||||
c.error("error while listening: %v\n", err)
|
||||
c.server = nil
|
||||
|
@ -98,11 +98,8 @@ func (c *Client) log(state string, format string, a ...interface{}) {
|
|||
c.EventLogger.Log(state, fmt.Sprintf(format, a...))
|
||||
return
|
||||
}
|
||||
if format == "" {
|
||||
log.Print(a...)
|
||||
return
|
||||
}
|
||||
log.Printf(format, a...)
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) error(format string, a ...interface{}) {
|
||||
|
@ -110,10 +107,6 @@ func (c *Client) error(format string, a ...interface{}) {
|
|||
c.EventLogger.Error(fmt.Sprintf(format, a...))
|
||||
return
|
||||
}
|
||||
if format == "" {
|
||||
log.Print(a...)
|
||||
return
|
||||
}
|
||||
log.Printf(format, a...)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
package demux
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
var readErrSynthEvent = []byte("FATAL:Error reading from OpenVPN")
|
||||
|
||||
// Demultiplex reads from the given io.Reader, assumed to be the client
|
||||
// end of an OpenVPN Management Protocol connection, and splits it into
|
||||
// distinct messages from OpenVPN.
|
||||
//
|
||||
// It then writes the raw message buffers into either replyCh or eventCh
|
||||
// depending on whether each message is a reply to a client command or
|
||||
// an asynchronous event notification.
|
||||
//
|
||||
// The buffers written to replyCh are entire raw message lines (without the
|
||||
// trailing newlines), while the buffers written to eventCh are the raw
|
||||
// event strings with the prototcol's leading '>' indicator omitted.
|
||||
//
|
||||
// The caller should usually provide buffered channels of sufficient buffer
|
||||
// depth so that the reply channel will not be starved by slow event
|
||||
// processing.
|
||||
//
|
||||
// Once the io.Reader signals EOF, eventCh will be closed, then replyCh
|
||||
// will be closed, and then this function will return.
|
||||
//
|
||||
// As a special case, if a non-EOF error occurs while reading from the
|
||||
// io.Reader then a synthetic "FATAL" event will be written to eventCh
|
||||
// before the two buffers are closed and the function returns. This
|
||||
// synthetic message will have the error message "Error reading from OpenVPN".
|
||||
func Demultiplex(r io.Reader, replyCh, eventCh chan<- []byte) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
buf := scanner.Bytes()
|
||||
|
||||
if len(buf) < 1 {
|
||||
// Should never happen but we'll be robust and ignore this,
|
||||
// rather than crashing below.
|
||||
continue
|
||||
}
|
||||
|
||||
// Asynchronous messages always start with > to differentiate
|
||||
// them from replies.
|
||||
if buf[0] == '>' {
|
||||
// Trim off the > when we post the message, since it's
|
||||
// redundant after we've demuxed.
|
||||
eventCh <- buf[1:]
|
||||
} else {
|
||||
replyCh <- buf
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
// Generate a synthetic FATAL event so that the caller can
|
||||
// see that the connection was not gracefully closed.
|
||||
eventCh <- readErrSynthEvent
|
||||
}
|
||||
|
||||
close(eventCh)
|
||||
close(replyCh)
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Package demux implements low-level demultiplexing of the stream of
|
||||
// messages sent from OpenVPN on the management channel.
|
||||
//
|
||||
// OpenVPN's protocol includes two different kinds of message from the OpenVPN
|
||||
// process: replies to commands sent by the management client, and asynchronous
|
||||
// event notifications.
|
||||
//
|
||||
// This package's purpose is to split these messages into two separate streams,
|
||||
// so that functions executing command/response sequences can just block
|
||||
// on the reply channel while an event loop elsewhere deals with any async
|
||||
// events that might show up.
|
||||
package demux
|
|
@ -1,337 +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")
|
||||
|
||||
// StatusFormat enum type
|
||||
type StatusFormat string
|
||||
|
||||
// StatusFormatDefault openvpn default status format
|
||||
// StatusFormatV3 openvpn version 3 status format
|
||||
const (
|
||||
StatusFormatDefault StatusFormat = ""
|
||||
StatusFormatV3 StatusFormat = "3"
|
||||
)
|
||||
|
||||
// MgmtClient .
|
||||
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
|
||||
}
|
||||
|
||||
// LatestStatus retrieves the current daemon status information, in the same format as that produced by the OpenVPN --status directive.
|
||||
func (c *MgmtClient) LatestStatus(statusFormat StatusFormat) ([][]byte, error) {
|
||||
var cmd []byte
|
||||
if statusFormat == StatusFormatDefault {
|
||||
cmd = []byte("status")
|
||||
} else if statusFormat == StatusFormatV3 {
|
||||
cmd = []byte("status 3")
|
||||
} else {
|
||||
return nil, fmt.Errorf("Incorrect 'status' format option")
|
||||
}
|
||||
err := c.sendCommand(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := c.readCommandResponsePayload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, 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()
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package openvpn
|
||||
|
||||
type ErrorFromServer []byte
|
||||
|
||||
func (err ErrorFromServer) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
func (err ErrorFromServer) String() string {
|
||||
return string(err)
|
||||
}
|
|
@ -1,299 +0,0 @@
|
|||
package openvpn
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var eventSep = []byte(":")
|
||||
var fieldSep = []byte(",")
|
||||
var byteCountEventKW = []byte("BYTECOUNT")
|
||||
var byteCountCliEventKW = []byte("BYTECOUNT_CLI")
|
||||
var clientEventKW = []byte("CLIENT")
|
||||
var echoEventKW = []byte("ECHO")
|
||||
var fatalEventKW = []byte("FATAL")
|
||||
var holdEventKW = []byte("HOLD")
|
||||
var infoEventKW = []byte("INFO")
|
||||
var logEventKW = []byte("LOG")
|
||||
var needOkEventKW = []byte("NEED-OK")
|
||||
var needStrEventKW = []byte("NEED-STR")
|
||||
var passwordEventKW = []byte("PASSWORD")
|
||||
var stateEventKW = []byte("STATE")
|
||||
|
||||
type Event interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// UnknownEvent represents an event of a type that this package doesn't
|
||||
// know about.
|
||||
//
|
||||
// Future versions of this library may learn about new event types, so a
|
||||
// caller should exercise caution when making use of events of this type
|
||||
// to access unsupported behavior. Backward-compatibility is *not*
|
||||
// guaranteed for events of this type.
|
||||
type UnknownEvent struct {
|
||||
keyword []byte
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (e *UnknownEvent) Type() string {
|
||||
return string(e.keyword)
|
||||
}
|
||||
|
||||
func (e *UnknownEvent) Body() string {
|
||||
return string(e.body)
|
||||
}
|
||||
|
||||
func (e *UnknownEvent) String() string {
|
||||
return fmt.Sprintf("%s: %s", e.keyword, e.body)
|
||||
}
|
||||
|
||||
// MalformedEvent represents a message from the OpenVPN process that is
|
||||
// presented as an event but does not comply with the expected event syntax.
|
||||
//
|
||||
// Events of this type should never be seen but robust callers will accept
|
||||
// and ignore them, possibly generating some kind of debugging message.
|
||||
//
|
||||
// One reason for potentially seeing events of this type is when the target
|
||||
// program is actually not an OpenVPN process at all, but in fact this client
|
||||
// has been connected to a different sort of server by mistake.
|
||||
type MalformedEvent struct {
|
||||
raw []byte
|
||||
}
|
||||
|
||||
func (e *MalformedEvent) String() string {
|
||||
return fmt.Sprintf("Malformed Event %q", e.raw)
|
||||
}
|
||||
|
||||
// HoldEvent is a notification that the OpenVPN process is in a management
|
||||
// hold and will not continue connecting until the hold is released, e.g.
|
||||
// by calling client.HoldRelease()
|
||||
type HoldEvent struct {
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (e *HoldEvent) String() string {
|
||||
return string(e.body)
|
||||
}
|
||||
|
||||
// StateEvent is a notification of a change of connection state. It can be
|
||||
// used, for example, to detect if the OpenVPN connection has been interrupted
|
||||
// and the OpenVPN process is attempting to reconnect.
|
||||
type StateEvent struct {
|
||||
body []byte
|
||||
|
||||
// bodyParts is populated only on first request, giving us the
|
||||
// separate comma-separated elements of the message. Not all
|
||||
// fields are populated for all states.
|
||||
bodyParts [][]byte
|
||||
}
|
||||
|
||||
func (e *StateEvent) RawTimestamp() string {
|
||||
parts := e.parts()
|
||||
return string(parts[0])
|
||||
}
|
||||
|
||||
func (e *StateEvent) NewState() string {
|
||||
parts := e.parts()
|
||||
return string(parts[1])
|
||||
}
|
||||
|
||||
func (e *StateEvent) Description() string {
|
||||
parts := e.parts()
|
||||
return string(parts[2])
|
||||
}
|
||||
|
||||
// LocalTunnelAddr returns the IP address of the local interface within
|
||||
// the tunnel, as a string that can be parsed using net.ParseIP.
|
||||
//
|
||||
// This field is only populated for events whose NewState returns
|
||||
// either ASSIGN_IP or CONNECTED.
|
||||
func (e *StateEvent) LocalTunnelAddr() string {
|
||||
parts := e.parts()
|
||||
return string(parts[3])
|
||||
}
|
||||
|
||||
// RemoteAddr returns the non-tunnel IP address of the remote
|
||||
// system that has connected to the local OpenVPN process.
|
||||
//
|
||||
// This field is only populated for events whose NewState returns
|
||||
// CONNECTED.
|
||||
func (e *StateEvent) RemoteAddr() string {
|
||||
parts := e.parts()
|
||||
return string(parts[4])
|
||||
}
|
||||
|
||||
func (e *StateEvent) String() string {
|
||||
newState := e.NewState()
|
||||
switch newState {
|
||||
case "ASSIGN_IP":
|
||||
return fmt.Sprintf("%s: %s", newState, e.LocalTunnelAddr())
|
||||
case "CONNECTED":
|
||||
return fmt.Sprintf("%s: %s", newState, e.RemoteAddr())
|
||||
default:
|
||||
desc := e.Description()
|
||||
if desc != "" {
|
||||
return fmt.Sprintf("%s: %s", newState, desc)
|
||||
} else {
|
||||
return newState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *StateEvent) parts() [][]byte {
|
||||
if e.bodyParts == nil {
|
||||
// State messages currently have only five segments, but
|
||||
// we'll ask for 5 so any additional fields that might show
|
||||
// up in newer versions will gather in an element we're
|
||||
// not actually using.
|
||||
e.bodyParts = bytes.SplitN(e.body, fieldSep, 6)
|
||||
|
||||
// Prevent crash if the server has sent us a malformed
|
||||
// status message. This should never actually happen if
|
||||
// the server is behaving itself.
|
||||
if len(e.bodyParts) < 5 {
|
||||
expanded := make([][]byte, 5)
|
||||
copy(expanded, e.bodyParts)
|
||||
e.bodyParts = expanded
|
||||
}
|
||||
}
|
||||
return e.bodyParts
|
||||
}
|
||||
|
||||
// EchoEvent is emitted by an OpenVPN process running in client mode when
|
||||
// an "echo" command is pushed to it by the server it has connected to.
|
||||
//
|
||||
// The format of the echo message is free-form, since this message type is
|
||||
// intended to pass application-specific data from the server-side config
|
||||
// into whatever client is consuming the management prototcol.
|
||||
//
|
||||
// This event is emitted only if the management client has turned on events
|
||||
// of this type using client.SetEchoEvents(true)
|
||||
type EchoEvent struct {
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (e *EchoEvent) RawTimestamp() string {
|
||||
sepIndex := bytes.Index(e.body, fieldSep)
|
||||
if sepIndex == -1 {
|
||||
return ""
|
||||
}
|
||||
return string(e.body[:sepIndex])
|
||||
}
|
||||
|
||||
func (e *EchoEvent) Message() string {
|
||||
sepIndex := bytes.Index(e.body, fieldSep)
|
||||
if sepIndex == -1 {
|
||||
return ""
|
||||
}
|
||||
return string(e.body[sepIndex+1:])
|
||||
}
|
||||
|
||||
func (e *EchoEvent) String() string {
|
||||
return fmt.Sprintf("ECHO: %s", e.Message())
|
||||
}
|
||||
|
||||
// ByteCountEvent represents a periodic snapshot of data transfer in bytes
|
||||
// on a VPN connection.
|
||||
//
|
||||
// For OpenVPN *servers*, events are emitted for each client and the method
|
||||
// ClientId identifies thet client.
|
||||
//
|
||||
// For other OpenVPN modes, events are emitted only once per interval for the
|
||||
// single connection managed by the target process, and ClientId returns
|
||||
// the empty string.
|
||||
type ByteCountEvent struct {
|
||||
hasClient bool
|
||||
body []byte
|
||||
|
||||
// populated on first call to parts()
|
||||
bodyParts [][]byte
|
||||
}
|
||||
|
||||
func (e *ByteCountEvent) ClientId() string {
|
||||
if !e.hasClient {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(e.parts()[0])
|
||||
}
|
||||
|
||||
func (e *ByteCountEvent) BytesIn() int {
|
||||
index := 0
|
||||
if e.hasClient {
|
||||
index = 1
|
||||
}
|
||||
str := string(e.parts()[index])
|
||||
val, _ := strconv.Atoi(str)
|
||||
// Ignore error, since this should never happen if OpenVPN is
|
||||
// behaving itself.
|
||||
return val
|
||||
}
|
||||
|
||||
func (e *ByteCountEvent) BytesOut() int {
|
||||
index := 1
|
||||
if e.hasClient {
|
||||
index = 2
|
||||
}
|
||||
str := string(e.parts()[index])
|
||||
val, _ := strconv.Atoi(str)
|
||||
// Ignore error, since this should never happen if OpenVPN is
|
||||
// behaving itself.
|
||||
return val
|
||||
}
|
||||
|
||||
func (e *ByteCountEvent) String() string {
|
||||
if e.hasClient {
|
||||
return fmt.Sprintf("Client %s: %d in, %d out", e.ClientId(), e.BytesIn(), e.BytesOut())
|
||||
} else {
|
||||
return fmt.Sprintf("%d in, %d out", e.BytesIn(), e.BytesOut())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ByteCountEvent) parts() [][]byte {
|
||||
if e.bodyParts == nil {
|
||||
e.bodyParts = bytes.SplitN(e.body, fieldSep, 4)
|
||||
|
||||
wantCount := 2
|
||||
if e.hasClient {
|
||||
wantCount = 3
|
||||
}
|
||||
|
||||
// Prevent crash if the server has sent us a malformed
|
||||
// message. This should never actually happen if the
|
||||
// server is behaving itself.
|
||||
if len(e.bodyParts) < wantCount {
|
||||
expanded := make([][]byte, wantCount)
|
||||
copy(expanded, e.bodyParts)
|
||||
e.bodyParts = expanded
|
||||
}
|
||||
}
|
||||
return e.bodyParts
|
||||
}
|
||||
|
||||
func upgradeEvent(raw []byte) Event {
|
||||
splitIdx := bytes.Index(raw, eventSep)
|
||||
if splitIdx == -1 {
|
||||
// Should never happen, but we'll handle it robustly if it does.
|
||||
return &MalformedEvent{raw}
|
||||
}
|
||||
|
||||
keyword := raw[:splitIdx]
|
||||
body := raw[splitIdx+1:]
|
||||
|
||||
switch {
|
||||
case bytes.Equal(keyword, stateEventKW):
|
||||
return &StateEvent{body: body}
|
||||
case bytes.Equal(keyword, holdEventKW):
|
||||
return &HoldEvent{body}
|
||||
case bytes.Equal(keyword, echoEventKW):
|
||||
return &EchoEvent{body}
|
||||
case bytes.Equal(keyword, byteCountEventKW):
|
||||
return &ByteCountEvent{hasClient: false, body: body}
|
||||
case bytes.Equal(keyword, byteCountCliEventKW):
|
||||
return &ByteCountEvent{hasClient: true, body: body}
|
||||
default:
|
||||
return &UnknownEvent{keyword, body}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# 0xacab.org/leap/obfsvpn v0.0.0-20220626143947-feff527c00e5 => ../obfsvpn
|
||||
# 0xacab.org/leap/obfsvpn v0.0.0-20220626143947-feff527c00e5
|
||||
## explicit; go 1.17
|
||||
0xacab.org/leap/obfsvpn
|
||||
0xacab.org/leap/obfsvpn/client
|
||||
|
@ -17,8 +17,6 @@ git.torproject.org/pluggable-transports/snowflake.git/common/util
|
|||
github.com/ProtonMail/go-autostart
|
||||
# github.com/apparentlymart/go-openvpn-mgmt v0.0.0-20200929191752-4d2ce95ae600
|
||||
## explicit
|
||||
github.com/apparentlymart/go-openvpn-mgmt/demux
|
||||
github.com/apparentlymart/go-openvpn-mgmt/openvpn
|
||||
# github.com/cretz/bine v0.2.0
|
||||
## explicit; go 1.15
|
||||
github.com/cretz/bine/control
|
||||
|
@ -242,4 +240,3 @@ golang.org/x/sys/windows/svc/eventlog
|
|||
golang.org/x/sys/windows/svc/mgr
|
||||
# gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
||||
## explicit
|
||||
# 0xacab.org/leap/obfsvpn => ../obfsvpn
|
||||
|
|
Loading…
Reference in New Issue