add more nat func

master
兔子 5 months ago
parent 60a73eb0d8
commit 7d85b14d60

@ -41,7 +41,7 @@ import (
var cmdRoot = &cobra.Command{ var cmdRoot = &cobra.Command{
Use: "b612", Use: "b612",
Version: "2.1.0.beta.8", Version: "2.1.0.beta.10",
} }
func init() { func init() {

@ -29,6 +29,10 @@ var maxHop int
var disableIpInfo bool var disableIpInfo bool
var bindAddr string var bindAddr string
var hideIncorrect bool var hideIncorrect bool
var natt NatThroughs
var scanip ScanIP
var scanport ScanPort
func init() { func init() {
CmdNatPClient.Flags().StringVarP(&natc.ServiceTarget, "target", "t", "", "forward server target address") CmdNatPClient.Flags().StringVarP(&natc.ServiceTarget, "target", "t", "", "forward server target address")
@ -68,6 +72,36 @@ func init() {
CmdNatServer.Flags().StringVarP(&nattests.AltPort, "alt-port", "A", "46610", "备用端口") CmdNatServer.Flags().StringVarP(&nattests.AltPort, "alt-port", "A", "46610", "备用端口")
CmdNatServer.Flags().StringVarP(&nattests.LogPath, "log", "l", "", "日志文件") CmdNatServer.Flags().StringVarP(&nattests.LogPath, "log", "l", "", "日志文件")
Cmd.AddCommand(CmdNatServer) Cmd.AddCommand(CmdNatServer)
CmdNatThrough.Flags().StringVarP(&natt.STUN, "stun", "s", "turn.b612.me:3478", "stun服务器")
CmdNatThrough.Flags().StringVarP(&natt.Remote, "remote", "r", "baidu.com:80", "keepalive地址")
CmdNatThrough.Flags().IntVarP(&natt.KeepAlivePeriod, "keepalive-period", "p", 30, "KeepAlive周期")
CmdNatThrough.Flags().IntVarP(&natt.KeepAliveIdel, "keepalive-idel", "i", 30, "KeepAlive空闲时间")
CmdNatThrough.Flags().IntVarP(&natt.KeepAliveCount, "keepalive-count", "c", 5, "KeepAlive次数")
CmdNatThrough.Flags().BoolVarP(&natt.AutoUPnP, "auto-upnp", "u", true, "自动UPnP")
CmdNatThrough.Flags().IntVarP(&natt.WebPort, "web-port", "w", 8080, "web端口")
CmdNatThrough.Flags().IntVarP(&natt.HealthCheckInterval, "health-check-interval", "H", 30, "健康检查间隔")
Cmd.AddCommand(CmdNatThrough)
CmdScanIP.Flags().StringVarP(&scanip.Host, "ip", "i", "", "扫描IP地址")
CmdScanIP.Flags().IntVarP(&scanip.Port, "port", "p", 80, "TCP模式扫描端口")
CmdScanIP.Flags().IntVarP(&scanip.Timeout, "timeout", "t", 2000, "超时时间")
CmdScanIP.Flags().IntVarP(&scanip.Threads, "threads", "m", 100, "最大线程数")
CmdScanIP.Flags().StringVarP(&scanip.Log, "log", "l", "", "日志文件地址")
CmdScanIP.Flags().StringVarP(&scanip.Mask, "mask", "M", "", "掩码")
CmdScanIP.Flags().IntVarP(&scanip.CIDR, "cidr", "c", 24, "CIDR")
CmdScanIP.Flags().StringVarP(&scanip.ScanType, "type", "T", "icmp", "扫描类型")
CmdScanIP.Flags().IntVarP(&scanip.Retry, "retry", "r", 2, "重试次数")
CmdScanIP.Flags().BoolVarP(&scanip.WithHostname, "with-hostname", "H", false, "显示主机名")
Cmd.AddCommand(CmdScanIP)
CmdScanPort.Flags().StringVarP(&scanport.Host, "ip", "i", "", "扫描IP地址")
CmdScanPort.Flags().IntVarP(&scanport.Timeout, "timeout", "t", 2000, "超时时间")
CmdScanPort.Flags().IntVarP(&scanport.Threads, "threads", "m", 100, "最大线程数")
CmdScanPort.Flags().StringVarP(&scanport.Log, "log", "l", "", "日志文件地址")
CmdScanPort.Flags().IntVarP(&scanport.Retry, "retry", "r", 2, "重试次数")
Cmd.AddCommand(CmdScanPort)
} }
var CmdNatPClient = &cobra.Command{ var CmdNatPClient = &cobra.Command{
@ -147,3 +181,56 @@ var CmdNetTrace = &cobra.Command{
} }
}, },
} }
var CmdNatThrough = &cobra.Command{
Use: "natt",
Short: "nat tcp直接穿透",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
starlog.Errorf("请按照如下格式输入:\n [远端地址] 或 [本地地址|远端地址] 或 [名字@本地地址|远端地址] 或 [协议|名字|本地地址|远端地址]\n")
return
}
if err := natt.Parse(args); err != nil {
starlog.Errorln(err)
return
}
if err := natt.Run(); err != nil {
starlog.Errorln(err)
}
},
}
var CmdScanIP = &cobra.Command{
Use: "scanip",
Short: "扫描IP",
Run: func(cmd *cobra.Command, args []string) {
if scanip.Host == "" {
cmd.Help()
return
}
if scanip.ScanType == "icmp" {
scanip.ICMP()
} else {
scanip.TCP(scanip.Port)
}
},
}
var CmdScanPort = &cobra.Command{
Use: "scanport",
Short: "扫描端口",
Run: func(cmd *cobra.Command, args []string) {
if scanport.Host == "" {
cmd.Help()
return
}
if len(args) != 1 {
starlog.Errorln("请指定端口范围,如80,443,1000-2000")
}
if err := scanport.Parse(args[0]); err != nil {
starlog.Errorln(err)
return
}
scanport.Run()
},
}

