Compare commits

..

27 Commits

Author SHA1 Message Date
世界
18162df6de Update documentation 2022-11-21 14:28:28 +08:00
世界
0c48aaef08 Fix default dns transport strategy 2022-11-21 14:26:56 +08:00
世界
871f4aebbd Remove unused 2022-11-21 14:26:53 +08:00
世界
12ad50d427 Fix connect packet connection for mux client 2022-11-21 14:26:53 +08:00
世界
14329ad62d Remove follow in update script 2022-11-21 14:26:45 +08:00
世界
eb15e885b9 Support x/h2 v0.2.0 deadline 2022-11-21 14:26:43 +08:00
世界
a55726694a Fix tor geoip 2022-11-21 14:26:40 +08:00
世界
b0235c6257 Fix h2c transport 2022-11-21 14:26:30 +08:00
世界
9a01d52a2c Fix vmess request buffer 2022-11-21 14:21:11 +08:00
世界
2c0524578f Fix smux keep alive 2022-11-13 12:18:08 +08:00
Dreamacro
b10a8d55f3 Fix macOS Ventura process name match 2022-11-13 12:16:25 +08:00
世界
9a8de0f378 Fix decrypt xplus packet 2022-11-13 12:16:00 +08:00
世界
e5018383fe Fix copy pipe 2022-11-13 12:15:45 +08:00
世界
1e4f68b024 Update issue template 2022-11-13 12:14:20 +08:00
Fei1Yang
8c00885599 Update container action
* Fix non-native architectures build for Docker

* Tag latest image with a pre-defined tag

* Replace set-output commands
2022-11-13 12:14:19 +08:00
永雏塔菲
7cf5169c98 Add s390x architecture support
* Update debug.yml

Signed-off-by: 永雏塔菲 <108621198+taffychan@users.noreply.github.com>
2022-11-13 12:14:11 +08:00
世界
ce396466bd Add go1.18 debug build 2022-11-13 12:14:08 +08:00
世界
1635c98783 Bump version 2022-10-19 10:33:46 +08:00
Skyxim
cbcaa0f590 Check destination before udp connect 2022-10-19 10:33:28 +08:00
世界
763b93c021 Fix naive overflow 2022-10-19 10:33:28 +08:00
世界
00da3e0765 Fix sniff fragmented quic client hello 2022-10-19 10:33:28 +08:00
世界
46d7a78158 Fix ssh outbound 2022-10-19 10:31:43 +08:00
世界
de3f70195e Add binary to .gitignore 2022-10-19 10:30:05 +08:00
世界
3105b8c920 Bump version 2022-09-25 22:27:23 +08:00
世界
4c67ab1a54 Fix read source address from grpc-go 2022-09-25 22:27:23 +08:00
世界
84783c5359 Fix fqdn socks5 outbound connection 2022-09-25 14:44:39 +08:00
世界
22b16f82bd Fix missing source address from transport connection 2022-09-25 14:44:33 +08:00
33 changed files with 414 additions and 393 deletions

View File

@@ -12,7 +12,7 @@ body:
required: true
- label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true
- label: Yes, I've included all information below (version, config, log, etc).
- label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc).
required: true
- type: textarea

View File

@@ -55,6 +55,27 @@ jobs:
- name: Run Test
run: |
go test -v ./...
build_go118:
name: Debug build (Go 1.18)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.18.7
- name: Cache go module
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
key: go118-${{ hashFiles('**/go.sum') }}
- name: Run Test
run: |
go test -v ./...
cross:
strategy:
matrix:
@@ -128,6 +149,9 @@ jobs:
- name: linux-mips64el
goos: linux
goarch: mips64le
- name: linux-s390x
goos: linux
goarch: s390x
# darwin
- name: darwin-amd64
goos: darwin
@@ -192,4 +216,4 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: sing-box-${{ matrix.name }}
path: sing-box*
path: sing-box*

View File

