mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
234 lines
5.1 KiB
Go
234 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/pbkdf2"
|
|
|
|
"github.com/hashicorp/yamux"
|
|
"github.com/urfave/cli"
|
|
"github.com/xtaci/kcp-go"
|
|
)
|
|
|
|
var (
|
|
VERSION = "SELFBUILD"
|
|
SALT = "kcp-go"
|
|
)
|
|
|
|
func handleClient(p1, p2 net.Conn) {
|
|
log.Println("stream opened")
|
|
defer log.Println("stream closed")
|
|
defer p1.Close()
|
|
defer p2.Close()
|
|
|
|
// start tunnel
|
|
p1die := make(chan struct{})
|
|
go func() {
|
|
io.Copy(p1, p2)
|
|
close(p1die)
|
|
}()
|
|
|
|
p2die := make(chan struct{})
|
|
go func() {
|
|
io.Copy(p2, p1)
|
|
close(p2die)
|
|
}()
|
|
|
|
// wait for tunnel termination
|
|
select {
|
|
case <-p1die:
|
|
case <-p2die:
|
|
}
|
|
}
|
|
|
|
func checkError(err error) {
|
|
if err != nil {
|
|
log.Println(err)
|
|
os.Exit(-1)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
rand.Seed(int64(time.Now().Nanosecond()))
|
|
myApp := cli.NewApp()
|
|
myApp.Name = "kcptun"
|
|
myApp.Usage = "kcptun client"
|
|
myApp.Version = VERSION
|
|
myApp.Flags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "localaddr,l",
|
|
Value: ":12948",
|
|
Usage: "local listen address",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "remoteaddr, r",
|
|
Value: "vps:29900",
|
|
Usage: "kcp server address",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "key",
|
|
Value: "it's a secrect",
|
|
Usage: "key for communcation, must be the same as kcptun server",
|
|
EnvVar: "KCPTUN_KEY",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "crypt",
|
|
Value: "aes",
|
|
Usage: "methods for encryption: aes, tea, xor, none",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "mode",
|
|
Value: "fast",
|
|
Usage: "mode for communication: fast3, fast2, fast, normal",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "conn",
|
|
Value: 1,
|
|
Usage: "establish N physical connections as specified by 'conn' to server",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "mtu",
|
|
Value: 1350,
|
|
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "sndwnd",
|
|
Value: 128,
|
|
Usage: "set send window size(num of packets)",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "rcvwnd",
|
|
Value: 1024,
|
|
Usage: "set receive window size(num of packets)",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "fec",
|
|
Value: 4,
|
|
Usage: "set FEC group size, must be the same as server",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "acknodelay",
|
|
Usage: "flush ack immediately when a packet is received",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "dscp",
|
|
Value: 0,
|
|
Usage: "set DSCP(6bit)",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "nodelay",
|
|
Value: 0,
|
|
Hidden: true,
|
|
},
|
|
cli.IntFlag{
|
|
Name: "interval",
|
|
Value: 40,
|
|
Hidden: true,
|
|
},
|
|
cli.IntFlag{
|
|
Name: "resend",
|
|
Value: 0,
|
|
Hidden: true,
|
|
},
|
|
cli.IntFlag{
|
|
Name: "nc",
|
|
Value: 0,
|
|
Hidden: true,
|
|
},
|
|
}
|
|
myApp.Action = func(c *cli.Context) {
|
|
log.Println("version:", VERSION)
|
|
addr, err := net.ResolveTCPAddr("tcp", c.String("localaddr"))
|
|
checkError(err)
|
|
listener, err := net.ListenTCP("tcp", addr)
|
|
checkError(err)
|
|
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
|
|
|
// kcp server
|
|
var block kcp.BlockCrypt
|
|
switch c.String("crypt") {
|
|
case "tea":
|
|
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
|
case "xor":
|
|
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
|
case "none":
|
|
block, _ = kcp.NewNoneBlockCrypt(pass)
|
|
default:
|
|
block, _ = kcp.NewAESBlockCrypt(pass)
|
|
}
|
|
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
|
|
|
switch c.String("mode") {
|
|
case "normal":
|
|
nodelay, interval, resend, nc = 0, 30, 2, 1
|
|
case "fast":
|
|
nodelay, interval, resend, nc = 0, 20, 2, 1
|
|
case "fast2":
|
|
nodelay, interval, resend, nc = 1, 20, 2, 1
|
|
case "fast3":
|
|
nodelay, interval, resend, nc = 1, 10, 2, 1
|
|
}
|
|
|
|
log.Println("listening on:", listener.Addr())
|
|
log.Println("encryption:", c.String("crypt"))
|
|
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
|
|
log.Println("remote address:", c.String("remoteaddr"))
|
|
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
|
|
log.Println("mtu:", c.Int("mtu"))
|
|
log.Println("fec:", c.Int("fec"))
|
|
log.Println("acknodelay:", c.Bool("acknodelay"))
|
|
log.Println("dscp:", c.Int("dscp"))
|
|
log.Println("conn:", c.Int("conn"))
|
|
createConn := func() *yamux.Session {
|
|
kcpconn, err := kcp.DialWithOptions(c.Int("fec"), c.String("remoteaddr"), block)
|
|
checkError(err)
|
|
kcpconn.SetNoDelay(nodelay, interval, resend, nc)
|
|
kcpconn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
|
|
kcpconn.SetMtu(c.Int("mtu"))
|
|
kcpconn.SetACKNoDelay(c.Bool("acknodelay"))
|
|
kcpconn.SetDSCP(c.Int("dscp"))
|
|
|
|
// stream multiplex
|
|
config := &yamux.Config{
|
|
AcceptBacklog: 256,
|
|
EnableKeepAlive: true,
|
|
KeepAliveInterval: 30 * time.Second,
|
|
ConnectionWriteTimeout: 30 * time.Second,
|
|
MaxStreamWindowSize: 16777216,
|
|
LogOutput: os.Stderr,
|
|
}
|
|
session, err := yamux.Client(kcpconn, config)
|
|
checkError(err)
|
|
return session
|
|
}
|
|
|
|
var muxes []*yamux.Session
|
|
for i := 0; i < c.Int("conn"); i++ {
|
|
muxes = append(muxes, createConn())
|
|
}
|
|
|
|
rr := 0
|
|
for {
|
|
p1, err := listener.AcceptTCP()
|
|
checkError(err)
|
|
mux := muxes[rr%len(muxes)]
|
|
p2, err := mux.Open()
|
|
if err != nil { // yamux failure
|
|
log.Println(err)
|
|
p1.Close()
|
|
mux.Close()
|
|
muxes[rr%len(muxes)] = createConn()
|
|
}
|
|
go handleClient(p1, p2)
|
|
rr++
|
|
}
|
|
}
|
|
myApp.Run(os.Args)
|
|
}
|