Add shadowboom

This commit is contained in:
世界 2022-02-02 20:27:50 +08:00
parent 5cc189a169
commit f63868bf82
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
30 changed files with 1923 additions and 147 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
/.idea/
/.idea/
/sing_*

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# sing
Do you hear the people sing?

View file

@ -1,9 +1,307 @@
package buf
var Empty []byte
import (
"crypto/rand"
"fmt"
"io"
"sing/common/list"
func init() {
Empty = make([]byte, 128)
"sing/common"
)
type Buffer struct {
data []byte
start int
end int
managed bool
}
func New() *Buffer {
return &Buffer{
data: GetBytes(),
start: ReversedHeader,
end: ReversedHeader,
managed: true,
}
}
func StackNew() Buffer {
return Buffer{
data: GetBytes(),
managed: true,
}
}
func From(data []byte) *Buffer {
buffer := New()
buffer.Write(data)
return buffer
}
func As(data []byte) *Buffer {
size := len(data)
max := cap(data)
if size != max {
data = data[:max]
}
return &Buffer{
data: data,
end: size,
}
}
func With(data []byte) *Buffer {
return &Buffer{
data: data,
}
}
func (b *Buffer) Byte(index int) byte {
return b.data[b.start+index]
}
func (b *Buffer) SetByte(index int, value byte) {
b.data[b.start+index] = value
}
func (b *Buffer) Extend(n int) []byte {
if b.start == b.end {
b.start = 0
b.end = n
return b.data[:n]
}
end := b.end + n
ext := b.data[b.end:end]
b.end = end
return ext
}
func (b *Buffer) Advance(from int) {
b.start += from
}
func (b *Buffer) Truncate(to int) {
b.end = b.start + to
}
func (b *Buffer) Write(data []byte) (n int, err error) {
if b.IsFull() {
return 0, io.ErrShortBuffer
}
n = copy(b.data[b.end:], data)
b.end += n
return
}
func (b *Buffer) WriteAtFirst(data []byte) (n int, err error) {
size := len(data)
if b.start >= size {
n = copy(b.data[b.start-size:b.start], data)
b.start -= n
return
}
offset := size - b.start
copy(b.data[offset:], b.data[b.start:b.end])
n = copy(b.data[:offset], data)
b.end += offset
return
}
func (b *Buffer) WriteRandom(size int) {
common.Must1(io.ReadFull(rand.Reader, b.Extend(size)))
}
func (b *Buffer) WriteByte(byte byte) error {
if b.IsFull() {
return io.ErrShortBuffer
}
b.data[b.end] = byte
b.end++
return nil
}
func (b *Buffer) ReadFrom(r io.Reader) (int64, error) {
if b.IsFull() {
return 0, io.ErrShortBuffer
}
n, err := r.Read(b.FreeBytes())
if err != nil {
return 0, err
}
b.end += n
return int64(n), nil
}
func (b *Buffer) ReadFullFrom(r io.Reader, size int) (n int, err error) {
if b.IsFull() {
return 0, io.ErrShortBuffer
}
end := b.end + size
n, err = io.ReadFull(r, b.data[b.start:end])
if err != nil {
return 0, err
}
b.end += n
return
}
func (b *Buffer) WriteRune(s rune) (int, error) {
return b.Write([]byte{byte(s)})
}
func (b *Buffer) WriteString(s string) (int, error) {
return b.Write([]byte(s))
}
func (b *Buffer) WriteSprint(s ...any) (int, error) {
return b.WriteString(fmt.Sprint(s...))
}
func (b *Buffer) WriteZero() error {
if b.IsFull() {
return io.ErrShortBuffer
}
b.end++
b.data[b.end] = 0
return nil
}
func (b *Buffer) WriteZeroN(n int) error {
if b.end+n > b.Cap() {
return io.ErrShortBuffer
}
for i := b.end; i <= b.end+n; i++ {
b.data[i] = 0
}
b.end += n
return nil
}
func (b *Buffer) ReadByte() (byte, error) {
if b.IsEmpty() {
return 0, io.EOF
}
nb := b.data[b.start]
b.start++
return nb, nil
}
func (b *Buffer) ReadBytes(n int) ([]byte, error) {
if b.end-b.start < n {
return nil, io.EOF
}
nb := b.data[b.start : b.start+n]
b.start += n
return nb, nil
}
func (b *Buffer) Read(data []byte) (n int, err error) {
if b.Len() == 0 {
return 0, io.EOF
}
n = copy(data, b.data[b.start:b.end])
if n == b.Len() {
b.Reset()
} else {
b.start += n
}
return n, nil
}
func (b *Buffer) WriteTo(w io.Writer) (int64, error) {
n, err := w.Write(b.Bytes())
return int64(n), err
}
func (b *Buffer) Resize(start, end int) {
b.start = start
b.end = b.start + end
}
func (b *Buffer) Reset() {
b.start = ReversedHeader
b.end = ReversedHeader
}
func (b *Buffer) Release() {
if b == nil || b.data == nil || !b.managed {
return
}
PutBytes(b.data)
*b = Buffer{}
}
func (b Buffer) Len() int {
return b.end - b.start
}
func (b Buffer) Cap() int {
return cap(b.data)
}
func (b Buffer) Bytes() []byte {
return b.data[b.start:b.end]
}
func (b Buffer) Slice() []byte {
return b.data
}
func (b Buffer) From(n int) []byte {
return b.data[b.start+n : b.end]
}
func (b Buffer) To(n int) []byte {
return b.data[b.start : b.start+n]
}
func (b Buffer) Index(start int) []byte {
return b.data[b.start+start : b.start+start]
}
func (b Buffer) FreeLen() int {
return b.Cap() - b.end
}
func (b Buffer) FreeBytes() []byte {
return b.data[b.end:b.Cap()]
}
func (b Buffer) IsEmpty() bool {
return b.end-b.start == 0
}
func (b Buffer) IsFull() bool {
return b.end == b.Cap()
}
func (b Buffer) ToOwned() *Buffer {
var buffer *Buffer
if b.Len() > BufferSize {
buffer = As(make([]byte, b.Len()))
copy(buffer.data, b.Bytes())
} else {
buffer = New()
buffer.Write(b.Bytes())
}
return buffer
}
func (b Buffer) Copy() []byte {
buffer := make([]byte, b.Len())
copy(buffer, b.Bytes())
return buffer
}
func ReleaseMulti(mb *list.List[*Buffer]) {
for entry := mb.Front(); entry != nil; entry = entry.Next() {
// TODO: remove cast
var buffer *Buffer = entry.Value
buffer.Release()
}
}
func ForeachN(b []byte, size int) [][]byte {

23
common/buf/buffer_test.go Normal file
View file

@ -0,0 +1,23 @@
package buf_test
import (
"bytes"
"crypto/rand"
"testing"
vb "github.com/v2fly/v2ray-core/v5/common/buf"
"sing/common/buf"
)
func TestBuffer(t *testing.T) {
v := vb.New()
v.ReadFullFrom(rand.Reader, 1024)
buffer := buf.New()
buffer.Write(v.Bytes())
v.Write(v.Bytes())
buffer.Write(buffer.Bytes())
if bytes.Compare(v.Bytes(), buffer.Bytes()) > 0 {
t.Fatal("bad request data\n", v.Bytes(), "\n", buffer.Bytes())
}
}

98
common/buf/multi.go Normal file
View file

@ -0,0 +1,98 @@
package buf
type MultiBuffer struct {
buffers []*Buffer
index int
}
func (b MultiBuffer) Size() int {
return len(b.buffers)
}
func (b MultiBuffer) Len() int {
var length int
for _, buffer := range b.buffers {
length += buffer.Len()
}
return length
}
func (b *MultiBuffer) Release() {
for _, buffer := range b.buffers {
buffer.Release()
}
b.buffers = nil
b.index = 0
}
func (b MultiBuffer) From(n int) MultiBuffer {
var newBuffer MultiBuffer
for _, buffer := range b.buffers {
if n == 0 {
newBuffer.buffers = append(newBuffer.buffers, buffer)
} else if buffer.Len() < n {
n -= buffer.Len()
} else {
newBuffer.buffers = append(newBuffer.buffers, As(buffer.From(n)))
n = 0
}
}
return newBuffer
}
func (b *MultiBuffer) BufferForWrite() *Buffer {
var buffer *Buffer
if b.Size() > 0 && !b.buffers[b.index].IsFull() {
buffer = b.buffers[b.index]
} else {
buffer = New()
b.buffers = append(b.buffers, buffer)
b.index++
}
return buffer
}
func (b *MultiBuffer) Write(data []byte) (n int, err error) {
size := len(data)
var wn int
for wn < size {
n, err = b.BufferForWrite().Write(data)
if err != nil {
return 0, err
}
wn += n
}
return wn, nil
}
func (b *MultiBuffer) WriteAtFirst(data []byte) (n int, err error) {
length := len(data)
if b.Size() > 0 {
buffer := b.buffers[0]
if buffer.start > 0 {
n = copy(buffer.data[:buffer.start], data[length-buffer.start:length])
buffer.start -= n
}
}
if n < length {
b.buffers = append([]*Buffer{As(data[n:length])}, b.buffers...)
b.index++
}
return
}
func (b *MultiBuffer) WriteMulti(data *MultiBuffer) (n int, err error) {
defer data.Release()
for _, buffer := range data.buffers {
writeN, err := b.Write(buffer.Bytes())
if err != nil {
return 0, err
}
n += writeN
}
return
}
func (b *MultiBuffer) WriteString(str string) (n int, err error) {
return b.Write([]byte(str))
}

View file

@ -1,30 +1,23 @@
package buf
import (
"bytes"
"sync"
import "sync"
const (
ReversedHeader = 1024
BufferSize = 20 * 1024
)
const BufferSize = 20 * 1024
var bufferPool = sync.Pool{
var pool = sync.Pool{
New: func() any {
var data [BufferSize]byte
return bytes.NewBuffer(data[:0])
var buffer [BufferSize]byte
return buffer[:]
},
}
func New() *bytes.Buffer {
return bufferPool.Get().(*bytes.Buffer)
func GetBytes() []byte {
return pool.Get().([]byte)
}
func Extend(buffer *bytes.Buffer, size int) []byte {
l := buffer.Len()
buffer.Grow(size)
return buffer.Bytes()[l : l+size]
}
func Release(buffer *bytes.Buffer) {
buffer.Reset()
bufferPool.Put(buffer)
func PutBytes(buffer []byte) {
pool.Put(buffer)
}

View file

@ -84,11 +84,20 @@ func Must1(_ any, err error) {
}
}
func Close(closers ...io.Closer) {
func Close(closers ...any) {
for _, closer := range closers {
if closer == nil {
continue
}
closer.Close()
switch c := closer.(type) {
case io.Closer:
c.Close()
}
switch c := closer.(type) {
case ReaderWithUpstream:
Close(c.Upstream())
case WriterWithUpstream:
Close(c.Upstream())
}
}
}

87
common/conn.go Normal file
View file

@ -0,0 +1,87 @@
package common
import (
"io"
"net"
"time"
)
type ReadOnlyException struct {
}
func (e *ReadOnlyException) Error() string {
return "read only connection"
}
type WriteOnlyException struct {
}
func (e *WriteOnlyException) Error() string {
return "write only connection"
}
type readWriteConn struct {
io.Reader
io.Writer
}
func (r *readWriteConn) Close() error {
Close(r.Reader)
return nil
}
func (r *readWriteConn) LocalAddr() net.Addr {
return new(DummyAddr)
}
func (r *readWriteConn) RemoteAddr() net.Addr {
return new(DummyAddr)
}
func (r *readWriteConn) SetDeadline(t time.Time) error {
return nil
}
func (r *readWriteConn) SetReadDeadline(t time.Time) error {
return nil
}
func (r *readWriteConn) SetWriteDeadline(t time.Time) error {
return nil
}
type readConn struct {
readWriteConn
}
func (r *readConn) Write(b []byte) (n int, err error) {
return 0, new(ReadOnlyException)
}
type writeConn struct {
readWriteConn
io.Writer
}
func (w *writeConn) Read(p []byte) (n int, err error) {
return 0, new(WriteOnlyException)
}
func NewReadConn(reader io.Reader) net.Conn {
c := new(readConn)
c.Reader = reader
return c
}
func NewWritConn(writer io.Writer) net.Conn {
c := new(writeConn)
c.Writer = writer
return c
}
func NewReadWriteConn(reader io.Reader, writer io.Writer) net.Conn {
c := new(readWriteConn)
c.Reader = reader
c.Writer = writer
return c
}

View file

@ -1,3 +1,14 @@
package common
const EmptyString = ""
type DummyAddr struct {
}
func (d *DummyAddr) Network() string {
return "dummy"
}
func (d *DummyAddr) String() string {
return "dummy"
}

View file

@ -10,6 +10,11 @@ type Exception interface {
Cause() error
}
type SuppressedException interface {
error
Suppressed() error
}
type exception struct {
message string
cause error

24
common/flush.go Normal file
View file

@ -0,0 +1,24 @@
package common
import "io"
type Flusher interface {
Flush() error
}
func Flush(writer io.Writer) error {
for {
if f, ok := writer.(Flusher); ok {
err := f.Flush()
if err != nil {
return err
}
}
if u, ok := writer.(WriterWithUpstream); ok {
writer = u.Upstream()
} else {
break
}
}
return nil
}

394
common/genericsync/map.go Normal file
View file

@ -0,0 +1,394 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package genericsync
import (
"sync"
"sync/atomic"
"unsafe"
)
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
// Loads, stores, and deletes run in amortized constant time.
//
// The Map type is specialized. Most code should use a plain Go map instead,
// with separate locking or coordination, for better type safety and to make it
// easier to maintain other invariants along with the map content.
//
// The Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a Map may significantly reduce lock
// contention compared to a Go map paired with a separate Mutex or RWMutex.
//
// The zero Map is empty and ready for use. A Map must not be copied after first use.
type Map[K comparable, V any] struct {
mu sync.Mutex
// read contains the portion of the map's contents that are safe for
// concurrent access (with or without mu held).
//
// The read field itself is always safe to load, but must only be stored with
// mu held.
//
// Entries stored in read may be updated concurrently without mu, but updating
// a previously-expunged entry requires that the entry be copied to the dirty
// map and unexpunged with mu held.
read atomic.Value // readOnly
// dirty contains the portion of the map's contents that require mu to be
// held. To ensure that the dirty map can be promoted to the read map quickly,
// it also includes all of the non-expunged entries in the read map.
//
// Expunged entries are not stored in the dirty map. An expunged entry in the
// clean map must be unexpunged and added to the dirty map before a new value
// can be stored to it.
//
// If the dirty map is nil, the next write to the map will initialize it by
// making a shallow copy of the clean map, omitting stale entries.
dirty map[K]*entry[V]
// misses counts the number of loads since the read map was last updated that
// needed to lock mu to determine whether the key was present.
//
// Once enough misses have occurred to cover the cost of copying the dirty
// map, the dirty map will be promoted to the read map (in the unamended
// state) and the next store to the map will make a new dirty copy.
misses int
}
// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly[K comparable, V any] struct {
m map[K]*entry[V]
amended bool // true if the dirty map contains some key not in m.
}
// expunged is an arbitrary pointer that marks entries which have been deleted
// from the dirty map.
var expunged = unsafe.Pointer(new(any))
// An entry is a slot in the map corresponding to a particular key.
type entry[T any] struct {
// p points to the interface{} value stored for the entry.
//
// If p == nil, the entry has been deleted, and either m.dirty == nil or
// m.dirty[key] is e.
//
// If p == expunged, the entry has been deleted, m.dirty != nil, and the entry
// is missing from m.dirty.
//
// Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty
// != nil, in m.dirty[key].
//
// An entry can be deleted by atomic replacement with nil: when m.dirty is
// next created, it will atomically replace nil with expunged and leave
// m.dirty[key] unset.
//
// An entry's associated value can be updated by atomic replacement, provided
// p != expunged. If p == expunged, an entry's associated value can be updated
// only after first setting m.dirty[key] = e so that lookups using the dirty
// map find the entry.
p unsafe.Pointer // *interface{}
}
func newEntry[T any](i T) *entry[T] {
var anyValue any = i
return &entry[T]{p: unsafe.Pointer(&anyValue)}
}
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *Map[K, V]) Load(key K) (value V, ok bool) {
read, _ := m.read.Load().(readOnly[K, V])
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// Avoid reporting a spurious miss if m.dirty got promoted while we were
// blocked on m.mu. (If further loads of the same key will not miss, it's
// not worth copying the dirty map for this key.)
read, _ = m.read.Load().(readOnly[K, V])
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()
}
m.mu.Unlock()
}
if !ok {
var defaultValue V
return defaultValue, false
}
return e.load()
}
func (e *entry[T]) load() (value T, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expunged {
var defaultValue T
return defaultValue, false
}
return (*(*any)(p)).(T), true
}
// Store sets the value for a key.
func (m *Map[K, V]) Store(key K, value V) {
read, _ := m.read.Load().(readOnly[K, V])
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
read, _ = m.read.Load().(readOnly[K, V])
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
// non-nil dirty map and this entry is not in it.
m.dirty[key] = e
}
e.storeLocked(&value)
} else if e, ok := m.dirty[key]; ok {
e.storeLocked(&value)
} else {
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnly[K, V]{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
}
m.mu.Unlock()
}
// tryStore stores a value if the entry has not been expunged.
//
// If the entry is expunged, tryStore returns false and leaves the entry
// unchanged.
func (e *entry[T]) tryStore(i *T) bool {
for {
p := atomic.LoadPointer(&e.p)
if p == expunged {
return false
}
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
return true
}
}
}
// unexpungeLocked ensures that the entry is not marked as expunged.
//
// If the entry was previously expunged, it must be added to the dirty map
// before m.mu is unlocked.
func (e *entry[T]) unexpungeLocked() (wasExpunged bool) {
return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
}
// storeLocked unconditionally stores a value to the entry.
//
// The entry must be known not to be expunged.
func (e *entry[T]) storeLocked(i *T) {
atomic.StorePointer(&e.p, unsafe.Pointer(i))
}
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
// Avoid locking if it's a clean hit.
read, _ := m.read.Load().(readOnly[K, V])
if e, ok := read.m[key]; ok {
actual, loaded, ok := e.tryLoadOrStore(value)
if ok {
return actual, loaded
}
}
m.mu.Lock()
read, _ = m.read.Load().(readOnly[K, V])
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
m.dirty[key] = e
}
actual, loaded, _ = e.tryLoadOrStore(value)
} else if e, ok := m.dirty[key]; ok {
actual, loaded, _ = e.tryLoadOrStore(value)
m.missLocked()
} else {
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnly[K, V]{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
actual, loaded = value, false
}
m.mu.Unlock()
return actual, loaded
}
// tryLoadOrStore atomically loads or stores a value if the entry is not
// expunged.
//
// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
// returns with ok==false.
func (e *entry[T]) tryLoadOrStore(i T) (actual T, loaded, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == expunged {
var defaultValue T
return defaultValue, false, false
}
if p != nil {
return (*(*any)(p)).(T), true, true
}
// Copy the interface after the first load to make this method more amenable
// to escape analysis: if we hit the "load" path or the entry is expunged, we
// shouldn't bother heap-allocating.
ic := i
for {
if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
return i, false, true
}
p = atomic.LoadPointer(&e.p)
if p == expunged {
var defaultValue T
return defaultValue, false, false
}
if p != nil {
return (*(*any)(p)).(T), true, true
}
}
}
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
read, _ := m.read.Load().(readOnly[K, V])
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly[K, V])
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
delete(m.dirty, key)
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()
}
m.mu.Unlock()
}
if ok {
return e.delete()
}
var defaultValue V
return defaultValue, false
}
// Delete deletes the value for a key.
func (m *Map[K, V]) Delete(key K) {
m.LoadAndDelete(key)
}
func (e *entry[T]) delete() (value T, ok bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expunged {
var defaultValue T
return defaultValue, false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
return (*(*any)(p)).(T), true
}
}
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
//
// Range does not necessarily correspond to any consistent snapshot of the Map's
// contents: no key will be visited more than once, but if the value for any key
// is stored or deleted concurrently (including by f), Range may reflect any
// mapping for that key from any point during the Range call. Range does not
// block other methods on the receiver; even f itself may call any method on m.
//
// Range may be O(N) with the number of elements in the map even if f returns
// false after a constant number of calls.
func (m *Map[K, V]) Range(f func(key K, value V) bool) {
// We need to be able to iterate over all of the keys that were already
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
// requiring us to hold m.mu for a long time.
read, _ := m.read.Load().(readOnly[K, V])
if read.amended {
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
// (assuming the caller does not break out early), so a call to Range
// amortizes an entire copy of the map: we can promote the dirty copy
// immediately!
m.mu.Lock()
read, _ = m.read.Load().(readOnly[K, V])
if read.amended {
read = readOnly[K, V]{m: m.dirty}
m.read.Store(read)
m.dirty = nil
m.misses = 0
}
m.mu.Unlock()
}
for k, e := range read.m {
v, ok := e.load()
if !ok {
continue
}
if !f(k, v) {
break
}
}
}
func (m *Map[K, V]) missLocked() {
m.misses++
if m.misses < len(m.dirty) {
return
}
m.read.Store(readOnly[K, V]{m: m.dirty})
m.dirty = nil
m.misses = 0
}
func (m *Map[K, V]) dirtyLocked() {
if m.dirty != nil {
return
}
read, _ := m.read.Load().(readOnly[K, V])
m.dirty = make(map[K]*entry[V], len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {
m.dirty[k] = e
}
}
}
func (e *entry[T]) tryExpungeLocked() (isExpunged bool) {
p := atomic.LoadPointer(&e.p)
for p == nil {
if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
return true
}
p = atomic.LoadPointer(&e.p)
}
return p == expunged
}

