Moved tests from lib/srv and lib/utils into integrations.

This commit is contained in:
Russell Jones 2017-05-31 11:48:20 -07:00
parent e5d6faf482
commit 5f670ef7d9
5 changed files with 232 additions and 535 deletions

View file

@ -6,11 +6,12 @@ import (
"io"
"io/ioutil"
"net"
"net/http"
"os"
"strconv"
"sync"
"time"
log "github.com/Sirupsen/logrus"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/native"
@ -23,7 +24,10 @@ import (
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
log "github.com/Sirupsen/logrus"
)
// SetTestTimeouts affects global timeouts inside Teleport, making connections
@ -558,6 +562,88 @@ func (i *TeleInstance) Stop(removeData bool) error {
return i.Process.Wait()
}
type proxyServer struct {
sync.Mutex
count int
}
// ServeHTTP only accepts the CONNECT verb and will tunnel your connection to
// the specified host. Also tracks the number of connections that it proxies for
// debugging purposes.
func (p *proxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// validate http connect parameters
if r.Method != http.MethodConnect {
trace.WriteError(w, trace.BadParameter("%v not supported", r.Method))
return
}
if r.Host == "" {
trace.WriteError(w, trace.BadParameter("host not set"))
return
}
// hijack request so we can get underlying connection
hj, ok := w.(http.Hijacker)
if !ok {
trace.WriteError(w, trace.AccessDenied("unable to hijack connection"))
return
}
sconn, _, err := hj.Hijack()
if err != nil {
trace.WriteError(w, err)
return
}
defer sconn.Close()
// dial to host we want to proxy connection to
dconn, err := net.Dial("tcp", r.Host)
if err != nil {
trace.WriteError(w, err)
return
}
defer dconn.Close()
// write 200 OK to the source, but don't close the connection
resp := &http.Response{
Status: "OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 0,
}
err = resp.Write(sconn)
if err != nil {
trace.WriteError(w, err)
return
}
// success, we're proxying data now
p.Lock()
p.count = p.count + 1
p.Unlock()
// copy from src to dst and dst to src
errc := make(chan error, 2)
replicate := func(dst io.Writer, src io.Reader) {
_, err := io.Copy(dst, src)
errc <- err
}
go replicate(sconn, dconn)
go replicate(dconn, sconn)
// wait until done, error, or 10 second
select {
case <-time.After(10 * time.Second):
case <-errc:
}
}
// Count returns the number of connections that have been proxied.
func (p *proxyServer) Count() int {
p.Lock()
defer p.Unlock()
return p.count
}
func fatalIf(err error) {
if err != nil {
log.Fatal("", err)

View file

@ -21,8 +21,12 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http/httptest"
"net/url"
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
"testing"
@ -275,6 +279,86 @@ func (s *IntSuite) TestAudit(c *check.C) {
}
}
// TestInteroperability checks if Teleport and OpenSSH behave in the same way
// when executing commands.
func (s *IntSuite) TestInteroperability(c *check.C) {
tempdir, err := ioutil.TempDir("", "teleport-")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tempdir)
tempfile := filepath.Join(tempdir, "file.txt")
// create new teleport server that will be used by all tests
t := s.newTeleport(c, nil, true)
defer t.Stop(true)
var tests = []struct {
inCommand string
inStdin string
outContains string
outFile bool
}{
// 0 - echo "1\n2\n" | ssh localhost "cat -"
// this command can be used to copy files by piping stdout to stdin over ssh.
{
"cat -",
"1\n2\n",
"1\n2\n",
false,
},
// 1 - ssh -tt locahost '/bin/sh -c "mkdir -p /tmp && echo a > /tmp/file.txt"'
// programs like ansible execute commands like this
{
fmt.Sprintf(`/bin/sh -c "mkdir -p /tmp && echo a > %v"`, tempfile),
"",
"a",
true,
},
// 2 - ssh localhost tty
// should print "not a tty"
{
"tty",
"",
"not a tty",
false,
},
}
for i, tt := range tests {
// create new teleport client
cl, err := t.NewClient(s.me.Username, Site, Host, t.GetPortSSHInt())
c.Assert(err, check.IsNil)
// hook up stdin and stdout to a buffer for reading and writing
inbuf := bytes.NewReader([]byte(tt.inStdin))
outbuf := &bytes.Buffer{}
cl.Stdin = inbuf
cl.Stdout = outbuf
cl.Stderr = outbuf
// run command and wait a maximum of 10 seconds for it to complete
sessionEndC := make(chan interface{}, 0)
go func() {
// don't check for err, because sometimes this process should fail
// with an error and that's what the test is checking for.
cl.SSH(context.TODO(), []string{tt.inCommand}, false)
sessionEndC <- true
}()
waitFor(sessionEndC, time.Second*10)
// if we are looking for the output in a file, look in the file
// otherwise check stdout and stderr for the expected output
if tt.outFile {
bytes, err := ioutil.ReadFile(tempfile)
c.Assert(err, check.IsNil)
comment := check.Commentf("Test %v: %q does not contain: %q", i, string(bytes), tt.outContains)
c.Assert(strings.Contains(string(bytes), tt.outContains), check.Equals, true, comment)
} else {
comment := check.Commentf("Test %v: %q does not contain: %q", i, outbuf.String(), tt.outContains)
c.Assert(strings.Contains(outbuf.String(), tt.outContains), check.Equals, true, comment)
}
}
}
// TestInteractive covers SSH into shell and joining the same session from another client
func (s *IntSuite) TestInteractive(c *check.C) {
t := s.newTeleport(c, nil, true)
@ -382,6 +466,16 @@ func (s *IntSuite) TestInvalidLogins(c *check.C) {
// Then it executes an SSH command on A by connecting directly
// to A and by connecting to B via B<->A tunnel
func (s *IntSuite) TestTwoClusters(c *check.C) {
// start the http proxy, we need to make sure this was not used
ps := &proxyServer{}
ts := httptest.NewServer(ps)
defer ts.Close()
// clear out any proxy environment variables
for _, v := range []string{"http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"} {
os.Setenv(v, "")
}
username := s.me.Username
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
@ -410,6 +504,9 @@ func (s *IntSuite) TestTwoClusters(c *check.C) {
outputB bytes.Buffer
)
// make sure the direct dialer was used and not the proxy dialer
c.Assert(ps.Count(), check.Equals, 0)
// if we got here, it means two sites are cross-connected. lets execute SSH commands
sshPort := a.GetPortSSHInt()
cmd := []string{"echo", "hello world"}
@ -457,6 +554,51 @@ func (s *IntSuite) TestTwoClusters(c *check.C) {
c.Assert(a.Stop(true), check.IsNil)
}
// TestTwoClustersProxy checks if the reverse tunnel uses a HTTP PROXY to
// establish a connection.
func (s *IntSuite) TestTwoClustersProxy(c *check.C) {
// start the http proxy
ps := &proxyServer{}
ts := httptest.NewServer(ps)
defer ts.Close()
// set the http_proxy environment variable
u, err := url.Parse(ts.URL)
c.Assert(err, check.IsNil)
os.Setenv("http_proxy", u.Host)
defer os.Setenv("http_proxy", "")
username := s.me.Username
a := NewInstance("site-A", HostID, Host, s.getPorts(5), s.priv, s.pub)
b := NewInstance("site-B", HostID, Host, s.getPorts(5), s.priv, s.pub)
a.AddUser(username, []string{username})
b.AddUser(username, []string{username})
c.Assert(b.Create(a.Secrets.AsSlice(), false, nil), check.IsNil)
c.Assert(a.Create(b.Secrets.AsSlice(), true, nil), check.IsNil)
c.Assert(b.Start(), check.IsNil)
c.Assert(a.Start(), check.IsNil)
// wait for both sites to see each other via their reverse tunnels (for up to 10 seconds)
abortTime := time.Now().Add(time.Second * 10)
for len(b.Tunnel.GetSites()) < 2 && len(b.Tunnel.GetSites()) < 2 {
time.Sleep(time.Millisecond * 200)
if time.Now().After(abortTime) {
c.Fatalf("two sites do not see each other: tunnels are not working")
}
}
// make sure the reverse tunnel went through the proxy
c.Assert(ps.Count() > 0, check.Equals, true, check.Commentf("proxy did not intercept any connection"))
// stop both sites for real
c.Assert(b.Stop(true), check.IsNil)
c.Assert(a.Stop(true), check.IsNil)
}
// TestHA tests scenario when auth server for the cluster goes down
// and we switch to local persistent caches
func (s *IntSuite) TestHA(c *check.C) {

View file

@ -23,7 +23,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"os/user"
@ -291,254 +290,6 @@ func (s *SrvSuite) TestAgentForward(c *C) {
c.Fatalf("expected socket to be closed, still could dial after 150 ms")
}
// TestExecCommandLocalPtyAndRemotePty tests executing a command where you have
// a local and remote PTY.
func (s *SrvSuite) TestExecCommandLocalPtyAndRemotePty(c *C) {
// expected output: success. top has a remote and local pty.
term, err := newTerminal()
c.Assert(err, IsNil)
session, err := s.clt.NewSession()
c.Assert(err, IsNil)
session.Stdin = term.tty
session.Stdout = term.tty
session.Stderr = term.tty
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
err = session.RequestPty("term", 40, 80, modes)
c.Assert(err, IsNil)
// run top, wait 500 ms for top to start, send ^C
go func() {
err := session.Run("top")
c.Assert(err, NotNil)
}()
time.Sleep(500 * time.Millisecond)
_, err = term.tty.Write([]byte("\x03"))
c.Assert(err, IsNil)
output := make([]byte, 2048)
_, err = term.pty.Read(output)
c.Assert(err, IsNil)
outputContains := strings.Contains(string(output), "%CPU")
c.Assert(outputContains, Equals, true, Commentf("Output does not contain %CPU expected from top"))
// 2 - ssh -tt locahost '/bin/sh -c "mkdir -p /tmp && echo a > /tmp/file.txt"'
// expected output: success. this mirrors how ansible runs commands.
term, err = newTerminal()
c.Assert(err, IsNil)
session, err = s.clt.NewSession()
c.Assert(err, IsNil)
session.Stdin = term.tty
session.Stdout = term.tty
session.Stderr = term.tty
modes = ssh.TerminalModes{
ssh.ECHO: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
err = session.RequestPty("term", 40, 80, modes)
c.Assert(err, IsNil)
tempdir, err := ioutil.TempDir("", "teleport-")
c.Assert(err, IsNil)
os.RemoveAll(tempdir)
tempfile := tempdir + "/file.txt"
command := fmt.Sprintf(`/bin/sh -c "mkdir -p %v && echo a > %v"`, tempdir, tempfile)
err = session.Run(command)
c.Assert(err, IsNil)
time.Sleep(100 * time.Millisecond)
bytes, err := ioutil.ReadFile(tempfile)
c.Assert(err, IsNil)
c.Assert(string(bytes), Equals, "a\n")
}
// TestExecCommandLocalPtyAndNoRemotePty tests executing a command where you a
// local PTY but no remote PTY.
func (s *SrvSuite) TestExecCommandLocalPtyAndNoRemotePty(c *C) {
// 1 - command: ssh localhost top
// expected output: failure. no remote pty exists so top will not start.
term, err := newTerminal()
c.Assert(err, IsNil)
session, err := s.clt.NewSession()
c.Assert(err, IsNil)
session.Stdin = term.tty
session.Stdout = term.tty
session.Stderr = term.tty
err = session.Run("top")
c.Assert(err, NotNil)
output := make([]byte, 21)
_, err = term.pty.Read(output)
c.Assert(err, IsNil)
c.Assert(string(output), Equals, "top: failed tty get\r\n")
// 2 - command: ssh localhost seq 2
// expected output: success. because we have a local pty attached
// to stdout, the pty transforms 1\n2\n to 1\r\n2\r\n.
term, err = newTerminal()
c.Assert(err, IsNil)
session, err = s.clt.NewSession()
c.Assert(err, IsNil)
session.Stdin = term.tty
session.Stdout = term.tty
session.Stderr = term.tty
err = session.Run("seq 2")
c.Assert(err, IsNil)
output = make([]byte, 6)
_, err = term.pty.Read(output)
c.Assert(err, IsNil)
c.Assert(string(output), Equals, "1\r\n2\r\n")
}
// TestExecCommandNoLocalAndPtyRemotePty tests executing a command where you
// have no local PTY but have a remote PTY.
func (s *SrvSuite) TestExecCommandNoLocalPtyAndRemotePty(c *C) {
c.Skip("Temporarily remove test until we track down why it's flaky. Output of [0x0a 0x31 0x0a 0x32 0x0a] does not match expected output of [0x31 0x0d 0x0a 0x032 0x0d 0x0a 0x0a]")
// 1 - command: seq 2 | ssh -tt localhost 'stty -opost; echo'
// expected output: pipe stdin to ssh and turn off post-processing
// and echo stdin. we should see "1\r\n2\r\n\n"
session, err := s.clt.NewSession()
c.Assert(err, IsNil)
stdinPipe, err := session.StdinPipe()
c.Assert(err, IsNil)
stdoutPipe, err := session.StdoutPipe()
c.Assert(err, IsNil)
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
err = session.RequestPty("term", 40, 80, modes)
c.Assert(err, IsNil)
_, err = stdinPipe.Write([]byte("1\n2\n"))
c.Assert(err, IsNil)
stdinPipe.Close()
err = session.Run("stty -opost; echo")
c.Assert(err, IsNil)
stdout, err := ioutil.ReadAll(stdoutPipe)
c.Assert(err, IsNil)
outputContains := strings.Contains(string(stdout), "1\r\n2\r\n\n")
c.Assert(outputContains, Equals, true, Commentf("Output [%# x] does not match expected output: [%# x]", string(stdout), "1\r\n2\r\n\n"))
}
// TestExecCommandNoLocalPtyAndNoRemotePty tests executing a command where
// you have no local PTY and no remote PTY. This is like running ssh
// with the -T flag.
func (s *SrvSuite) TestExecCommandNoLocalPtyAndNoRemotePty(c *C) {
// 1 - command: echo "1\n2\n" | ssh -T localhost "cat -"
// expected output: since no pty exists, cat takes stdin
// from the pipe and prints it out as-is: "1\n2\n"
session, err := s.clt.NewSession()
c.Assert(err, IsNil)
stdinPipe, err := session.StdinPipe()
c.Assert(err, IsNil)
stdoutPipe, err := session.StdoutPipe()
c.Assert(err, IsNil)
_, err = stdinPipe.Write([]byte("1\n2\n"))
c.Assert(err, IsNil)
stdinPipe.Close()
err = session.Run("cat -")
c.Assert(err, IsNil)
stdout, err := ioutil.ReadAll(stdoutPipe)
c.Assert(err, IsNil)
c.Assert(string(stdout), Equals, "1\n2\n")
// 2 - command: echo "1\n2\n" | ssh -T localhost "cat - > /tmp/file"
// expected output: this is like using ssh to copy a file from one
// machine to another. since no pty exists, cat takes stdin from the pipe
// and writes it to disk as-is: "1\n2\n"
session, err = s.clt.NewSession()
c.Assert(err, IsNil)
stdinPipe, err = session.StdinPipe()
c.Assert(err, IsNil)
stdoutPipe, err = session.StdoutPipe()
c.Assert(err, IsNil)
tmpfile, err := ioutil.TempFile("", "teleport-")
c.Assert(err, IsNil)
_, err = stdinPipe.Write([]byte("1\n2\n"))
c.Assert(err, IsNil)
stdinPipe.Close()
cmd := fmt.Sprintf("cat - > %v", tmpfile.Name())
err = session.Run(cmd)
c.Assert(err, IsNil)
bytes, err := ioutil.ReadFile(tmpfile.Name())
c.Assert(err, IsNil)
c.Assert(string(bytes), Equals, "1\n2\n")
c.Assert(os.Remove(tmpfile.Name()), IsNil)
}
// TestShell launches interactive shell session and executes a command
func (s *SrvSuite) TestShell(c *C) {
c.Skip("disabled")
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
writer, err := se.StdinPipe()
c.Assert(err, IsNil)
stdoutPipe, err := se.StdoutPipe()
c.Assert(err, IsNil)
reader := bufio.NewReader(stdoutPipe)
c.Assert(se.Shell(), IsNil)
buf := make([]byte, 256)
_, err = reader.Read(buf)
c.Assert(err, IsNil)
c.Assert(len(buf) > 0, Equals, true)
// send a few "keyboard inputs" into the session:
_, err = io.WriteString(writer, "echo $((50+100))\n\r")
c.Assert(err, IsNil)
// read the output and make sure that "150" (output of $((50+100)) is there
// NOTE: this test may fail if you have errors in your .bashrc or .profile
// leading to tons of output when opening new bash session
foundOutput := false
for i := 0; i < 50 && !foundOutput; i++ {
time.Sleep(time.Millisecond)
_, err = reader.Read(buf)
c.Assert(err, IsNil)
foundOutput = strings.Contains(string(buf), "150")
}
c.Assert(foundOutput, Equals, true)
c.Assert(se.Close(), IsNil)
}
func (s *SrvSuite) TestAllowedUsers(c *C) {
up, err := newUpack(s.user, []string{s.user}, s.a)
c.Assert(err, IsNil)

Binary file not shown.

View file

@ -16,28 +16,12 @@ limitations under the License.
package proxy
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"os/exec"
"sync"
"testing"
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
log "github.com/Sirupsen/logrus"
"gopkg.in/check.v1"
)
@ -55,58 +39,6 @@ func (s *ProxySuite) TearDownSuite(c *check.C) {}
func (s *ProxySuite) SetUpTest(c *check.C) {}
func (s *ProxySuite) TearDownTest(c *check.C) {}
func (s *ProxySuite) TestDirectDial(c *check.C) {
os.Unsetenv("https_proxy")
os.Unsetenv("http_proxy")
d := debugServer{}
err := d.Start()
c.Assert(err, check.IsNil)
dialer := DialerFromEnvironment()
client, err := dialer.Dial("tcp", d.Address(), &ssh.ClientConfig{})
c.Assert(err, check.IsNil)
session, err := client.NewSession()
c.Assert(err, check.IsNil)
defer session.Close()
session.Run("date")
session.Close()
client.Close()
c.Assert(d.Commands(), check.DeepEquals, []string{"date"})
}
func (s *ProxySuite) TestProxyDial(c *check.C) {
dh := &debugHandler{}
ts := httptest.NewServer(dh)
defer ts.Close()
u, err := url.Parse(ts.URL)
c.Assert(err, check.IsNil)
os.Setenv("http_proxy", u.Host)
ds := debugServer{}
err = ds.Start()
c.Assert(err, check.IsNil)
dialer := DialerFromEnvironment()
client, err := dialer.Dial("tcp", ds.Address(), &ssh.ClientConfig{})
c.Assert(err, check.IsNil)
session, err := client.NewSession()
c.Assert(err, check.IsNil)
defer session.Close()
session.Run("date")
session.Close()
client.Close()
c.Assert(ds.Commands(), check.DeepEquals, []string{"date"})
c.Assert(dh.Count(), check.Equals, 1)
}
func (s *ProxySuite) TestGetProxyAddress(c *check.C) {
var tests = []struct {
inEnvName string
@ -151,222 +83,8 @@ func (s *ProxySuite) TestGetProxyAddress(c *check.C) {
}
}
type debugServer struct {
sync.Mutex
addr string
commands []string
}
func (d *debugServer) Start() error {
hostkey, err := d.generateHostKey()
if err != nil {
return err
}
freePorts, err := utils.GetFreeTCPPorts(10)
if err != nil {
return err
}
srvPort := freePorts[len(freePorts)-1]
d.addr = "127.0.0.1:" + srvPort
config := &ssh.ServerConfig{
NoClientAuth: true,
}
config.AddHostKey(hostkey)
listener, err := net.Listen("tcp", d.addr)
if err != nil {
return err
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Debugf("Unable to accept: %v", err)
}
go d.handleConnection(conn, config)
}
}()
return nil
}
func (d *debugServer) handleConnection(conn net.Conn, config *ssh.ServerConfig) error {
sconn, chans, reqs, err := ssh.NewServerConn(conn, config)
if err != nil {
return err
}
go ssh.DiscardRequests(reqs)
newchan := <-chans
channel, requests, err := newchan.Accept()
if err != nil {
return err
}
req := <-requests
err = d.handleRequest(channel, req)
if err != nil {
return err
}
channel.Close()
sconn.Close()
return nil
}
func (d *debugServer) handleRequest(channel ssh.Channel, req *ssh.Request) error {
if req.Type != "exec" {
req.Reply(false, nil)
return trace.BadParameter("only exec type supported")
}
type execRequest struct {
Command string
}
var e execRequest
if err := ssh.Unmarshal(req.Payload, &e); err != nil {
return err
}
out, err := exec.Command(e.Command).Output()
if err != nil {
return err
}
io.Copy(channel, bytes.NewReader(out))
channel.Close()
d.Lock()
d.commands = append(d.commands, e.Command)
d.Unlock()
if req.WantReply {
req.Reply(true, nil)
}
return nil
}
func (d *debugServer) generateHostKey() (ssh.Signer, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
privateKeyPEM := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
var privateKeyBuffer bytes.Buffer
err = pem.Encode(&privateKeyBuffer, privateKeyPEM)
if err != nil {
return nil, err
}
hostkey, err := ssh.ParsePrivateKey(privateKeyBuffer.Bytes())
if err != nil {
return nil, err
}
return hostkey, nil
}
func (d *debugServer) Commands() []string {
d.Lock()
defer d.Unlock()
return d.commands
}
func (d *debugServer) Address() string {
return d.addr
}
type debugHandler struct {
sync.Mutex
count int
}
func (d *debugHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// validate http connect parameters
if r.Method != http.MethodConnect {
http.Error(w, fmt.Sprintf("%v not supported", r.Method), http.StatusInternalServerError)
return
}
if r.Host == "" {
http.Error(w, fmt.Sprintf("host not set"), http.StatusInternalServerError)
return
}
// hijack request so we can get underlying connection
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "unable to hijack connection", http.StatusInternalServerError)
return
}
sconn, _, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// dial to host we want to proxy connection to
dconn, err := net.Dial("tcp", r.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// write 200 OK to the source, but don't close the connection
resp := &http.Response{
Status: "OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 0,
}
resp.Write(sconn)
// copy from src to dst and dst to src
done := make(chan bool)
go func() {
io.Copy(sconn, dconn)
done <- true
}()
go func() {
io.Copy(dconn, sconn)
done <- true
}()
d.Lock()
d.count = d.count + 1
d.Unlock()
// wait until done
<-done
<-done
// close the connections
sconn.Close()
dconn.Close()
}
func (d *debugHandler) Count() int {
d.Lock()
defer d.Unlock()
return d.count
}
func unsetEnv() {
os.Unsetenv("http_proxy")
os.Unsetenv("HTTP_PROXY")
os.Unsetenv("https_proxy")
os.Unsetenv("HTTPS_PROXY")
for _, envname := range []string{"http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"} {
os.Unsetenv(envname)
}
}