add more nat func
parent
60a73eb0d8
commit
7d85b14d60
@ -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
|
||||
}
|
Loading…
Reference in New Issue