36
common/list/cond.go Normal file
View file

@ -0,0 +1,36 @@
package list
func (l List[T]) IsEmpty() bool {
return l.len == 0
}
func (l *List[T]) PopBack() T {
if l.len == 0 {
var defaultValue T
return defaultValue
}
entry := l.root.prev
l.remove(entry)
return entry.Value
}
func (l *List[T]) PopFront() T {
if l.len == 0 {
var defaultValue T
return defaultValue
}
entry := l.root.next
l.remove(entry)
return entry.Value
}
func (l *List[T]) Array() []T {
if l.len == 0 {
return nil
}
array := make([]T, 0, l.len)
for element := l.Front(); element != nil; element = element.Next() {
array = append(array, element.Value)
}
return array
}

234
common/list/list.go Normal file
View file

@ -0,0 +1,234 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package list implements a doubly linked list.
//
// To iterate over a list (where l is a *List[T]):
// for e := l.Front(); e != nil; e = e.Next() {
// // do something with e.Value
// }
//
package list
// Element is an element of a linked list.
type Element[T any] struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element[T]
// The list to which this element belongs.
list *List[T]
// The value stored with this element.
Value T
}
// Next returns the next list element or nil.
func (e *Element[T]) Next() *Element[T] {
if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
}
// Prev returns the previous list element or nil.
func (e *Element[T]) Prev() *Element[T] {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
}
// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List[T any] struct {
root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
// Init initializes or clears list l.
func (l *List[T]) Init() *List[T] {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
// Len returns the number of elements of list l.
// The complexity is O(1).
func (l *List[T]) Len() int { return l.len }
// Front returns the first element of list l or nil if the list is empty.
func (l *List[T]) Front() *Element[T] {
if l.len == 0 {
return nil
}
return l.root.next
}
// Back returns the last element of list l or nil if the list is empty.
func (l *List[T]) Back() *Element[T] {
if l.len == 0 {
return nil
}
return l.root.prev
}
// lazyInit lazily initializes a zero List value.
func (l *List[T]) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
// insert inserts e after at, increments l.len, and returns e.
func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
e.list = l
l.len++
return e
}
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
func (l *List[T]) insertValue(v any, at *Element[T]) *Element[T] {
e := new(Element[T])
e.Value = v.(T)
return l.insert(e, at)
}
// remove removes e from its list, decrements l.len
func (l *List[T]) remove(e *Element[T]) {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
e.list = nil
l.len--
}
// move moves e to next to at.
func (l *List[T]) move(e, at *Element[T]) {
if e == at {
return
}
e.prev.next = e.next
e.next.prev = e.prev
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
}
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List[T]) Remove(e *Element[T]) any {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero Element) and l.remove will crash
l.remove(e)
}
return e.Value
}
// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *List[T]) PushFront(v T) *Element[T] {
l.lazyInit()
return l.insertValue(v, &l.root)
}
// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *List[T]) PushBack(v T) *Element[T] {
l.lazyInit()
return l.insertValue(v, l.root.prev)
}
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
// see comment in List.Remove about initialization of l
return l.insertValue(v, mark.prev)
}
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
// see comment in List.Remove about initialization of l
return l.insertValue(v, mark)
}
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToFront(e *Element[T]) {
if e.list != l || l.root.next == e {
return
}
// see comment in List.Remove about initialization of l
l.move(e, &l.root)
}
// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToBack(e *Element[T]) {
if e.list != l || l.root.prev == e {
return
}
// see comment in List.Remove about initialization of l
l.move(e, l.root.prev)
}
// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark.prev)
}
// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark)
}
// PushBackList inserts a copy of another list at the back of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushBackList(other *List[T]) {
l.lazyInit()
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
l.insertValue(e.Value, l.root.prev)
}
}
// PushFrontList inserts a copy of another list at the front of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushFrontList(other *List[T]) {
l.lazyInit()
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
l.insertValue(e.Value, &l.root)
}
}

