Skip to content

Commit

Permalink
Fix TTL and TCP MD5 Signature
Browse files Browse the repository at this point in the history
  • Loading branch information
Alim Shapiev committed Nov 2, 2024
1 parent 6da1412 commit bf61e14
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 78 deletions.
5 changes: 2 additions & 3 deletions x/configurl/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,12 @@ func registerFakeStreamDialer(r TypeRegistry[transport.StreamDialer], typeID str
if err != nil {
return nil, fmt.Errorf("prefixBytes is not a number: %v. Fake config should be in fake:<number> format", prefixBytesStr)
}
// TODO: Read fake data from the CLI
// TODO: Read fake data from the CLI or use a default value (depending on the protocol).
var fakeData []byte
// or use a default value (depending on the protocol).
// 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 int64 = 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)
Expand Down
63 changes: 0 additions & 63 deletions x/fake/md5_sig.go

This file was deleted.

79 changes: 79 additions & 0 deletions x/fake/signature/signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package signature

import (
"fmt"
"golang.org/x/sys/unix"
"net"
"unsafe"
)

const socketFlag = 14

type signature struct {
Addr [16]byte
Len uint16
Flags uint16
Key [80]byte
}

func Add(conn *net.TCPConn, remoteAddr string, data string) error {
ip := net.ParseIP(remoteAddr)
if ip == nil {
return fmt.Errorf("invalid remote IP address: %s", remoteAddr)
}

address, err := ip.To16().MarshalText()
if err != nil {
return fmt.Errorf("failed to marshal IP address: %w", err)
}

key := []byte(data)

sig := signature{
Addr: [16]byte(address),
Len: uint16(len(data)),
Key: [80]byte(key),
}

if err := setOption(conn, sig); err != nil {
return fmt.Errorf("failed to set socket option: %w", err)
}

return nil
}

func setOption(conn *net.TCPConn, md5sig signature) error {
file, err := conn.File()
if err != nil {
return fmt.Errorf("failed to get file descriptor: %w", err)
}
defer file.Close()

size := unsafe.Sizeof(md5sig)
buffer := (*[unsafe.Sizeof(md5sig)]byte)(unsafe.Pointer(&md5sig))[:size]
fd := int(file.Fd())

err = unix.SetsockoptString(fd, unix.IPPROTO_TCP, socketFlag, string(buffer))
if err != nil {
return fmt.Errorf("failed to set TCP_MD5SIG: %w", err)
}

return nil
}

func Remove(conn *net.TCPConn) error {
file, err := conn.File()
if err != nil {
return fmt.Errorf("failed to get underlying file descriptor: %w", err)
}
defer file.Close()

fd := int(file.Fd())

err = unix.SetsockoptString(fd, unix.IPPROTO_TCP, socketFlag, "")
if err != nil {
return fmt.Errorf("failed to clear TCP_MD5SIG: %w", err)
}

return nil
}
12 changes: 6 additions & 6 deletions x/fake/stream_dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"errors"
"fmt"
"github.com/Jigsaw-Code/outline-sdk/x/fake/signature"
"net"

"github.com/Jigsaw-Code/outline-sdk/transport"
Expand All @@ -28,7 +29,7 @@ type fakeDialer struct {
splitPoint int64
fakeData []byte
fakeOffset int64
fakeTtl int64
fakeTtl int
md5Sig bool
}

Expand All @@ -41,7 +42,7 @@ func NewStreamDialer(
prefixBytes int64,
fakeData []byte,
fakeOffset int64,
fakeTtl int64,
fakeTtl int,
md5Sig bool,
) (transport.StreamDialer, error) {
if dialer == nil {
Expand All @@ -63,11 +64,10 @@ func (d *fakeDialer) DialStream(ctx context.Context, remoteAddr string) (transpo
if err != nil {
return nil, err
}
if d.md5Sig {
conn := innerConn.(*net.TCPConn)
err := setMd5Sig(conn, remoteAddr, conn.RemoteAddr().String())
if tcpInnerConn, isTcp := innerConn.(*net.TCPConn); isTcp && d.md5Sig {
err := signature.Add(tcpInnerConn, remoteAddr, tcpInnerConn.RemoteAddr().String())
if err != nil {
return nil, fmt.Errorf("failed to set MD5 signature: %w", err)
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
Expand Down
31 changes: 29 additions & 2 deletions x/fake/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package fake

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

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

var _ io.Writer = (*fakeWriter)(nil)
Expand All @@ -26,7 +29,7 @@ var _ io.ReaderFrom = (*fakeWriterReaderFrom)(nil)
// 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 int64) io.Writer {
func NewWriter(writer io.Writer, fakeBytes int64, fakeData []byte, fakeOffset int64, fakeTtl int) io.Writer {
sw := &fakeWriter{writer, fakeBytes, fakeData, fakeOffset, fakeTtl}
if rf, ok := writer.(io.ReaderFrom); ok {
return &fakeWriterReaderFrom{sw, rf}
Expand All @@ -35,8 +38,20 @@ 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()
if fakeData != nil {
if isNetConn {
oldTtl, err := ttl.Set(conn, w.ttl)
if err != nil {
return written, fmt.Errorf("failed to set TTL before writing fake data: %w", err)
}
defer func() {
if _, err = ttl.Set(conn, oldTtl); err != nil {
err = fmt.Errorf("failed to restore TTL after writing fake data: %w", err)
}
}()
}
fakeN, err := w.rf.ReadFrom(bytes.NewReader(fakeData))
written += fakeN
if err != nil {
Expand All @@ -50,8 +65,20 @@ 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()
if fakeData != nil {
if isNetConn {
oldTtl, err := ttl.Set(conn, w.ttl)
if err != nil {
return written, fmt.Errorf("failed to set TTL before writing fake data: %w", err)
}
defer func() {
if _, err = ttl.Set(conn, oldTtl); err != nil {
err = fmt.Errorf("failed to restore TTL after writing fake data: %w", err)
}
}()
}
fakeN, err := w.writer.Write(fakeData)
written += fakeN
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions x/fake/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@ func TestWrite_Compound(t *testing.T) {
fakeData1 := []byte("F")
fakeBytes1 := int64(1)
fakeOffset1 := int64(0)
fakeTtl1 := int64(0)
fakeTtl1 := 0
writer1 := NewWriter(&innerWriter, fakeBytes1, fakeData1, fakeOffset1, fakeTtl1)

// Second fakeWriter: fakeBytes=3, fakeData="ake d", fakeOffset=0
fakeData2 := []byte("ake") // Total fakeData now: "Fake d"
fakeBytes2 := int64(3)
fakeOffset2 := int64(0)
fakeTtl2 := int64(0)
fakeTtl2 := 0
fakeWriter := NewWriter(writer1, fakeBytes2, fakeData2, fakeOffset2, fakeTtl2)

// Write "Request"
Expand Down Expand Up @@ -205,14 +205,14 @@ func TestReadFrom_Compound(t *testing.T) {
fakeData1 := []byte("Fake ")
fakeBytes1 := int64(3)
fakeOffset1 := int64(0)
fakeTtl1 := int64(0)
fakeTtl1 := 0
writer1 := NewWriter(&innerWriter, fakeBytes1, fakeData1, fakeOffset1, fakeTtl1)

// Second fakeWriter: fakeBytes=5, fakeData="data", fakeOffset=0
fakeData2 := []byte("data")
fakeBytes2 := int64(5)
fakeOffset2 := int64(0)
fakeTtl2 := int64(0)
fakeTtl2 := 0
writer2 := NewWriter(writer1, fakeBytes2, fakeData2, fakeOffset2, fakeTtl2)

n, err := writer2.Write([]byte("Request"))
Expand Down
33 changes: 33 additions & 0 deletions x/ttl/ttl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ttl

import (
"fmt"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net"
"net/netip"
)

func Set(conn net.Conn, ttl int) (old int, err error) {
addr, err := netip.ParseAddrPort(conn.RemoteAddr().String())
if err != nil {
return 0, err
}

switch {
case addr.Addr().Is4():
conn := ipv4.NewConn(conn)
old, _ = conn.TTL()
if err := conn.SetTTL(ttl); err != nil {
return 0, fmt.Errorf("failed to set TTL: %w", err)
}
case addr.Addr().Is6():
conn := ipv6.NewConn(conn)
old, _ = conn.HopLimit()
if err := conn.SetHopLimit(ttl); err != nil {
return 0, fmt.Errorf("failed to set hop limit: %w", err)
}
}

return
}

0 comments on commit bf61e14

Please sign in to comment.