Skip to content

Commit

Permalink
Refactoring and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Alim Shapiev committed Nov 3, 2024
1 parent bf61e14 commit ed4022c
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 113 deletions.
66 changes: 4 additions & 62 deletions x/configurl/fake.go
Original file line number Diff line number Diff line change
@@ -1,80 +1,22 @@
// Copyright 2024 The Outline Authors
//
// 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
//
// https://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.

package configurl

import (
"context"
"fmt"
"github.com/Jigsaw-Code/outline-sdk/x/fake"
"strconv"

"github.com/Jigsaw-Code/outline-sdk/transport"
"github.com/Jigsaw-Code/outline-sdk/x/fake"
)

// tls wikipedia request
var tlsData = [517]byte{
0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x03, 0x5f,
0x6f, 0x2c, 0xed, 0x13, 0x22, 0xf8, 0xdc, 0xb2, 0xf2, 0x60, 0x48, 0x2d, 0x72,
0x66, 0x6f, 0x57, 0xdd, 0x13, 0x9d, 0x1b, 0x37, 0xdc, 0xfa, 0x36, 0x2e, 0xba,
0xf9, 0x92, 0x99, 0x3a, 0x20, 0xf9, 0xdf, 0x0c, 0x2e, 0x8a, 0x55, 0x89, 0x82,
0x31, 0x63, 0x1a, 0xef, 0xa8, 0xbe, 0x08, 0x58, 0xa7, 0xa3, 0x5a, 0x18, 0xd3,
0x96, 0x5f, 0x04, 0x5c, 0xb4, 0x62, 0xaf, 0x89, 0xd7, 0x0f, 0x8b, 0x00, 0x3e,
0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc,
0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24,
0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0,
0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c,
0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x01,
0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x14, 0x00, 0x00, 0x11, 0x77, 0x77, 0x77,
0x2e, 0x77, 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x6f, 0x72,
0x67, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x16,
0x00, 0x14, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x01,
0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0e,
0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e,
0x31, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00,
0x00, 0x0d, 0x00, 0x2a, 0x00, 0x28, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,
0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05,
0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, 0x01, 0x03,
0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03,
0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01,
0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x11, 0x8c, 0xb8,
0x8c, 0xe8, 0x8a, 0x08, 0x90, 0x1e, 0xee, 0x19, 0xd9, 0xdd, 0xe8, 0xd4, 0x06,
0xb1, 0xd1, 0xe2, 0xab, 0xe0, 0x16, 0x63, 0xd6, 0xdc, 0xda, 0x84, 0xa4, 0xb8,
0x4b, 0xfb, 0x0e, 0x00, 0x15, 0x00, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
var httpData = []byte("GET / HTTP/1.1\r\nHost: www.wikipedia.org\r\n\r\n")
var udpData [64]byte

func registerFakeStreamDialer(r TypeRegistry[transport.StreamDialer], typeID string, newSD BuildFunc[transport.StreamDialer]) {
r.RegisterType(typeID, func(ctx context.Context, config *Config) (transport.StreamDialer, error) {
sd, err := newSD(ctx, config.BaseConfig)
if err != nil {
return nil, err
}
prefixBytesStr := config.URL.Opaque
prefixBytes, err := strconv.Atoi(prefixBytesStr)
settings, err := fake.ParseSettings(config.URL.Opaque)
if err != nil {
return nil, fmt.Errorf("prefixBytes is not a number: %v. Fake config should be in fake:<number> format", prefixBytesStr)
return nil, fmt.Errorf("failed to parse settings: %w", err)
}
// TODO: Read fake data from the CLI or use a default value (depending on the protocol).
var fakeData []byte
// TODO: Read fake offset from the CLI
var fakeOffset int64 = 0
// TODO: Read fake TTL from the CLI or use a default value (8)
var fakeTtl int = 8
// TODO: Read md5 signature from the CLI or use a default value (false).
var md5Sig bool = false
return fake.NewStreamDialer(sd, int64(prefixBytes), fakeData, fakeOffset, fakeTtl, md5Sig)
return fake.NewStreamDialer(sd, settings.FakeData, settings.FakeOffset, settings.FakeBytes, settings.FakeTtl, settings.Md5Sig)
})
}
57 changes: 57 additions & 0 deletions x/fake/settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fake

import (
"fmt"
"strconv"
"strings"
)

const defaultTtl = 8

type Settings struct {
FakeData []byte
FakeOffset int64
FakeBytes int64
FakeTtl int
Md5Sig bool
}

// ParseSettings expects the settings to be in the format "[data(str)]:<offset(int)>:<bytes(int)>:<ttl(int)>:<Md5Sig(int=0/1)>"
func ParseSettings(raw string) (*Settings, error) {
result := Settings{
FakeTtl: defaultTtl,
}
parts := strings.Split(raw, ":")
if len(parts) > 0 && parts[0] != "" {
result.FakeData = []byte(parts[0])
}
if len(parts) > 1 && parts[1] != "" {
offset, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse offset: %w", err)
}
result.FakeOffset = offset
}
if len(parts) > 2 && parts[2] != "" {
bytes, err := strconv.ParseInt(parts[2], 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse bytes: %w", err)
}
result.FakeBytes = bytes
}
if len(parts) > 3 && parts[3] != "" {
ttl, err := strconv.Atoi(parts[3])
if err != nil {
return nil, fmt.Errorf("failed to parse TTL: %w", err)
}
result.FakeTtl = ttl
}
if len(parts) > 4 && parts[4] != "" {
md5Sig, err := strconv.ParseBool(parts[4])
if err != nil {
return nil, fmt.Errorf("failed to parse MD5 signature flag: %w", err)
}
result.Md5Sig = md5Sig
}
return &result, nil
}
57 changes: 49 additions & 8 deletions x/fake/stream_dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,64 @@ import (
"context"
"errors"
"fmt"
"github.com/Jigsaw-Code/outline-sdk/x/fake/signature"
"github.com/Jigsaw-Code/outline-sdk/x/md5signature"
"net"

"github.com/Jigsaw-Code/outline-sdk/transport"
)