93
common/rw/output.go Normal file
View file

@ -0,0 +1,93 @@
package rw
import (
"io"
"sing/common"
"sing/common/buf"
"sing/common/list"
)
type OutputStream interface {
common.WriterWithUpstream
Process(p []byte) (n int, buffer *buf.Buffer, flush bool, err error)
}
type DirectException struct {
Suppressed error
}
func (e *DirectException) Error() string {
return "upstream used directly"
}
type processFunc func(p []byte) (n int, buffer *buf.Buffer, flush bool, err error)
type OutputStreamWriter struct {
upstream io.Writer
chain list.List[processFunc]
}
func (w *OutputStreamWriter) Upstream() io.Writer {
return w.upstream
}
func (w *OutputStreamWriter) Write(p []byte) (n int, err error) {
var needFlush bool
var buffers list.List[*buf.Buffer]
defer buf.ReleaseMulti(&buffers)
for stream := w.chain.Back(); stream != nil; stream = stream.Prev() {
// TODO: remove cast
var process processFunc = stream.Value
processed, buffer, flush, err := process(p)
if buffer != nil {
p = buffer.Bytes()
processed = buffer.Len()
buffers.PushBack(buffer)
}
if err != nil {
if directException, isDirectException := err.(*DirectException); isDirectException {
return processed, directException.Suppressed
}
return 0, err
}
p = p[:processed]
if flush {
needFlush = true
}
}
n, err = w.upstream.Write(p)
if err != nil {
return
}
if needFlush {
err = common.Flush(w.upstream)
}
return
}
func GetWriter(writer io.Writer) io.Writer {
if _, isOutputStreamWriter := writer.(*OutputStreamWriter); isOutputStreamWriter {
return writer
}
output := OutputStreamWriter{}
for index := 0; ; index++ {
if outputStream, isOutputStream := writer.(OutputStream); isOutputStream {
output.chain.PushFront(outputStream.Process)
writer = outputStream.Upstream()
} else if outputStreamWriter, isOutputStreamWriter := writer.(*OutputStreamWriter); isOutputStreamWriter {
writer = outputStreamWriter.upstream
output.chain.PushFrontList(&outputStreamWriter.chain)
} else {
if index == 0 {
return writer
}
break
}
}
output.upstream = writer
return &output
}

