mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
167 lines
3.5 KiB
Go
167 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/codegangsta/cli"
|
|
"github.com/hashicorp/yamux"
|
|
"github.com/xtaci/kcp-go"
|
|
)
|
|
|
|
var VERSION = "SELFBUILD"
|
|
|
|
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: "mode",
|
|
Value: "fast",
|
|
Usage: "mode for communication: fast2, fast, normal, default",
|
|
},
|
|
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: 512,
|
|
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",
|
|
},
|
|
}
|
|
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)
|
|
log.Println("listening on:", listener.Addr())
|
|
|
|
START_KCP:
|
|
var mode kcp.Mode
|
|
switch c.String("mode") {
|
|
case "normal":
|
|
mode = kcp.MODE_NORMAL
|
|
case "default":
|
|
mode = kcp.MODE_DEFAULT
|
|
case "fast":
|
|
mode = kcp.MODE_FAST
|
|
case "fast2":
|
|
mode = kcp.MODE_FAST2
|
|
default:
|
|
log.Println("unrecognized mode:", c.String("mode"))
|
|
return
|
|
}
|
|
log.Println("communication mode:", c.String("mode"))
|
|
// kcp server
|
|
kcpconn, err := kcp.DialEncrypted(mode, c.Int("fec"), c.String("remoteaddr"), []byte(c.String("key")))
|
|
checkError(err)
|
|
kcpconn.SetRetries(50)
|
|
log.Println("remote address:", c.String("remoteaddr"))
|
|
kcpconn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
|
|
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
|
|
kcpconn.SetMtu(c.Int("mtu"))
|
|
log.Println("mtu:", c.Int("mtu"))
|
|
log.Println("fec:", c.Int("fec"))
|
|
|
|
// stream multiplex
|
|
var mux *yamux.Session
|
|
config := &yamux.Config{
|
|
AcceptBacklog: 256,
|
|
EnableKeepAlive: true,
|
|
KeepAliveInterval: 30 * time.Second,
|
|
ConnectionWriteTimeout: 10 * time.Second,
|
|
MaxStreamWindowSize: 16777216,
|
|
LogOutput: os.Stderr,
|
|
}
|
|
session, err := yamux.Client(kcpconn, config)
|
|
checkError(err)
|
|
mux = session
|
|
|
|
for {
|
|
p1, err := listener.AcceptTCP()
|
|
if err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
p2, err := mux.Open()
|
|
if err != nil { // yamux failure
|
|
log.Println(err)
|
|
kcpconn.Close()
|
|
p1.Close()
|
|
goto START_KCP
|
|
}
|
|
go handleClient(p1, p2)
|
|
}
|
|
}
|
|
myApp.Run(os.Args)
|
|
}
|