// Example of fake data for TLS
var defaultTlsFakeData = [517]byte{
0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x03, 0x5f,
0x6f, 0x2c, 0xed, 0x13, 0x22, 0xf8, 0xdc, 0xb2, 0xf2, 0x60, 0x48, 0x2d, 0x72,
0x66, 0x6f, 0x57, 0xdd, 0x13, 0x9d, 0x1b, 0x37, 0xdc, 0xfa, 0x36, 0x2e, 0xba,
0xf9, 0x92, 0x99, 0x3a, 0x20, 0xf9, 0xdf, 0x0c, 0x2e, 0x8a, 0x55, 0x89, 0x82,
0x31, 0x63, 0x1a, 0xef, 0xa8, 0xbe, 0x08, 0x58, 0xa7, 0xa3, 0x5a, 0x18, 0xd3,
0x96, 0x5f, 0x04, 0x5c, 0xb4, 0x62, 0xaf, 0x89, 0xd7, 0x0f, 0x8b, 0x00, 0x3e,
0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc,
0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24,
0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0,
0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c,
0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x01,
0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x14, 0x00, 0x00, 0x11, 0x77, 0x77, 0x77,
0x2e, 0x77, 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x6f, 0x72,
0x67, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x16,
0x00, 0x14, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x01,
0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0e,
0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e,
0x31, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00,
0x00, 0x0d, 0x00, 0x2a, 0x00, 0x28, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,
0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05,
0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, 0x01, 0x03,
0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03,
0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01,
0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x11, 0x8c, 0xb8,
0x8c, 0xe8, 0x8a, 0x08, 0x90, 0x1e, 0xee, 0x19, 0xd9, 0xdd, 0xe8, 0xd4, 0x06,
0xb1, 0xd1, 0xe2, 0xab, 0xe0, 0x16, 0x63, 0xd6, 0xdc, 0xda, 0x84, 0xa4, 0xb8,
0x4b, 0xfb, 0x0e, 0x00, 0x15, 0x00, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

// Example of fake data for HTTP
var defaultHttpFakeData = []byte("GET / HTTP/1.1\r\nHost: www.wikipedia.org\r\n\r\n")

type fakeDialer struct {
dialer transport.StreamDialer
splitPoint int64
fakeData []byte
fakeOffset int64
fakeBytes int64
fakeTtl int
md5Sig bool
}

var _ transport.StreamDialer = (*fakeDialer)(nil)

// NewStreamDialer creates a [transport.StreamDialer] that writes "fakeData" in the beginning of the stream and
// then splits the outgoing stream after writing "fakeBytes" bytes using [FakeWriter].
// NewStreamDialer creates a [transport.StreamDialer] that writes "FakeData" in the beginning of the stream and
// then splits the outgoing stream after writing "FakeBytes" bytes using [FakeWriter].
func NewStreamDialer(
dialer transport.StreamDialer,
prefixBytes int64,
fakeData []byte,
fakeOffset int64,
fakeBytes int64,
fakeTtl int,
md5Sig bool,
) (transport.StreamDialer, error) {
Expand All @@ -50,9 +84,9 @@ func NewStreamDialer(
}
return &fakeDialer{
dialer: dialer,
splitPoint: prefixBytes,
fakeData: fakeData,
fakeOffset: fakeOffset,
fakeBytes: fakeBytes,
fakeTtl: fakeTtl,
md5Sig: md5Sig,
}, nil
Expand All @@ -65,10 +99,17 @@ func (d *fakeDialer) DialStream(ctx context.Context, remoteAddr string) (transpo
return nil, err
}
if tcpInnerConn, isTcp := innerConn.(*net.TCPConn); isTcp && d.md5Sig {
err := signature.Add(tcpInnerConn, remoteAddr, tcpInnerConn.RemoteAddr().String())
err := md5signature.Add(tcpInnerConn, remoteAddr, tcpInnerConn.RemoteAddr().String())
if err != nil {
return nil, fmt.Errorf("failed to add MD5 signature: %w", err)
}
}
return transport.WrapConn(innerConn, innerConn, NewWriter(innerConn, d.splitPoint, d.fakeData, d.fakeOffset, d.fakeTtl)), nil
return transport.WrapConn(innerConn, innerConn, NewWriter(innerConn, d.fakeData, d.fakeOffset, d.fakeBytes, d.fakeTtl)), nil
}

func getDefaultFakeData(isHttp bool) []byte {
if isHttp {
return defaultHttpFakeData
}
return defaultTlsFakeData[:]
}
43 changes: 27 additions & 16 deletions x/fake/writer.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package fake

import (
"bufio"
"bytes"
"fmt"
"github.com/Jigsaw-Code/outline-sdk/x/packet"
"github.com/Jigsaw-Code/outline-sdk/x/ttl"
"io"
"net"
)

type fakeWriter struct {
writer io.Writer
fakeBytes int64
fakeData []byte
fakeOffset int64
fakeBytes int64
ttl int
}

Expand All @@ -26,11 +28,11 @@ type fakeWriterReaderFrom struct {
var _ io.ReaderFrom = (*fakeWriterReaderFrom)(nil)

// NewWriter creates a [io.Writer] that ensures the fake data is written before the real data.
// A write will end right after byte index fakeBytes - 1, before a write starting at byte index fakeBytes.
// For example, if you have a write of [0123456789], fakeData = [abc], fakeOffset = 1, and fakeBytes = 3,
// A write will end right after byte index FakeBytes - 1, before a write starting at byte index FakeBytes.
// For example, if you have a write of [0123456789], FakeData = [abc], FakeOffset = 1, and FakeBytes = 3,
// you will get writes [bc] and [0123456789]. If the input writer is a [io.ReaderFrom], the output writer will be too.
func NewWriter(writer io.Writer, fakeBytes int64, fakeData []byte, fakeOffset int64, fakeTtl int) io.Writer {
sw := &fakeWriter{writer, fakeBytes, fakeData, fakeOffset, fakeTtl}
func NewWriter(writer io.Writer, fakeData []byte, fakeOffset int64, fakeBytes int64, fakeTtl int) io.Writer {
sw := &fakeWriter{writer, fakeData, fakeOffset, fakeBytes, fakeTtl}
if rf, ok := writer.(io.ReaderFrom); ok {
return &fakeWriterReaderFrom{sw, rf}
}
Expand All @@ -39,7 +41,8 @@ func NewWriter(writer io.Writer, fakeBytes int64, fakeData []byte, fakeOffset in

func (w *fakeWriterReaderFrom) ReadFrom(source io.Reader) (written int64, err error) {
conn, isNetConn := w.writer.(net.Conn)
fakeData := w.getFakeData()
bufioReader := bufio.NewReader(source)
fakeData := w.getFakeData(bufioReader)
if fakeData != nil {
if isNetConn {
oldTtl, err := ttl.Set(conn, w.ttl)
Expand All @@ -66,7 +69,7 @@ func (w *fakeWriterReaderFrom) ReadFrom(source io.Reader) (written int64, err er

func (w *fakeWriter) Write(data []byte) (written int, err error) {
conn, isNetConn := w.writer.(net.Conn)
fakeData := w.getFakeData()
fakeData := w.getFakeData(bufio.NewReader(bytes.NewReader(data)))
if fakeData != nil {
if isNetConn {
oldTtl, err := ttl.Set(conn, w.ttl)
Expand All @@ -79,27 +82,35 @@ func (w *fakeWriter) Write(data []byte) (written int, err error) {
}
}()
}
fmt.Printf("Writing fake data with TTL %d:\n---\n%s\n---\n", w.ttl, fakeData)
fakeData = append(fakeData, make([]byte, len(data)-len(fakeData))...)
fakeN, err := w.writer.Write(fakeData)
written += fakeN
if err != nil {
return written, err
}
}
n, err := w.writer.Write(data)
written += n
fmt.Printf("Writing real data:\n---\n%s\n---\n", data)
//n, err := w.writer.Write(data)
//written += n
return written, err
}

func (w *fakeWriter) getFakeData() []byte {
if w.fakeOffset >= int64(len(w.fakeData)) {
func (w *fakeWriter) getFakeData(dataReader *bufio.Reader) []byte {
fakeData := w.fakeData
if fakeData == nil {
isHttp := packet.IsHTTP(dataReader)
fakeData = getDefaultFakeData(isHttp)
}
if w.fakeOffset >= int64(len(fakeData)) {
return nil
}
data := w.fakeData[w.fakeOffset:]
if w.fakeBytes < int64(len(data)) {
data = data[:w.fakeBytes]
fakeData = fakeData[w.fakeOffset:]
if w.fakeBytes < int64(len(fakeData)) {
fakeData = fakeData[:w.fakeBytes]
}
if len(data) == 0 {
if len(fakeData) == 0 {
return nil
}
return data
return fakeData
}
Loading

0 comments on commit ed4022c

Please sign in to comment.