commit message

This commit is contained in:
xtaci
2016-05-14 15:54:59 +08:00
commit 8b61a91ef0
8 changed files with 607 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
client/client
server/server
+6
View File
@@ -0,0 +1,6 @@
FROM golang:latest
MAINTAINER xtaci <daniel820313@gmail.com>
RUN go get github.com/xtaci/kcptun/client
RUN go get github.com/xtaci/kcptun/server
EXPOSE 29900/udp
EXPOSE 12948
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Daniel Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+80
View File
@@ -0,0 +1,80 @@
# kcptun(KT)
TCP流转换为KCP+UDP流,>>>[下载地址](https://github.com/xtaci/kcptun/releases/latest)<<< 用于***高丢包***环境中的数据传输,工作示意图:
```
+---------------------------------------+
| |
| KCPTUN |
| |
+--------+ | +------------+ +------------+ | +--------+
| | | | | | | | | |
| Client | +--> | | KCP Client | +---> | KCP Server | | +--> | Server |
| | TCP | | | UDP | | | TCP | |
+--------+ | +------------+ +------------+ | +--------+
| |
| |
+---------------------------------------+
```
***kcptun是[kcp](https://github.com/skywind3000/kcp)协议的一个简单应用,可以用于任意tcp网络程序的传输承载,以提高软件网络流畅度(如浏览器,telnet等),降低掉线情况。***
<img src="kitty.jpg" style="width: 300px;"/>
### Docker
```
docker pull xtaci/kcptun
```
### 使用方法
```
D:\>client_windows_amd64.exe -h
NAME:
kcptun - kcptun client
USAGE:
client_windows_amd64.exe [global options] command [command options] [arguments...]
VERSION:
20160507
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--localaddr, -l ":12948" local listen addr:
--remoteaddr, -r "vps:29900" kcp server addr
--key "it's a secrect" key for communcation, must be the same as kcptun server [$KCPTUN_KEY]
--mode "fast" mode for communication: fast, normal, default
--tuncrypt enable tunnel encryption, adds extra secrecy for data transfer
--help, -h show help
--version, -v print the version
D:\>server_windows_amd64.exe -h
NAME:
kcptun - kcptun server
USAGE:
server_windows_amd64.exe [global options] command [command options] [arguments...]
VERSION:
20160507
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--listen, -l ":29900" kcp server listen addr:
--target, -t "127.0.0.1:12948" target server addr
--key "it's a secrect" key for communcation, must be the same as kcptun client [$KCPTUN_KEY]
--mode "fast" mode for communication: fast, normal, default
--tuncrypt enable tunnel encryption, adds extra secrecy for data transfer
--help, -h show help
--version, -v print the version
```
### 适用范围(包括但不限于):
1. 网络游戏的数据传输
2. 跨运营商的流量传输
3. 其他高丢包,高干扰通信环境的TCP数据传输
# 免责申明
用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。
对免责声明的解释、修改及更新权均属于作者本人所有。
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
VERSION=`date -u +%Y%m%d`
LDFLAGS="-X main.VERSION=$VERSION"
OSES=(linux darwin windows)
ARCHS=(amd64 386)
for os in ${OSES[@]}; do
for arch in ${ARCHS[@]}; do
suffix=""
if [ "$os" == "windows" ]
then
suffix=".exe"
fi
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
tar -zcf kcptun-${os}-${arch}-$VERSION.tar.gz client_${os}_${arch}${suffix} server_${os}_${arch}${suffix}
md5 kcptun-${os}-${arch}-$VERSION.tar.gz
done
done
# ARM
ARMS=(5 6 7)
for v in ${ARMS[@]}; do
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
done
tar -zcf kcptun-linux-arm-$VERSION.tar.gz client_linux_arm* server_linux_arm*
md5 kcptun-linux-arm-$VERSION.tar.gz
+218
View File
@@ -0,0 +1,218 @@
package main
import (
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
"crypto/sha256"
"io"
"log"
"math/rand"
"net"
"os"
"time"
"github.com/codegangsta/cli"
"github.com/hashicorp/yamux"
"github.com/xtaci/kcp-go"
)
var VERSION = "SELFBUILD"
type secureConn struct {
encoder cipher.Stream
decoder cipher.Stream
conn net.Conn
}
func newSecureConn(key string, conn net.Conn, iv []byte) *secureConn {
sc := new(secureConn)
sc.conn = conn
commkey := sha256.Sum256([]byte(key))
// encoder
block, err := aes.NewCipher(commkey[:])
if err != nil {
log.Println(err)
return nil
}
sc.encoder = cipher.NewCFBEncrypter(block, iv[:aes.BlockSize])
// decoder
block, err = aes.NewCipher(commkey[:])
if err != nil {
log.Println(err)
return nil
}
sc.decoder = cipher.NewCFBDecrypter(block, iv[aes.BlockSize:])
return sc
}
func (sc *secureConn) Read(p []byte) (n int, err error) {
n, err = sc.conn.Read(p)
if err == nil {
sc.decoder.XORKeyStream(p[:n], p[:n])
}
return
}
func (sc *secureConn) Write(p []byte) (n int, err error) {
sc.encoder.XORKeyStream(p, p)
return sc.conn.Write(p)
}
func (sc *secureConn) Close() (err error) {
return sc.conn.Close()
}
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 addr:",
},
cli.StringFlag{
Name: "remoteaddr, r",
Value: "vps:29900",
Usage: "kcp server addr",
},
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: fast, normal, default",
},
cli.BoolFlag{
Name: "tuncrypt",
Usage: "enable tunnel encryption, adds extra secrecy for data transfer",
},
cli.IntFlag{
Name: "mtu",
Value: 1400,
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)",
},
}
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
default:
log.Println("unrecognized mode:", c.String("mode"))
return
}
log.Println("communication mode:", c.String("mode"))
// kcp server
kcpconn, err := kcp.DialEncrypted(mode, 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"))
// generate & send iv
iv := make([]byte, 2*aes.BlockSize)
io.ReadFull(crand.Reader, iv)
_, err = kcpconn.Write(iv)
checkError(err)
// stream multiplex
var mux *yamux.Session
if c.Bool("tuncrypt") {
scon := newSecureConn(c.String("key"), kcpconn, iv)
session, err := yamux.Client(scon, nil)
checkError(err)
mux = session
} else {
session, err := yamux.Client(kcpconn, nil)
checkError(err)
mux = session
}
log.Println("tunnel encryption:", c.Bool("tuncrypt"))
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)
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+227
View File
@@ -0,0 +1,227 @@
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"io"
"log"
"math/rand"
"net"
"os"
"time"
"github.com/codegangsta/cli"
"github.com/hashicorp/yamux"
"github.com/xtaci/kcp-go"
)
var VERSION = "SELFBUILD"
type secureConn struct {
encoder cipher.Stream
decoder cipher.Stream
conn net.Conn
}
func newSecureConn(key string, conn net.Conn, iv []byte) *secureConn {
sc := new(secureConn)
sc.conn = conn
commkey := sha256.Sum256([]byte(key))
// encoder
block, err := aes.NewCipher(commkey[:])
if err != nil {
log.Println(err)
return nil
}
sc.encoder = cipher.NewCFBEncrypter(block, iv[aes.BlockSize:])
// decoder
block, err = aes.NewCipher(commkey[:])
if err != nil {
log.Println(err)
return nil
}
sc.decoder = cipher.NewCFBDecrypter(block, iv[:aes.BlockSize])
return sc
}
func (sc *secureConn) Read(p []byte) (n int, err error) {
n, err = sc.conn.Read(p)
if err == nil {
sc.decoder.XORKeyStream(p[:n], p[:n])
}
return
}
func (sc *secureConn) Write(p []byte) (n int, err error) {
sc.encoder.XORKeyStream(p, p)
return sc.conn.Write(p)
}
func (sc *secureConn) Close() (err error) {
return sc.conn.Close()
}
// handle multiplex-ed connection
func handleMux(conn *kcp.UDPSession, key, target string, tuncrypt bool, mtu, sndwnd, rcvwnd int) {
conn.SetRetries(50)
conn.SetWindowSize(1024, 1024)
conn.SetDeadline(time.Now().Add(2 * time.Second))
conn.SetMtu(mtu)
conn.SetWindowSize(sndwnd, rcvwnd)
// read iv
iv := make([]byte, 2*aes.BlockSize)
if _, err := io.ReadFull(conn, iv); err != nil {
log.Println(err)
conn.Close()
return
}
conn.SetDeadline(time.Time{})
// stream multiplex
var mux *yamux.Session
if tuncrypt {
scon := newSecureConn(key, conn, iv)
m, err := yamux.Server(scon, nil)
if err != nil {
log.Println(err)
return
}
mux = m
} else {
m, err := yamux.Server(conn, nil)
if err != nil {
log.Println(err)
return
}
mux = m
}
defer mux.Close()
for {
p1, err := mux.Accept()
if err != nil {
log.Println(err)
return
}
p2, err := net.Dial("tcp", target)
if err != nil {
log.Println(err)
return
}
go handleClient(p1, p2)
}
}
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 main() {
rand.Seed(int64(time.Now().Nanosecond()))
myApp := cli.NewApp()
myApp.Name = "kcptun"
myApp.Usage = "kcptun server"
myApp.Version = VERSION
myApp.Flags = []cli.Flag{
cli.StringFlag{
Name: "listen,l",
Value: ":29900",
Usage: "kcp server listen addr:",
},
cli.StringFlag{
Name: "target, t",
Value: "127.0.0.1:12948",
Usage: "target server addr",
},
cli.StringFlag{
Name: "key",
Value: "it's a secrect",
Usage: "key for communcation, must be the same as kcptun client",
EnvVar: "KCPTUN_KEY",
},
cli.StringFlag{
Name: "mode",
Value: "fast",
Usage: "mode for communication: fast, normal, default",
},
cli.BoolFlag{
Name: "tuncrypt",
Usage: "enable tunnel encryption, adds extra secrecy for data transfer",
},
cli.IntFlag{
Name: "mtu",
Value: 1400,
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
},
cli.IntFlag{
Name: "sndwnd",
Value: 1024,
Usage: "set send window size(num of packets)",
},
cli.IntFlag{
Name: "rcvwnd",
Value: 1024,
Usage: "set receive window size(num of packets)",
},
}
myApp.Action = func(c *cli.Context) {
log.Println("version:", VERSION)
// KCP listen
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
default:
log.Println("unrecognized mode:", c.String("mode"))
return
}
lis, err := kcp.ListenEncrypted(mode, c.String("listen"), []byte(c.String("key")))
if err != nil {
log.Fatal(err)
}
log.Println("listening on ", lis.Addr())
log.Println("communication mode:", c.String("mode"))
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
log.Println("mtu:", c.Int("mtu"))
log.Println("tunnel encryption:", c.Bool("tuncrypt"))
for {
if conn, err := lis.Accept(); err == nil {
log.Println("remote address:", conn.RemoteAddr())
go handleMux(conn, c.String("key"), c.String("target"), c.Bool("tuncrypt"), c.Int("mtu"), c.Int("sndwnd"), c.Int("rcvwnd"))
} else {
log.Println(err)
}
}
}
myApp.Run(os.Args)
}