@@ -15,6 +15,8 @@ jobs:
uses: actions/checkout@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Setup QEMU for Docker Buildx
uses: docker/setup-qemu-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
@@ -29,15 +31,18 @@ jobs:
- name: Get tag to build
id: tag
run: |
echo "latest=ghcr.io/sagernet/sing-box:latest" >> $GITHUB_OUTPUT
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.ref_name }}
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.ref_name }}" >> $GITHUB_OUTPUT
else
echo ::set-output name=tag::ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
fi
- name: Build and release Docker images
uses: docker/build-push-action@v2
with:
platforms: linux/386,linux/amd64
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
target: dist
tags: ${{ steps.tag.outputs.tag }}
push: true
tags: |
${{ steps.tag.outputs.latest }}
${{ steps.tag.outputs.versioned }}
push: true

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@
/*.db
/site/
/bin/
/dist/
/dist/
/sing-box

View File

@@ -24,6 +24,7 @@ builds:
- linux_amd64_v3
- linux_arm64
- linux_arm_7
- linux_s390x
- windows_amd64_v1
- windows_amd64_v3
- windows_386

View File

@@ -38,13 +38,25 @@ type myUpstreamHandlerWrapper struct {
}
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
w.metadata.Destination = metadata.Destination
return w.connectionHandler(ctx, conn, w.metadata)
myMetadata := w.metadata
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.connectionHandler(ctx, conn, myMetadata)
}
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
w.metadata.Destination = metadata.Destination
return w.packetHandler(ctx, conn, w.metadata)
myMetadata := w.metadata
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.packetHandler(ctx, conn, myMetadata)
}
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
@@ -78,13 +90,23 @@ func NewUpstreamContextHandler(
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
myMetadata := ContextFrom(ctx)
myMetadata.Destination = metadata.Destination
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.connectionHandler(ctx, conn, *myMetadata)
}
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
myMetadata := ContextFrom(ctx)
myMetadata.Destination = metadata.Destination
if metadata.Source.IsValid() {
myMetadata.Source = metadata.Source
}
if metadata.Destination.IsValid() {
myMetadata.Destination = metadata.Destination
}
return w.packetHandler(ctx, conn, *myMetadata)
}

View File

@@ -329,6 +329,23 @@ func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
return c.ExtendedConn.Write(b)
}
func (c *ClientPacketConn) ReadBuffer(buffer *buf.Buffer) (err error) {
if !c.responseRead {
err = c.readResponse()
if err != nil {
return
}
c.responseRead = true
}
var length uint16
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
if err != nil {
return
}
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
return
}
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
if !c.requestWrite {
defer buffer.Release()
@@ -343,6 +360,11 @@ func (c *ClientPacketConn) FrontHeadroom() int {
return 2
}
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
err = c.ReadBuffer(buffer)
return
}
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
return c.WriteBuffer(buffer)
}

View File

@@ -43,7 +43,7 @@ func ParseProtocol(name string) (Protocol, error) {
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Server(conn, nil)
session, err := smux.Server(conn, smuxConfig())
if err != nil {
return nil, err
}
@@ -58,7 +58,7 @@ func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
switch p {
case ProtocolSMux:
session, err := smux.Client(conn, nil)
session, err := smux.Client(conn, smuxConfig())
if err != nil {
return nil, err
}
@@ -70,6 +70,12 @@ func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
}
}
func smuxConfig() *smux.Config {
config := smux.DefaultConfig()
config.KeepAliveDisabled = true
return config
}
func yaMuxConfig() *yamux.Config {
config := yamux.DefaultConfig()
config.LogOutput = io.Discard

View File

@@ -5,6 +5,8 @@ import (
"encoding/binary"
"net/netip"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
@@ -29,6 +31,22 @@ func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, so
return &Info{ProcessPath: processName, UserId: -1}, nil
}
var structSize = func() int {
value, _ := syscall.Sysctl("kern.osrelease")
major, _, _ := strings.Cut(value, ".")
n, _ := strconv.ParseInt(major, 10, 64)
switch true {
case n >= 22:
return 408
default:
// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
// size/offset are round up (aligned) to 8 bytes in darwin
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
return 384
}
}()
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
var spath string
switch network {
@@ -53,7 +71,7 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
// size/offset are round up (aligned) to 8 bytes in darwin
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
itemSize := 384
itemSize := structSize
if network == N.NetworkTCP {
// rup8(sizeof(xtcpcb_n))
itemSize += 208

View File

@@ -24,8 +24,7 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil {
return nil, err
}
if typeByte&0x80 == 0 || typeByte&0x40 == 0 {
if typeByte&0x40 == 0 {
return nil, E.New("bad type byte")
}
var versionNumber uint32
@@ -145,9 +144,6 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
default:
return nil, E.New("bad packet number length")
}
if packetNumber != 0 {
return nil, E.New("bad packet number: ", packetNumber)
}
extHdrLen := hdrLen + int(packetNumberLength)
copy(newPacket[extHdrLen:hdrLen+4], packet[extHdrLen:])
data := newPacket[extHdrLen : int(packetLen)+hdrLen]
@@ -172,37 +168,76 @@ func QUICClientHello(ctx context.Context, packet []byte) (*adapter.InboundContex
if err != nil {
return nil, err
}
var frameType byte
var frameLen uint64
var fragments []struct {
offset uint64
length uint64
payload []byte
}
decryptedReader := bytes.NewReader(decrypted)
frameType, err := decryptedReader.ReadByte()
if err != nil {
return nil, err
}
for frameType == 0x0 {
// skip padding
for {
frameType, err = decryptedReader.ReadByte()
if err != nil {
return nil, err
if err == io.EOF {
break
}
switch frameType {
case 0x0:
continue
case 0x1:
continue
case 0x6:
var offset uint64
offset, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
var length uint64
length, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
index := len(decrypted) - decryptedReader.Len()
fragments = append(fragments, struct {
offset uint64
length uint64
payload []byte
}{offset, length, decrypted[index : index+int(length)]})
frameLen += length
_, err = decryptedReader.Seek(int64(length), io.SeekCurrent)
if err != nil {
return nil, err
}
default:
// ignore unknown frame type
}
}
if frameType != 0x6 {
// not crypto frame
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, nil
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
}
_, err = qtls.ReadUvarint(decryptedReader)
if err != nil {
return nil, err
}
tlsHdr := make([]byte, 5)
tlsHdr[0] = 0x16
binary.BigEndian.PutUint16(tlsHdr[1:], uint16(0x0303))
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(decryptedReader.Len()))
metadata, err := TLSClientHello(ctx, io.MultiReader(bytes.NewReader(tlsHdr), decryptedReader))
binary.BigEndian.PutUint16(tlsHdr[3:], uint16(frameLen))
var index uint64
var length int
var readers []io.Reader
readers = append(readers, bytes.NewReader(tlsHdr))
find:
for {
for _, fragment := range fragments {
if fragment.offset == index {
readers = append(readers, bytes.NewReader(fragment.payload))
index = fragment.offset + fragment.length
length++
continue find
}
}
if length == len(fragments) {
break
}
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, E.New("bad fragments")
}
metadata, err := TLSClientHello(ctx, io.MultiReader(readers...))
if err != nil {
return nil, err
return &adapter.InboundContext{Protocol: C.ProtocolQUIC}, err
}
metadata.Protocol = C.ProtocolQUIC
return metadata, nil

View File

@@ -19,6 +19,15 @@ func TestSniffQUICv1(t *testing.T) {
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
}
func TestSniffQUICFragment(t *testing.T) {
t.Parallel()
pkt, err := hex.DecodeString("cc00000001082e3d5d1b64040c55000044d0ccea69e773f6631c1d18b04ae9ee75fcfc34ef74fa62533c93534338a86f101a05d70e0697fb483063fa85db1c59ccfbda5c35234931d8524d8aac37eaaad649470a67794cd754b23c98695238b8363452333bc8c4858376b4166e001da2006e35cf98a91e11a56419b2786775284942d0f7163982f7c248867d12dd374957481dbc564013ff785e1916195eef671f725908f761099d992d69231336ba81d9e25fe2fa3a6eff4318a6ccf10176fc841a1b315f7b35c5b292266fc869d76ca533e7d14e86d82db2e22eacd350977e47d2e012d8a5891c5aaf2a0f4c2b2dae897c161e5b68cbb4dee952472bdc1e21504b8f02534ec4366ce3f8bf86efc78e0232778fbd554457567112abdcafcf6d4d8fcf35083c25d9495679614aba21696e338c62b585046cc55ba8c09c844361d889a47c3ea703b4e23545a9ab2c0bb369693a9ddfb5daffa85cf80fdd6ad66738664e5b0a551729b4955cff7255afcb04dee88c2f072c9de7400947a1bd9327ac5d012a33000ada021d4c03d249fb017d6ac9200b2f9436beab8183ddfbe2d8aee31ffb7df9e1cc181c1af80c39a89965d18ed12da8e3ebe2ae1fbe4b348f83ba19e3e3d1c9b22bcf03ab6ad9b30fe180623faa291ebad83bcd71d7b57f2f5e2f3b8e81d24fb70b2f2159239e8f21ffafef2747aba47d97ab4081e603c018b10678cf99cab1fb42156a14486fa435153979d7279fd22cd40af7088bfc7eff41af2f4b3c0c8864d0040d74dff427f7bffdb8c278474ea00311326cf4925471a8cf596cb92119f19e0f789490ba9cb77b98015a987d93e0324cf1a38b55109f00c3e6ddc5180fb107bf468323afec9bb49fd6a86418569789d66cafe3b8253c2aebb3af3782c1c54dd560487d031d28e6a6e23e159581bb1d47efc4da3fe1d169f9ffb0ca9ba61af0a38a92fde5bc5e6ec026e8378a6315a7b95abf1d2da790a391306ce74d0baf8e2ce648ca74c487f2c0a76a28a80cdf5bd34316eb607684fe7e6d9e83824a00e07660d0b90e3cddd61ebf10748263474afa88c300549e64ce2e90560bb1a12dee7e9484f729a8a4ee7c5651adb5194b3b3ae38e501567c7dbf36e7bb37a2c20b74655f47f2d9af18e52e9d4c9c9eee8e63745779b8f0b06f3a09d846ba62eb978ad77c85de1ee2fee3fbb4c2d283c73e1ccba56a4658e48a2665d200f7f9342f8e84c2ba490094a4f94feec89e42d2f654f564c2beb2997bafa1fc2c68ad8e160b63587d49abc31b834878d52acfb05fb73d0e059b206162e3c90b40c4bc08407ffcb3c08431895b691a3fea923f1f3b48db75d3e6b91fd319ffe4d486e0e14bd5c6affc838dee63d9e0b80f169b5e6c02c7321dcb20deb2b8e707b60e345a308d505bbf26a93d8f18b39d62632e9a77cbe48b3b32eb8819d6311a49820d40f5acbf0273c91c36b2269a03e72ee64df3dfb10ddefe73c64ef60870b2b77bd99dea655f5fe791b538a929a14d99f6d69685d72431ea5f0f4b27a044f2f575ab474fcc3857895934de1ca2581798eaef2c17fe5aaf2e6add97fa32997c7026f15c1b1ad0e6043ae506027a7c0242546fdc851cca39a204e56879f2cef838be8ec66e0f2292f8c862e06f810eb9b80c7a467ce6e90155206352c7f82b1173ba3b98d35bb72c259a60db20dd1a43fe6d7aef0265e6eaa5caafd9b64b448ff745a2046acbdb65cf2a5007809808a4828dc99097feedc734c236260c584")
require.NoError(t, err)
metadata, err := sniff.QUICClientHello(context.Background(), pkt)
require.NoError(t, err)
require.Equal(t, metadata.Domain, "cloudflare-quic.com")
}
func FuzzSniffQUIC(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
sniff.QUICClientHello(context.Background(), data)

View File

@@ -5,7 +5,6 @@ import (
"context"
"io"
"net"
"os"
"time"
"github.com/sagernet/sing-box/adapter"
@@ -30,23 +29,25 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, sniffers
return nil, err
}
var metadata *adapter.InboundContext
var errors []error
for _, sniffer := range sniffers {
metadata, err = sniffer(ctx, bytes.NewReader(buffer.Bytes()))
if err != nil {
continue
if metadata != nil {
return metadata, nil
}
return metadata, nil
errors = append(errors, err)
}
return nil, os.ErrInvalid
return nil, E.Errors(errors...)
}
func PeekPacket(ctx context.Context, packet []byte, sniffers ...PacketSniffer) (*adapter.InboundContext, error) {
var errors []error
for _, sniffer := range sniffers {
sniffMetadata, err := sniffer(ctx, packet)
if err != nil {
continue
metadata, err := sniffer(ctx, packet)
if metadata != nil {
return metadata, nil
}
return sniffMetadata, nil
errors = append(errors, err)
}
return nil, os.ErrInvalid
return nil, E.Errors(errors...)
}

View File

@@ -1,145 +0,0 @@
package trafficcontrol
import (
"io"
"net"
"sync"
"sync/atomic"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type Manager[U comparable] struct {
access sync.Mutex
users map[U]*Traffic
}
type Traffic struct {
Upload uint64
Download uint64
}
func NewManager[U comparable]() *Manager[U] {
return &Manager[U]{
users: make(map[U]*Traffic),
}
}
func (m *Manager[U]) Reset() {
m.users = make(map[U]*Traffic)
}
func (m *Manager[U]) TrackConnection(user U, conn net.Conn) net.Conn {
m.access.Lock()
defer m.access.Unlock()
var traffic *Traffic
if t, loaded := m.users[user]; loaded {
traffic = t
} else {
traffic = new(Traffic)
m.users[user] = traffic
}
return &TrackConn{conn, traffic}
}
func (m *Manager[U]) TrackPacketConnection(user U, conn N.PacketConn) N.PacketConn {
m.access.Lock()
defer m.access.Unlock()
var traffic *Traffic
if t, loaded := m.users[user]; loaded {
traffic = t
} else {
traffic = new(Traffic)
m.users[user] = traffic
}
return &TrackPacketConn{conn, traffic}
}
func (m *Manager[U]) ReadTraffics() map[U]Traffic {
m.access.Lock()
defer m.access.Unlock()
trafficMap := make(map[U]Traffic)
for user, traffic := range m.users {
upload := atomic.SwapUint64(&traffic.Upload, 0)
download := atomic.SwapUint64(&traffic.Download, 0)
if upload == 0 && download == 0 {
continue
}
trafficMap[user] = Traffic{
Upload: upload,
Download: download,
}
}
return trafficMap
}
type TrackConn struct {
net.Conn
*Traffic
}
func (c *TrackConn) Read(p []byte) (n int, err error) {
n, err = c.Conn.Read(p)
if n > 0 {
atomic.AddUint64(&c.Upload, uint64(n))
}
return
}
func (c *TrackConn) Write(p []byte) (n int, err error) {
n, err = c.Conn.Write(p)
if n > 0 {
atomic.AddUint64(&c.Download, uint64(n))
}
return
}
func (c *TrackConn) WriteTo(w io.Writer) (n int64, err error) {
n, err = bufio.Copy(w, c.Conn)
if n > 0 {
atomic.AddUint64(&c.Upload, uint64(n))
}
return
}
func (c *TrackConn) ReadFrom(r io.Reader) (n int64, err error) {
n, err = bufio.Copy(c.Conn, r)
if n > 0 {
atomic.AddUint64(&c.Download, uint64(n))
}
return
}
func (c *TrackConn) Upstream() any {
return c.Conn
}
type TrackPacketConn struct {
N.PacketConn
*Traffic
}
func (c *TrackPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
destination, err := c.PacketConn.ReadPacket(buffer)
if err == nil {
atomic.AddUint64(&c.Upload, uint64(buffer.Len()))
}
return destination, err
}
func (c *TrackPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
n := buffer.Len()
err := c.PacketConn.WritePacket(buffer, destination)
if err == nil {
atomic.AddUint64(&c.Download, uint64(n))
}
return err
}
func (c *TrackPacketConn) Upstream() any {
return c.PacketConn
}

View File

@@ -1,6 +1,6 @@
package constant
var (
Version = "1.0.4"
Version = "1.0.7"
Commit = ""
)

View File

@@ -1,3 +1,46 @@
#### 1.0.7
* Add support for new x/h2 deadline
* Fix copy pipe
* Fix decrypt xplus packet
* Fix macOS Ventura process name match
* Fix smux keepalive
* Fix vmess request buffer
* Fix h2c transport
* Fix tor geoip
* Fix udp connect for mux client
* Fix default dns transport strategy
#### 1.0.6
* Fix ssh outbound
* Fix sniff fragmented quic client hello
* Fix naive overflow
* Check destination before udp connect
#### 1.0.5
* Fix missing source address from transport connection
* Fix fqdn socks5 outbound connection
* Fix read source address from grpc-go
#### 1.0.4
* Fix close grpc conn
* Fix port rule match logic
* Fix clash api proxy type
#### 1.0.3
* Fix socks4 client
* Fix hysteria inbound
* Fix concurrent write
#### 1.0.2
* Fix socks4 request
* Fix processing empty dns result
#### 1.0.1
* Fix match 4in6 address in ip_cidr

10
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
github.com/sagernet/sing-vmess v0.0.0-20221121061945-608aaec6402b
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/spf13/cobra v1.5.0
@@ -31,8 +31,8 @@ require (
go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
golang.org/x/net v0.2.0
golang.org/x/sys v0.2.0
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
@@ -66,9 +66,9 @@ require (
go.uber.org/zap v1.22.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
golang.org/x/tools v0.1.12 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

21
go.sum
View File

@@ -141,8 +141,8 @@ github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDe
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/sing-vmess v0.0.0-20221121061945-608aaec6402b h1:zG+tXOke49idJeqL8MmcWk7S8QlKH/W7AsgbEeBqUPc=
github.com/sagernet/sing-vmess v0.0.0-20221121061945-608aaec6402b/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
@@ -212,8 +212,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -244,18 +244,19 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -268,8 +269,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -248,7 +248,14 @@ func (c *naiveH1Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0
}
if c.readPadding < kFirstPaddings {
paddingHdr := p[:3]
var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.Conn, paddingHdr)
if err != nil {
return
@@ -420,7 +427,14 @@ func (c *naiveH2Conn) read(p []byte) (n int, err error) {
c.paddingRemaining = 0
}
if c.readPadding < kFirstPaddings {
paddingHdr := p[:3]
var paddingHdr []byte
if len(p) >= 3 {
paddingHdr = p[:3]
} else {
_paddingHdr := make([]byte, 3)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.reader, paddingHdr)
if err != nil {
return

View File

@@ -1,94 +0,0 @@
package inbound
import (
"encoding/json"
"io"
"net/http"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
func (h *ShadowsocksMulti) createHandler() http.Handler {
router := chi.NewRouter()
router.Get("/", h.handleHello)
router.Put("/users", h.handleUpdateUsers)
router.Get("/traffics", h.handleReadTraffics)
return router
}
func (h *ShadowsocksMulti) handleHello(writer http.ResponseWriter, request *http.Request) {
render.JSON(writer, request, render.M{
"server": "sing-box",
"version": C.Version,
})
}
func (h *ShadowsocksMulti) handleUpdateUsers(writer http.ResponseWriter, request *http.Request) {
var users []option.ShadowsocksUser
err := readRequest(request, &users)
if err != nil {
h.newError(E.Cause(err, "controller: update users: parse request"))
writer.WriteHeader(http.StatusBadRequest)
writer.Write([]byte(F.ToString(err)))
return
}
users = append([]option.ShadowsocksUser{{
Name: "control",
Password: h.users[0].Password,
}}, users...)
err = h.service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
return index
}), common.Map(users, func(user option.ShadowsocksUser) string {
return user.Password
}))
if err != nil {
h.newError(E.Cause(err, "controller: update users"))
writer.WriteHeader(http.StatusBadRequest)
writer.Write([]byte(F.ToString(err)))
return
}
h.users = users
h.trafficManager.Reset()
writer.WriteHeader(http.StatusNoContent)
h.logger.Info("controller: updated ", len(users)-1, " users")
}
type ShadowsocksUserTraffic struct {
Name string `json:"name,omitempty"`
Upload uint64 `json:"upload,omitempty"`
Download uint64 `json:"download,omitempty"`
}
func (h *ShadowsocksMulti) handleReadTraffics(writer http.ResponseWriter, request *http.Request) {
h.logger.Debug("controller: traffics sent")
trafficMap := h.trafficManager.ReadTraffics()
if len(trafficMap) == 0 {
writer.WriteHeader(http.StatusNoContent)
return
}
traffics := make([]ShadowsocksUserTraffic, 0, len(trafficMap))
for user, traffic := range trafficMap {
traffics = append(traffics, ShadowsocksUserTraffic{
Name: h.users[user].Name,
Upload: traffic.Upload,
Download: traffic.Download,
})
}
render.JSON(writer, request, traffics)
}
func readRequest(request *http.Request, v any) error {
defer request.Body.Close()
content, err := io.ReadAll(request.Body)
if err != nil {
return err
}
return json.Unmarshal(content, v)
}

View File

@@ -3,12 +3,9 @@ package inbound
import (
"context"
"net"
"net/http"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/pipelistener"
"github.com/sagernet/sing-box/common/trafficcontrol"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
@@ -16,7 +13,6 @@ import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network"
)
@@ -28,12 +24,8 @@ var (
type ShadowsocksMulti struct {
myInboundAdapter
service *shadowaead_2022.MultiService[int]
users []option.ShadowsocksUser
controlEnabled bool
controller *http.Server
controllerPipe *pipelistener.Listener
trafficManager *trafficcontrol.Manager[int]
service *shadowaead_2022.MultiService[int]
users []option.ShadowsocksUser
}
func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) {
@@ -62,20 +54,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
)
users := options.Users
if options.ControlPassword != "" {
inbound.controlEnabled = true
users = append([]option.ShadowsocksUser{{
Name: "control",
Password: options.ControlPassword,
}}, users...)
inbound.controller = &http.Server{Handler: inbound.createHandler()}
inbound.trafficManager = trafficcontrol.NewManager[int]()
}
if err != nil {
return nil, err
}
err = service.UpdateUsersWithPasswords(common.MapIndexed(users, func(index int, user option.ShadowsocksUser) int {
err = service.UpdateUsersWithPasswords(common.MapIndexed(options.Users, func(index int, user option.ShadowsocksUser) int {
return index
}), common.Map(options.Users, func(user option.ShadowsocksUser) string {
return user.Password
@@ -85,30 +64,10 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
}
inbound.service = service
inbound.packetUpstream = service
inbound.users = users
inbound.users = options.Users
return inbound, err
}
func (h *ShadowsocksMulti) Start() error {
if h.controlEnabled {
h.controllerPipe = pipelistener.New(16)
go func() {
err := h.controller.Serve(h.controllerPipe)
if err != nil {
h.newError(E.Cause(err, "controller serve error"))
}
}()
}
return h.myInboundAdapter.Start()
}
func (h *ShadowsocksMulti) Close() error {
if h.controlEnabled {
h.controllerPipe.Close()
}
return h.myInboundAdapter.Close()
}
func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
}
@@ -126,11 +85,6 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met
if !loaded {
return os.ErrInvalid
}
if userIndex == 0 && h.controlEnabled {
h.logger.InfoContext(ctx, "inbound control connection")
h.controllerPipe.Serve(conn)
return nil
}
user := h.users[userIndex].Name
if user == "" {
user = F.ToString(userIndex)

View File

@@ -3,6 +3,7 @@ package outbound
import (
"context"
"net"
"os"
"runtime"
"time"
@@ -79,7 +80,9 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
switch metadata.Protocol {
case C.ProtocolQUIC, C.ProtocolDNS:
return connectPacketConnection(ctx, this, conn, metadata)
if !metadata.Destination.Addr.IsUnspecified() {
return connectPacketConnection(ctx, this, conn, metadata)
}
}
ctx = adapter.WithContext(ctx, &metadata)
var outConn net.PacketConn
@@ -134,22 +137,25 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
_payload := buf.StackNew()
payload := common.Dup(_payload)
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
if err != nil {
return err
}
_, err = payload.ReadOnceFrom(conn)
if err != nil && !E.IsTimeout(err) {
return E.Cause(err, "read payload")
}
err = conn.SetReadDeadline(time.Time{})
if err != nil {
payload.Release()
return err
if err != os.ErrInvalid {
if err != nil {
return err
}
_, err = payload.ReadOnceFrom(conn)
if err != nil && !E.IsTimeout(err) {
return E.Cause(err, "read payload")
}
err = conn.SetReadDeadline(time.Time{})
if err != nil {
payload.Release()
return err
}
}
_, err = serverConn.Write(payload.Bytes())
if err != nil {
return N.HandshakeFailure(conn, err)
}
runtime.KeepAlive(_payload)
payload.Release()
return bufio.CopyConn(ctx, conn, serverConn)
}

View File

@@ -74,7 +74,7 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
if destination.IsFqdn() {
if h.resolve && destination.IsFqdn() {
addrs, err := h.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err

View File

@@ -122,6 +122,9 @@ func (s *SSH) connect() (*ssh.Client, error) {
Auth: s.authMethod,
ClientVersion: s.clientVersion,
HostKeyAlgorithms: s.hostKeyAlgorithms,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
clientConn, chans, reqs, err := ssh.NewClientConn(conn, s.serverAddr.Addr.String(), config)
if err != nil {

View File

@@ -41,9 +41,18 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
startConf := newConfig()
startConf.DataDir = os.ExpandEnv(options.DataDirectory)
startConf.TempDataDirBase = os.TempDir()
startConf.ExtraArgs = options.ExtraArgs
if options.DataDirectory != "" {
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.FileExists(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
}
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.FileExists(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
}
}
if options.ExecutablePath != "" {
startConf.ExePath = options.ExecutablePath
startConf.ExtraArgs = options.ExtraArgs
startConf.ProcessCreator = nil
startConf.UseEmbeddedControlConn = false
}

View File

@@ -16,4 +16,3 @@ popd
sudo systemctl stop sing-box
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
sudo systemctl start sing-box
sudo journalctl -u sing-box --output cat -f

View File

@@ -541,8 +541,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.SniffEnabled {
buffer := buf.NewPacket()
buffer.FullReset()
sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
if err == nil {
sniffMetadata, _ := sniff.PeekStream(ctx, conn, buffer, sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {
@@ -618,8 +618,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
buffer.Release()
return err
}
sniffMetadata, err := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if err == nil {
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
if metadata.SniffOverrideDestination && sniff.IsDomainName(metadata.Domain) {

View File

@@ -37,7 +37,11 @@ func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport,
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
}
}
return ctx, r.defaultTransport, r.defaultDomainStrategy
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
return ctx, r.defaultTransport, domainStrategy
} else {
return ctx, r.defaultTransport, r.defaultDomainStrategy
}
}
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {

View File

@@ -10,15 +10,12 @@ import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
const xplusSaltLen = 16
var errInalidPacket = E.New("invalid packet")
func NewXPlusPacketConn(conn net.PacketConn, key []byte) net.PacketConn {
vectorisedWriter, isVectorised := bufio.CreateVectorisedPacketWriter(conn)
if isVectorised {
@@ -51,7 +48,8 @@ func (c *XPlusPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
if err != nil {
return
} else if n < xplusSaltLen {
return 0, nil, errInalidPacket
n = 0
return
}
key := sha256.Sum256(append(c.key, p[:xplusSaltLen]...))
for i := range p[xplusSaltLen:] {

View File

@@ -5,6 +5,7 @@ import (
"crypto/tls"
"net"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
@@ -13,6 +14,8 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
gM "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -37,7 +40,22 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *
func (s *Server) Tun(server GunService_TunServer) error {
ctx, cancel := context.WithCancel(s.ctx)
conn := NewGRPCConn(server, cancel)
go s.handler.NewConnection(ctx, conn, M.Metadata{})
var metadata M.Metadata
if remotePeer, loaded := peer.FromContext(server.Context()); loaded {
metadata.Source = M.SocksaddrFromNet(remotePeer.Addr)
}
if grpcMetadata, loaded := gM.FromIncomingContext(server.Context()); loaded {
forwardFrom := strings.Join(grpcMetadata.Get("X-Forwarded-For"), ",")
if forwardFrom != "" {
for _, from := range strings.Split(forwardFrom, ",") {
originAddr := M.ParseSocksaddr(from)
if originAddr.IsValid() {
metadata.Source = originAddr.Unwrap()
}
}
}
}
go s.handler.NewConnection(ctx, conn, metadata)
<-ctx.Done()
return nil
}

View File

@@ -153,13 +153,31 @@ func (c *GunConn) RemoteAddr() net.Addr {
}
func (c *GunConn) SetDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetWriteDeadline(t)
}
func (c *GunConn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetReadDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetReadDeadline(t)
}
func (c *GunConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetWriteDeadline(t)
}

View File

@@ -17,6 +17,9 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
sHttp "github.com/sagernet/sing/protocol/http"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -25,6 +28,8 @@ type Server struct {
handler N.TCPConnectionHandler
errorHandler E.Handler
httpServer *http.Server
h2Server *http2.Server
h2cHandler http.Handler
path string
}
@@ -37,6 +42,7 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *
handler: handler,
errorHandler: errorHandler,
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
h2Server: new(http2.Server),
}
if tlsConfig != nil {
if !common.Contains(tlsConfig.NextProtos, "h2") {
@@ -47,10 +53,15 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *
Handler: server,
TLSConfig: tlsConfig,
}
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
return server
}
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
s.h2cHandler.ServeHTTP(writer, request)
return
}
if request.URL.Path != s.path {
writer.WriteHeader(http.StatusNotFound)
s.badRequest(request, E.New("bad path: ", request.URL.Path))
@@ -80,6 +91,10 @@ func (s *Server) badRequest(request *http.Request, err error) {
}
func (s *Server) Serve(listener net.Listener) error {
err := http2.ConfigureServer(s.httpServer, s.h2Server)
if err != nil {
return err
}
if s.httpServer.TLSConfig == nil {
return s.httpServer.Serve(listener)
} else {

View File

@@ -39,15 +39,33 @@ func (c *HTTPConn) RemoteAddr() net.Addr {
}
func (c *HTTPConn) SetDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetWriteDeadline(t)
}
func (c *HTTPConn) SetReadDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetReadDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetReadDeadline(t)
}
func (c *HTTPConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid
responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
})
if !loaded {
return os.ErrInvalid
}
return responseWriter.SetWriteDeadline(t)
}
type ServerHTTPConn struct {

View File

@@ -16,6 +16,9 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
sHttp "github.com/sagernet/sing/protocol/http"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -25,6 +28,8 @@ type Server struct {
handler N.TCPConnectionHandler
errorHandler E.Handler
httpServer *http.Server
h2Server *http2.Server
h2cHandler http.Handler
host []string
path string
method string
@@ -44,6 +49,7 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig *
path: options.Path,
method: options.Method,
headers: make(http.Header),
h2Server: new(http2.Server),
}
if server.method == "" {
server.method = "PUT"
@@ -60,10 +66,16 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig *
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
TLSConfig: tlsConfig,
}
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
return server
}
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
s.h2cHandler.ServeHTTP(writer, request)
return
}
host := request.Host
if len(s.host) > 0 && !common.Contains(s.host, host) {
writer.WriteHeader(http.StatusBadRequest)
@@ -119,6 +131,10 @@ func (s *Server) badRequest(request *http.Request, err error) {
}
func (s *Server) Serve(listener net.Listener) error {
err := http2.ConfigureServer(s.httpServer, s.h2Server)
if err != nil {
return err
}
if s.httpServer.TLSConfig == nil {
return s.httpServer.Serve(listener)
} else {