Skip to content

Commit

Permalink
temp
Browse files Browse the repository at this point in the history
  • Loading branch information
weiiwang01 committed Apr 2, 2024
1 parent 21a77a3 commit a0ebb9b
Show file tree
Hide file tree
Showing 7 changed files with 508 additions and 203 deletions.
119 changes: 1 addition & 118 deletions aproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"bufio"
"context"
"encoding/binary"
"errors"
"flag"
"fmt"
Expand All @@ -13,126 +12,10 @@ import (
"os"
"os/signal"
"strings"

"golang.org/x/crypto/cryptobyte"
)

var version = "1.0.0"

// PrereadSNI pre-reads the Server Name Indication (SNI) from a TLS connection.
func PrereadSNI(conn *PrereadConn) (_ string, err error) {
defer conn.EndPreread()
defer func() {
if err != nil {
err = fmt.Errorf("failed to preread TLS client hello: %w", err)
}
}()
typeVersionLen := make([]byte, 5)
n, err := conn.Read(typeVersionLen)
if n != 5 {
return "", errors.New("too short")
}
if err != nil {
return "", err
}
if typeVersionLen[0] != 22 {
return "", errors.New("not a TCP handshake")
}
msgLen := binary.BigEndian.Uint16(typeVersionLen[3:])
buf := make([]byte, msgLen+5)
n, err = conn.Read(buf[5:])
if n != int(msgLen) {
return "", errors.New("too short")
}
if err != nil {
return "", err
}
copy(buf[:5], typeVersionLen)
return extractSNI(buf)
}

func extractSNI(data []byte) (string, error) {
s := cryptobyte.String(data)
var (
version uint16
random []byte
sessionId []byte
)

if !s.Skip(9) ||
!s.ReadUint16(&version) || !s.ReadBytes(&random, 32) ||
!s.ReadUint8LengthPrefixed((*cryptobyte.String)(&sessionId)) {
return "", fmt.Errorf("failed to parse TLS client hello version, random or session id")
}

var cipherSuitesData cryptobyte.String
if !s.ReadUint16LengthPrefixed(&cipherSuitesData) {
return "", fmt.Errorf("failed to parse TLS client hello cipher suites")
}

var cipherSuites []uint16
for !cipherSuitesData.Empty() {
var suite uint16
if !cipherSuitesData.ReadUint16(&suite) {
return "", fmt.Errorf("failed to parse TLS client hello cipher suites")
}
cipherSuites = append(cipherSuites, suite)
}

var compressionMethods []byte
if !s.ReadUint8LengthPrefixed((*cryptobyte.String)(&compressionMethods)) {
return "", fmt.Errorf("failed to parse TLS client hello compression methods")
}

if s.Empty() {
// ClientHello is optionally followed by extension data
return "", fmt.Errorf("no extension data in TLS client hello")
}

var extensions cryptobyte.String
if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
return "", fmt.Errorf("failed to parse TLS client hello extensions")
}

finalServerName := ""
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) ||
!extensions.ReadUint16LengthPrefixed(&extData) {
return "", fmt.Errorf("failed to parse TLS client hello extension")
}
if extension != 0 {
continue
}
var nameList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
return "", fmt.Errorf("failed to parse server name extension")
}

for !nameList.Empty() {
var nameType uint8
var serverName cryptobyte.String
if !nameList.ReadUint8(&nameType) ||
!nameList.ReadUint16LengthPrefixed(&serverName) ||
serverName.Empty() {
return "", fmt.Errorf("failed to parse server name indication extension")
}
if nameType != 0 {
continue
}
if len(finalServerName) != 0 {
return "", fmt.Errorf("multiple names of the same name_type are prohibited in server name extension")
}
finalServerName = string(serverName)
if strings.HasSuffix(finalServerName, ".") {
return "", fmt.Errorf("SNI name ends with a trailing dot")
}
}
}
return finalServerName, nil
}

// PrereadHTTPHost pre-reads the HTTP Host header from an HTTP connection.
func PrereadHTTPHost(conn *PrereadConn) (_ string, err error) {
defer func() {
Expand Down Expand Up @@ -236,7 +119,7 @@ func main() {
for {
conn, err := listener.Accept()
if err != nil {
logger.ErrorContext(ctx, "failed to accept connection", "error", err)
logger.ErrorContext(ctx, "failed to accept connection", "error", err)
continue
}
go HandleConn(conn, proxy)

Check failure on line 125 in aproxy.go

View workflow job for this annotation

GitHub Actions / Run Tests

undefined: proxy

Check failure on line 125 in aproxy.go

View workflow job for this annotation

GitHub Actions / Run Tests

not enough arguments in call to HandleConn
Expand Down
52 changes: 0 additions & 52 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,8 @@ package main

import (
"net"
"sync"
)

// PrereadConn is a wrapper around net.Conn that supports pre-reading from the underlying connection.
// Any Read before the EndPreread can be undone and read again by calling the EndPreread function.
type PrereadConn struct {
ended bool
buf []byte
mu sync.Mutex
conn net.Conn
}

// EndPreread ends the pre-reading phase. Any Read before will be undone and data in the stream can be read again.
// EndPreread can be only called once.
func (c *PrereadConn) EndPreread() {
c.mu.Lock()
defer c.mu.Unlock()
if c.ended {
panic("call EndPreread after preread has ended or hasn't started")
}
c.ended = true
}

// Read reads from the underlying connection. Read during the pre-reading phase can be undone by EndPreread.
func (c *PrereadConn) Read(p []byte) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.ended {
n = copy(p, c.buf)
bufLen := len(c.buf)
c.buf = c.buf[n:]
if n == len(p) || (bufLen > 0 && bufLen == n) {
return n, nil
}
rn, err := c.conn.Read(p[n:])
return rn + n, err
} else {
n, err = c.conn.Read(p)
c.buf = append(c.buf, p[:n]...)
return n, err
}
}

// Write writes data to the underlying connection.
func (c *PrereadConn) Write(p []byte) (n int, err error) {
return c.conn.Write(p)
}

// NewPrereadConn wraps the network connection and return a *PrereadConn.
// It's recommended to not touch the original connection after wrapped.
func NewPrereadConn(conn net.Conn) *PrereadConn {
return &PrereadConn{conn: conn}
}

// ConsignedConn wraps the PrereadConn and provides some slots to attach information related to the connection.
type ConsignedConn struct {
*PrereadConn
Expand Down
32 changes: 0 additions & 32 deletions conn_test.go

This file was deleted.

54 changes: 54 additions & 0 deletions relay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"errors"
"io"
"net"
"time"
)

type tcpForwarder struct {
Fwmark uint32
ReadTimeout time.Duration
WriteTimeout time.Duration
}

func (f *tcpForwarder) copyBuffer(dst net.Conn, src net.Conn) (written int64, err error) {
buf := make([]byte, 32*1024)
for {
err = src.SetReadDeadline(time.Now().Add(f.ReadTimeout))
if err != nil {
break
}
nr, er := src.Read(buf)
if nr > 0 {
err = src.SetWriteDeadline(time.Now().Add(f.ReadTimeout))
if err != nil {
break
}
nw, ew := dst.Write(buf[0:nr])
if nw < 0 || nr < nw {
nw = 0
if ew == nil {
ew = errors.New("invalid write result")
}
}
written += int64(nw)
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}
Loading

0 comments on commit a0ebb9b

Please sign in to comment.