@ -0,0 +1,642 @@
package net
import (
"b612.me/apps/b612/netforward"
"b612.me/starlog"
"context"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/huin/goupnp/dcps/internetgateway2"
"math/rand"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
type NatThroughs struct {
Lists []*NatThrough
WebPort int
AutoUPnP bool
KeepAlivePeriod int
KeepAliveIdel int
KeepAliveCount int
STUN string
Remote string
HealthCheckInterval int
}
func (n *NatThroughs) Close() {
for _, v := range n.Lists {
v.Close()
}
}
func (n *NatThroughs) Parse(reqs []string) error {
if n.KeepAlivePeriod == 0 {
n.KeepAlivePeriod = 10
}
if n.KeepAliveIdel == 0 {
n.KeepAliveIdel = 30
}
if n.KeepAliveCount == 0 {
n.KeepAliveCount = 5
}
if n.STUN == "" {
n.STUN = "turn.b612.me:3478"
}
for _, v := range reqs {
var req = NatThrough{
Forward: netforward.NetForward{
LocalAddr: "0.0.0.0",
DialTimeout: 3000,
UDPTimeout: 20000,
KeepAlivePeriod: n.KeepAlivePeriod,
KeepAliveIdel: n.KeepAliveIdel,
KeepAliveCount: n.KeepAliveCount,
UsingKeepAlive: true,
EnableTCP: true,
},
Type: "tcp",
STUN: n.STUN,
Remote: n.Remote,
KeepAlivePeriod: n.KeepAlivePeriod,
KeepAliveIdel: n.KeepAliveIdel,
KeepAliveCount: n.KeepAliveCount,
AutoUPnP: n.AutoUPnP,
HealthCheckInterval: n.HealthCheckInterval,
}
strs := strings.Split(v, ",")
switch len(strs) {
case 1:
req.Type = "tcp"
req.Forward.RemoteURI = strs[0]
case 2:
ipport := strings.Split(strs[0], ":")
if len(ipport) == 1 {
port, err := strconv.Atoi(ipport[0])
if err != nil {
return err
}
req.Forward.LocalPort = port
} else {
req.Forward.LocalAddr = ipport[0]
port, err := strconv.Atoi(ipport[1])
if err != nil {
return err
}
req.Forward.LocalPort = port
}
req.Type = "tcp"
req.Forward.RemoteURI = strs[1]
case 3:
ipport := strings.Split(strs[1], ":")
if len(ipport) == 1 {
port, err := strconv.Atoi(ipport[0])
if err != nil {
return err
}
req.Forward.LocalPort = port
} else {
req.Forward.LocalAddr = ipport[0]
port, err := strconv.Atoi(ipport[1])
if err != nil {
return err
}
req.Forward.LocalPort = port
}
req.Type = "tcp"
req.Forward.RemoteURI = strs[2]
req.Name = strs[0]
case 4:
ipport := strings.Split(strs[2], ":")
if len(ipport) == 1 {
port, err := strconv.Atoi(ipport[0])
if err != nil {
return err
}
req.Forward.LocalPort = port
} else {
req.Forward.LocalAddr = ipport[0]
port, err := strconv.Atoi(ipport[1])
if err != nil {
return err
}
req.Forward.LocalPort = port
}
req.Type = strings.ToLower(strs[0])
req.Forward.RemoteURI = strs[3]
req.Name = strs[1]
}
n.Lists = append(n.Lists, &req)
}
return nil
}
func (n *NatThroughs) Run() error {
go n.WebService()
wg := sync.WaitGroup{}
for _, v := range n.Lists {
wg.Add(1)
go func(v *NatThrough) {
defer wg.Done()
if err := v.Run(); err != nil {
starlog.Errorf("Failed to run forward: %v\n", err)
}
v.HealthCheck()
}(v)
}
wg.Wait()
return nil
}
type nattinfo struct {
Id int `json:"id"`
Name string `json:"name"`
Ext string `json:"ext"`
Local string `json:"local"`
Forward string `json:"forward"`
}
func (n *NatThroughs) WebService() error {
if n.WebPort == 0 {
return nil
}
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", n.WebPort))
if err != nil {
return err
}
starlog.Infof("Web service listen on %d\n", n.WebPort)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var str string
for k, v := range n.Lists {
str += fmt.Sprintf("id:%d name:%s : %s <----> %s <-----> %s\n", k, v.Name, v.ExtUrl, v.localipport, v.Forward.RemoteURI)
}
w.Write([]byte(str))
})
http.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
var res []nattinfo
for k, v := range n.Lists {
res = append(res, nattinfo{
Id: k,
Name: v.Name,
Ext: v.ExtUrl,
Local: v.localipport,
Forward: v.Forward.RemoteURI,
})
}
w.Header().Set("Content-Type", "application/json")
data, _ := json.Marshal(res)
w.Write(data)
return
})
http.HandleFunc("/jump", func(w http.ResponseWriter, r *http.Request) {
types := "https://"
name := r.URL.Query().Get("name")
if name == "" {
w.Write([]byte("id is empty"))
return
}
if r.URL.Query().Get("type") == "http" {
types = "http://"
}
for _, v := range n.Lists {
if v.Name == name {
http.Redirect(w, r, types+v.ExtUrl, http.StatusFound)
return
}
}
})
return http.Serve(listener, nil)
}
// NatThrough 类似于natter.py 是一个用于Full Cone NAT直接穿透的工具
type NatThrough struct {
Name string
OriginLocalPort int
Forward netforward.NetForward
Type string
STUN string
Remote string
KeepAlivePeriod int
KeepAliveIdel int
KeepAliveCount int
AutoUPnP bool
isOk bool
ExtUrl string
localipport string
keepaliveConn net.Conn
HealthCheckInterval int
stopFn context.CancelFunc
stopCtx context.Context
}
func (n *NatThrough) Close() {
n.stopFn()
n.Forward.Close()
}
func (c *NatThrough) Run() error {
c.isOk = false
c.stopCtx, c.stopFn = context.WithCancel(context.Background())
c.OriginLocalPort = c.Forward.LocalPort
if c.Forward.LocalPort == 0 {
listener, err := net.Listen(c.Type, c.Forward.LocalAddr+":0")
if err != nil {
return fmt.Errorf("Failed to listen on %s: %v", c.Forward.LocalAddr, err)
}
if c.Type == "tcp" {
c.Forward.LocalPort = listener.Addr().(*net.TCPAddr).Port
} else {
c.Forward.LocalPort = listener.Addr().(*net.UDPAddr).Port
}
listener.Close()
}
if c.Type == "tcp" {
c.Forward.EnableTCP = true
c.Forward.EnableUDP = false
} else {
c.Forward.EnableTCP = false
c.Forward.EnableUDP = true
}
starlog.Infof("Local Port: %d\n", c.Forward.LocalPort)
starlog.Infof("Keepalive To: %s\n", c.Remote)
starlog.Infof("Forward To: %s\n", c.Forward.RemoteURI)
innerIp, extIp, err := c.GetIPPortFromSTUN(c.Type, c.Forward.LocalAddr, c.Forward.LocalPort, c.STUN)
if err != nil {
return fmt.Errorf("Failed to get external IP and port: %v", err)
}
starlog.Infof("Internal Addr: %s \n", innerIp.String())
starlog.Infof("External Addr: %s \n", extIp.String())
getIP := func(ip net.Addr) string {
switch ip.(type) {
case *net.TCPAddr:
return ip.(*net.TCPAddr).IP.String()
case *net.UDPAddr:
return ip.(*net.UDPAddr).IP.String()
default:
return ""
}
}
getPort := func(ip net.Addr) int {
switch ip.(type) {
case *net.TCPAddr:
return ip.(*net.TCPAddr).Port
case *net.UDPAddr:
return ip.(*net.UDPAddr).Port
default:
return 0
}
}
go func() {
if err := c.KeepAlive(c.Forward.LocalAddr, c.Forward.LocalPort); err != nil {
starlog.Errorf("Failed to run forward: %v\n", err)
c.Forward.Close()
c.stopFn()
}
}()
innerIp, extIp, err = c.GetIPPortFromSTUN(c.Type, c.Forward.LocalAddr, c.Forward.LocalPort, c.STUN)
if err != nil {
return fmt.Errorf("Failed to get external IP and port: %v", err)
}
starlog.Infof("Retest Internal Addr: %s \n", innerIp.String())
starlog.Infof("Retest External Addr: %s \n", extIp.String())
if c.AutoUPnP {
go c.HandleUPnP(getIP(innerIp), uint16(getPort(extIp)))
}
err = c.Forward.Run()
if err != nil {
return fmt.Errorf("Failed to run forward: %v", err)
}
c.isOk = true
c.localipport = fmt.Sprintf("%s:%d", getIP(innerIp), getPort(innerIp))
c.ExtUrl = fmt.Sprintf("%s:%d", getIP(extIp), getPort(extIp))
return nil
}
func (c *NatThrough) HealthCheck() {
count := 0
if c.HealthCheckInterval == 0 {
c.HealthCheckInterval = 30
}
for {
select {
case <-c.stopCtx.Done():
return
case <-time.After(time.Second * time.Duration(c.HealthCheckInterval)):
}
if c.Type == "udp" {
continue
}
conn, err := net.DialTimeout("tcp", c.ExtUrl, time.Second*2)
if err != nil {
starlog.Warningf("Health Check Fail: %v\n", err)
count++
} else {
starlog.Infof("Health Check Ok\n")
conn.(*net.TCPConn).SetLinger(0)
conn.Close()
}
if count >= 3 {
count = 0
starlog.Errorf("Failed to connect to remote, close connection retrying\n")
c.stopFn()
c.keepaliveConn.Close()
c.Forward.Close()
forward := netforward.NetForward{
LocalAddr: c.Forward.LocalAddr,
LocalPort: c.OriginLocalPort,
RemoteURI: c.Forward.RemoteURI,
KeepAlivePeriod: c.KeepAlivePeriod,
KeepAliveIdel: c.KeepAliveIdel,
KeepAliveCount: c.KeepAliveCount,
UsingKeepAlive: true,
}
time.Sleep(time.Second * 22)
c.Forward = forward
c.Run()
}
}
}
func (c *NatThrough) KeepAlive(localAddr string, localPort int) error {
for {
select {
case <-c.stopCtx.Done():
return nil
default:
}
if c.Type == "tcp" {
dialer := net.Dialer{
Control: netforward.ControlSetReUseAddr,
LocalAddr: &net.TCPAddr{IP: net.ParseIP(localAddr), Port: localPort},
}
conn, err := dialer.Dial("tcp", c.Remote)
if err != nil {
starlog.Errorf("Failed to dial remote: %v\n", err)
time.Sleep(time.Second * 5)
continue
}
c.keepaliveConn = conn
conn.(*net.TCPConn).SetLinger(0)
netforward.SetTcpInfo(conn.(*net.TCPConn), true, c.KeepAliveIdel, c.KeepAlivePeriod, c.KeepAliveCount, 0)
starlog.Infof("Keepalive local:%s remote: %s\n", conn.LocalAddr().String(), conn.RemoteAddr().String())
go func() {
for {
str := fmt.Sprintf("HEAD /keep-alive HTTP/1.1\r\n"+
"Host: %s\r\n"+
"User-Agent: curl/8.0.0 (B612)\r\n"+
"Accept: */*\r\n"+
"Connection: keep-alive\r\n\r\n", strings.Split(c.Remote, ":")[0])
//fmt.Println(str)
if _, err = conn.Write([]byte(str)); err != nil {
fmt.Println(err)
}
time.Sleep(time.Second * 20)
}
}()
for {
_, err := conn.Read(make([]byte, 4096))
if err != nil {
starlog.Warningf("Failed to keepalive remote: %v\n", err)
conn.Close()
break
}
}
} else if c.Type == "udp" {
rmtUdpAddr, err := net.ResolveUDPAddr("udp", c.Remote)
if err != nil {
return err
}
conn, err := net.DialUDP("udp", &net.UDPAddr{IP: net.ParseIP(localAddr), Port: localPort}, rmtUdpAddr)
if err != nil {
starlog.Errorf("Failed to dial remote: %v\n", err)
time.Sleep(time.Second * 5)
continue
}
c.keepaliveConn = conn
for {
_, err = conn.Write([]byte("b612 tcp nat through"))
if err != nil {
conn.Close()
starlog.Warningf("Failed to keepalive remote: %v\n", err)
break
}
time.Sleep(time.Second * 30)
}
}
}
}
func (c *NatThrough) HandleUPnP(localaddr string, extPort uint16) {
for {
select {
case <-c.stopCtx.Done():
return
default:
}
client, err := c.FoundUsableUPnP()
if err != nil {
starlog.Errorf("Failed to find UPnP device: %v\n", err)
time.Sleep(time.Second * 20)
continue
}
starlog.Infof("Found UPnP device!\n")
_, _, _, _, _, err = client.GetSpecificPortMappingEntry("", uint16(c.Forward.LocalPort), "TCP")
if err == nil {
starlog.Infof("Port mapping Ok\n")
time.Sleep(time.Second * 20)
continue
}
err = client.AddPortMapping("", uint16(c.Forward.LocalPort), strings.ToUpper(c.Type), uint16(c.Forward.LocalPort), localaddr, true, "B612 TCP Nat PassThrough", 75)
if err != nil {
starlog.Errorf("Failed to add port mapping: %v\n", err)
time.Sleep(time.Second * 20)
continue
}
starlog.Infof("Port mapping added:externalPort %d,localAddr %s,localPort %d\n", extPort, localaddr, c.Forward.LocalPort)
time.Sleep(time.Second * 20)
}
}
func (c *NatThrough) GetIPPortFromSTUN(netType string, localip string, localPort int, stunServer string) (net.Addr, net.Addr, error) {
// 替换为你的 TURN 服务器地址
stunAddr, err := net.ResolveUDPAddr("udp", stunServer)
if err != nil {
return nil, nil, fmt.Errorf("failed to resolve STUN server address: %v", err)
}
var conn net.Conn
if netType == "tcp" {
dialer := net.Dialer{
Control: netforward.ControlSetReUseAddr,
LocalAddr: &net.TCPAddr{IP: net.ParseIP(localip), Port: localPort},
}
conn, err = dialer.Dial("tcp", stunAddr.String())
if err != nil {
return nil, nil, err
}
conn.(*net.TCPConn).SetLinger(0)
}
if netType == "udp" {
conn, err = net.DialUDP(netType, &net.UDPAddr{IP: net.ParseIP(localip), Port: localPort}, stunAddr)
if err != nil {
return nil, nil, fmt.Errorf("failed to connect to STUN server: %v", err)
}
}
defer conn.Close()
innerAddr := conn.LocalAddr()
// Create STUN request
transactionID := make([]byte, 12)
rand.Read(transactionID)
stunRequest := make([]byte, 20)
binary.BigEndian.PutUint16(stunRequest[0:], 0x0001) // Message Type: Binding Request
binary.BigEndian.PutUint16(stunRequest[2:], 0x0000) // Message Length
copy(stunRequest[4:], []byte{0x21, 0x12, 0xa4, 0x42}) // Magic Cookie
copy(stunRequest[8:], transactionID) // Transaction ID
_, err = conn.Write(stunRequest)
if err != nil {
return nil, nil, fmt.Errorf("failed to send STUN request: %v", err)
}
buf := make([]byte, 1500)
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
n, err := conn.Read(buf)
if err != nil {
return nil, nil, fmt.Errorf("failed to receive STUN response: %v", err)
}
// Parse STUN response
if n < 20 {
return nil, nil, fmt.Errorf("invalid STUN response")
}
payload := buf[20:n]
var ip uint32
var port uint16
for len(payload) > 0 {
attrType := binary.BigEndian.Uint16(payload[0:])
attrLen := binary.BigEndian.Uint16(payload[2:])
if len(payload) < int(4+attrLen) {
return nil, nil, fmt.Errorf("invalid STUN attribute length")
}
if attrType == 0x0001 || attrType == 0x0020 {
port = binary.BigEndian.Uint16(payload[6:])
ip = binary.BigEndian.Uint32(payload[8:])
if attrType == 0x0020 {
port ^= 0x2112
ip ^= 0x2112a442
}
break
}
payload = payload[4+attrLen:]
}
if ip == 0 || port == 0 {
return nil, nil, fmt.Errorf("invalid STUN response")
}
outerAddr := &net.UDPAddr{
IP: net.IPv4(byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip)),
Port: int(port),
}
return innerAddr, outerAddr, nil
}
func (c *NatThrough) GetMyOutIP() string {
tmp, err := net.Dial("udp", "8.8.8.8:53")
if err != nil {
return ""
}
return tmp.LocalAddr().(*net.UDPAddr).IP.String()
}
func (c *NatThrough) FoundUsableUPnP() (RouterClient, error) {
wg := sync.WaitGroup{}
found := false
result := make(chan RouterClient, 3)
defer close(result)
wg.Add(3)
go func() {
defer wg.Done()
clients, errors, err := internetgateway2.NewWANIPConnection2Clients()
if err != nil {
return
}
if len(errors) > 0 {
return
}
if len(clients) == 0 {
return
}
starlog.Infof("Found WANIPConnection2 clients:%s\n", clients[0].Location.String())
found = true
result <- clients[0]
}()
go func() {
defer wg.Done()
clients, errors, err := internetgateway2.NewWANIPConnection1Clients()
if err != nil {
return
}
if len(errors) > 0 {
return
}
if len(clients) == 0 {
return
}
starlog.Infof("Found WANIPConnection1 clients:%s\n", clients[0].Location.String())
found = true
result <- clients[0]
}()
go func() {
defer wg.Done()
clients, errors, err := internetgateway2.NewWANPPPConnection1Clients()
if err != nil {
return
}
if len(errors) > 0 {
return
}
if len(clients) == 0 {
return
}
starlog.Infof("Found WANPPPConnection1 clients:%s\n", clients[0].Location.String())
found = true
result <- clients[0]
}()
wg.Wait()
if found {
return <-result, nil
}
return nil, fmt.Errorf("no UPnP devices discovered")
}
type RouterClient interface {
AddPortMapping(
NewRemoteHost string,
NewExternalPort uint16,
NewProtocol string,
NewInternalPort uint16,
NewInternalClient string,
NewEnabled bool,
NewPortMappingDescription string,
NewLeaseDuration uint32,
) (err error)
GetExternalIPAddress() (
NewExternalIPAddress string,
err error,
)
DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error)
GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error)
}