View file

@ -3,7 +3,6 @@ package socksaddr
import (
"encoding/binary"
"io"
"sing/common"
"sing/common/exceptions"
"sing/common/rw"

13
common/upstream.go Normal file
View file

@ -0,0 +1,13 @@
package common
import (
"io"
)
type ReaderWithUpstream interface {
Upstream() io.Reader
}
type WriterWithUpstream interface {
Upstream() io.Writer
}

222
example/shadowboom/main.go Normal file
View file

@ -0,0 +1,222 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"sing/common"
"sing/common/buf"
"sing/common/socksaddr"
"sing/protocol/shadowsocks"
_ "sing/protocol/shadowsocks/shadowstream"
cObfs "github.com/Dreamacro/clash/transport/ssr/obfs"
cProtocol "github.com/Dreamacro/clash/transport/ssr/protocol"
)
var (
address string
port int
method string
password string
obfs string
obfsParam string
protocol string
protocolParam string
ring int
uniqueIV bool
)
func main() {
fs := flag.NewFlagSet("shadowboom", flag.ExitOnError)
fs.StringVar(&address, "address", "", "server address")
fs.IntVar(&port, "port", 0, "server port")
fs.StringVar(&method, "method", "", "server cipher")
fs.StringVar(&password, "password", "", "server password")
fs.StringVar(&obfs, "obfs", "", "shadowsocksr obfuscate")
fs.StringVar(&obfsParam, "obfs-param", "", "shadowsocksr obfuscate parameter")
fs.StringVar(&protocol, "protocol", "", "shadowsocksr protocol")
fs.StringVar(&protocolParam, "protocol-param", "", "shadowsocksr protocol parameter")
fs.IntVar(&ring, "ring", 5000, "requests")
fs.BoolVar(&uniqueIV, "uniqueIV", false, "use unique iv for each request")
_ = fs.Parse(os.Args[1:])
if common.IsBlank(method) {
fs.Usage()
log.Fatal("method not defined")
}
if common.IsBlank(password) {
fs.Usage()
log.Fatal("password not defined")
}
cipher, err := shadowsocks.CreateCipher(method)
if err != nil {
log.Fatal(err)
}
key := shadowsocks.Key([]byte(password), cipher.KeySize())
if _, isAEAD := cipher.(*shadowsocks.AEADCipher); isAEAD {
log.Fatal("not a stream cipher: ", method)
}
ipAddr, err := net.ResolveIPAddr("ip", address)
if err != nil {
log.Fatal("unable to resolve server address: ", address, ": ", err)
}
addr := socksaddr.AddrFromIP(ipAddr.IP)
var sharedPayload *bytes.Buffer
if !uniqueIV {
sharedPayload = createRequest(cipher, key, addr, uint16(port))
}
for {
var payload *bytes.Buffer
if !uniqueIV {
payload = sharedPayload
} else {
payload = createRequest(cipher, key, addr, uint16(port))
}
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: ipAddr.IP,
Port: port,
})
if err != nil {
log.Print("failed to connect to server: ", err)
return
}
log.Print(fmt.Sprint("open connection to ", address, ":", port))
_, err = conn.Write(payload.Bytes())
if err != nil {
log.Print("failed to write request: ", err)
return
}
if uniqueIV {
payload.Reset()
}
go func() {
_, err = io.Copy(io.Discard, conn)
}()
}
}
func createRequest(cipher shadowsocks.Cipher, key []byte, addr socksaddr.Addr, port uint16) *bytes.Buffer {
fmt.Println("creating payload")
content := new(bytes.Buffer)
iv := buf.New()
iv.WriteZeroN(cipher.IVSize())
defer iv.Release()
var (
obfsInstance cObfs.Obfs
protocolInstance cProtocol.Protocol
overhead int
err error
)
if common.IsNotBlank(obfs) && obfs != "plain" {
obfsInstance, overhead, err = cObfs.PickObfs(obfs, &cObfs.Base{
Host: address,
Port: int(port),
Key: key,
IVSize: cipher.IVSize(),
Param: obfsParam,
})
if err != nil {
log.Fatalln(err)
}
}
if common.IsNotBlank(protocol) && protocol != "origin" {
protocolInstance, err = cProtocol.PickProtocol(protocol, &cProtocol.Base{
Key: key,
Overhead: overhead,
Param: protocolParam,
})
if err != nil {
log.Fatalln(err)
}
}
for i := 0; i < ring; i++ {
var buffer bytes.Buffer
var writer io.Writer = &buffer
if uniqueIV {
iv.Reset()
iv.WriteRandom(cipher.IVSize())
}
if obfsInstance != nil {
writer = obfsInstance.StreamConn(common.NewWritConn(writer))
}
_, err = writer.Write(iv.Bytes())
if err != nil {
fmt.Println(err)
break
}
writer, err = cipher.NewEncryptionWriter(key, iv.Bytes(), writer)
if err != nil {
fmt.Println(err)
break
}
if protocolInstance != nil {
writer = protocolInstance.StreamConn(common.NewWritConn(writer), iv.Bytes())
}
var addressAndPort bytes.Buffer
shadowsocks.AddressSerializer.WriteAddressAndPort(&addressAndPort, addr, port)
_, err = writer.Write(addressAndPort.Bytes())
if err != nil {
fmt.Println(err)
break
}
_, err = writer.Write(content.Bytes())
if err != nil {
fmt.Println(err)
break
}
addressAndPort.Reset()
content.Reset()
if i%1000 == 0 {
log.Print("ring ", i, ": ", byteSize(buffer.Len()))
}
content = &buffer
}
log.Print("finished ", ring, ": ", byteSize(content.Len()))
return content
}
func byteSize(b int) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "KMGTPE"[exp])
}

