diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..4a93477 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,23 @@ +kind: pipeline +name: default + +platform: + os: linux + arch: amd64 + +steps: +- name: test + image: golang + privileged: true + environment: + COVERALLS_TOKEN: + from_secret: coveralls_token + commands: + - apt-get -q -y update + - apt-get -q -y install lsof net-tools iptables + - iptables -D ufw-before-input -m conntrack --ctstate INVALID -j DROP || echo "Rule not found" + - iptables -I OUTPUT -p tcp -m conntrack --ctstate ESTABLISHED --ctdir ORIGINAL --tcp-flags RST RST -j DROP + - go test -race + - go test -covermode=count -coverprofile=profile.cov + - go get github.com/mattn/goveralls + - goveralls -coverprofile=profile.cov \ No newline at end of file diff --git a/.gitignore b/.gitignore index 53253b0..c827cdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ packetforward.test +.vscode +demo/client/client +demo/server/server \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd655e8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Brave New Software Project, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 750300d..c1015c3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# Packet Forward +packetforward [![GoDoc](https://godoc.org/github.com/getlantern/packetforward?status.png)](http://godoc.org/github.com/getlantern/packetforward) [![Build Status](https://drone.lantern.io/api/badges/getlantern/packetforward/status.svg)](https://drone.lantern.io/getlantern/packetforward) [![Coverage Status](https://coveralls.io/repos/github/getlantern/packetforward/badge.svg?branch=init)](https://coveralls.io/github/getlantern/packetforward) +========== A library for forwarding packets. @@ -10,4 +11,30 @@ This library uses Go modules. When running commands like `go test` in this repos Tests in this package require root access. The easiest way to test is to compile the tests with `go test -c` and run the output binary using the `sudo` command. -Be careful if you choose to run the Go tool with the sudo command (e.g. `sudo go test`). This can cause issues if the tool attempts to download missing dependencies. Namely, the Go tool may not be able to download anything as Git will likely be using a different SSH keypair (or no keypair at all). Worse, the Go tool may create folders in $GOPATH/pkg/mod/cache owned by the root user. This can disrupt future use of the Go tool, even outside of this repository. \ No newline at end of file +Be careful if you choose to run the Go tool with the sudo command (e.g. `sudo go test`). This can cause issues if the tool attempts to download missing dependencies. Namely, the Go tool may not be able to download anything as Git will likely be using a different SSH keypair (or no keypair at all). Worse, the Go tool may create folders in $GOPATH/pkg/mod/cache owned by the root user. This can disrupt future use of the Go tool, even outside of this repository. + +## Demo + +This repository includes a demo client and server in `demo/client` and `demo/server`. In order +to run the server, you'll need the prerequisites listed in http://github.com/getlantern/gotun. + +The server forwards TCP and UDP packets to hardcoded IP addresses configured with the `-tcpdest` and `-udpdest` flags. + +The client opens a TUN device. + +For example: + +``` +sudo iptables -d OUTPUT -p tcp -m conntrack --ctstate ESTABLISHED --ctdir ORIGINAL --tcp-flags RST RST -j DROP +cd demo/server +go build && sudo ./server +``` + +``` +cd demo/client +go build && sudo ./client +``` + +``` +curl http://10.0.0.1/1GB.zip > /dev/null +``` \ No newline at end of file diff --git a/client.go b/client.go index 885d72b..7713f07 100644 --- a/client.go +++ b/client.go @@ -1,3 +1,12 @@ +// packetforward provides a mechanism for forwarding IP packets from a client +// to a NAT server, which in turn proxies them to their final destination. +// +// - Clients are uniquely identified by a random UUID. +// - Clients connect to the server using a configurable dial function. +// - In the event of a disconnect, clients can reconnect with the same client ID +// - Interrupted and resumed client connections do not disconnect the clients' TCP connections to the origin +// - Currently, packetforward supports only TCP and UDP +// package packetforward import ( @@ -7,9 +16,12 @@ import ( "net" "time" + "github.com/getlantern/errors" "github.com/getlantern/framed" "github.com/getlantern/golog" + "github.com/getlantern/gonat" "github.com/getlantern/idletiming" + "github.com/getlantern/ops" "github.com/getlantern/uuid" ) @@ -19,12 +31,13 @@ const ( maxDialDelay = 1 * time.Second ) +// DialFunc is a function that dials a server, preferrably respecting any timeout +// in the provided Context. type DialFunc func(ctx context.Context) (net.Conn, error) type forwarder struct { id string downstream io.Writer - mtu int idleTimeout time.Duration dialServer DialFunc upstreamConn net.Conn @@ -32,12 +45,17 @@ type forwarder struct { copyToDownstreamError chan error } -func Client(downstream io.Writer, mtu int, idleTimeout time.Duration, dialServer DialFunc) io.WriteCloser { +// Client creates a new packetforward client and returns a WriteCloser. Consumers of packetforward +// should write whole IP packets to this WriteCloser. The packetforward client will write response +// packets to the specified downstream Writer. idleTimeout specifies a timeout for idle clients. +// When the client to server connection remains idle for longer than idleTimeout, it is automatically +// closed. dialServer configures how to connect to the packetforward server. When packetforwarding is +// no longer needed, consumers should Close the returned WriteCloser to clean up any outstanding resources. +func Client(downstream io.Writer, idleTimeout time.Duration, dialServer DialFunc) io.WriteCloser { id := uuid.New().String() return &forwarder{ id: id, downstream: downstream, - mtu: mtu, idleTimeout: idleTimeout, dialServer: dialServer, copyToDownstreamError: make(chan error, 1), @@ -52,73 +70,36 @@ func (f *forwarder) Write(b []byte) (int, error) { return len(b), nil } -func (f *forwarder) Close() error { - f.closeUpstream() - return nil -} - -func (f *forwarder) copyToDownstream(upstreamConn net.Conn, upstream io.ReadWriteCloser) { - b := make([]byte, f.mtu) - for { - n, readErr := upstream.Read(b) - if n > 0 { - _, writeErr := f.downstream.Write(b[:n]) - if writeErr != nil { - upstream.Close() - f.copyToDownstreamError <- writeErr - return - } - } - if readErr != nil { - upstream.Close() - f.copyToDownstreamError <- readErr - return - } - } -} - func (f *forwarder) writeToUpstream(b []byte) error { // Keep trying to transmit the client packet - attempts := float64(-100000) + priorAttempts := float64(-1) sleepTime := 50 * time.Millisecond maxSleepTime := f.idleTimeout firstDial := true for { - if attempts > -1 { - sleepTime := time.Duration(math.Pow(2, attempts)) * sleepTime + if priorAttempts > -1 { + sleepTime := time.Duration(math.Pow(2, priorAttempts)) * sleepTime if sleepTime > maxSleepTime { sleepTime = maxSleepTime } time.Sleep(sleepTime) } - attempts++ + priorAttempts++ if f.upstreamConn == nil { if !firstDial { // wait for copying to downstream to finish <-f.copyToDownstreamError - } else { - firstDial = false } - - ctx, cancel := context.WithTimeout(context.Background(), f.idleTimeout) - upstreamConn, dialErr := f.dialServer(ctx) - cancel() - if dialErr != nil { - log.Errorf("Error dialing upstream, will retry: %v", dialErr) + if err := f.dialUpstream(); err != nil { + log.Error(err) continue } - f.upstreamConn = idletiming.Conn(upstreamConn, f.idleTimeout, nil) - f.upstream = framed.NewReadWriteCloser(f.upstreamConn) - if _, err := f.upstream.Write([]byte(f.id)); err != nil { - log.Errorf("Error sending client ID to upstream, will retry: %v", err) - continue - } - go f.copyToDownstream(f.upstreamConn, f.upstream) + firstDial = false } - attempts = -1 + priorAttempts = -1 _, writeErr := f.upstream.Write(b) if writeErr != nil { @@ -131,6 +112,50 @@ func (f *forwarder) writeToUpstream(b []byte) error { } } +func (f *forwarder) dialUpstream() error { + log.Debug("Dialing upstream") + ctx, cancel := context.WithTimeout(context.Background(), f.idleTimeout) + upstreamConn, dialErr := f.dialServer(ctx) + cancel() + if dialErr != nil { + return errors.New("Error dialing upstream, will retry: %v", dialErr) + } + upstreamConn = idletiming.Conn(upstreamConn, f.idleTimeout, nil) + rwc := framed.NewReadWriteCloser(upstreamConn) + rwc.EnableBigFrames() + rwc.EnableBuffering(gonat.MaximumIPPacketSize) + rwc.DisableThreadSafety() + upstream := rwc + if _, err := upstream.Write([]byte(f.id)); err != nil { + return errors.New("Error sending client ID to upstream, will retry: %v", err) + } + f.upstreamConn, f.upstream = upstreamConn, upstream + ops.Go(func() { + f.copyToDownstream(upstreamConn, upstream) + }) + return nil +} + +func (f *forwarder) copyToDownstream(upstreamConn net.Conn, upstream io.ReadWriteCloser) { + b := make([]byte, gonat.MaximumIPPacketSize) + for { + n, readErr := upstream.Read(b) + if n > 0 { + _, writeErr := f.downstream.Write(b[:n]) + if writeErr != nil { + upstream.Close() + f.copyToDownstreamError <- writeErr + return + } + } + if readErr != nil { + upstream.Close() + f.copyToDownstreamError <- readErr + return + } + } +} + func (f *forwarder) closeUpstream() { if f.upstream != nil { f.upstream.Close() @@ -138,3 +163,9 @@ func (f *forwarder) closeUpstream() { f.upstreamConn = nil } } + +func (f *forwarder) Close() error { + f.closeUpstream() + <-f.copyToDownstreamError + return nil +} diff --git a/demo/client/client.go b/demo/client/client.go new file mode 100644 index 0000000..4282760 --- /dev/null +++ b/demo/client/client.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "flag" + "io" + "net" + "net/http" + _ "net/http/pprof" + "os" + "os/signal" + "syscall" + "time" + + "github.com/getlantern/golog" + tun "github.com/getlantern/gotun" + "github.com/getlantern/packetforward" +) + +var ( + log = golog.LoggerFor("packetforward-demo-client") +) + +var ( + tunDevice = flag.String("tun-device", "tun0", "tun device name") + tunAddr = flag.String("tun-address", "10.0.0.2", "tun device address") + tunMask = flag.String("tun-mask", "255.255.255.0", "tun device netmask") + tunGW = flag.String("tun-gw", "10.0.0.1", "tun device gateway") + mtu = flag.Int("mtu", 1500, "maximum transmission unit for TUN device") + addr = flag.String("addr", "127.0.0.1:9780", "address of server") + pprofAddr = flag.String("pprofaddr", "", "pprof address to listen on, not activate pprof if empty") +) + +func main() { + flag.Parse() + + if *pprofAddr != "" { + go func() { + log.Debugf("Starting pprof page at http://%s/debug/pprof", *pprofAddr) + srv := &http.Server{ + Addr: *pprofAddr, + } + if err := srv.ListenAndServe(); err != nil { + log.Error(err) + } + }() + } + + dev, err := tun.OpenTunDevice(*tunDevice, *tunAddr, *tunGW, *tunMask, *mtu) + if err != nil { + log.Fatal(err) + } + defer dev.Close() + + ch := make(chan os.Signal, 1) + signal.Notify(ch, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + go func() { + <-ch + log.Debug("Closing TUN device") + dev.Close() + log.Debug("Closed TUN device") + os.Exit(0) + }() + + log.Debugf("Using packetforward server at %v", *addr) + var d net.Dialer + c := packetforward.Client(dev, 70*time.Second, func(ctx context.Context) (net.Conn, error) { + return d.DialContext(ctx, "tcp", *addr) + }) + + log.Debug("Reading from TUN device") + b := make([]byte, *mtu) + for { + n, err := dev.Read(b) + if n > 0 { + c.Write(b[:n]) + } + if err != nil { + if err != io.EOF { + log.Errorf("Unexpected error reading from TUN device: %v", err) + } + return + } + } +} diff --git a/demo/demo b/demo/demo new file mode 100755 index 0000000..fde0225 Binary files /dev/null and b/demo/demo differ diff --git a/demo/server/server_linux.go b/demo/server/server_linux.go new file mode 100644 index 0000000..8914186 --- /dev/null +++ b/demo/server/server_linux.go @@ -0,0 +1,85 @@ +package main + +import ( + "flag" + "net" + "net/http" + _ "net/http/pprof" + "os" + "os/signal" + "syscall" + "time" + + "github.com/getlantern/golog" + "github.com/getlantern/gonat" + "github.com/getlantern/ops" + pserver "github.com/getlantern/packetforward/server" +) + +var ( + log = golog.LoggerFor("packetforward-demo-client") +) + +var ( + addr = flag.String("addr", "127.0.0.1:9780", "address of server") + tunGW = flag.String("tun-gw", "10.0.0.1", "tun device gateway") + ifOut = flag.String("ifout", "", "name of interface to use for outbound connections") + tcpDest = flag.String("tcpdest", "80.249.99.148", "destination to which to connect all TCP traffic") + udpDest = flag.String("udpdest", "8.8.8.8", "destination to which to connect all UDP traffic") + pprofAddr = flag.String("pprofaddr", "", "pprof address to listen on, not activate pprof if empty") +) + +func main() { + flag.Parse() + + if *pprofAddr != "" { + go func() { + log.Debugf("Starting pprof page at http://%s/debug/pprof", *pprofAddr) + srv := &http.Server{ + Addr: *pprofAddr, + } + if err := srv.ListenAndServe(); err != nil { + log.Error(err) + } + }() + } + + l, err := net.Listen("tcp", *addr) + if err != nil { + log.Fatal(err) + } + defer l.Close() + log.Debugf("Listening for packetforward connections at %v", l.Addr().String()) + + ch := make(chan os.Signal, 1) + signal.Notify(ch, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + ops.Go(func() { + <-ch + log.Debug("Closing listener") + l.Close() + log.Debug("Closed listener") + os.Exit(0) + }) + + s, err := pserver.NewServer(&pserver.Opts{ + Opts: gonat.Opts{ + IFName: *ifOut, + IdleTimeout: 70 * time.Second, + BufferDepth: 100000, + OnOutbound: func(pkt *gonat.IPPacket) { + pkt.SetDest(gonat.Addr{*tcpDest, pkt.FT().Dst.Port}) + }, + OnInbound: func(pkt *gonat.IPPacket, downFT gonat.FiveTuple) { + pkt.SetSource(gonat.Addr{*tunGW, downFT.Dst.Port}) + }, + }, + }) + if err != nil { + log.Fatal(err) + } + log.Debugf("Final result: %v", s.Serve(l)) +} diff --git a/go.mod b/go.mod index a88e445..ce298cd 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,19 @@ module github.com/getlantern/packetforward go 1.12 require ( - github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 // indirect - github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect - github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect - github.com/getlantern/eventual v0.0.0-20180125201821-84b02499361b // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 + github.com/getlantern/eventual v0.0.0-20180125201821-84b02499361b github.com/getlantern/fdcount v0.0.0-20170105153814-6a6cb5839bc5 - github.com/getlantern/framed v0.0.0-20190306221922-7f7919c8cf9b + github.com/getlantern/framed v0.0.0-20190530205914-53a68d971aef github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5 - github.com/getlantern/gotun v0.0.0-20190422200803-35dee1b197b5 - github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect - github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect - github.com/getlantern/idletiming v0.0.0-20190331182121-9540d1aeda2b + github.com/getlantern/gonat v0.0.0-20190530205736-af2e31f0c56d + github.com/getlantern/gotun v0.0.0-20190523194503-885514e382d2 + github.com/getlantern/idletiming v0.0.0-20190529182719-d2fbc83372a5 github.com/getlantern/ipproxy v0.0.0-20190502203022-c6564ee6fba1 - github.com/getlantern/mtime v0.0.0-20170117193331-ba114e4a82b0 // indirect - github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd // indirect - github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect + github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f github.com/getlantern/uuid v1.1.2-0.20190507182000-5c9436b8c718 - github.com/go-stack/stack v1.8.0 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/netstack v0.0.0-20190505230633-4391e4a763ab // indirect - github.com/oxtoacart/bpool v0.0.0-20190227141107-8c4636f812cc // indirect + github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c github.com/stretchr/testify v1.3.0 ) - -replace github.com/google/netstack => github.com/getlantern/netstack v0.0.0-20190313202628-8999826b821d diff --git a/go.sum b/go.sum index 98ca361..6ffbafd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,15 @@ +github.com/aristanetworks/goarista v0.0.0-20190429220743-799535f6f364/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 h1:tS7jSmwRqSxTnonTRlDD1oHo6Q9YOK4xHS9/v4L56eg= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190514202536-8f808a500156 h1:sdAZ4pJ5nD/EzLkcw4AonvhgrU1aBKxx6ga1b7Psr9o= +github.com/aristanetworks/goarista v0.0.0-20190514202536-8f808a500156/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190522184417-004c259faaeb/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/florianl/go-conntrack v0.0.0-20190502110113-6d50e184fe3e h1:QslAYPskpG2G2DwTIWTyjB490R8Cg6EO6lQ7I3p/gag= +github.com/florianl/go-conntrack v0.0.0-20190502110113-6d50e184fe3e/go.mod h1:bVjd6LQuEVqwX8Mw3sMv22v569+H/3TQWW6itTFrBh8= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= @@ -10,42 +18,107 @@ github.com/getlantern/eventual v0.0.0-20180125201821-84b02499361b h1:re/zIJk8bd7 github.com/getlantern/eventual v0.0.0-20180125201821-84b02499361b/go.mod h1:O8T3zFEcY6+LRXFcVV4q8mEu2tDIixG8edC84DfswBc= github.com/getlantern/fdcount v0.0.0-20170105153814-6a6cb5839bc5 h1:8Q9iN/V24EG01IgXEKVScth/rTXpplBxCYio/yIKtUw= github.com/getlantern/fdcount v0.0.0-20170105153814-6a6cb5839bc5/go.mod h1:XZwE+iIlAgr64OFbXKFNCllBwV4wEipPx8Hlo2gZdbM= -github.com/getlantern/framed v0.0.0-20190306221922-7f7919c8cf9b h1:rLHDb6VkSBQZTnY1ttltW1Sxp16XKLYlF06o8ukMg2o= -github.com/getlantern/framed v0.0.0-20190306221922-7f7919c8cf9b/go.mod h1:c6/4xBJDKOXLNKPJH//5z5EEHiFBPt+HpDFAlcvjDWw= +github.com/getlantern/framed v0.0.0-20190530204739-68011036ad56 h1:gFh02u57H6SepEjVLPaqM9zBZjxv9tLOlakoxXsIclI= +github.com/getlantern/framed v0.0.0-20190530204739-68011036ad56/go.mod h1:FE5aaC48/o6r8V/COJsEPUtqCrv0g/W6xQv7vgochJk= +github.com/getlantern/framed v0.0.0-20190530205914-53a68d971aef h1:p/ir5K29w2zuV8DkAYurmkPG3A7EiFHVPmH3/KDMYyc= +github.com/getlantern/framed v0.0.0-20190530205914-53a68d971aef/go.mod h1:nhnoiS6DE6zfe+BaCMU4YI01UpsuiXnDqM5S8jxHuuI= github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5 h1:Okd7vkn9CfIgDBj1ST/vtBTCfD/kxIhYD412K+FRKPc= github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5/go.mod h1:Vwx1Cg64gCdIalad44uvQsKZw6LsVczIKZrUBStEjVw= +github.com/getlantern/gonat v0.0.0-20190530205736-af2e31f0c56d h1:Suy5N5Sw+OxPih9K19gxebVvPiZ+B/dLnKa3Q0hiWrY= +github.com/getlantern/gonat v0.0.0-20190530205736-af2e31f0c56d/go.mod h1:0md16xZgTqmSLnbXU4FQzpUcZ9kt/1ZdZ0kB+QI6/vE= github.com/getlantern/gotun v0.0.0-20190422200803-35dee1b197b5 h1:RgknT+sLDtsoJEySPGJHagDVJFyOVIXTkY4Cks8xWTg= github.com/getlantern/gotun v0.0.0-20190422200803-35dee1b197b5/go.mod h1:xBRkm/iNqMm5ND8ZTgiCoyyPAyfY89vbYgtYFWx66lw= +github.com/getlantern/gotun v0.0.0-20190517161228-5fd9609427d4 h1:xt8Pw0bYiBX6USldD9vH5hS6pLCBup4pedYHDWMV3VI= +github.com/getlantern/gotun v0.0.0-20190517161228-5fd9609427d4/go.mod h1:1tN1bcYuIxD79XpS3RD+3ptxiBEWwR0p+PG2MsC4Nbo= +github.com/getlantern/gotun v0.0.0-20190521133812-51bfa687e26b h1:b/m0EoOnfn9NSKpCaCe1nxC8NLZ/CiqvfPx5v5tbCKk= +github.com/getlantern/gotun v0.0.0-20190521133812-51bfa687e26b/go.mod h1:1tN1bcYuIxD79XpS3RD+3ptxiBEWwR0p+PG2MsC4Nbo= +github.com/getlantern/gotun v0.0.0-20190523194503-885514e382d2 h1:B1Hyjlam0zeV5vgnAlnW666yFPeaj6rEm9AOUuzii1A= +github.com/getlantern/gotun v0.0.0-20190523194503-885514e382d2/go.mod h1:1tN1bcYuIxD79XpS3RD+3ptxiBEWwR0p+PG2MsC4Nbo= +github.com/getlantern/grtrack v0.0.0-20160824195228-cbf67d3fa0fd h1:GPrx88jy222gMuRHXxBSViT/3zdNO210nRAaXn+lL6s= +github.com/getlantern/grtrack v0.0.0-20160824195228-cbf67d3fa0fd/go.mod h1:RkQEgBdrJCH5tYJP2D+a/aJ216V3c9q8w/tCJtEiDoY= github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= github.com/getlantern/idletiming v0.0.0-20190331182121-9540d1aeda2b h1:fR5xnj6A0dJ3/H+xyR2QeKy98QwQlcuk0Qczu2hdqHk= github.com/getlantern/idletiming v0.0.0-20190331182121-9540d1aeda2b/go.mod h1:MGP8kEgZGgAhvHISt0hJGQgxg/VAqGdw3+kSZBnfC/4= +github.com/getlantern/idletiming v0.0.0-20190529141348-2e897609c5cd h1:XQ77PUQ6v4sqrX8AcZN3jEze+rU7CchCAdZpF+/5Q/U= +github.com/getlantern/idletiming v0.0.0-20190529141348-2e897609c5cd/go.mod h1:MGP8kEgZGgAhvHISt0hJGQgxg/VAqGdw3+kSZBnfC/4= +github.com/getlantern/idletiming v0.0.0-20190529182719-d2fbc83372a5 h1:laM1s/bxUH8xbbC9TBGWsOc7A0KCAPZMa4pdwO5e6Vw= +github.com/getlantern/idletiming v0.0.0-20190529182719-d2fbc83372a5/go.mod h1:MGP8kEgZGgAhvHISt0hJGQgxg/VAqGdw3+kSZBnfC/4= github.com/getlantern/ipproxy v0.0.0-20190502203022-c6564ee6fba1 h1:05pPp9/ONzEo7uGR79DLUDkkzNy4OLvt1x22BAjJ/cc= github.com/getlantern/ipproxy v0.0.0-20190502203022-c6564ee6fba1/go.mod h1:AX6L48UflMFs/pkaXppIu4VblJZvT8ES9mTO0nSnpd8= +github.com/getlantern/ipproxy v0.0.0-20190508162323-6329c3cbf2fa/go.mod h1:fVWS6Ae51b4r28K6AQPgt6ZER9wX6wGrDzFm2JpGttw= +github.com/getlantern/mockconn v0.0.0-20190403061815-a8ffa60494a6/go.mod h1:+F5GJ7qGpQ03DBtcOEyQpM30ix4BLswdaojecFtsdy8= github.com/getlantern/mtime v0.0.0-20170117193331-ba114e4a82b0 h1:1VNkP55LM/W2IwWN+qi+5X3gZcEQHfj8X9E+FNxVgM4= github.com/getlantern/mtime v0.0.0-20170117193331-ba114e4a82b0/go.mod h1:u537FS7ld4Whf7h7/0ql/myAudWWBNgeRhgE9XXH4Pk= -github.com/getlantern/netstack v0.0.0-20190313202628-8999826b821d h1:Emny4kQLL9uuaqt5FgOaLuQe1v1s2xHGAz81yGGghbM= -github.com/getlantern/netstack v0.0.0-20190313202628-8999826b821d/go.mod h1:boCZS+mc2Ckrx/BUsPJx3yaoyidXIn/xzXwV9CQtHw4= github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd h1:mn98vs69Kqw56iKhR82mjk16Q1q5aDFFW0E89/QbXkQ= github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE= github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= +github.com/getlantern/packetforward v0.0.1/go.mod h1:0LcDJxQJQrC0xOokc940jFtAQ4GymkGmlDGyAvPKaNg= +github.com/getlantern/testify v0.0.0-20160317154340-2eeb3906e78f/go.mod h1:uKKI9HSwZ4C8tx1vV+ovbG32Lw9LixtzPLNiS8U/ddA= github.com/getlantern/uuid v1.1.2-0.20190507171546-7c701662a263 h1:TL1Ks/pt4HGLuBxJeb5/XNarJ6J3zykmIZBY8OS17VY= github.com/getlantern/uuid v1.1.2-0.20190507171546-7c701662a263/go.mod h1:uX10hOzZUUDR+oYNSIks+RcozOEiwTNC/K2rw9SUi1k= github.com/getlantern/uuid v1.1.2-0.20190507182000-5c9436b8c718 h1:CerNWjQqXU4l70wjIDMtnDIRsKvBRjDOKfCg4s56zes= github.com/getlantern/uuid v1.1.2-0.20190507182000-5c9436b8c718/go.mod h1:uX10hOzZUUDR+oYNSIks+RcozOEiwTNC/K2rw9SUi1k= +github.com/getlantern/uuid v1.2.0/go.mod h1:uX10hOzZUUDR+oYNSIks+RcozOEiwTNC/K2rw9SUi1k= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/netstack v0.0.0-20190505230633-4391e4a763ab h1:pL7LhIuypqBDG+1k5qJMLaOUaGungvrFY/Y3szikrM0= -github.com/google/netstack v0.0.0-20190505230633-4391e4a763ab/go.mod h1:r/rILWg3r1Qy9G1IFMhsqWLq2GjwuYoTuPgG7ckMAjk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= +github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v0.0.0-20190411141321-3cae06de9d30/go.mod h1:t19erlH/I/040715SD0/ktZi33scgSEuOlp57AIFe/c= +github.com/mdlayher/netlink v0.0.0-20190514163018-336c8d74f4a0 h1:93Nlj3OMYLLA6QA6baiIeWW8P8CTlwQFA4pAecq7f/8= +github.com/mdlayher/netlink v0.0.0-20190514163018-336c8d74f4a0/go.mod h1:gOrA34zDL0K3RsACQe54bDYLF/CeFspQ9m5DOycycQ8= +github.com/mdlayher/netlink v0.0.0-20190516121005-0087c778e469/go.mod h1:gOrA34zDL0K3RsACQe54bDYLF/CeFspQ9m5DOycycQ8= github.com/oxtoacart/bpool v0.0.0-20190227141107-8c4636f812cc h1:uhnyuvDwdKbjemAXHKsiEZOPagHim2nRjMcazH1g26A= github.com/oxtoacart/bpool v0.0.0-20190227141107-8c4636f812cc/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/oxtoacart/bpool v0.0.0-20190524125616-8c0b41497736 h1:C9bEdTfu5QY+TIf4ohXC2oWkT88Qq3/t1yiUxf/Guvs= +github.com/oxtoacart/bpool v0.0.0-20190524125616-8c0b41497736/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/oxtoacart/bpool v0.0.0-20190530173437-18b0496e4113 h1:sN9LdxWNPtGP6pKLZMHbs5ZF6gcj0bN7KjmrGGcKPDc= +github.com/oxtoacart/bpool v0.0.0-20190530173437-18b0496e4113/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/ti-mo/conntrack v0.0.0-20190323132511-733fb77b6071 h1:NnXFjutDwCYD2AoSFSEuUKC1H0f0Y8jeYXapxZSlg84= +github.com/ti-mo/conntrack v0.0.0-20190323132511-733fb77b6071/go.mod h1:4S0aZBgVjqDnOB7vilkmLDNHivyRGwPbDfTak5XQ0no= +github.com/ti-mo/netfilter v0.2.0 h1:mMZ70vvHTlY9y8ElWflp5nVN5kkUDvm6D1JXRgartKI= +github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190410235845-0ad05ae3009d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190415081028-16da32be82c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug= +golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E= +golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= diff --git a/packetforward_test.go b/packetforward_test.go index 981764a..5da825c 100644 --- a/packetforward_test.go +++ b/packetforward_test.go @@ -3,23 +3,19 @@ package packetforward import ( "context" "io" + "math/rand" "net" - "strings" - "sync/atomic" "testing" "time" - "github.com/getlantern/fdcount" - tun "github.com/getlantern/gotun" - "github.com/getlantern/ipproxy" + "github.com/getlantern/gonat" "github.com/getlantern/packetforward/server" - - "github.com/stretchr/testify/assert" ) const ( - idleTimeout = 10 * time.Second - clientIdleTimeout = 100 * time.Second + idleTimeout = 100 * time.Millisecond + clientIdleTimeout = 1 * time.Second + tunGW = "10.0.0.9" ) var ( @@ -29,176 +25,79 @@ var ( // Note - this test has to be run with root permissions to allow setting up the // TUN device. func TestEndToEnd(t *testing.T) { - ip := "127.0.0.1" - - // Create a packetforward server - pfl, err := net.Listen("tcp", "localhost:0") - if !assert.NoError(t, err) { - return - } - defer pfl.Close() - - d := &net.Dialer{} - s := server.NewServer(&ipproxy.Opts{ - IdleTimeout: idleTimeout, - StatsInterval: 250 * time.Millisecond, - DialTCP: func(ctx context.Context, network, addr string) (net.Conn, error) { - // Send everything to local echo server - _, port, _ := net.SplitHostPort(addr) - return d.DialContext(ctx, network, ip+":"+port) - }, - DialUDP: func(ctx context.Context, network, addr string) (*net.UDPConn, error) { - // Send everything to local echo server - _, port, _ := net.SplitHostPort(addr) - conn, dialErr := net.Dial(network, ip+":"+port) - if dialErr != nil { - return nil, dialErr - } - return conn.(*net.UDPConn), nil - }, - }) - go s.Serve(pfl) - - // Open a TUN device - dev, err := tun.OpenTunDevice("tun0", "10.0.0.10", "10.0.0.9", "255.255.255.0") - if err != nil { + gonat.RunTest(t, "tun0", "10.0.0.10", tunGW, "255.255.255.0", 1500, func(ifAddr string, dev io.ReadWriter, origEchoAddr gonat.Addr, finishedCh chan interface{}) (func() error, error) { + // Create a packetforward server + pfl, err := net.Listen("tcp", ifAddr+":0") if err != nil { - if strings.HasSuffix(err.Error(), "operation not permitted") { - t.Log("This test requires root access. Compile, then run with root privileges. See the README for more details.") - } - t.Fatal(err) + return nil, err } - } - defer func() { - dev.Close() - }() - - // Forward packets from TUN device - writer := Client(dev, 1400, clientIdleTimeout, func(ctx context.Context) (net.Conn, error) { - return d.DialContext(ctx, "tcp", pfl.Addr().String()) - }) - go func() { - b := make([]byte, 1400) - for { - n, err := dev.Read(b) - if n > 0 { - writer.Write(b[:n]) + log.Debugf("Packetforward listening at %v", pfl.Addr()) + + d := &net.Dialer{} + s, err := server.NewServer(&server.Opts{ + Opts: gonat.Opts{ + IdleTimeout: idleTimeout, + StatsInterval: 250 * time.Millisecond, + OnOutbound: func(pkt *gonat.IPPacket) { + // Send everything to local echo server + pkt.SetDest(origEchoAddr) + }, + OnInbound: func(pkt *gonat.IPPacket, downFT gonat.FiveTuple) { + pkt.SetSource(gonat.Addr{tunGW, downFT.Dst.Port}) + }, + }, + }) + if err != nil { + return nil, err + } + go func() { + s.Serve(pfl) + close(finishedCh) + }() + + // Forward packets from TUN device + writer := Client(dev, clientIdleTimeout, func(ctx context.Context) (net.Conn, error) { + conn, err := d.DialContext(ctx, "tcp", pfl.Addr().String()) + if conn != nil { + conn = &autoCloseConn{Conn: conn} } - if err != nil { - return + return conn, err + }) + go func() { + b := make([]byte, gonat.MaximumIPPacketSize) + for { + n, err := dev.Read(b) + log.Debugf("Read %d: %v", n, err) + if n > 0 { + log.Debugf("Writing %d", n) + writer.Write(b[:n]) + } + if err != nil { + return + } } - } - }() - defer writer.Close() - - closeCh := make(chan interface{}) - echoAddr := tcpEcho(t, closeCh, ip) - udpEcho(t, closeCh, echoAddr) - - // point at TUN device rather than echo server directly - _, port, _ := net.SplitHostPort(echoAddr) - echoAddr = "10.0.0.9:" + port - - b := make([]byte, 8) - - _, connCount, err := fdcount.Matching("TCP") - if !assert.NoError(t, err, "unable to get initial socket count") { - return - } - - log.Debugf("Dialing echo server at: %v", echoAddr) - uconn, err := net.Dial("udp", echoAddr) - if !assert.NoError(t, err, "Unable to get UDP connection to TUN device") { - return - } - defer uconn.Close() - - _, err = uconn.Write([]byte("helloudp")) - if !assert.NoError(t, err) { - return - } - - // Sleep long enough to hit idle timeout so that client will have to reconnect - // time.Sleep(clientIdleTimeout + 100*time.Millisecond) - uconn.SetDeadline(time.Now().Add(250 * time.Millisecond)) - _, err = io.ReadFull(uconn, b) - if !assert.NoError(t, err) { - return - } - assert.Equal(t, "helloudp", string(b)) - - log.Debug("Here!") - conn, err := net.DialTimeout("tcp", echoAddr, 5*time.Second) - if !assert.NoError(t, err) { - return - } - defer conn.Close() - - _, err = conn.Write([]byte("hellotcp")) - if !assert.NoError(t, err) { - return - } - - // Sleep long enough to hit idle timeout so that client will have to reconnect - // time.Sleep(clientIdleTimeout + 100*time.Second) - _, err = io.ReadFull(conn, b) - if !assert.NoError(t, err) { - return - } - assert.Equal(t, "hellotcp", string(b)) - conn.Close() - time.Sleep(50 * time.Millisecond) - assert.Zero(t, atomic.LoadInt64(&serverTCPConnections), "Server-side TCP connection should have been closed") - - time.Sleep(2 * idleTimeout) - connCount.AssertDelta(0) + }() + return func() error { + writer.Close() + s.Close() + return pfl.Close() + }, nil + }) } -func tcpEcho(t *testing.T, closeCh <-chan interface{}, ip string) string { - l, err := net.Listen("tcp", ip+":0") - if err != nil { - t.Fatal(err) - } - go func() { - <-closeCh - l.Close() - }() - - go func() { - for { - conn, err := l.Accept() - if err != nil { - t.Error(err) - return - } - atomic.AddInt64(&serverTCPConnections, 1) - go io.Copy(conn, conn) - atomic.AddInt64(&serverTCPConnections, -1) - } - }() +var writes = 0 - return l.Addr().String() +type autoCloseConn struct { + net.Conn } -func udpEcho(t *testing.T, closeCh <-chan interface{}, echoAddr string) { - conn, err := net.ListenPacket("udp", echoAddr) - if err != nil { - t.Fatal(err) +func (c *autoCloseConn) Write(b []byte) (int, error) { + n, err := c.Conn.Write(b) + log.Debugf("%d / %d: %v", n, len(b), err) + if writes > 2 && rand.Float64() < 0.20 { + // Randomly close the connection 20% of the time, but not on the first two writes (client id) + c.Close() } - go func() { - <-closeCh - conn.Close() - }() - - go func() { - b := make([]byte, 20480) - for { - n, addr, err := conn.ReadFrom(b) - if err != nil { - t.Error(err) - return - } - conn.WriteTo(b[:n], addr) - } - }() + writes++ + return n, err } diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 73031fc..0000000 --- a/server/server.go +++ /dev/null @@ -1,198 +0,0 @@ -package server - -import ( - "io" - "math" - "net" - "sync" - "sync/atomic" - "time" - - "github.com/getlantern/framed" - "github.com/getlantern/golog" - "github.com/getlantern/idletiming" - "github.com/getlantern/ipproxy" -) - -var log = golog.LoggerFor("packetforward") - -const ( - maxListenDelay = 1 * time.Second - - baseIODelay = 10 * time.Millisecond - maxIODelay = 1 * time.Second -) - -type Server interface { - Serve(l net.Listener) error -} - -type server struct { - opts *ipproxy.Opts - clients map[string]*client - clientsMx sync.Mutex -} - -func NewServer(opts *ipproxy.Opts) Server { - opts = opts.ApplyDefaults() - opts.MTU -= framed.FrameHeaderLength // leave room for framed header - - s := &server{ - opts: opts, - clients: make(map[string]*client), - } - go s.printStats() - return s -} - -func (s *server) Serve(l net.Listener) error { - defer s.forgetClients() - - tempDelay := time.Duration(0) - for { - conn, err := l.Accept() - if err != nil { - if ne, ok := err.(net.Error); ok && ne.Temporary() { - // delay code based on net/http.Server - if tempDelay == 0 { - tempDelay = 5 * time.Millisecond - } else { - tempDelay *= 2 - } - if tempDelay > maxListenDelay { - tempDelay = maxListenDelay - } - log.Debugf("Error accepting connection: %v; retrying in %v", err, tempDelay) - time.Sleep(tempDelay) - continue - } - return log.Errorf("Error accepting: %v", err) - } - tempDelay = 0 - s.handle(conn) - } -} - -func (s *server) handle(conn net.Conn) { - // use framed protocol - framedConn := framed.NewReadWriteCloser(conn) - - // Read client ID - b := make([]byte, 36) - _, err := framedConn.Read(b) - if err != nil { - log.Errorf("Unable to read client ID: %v", err) - return - } - id := string(b) - - s.clientsMx.Lock() - c := s.clients[id] - if c == nil { - c = &client{ - id: id, - s: s, - framedConn: framedConn, - } - - ipp, err := ipproxy.New(c, s.opts) - if err != nil { - log.Errorf("Unable to open ipproxy: %v", err) - return - } - go func() { - if serveErr := ipp.Serve(); serveErr != nil { - log.Errorf("Error handling packets: %v", serveErr) - } - }() - s.clients[id] = c - } else { - c.attach(framedConn) - } - s.clientsMx.Unlock() -} - -func (s *server) forgetClients() { - s.clientsMx.Lock() - s.clients = make(map[string]*client) - s.clientsMx.Unlock() -} - -func (s *server) forgetClient(id string) { - s.clientsMx.Lock() - delete(s.clients, id) - s.clientsMx.Unlock() -} - -type client struct { - id string - s *server - framedConn io.ReadWriteCloser - lastActive int64 - mx sync.RWMutex -} - -func (c *client) getFramedConn() io.ReadWriteCloser { - c.mx.RLock() - framedConn := c.framedConn - c.mx.RUnlock() - return framedConn -} - -func (c *client) attach(framedConn io.ReadWriteCloser) { - if c.framedConn != nil { - go c.framedConn.Close() - } - c.framedConn = framedConn -} - -func (c *client) Read(b []byte) (int, error) { - i := float64(0) - for { - n, err := c.getFramedConn().Read(b) - if err == nil { - c.markActive() - return n, err - } - if c.idle() { - c.s.forgetClient(c.id) - return 0, io.EOF - } - // ignore errors and retry, because clients can reconnect - sleepTime := time.Duration(math.Pow(2, i)) * baseIODelay - if sleepTime > maxIODelay { - sleepTime = maxIODelay - } - time.Sleep(sleepTime) - i++ - } -} - -func (c *client) Write(b []byte) (int, error) { - i := float64(0) - for { - n, err := c.getFramedConn().Write(b) - if err == nil { - c.markActive() - return n, err - } - if c.idle() { - return 0, idletiming.ErrIdled - } - // ignore errors and retry, because clients can reconnect - sleepTime := time.Duration(math.Pow(2, i)) * baseIODelay - if sleepTime > maxIODelay { - sleepTime = maxIODelay - } - time.Sleep(sleepTime) - i++ - } -} - -func (c *client) markActive() { - atomic.StoreInt64(&c.lastActive, time.Now().UnixNano()) -} - -func (c *client) idle() bool { - return time.Duration(time.Now().UnixNano()-atomic.LoadInt64(&c.lastActive)) > c.s.opts.IdleTimeout -} diff --git a/server/server_linux.go b/server/server_linux.go new file mode 100644 index 0000000..320892f --- /dev/null +++ b/server/server_linux.go @@ -0,0 +1,274 @@ +// server provides the server end of packetforward functionality. The server reads +// IP packets from the client's connection, forwards these to the final origin using +// gonat and writes response packets back to the client. +package server + +import ( + "errors" + "io" + "math" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/getlantern/eventual" + "github.com/getlantern/framed" + "github.com/getlantern/golog" + "github.com/getlantern/gonat" + "github.com/getlantern/idletiming" + "github.com/oxtoacart/bpool" +) + +var ( + log = golog.LoggerFor("packetforward") + + // ErrNoConnection means that we attempted to write to client for which we have no current connection + ErrNoConnection = errors.New("no client connection") +) + +const ( + // DefaultBufferPoolSize is 1MB + DefaultBufferPoolSize = 1000000 +) + +const ( + maxListenDelay = 1 * time.Second + + baseIODelay = 10 * time.Millisecond + maxIODelay = 1 * time.Second +) + +type Opts struct { + gonat.Opts + + // BufferPoolSize is the size of the buffer pool in bytes. If not specified, defaults to 1 MB + BufferPoolSize int +} + +type Server interface { + Serve(l net.Listener) error + + // Close closes this server and associated resources. + Close() error +} + +type server struct { + opts *Opts + clients map[string]*client + clientsMx sync.Mutex + close chan interface{} + closed chan interface{} +} + +// NewServer constructs a new unstarted packetforward Server. The server can be started by +// calling Serve(). +func NewServer(opts *Opts) (Server, error) { + if opts.BufferPoolSize <= 0 { + opts.BufferPoolSize = DefaultBufferPoolSize + } + + // Apply defaults + err := opts.ApplyDefaults() + if err != nil { + return nil, err + } + + opts.BufferPool = framed.NewHeaderPreservingBufferPool(opts.BufferPoolSize, gonat.MaximumIPPacketSize, true) + + s := &server{ + opts: opts, + clients: make(map[string]*client), + close: make(chan interface{}), + closed: make(chan interface{}), + } + go s.printStats() + return s, nil +} + +// Serve serves new packetforward client connections inbound on the given Listener. +func (s *server) Serve(l net.Listener) error { + defer s.Close() + defer s.forgetClients() + + tempDelay := time.Duration(0) + for { + conn, err := l.Accept() + if err != nil { + if ne, ok := err.(net.Error); ok && ne.Temporary() { + // delay code based on net/http.Server + if tempDelay == 0 { + tempDelay = 5 * time.Millisecond + } else { + tempDelay *= 2 + } + if tempDelay > maxListenDelay { + tempDelay = maxListenDelay + } + log.Debugf("Error accepting connection: %v; retrying in %v", err, tempDelay) + time.Sleep(tempDelay) + continue + } + return log.Errorf("Error accepting: %v", err) + } + tempDelay = 0 + s.handle(conn) + } +} + +func (s *server) handle(conn net.Conn) { + // use framed protocol + framedConn := framed.NewReadWriteCloser(conn) + framedConn.EnableBigFrames() + framedConn.DisableThreadSafety() + framedConn.EnableBuffering(gonat.MaximumIPPacketSize) + + // Read client ID + b := make([]byte, 36) + _, err := framedConn.Read(b) + if err != nil { + log.Errorf("Unable to read client ID: %v", err) + return + } + id := string(b) + + s.clientsMx.Lock() + c := s.clients[id] + if c == nil { + efc := eventual.NewValue() + efc.Set(framedConn) + c = &client{ + id: id, + s: s, + framedConn: efc, + } + + gn, err := gonat.NewServer(c, &s.opts.Opts) + if err != nil { + log.Errorf("Unable to open gonat: %v", err) + return + } + go func() { + if serveErr := gn.Serve(); serveErr != nil { + if serveErr != io.EOF { + log.Errorf("Error handling packets: %v", serveErr) + } + } + }() + s.clients[id] = c + } else { + c.attach(framedConn) + } + s.clientsMx.Unlock() +} + +func (s *server) forgetClients() { + s.clientsMx.Lock() + s.clients = make(map[string]*client) + s.clientsMx.Unlock() +} + +func (s *server) forgetClient(id string) { + s.clientsMx.Lock() + delete(s.clients, id) + s.clientsMx.Unlock() +} + +func (s *server) Close() error { + select { + case <-s.close: + // already closed + default: + close(s.close) + } + <-s.closed + return nil +} + +type client struct { + id string + s *server + framedConn eventual.Value + lastActive int64 + mx sync.RWMutex +} + +func (c *client) getFramedConn(timeout time.Duration) *framed.ReadWriteCloser { + _framedConn, ok := c.framedConn.Get(timeout) + if !ok { + return nil + } + return _framedConn.(*framed.ReadWriteCloser) +} + +func (c *client) attach(framedConn io.ReadWriteCloser) { + oldFramedConn := c.getFramedConn(0) + if oldFramedConn != nil { + go oldFramedConn.Close() + } + c.framedConn.Set(framedConn) +} + +func (c *client) Read(b bpool.ByteSlice) (int, error) { + i := float64(0) + for { + conn := c.getFramedConn(c.s.opts.IdleTimeout) + if conn == nil { + return c.finished(io.EOF) + } + n, err := conn.Read(b.Bytes()) + if err == nil { + log.Debugf("Read %d", n) + c.markActive() + return n, err + } + if c.idle() { + return c.finished(io.EOF) + } + // ignore errors and retry, because clients can reconnect + sleepTime := time.Duration(math.Pow(2, i)) * baseIODelay + if sleepTime > maxIODelay { + sleepTime = maxIODelay + } + time.Sleep(sleepTime) + i++ + } +} + +func (c *client) Write(b bpool.ByteSlice) (int, error) { + i := float64(0) + for { + conn := c.getFramedConn(c.s.opts.IdleTimeout) + if conn == nil { + return c.finished(ErrNoConnection) + } + n, err := conn.WriteAtomic(b) + if err == nil { + c.markActive() + return n, err + } + if c.idle() { + return c.finished(idletiming.ErrIdled) + } + // ignore errors and retry, because clients can reconnect + sleepTime := time.Duration(math.Pow(2, i)) * baseIODelay + if sleepTime > maxIODelay { + sleepTime = maxIODelay + } + time.Sleep(sleepTime) + i++ + } +} + +func (c *client) finished(err error) (int, error) { + c.s.forgetClient(c.id) + return 0, err +} + +func (c *client) markActive() { + atomic.StoreInt64(&c.lastActive, time.Now().UnixNano()) +} + +func (c *client) idle() bool { + return time.Duration(time.Now().UnixNano()-atomic.LoadInt64(&c.lastActive)) > c.s.opts.IdleTimeout +} diff --git a/server/stats.go b/server/stats.go deleted file mode 100644 index fef6dc5..0000000 --- a/server/stats.go +++ /dev/null @@ -1,15 +0,0 @@ -package server - -import ( - "time" -) - -func (s *server) printStats() { - for { - time.Sleep(s.opts.StatsInterval) - s.clientsMx.Lock() - numClients := len(s.clients) - s.clientsMx.Unlock() - log.Debugf("Number of Clients: %d", numClients) - } -} diff --git a/server/stats_linux.go b/server/stats_linux.go new file mode 100644 index 0000000..b7095e0 --- /dev/null +++ b/server/stats_linux.go @@ -0,0 +1,25 @@ +package server + +import ( + "time" +) + +func (s *server) printStats() { + defer close(s.closed) + defer s.opts.Opts.StatsTracker.Close() + + ticker := time.NewTicker(s.opts.StatsInterval) + defer ticker.Stop() + + for { + select { + case <-s.close: + return + case <-ticker.C: + s.clientsMx.Lock() + numClients := len(s.clients) + s.clientsMx.Unlock() + log.Debugf("Number of Clients: %d", numClients) + } + } +}