@ -0,0 +1,50 @@
package net
import (
"b612.me/apps/b612/netforward"
"fmt"
"testing"
"time"
)
func TestNathrough(t *testing.T) {
var n = NatThrough{
Forward: netforward.NetForward{
LocalAddr: "0.0.0.0",
LocalPort: 0,
RemoteURI: "127.0.0.1:88",
EnableTCP: true,
EnableUDP: false,
DelayMilSec: 0,
DelayToward: 0,
StdinMode: false,
IgnoreEof: false,
DialTimeout: 3000,
UDPTimeout: 3000,
KeepAlivePeriod: 30,
KeepAliveIdel: 30,
KeepAliveCount: 5,
UserTimeout: 0,
UsingKeepAlive: true,
},
Type: "tcp",
STUN: "turn.b612.me:3478",
Remote: "baidu.com:80",
KeepAlivePeriod: 3000,
KeepAliveIdel: 3000,
KeepAliveCount: 5,
AutoUPnP: true,
stopFn: nil,
stopCtx: nil,
}
go func() {
time.Sleep(time.Second * 10)
fmt.Println(n.ExtUrl)
}()
if err := n.Run(); err != nil {
fmt.Println(err)
t.Error(err)
}
n.HealthCheck()
time.Sleep(time.Second * 5)
}