View file

@ -0,0 +1,25 @@
{
"log": {
"loglevel": "debug"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 1234,
"protocol": "shadowsocks",
"settings": {
"method": "aes-128-cfb",
"password": "test"
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
}
]
}

10
go.mod
View file

@ -12,9 +12,12 @@ require (
golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8
)
// for testing only
// for testing and example only
require github.com/v2fly/v2ray-core/v5 v5.0.3
require (
github.com/Dreamacro/clash v1.9.0
github.com/v2fly/v2ray-core/v5 v5.0.3
)
//replace github.com/v2fly/v2ray-core/v5 => ../v2ray-core
replace github.com/v2fly/v2ray-core/v5 => github.com/sagernet/v2ray-core/v5 v5.0.7-0.20220128184540-38f59e02f567
@ -24,12 +27,15 @@ replace github.com/v2fly/v2ray-core/v5 => github.com/sagernet/v2ray-core/v5 v5.0
replace gvisor.dev/gvisor => github.com/sagernet/gvisor v0.0.0-20220109124627-f8f67dadd776
require (
github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/pires/go-proxyproto v0.6.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
google.golang.org/protobuf v1.27.1 // indirect

18
go.sum
View file

@ -1,3 +1,7 @@
github.com/Dreamacro/clash v1.9.0 h1:IfmPW86Klngu0iQ4LL6Bhxcvtr+QaI7Oppa9qRPX/Q8=
github.com/Dreamacro/clash v1.9.0/go.mod h1:vOzDB9KKD/PirNdSlsH4soMl1xF5lk8SwNQiVY5UacE=
github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
@ -54,7 +58,10 @@ github.com/sagernet/v2ray-core/v5 v5.0.7-0.20220128184540-38f59e02f567 h1:ZqzVNu
github.com/sagernet/v2ray-core/v5 v5.0.7-0.20220128184540-38f59e02f567/go.mod h1:4FMkEwBDneahJymFQGpJtQ0OlC33hpmCoyUneaOQDno=
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 h1:4Yh46CVE3k/lPq6hUbEdbB1u1anRBXLewm3k+L0iOMc=
@ -63,18 +70,27 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
go.starlark.net v0.0.0-20211203141949-70c0e40ae128 h1:bxH+EXOo87zEOwKDdZ8Tevgi6irRbqheRm/fr293c58=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8 h1:kACShD3qhmr/3rLmg1yXyt+N4HcwutKyPRB93s54TIU=
golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=

View file

@ -1,10 +1,10 @@
package shadowsocks
import (
"bytes"
"io"
"sing/common/buf"
"sing/common/exceptions"
"sing/common/list"
)
type Cipher interface {
@ -12,26 +12,33 @@ type Cipher interface {
IVSize() int
NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (io.Writer, error)
NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (io.Reader, error)
EncodePacket(key []byte, buffer *bytes.Buffer) error
DecodePacket(key []byte, buffer *bytes.Buffer) error
EncodePacket(key []byte, buffer *buf.Buffer) error
DecodePacket(key []byte, buffer *buf.Buffer) error
}
type CipherCreator func() Cipher
var cipherList map[string]CipherCreator
var cipherList *list.List[string]
var cipherMap map[string]CipherCreator
func init() {
cipherList = make(map[string]CipherCreator)
cipherList = new(list.List[string])
cipherMap = make(map[string]CipherCreator)
}
func RegisterCipher(method string, creator CipherCreator) {
cipherList[method] = creator
cipherList.PushBack(method)
cipherMap[method] = creator
}
func CreateCipher(method string) (Cipher, error) {
creator := cipherList[method]
creator := cipherMap[method]
if creator != nil {
return creator(), nil
}
return nil, exceptions.New("unsupported method: ", method)
}
func ListCiphers() []string {
return cipherList.Array()
}

View file

@ -1,7 +1,6 @@
package shadowsocks
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/binary"
@ -14,6 +13,8 @@ import (
"sing/common/rw"
)
const PacketLengthBufferSize = 2
func init() {
RegisterCipher("aes-128-gcm", func() Cipher {
return &AEADCipher{
@ -94,31 +95,29 @@ func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader
return NewAEADReader(reader, c.Constructor(Kdf(key, iv, c.KeyLength))), nil
}
func (c *AEADCipher) EncodePacket(key []byte, buffer *bytes.Buffer) error {
aead := c.Constructor(Kdf(key, buffer.Bytes()[:c.IVLength], c.KeyLength))
end := buffer.Len()
buffer.Grow(aead.Overhead())
aead.Seal(buffer.Bytes()[:c.IVLength], rw.ZeroBytes[:aead.NonceSize()], buffer.Bytes()[c.IVLength:end], nil)
func (c *AEADCipher) EncodePacket(key []byte, buffer *buf.Buffer) error {
aead := c.Constructor(Kdf(key, buffer.To(c.IVLength), c.KeyLength))
aead.Seal(buffer.From(c.IVLength)[:0], rw.ZeroBytes[:aead.NonceSize()], buffer.From(c.IVLength), nil)
buffer.Extend(aead.Overhead())
return nil
}
func (c *AEADCipher) DecodePacket(key []byte, buffer *bytes.Buffer) error {
func (c *AEADCipher) DecodePacket(key []byte, buffer *buf.Buffer) error {
if buffer.Len() < c.IVLength {
return exceptions.New("bad packet")
}
aead := c.Constructor(Kdf(key, buffer.Bytes()[:c.IVLength], c.KeyLength))
_, err := aead.Open(buffer.Bytes()[:c.IVLength], rw.ZeroBytes[:aead.NonceSize()], buffer.Bytes()[c.IVLength:], nil)
aead := c.Constructor(Kdf(key, buffer.To(c.IVLength), c.KeyLength))
packet, err := aead.Open(buffer.Index(0), rw.ZeroBytes[:aead.NonceSize()], buffer.From(c.IVLength), nil)
if err != nil {
return err
}
buffer.Truncate(aead.Overhead())
buffer.Truncate(len(packet))
return nil
}
type AEADReader struct {
upstream io.Reader
cipher cipher.AEAD
buffer *bytes.Buffer
data []byte
nonce []byte
index int
@ -126,17 +125,18 @@ type AEADReader struct {
}
func NewAEADReader(upstream io.Reader, cipher cipher.AEAD) *AEADReader {
buffer := buf.New()
buffer.Grow(MaxPacketSize)
return &AEADReader{
upstream: upstream,
cipher: cipher,
buffer: buffer,
data: buffer.Bytes(),
data: buf.GetBytes(),
nonce: make([]byte, cipher.NonceSize()),
}
}
func (r *AEADReader) Upstream() io.Reader {
return r.upstream
}
func (r *AEADReader) Read(b []byte) (n int, err error) {
if r.cached > 0 {
n = copy(b, r.data[r.index:r.index+r.cached])
@ -187,34 +187,67 @@ func (r *AEADReader) Read(b []byte) (n int, err error) {
}
func (r *AEADReader) Close() error {
buf.Release(r.buffer)
if r.data != nil {
buf.PutBytes(r.data)
r.data = nil
}
return nil
}
type AEADWriter struct {
upstream io.Writer
cipher cipher.AEAD
buffer *bytes.Buffer
data []byte
nonce []byte
upstream io.Writer
cipher cipher.AEAD
data []byte
nonce []byte
maxDataSize int
}
func NewAEADWriter(upstream io.Writer, cipher cipher.AEAD) *AEADWriter {
buffer := buf.New()
buffer.Grow(MaxPacketSize)
return &AEADWriter{
upstream: upstream,
cipher: cipher,
buffer: buffer,
data: buffer.Bytes(),
nonce: make([]byte, cipher.NonceSize()),
upstream: upstream,
cipher: cipher,
data: buf.GetBytes(),
nonce: make([]byte, cipher.NonceSize()),
maxDataSize: MaxPacketSize - PacketLengthBufferSize - cipher.Overhead()*2,
}
}
func (w *AEADWriter) Write(p []byte) (n int, err error) {
maxDataSize := MaxPacketSize - PacketLengthBufferSize - w.cipher.Overhead()*2
func (w *AEADWriter) Upstream() io.Writer {
return w.upstream
}
for _, data := range buf.ForeachN(p, maxDataSize) {
func (w *AEADWriter) Process(p []byte) (n int, buffer *buf.Buffer, flush bool, err error) {
if len(p) > w.maxDataSize {
n, err = w.Write(p)
err = &rw.DirectException{
Suppressed: err,
}
return
}
binary.BigEndian.PutUint16(w.data[:PacketLengthBufferSize], uint16(len(p)))
encryptedLength := w.cipher.Seal(w.data[:0], w.nonce, w.data[:PacketLengthBufferSize], nil)
increaseNonce(w.nonce)
start := len(encryptedLength)
/*
no usage
if cap(p) > len(p)+PacketLengthBufferSize+2*w.cipher.Overhead() {
packet := w.cipher.Seal(p[:start], w.nonce, p, nil)
increaseNonce(w.nonce)
copy(p[:start], encryptedLength)
n = start + len(packet)
return
}
*/
packet := w.cipher.Seal(w.data[:start], w.nonce, p, nil)
increaseNonce(w.nonce)
return 0, buf.As(packet), false, err
}
func (w *AEADWriter) Write(p []byte) (n int, err error) {
for _, data := range buf.ForeachN(p, w.maxDataSize) {
binary.BigEndian.PutUint16(w.data[:PacketLengthBufferSize], uint16(len(data)))
w.cipher.Seal(w.data[:0], w.nonce, w.data[:PacketLengthBufferSize], nil)
@ -235,7 +268,10 @@ func (w *AEADWriter) Write(p []byte) (n int, err error) {
}
func (w *AEADWriter) Close() error {
buf.Release(w.buffer)
if w.data != nil {
buf.PutBytes(w.data)
w.data = nil
}
return nil
}

View file

@ -1,8 +1,9 @@
package shadowsocks
import (
"bytes"
"io"
"sing/common/buf"
)
func init() {
@ -29,10 +30,10 @@ func (c *NoneCipher) NewDecryptionReader(_ []byte, _ []byte, reader io.Reader) (
return reader, nil
}
func (c *NoneCipher) EncodePacket([]byte, *bytes.Buffer) error {
func (c *NoneCipher) EncodePacket([]byte, *buf.Buffer) error {
return nil
}
func (c *NoneCipher) DecodePacket([]byte, *bytes.Buffer) error {
func (c *NoneCipher) DecodePacket([]byte, *buf.Buffer) error {
return nil
}

View file

@ -5,6 +5,7 @@ import (
"bytes"
"io"
"net"
"sing/common/rw"
"strings"
"sync"
"testing"
@ -18,19 +19,19 @@ import (
"sing/common/crypto"
"sing/common/socksaddr"
"sing/protocol/shadowsocks"
_ "sing/protocol/shadowsocks/shadowstream"
)
func TestShadowsocksTCP(t *testing.T) {
func TestShadowsocks(t *testing.T) {
for index := 1; index <= int(vs.CipherType_XCHACHA20); index++ {
if index == 0 {
continue
}
cipherType := vs.CipherType(index)
cipher := strings.ReplaceAll(strings.ToLower(cipherType.String()), "_", "-")
t.Log("Test", cipher, "server")
testShadowsocksServerTCPWithCipher(t, cipherType, cipher)
t.Log("Test", cipher, "client")
testShadowsocksClientTCPWithCipher(t, cipherType, cipher)
t.Log("Test", cipher, "udp")
testShadowsocksUDPWithCipher(t, cipherType, cipher)
}
}
@ -43,7 +44,9 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
}
key := shadowsocks.Key([]byte(password), cipher.KeySize())
address := socksaddr.AddrFromFqdn("internal.sagernet.org")
data := crypto.RandomBytes(1024)
data := buf.New()
defer data.Release()
data.WriteRandom(1024)
protoAccount := &vs.Account{
Password: password,
@ -84,10 +87,7 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
return
}
conn := vb.NewConnection(vb.ConnectionOutputMulti(reader), vb.ConnectionInputMulti(writer))
buffer := vb.New()
defer buffer.Release()
buffer.Write(data)
_, err = conn.Write(buffer.Bytes())
_, err = conn.Write(data.ToOwned().Bytes())
if err != nil {
t.Error(err)
return
@ -98,7 +98,7 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
t.Error(err)
return
}
if bytes.Compare(clientRead, data) > 0 {
if bytes.Compare(clientRead, data.Bytes()) > 0 {
t.Error("bad response data")
return
}
@ -117,6 +117,7 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
if err != nil {
t.Fatal(err)
}
defer common.Close(reader)
addr, port, err := shadowsocks.AddressSerializer.ReadAddressAndPort(reader)
if err != nil {
@ -144,7 +145,7 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
t.Fatal(err)
}
if bytes.Compare(serverRead, data) > 0 {
if bytes.Compare(serverRead, data.Bytes()) > 0 {
t.Fatal("bad request data")
}
@ -152,10 +153,9 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
if err != nil {
t.Fatal(err)
}
buffer := buf.New()
defer buf.Release(buffer)
buffer.Write(data)
_, err = writer.Write(buffer.Bytes())
writer = rw.GetWriter(writer)
defer common.Close(writer)
_, err = writer.Write(data.ToOwned().Bytes())
if err != nil {
t.Fatal(err)
}
@ -163,6 +163,41 @@ func testShadowsocksServerTCPWithCipher(t *testing.T, cipherType vs.CipherType,
wg.Wait()
}
func BenchmarkShadowsocks(b *testing.B) {
b.ReportAllocs()
for _, cipher := range shadowsocks.ListCiphers() {
b.Run(cipher, func(b *testing.B) {
benchmarkShadowsocksCipher(b, cipher, 14*1024)
})
}
}
func benchmarkShadowsocksCipher(b *testing.B, method string, data int) {
b.StopTimer()
b.ResetTimer()
b.SetBytes(int64(data))
cipher, _ := shadowsocks.CreateCipher(method)
iv := buf.New()
defer iv.Release()
iv.WriteRandom(cipher.IVSize())
writer, _ := cipher.NewEncryptionWriter(shadowsocks.Key([]byte("test"), cipher.KeySize()), iv.Bytes(), io.Discard)
defer common.Close(writer)
buffer := buf.New()
defer buffer.Release()
buffer.Extend(data)
b.StartTimer()
if output, ok := writer.(rw.OutputStream); ok {
for i := 0; i < b.N; i++ {
output.Process(buffer.Bytes())
}
} else {
writer.Write(buffer.Bytes())
}
}
func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType, cipherName string) {
password := "fuck me till the daylight"
cipher, err := shadowsocks.CreateCipher(cipherName)
@ -172,7 +207,9 @@ func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType,
}
key := shadowsocks.Key([]byte(password), cipher.KeySize())
address := socksaddr.AddrFromFqdn("internal.sagernet.org")
data := crypto.RandomBytes(1024)
data := buf.New()
data.WriteRandom(1024)
defer data.Release()
protoAccount := &vs.Account{
Password: password,
@ -214,10 +251,7 @@ func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType,
return
}
conn := vb.NewConnection(vb.ConnectionOutputMulti(reader), vb.ConnectionInputMulti(writer))
buffer := vb.New()
defer buffer.Release()
buffer.Write(data)
_, err = conn.Write(buffer.Bytes())
_, err = conn.Write(data.Bytes())
if err != nil {
t.Error(err)
return
@ -228,7 +262,7 @@ func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType,
t.Error(err)
return
}
if bytes.Compare(serverRead, data) > 0 {
if bytes.Compare(serverRead, data.Bytes()) > 0 {
t.Error("bad request data")
return
}
@ -245,15 +279,13 @@ func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType,
if err != nil {
t.Fatal(err)
}
defer common.Close(ew)
bw := bufio.NewWriter(ew)
err = shadowsocks.AddressSerializer.WriteAddressAndPort(bw, address, 443)
if err != nil {
t.Fatal(err)
}
buffer := buf.New()
defer buf.Release(buffer)
buffer.Write(data)
_, err = bw.Write(buffer.Bytes())
_, err = bw.Write(data.ToOwned().Bytes())
if err != nil {
t.Fatal(err)
}
@ -274,15 +306,70 @@ func testShadowsocksClientTCPWithCipher(t *testing.T, cipherType vs.CipherType,
if err != nil {
t.Fatal(err)
}
defer common.Close(input)
clientRead := make([]byte, 1024)
_, err = io.ReadFull(input, clientRead)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(clientRead, data) > 0 {
if bytes.Compare(clientRead, data.Bytes()) > 0 {
t.Fatal("bad response data")
}
client.Close()
wg.Wait()
}
func testShadowsocksUDPWithCipher(t *testing.T, cipherType vs.CipherType, cipherName string) {
password := "fuck me till the daylight"
cipher, err := shadowsocks.CreateCipher(cipherName)
if err != nil {
t.Log("Skip unsupported method: ", cipherName)
return
}
key := shadowsocks.Key([]byte(password), cipher.KeySize())
address := socksaddr.AddrFromFqdn("internal.sagernet.org")
data := buf.New()
defer data.Release()
data.WriteRandom(1024)
protoAccount := &vs.Account{
Password: password,
CipherType: cipherType,
}
memoryAccount, err := protoAccount.AsAccount()
common.Must(err)
memoryUser := &vp.MemoryUser{
Account: memoryAccount,
}
req := &vp.RequestHeader{
Version: vs.Version,
Command: vp.RequestCommandUDP,
Address: vn.DomainAddress(address.Fqdn()),
Port: 443,
User: memoryUser,
}
packet, err := vs.EncodeUDPPacket(req, data.Bytes(), nil)
if err != nil {
t.Fatal(err)
}
buffer := buf.New()
defer buffer.Release()
buffer.Write(packet.BytesTo(int32(cipher.IVSize())))
err = shadowsocks.AddressSerializer.WriteAddressAndPort(buffer, address, 443)
if err != nil {
t.Fatal(err)
}
buffer.Write(data.Bytes())
err = cipher.EncodePacket(key, buffer)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(packet.Bytes(), buffer.Bytes()) > 0 {
t.Fatal("bad request data\n", packet.Bytes(), "\n", buffer.Bytes())
}
}

View file

@ -3,17 +3,16 @@ package shadowsocks
import (
"crypto/md5"
"crypto/sha1"
"hash/crc32"
"io"
"math/rand"
"golang.org/x/crypto/hkdf"
"sing/common"
"sing/common/socksaddr"
)
const (
MaxPacketSize = 16*1024 - 1
PacketLengthBufferSize = 2
)
const MaxPacketSize = 16*1024 - 1
func Kdf(key, iv []byte, keyLength int) []byte {
subKey := make([]byte, keyLength)
@ -44,6 +43,14 @@ func Key(password []byte, keySize int) []byte {
return m[:keySize]
}
func RemapToPrintable(input []byte) {
const charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\\\""
seed := rand.New(rand.NewSource(int64(crc32.ChecksumIEEE(input))))
for i := range input {
input[i] = charSet[seed.Intn(len(charSet))]
}
}
var AddressSerializer = socksaddr.NewSerializer(
socksaddr.AddressFamilyByte(0x01, socksaddr.AddressFamilyIPv4),
socksaddr.AddressFamilyByte(0x04, socksaddr.AddressFamilyIPv6),

View file

@ -1,16 +1,17 @@
//go:build !no_shadowsocks_stream && !(arm64 || ppc64le || s390x)
//go:build !(arm64 || ppc64le || s390x)
package shadowsocks
package shadowstream
import (
"crypto/cipher"
"github.com/aead/chacha20"
"github.com/aead/chacha20/chacha"
"sing/protocol/shadowsocks"
)
func init() {
RegisterCipher("chacha20", func() Cipher {
shadowsocks.RegisterCipher("chacha20", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: chacha.KeySize,
IVLength: chacha.NonceSize,
@ -22,7 +23,7 @@ func init() {
},
}
})
RegisterCipher("xchacha20", func() Cipher {
shadowsocks.RegisterCipher("xchacha20", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: chacha.KeySize,
IVLength: chacha.XNonceSize,

View file

@ -1,15 +1,16 @@
//go:build !no_shadowsocks_stream && (arm64 || ppc64le || s390x)
//go:build arm64 || ppc64le || s390x
package shadowsocks
package shadowstream
import (
"crypto/cipher"
"golang.org/x/crypto/chacha20"
"sing/protocol/shadowsocks"
)
func init() {
RegisterCipher("chacha20", func() Cipher {
shadowsocks.RegisterCipher("chacha20", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: chacha20.KeySize,
IVLength: chacha20.NonceSize,
@ -21,7 +22,7 @@ func init() {
},
}
})
RegisterCipher("xchacha20", func() Cipher {
shadowsocks.RegisterCipher("xchacha20", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: chacha20.KeySize,
IVLength: chacha20.NonceSizeX,

View file

@ -1,9 +1,6 @@
//go:build !no_shadowsocks_stream
package shadowsocks
package shadowstream
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/des"
@ -21,12 +18,14 @@ import (
"github.com/kierdavis/cfb8"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
"sing/common/buf"
"sing/common/crypto"
"sing/common/exceptions"
"sing/protocol/shadowsocks"
)
func init() {
RegisterCipher("aes-128-ctr", func() Cipher {
shadowsocks.RegisterCipher("aes-128-ctr", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: aes.BlockSize,
@ -34,7 +33,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewCTR),
}
})
RegisterCipher("aes-192-ctr", func() Cipher {
shadowsocks.RegisterCipher("aes-192-ctr", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: aes.BlockSize,
@ -42,7 +41,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewCTR),
}
})
RegisterCipher("aes-256-ctr", func() Cipher {
shadowsocks.RegisterCipher("aes-256-ctr", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: aes.BlockSize,
@ -51,7 +50,7 @@ func init() {
}
})
RegisterCipher("aes-128-cfb", func() Cipher {
shadowsocks.RegisterCipher("aes-128-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: aes.BlockSize,
@ -59,7 +58,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewCFBDecrypter),
}
})
RegisterCipher("aes-192-cfb", func() Cipher {
shadowsocks.RegisterCipher("aes-192-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: aes.BlockSize,
@ -67,7 +66,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewCFBDecrypter),
}
})
RegisterCipher("aes-256-cfb", func() Cipher {
shadowsocks.RegisterCipher("aes-256-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: aes.BlockSize,
@ -76,7 +75,7 @@ func init() {
}
})
RegisterCipher("aes-128-cfb8", func() Cipher {
shadowsocks.RegisterCipher("aes-128-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: aes.BlockSize,
@ -84,7 +83,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cfb8.NewDecrypter),
}
})
RegisterCipher("aes-192-cfb8", func() Cipher {
shadowsocks.RegisterCipher("aes-192-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: aes.BlockSize,
@ -92,7 +91,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cfb8.NewDecrypter),
}
})
RegisterCipher("aes-256-cfb8", func() Cipher {
shadowsocks.RegisterCipher("aes-256-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: aes.BlockSize,
@ -101,7 +100,7 @@ func init() {
}
})
RegisterCipher("aes-128-ofb", func() Cipher {
shadowsocks.RegisterCipher("aes-128-ofb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: aes.BlockSize,
@ -109,7 +108,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewOFB),
}
})
RegisterCipher("aes-192-ofb", func() Cipher {
shadowsocks.RegisterCipher("aes-192-ofb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: aes.BlockSize,
@ -117,7 +116,7 @@ func init() {
DecryptConstructor: blockStream(aes.NewCipher, cipher.NewOFB),
}
})
RegisterCipher("aes-256-ofb", func() Cipher {
shadowsocks.RegisterCipher("aes-256-ofb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: aes.BlockSize,
@ -126,7 +125,7 @@ func init() {
}
})
RegisterCipher("rc4", func() Cipher {
shadowsocks.RegisterCipher("rc4", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: 16,
@ -138,7 +137,7 @@ func init() {
},
}
})
RegisterCipher("rc4-md5", func() Cipher {
shadowsocks.RegisterCipher("rc4-md5", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: 16,
@ -157,7 +156,7 @@ func init() {
}
})
RegisterCipher("bf-cfb", func() Cipher {
shadowsocks.RegisterCipher("bf-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: blowfish.BlockSize,
@ -165,7 +164,7 @@ func init() {
DecryptConstructor: blockStream(func(key []byte) (cipher.Block, error) { return blowfish.NewCipher(key) }, cipher.NewCFBDecrypter),
}
})
RegisterCipher("cast5-cfb", func() Cipher {
shadowsocks.RegisterCipher("cast5-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: cast5.BlockSize,
@ -173,7 +172,7 @@ func init() {
DecryptConstructor: blockStream(func(key []byte) (cipher.Block, error) { return cast5.NewCipher(key) }, cipher.NewCFBDecrypter),
}
})
RegisterCipher("des-cfb", func() Cipher {
shadowsocks.RegisterCipher("des-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 8,
IVLength: des.BlockSize,
@ -181,7 +180,7 @@ func init() {
DecryptConstructor: blockStream(des.NewCipher, cipher.NewCFBDecrypter),
}
})
RegisterCipher("idea-cfb", func() Cipher {
shadowsocks.RegisterCipher("idea-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: 8,
@ -189,7 +188,7 @@ func init() {
DecryptConstructor: blockStream(idea.NewCipher, cipher.NewCFBDecrypter),
}
})
RegisterCipher("rc2-cfb", func() Cipher {
shadowsocks.RegisterCipher("rc2-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: rc2.BlockSize,
@ -197,7 +196,7 @@ func init() {
DecryptConstructor: blockStream(func(key []byte) (cipher.Block, error) { return rc2.New(key, 16) }, cipher.NewCFBDecrypter),
}
})
RegisterCipher("seed-cfb", func() Cipher {
shadowsocks.RegisterCipher("seed-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: seed.BlockSize,
@ -206,7 +205,7 @@ func init() {
}
})
RegisterCipher("camellia-128-cfb", func() Cipher {
shadowsocks.RegisterCipher("camellia-128-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: camellia.BlockSize,
@ -214,7 +213,7 @@ func init() {
DecryptConstructor: blockStream(camellia.New, cipher.NewCFBDecrypter),
}
})
RegisterCipher("camellia-192-cfb", func() Cipher {
shadowsocks.RegisterCipher("camellia-192-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: camellia.BlockSize,
@ -222,7 +221,7 @@ func init() {
DecryptConstructor: blockStream(camellia.New, cipher.NewCFBDecrypter),
}
})
RegisterCipher("camellia-256-cfb", func() Cipher {
shadowsocks.RegisterCipher("camellia-256-cfb", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: camellia.BlockSize,
@ -231,7 +230,7 @@ func init() {
}
})
RegisterCipher("camellia-128-cfb8", func() Cipher {
shadowsocks.RegisterCipher("camellia-128-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 16,
IVLength: camellia.BlockSize,
@ -239,7 +238,7 @@ func init() {
DecryptConstructor: blockStream(camellia.New, cfb8.NewDecrypter),
}
})
RegisterCipher("camellia-192-cfb8", func() Cipher {
shadowsocks.RegisterCipher("camellia-192-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 24,
IVLength: camellia.BlockSize,
@ -247,7 +246,7 @@ func init() {
DecryptConstructor: blockStream(camellia.New, cfb8.NewDecrypter),
}
})
RegisterCipher("camellia-256-cfb8", func() Cipher {
shadowsocks.RegisterCipher("camellia-256-cfb8", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: camellia.BlockSize,
@ -256,7 +255,7 @@ func init() {
}
})
RegisterCipher("salsa20", func() Cipher {
shadowsocks.RegisterCipher("salsa20", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: 32,
IVLength: 8,
@ -265,7 +264,7 @@ func init() {
}
})
RegisterCipher("chacha20-ietf", func() Cipher {
shadowsocks.RegisterCipher("chacha20-ietf", func() shadowsocks.Cipher {
return &StreamCipher{
KeyLength: chacha.KeySize,
IVLength: chacha.INonceSize,
@ -321,22 +320,22 @@ func (s *StreamCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Read
return &StreamReader{reader, streamCipher}, nil
}
func (s *StreamCipher) EncodePacket(key []byte, buffer *bytes.Buffer) error {
iv := buffer.Bytes()[:s.IVLength]
func (s *StreamCipher) EncodePacket(key []byte, buffer *buf.Buffer) error {
iv := buffer.To(s.IVLength)
streamCipher, err := s.EncryptConstructor(key, iv)
if err != nil {
return err
}
data := buffer.Bytes()[s.IVLength:]
data := buffer.From(s.IVLength)
streamCipher.XORKeyStream(data, data)
return nil
}
func (s *StreamCipher) DecodePacket(key []byte, buffer *bytes.Buffer) error {
func (s *StreamCipher) DecodePacket(key []byte, buffer *buf.Buffer) error {
if buffer.Len() <= s.IVLength {
return exceptions.New("insufficient data: ", buffer.Len())
}
iv := buffer.Bytes()[:s.IVLength]
iv := buffer.From(s.IVLength)
streamCipher, err := s.DecryptConstructor(key, iv)
if err != nil {
return err
@ -352,6 +351,18 @@ type StreamReader struct {
cipher cipher.Stream
}
func (r *StreamReader) Upstream() io.Reader {
return r.upstream
}
func (r *StreamReader) Process(p []byte, readN int) (n int, err error) {
n = readN
if n > 0 {
r.cipher.XORKeyStream(p[:n], p[:n])
}
return
}
func (r *StreamReader) Read(p []byte) (n int, err error) {
n, err = r.upstream.Read(p)
if err != nil {
@ -368,6 +379,16 @@ type StreamWriter struct {
cipher cipher.Stream
}
func (w *StreamWriter) Upstream() io.Writer {
return w.upstream
}
func (w *StreamWriter) Process(p []byte) (n int, buffer *buf.Buffer, flush bool, err error) {
w.cipher.XORKeyStream(p, p)
n = len(p)
return
}
func (w *StreamWriter) Write(p []byte) (n int, err error) {
w.cipher.XORKeyStream(p, p)
return w.upstream.Write(p)

25
shadowboom.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
build() {
go build -v -o "$1" -trimpath -buildinfo=false -buildvcs=false -ldflags "-s -w -buildid=" ./example/shadowboom
}
export GOARCH=amd64
build sing_shadowboom_amd64
export GOARCH=386
build sing_shadowboom_386
export GOARCH=arm64
build sing_shadowboom_arm64
export GOOS=windows
export GOARCH=amd64
build sing_shadowboom_amd64.exe
export GOARCH=386
build sing_shadowboom_386.exe
export GOARCH=arm64
build sing_shadowboom_arm64.exe