Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

netcat integration test #149

Merged
merged 5 commits into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 143 additions & 75 deletions netcat/netcat_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,103 +12,171 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build old_integration
// +build integration

package main

import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os/exec"
"strings"
"os"
"path"
"testing"
"time"

log "github.com/inconshreveable/log15"
"github.com/netsec-ethz/scion-apps/pkg/integration"
)

func TestSCIONNetcat(t *testing.T) {
const (
name = "netcat"
clientBin = "scion-netcat"
serverBin = "scion-netcat"
)

func TestIntegrationScionNetcat(t *testing.T) {
if err := integration.Init(name); err != nil {
t.Fatalf("Failed to init: %s\n", err)
}
// Start a scion-netcat server socket and query it with a scion-netcat client
var commands []*exec.Cmd
defer func() {
// cleanup after test
for _, cmd := range commands {
if err := cmd.Process.Kill(); err != nil {
fmt.Printf("Failed to kill process: %v", err)
}
}
}()

// Check we are running the right scion-netcat, inspect the help
cmd := exec.Command("bin/scion-netcat",
"--help",
)
ncOut, _ := cmd.StdoutPipe()
log.Info("Run netcat help", "cmd", fmt.Sprintf("%s %s", cmd.Path, cmd.Args))
if err := cmd.Start(); err != nil {
t.Fatalf("Error during help %v: %v", "netcat", err)
// Common arguments
cmnArgs := []string{"-vv"}
// Server
serverPort := "1234"
serverArgs := []string{"-l", serverPort}
serverArgs = append(cmnArgs, serverArgs...)

testMessage := "Hello World!"
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
ncStdOutput, _ := ioutil.ReadAll(ncOut)
_ = cmd.Wait()

// Check help output
if strings.Contains(string(ncStdOutput), "SCION") {
fmt.Println("Using scion-netcat")
} else {
t.Fatalf("Wrong netcat on PATH. Output=%s", ncStdOutput)
defer os.RemoveAll(tmpDir)
clientBinWrapperCmd, err := wrapperCommand(tmpDir, fmt.Sprintf("echo -e '%s'", testMessage),
integration.AppBinPath(clientBin))
if err != nil {
t.Fatalf("Failed to wrap scion-netcat input: %s\n", err)
}
clientCmd := clientBinWrapperCmd
serverCmd := integration.AppBinPath(serverBin)

// Start the actual test
testMessage := "Hello World!"
// Server command
cmd = exec.Command("bin/scion-netcat",
"-l",
"1234",
)
serverOut, _ := cmd.StdoutPipe()
serverStdoutScanner := bufio.NewScanner(serverOut)
log.Info("Start server", "cmd", fmt.Sprintf("%s %s", cmd.Path, cmd.Args))
if err := cmd.Start(); err != nil {
t.Fatalf("Error during netcat server socket listen %v: %v", "netcat", err)
// QUIC tests (default mode)
testCases := []struct {
Name string
Args []string
ServerOutMatchFun func(bool, string) bool
ServerErrMatchFun func(bool, string) bool
ClientOutMatchFun func(bool, string) bool
ClientErrMatchFun func(bool, string) bool
}{
{
"client_help",
append(cmnArgs, "--help"),
nil,
nil,
integration.RegExp("^.*SCION.*$"),
nil,
},
{
"client_hello",
append(cmnArgs, integration.DstAddrPattern + ":" + serverPort),
integration.RegExp(fmt.Sprintf("^%s$", testMessage)),
nil,
nil,
integration.NoPanic(),
},
}

for _, tc := range testCases {
in := integration.NewAppsIntegration(name, tc.Name, clientCmd, serverCmd, tc.Args, serverArgs, true)
in.ServerStdout(tc.ServerOutMatchFun)
in.ServerStderr(tc.ServerErrMatchFun)
in.ClientStdout(tc.ClientOutMatchFun)
in.ClientStderr(tc.ClientErrMatchFun)

hostAddr := integration.HostAddr

IAPairs := integration.IAPairs(hostAddr)
IAPairs = IAPairs[:5]

if err := integration.RunTests(in, IAPairs, integration.DefaultClientTimeout, 250 * time.Millisecond); err != nil {
t.Fatalf("Error during tests err: %v", err)
}
}
}

func TestIntegrationScionNetcatUDP(t *testing.T) {
// UDP tests
// Common arguments
cmnArgs := []string{"-vv", "-u"}

// Server
serverPort := "1234"
serverArgs := []string{"-l", serverPort}
serverArgs = append(cmnArgs, serverArgs...)

testMessage := "Hello UDP World!"
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
commands = append(commands, cmd)
time.Sleep(250 * time.Millisecond)

// Echo command
echoCmd := exec.Command("echo",
"-e",
fmt.Sprintf("\n\n%s\n", testMessage),
)
echoOut, _ := echoCmd.StdoutPipe()

// Client command
cmd = exec.Command("bin/scion-netcat",
"1-ff00:0:110,[127.0.0.1]:1234",
)
cmd.Stdin = echoOut
log.Info("Run client", "cmd", fmt.Sprintf("%s %s", cmd.Path, cmd.Args))
if err := cmd.Start(); err != nil {
t.Fatalf("Error during netcat piping on client side %v: %v", "netcat", err)
defer os.RemoveAll(tmpDir)
clientBinWrapperCmd, err := wrapperCommand(tmpDir, fmt.Sprintf("echo -e '%s'", testMessage),
integration.AppBinPath(clientBin))
if err != nil {
t.Fatalf("Failed to wrap scion-netcat input: %s\n", err)
}
commands = append(commands, cmd)
clientCmd := clientBinWrapperCmd
serverCmd := integration.AppBinPath(serverBin)

// Run echo piped into netcat client
if err := echoCmd.Run(); err != nil {
t.Fatalf("Error during echo to netcat pipe on client side %v: %v", "echo | netcat", err)
testCases := []struct {
Name string
Args []string
ServerOutMatchFun func(bool, string) bool
ServerErrMatchFun func(bool, string) bool
ClientOutMatchFun func(bool, string) bool
ClientErrMatchFun func(bool, string) bool
}{
{
"client_hello_UDP",
append(cmnArgs, integration.DstAddrPattern + ":" + serverPort),
nil,
nil,
nil,
integration.RegExp("^.*Connected.*$"),
},
}

// Check server output
fullServerOutput := ""
for serverStdoutScanner.Scan() {
serverOutput := serverStdoutScanner.Text()
fullServerOutput += fmt.Sprintln(serverOutput)
if strings.Contains(serverOutput, testMessage) {
fmt.Printf("Server received client message: %s\n", serverOutput)
break
for _, tc := range testCases {
in := integration.NewAppsIntegration(name, tc.Name, clientCmd, serverCmd, tc.Args, serverArgs, true)
in.ServerStdout(tc.ServerOutMatchFun)
in.ServerStderr(tc.ServerErrMatchFun)
in.ClientStdout(tc.ClientOutMatchFun)
in.ClientStderr(tc.ClientErrMatchFun)

hostAddr := integration.HostAddr

IAPairs := integration.IAPairs(hostAddr)
IAPairs = IAPairs[:5]

if err := integration.RunTests(in, IAPairs, integration.DefaultClientTimeout, 250 * time.Millisecond); err != nil {
t.Fatalf("Error during tests err: %v", err)
}
}
if err := serverStdoutScanner.Err(); err != nil {
t.Fatalf("Server failed to receive `%s`. Output=%s", testMessage, fullServerOutput)
}


func wrapperCommand(tmpDir string, inputSource string, command string) (wrapperCmd string, err error){
wrapperCmd = path.Join(tmpDir, fmt.Sprintf("%s_wrapper.sh", serverBin))
f, err := os.OpenFile(wrapperCmd, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
return "", errors.New(fmt.Sprintf("failed to create %s: %v", wrapperCmd, err))
}
defer f.Close()
w := bufio.NewWriter(f)
defer w.Flush()
_, _ = w.WriteString(fmt.Sprintf("#!/bin/bash\ntimeout 5 /bin/bash -c \"%s | %s $1 $2 $3 $4\"",
inputSource, command))
return wrapperCmd, nil
}
2 changes: 2 additions & 0 deletions pkg/appnet/appquic/appquic.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func DialAddr(raddr *snet.UDPAddr, host string, tlsConf *tls.Config, quicConf *q
if err != nil {
return nil, err
}
host = appnet.MangleSCIONAddr(host)
session, err := quic.Dial(sconn, raddr, host, tlsConf, quicConf)
if err != nil {
return nil, err
Expand All @@ -110,6 +111,7 @@ func DialAddrEarly(raddr *snet.UDPAddr, host string, tlsConf *tls.Config, quicCo
if err != nil {
return nil, err
}
host = appnet.MangleSCIONAddr(host)
session, err := quic.DialEarly(sconn, raddr, host, tlsConf, quicConf)
if err != nil {
return nil, err
Expand Down
48 changes: 48 additions & 0 deletions pkg/appnet/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,51 @@ func addrFromString(address string) (snet.SCIONAddress, error) {
func addrToString(addr snet.SCIONAddress) string {
return fmt.Sprintf("%s,[%s]", addr.IA, addr.Host)
}

// MangleSCIONAddr mangles a SCION address string (if it is one) so it can be
// safely used in the host part of a URL.
func MangleSCIONAddr(address string) string {

raddr, err := snet.ParseUDPAddr(address)
if err != nil {
return address
}

// Turn this into [IA,IP]:port format. This is a valid host in a URI, as per
// the "IP-literal" case in RFC 3986, §3.2.2.
// Unfortunately, this is not currently compatible with snet.ParseUDPAddr,
// so this will have to be _unmangled_ before use.
mangledAddr := fmt.Sprintf("[%s,%s]", raddr.IA, raddr.Host.IP)
if raddr.Host.Port != 0 {
mangledAddr += fmt.Sprintf(":%d", raddr.Host.Port)
}
return mangledAddr
}

// UnmangleSCIONAddr returns a SCION address that can be parsed with
// with snet.ParseUDPAddr.
// If the input is not a SCION address (e.g. a hostname), the address is
// returned unchanged.
// This parses the address, so that it can safely join host and port, with the
// brackets in the right place. Yes, this means this will be parsed twice.
//
// Assumes that address always has a port (this is enforced by the http3
// roundtripper code)
func UnmangleSCIONAddr(address string) string {
host, port, err := net.SplitHostPort(address)
if err != nil || port == "" {
panic(fmt.Sprintf("UnmangleSCIONAddr assumes that address is of the form host:port %s", err))
}
// brackets are removed from [I-A,IP] part by SplitHostPort, so this can be
// parsed with ParseUDPAddr:
udpAddr, err := snet.ParseUDPAddr(host)
if err != nil {
return address
}
p, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return address
}
udpAddr.Host.Port = int(p)
return udpAddr.String()
}
16 changes: 16 additions & 0 deletions pkg/integration/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,19 @@ func RegExp(regularExpression string) func(prev bool, line string) bool {
return prev || matched // return true if any output line matches the expression
}
}

func NoPanic() func(prev bool, line string) bool {
return func(prev bool, line string) bool {
matched, err := regexp.MatchString("^.*panic: .*$", line)
if err != nil {
// invalid regexp, don't count as a match
return prev
}
if init, err := regexp.MatchString("^.*Registered with dispatcher.*$", line); err == nil {
if init {
return init
}
}
return prev && !matched
}
}
26 changes: 25 additions & 1 deletion pkg/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func RunTests(in sintegration.Integration, pairs []sintegration.IAPair, clientTi
}
}(&serverClosers)
for _, dst := range dsts {
c, err := sintegration.StartServer(in, dst)
c, err := StartServer(in, dst)
if err != nil {
log.Error(fmt.Sprintf("Error in server: %s", dst.String()), "err", err)
return err
Expand Down Expand Up @@ -232,3 +232,27 @@ func findAnyHostInLocalAS(ctx context.Context, sciondConn sciond.Connector) (net
}
return bsAddr.IP, nil
}

// Duplicated from "github.com/scionproto/scion/go/lib/integration", but do not swallow error
func (s *serverStop) Close() error {
s.cancel()
err := s.wait.Wait()
return err // Do return the error
}

type serverStop struct {
cancel context.CancelFunc
wait sintegration.Waiter
}

// StartServer runs a server. The server can be stopped by calling Close() on the returned Closer.
// To start a server with a custom context use in.StartServer directly.
func StartServer(in sintegration.Integration, dst *snet.UDPAddr) (io.Closer, error) {
serverCtx, serverCancel := context.WithCancel(context.Background())
s, err := in.StartServer(serverCtx, dst)
if err != nil {
serverCancel()
return nil, err
}
return &serverStop{serverCancel, s}, nil
}
Loading