@ -0,0 +1,33 @@
package net
import (
"testing"
)
func TestScan(t *testing.T) {
s := ScanPort{
Host: "192.168.2.109",
Timeout: 2000,
Threads: 5000,
}
if err := s.Parse("1-65535"); err != nil {
t.Error(err)
}
if err := s.Run(); err != nil {
t.Error(err)
}
}
func TestScanIP(t *testing.T) {
s := ScanIP{
Host: "192.168.2.1",
CIDR: 23,
Timeout: 2000,
Threads: 5000,
ScanType: "icmp",
WithHostname: true,
}
if err := s.ICMP(); err != nil {
t.Error(err)
}
}

@ -0,0 +1,279 @@
package net
import (
"b612.me/apps/b612/netforward"
"b612.me/stario"
"b612.me/starlog"
"b612.me/starnet"
"fmt"
"math"
"net"
"sync/atomic"
"time"
)
type ScanIP struct {
Host string
CIDR int
Port int
Mask string
Threads int
Timeout int
ScanType string
ipNet *net.IPNet
Log string
Retry int
WithHostname bool
}
func (s *ScanIP) Parse() error {
if s.CIDR == 0 && s.Mask == "" {
return fmt.Errorf("CIDR or Mask must be set")
}
if s.CIDR != 0 {
return nil
}
//mask to cidr
ipMask := net.IPMask(net.ParseIP(s.Mask).To4())
if ipMask == nil {
return fmt.Errorf("invalid mask")
}
s.CIDR, _ = ipMask.Size()
return nil
}
func (s *ScanIP) nextIP(ipStr string) (net.IP, error) {
var err error
if s.ipNet == nil {
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
if err != nil {
return nil, fmt.Errorf("invalid CIDR: %v", err)
}
}
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("invalid IP: %v", ipStr)
}
// Convert IP to 4-byte representation
ip = ip.To4()
if ip == nil {
return nil, fmt.Errorf("non-IPv4 address: %v", ipStr)
}
// Increment IP
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 {
break
}
}
// Check if incremented IP is still in range
if !s.ipNet.Contains(ip) {
return nil, nil
}
return ip, nil
}
func (s *ScanIP) NetSize() (int, error) {
var err error
if s.ipNet == nil {
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
if err != nil {
return 0, fmt.Errorf("invalid CIDR: %v", err)
}
}
maskSize, _ := s.ipNet.Mask.Size()
return int(math.Pow(2, float64(32-maskSize))) - 2, nil
}
func (s *ScanIP) FirstLastIP() (net.IP, net.IP, error) {
var err error
if s.ipNet == nil {
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
if err != nil {
return nil, nil, fmt.Errorf("invalid CIDR: %v", err)
}
}
firstIP := s.ipNet.IP.Mask(s.ipNet.Mask)
lastIP := make(net.IP, len(firstIP))
copy(lastIP, firstIP)
for i := range firstIP {
lastIP[i] = firstIP[i] | ^s.ipNet.Mask[i]
}
return firstIP, lastIP, nil
}
func (s *ScanIP) ICMP() error {
if s.ScanType != "icmp" {
return fmt.Errorf("scan type must be icmp")
}
if err := s.Parse(); err != nil {
return err
}
if s.Log != "" {
starlog.SetLogFile(s.Log, starlog.Std, true)
}
firstIP, lastIP, err := s.FirstLastIP()
if err != nil {
return err
}
ns, _ := s.NetSize()
starlog.Infof("Scan %s/%d\n", s.Host, s.CIDR)
starlog.Infof("Scan %s-%s\n", firstIP.String(), lastIP.String())
starlog.Infof("There are %d hosts\n", ns)
starlog.Infof("Threads: %d\n", s.Threads)
wg := stario.NewWaitGroup(s.Threads)
count := int32(0)
allcount := int32(0)
interrupt := make(chan [2]string)
go func() {
for {
select {
case <-time.After(time.Second * 2):
fmt.Printf("scan %d ips, %d up\r", allcount, count)
case ip, opened := <-interrupt:
if !opened {
return
}
if s.WithHostname {
starlog.Infof("Host %v is up, Name:%v\n", ip[0], ip[1])
} else {
starlog.Infof("Host %v is up\n", ip[0])
}
}
}
}()
idx := 0
for {
ip := firstIP.String()
if ip == lastIP.String() {
break
}
idx++
wg.Add(1)
go func(ip string, idx int) {
defer func() {
atomic.AddInt32(&allcount, 1)
}()
defer wg.Done()
for i := 0; i < s.Retry+1; i++ {
_, err := starnet.Ping(ip, idx, time.Duration(s.Timeout)*time.Millisecond)
if err == nil {
atomic.AddInt32(&count, 1)
if s.WithHostname {
hostname, err := net.LookupAddr(ip)
if err == nil {
interrupt <- [2]string{ip, hostname[0]}
return
}
}
interrupt <- [2]string{ip, ""}
return
}
}
}(ip, idx)
firstIP, _ = s.nextIP(ip)
}
wg.Wait()
close(interrupt)
starlog.Infof("scan %d ips, %d up\n", ns, count)
return nil
}
func (s *ScanIP) TCP(port int) error {
if s.ScanType != "tcp" {
return fmt.Errorf("scan type must be tcp")
}
if err := s.Parse(); err != nil {
return err
}
if s.Log != "" {
starlog.SetLogFile(s.Log, starlog.Std, true)
}
firstIP, lastIP, err := s.FirstLastIP()
if err != nil {
return err
}
ns, _ := s.NetSize()
starlog.Infof("Scan %s/%d\n", s.Host, s.CIDR)
starlog.Infof("Scan %s-%s\n", firstIP.String(), lastIP.String())
starlog.Infof("There are %d hosts\n", ns)
starlog.Infof("Threads: %d\n", s.Threads)
wg := stario.NewWaitGroup(s.Threads)
count := int32(0)
allcount := int32(0)
interrupt := make(chan [2]string)
go func() {
for {
select {
case <-time.After(time.Second * 2):
fmt.Printf("scan %d ips, %d up\r", allcount, count)
case ip, opened := <-interrupt:
if !opened {
return
}
if s.WithHostname {
starlog.Infof("Host %v is up, Name:%v\n", ip[0], ip[1])
} else {
starlog.Infof("Host %v is up\n", ip[0])
}
}
}
}()
idx := 0
localAddr, err := net.ResolveTCPAddr("tcp", ":0")
if err != nil {
starlog.Errorln("ResolveTCPAddr error, ", err)
return err
}
for {
ip := firstIP.String()
if ip == lastIP.String() {
break
}
idx++
wg.Add(1)
go func(ip string, idx int) {
defer func() {
atomic.AddInt32(&allcount, 1)
}()
defer wg.Done()
for i := 0; i < s.Retry+1; i++ {
dialer := net.Dialer{
LocalAddr: localAddr,
Timeout: time.Duration(s.Timeout) * time.Millisecond,
Control: netforward.ControlSetReUseAddr,
}
_, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port))
if err == nil {
atomic.AddInt32(&count, 1)
if s.WithHostname {
hostname, err := net.LookupAddr(ip)
if err == nil {
interrupt <- [2]string{ip, hostname[0]}
return
}
}
interrupt <- [2]string{ip, ""}
return
}
}
}(ip, idx)
firstIP, _ = s.nextIP(ip)
}
wg.Wait()
close(interrupt)
starlog.Infof("scan %d ips, %d up\n", ns, count)
return nil
}

