shadowsocks

package
v0.0.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2024 License: Apache-2.0 Imports: 21 Imported by: 5

Documentation

Overview

Package shadowsocks implements the Shadowsocks secure transport and proxy protocols.

Shadowsocks is a a combination of two protocols:

  • Encrypted transport: uses authenticated encryption for privacy and security. Traffic appears random to avoid detection, with no distinguishable pattern or markers.
  • Proxy protocol: a simplified SOCKS5-like protocol for routing TCP and UDP connections to various destinations.

Setting up Shadowsocks Servers

When using Shadowsocks, you will need a server. There are many ways to run Shadowsocks servers. We recommend:

  • Outline Manager app: The easiest way to create and manage Shadowsocks servers in the cloud.
  • outline-ss-server: A command-line tool for advanced users offering greater configuration flexibility.

IPv6 Limitations

The Shadowsocks proxy protocol lacks a mechanism for servers to signal successful connection to a destination. For that reason, the StreamDialer immediately returns a connection once the TCP connection to the proxy is established, but before the connection to the destination by the proxy happens.

This is fine for dialed addresses that use a host name, since the name resolution will happen in the proxy, and the proxy will handle address selection for the client. That is usually the case for proxy apps. However in VPN apps using a "tun2socks" approach, the client is doing the name resolution and address selection, dialing using IP addresses. Because the dialer returns a successful connection regardless of the destination connectivity, this breaks the Happy Eyeballs address selection, effectively breaking IPv6 support.

It's recommended that you prioritize hostname-based dialing for optimal IPv6 compatibility, and disable IPv6 if name resolution and address selection happens on the client side, as is the case of VPN apps.

Security Considerations

Shadowsocks uses strong authenticated encryption (AEAD), standardized by the IETF. For privacy and security, this package does not support the legacy and unsafe stream ciphers.

Shadowsocks does not provide forward-secrecy. That can be accomplished by generating a new, completely random secret for every session, and delivering it to the client in a forward-secret way. With Outline, that can be done via Dynamic Keys: when the Dynamic Key is requested, generate a new secret. The response is sent over TLS, which implements forward-secrecy.

Index

Constants

This section is empty.

Variables

View Source
var (
	CHACHA20IETFPOLY1305 = "AEAD_CHACHA20_POLY1305"
	AES256GCM            = "AEAD_AES_256_GCM"
	AES192GCM            = "AEAD_AES_192_GCM"
	AES128GCM            = "AEAD_AES_128_GCM"
)

List of supported AEAD ciphers, as specified at https://shadowsocks.org/guide/aead.html

View Source
var ErrShortPacket = errors.New("short packet")

ErrShortPacket indicates that the destination packet given to Unpack is too short.

Functions

func NewPacketListener

func NewPacketListener(endpoint transport.PacketEndpoint, key *EncryptionKey) (transport.PacketListener, error)

func Pack

func Pack(dst, plaintext []byte, key *EncryptionKey) ([]byte, error)

Pack encrypts a Shadowsocks-UDP packet and returns a slice containing the encrypted packet. dst must be big enough to hold the encrypted packet. If plaintext and dst overlap but are not aligned for in-place encryption, this function will panic.

func Unpack

func Unpack(dst, pkt []byte, key *EncryptionKey) ([]byte, error)

Unpack decrypts a Shadowsocks-UDP packet in the format [salt][cipherText][AEAD tag] and returns a slice containing the decrypted payload or an error. If dst is present, it is used to store the plaintext, and must have enough capacity. If dst is nil, decryption proceeds in-place.

Types

type EncryptionKey

type EncryptionKey struct {
	// contains filtered or unexported fields
}

EncryptionKey encapsulates a Shadowsocks AEAD spec and a secret

func NewEncryptionKey

func NewEncryptionKey(cipherName string, secretText string) (*EncryptionKey, error)

