mirror of
https://github.com/xvzc/SpoofDPI.git
synced 2025-01-06 22:27:31 +00:00
225 lines
4.8 KiB
Go
225 lines
4.8 KiB
Go
package test
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
)
|
|
import "github.com/docker/docker/api/types/network"
|
|
|
|
const proxyDockerfile = `
|
|
FROM golang:alpine as builder
|
|
|
|
WORKDIR /SpoofDPI
|
|
|
|
COPY . .
|
|
|
|
RUN go build -o spoof-dpi ./cmd/spoof-dpi/main.go
|
|
|
|
FROM alpine:latest
|
|
|
|
WORKDIR /
|
|
|
|
COPY --from=builder /SpoofDPI/spoof-dpi .
|
|
`
|
|
|
|
type TarConsumer interface {
|
|
Accept(t *tar.Writer) error
|
|
}
|
|
|
|
type TarConsumerFn func(*tar.Writer) error
|
|
|
|
func (f TarConsumerFn) Accept(t *tar.Writer) error {
|
|
return f(t)
|
|
}
|
|
|
|
type FilePredicate interface {
|
|
Test(info FileInfo) bool
|
|
}
|
|
|
|
type FileInfo struct {
|
|
AbsPath string
|
|
Info fs.FileInfo
|
|
}
|
|
|
|
type FilePredicateFn func(info FileInfo) bool
|
|
|
|
func (f FilePredicateFn) Test(info FileInfo) bool {
|
|
return f(info)
|
|
}
|
|
|
|
func WriteHeaderAndContent(t *tar.Writer, h *tar.Header, content []byte) error {
|
|
if err := t.WriteHeader(h); err != nil {
|
|
return err
|
|
}
|
|
if _, err := t.Write(content); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func WriteFile(t *tar.Writer, mode int64, name string, size int64, file io.Reader) error {
|
|
hdr := &tar.Header{
|
|
Name: name,
|
|
Mode: mode,
|
|
Size: size,
|
|
Typeflag: tar.TypeReg,
|
|
Format: tar.FormatGNU,
|
|
}
|
|
if content, err := io.ReadAll(file); err == nil {
|
|
if err := WriteHeaderAndContent(t, hdr, content); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func WriteDir(t *tar.Writer, mode int64, dirName string) error {
|
|
return WriteHeaderAndContent(t, &tar.Header{
|
|
Mode: int64(os.FileMode(mode) | os.ModeDir),
|
|
Name: dirName,
|
|
Size: 0,
|
|
}, nil)
|
|
}
|
|
|
|
func MakeTar(sourceDir string, filePredicate FilePredicate, consumers ...TarConsumer) (io.Reader, error) {
|
|
absSourceDir, err := filepath.Abs(sourceDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
filechan := make(chan FileInfo)
|
|
walkCtx, cancelWalk := context.WithCancel(context.Background())
|
|
defer cancelWalk()
|
|
go func() {
|
|
filepath.Walk(sourceDir, func(path string, info fs.FileInfo, err error) error {
|
|
abs, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
select {
|
|
case filechan <- FileInfo{
|
|
Info: info,
|
|
AbsPath: abs,
|
|
}:
|
|
case <-walkCtx.Done():
|
|
return fmt.Errorf("canceled")
|
|
}
|
|
return nil
|
|
})
|
|
close(filechan)
|
|
}()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
t := tar.NewWriter(&buf)
|
|
|
|
for f := range filechan {
|
|
if !(filePredicate.Test(f)) {
|
|
continue
|
|
}
|
|
info := f.Info
|
|
absFilePath := f.AbsPath
|
|
relName, err := filepath.Rel(absSourceDir, absFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if info.IsDir() {
|
|
if err := WriteDir(t, int64(info.Mode()), relName); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
file, err := os.OpenFile(absFilePath, os.O_RDONLY, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = WriteFile(t, int64(info.Mode()), relName, info.Size(), file); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
for _, consumer := range consumers {
|
|
err := consumer.Accept(t)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := t.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
return bytes.NewReader(buf.Bytes()), nil
|
|
}
|
|
|
|
type Logging interface {
|
|
Printf(format string, v ...interface{})
|
|
}
|
|
|
|
func SpoofDPIContainer(port uint16, log Logging, proxyRunArgs []string) (testcontainers.Container, error) {
|
|
ctx := context.Background()
|
|
projectTar, err := MakeTar("../", FilePredicateFn(func(i FileInfo) bool {
|
|
if i.Info.IsDir() {
|
|
return false
|
|
}
|
|
if strings.Contains(i.AbsPath, ".git") {
|
|
return false
|
|
}
|
|
if strings.Contains(i.AbsPath, "Dockerfile") {
|
|
return false
|
|
}
|
|
if strings.Contains(i.AbsPath, ".idea") {
|
|
return false
|
|
}
|
|
return true
|
|
}), TarConsumerFn(func(writer *tar.Writer) error {
|
|
return WriteFile(writer, 0644, "Dockerfile", int64(len(proxyDockerfile)), strings.NewReader(proxyDockerfile))
|
|
}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cmd := []string{"./spoof-dpi"}
|
|
cmd = append(cmd, "-port", strconv.Itoa(int(port)))
|
|
cmd = append(cmd, proxyRunArgs...)
|
|
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
|
|
|
ContainerRequest: testcontainers.ContainerRequest{
|
|
FromDockerfile: testcontainers.FromDockerfile{
|
|
Tag: "spoof-dpi",
|
|
Repo: "spoof-dpi",
|
|
PrintBuildLog: true,
|
|
ContextArchive: projectTar,
|
|
Dockerfile: "Dockerfile",
|
|
KeepImage: true,
|
|
},
|
|
WaitingFor: wait.ForLog("[PROXY]").WithOccurrence(1),
|
|
Cmd: cmd,
|
|
LogConsumerCfg: &testcontainers.LogConsumerConfig{
|
|
Consumers: []testcontainers.LogConsumer{
|
|
&testcontainers.StdoutLogConsumer{},
|
|
},
|
|
},
|
|
HostConfigModifier: func(config *container.HostConfig) {
|
|
config.NetworkMode = network.NetworkHost
|
|
},
|
|
},
|
|
Logger: log,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|