@ -0,0 +1,131 @@
package net
import (
"b612.me/apps/b612/netforward"
"b612.me/stario"
"b612.me/starlog"
"fmt"
"net"
"sort"
"strconv"
"strings"
"sync/atomic"
"time"
)
type ScanPort struct {
Host string
Ports []int
Timeout int
Threads int
Log string
Retry int
}
func (s *ScanPort) Parse(potStr string) error {
ports := strings.Split(potStr, ",")
for _, port := range ports {
port = strings.TrimSpace(port)
if strings.Contains(port, "-") {
// range
r := strings.Split(port, "-")
if len(r) != 2 {
continue
}
start, err := strconv.Atoi(r[0])
if err != nil {
starlog.Warningf("invalid port: %s\n", r[0])
continue
}
end, err := strconv.Atoi(r[1])
if err != nil {
starlog.Warningf("invalid port: %s\n", r[1])
continue
}
for i := start; i <= end; i++ {
if i < 1 || i > 65535 {
starlog.Warningf("invalid port: %d\n", i)
continue
}
s.Ports = append(s.Ports, i)
}
} else {
// single port
tmp, err := strconv.Atoi(port)
if err != nil {
starlog.Warningf("invalid port: %s\n", port)
continue
}
if tmp < 1 || tmp > 65535 {
starlog.Warningf("invalid port: %d\n", tmp)
continue
}
s.Ports = append(s.Ports, tmp)
}
}
return nil
}
func (s *ScanPort) Run() error {
if s.Threads < 1 {
s.Threads = 1
}
if s.Log != "" {
starlog.SetLogFile(s.Log, starlog.Std, true)
}
sort.Ints(s.Ports)
starlog.Infof("scan count %d ports for host %v\n", len(s.Ports), s.Host)
wg := stario.NewWaitGroup(s.Threads)
localAddr, err := net.ResolveTCPAddr("tcp", ":0")
if err != nil {
starlog.Errorln("ResolveTCPAddr error, ", err)
return err
}
count := int32(0)
allcount := int32(0)
interrupt := make(chan int)
go func() {
for {
select {
case <-time.After(time.Second * 2):
fmt.Printf("scan %d ports, %d open\r", atomic.LoadInt32(&allcount), count)
case port, opened := <-interrupt:
if !opened {
return
}
starlog.Infof("port %d is open\n", port)
}
}
}()
for _, port := range s.Ports {
wg.Add(1)
go func(port int) {
defer wg.Done()
defer func() {
atomic.AddInt32(&allcount, 1)
}()
for i := 0; i < s.Retry+1; i++ {
dialer := net.Dialer{
LocalAddr: localAddr,
Timeout: time.Duration(s.Timeout) * time.Millisecond,
Control: netforward.ControlSetReUseAddr,
}
conn, err := dialer.Dial("tcp", net.JoinHostPort(s.Host, strconv.Itoa(port)))
if err != nil {
continue
}
conn.(*net.TCPConn).SetLinger(0)
conn.Close()
interrupt <- port
atomic.AddInt32(&count, 1)
return
}
}(port)
}
wg.Wait()
close(interrupt)
starlog.Infof("scan %d ports, %d open\n", len(s.Ports), count)
return nil
}