NewEncryptionKey creates a Cipher with a cipher name and a secret. The cipher name must be the IETF name (as per https://www.iana.org/assignments/aead-parameters/aead-parameters.xhtml) or the Shadowsocks alias from https://shadowsocks.org/guide/aead.html.

func (*EncryptionKey) NewAEAD

func (c *EncryptionKey) NewAEAD(salt []byte) (cipher.AEAD, error)

NewAEAD creates the AEAD for this cipher

func (*EncryptionKey) SaltSize

func (c *EncryptionKey) SaltSize() int

SaltSize is the size of the salt for this Cipher

func (*EncryptionKey) TagSize

func (c *EncryptionKey) TagSize() int

TagSize is the size of the AEAD tag for this Cipher

type ErrUnsupportedCipher

type ErrUnsupportedCipher struct {
	// The name of the requested [Cipher]
	Name string
}

ErrUnsupportedCipher is returned by [CypherByName] when the named cipher is not supported.

func (ErrUnsupportedCipher) Error

func (err ErrUnsupportedCipher) Error() string

type Reader

type Reader interface {
	io.Reader
	io.WriterTo
}

Reader is an io.Reader that also implements io.WriterTo to allow for piping the data without extra allocations and copies.

func NewReader

func NewReader(reader io.Reader, key *EncryptionKey) Reader

NewReader creates a Reader that decrypts the given io.Reader using the shadowsocks protocol with the given encryption key.

type SaltGenerator

type SaltGenerator interface {
	// Returns a new salt
	GetSalt(salt []byte) error
}

SaltGenerator generates unique salts to use in Shadowsocks connections.

var RandomSaltGenerator SaltGenerator = randomSaltGenerator{}

RandomSaltGenerator is a basic SaltGenerator.

func NewPrefixSaltGenerator

func NewPrefixSaltGenerator(prefix []byte) SaltGenerator

NewPrefixSaltGenerator returns a SaltGenerator with output including the provided prefix, followed by random bytes. This is useful to change how shadowsocks traffic is classified by middleboxes.

Note: Prefixes steal entropy from the initialization vector. This weakens security by increasing the likelihood that the same IV is used in two different connections (which becomes likely once 2^(N/2) connections are made, due to the birthday attack). If an IV is reused, the attacker can not only decrypt the ciphertext of those two connections; they can also easily recover the shadowsocks key and decrypt all other connections to this server. Use with care!

type StreamDialer

type StreamDialer struct {

	// SaltGenerator is used by Shadowsocks to generate the connection salts.
	// `SaltGenerator` can be `nil`, which defaults to [shadowsocks.RandomSaltGenerator].
	SaltGenerator SaltGenerator

	// ClientDataWait specifies the amount of time to wait for client data before sending
	// the Shadowsocks connection request to the proxy server. This value is 10 milliseconds
	// by default.
	//
	// StreamDialer has an optimization to send the initial client payload along with
	// the Shadowsocks connection request.  This saves one packet during connection, and also
	// reduces the distinctiveness of the connection pattern.
	//
	// Normally, the initial payload will be sent as soon as the socket is connected,
	// except for delays due to inter-process communication.  However, some protocols
	// expect the server to send data first, in which case there is no client payload.
	// We therefore use a short delay by default (10ms), longer than any reasonable IPC but shorter than
	// typical network latency.  (In an Android emulator, the 90th percentile delay
	// was ~1 ms.)  If no client payload is received by this time, we connect without it.
	ClientDataWait time.Duration
	// contains filtered or unexported fields
}

func NewStreamDialer

func NewStreamDialer(endpoint transport.StreamEndpoint, key *EncryptionKey) (*StreamDialer, error)

NewStreamDialer creates a client that routes connections to a Shadowsocks proxy listening at the given StreamEndpoint, with `key` as the Shadowsocks encyption key.

func (*StreamDialer) DialStream added in v0.0.12

func (c *StreamDialer) DialStream(ctx context.Context, remoteAddr string) (transport.StreamConn, error)

DialStream implements StreamDialer.DialStream using a Shadowsocks server.

The Shadowsocks StreamDialer returns a connection after the connection to the proxy is established, but before the connection to the target is established. That means we cannot signal "connection refused" or "connection timeout" errors from the target to the application.

This behavior breaks IPv6 Happy Eyeballs because the application IPv6 socket will connect successfully, even if the proxy fails to connect to the IPv6 destination. The broken Happy Eyeballs behavior makes IPv6 unusable if the proxy cannot use IPv6.

We can't easily fix that issue because Shadowsocks, unlike SOCKS, does not have a way to indicate whether the target connection is successful. Even if that was possible, we want to wait until we have initial data from the application in order to send the Shadowsocks salt, SOCKS address and initial data all in one packet. This makes the size of the initial packet hard to predict, avoiding packet size fingerprinting. We can only get the application initial data if we return a connection first.

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer is an io.Writer that also implements io.ReaderFrom to allow for piping the data without extra allocations and copies. The LazyWrite and Flush methods allow a header to be added but delayed until the first write, for concatenation. All methods except Flush must be called from a single thread.

func NewWriter

func NewWriter(writer io.Writer, key *EncryptionKey) *Writer

NewWriter creates a Writer that encrypts the given io.Writer using the shadowsocks protocol with the given encryption key.

func (*Writer) Flush

func (sw *Writer) Flush() error

Flush sends the pending data, if any. This method is thread-safe.

func (*Writer) LazyWrite

func (sw *Writer) LazyWrite(p []byte) (int, error)

LazyWrite queues p to be written, but doesn't send it until Flush() is called, a non-lazy write is made, or the buffer is filled.

func (*Writer) ReadFrom

func (sw *Writer) ReadFrom(r io.Reader) (int64, error)

ReadFrom implements the io.ReaderFrom interface.

func (*Writer) SetSaltGenerator

func (sw *Writer) SetSaltGenerator(saltGenerator SaltGenerator)

SetSaltGenerator sets the salt generator to be used. Must be called before the first write.

func (*Writer) Write

func (sw *Writer) Write(p []byte) (int, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL