Compare commits

...
5 Commits
Author SHA1 Message Date
xtaci 208deccd09 use rand port instead of sequential pattern 2022-10-15 11:07:47 +08:00
xtaci 2e5215a35d add multiport support on server 2022-10-08 18:03:33 +08:00
xtaci b9cb84efdf make parsing generic 2022-10-08 17:47:14 +08:00
xtaci f7a642525c upd test 2022-10-08 17:30:11 +08:00
xtaci 51e6fed5fc adding support for multiport dial 2022-10-08 13:02:18 +08:00
4 changed files with 149 additions and 16 deletions
+22 -3
View File
@@ -1,18 +1,37 @@
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
kcp "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/kcptun/generic"
"github.com/xtaci/tcpraw"
)
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
mp, err := generic.ParseMultiPort(config.RemoteAddr)
if err != nil {
return nil, err
}
var randport uint64
err = binary.Read(rand.Reader, binary.LittleEndian, &randport)
if err != nil {
return nil, err
}
remoteAddr := fmt.Sprintf("%v:%v", mp.Host, uint64(mp.MinPort)+randport%uint64(mp.MaxPort-mp.MinPort+1))
if config.TCP {
conn, err := tcpraw.Dial("tcp", config.RemoteAddr)
conn, err := tcpraw.Dial("tcp", remoteAddr)
if err != nil {
return nil, errors.Wrap(err, "tcpraw.Dial()")
}
return kcp.NewConn(config.RemoteAddr, block, config.DataShard, config.ParityShard, conn)
return kcp.NewConn(remoteAddr, block, config.DataShard, config.ParityShard, conn)
}
return kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
return kcp.DialWithOptions(remoteAddr, block, config.DataShard, config.ParityShard)
}
+50
View File
@@ -0,0 +1,50 @@
package generic
import (
"regexp"
"strconv"
"github.com/pkg/errors"
)
type MultiPort struct {
Host string
MinPort uint64
MaxPort uint64
}
// Parse mulitport listener or dialer
func ParseMultiPort(addr string) (*MultiPort, error) {
remoteAddrMatcher := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
matches := remoteAddrMatcher.FindStringSubmatch(addr)
if len(matches) >= 4 {
var minPort, maxPort int
minPort, err := strconv.Atoi(matches[2])
if err != nil {
return nil, err
}
maxPort = minPort
// multiport assignment
if matches[3] != "" {
maxPort, err = strconv.Atoi(matches[3])
if err != nil {
return nil, err
}
}
if (minPort > maxPort) || minPort > 65535 || maxPort > 65535 || minPort == 0 || maxPort == 0 {
return nil, errors.Errorf("invalid port range specified: minport:%v -> maxport %v", minPort, maxPort)
}
mp := new(MultiPort)
mp.Host = matches[1]
mp.MinPort = uint64(minPort)
mp.MaxPort = uint64(maxPort)
return mp, nil
}
return nil, errors.Errorf("malformed address:%v", addr)
}
+51
View File
@@ -0,0 +1,51 @@
package generic
import (
"fmt"
"regexp"
"strconv"
"testing"
)
func TestDial(t *testing.T) {
reg := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
matches := reg.FindStringSubmatch("www.unknown.unknown:20000-21000")
for i := 0; i < len(matches); i++ {
fmt.Println(matches[i])
}
minPort, err := strconv.Atoi(matches[2])
if err != nil {
t.Fatal(err)
}
maxPort, err := strconv.Atoi(matches[3])
if err != nil {
t.Fatal(err)
}
t.Log("minport:", minPort)
t.Log("maxport:", maxPort)
remoteAddr := fmt.Sprintf("%v:%v", matches[1], uint64(minPort)+1000%uint64(maxPort-minPort+1))
t.Log("RemoteAddr:", remoteAddr)
testcase2 := "1.2.3.4:20000"
matches = reg.FindStringSubmatch(testcase2)
for i := 0; i < len(matches); i++ {
t.Log(testcase2, "submatch", i, matches[i])
}
testcase3 := ":20000-20001"
matches = reg.FindStringSubmatch(testcase3)
for i := 0; i < len(matches); i++ {
t.Log(testcase3, "submatch", i, matches[i])
}
testcase4 := ":20000"
matches = reg.FindStringSubmatch(testcase4)
for i := 0; i < len(matches); i++ {
t.Log(testcase4, "submatch", i, matches[i])
}
}
+26 -13
View File
@@ -432,22 +432,35 @@ func main() {
}
}
if config.TCP { // tcp dual stack
if conn, err := tcpraw.Listen("tcp", config.Listen); err == nil {
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
checkError(err)
wg.Add(1)
go loop(lis)
} else {
log.Println(err)
mp, err := generic.ParseMultiPort(config.Listen)
if err != nil {
log.Println(err)
return err
}
// create multiple listener
for port := mp.MinPort; port <= mp.MaxPort; port++ {
listenAddr := fmt.Sprintf("%v:%v", mp.Host, port)
if config.TCP { // tcp dual stack
if conn, err := tcpraw.Listen("tcp", listenAddr); err == nil {
log.Printf("Listening on: %v/tcp", listenAddr)
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
checkError(err)
wg.Add(1)
go loop(lis)
} else {
log.Println(err)
}
}
// udp stack
log.Printf("Listening on: %v/udp", listenAddr)
lis, err := kcp.ListenWithOptions(listenAddr, block, config.DataShard, config.ParityShard)
checkError(err)
wg.Add(1)
go loop(lis)
}
// udp stack
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
checkError(err)
wg.Add(1)
go loop(lis)
wg.Wait()
return nil
}