@ -198,7 +198,7 @@ func (s *TcpServer) Run() error {
starlog.Errorln("AcceptTCP error:", err) starlog.Errorln("AcceptTCP error:", err)
continue continue
} }
starlog.Infof("Accept new connection from %s", conn.RemoteAddr().String()) starlog.Infof("Accept new connection from %s\n", conn.RemoteAddr().String())
s.Lock() s.Lock()
s.Clients[conn.RemoteAddr().String()] = s.getTcpConn(conn) s.Clients[conn.RemoteAddr().String()] = s.getTcpConn(conn)
s.Unlock() s.Unlock()

@ -12,6 +12,7 @@ import (
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall"
"time" "time"
) )
@ -127,7 +128,12 @@ func (n *NetForward) Run() error {
func (n *NetForward) runTCP() error { func (n *NetForward) runTCP() error {
atomic.AddInt32(&n.running, 1) atomic.AddInt32(&n.running, 1)
defer atomic.AddInt32(&n.running, -1) defer atomic.AddInt32(&n.running, -1)
listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) cfg := net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(SetReUseAddr)
},
}
listen, err := cfg.Listen(context.Background(), "tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort))
if err != nil { if err != nil {
starlog.Errorln("Listening On Tcp Failed:", err) starlog.Errorln("Listening On Tcp Failed:", err)
return err return err

@ -28,6 +28,7 @@ func TestForward(t *testing.T) {
fmt.Println(f.Status()) fmt.Println(f.Status())
continue continue
} }
return break
} }
time.Sleep(time.Second * 5)
} }

@ -3,6 +3,7 @@
package netforward package netforward
import ( import (
"golang.org/x/sys/unix"
"net" "net"
"syscall" "syscall"
) )
@ -33,3 +34,25 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive
} }
return err return err
} }
func SetReUseAddr(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}
func ControlSetReUseAddr(network, address string, c syscall.RawConn) (err error) {
if err := c.Control(func(fd uintptr) {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
return
}
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
return
}
}); err != nil {
return err
}
return err
}

@ -3,6 +3,7 @@
package netforward package netforward
import ( import (
"golang.org/x/sys/unix"
"net" "net"
"syscall" "syscall"
) )
@ -37,3 +38,25 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive
} }
return err return err
} }
func SetReUseAddr(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}
func ControlSetReUseAddr(network, address string, c syscall.RawConn) (err error) {
if err := c.Control(func(fd uintptr) {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
return
}
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
return
}
}); err != nil {
return err
}
return err
}

@ -31,3 +31,16 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive
} }
return conn.SetKeepAlive(false) return conn.SetKeepAlive(false)
} }
func SetReUseAddr(fd uintptr) {
syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
}
func ControlSetReUseAddr(network, address string, c syscall.RawConn) (err error) {
if err := c.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
}); err != nil {
return err
}
return
}

Loading…
Cancel
Save