From 5b0b3834bbb05cb0fd6641133efedd5e3dc9e043 Mon Sep 17 00:00:00 2001 From: starainrt Date: Wed, 18 Dec 2024 17:17:54 +0800 Subject: [PATCH] 1.add tcp kill/mon functions 2.support win 7 again 3.fix tcp keepalive period too fast on windows 4.other bug fix --- go.mod | 18 +- go.sum | 47 ++- httpserver/server.go | 7 +- main.go | 7 +- mget/wget.go | 6 +- net/cmd.go | 4 + net/scanip.go | 4 +- net/setcpinfo_darwin.go | 23 ++ net/setcpinfo_linux.go | 23 ++ net/setcpinfo_windows.go | 16 + net/tcpclient.go | 17 +- net/tcpcmd.go | 3 + net/tcpserver.go | 4 + netforward/setcpinfo_windows.go | 3 + tcm/cmd_unix.go | 38 ++ tcm/cmd_winarm64.go | 16 + tcm/cmd_windows.go | 33 ++ tcm/tcpmonitor_unix.go | 679 ++++++++++++++++++++++++++++++++ tcm/tcpmonitor_windows.go | 474 ++++++++++++++++++++++ tcpkill/cmd.go | 41 ++ tcpkill/cmd_winarm64.go | 16 + tcpkill/tcpkill.go | 108 +++++ tcpkill/tcpkill_unix.go | 372 +++++++++++++++++ tcpkill/tcpkill_windows.go | 390 ++++++++++++++++++ version/version.go | 3 + 25 files changed, 2320 insertions(+), 32 deletions(-) create mode 100644 tcm/cmd_unix.go create mode 100644 tcm/cmd_winarm64.go create mode 100644 tcm/cmd_windows.go create mode 100644 tcm/tcpmonitor_unix.go create mode 100644 tcm/tcpmonitor_windows.go create mode 100644 tcpkill/cmd.go create mode 100644 tcpkill/cmd_winarm64.go create mode 100644 tcpkill/tcpkill.go create mode 100644 tcpkill/tcpkill_unix.go create mode 100644 tcpkill/tcpkill_windows.go create mode 100644 version/version.go diff --git a/go.mod b/go.mod index a07de55..f553e5b 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module b612.me/apps/b612 -go 1.21.2 - -toolchain go1.22.4 +go 1.20 require ( + b612.me/bcap v0.0.4 b612.me/notify v1.2.6 b612.me/sdk/whois v0.0.0-20240816133027-129514a15991 b612.me/starcrypto v0.0.5 @@ -19,20 +18,23 @@ require ( github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 github.com/emersion/go-smtp v0.20.2 + github.com/florianl/go-nfqueue/v2 v2.0.0 github.com/go-acme/lego/v4 v4.16.1 github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/google/gopacket v1.1.19 github.com/huin/goupnp v1.3.0 github.com/inconshreveable/mousetrap v1.1.0 github.com/miekg/dns v1.1.58 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 + github.com/shirou/gopsutil/v4 v4.24.10 github.com/spf13/cobra v1.8.0 github.com/things-go/go-socks5 v0.0.5 github.com/vbauerster/mpb/v8 v8.8.3 golang.org/x/crypto v0.26.0 golang.org/x/net v0.28.0 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.26.0 software.sslmate.com/src/go-pkcs12 v0.4.0 ) @@ -50,28 +52,36 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cloudflare/cloudflare-go v0.86.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect + github.com/ebitengine/purego v0.8.1 // indirect github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/jlaffaye/ftp v0.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/native v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kr/fs v0.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.4.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/sftp v1.13.4 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/image v0.6.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/go.sum b/go.sum index fae4fd6..518664c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +b612.me/bcap v0.0.4 h1:iY2Oz+uyG/mue6a/dJiU82ci5Xwkj4xHhre/q0O8G60= +b612.me/bcap v0.0.4/go.mod h1:tpus+4iMpsnxb98Pck70s87Zt4sIWuRZjK23MmmzmoY= b612.me/notify v1.2.5/go.mod h1:GTnAdC6v9krGxtC8Gkn8TcyUsYnHSiHjRAXsONPiLpI= b612.me/notify v1.2.6 h1:fY+0ccP6cJCDvnfRilmPlDK+J8xTYBpYNwf7jaC2IIE= b612.me/notify v1.2.6/go.mod h1:awcFq3bvbkf3hdviUtOW16Io0IEJXkNPgno7IRe7B9g= @@ -36,11 +38,9 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aov github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 h1:8iR6OLffWWorFdzL2JFCab5xpD8VKEE2DUBBl+HNTDY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 h1:rR8ZW79lE/ppfXTfiYSnMFv5EzmVuY4pfZWIkscIJ64= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= @@ -60,7 +60,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 h1:m62nsMU279qRD9PQSWD1l66kmkXzuYcnVJqL4XLeV2M= github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= @@ -70,11 +71,14 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe github.com/emersion/go-smtp v0.20.2 h1:peX42Qnh5Q0q3vrAnRy43R/JwTnnv75AebxbkTL7Ia4= github.com/emersion/go-smtp v0.20.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo= +github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk= github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ= github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 h1:cC0Hbb+18DJ4i6ybqDybvj4wdIDS4vnD0QEci98PgM8= @@ -92,17 +96,16 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= @@ -116,6 +119,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -127,11 +132,13 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -148,11 +155,15 @@ github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg= github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM= +github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -161,8 +172,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg= @@ -172,7 +182,10 @@ github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRw github.com/vbauerster/mpb/v8 v8.8.3 h1:dTOByGoqwaTJYPubhVz3lO5O6MK553XVgUo33LdnNsQ= github.com/vbauerster/mpb/v8 v8.8.3/go.mod h1:JfCCrtcMsJwP6ZwMn9e5LMnNyp3TVNpUWWkN+nd4EWk= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -184,12 +197,15 @@ golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -211,7 +227,10 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -225,8 +244,9 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -254,12 +274,14 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -268,7 +290,6 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/httpserver/server.go b/httpserver/server.go index a773e84..4800816 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -1,6 +1,7 @@ package httpserver import ( + "b612.me/apps/b612/version" "b612.me/starcrypto" "b612.me/starlog" "b612.me/starnet" @@ -24,7 +25,7 @@ import ( "time" ) -var version = "2.1.0.b11" +var ver = version.Version type HttpServerCfgs func(cfg *HttpServerCfg) @@ -320,7 +321,7 @@ func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) { log.SetShowFuncName(false) log.SetShowOriginFile(false) w.Header().Set("X-Powered-By", "B612.ME") - w.Header().Set("Server", "B612/"+version) + w.Header().Set("Server", "B612/"+ver) if !h.BasicAuth(log, w, r) { return } @@ -617,7 +618,7 @@ func (h *HttpServer) getFolder(log *starlog.StarLogger, w http.ResponseWriter, r } err = tmpt.Execute(w, map[string]interface{}{ "IdxTitle": r.URL.Path, - "Version": version, + "Version": ver, "Idx": "Index of " + r.URL.Path, "Upload": template.HTML(upload), "Data": template.JS(jData), diff --git a/main.go b/main.go index e4337cf..0027ca8 100644 --- a/main.go +++ b/main.go @@ -29,9 +29,12 @@ import ( "b612.me/apps/b612/smtpserver" "b612.me/apps/b612/socks5" "b612.me/apps/b612/split" + "b612.me/apps/b612/tcm" "b612.me/apps/b612/tcping" + "b612.me/apps/b612/tcpkill" "b612.me/apps/b612/tls" "b612.me/apps/b612/uac" + "b612.me/apps/b612/version" "b612.me/apps/b612/vic" "b612.me/apps/b612/whois" "b612.me/stario" @@ -42,7 +45,7 @@ import ( var cmdRoot = &cobra.Command{ Use: "b612", - Version: "2.1.0.beta.13", + Version: version.Version, } func init() { @@ -51,7 +54,7 @@ func init() { base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd, ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd, calc.Cmd, net.Cmd, rmt.Cmds, rmt.Cmdc, keygen.Cmd, dns.Cmd, whois.Cmd, socks5.Cmd, httproxy.Cmd, smtpserver.Cmd, smtpclient.Cmd, - cert.Cmd, aes.Cmd, tls.Cmd, mget.Cmd) + cert.Cmd, aes.Cmd, tls.Cmd, mget.Cmd, tcpkill.Cmd, tcm.Cmd) } func main() { diff --git a/mget/wget.go b/mget/wget.go index e0f876c..d710ff9 100644 --- a/mget/wget.go +++ b/mget/wget.go @@ -223,10 +223,10 @@ func (w *Mget) Run() error { go w.Process() w.wg.Wait() time.Sleep(2 * time.Microsecond) - exitFn := sync.OnceFunc(w.fn) + var once sync.Once for { if w.writeEnable { - exitFn() + once.Do(w.fn) time.Sleep(time.Millisecond * 50) continue } @@ -236,7 +236,7 @@ func (w *Mget) Run() error { } break } - exitFn() + once.Do(w.fn) stario.WaitUntilTimeout(time.Second*2, func(c chan struct{}) error { for { diff --git a/net/cmd.go b/net/cmd.go index 60356cd..468b223 100644 --- a/net/cmd.go +++ b/net/cmd.go @@ -2,6 +2,9 @@ package net import ( "b612.me/apps/b612/netforward" + "b612.me/apps/b612/tcm" + "b612.me/apps/b612/tcping" + "b612.me/apps/b612/tcpkill" "b612.me/starlog" "fmt" "github.com/spf13/cobra" @@ -103,6 +106,7 @@ func init() { CmdScanPort.Flags().IntVarP(&scanport.Retry, "retry", "r", 2, "重试次数") Cmd.AddCommand(CmdScanPort) + Cmd.AddCommand(tcpkill.Cmd, tcping.Cmd, tcm.Cmd) } var CmdNatPClient = &cobra.Command{ diff --git a/net/scanip.go b/net/scanip.go index ef473ba..d24edfb 100644 --- a/net/scanip.go +++ b/net/scanip.go @@ -255,8 +255,10 @@ func (s *ScanIP) TCP(port int) error { Timeout: time.Duration(s.Timeout) * time.Millisecond, Control: netforward.ControlSetReUseAddr, } - _, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port)) + conn, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port)) if err == nil { + conn.(*net.TCPConn).SetLinger(0) + conn.Close() atomic.AddInt32(&count, 1) if s.WithHostname { hostname, err := net.LookupAddr(ip) diff --git a/net/setcpinfo_darwin.go b/net/setcpinfo_darwin.go index b815255..927477b 100644 --- a/net/setcpinfo_darwin.go +++ b/net/setcpinfo_darwin.go @@ -3,6 +3,7 @@ package net import ( + "golang.org/x/sys/unix" "net" "syscall" ) @@ -33,3 +34,25 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive } 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 +} diff --git a/net/setcpinfo_linux.go b/net/setcpinfo_linux.go index e6a19e3..44aed64 100644 --- a/net/setcpinfo_linux.go +++ b/net/setcpinfo_linux.go @@ -3,6 +3,7 @@ package net import ( + "golang.org/x/sys/unix" "net" "syscall" ) @@ -37,3 +38,25 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive } 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 +} diff --git a/net/setcpinfo_windows.go b/net/setcpinfo_windows.go index 79c0a07..da51cf3 100644 --- a/net/setcpinfo_windows.go +++ b/net/setcpinfo_windows.go @@ -11,6 +11,9 @@ import ( ) func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + //windows上,这两个值是毫秒,linux上则是秒 + keepAlivePeriod *= 1000 + keepAliveIdel *= 1000 if usingKeepAlive { rawConn, err := conn.SyscallConn() if err != nil { @@ -31,3 +34,16 @@ func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlive } 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 +} diff --git a/net/tcpclient.go b/net/tcpclient.go index 876233a..e3ae3f9 100644 --- a/net/tcpclient.go +++ b/net/tcpclient.go @@ -15,12 +15,14 @@ import ( ) type TcpClient struct { + DialTimeout int LocalAddr string RemoteAddr string UsingKeepAlive bool KeepAlivePeriod int KeepAliveIdel int KeepAliveCount int + UseLinger int Interactive bool UserTimeout int ShowRecv bool @@ -155,17 +157,17 @@ func (s *TcpClient) Run() error { return fmt.Errorf("ResolveTCPAddr error: %w", err) } } - remoteAddr, err := net.ResolveTCPAddr("tcp", s.RemoteAddr) - if err != nil { - starlog.Errorln("ResolveTCPAddr error:", err) - return fmt.Errorf("ResolveTCPAddr error: %w", err) + dialer := net.Dialer{ + LocalAddr: localAddr, + Timeout: time.Duration(s.DialTimeout) * time.Second, + Control: ControlSetReUseAddr, } - - conn, err := net.DialTCP("tcp", localAddr, remoteAddr) + tcpConn, err := dialer.Dial("tcp", s.RemoteAddr) if err != nil { starlog.Errorln("Dial TCP error:", err) return fmt.Errorf("Dial TCP error: %w", err) } + conn := tcpConn.(*net.TCPConn) starlog.Infof("Connected to %s LocalAddr: %s\n", conn.RemoteAddr().String(), conn.LocalAddr().String()) if s.Interactive { go s.handleInteractive() @@ -200,6 +202,9 @@ func (s *TcpClient) handleConn(conn *TcpConn) { conn.Close() return } + if s.UseLinger >= 0 { + conn.SetLinger(s.UseLinger) + } log.Infof("SetKeepAlive success for %s\n", conn.RemoteAddr().String()) log.Infof("KeepAlivePeriod: %d, KeepAliveIdel: %d, KeepAliveCount: %d, UserTimeout: %d\n", s.KeepAlivePeriod, s.KeepAliveIdel, s.KeepAliveCount, s.UserTimeout) if runtime.GOOS != "linux" { diff --git a/net/tcpcmd.go b/net/tcpcmd.go index e8eb08a..0b0005f 100644 --- a/net/tcpcmd.go +++ b/net/tcpcmd.go @@ -25,6 +25,7 @@ func init() { CmdTcps.Flags().BoolVarP(&tcps.ShowAsHex, "show-hex", "H", false, "显示十六进制") CmdTcps.Flags().StringVarP(&tcps.SaveToFolder, "save", "s", "", "保存到文件夹") CmdTcps.Flags().StringVarP(&tcps.LogPath, "log", "L", "", "日志文件路径") + CmdTcps.Flags().IntVarP(&tcpc.UseLinger, "linger", "g", -1, "Linger时间(秒)") Cmd.AddCommand(CmdTcps) CmdTcpc.Flags().StringVarP(&tcpc.LocalAddr, "local", "l", "", "本地地址") @@ -38,6 +39,8 @@ func init() { CmdTcpc.Flags().BoolVarP(&tcpc.ShowAsHex, "show-hex", "H", false, "显示十六进制") CmdTcpc.Flags().StringVarP(&tcpc.SaveToFolder, "save", "s", "", "保存到文件夹") CmdTcpc.Flags().StringVarP(&tcpc.LogPath, "log", "L", "", "日志文件路径") + CmdTcpc.Flags().IntVarP(&tcpc.DialTimeout, "dial-timeout", "t", 5, "连接超时时间(秒)") + CmdTcpc.Flags().IntVarP(&tcpc.UseLinger, "linger", "g", -1, "Linger时间(秒)") Cmd.AddCommand(CmdTcpc) } diff --git a/net/tcpserver.go b/net/tcpserver.go index 748ebf5..8a74b2a 100644 --- a/net/tcpserver.go +++ b/net/tcpserver.go @@ -26,6 +26,7 @@ type TcpServer struct { KeepAlivePeriod int KeepAliveIdel int KeepAliveCount int + UseLinger int sync.Mutex Clients map[string]*TcpConn Interactive bool @@ -233,6 +234,9 @@ func (s *TcpServer) handleConn(conn *TcpConn) { conn.Close() return } + if s.UseLinger >= 0 { + conn.SetLinger(s.UseLinger) + } log.Infof("SetKeepAlive success for %s\n", conn.RemoteAddr().String()) log.Infof("KeepAlivePeriod: %d, KeepAliveIdel: %d, KeepAliveCount: %d, UserTimeout: %d\n", s.KeepAlivePeriod, s.KeepAliveIdel, s.KeepAliveCount, s.UserTimeout) if runtime.GOOS != "linux" { diff --git a/netforward/setcpinfo_windows.go b/netforward/setcpinfo_windows.go index 9588f7a..614108f 100644 --- a/netforward/setcpinfo_windows.go +++ b/netforward/setcpinfo_windows.go @@ -11,6 +11,9 @@ import ( ) func SetTcpInfo(conn *net.TCPConn, usingKeepAlive bool, keepAliveIdel, keepAlivePeriod, keepAliveCount, userTimeout int) error { + //windows上,这两个值是毫秒,linux上则是秒 + keepAlivePeriod *= 1000 + keepAliveIdel *= 1000 if usingKeepAlive { rawConn, err := conn.SyscallConn() if err != nil { diff --git a/tcm/cmd_unix.go b/tcm/cmd_unix.go new file mode 100644 index 0000000..e2dfe3c --- /dev/null +++ b/tcm/cmd_unix.go @@ -0,0 +1,38 @@ +//go:build !windows + +package tcm + +import "github.com/spf13/cobra" + +var nf = NewNfCap() + +var Cmd = &cobra.Command{ + Use: "tcm", + Short: "TCP连接监视工具", + Run: func(cmd *cobra.Command, args []string) { + nf.Run() + }, +} + +func init() { + Cmd.Flags().IntVarP(&nf.monitorPort, "port", "p", 0, "nft转发端口地址,如果启用此参数,将会自动设置iptables,需要安装iptables") + Cmd.Flags().StringSliceVarP(&nf.target, "target", "t", []string{}, "监控的ip地址,可多个,本工具各类延迟等tcp操作仅对此类ip生效") + Cmd.Flags().StringSliceVarP(&nf.targetCmd, "cmd", "c", []string{}, "触发报文drop的关键词,utf8格式,如:show variables") + Cmd.Flags().BoolVarP(&nf.targetAsHex, "cmd-as-hex", "x", false, "启用此选项,cmd选项请传入hex字符,而不是utf-8") + Cmd.Flags().StringVarP(&nf.saveFile, "save", "w", "", "保存文件路径,将会保存所有报文到此文件") + Cmd.Flags().BoolVarP(&nf.interactive, "interactive", "i", false, "启用交互模式,可输入命令:allow ,drop ,delay ,loss ") + Cmd.Flags().BoolVarP(&nf.showAll, "display-all", "D", false, "显示所有报文,包括非target对象") + Cmd.Flags().BoolVarP(&nf.showAsHex, "as-hex", "a", false, "显示报文的hex内容") + Cmd.Flags().BoolVarP(&nf.showPayload, "show-payload", "S", false, "显示报文的payload") + Cmd.Flags().IntVarP(&nf.maxShowPayloadSize, "payload-maxlen", "m", 200, "显示payload的最大长度") + Cmd.Flags().BoolVarP(&nf.noShowMode, "no-show", "N", false, "不显示任何tcp报文,只统计数量") + Cmd.Flags().Float64VarP(&nf.loss, "loss", "l", 0, "丢包率,0-100之间,如10表示10%丢包") + Cmd.Flags().IntVarP(&nf.delay, "delay", "d", 0, "延迟时间,单位ms") + Cmd.Flags().IntVarP(&nf.packetDelay, "packet-delay-num", "n", 0, "触发封禁关键词后,延迟n个包再封禁") + Cmd.Flags().BoolVarP(&nf.useRST, "rst", "r", false, "触发封禁关键词后,同步发送RST报文") + Cmd.Flags().StringVarP(&nf.rstMode, "rstmode", "R", "reverse", "RST报文发送模式,可选值:both,target,reverse") + Cmd.Flags().BoolVarP(&nf.fastMode, "fastmode", "f", false, "快速模式,仅在模拟延迟或丢包时使用") + Cmd.Flags().IntVarP(&nf.NFQNums, "nfqueue-num", "q", 2, "nfqueue队列号") + Cmd.Flags().BoolVarP(&nf.allowRandomAck, "random-ack", "A", false, "允许并行乱序处理报文,如果需要模拟延迟,此选项需开启,但封禁功能可能受到乱序影响") + Cmd.Flags().BoolVarP(&nf.onlyDropblackwordPacket, "only-drop-blackword-packet", "o", false, "只封禁包含关键词的报文,此模式下,packetDelay会失效") +} diff --git a/tcm/cmd_winarm64.go b/tcm/cmd_winarm64.go new file mode 100644 index 0000000..51ed5cf --- /dev/null +++ b/tcm/cmd_winarm64.go @@ -0,0 +1,16 @@ +//go:build windows && arm64 + +package tcm + +import ( + "fmt" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "tcm", + Short: "TCP连接监视工具", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("windows on arm is not supported yet") + }, +} diff --git a/tcm/cmd_windows.go b/tcm/cmd_windows.go new file mode 100644 index 0000000..d33bb5f --- /dev/null +++ b/tcm/cmd_windows.go @@ -0,0 +1,33 @@ +//go:build windows && !arm64 + +package tcm + +import "github.com/spf13/cobra" + +var nf = NewLibpcap() +var Cmd = &cobra.Command{ + Use: "tcm", + Short: "TCP连接监视工具", + Run: func(cmd *cobra.Command, args []string) { + nf.Run() + }, +} + +func init() { + Cmd.Flags().StringSliceVarP(&nf.target, "target", "t", []string{}, "监控的ip地址,可多个,本工具各类延迟等tcp操作仅对此类ip生效") + Cmd.Flags().StringSliceVarP(&nf.targetCmd, "cmd", "c", []string{}, "触发报文drop的关键词,utf8格式,如:show variables") + Cmd.Flags().BoolVarP(&nf.targetAsHex, "cmd-as-hex", "x", false, "启用此选项,cmd选项请传入hex字符,而不是utf-8") + Cmd.Flags().StringVarP(&nf.saveFile, "save", "w", "", "保存文件路径,将会保存所有报文到此文件") + //Cmd.Flags().BoolVarP(&nf.interactive, "interactive", "i", false, "启用交互模式,可输入命令:allow ,drop ,delay ,loss ") + Cmd.Flags().BoolVarP(&nf.showAll, "display-all", "D", false, "显示所有报文,包括非target对象") + Cmd.Flags().BoolVarP(&nf.showAsHex, "as-hex", "a", false, "显示报文的hex内容") + Cmd.Flags().BoolVarP(&nf.showPayload, "show-payload", "S", false, "显示报文的payload") + Cmd.Flags().IntVarP(&nf.maxShowPayloadSize, "payload-maxlen", "m", 200, "显示payload的最大长度") + Cmd.Flags().BoolVarP(&nf.noShowMode, "no-show", "N", false, "不显示任何tcp报文,只统计数量") + Cmd.Flags().BoolVarP(&nf.useRST, "rst", "r", false, "触发封禁关键词后,同步发送RST报文") + Cmd.Flags().StringVarP(&nf.rstMode, "rstmode", "R", "reverse", "RST报文发送模式,可选值:both,target,reverse") + Cmd.Flags().StringVarP(&nf.eth, "eth", "e", "", "监听网卡名,如eth0") + Cmd.Flags().StringVarP(&nf.bpf, "bpf", "b", "tcp", "BPF过滤,如tcp port 80") + Cmd.Flags().StringVarP(&nf.host, "host", "i", "", "监听主机名,如127.0.0.1") + +} diff --git a/tcm/tcpmonitor_unix.go b/tcm/tcpmonitor_unix.go new file mode 100644 index 0000000..1ed9be7 --- /dev/null +++ b/tcm/tcpmonitor_unix.go @@ -0,0 +1,679 @@ +//go:build !windows + +package tcm + +import ( + "b612.me/bcap" + "b612.me/bcap/nfq" + "b612.me/stario" + "b612.me/starlog" + "context" + "encoding/hex" + "fmt" + "github.com/florianl/go-nfqueue/v2" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcapgo" + "math/rand" + "net" + "os" + "os/exec" + "os/signal" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +type NfCap struct { + count uint64 + // 按连接数最后一个历史报文 + cap *bcap.Packets + // 监控目标ip地址列表 + target []string + // 将军,下达命令吧! + // 监控命令列表 + targetCmd []string + targetAsHex bool + //保存为pcap格式的数据文件 + saveFile string + // 写缓存 + packetCache chan gopacket.Packet + //保存封禁的map + blockMap sync.Map + //交互模式 + interactive bool + //展示所有报文信息,包括未在追踪列表的 + showAll bool + //展示payload报文(utf-8直接输出) + showPayload bool + //payload展示为hex + showAsHex bool + //payload允许展示的最大字符 + maxShowPayloadSize int + //不展示任何信息 + noShowMode bool + // 全链路丢包率,百分比 + loss float64 + // 报文延迟抵达时间,毫秒 + delay int + // 触发封禁词后,再过N个包再封禁 + packetDelay int + // 触发封禁词后,使用RST重置链路 + useRST bool + // RST模式,target=目标端单向RST,reverse=对端反向RST,both=双向RST + rstMode string + fastMode bool //自探测 + printColor []*starlog.Color + logCache chan loged + monitorPort int + cache []string + NFQNums int + ctx context.Context + fn context.CancelFunc + requests chan *handler + allowRandomAck bool + onlyDropblackwordPacket bool +} + +type loged struct { + str string + stateLevel int + logLevel string +} + +func (n *NfCap) inactve() { + for { + f := strings.Fields(stario.MessageBox("", "").MustString()) + if len(f) < 2 { + continue + } + switch f[0] { + case "allow": + for _, v := range f[1:] { + n.blockMap.Delete(v) + starlog.Infof("允许%s报文\n", v) + } + case "drop": + for _, v := range f[1:] { + n.blockMap.Store(v, true) + starlog.Infof("封禁%s报文\n", v) + } + case "delay": + tmp, err := strconv.Atoi(f[1]) + if err != nil { + starlog.Errorln("输入延迟无效:%v\n", err) + continue + } + n.delay = tmp + starlog.Infof("延迟生效:%v ms\n", n.delay) + case "loss": + tmp, err := strconv.ParseFloat(f[1], 64) + if err != nil { + starlog.Errorln("输入丢包百分比无效:%v\n", err) + continue + } + n.loss = tmp + starlog.Infof("丢包百分比生效:%v ms\n", n.loss) + } + } +} + +func (t *NfCap) doIptables() error { + if t.monitorPort != 0 { + if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--dport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --dport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--sport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --sport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--sport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --sport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--dport", strconv.Itoa(t.monitorPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --dport "+strconv.Itoa(t.monitorPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + + } + return nil +} + +func (t *NfCap) undoIptables() { + for _, cmd := range t.cache { + exec.Command("iptables", strings.Fields(cmd)...).CombinedOutput() + } +} + +func (t *NfCap) Run() error { + stopSignal := make(chan os.Signal) + starlog.Noticef("Starting nfqueue capture\n") + nf := nfq.NewNfQueue(t.ctx, uint16(t.NFQNums), 65535) + nf.SetRecall(t.handleRoute) + if t.targetAsHex { + for k, v := range t.targetCmd { + tmp, _ := hex.DecodeString(v) + t.targetCmd[k] = string(tmp) + } + } + go func() { + if err := nf.Run(); err != nil { + starlog.Errorln(err) + stopSignal <- syscall.SIGKILL + } + }() + if t.saveFile != "" { + f, err := os.Create(t.saveFile) + if err != nil { + starlog.Errorln("创建写入文件失败", err) + os.Exit(4) + } + defer f.Close() + go t.pcapWriter(t.ctx, f) + } + if t.monitorPort != 0 { + if _, err := exec.Command("iptables", "-V").CombinedOutput(); err != nil { + starlog.Warningln("iptables not found, cannot auto set iptables") + return fmt.Errorf("iptables not found") + } + defer t.undoIptables() + if err := t.doIptables(); err != nil { + starlog.Errorf("Failed to set iptables:%v\n", err) + return err + } + starlog.Infof("Set iptables success\n") + } + signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) + go t.handleNfResult() + go t.logPrint(t.ctx) + select { + //todo need nf.Stop() + case <-stopSignal: + starlog.Warningf("Received stop signal\n") + + case <-t.ctx.Done(): + starlog.Infoln("TCPMonitor Task Finished") + + } + return nil + +} + +func NewNfCap() *NfCap { + var nf = new(NfCap) + nf.packetCache = make(chan gopacket.Packet, 2048) + nf.logCache = make(chan loged, 2048) + nf.printColor = []*starlog.Color{ + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgHiCyan), //1=tcp_connect_1, + starlog.NewColor(starlog.FgHiCyan), //2=tcp_connect_2, + starlog.NewColor(starlog.FgHiCyan), //3=tcp_connect_3, + starlog.NewColor(starlog.FgCyan), //4=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //5=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //6=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //7=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //8=tcp_bye_bye + starlog.NewColor(starlog.FgGreen), //9=tcp_ok + starlog.NewColor(starlog.BgRed, starlog.FgYellow), //10=tcp_retrans + starlog.NewColor(starlog.FgHiMagenta), //ece + starlog.NewColor(starlog.FgHiMagenta), //cwr + starlog.NewColor(starlog.FgRed), //rst + starlog.NewColor(starlog.FgHiGreen), //keepalive + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //20=udp + starlog.NewColor(starlog.FgWhite), //0=unknown + } + nf.cap = bcap.NewPackets() + nf.ctx, nf.fn = context.WithCancel(context.Background()) + nf.requests = make(chan *handler, 2048) + return nf +} +func (t *NfCap) handleNfResult() { + for { + select { + case <-t.ctx.Done(): + return + case info := <-t.requests: + if info == nil { + continue + } + if t.allowRandomAck { + go info.p.SetVerdict(info.id, <-info.fin) + continue + } + info.p.SetVerdict(info.id, <-info.fin) + } + } +} + +func (t *NfCap) handleRoute(id uint32, q *nfqueue.Nfqueue, p nfq.Packet) { + if t.requests == nil { + q.SetVerdict(id, nfqueue.NfAccept) + return + } + info, err := t.cap.ParsePacket(p.Packet) + if err != nil { + q.SetVerdict(id, nfqueue.NfAccept) + return + } + fin := make(chan int) + t.requests <- &handler{ + id: id, + p: q, + packet: p.Packet, + attr: p.Attr, + fin: fin, + } + go func() { + fin <- t.handlePacket(info, p) + }() +} + +type handler struct { + id uint32 + p *nfqueue.Nfqueue + packet gopacket.Packet + attr nfqueue.Attribute + fin chan int +} + +func (n *NfCap) logPrint(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case l := <-n.logCache: + if n.noShowMode { + fmt.Printf("已捕获报文数量:%v个\r", n.count) + continue + } + switch l.logLevel { + case "info": + starlog.Info(l.str) + case "notice": + starlog.Notice(l.str) + case "warning": + starlog.Warning(l.str) + case "error": + starlog.Error(l.str) + case "critical": + starlog.Critical(l.str) + case "debug": + starlog.Debug(l.str) + case "payload": + fmt.Println(l.str) + default: + n.printColor[l.stateLevel].Print(l.str) + } + } + } +} + +func (n *NfCap) ipInRange(ip string) bool { + if len(n.target) == 0 { + return false + } + for _, v := range n.target { + if v == ip { + return true + } + } + return false +} + +func (n *NfCap) strInRange(str string) bool { + if len(n.targetCmd) == 0 { + return false + } + for _, v := range n.targetCmd { + if v == "" { + continue + } + if strings.Contains(str, v) { + return true + } + } + return false +} + +func (n *NfCap) handlePacket(info bcap.PacketInfo, nfp nfq.Packet) int { + p := nfp.Packet + n.count++ + if n.saveFile != "" { + n.packetCache <- p + } + var layer gopacket.Layer + for _, layerType := range []gopacket.LayerType{ + layers.LayerTypeTCP, layers.LayerTypeUDP, layers.LayerTypeICMPv4, layers.LayerTypeICMPv6, + layers.LayerTypeIPv4, layers.LayerTypeIPv6, layers.LayerTypeARP, + } { + layer = p.Layer(layerType) + if layer == nil { + continue + } + break + } + if layer == nil { + n.logCache <- loged{ + str: "无法定义layer类型\n", + stateLevel: 0, + logLevel: "error", + } + return nfqueue.NfAccept + } + var shouldDisallow bool + if n.fastMode { + if n.delay > 0 || n.loss > 0 { + if n.delay > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) { + time.Sleep(time.Millisecond * time.Duration(n.delay)) + } + if n.loss > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) { + if rand.Intn(10000) < int(n.loss*100) { + shouldDisallow = true + } + } + } + if shouldDisallow { + return nfqueue.NfDrop + } + return nfqueue.NfAccept + } + if n.loss > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) { + if rand.Intn(10000) < int(n.loss*100) { + shouldDisallow = true + } + } + if n.delay > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) { + time.Sleep(time.Millisecond * time.Duration(n.delay)) + } + var dec string + switch info.StateDescript() { + case 0: + dec = "未知状态" + case 1: + dec = "SYN tcp建立一次握手" + case 2: + dec = "SYN,ACK tcp建立二次握手" + case 3: + dec = "ACK tcp建立三次握手" + case 4: + dec = "FIN tcp断开一次挥手" + case 5: + dec = "ACK tcp断开二次挥手" + case 6: + dec = "FIN,ACK tcp断开二次三次挥手" + case 7: + dec = "FIN tcp断开三次挥手" + case 8: + dec = "ACK tcp断开四次挥手" + case 9: + dec = "tcp报文" + case 10: + dec = "TCP重传" + case 11: + dec = "TCP ece" + case 12: + dec = "TCP cwr" + case 13: + dec = "TCP RST重置" + case 14: + dec = "TCP Keepalive" + } + if n.showAll || n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP) { + n.logCache <- loged{ + str: fmt.Sprintf("%s %v:%v -> %v:%v %s seq=%v ack=%v win=%v len=%v\n", time.Now().Format("2006-01-02 15:04:05.000000"), info.SrcIP, info.SrcPort, + info.DstIP, info.DstPort, dec, info.TcpSeq(), info.TcpAck(), info.TcpWindow(), info.TcpPayloads()), + stateLevel: int(info.StateDescript()), + logLevel: "", + } + if n.showPayload { + str := string(layer.LayerPayload()) + if n.maxShowPayloadSize > 0 { + if len(str) > n.maxShowPayloadSize { + str = str[:n.maxShowPayloadSize] + } + } + if n.showAsHex { + str = hex.EncodeToString([]byte(str)) + } + n.logCache <- loged{ + str: str, + stateLevel: int(info.StateDescript()), + logLevel: "payload", + } + } + } + if info.Comment() != "" || n.cap.Key(info.ReverseKey).Comment() != "" { + tmp := info.Comment() + if tmp == "" { + tmp = n.cap.Key(info.ReverseKey).Comment() + } + pkg, _ := strconv.Atoi(tmp) + n.logCache <- loged{ + str: fmt.Sprintf("current delay count:%v\n", pkg-1), + stateLevel: 0, + logLevel: "warning", + } + if pkg-1 <= 0 { + if n.useRST { + RealSendRST(info, n.rstMode, 3) + } + if n.ipInRange(info.SrcIP) { + n.blockMap.Store(info.SrcIP, true) + } else { + n.blockMap.Store(info.DstIP, true) + } + n.cap.SetComment(info.Key, "") + n.cap.SetComment(info.ReverseKey, "") + } else { + n.cap.SetComment(info.Key, strconv.Itoa(pkg-1)) + n.cap.SetComment(info.ReverseKey, strconv.Itoa(pkg-1)) + } + } + if len(n.targetCmd) > 0 && (n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP)) && n.strInRange(string(layer.LayerPayload())) { + n.logCache <- loged{ + str: fmt.Sprintf("%s:%s -> %s:%s !!Match Keyword,will block!!\n", info.SrcIP, info.SrcPort, + info.DstIP, info.DstPort), + stateLevel: 0, + logLevel: "warning", + } + if n.onlyDropblackwordPacket { + if n.useRST && info.StateDescript() == 13 { + return nfqueue.NfAccept + } + n.logCache <- loged{ + str: fmt.Sprintf("Block TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()), + stateLevel: 0, + logLevel: "warning", + } + return nfqueue.NfDrop + } + if n.packetDelay > 0 && info.Comment() == "" { + n.cap.SetComment(info.Key, strconv.Itoa(n.packetDelay)) + n.cap.SetComment(info.ReverseKey, strconv.Itoa(n.packetDelay)) + } else { + if n.useRST { + RealSendRST(info, n.rstMode, 3) + } + if n.ipInRange(info.SrcIP) { + n.blockMap.Store(info.SrcIP, true) + } else { + n.blockMap.Store(info.DstIP, true) + } + } + } + + _, ok1 := n.blockMap.Load(info.DstIP) + _, ok2 := n.blockMap.Load(info.SrcIP) + if ok1 || ok2 { + if n.useRST && info.StateDescript() == 13 { + return nfqueue.NfAccept + } + n.logCache <- loged{ + str: fmt.Sprintf("Block TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()), + stateLevel: 0, + logLevel: "warning", + } + return nfqueue.NfDrop + } + if shouldDisallow { + n.logCache <- loged{ + str: fmt.Sprintf("Block(loss) TCP %v:%v -> %v:%v,LEN=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpPayloads()), + stateLevel: 0, + logLevel: "warning", + } + return nfqueue.NfDrop + } + return nfqueue.NfAccept +} + +func (n *NfCap) pcapWriter(stopCtx context.Context, fp *os.File) error { + w := pcapgo.NewWriter(fp) + err := w.WriteFileHeader(65535, layers.LinkTypeRaw) + if err != nil { + return err + } + for { + select { + case <-stopCtx.Done(): + return nil + case p := <-n.packetCache: + w.WritePacket(gopacket.CaptureInfo{ + Timestamp: time.Now(), + CaptureLength: len(p.Data()), + Length: len(p.Data()), + }, p.Data()) + } + } +} + +func RealSendRST(info bcap.PacketInfo, target string, number int) { + for i := 0; i < number; i++ { + if target == "both" || target == "target" { + SendRST(info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow()))) + } + if target == "both" || target == "reverse" { + SendRST(info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + } + } +} + +func SendRST(srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, false) + } + return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, false) +} + +func SendSYN(srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, true) + } + return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, true) +} + +func sendIPv4(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + if err != nil { + return err + } + defer syscall.Close(fd) + + dstNetIP := net.ParseIP(dstIP) + iPv4 := layers.IPv4{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil { + return err + } + addr := syscall.SockaddrInet4{ + Port: dPort, + Addr: [4]byte{dstNetIP.To4()[0], dstNetIP.To4()[1], dstNetIP.To4()[2], dstNetIP.To4()[3]}, + } + return syscall.Sendto(fd, buffer.Bytes(), 0, &addr) +} + +func sendIPv6(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + if err != nil { + return err + } + defer syscall.Close(fd) + + dstNetIP := net.ParseIP(dstIP) + iPv6 := layers.IPv6{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 6, + NextHeader: layers.IPProtocolTCP, + HopLimit: 64, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil { + return err + } + addr := syscall.SockaddrInet6{ + Port: dPort, + Addr: [16]byte(dstNetIP.To16()), + } + return syscall.Sendto(fd, buffer.Bytes(), 0, &addr) +} diff --git a/tcm/tcpmonitor_windows.go b/tcm/tcpmonitor_windows.go new file mode 100644 index 0000000..1f91402 --- /dev/null +++ b/tcm/tcpmonitor_windows.go @@ -0,0 +1,474 @@ +//go:build windows && !arm64 + +package tcm + +import ( + "b612.me/bcap" + "b612.me/bcap/libpcap" + "b612.me/starlog" + "context" + "encoding/hex" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/pcapgo" + "net" + "os" + "os/signal" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +type Libpcap struct { + count uint64 + // 按连接数最后一个历史报文 + cap *bcap.Packets + // 监控目标ip地址列表 + target []string + // 将军,下达命令吧! + // 监控命令列表 + targetCmd []string + targetAsHex bool + //保存为pcap格式的数据文件 + saveFile string + // 写缓存 + packetCache chan gopacket.Packet + //交互模式 + interactive bool + //展示所有报文信息,包括未在追踪列表的 + showAll bool + //展示payload报文(utf-8直接输出) + showPayload bool + //payload展示为hex + showAsHex bool + //payload允许展示的最大字符 + maxShowPayloadSize int + //不展示任何信息 + noShowMode bool + // 触发封禁词后,使用RST重置链路 + useRST bool + // RST模式,target=目标端单向RST,reverse=对端反向RST,both=双向RST + rstMode string + printColor []*starlog.Color + logCache chan loged + ctx context.Context + fn context.CancelFunc + bpf string + eth string + host string + handle *libpcap.NetCatch + blockMap sync.Map +} + +type loged struct { + str string + stateLevel int + logLevel string +} + +func (t *Libpcap) Run() error { + var err error + stopSignal := make(chan os.Signal) + starlog.Noticef("Starting libpcap capture\n") + if t.bpf == "" { + starlog.Errorln("请输入一个抓包模式") + os.Exit(3) + } + if t.host == "" && t.eth == "" { + starlog.Errorln("请输入eth网卡名或host名") + ifs, err := libpcap.FindAllDevs() + if err == nil { + fmt.Println("网卡名如下:\n----------\n") + for k, v := range ifs { + var ips []string + for _, vv := range v.Addresses { + ips = append(ips, vv.IP.String()) + } + fmt.Printf("%d.\t%v\t%s\t%v\n", k+1, v.Name, strings.Join(ips, " , "), v.Description) + } + fmt.Println() + } + return fmt.Errorf("请输入eth网卡名或host名") + } + if t.host == "" { + t.handle, err = libpcap.NewCatchEth(t.eth, t.bpf) + } else { + t.handle, err = libpcap.NewCatch(t.host, t.bpf) + } + if err != nil { + starlog.Errorln("failed to listen:", err) + return err + } + t.handle.SetRecall(t.handlePacket) + if t.targetAsHex { + for k, v := range t.targetCmd { + tmp, _ := hex.DecodeString(v) + t.targetCmd[k] = string(tmp) + } + } + go func() { + if err = t.handle.Run(); err != nil { + starlog.Errorln(err) + stopSignal <- syscall.SIGKILL + } + }() + if t.saveFile != "" { + f, err := os.Create(t.saveFile) + if err != nil { + starlog.Errorln("创建写入文件失败", err) + os.Exit(4) + } + defer f.Close() + go t.pcapWriter(t.ctx, f) + } + signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) + go t.logPrint(t.ctx) + select { + //todo need nf.Stop() + case <-stopSignal: + starlog.Warningf("Received stop signal\n") + + case <-t.ctx.Done(): + starlog.Infoln("TCPMonitor Task Finished") + + } + return nil + +} + +func NewLibpcap() *Libpcap { + var nf = new(Libpcap) + nf.packetCache = make(chan gopacket.Packet, 2048) + nf.logCache = make(chan loged, 2048) + nf.printColor = []*starlog.Color{ + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgHiCyan), //1=tcp_connect_1, + starlog.NewColor(starlog.FgHiCyan), //2=tcp_connect_2, + starlog.NewColor(starlog.FgHiCyan), //3=tcp_connect_3, + starlog.NewColor(starlog.FgCyan), //4=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //5=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //6=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //7=tcp_bye_bye + starlog.NewColor(starlog.FgCyan), //8=tcp_bye_bye + starlog.NewColor(starlog.FgGreen), //9=tcp_ok + starlog.NewColor(starlog.BgRed, starlog.FgYellow), //10=tcp_retrans + starlog.NewColor(starlog.FgHiMagenta), //ece + starlog.NewColor(starlog.FgHiMagenta), //cwr + starlog.NewColor(starlog.FgRed), //rst + starlog.NewColor(starlog.FgHiGreen), //keepalive + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //0=unknown + starlog.NewColor(starlog.FgWhite), //20=udp + starlog.NewColor(starlog.FgWhite), //0=unknown + } + nf.cap = bcap.NewPackets() + nf.ctx, nf.fn = context.WithCancel(context.Background()) + return nf +} + +func (n *Libpcap) logPrint(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case l := <-n.logCache: + if n.noShowMode { + fmt.Printf("已捕获报文数量:%v个\r", n.count) + continue + } + switch l.logLevel { + case "info": + starlog.Info(l.str) + case "notice": + starlog.Notice(l.str) + case "warning": + starlog.Warning(l.str) + case "error": + starlog.Error(l.str) + case "critical": + starlog.Critical(l.str) + case "debug": + starlog.Debug(l.str) + case "payload": + fmt.Println(l.str) + default: + n.printColor[l.stateLevel].Print(l.str) + } + } + } +} + +func (n *Libpcap) ipInRange(ip string) bool { + if len(n.target) == 0 { + return false + } + for _, v := range n.target { + if v == ip { + return true + } + } + return false +} + +func (n *Libpcap) strInRange(str string) bool { + if len(n.targetCmd) == 0 { + return false + } + for _, v := range n.targetCmd { + if v == "" { + continue + } + if strings.Contains(str, v) { + return true + } + } + return false +} + +func (n *Libpcap) handlePacket(p gopacket.Packet) { + n.count++ + if n.saveFile != "" { + n.packetCache <- p + } + var layer gopacket.Layer + for _, layerType := range []gopacket.LayerType{ + layers.LayerTypeTCP, layers.LayerTypeUDP, layers.LayerTypeICMPv4, layers.LayerTypeICMPv6, + layers.LayerTypeIPv4, layers.LayerTypeIPv6, layers.LayerTypeARP, + } { + layer = p.Layer(layerType) + if layer == nil { + continue + } + break + } + if layer == nil { + n.logCache <- loged{ + str: "无法定义layer类型\n", + stateLevel: 0, + logLevel: "error", + } + return + } + info, err := n.cap.ParsePacket(p) + if err != nil { + starlog.Errorln(err) + return + } + var dec string + switch info.StateDescript() { + case 0: + dec = "未知状态" + case 1: + dec = "SYN tcp建立一次握手" + case 2: + dec = "SYN,ACK tcp建立二次握手" + case 3: + dec = "ACK tcp建立三次握手" + case 4: + dec = "FIN tcp断开一次挥手" + case 5: + dec = "ACK tcp断开二次挥手" + case 6: + dec = "FIN,ACK tcp断开二次三次挥手" + case 7: + dec = "FIN tcp断开三次挥手" + case 8: + dec = "ACK tcp断开四次挥手" + case 9: + dec = "tcp报文" + case 10: + dec = "TCP重传" + case 11: + dec = "TCP ece" + case 12: + dec = "TCP cwr" + case 13: + dec = "TCP RST重置" + case 14: + dec = "TCP Keepalive" + } + if !n.showAll && !n.ipInRange(info.SrcIP) && !n.ipInRange(info.DstIP) { + return + } + n.logCache <- loged{ + str: fmt.Sprintf("%s %v:%v -> %v:%v %s seq=%v ack=%v win=%v len=%v\n", time.Now().Format("2006-01-02 15:04:05.000000"), info.SrcIP, info.SrcPort, + info.DstIP, info.DstPort, dec, info.TcpSeq(), info.TcpAck(), info.TcpWindow(), info.TcpPayloads()), + stateLevel: int(info.StateDescript()), + logLevel: "", + } + + if n.showPayload { + str := string(layer.LayerPayload()) + if n.maxShowPayloadSize > 0 { + if len(str) > n.maxShowPayloadSize { + str = str[:n.maxShowPayloadSize] + } + } + if n.showAsHex { + str = hex.EncodeToString([]byte(str)) + } + n.logCache <- loged{ + str: str, + stateLevel: int(info.StateDescript()), + logLevel: "payload", + } + } + if n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP) { + _, ok := n.blockMap.Load(info.Key) + if n.useRST && (ok || n.strInRange(string(layer.LayerPayload()))) { + n.blockMap.Store(info.Key, true) + starlog.Warningf("触发封禁关键词 RST重置\n") + RealSendRST(n.handle.Handle, info, n.rstMode, 3) + } + } +} + +func (n *Libpcap) pcapWriter(stopCtx context.Context, fp *os.File) error { + w := pcapgo.NewWriter(fp) + err := w.WriteFileHeader(65535, layers.LinkTypeEthernet) + if err != nil { + return err + } + for { + select { + case <-stopCtx.Done(): + return nil + case p := <-n.packetCache: + w.WritePacket(p.Metadata().CaptureInfo, p.Data()) + } + } +} + +func RealSendRST(p *pcap.Handle, info bcap.PacketInfo, target string, number int) { + for i := 0; i < number; i++ { + if target == "both" || target == "target" { + SendRST(p, info.SrcMac, info.DstMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow()))) + //SendRST(p, info.DstMac, info.SrcMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow()))) + } + if target == "both" || target == "reverse" { + SendRST(p, info.DstMac, info.SrcMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + //SendRST(p, info.SrcMac, info.DstMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + } + } +} + +func SendRST(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) + } + return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) +} + +func SendSYN(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) + } + return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) +} + +func sendIPv4(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + + dstNetIP := net.ParseIP(dstIP) + eth := layers.Ethernet{ + SrcMAC: srcMac, + DstMAC: dstMac, + EthernetType: layers.EthernetTypeIPv4, + } + iPv4 := layers.IPv4{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if srcMac == nil && dstMac == nil { + if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil { + return err + } + } else { + if err = gopacket.SerializeLayers(buffer, options, ð, &iPv4, &tcp); err != nil { + return err + } + } + return p.WritePacketData(buffer.Bytes()) +} + +func sendIPv6(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + dstNetIP := net.ParseIP(dstIP) + eth := layers.Ethernet{ + SrcMAC: srcMac, + DstMAC: dstMac, + EthernetType: layers.EthernetTypeIPv6, + } + iPv6 := layers.IPv6{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 6, + NextHeader: layers.IPProtocolTCP, + HopLimit: 64, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if srcMac == nil && dstMac == nil { + if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil { + return err + } + } else { + if err = gopacket.SerializeLayers(buffer, options, ð, &iPv6, &tcp); err != nil { + return err + } + } + return p.WritePacketData(buffer.Bytes()) +} diff --git a/tcpkill/cmd.go b/tcpkill/cmd.go new file mode 100644 index 0000000..c42fcf1 --- /dev/null +++ b/tcpkill/cmd.go @@ -0,0 +1,41 @@ +//go:build !(windows && arm64) + +package tcpkill + +import ( + "github.com/spf13/cobra" + "os" + "runtime" +) + +var tck = &TCPKill{} + +func init() { + Cmd.Flags().StringVarP(&tck.SrcIP, "src-ip", "s", "", "本地源IP") + Cmd.Flags().IntVarP(&tck.SrcPort, "src-port", "p", 0, "本地源端口") + Cmd.Flags().StringVarP(&tck.DstIP, "dst-ip", "d", "", "目标IP") + Cmd.Flags().IntVarP(&tck.DstPort, "dst-port", "P", 0, "目标端口") + Cmd.Flags().StringVarP(&tck.Status, "status", "S", "", "匹配的连接状态,如ESTABLISHED,CLOSE_WAIT等,为空则匹配所有") + Cmd.Flags().IntVarP(&tck.RstNumbers, "rst-numbers", "n", 3, "RST包数量") + Cmd.Flags().BoolVarP(&tck.WaitMode, "wait", "w", false, "等待模式") + Cmd.Flags().StringVarP(&tck.KillType, "kill-type", "t", "both", "RST通知类型,both=都通知 target=目标地址通知 reverse=来源地址通知") + if runtime.GOOS != "windows" { + Cmd.Flags().BoolVarP(&tck.AutoIptables, "auto-iptables", "a", true, "自动设置iptables") + Cmd.Flags().IntVarP(&tck.NFQNums, "nfq-nums", "q", 0, "NFQ队列号") + } else { + Cmd.Flags().StringVarP(&tck.Eth, "eth", "e", "", "网卡") + Cmd.Flags().StringVarP(&tck.BPF, "bpf", "b", "", "BPF过滤") + Cmd.Flags().StringVarP(&tck.Host, "host", "i", "", "主机") + } +} + +var Cmd = &cobra.Command{ + Use: "tcpkill", + Short: "杀掉那个TCP连接", + Run: func(cmd *cobra.Command, args []string) { + err := tck.Run() + if err != nil { + os.Exit(1) + } + }, +} diff --git a/tcpkill/cmd_winarm64.go b/tcpkill/cmd_winarm64.go new file mode 100644 index 0000000..062110c --- /dev/null +++ b/tcpkill/cmd_winarm64.go @@ -0,0 +1,16 @@ +//go:build windows && arm64 + +package tcpkill + +import ( + "fmt" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "tcpkill", + Short: "杀掉那个TCP连接", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("windows on arm is not supported yet") + }, +} diff --git a/tcpkill/tcpkill.go b/tcpkill/tcpkill.go new file mode 100644 index 0000000..731c61a --- /dev/null +++ b/tcpkill/tcpkill.go @@ -0,0 +1,108 @@ +//go:build !(windows && arm64) + +package tcpkill + +import ( + "b612.me/bcap" + "b612.me/starlog" + "context" + "fmt" + netm "github.com/shirou/gopsutil/v4/net" + "net" + "runtime" +) + +func (t *TCPKill) PreRun() error { + if t.cap == nil { + t.cap = bcap.NewPackets() + t.ctx, t.stopFn = context.WithCancel(context.Background()) + } + t.requests = make(chan *handler, 1024) + if t.KillType == "" { + t.KillType = "both" + } + conns, err := netm.Connections("tcp") + if err != nil { + starlog.Errorf("Failed to get system connections:%v\n", err) + return err + } + starlog.Infof("Got %d connections\n", len(conns)) + for _, conn := range conns { + //fmt.Printf("Connection: %v => %v PID=%v Status=%v\n", conn.Laddr, conn.Raddr, conn.Pid, conn.Status) + if t.Match(conn) { + fmt.Printf("Matched connection: %v:%v => %v:%v PID=%v Status=%v\n", conn.Laddr.IP, conn.Laddr.Port, conn.Raddr.IP, conn.Raddr.Port, conn.Pid, conn.Status) + t.matchConns.Store(key(conn), conn) + t.matchCount++ + } + } + if t.matchCount == 0 && !t.WaitMode { + starlog.Warningln("No matched connection") + return fmt.Errorf("No matched connection") + } + starlog.Infof("Matched %d connections\n", t.matchCount) + return nil +} + +func (t *TCPKill) Match(info netm.ConnectionStat) bool { + if info.Status != "PCAP" { + if t.Status != "" && t.Status != info.Status { + return false + } + if info.Status == "DELETE" || info.Status == "CLOSED" || info.Status == "LISTEN" { + return false + } + if runtime.GOOS == "windows" && info.Status == "TIME_WAIT" { + return false + } + } + if _, ok := t.matchConns.Load(key(info)); ok { + return true + } + if t.SrcIP == "" && t.SrcPort == 0 && t.DstIP == "" && t.DstPort == 0 { + if t.Status != "" && info.Status != "PCAP" { + return true + } + return false + } + innerCheck := func(srcIP string, srcPort int, conns netm.Addr) bool { + sIp := net.ParseIP(srcIP) + if sIp == nil { + return false + } + lAddr := net.ParseIP(conns.IP) + if lAddr != nil { + if sIp.To16() != nil && lAddr.To16() != nil && !lAddr.To16().Equal(sIp.To16()) { + return false + } + if sIp.To4() != nil && lAddr.To4() != nil && !lAddr.To4().Equal(sIp.To4()) { + return false + } + if (sIp.To4() != nil && lAddr.To4() == nil) || (sIp.To4() == nil && lAddr.To4() != nil) { + return false + } + if srcPort != 0 && uint32(srcPort) != conns.Port { + return false + } + } + return true + } + if t.SrcIP != "" { + if !innerCheck(t.SrcIP, t.SrcPort, info.Laddr) { + return false + } + } else if t.SrcPort != 0 && t.SrcPort != int(info.Laddr.Port) { + return false + } + if t.DstIP != "" { + if !innerCheck(t.DstIP, t.DstPort, info.Raddr) { + return false + } + } else if t.DstPort != 0 && t.DstPort != int(info.Raddr.Port) { + return false + } + return true +} + +func key(i netm.ConnectionStat) string { + return fmt.Sprintf("tcp://%v:%v-%v:%v", i.Laddr.IP, i.Laddr.Port, i.Raddr.IP, i.Raddr.Port) +} diff --git a/tcpkill/tcpkill_unix.go b/tcpkill/tcpkill_unix.go new file mode 100644 index 0000000..bbc7371 --- /dev/null +++ b/tcpkill/tcpkill_unix.go @@ -0,0 +1,372 @@ +//go:build !windows + +package tcpkill + +import ( + "b612.me/bcap" + "b612.me/bcap/nfq" + "b612.me/starlog" + "context" + "fmt" + "github.com/florianl/go-nfqueue/v2" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + netm "github.com/shirou/gopsutil/v4/net" + "net" + "os" + "os/exec" + "os/signal" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +type TCPKill struct { + NFQNums int + AutoIptables bool + SrcIP string + SrcPort int + DstIP string + DstPort int + Status string + KillType string + RstNumbers int + WaitMode bool + matchConns sync.Map + matchCount uint + sync.Mutex + cache []string + cap *bcap.Packets + ctx context.Context + stopFn context.CancelFunc + requests chan *handler + BPF string + Eth string + Host string +} + +func (t *TCPKill) doIptables() error { + if t.SrcPort != 0 { + if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--dport", strconv.Itoa(t.SrcPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --dport "+strconv.Itoa(t.SrcPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--sport", strconv.Itoa(t.SrcPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --sport "+strconv.Itoa(t.SrcPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + return nil + } + if t.DstPort != 0 { + if _, err := exec.Command("iptables", "-t", "raw", "-A", "PREROUTING", "-p", "tcp", "--sport", strconv.Itoa(t.DstPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D PREROUTING -p tcp --sport "+strconv.Itoa(t.DstPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + if _, err := exec.Command("iptables", "-t", "raw", "-A", "OUTPUT", "-p", "tcp", "--dport", strconv.Itoa(t.DstPort), "-j", "NFQUEUE", "--queue-num", strconv.Itoa(t.NFQNums)).CombinedOutput(); err != nil { + return err + } + t.cache = append(t.cache, "-t raw -D OUTPUT -p tcp --dport "+strconv.Itoa(t.DstPort)+" -j NFQUEUE --queue-num "+strconv.Itoa(t.NFQNums)) + return nil + } + return fmt.Errorf("No src or dst port detect,it is too dangerous to set iptables automatically without port,please operate manually") +} + +func (t *TCPKill) undoIptables() { + for _, cmd := range t.cache { + exec.Command("iptables", strings.Fields(cmd)...).CombinedOutput() + } +} + +func (t *TCPKill) Run() error { + if err := t.PreRun(); err != nil { + return err + } + stopSignal := make(chan os.Signal) + starlog.Noticef("Starting nfqueue capture\n") + nf := nfq.NewNfQueue(t.ctx, uint16(t.NFQNums), 65535) + nf.SetRecall(t.handleRoute) + go func() { + if err := nf.Run(); err != nil { + starlog.Errorln(err) + stopSignal <- syscall.SIGKILL + } + }() + if t.AutoIptables { + if _, err := exec.Command("iptables", "-V").CombinedOutput(); err != nil { + starlog.Warningln("iptables not found, cannot auto set iptables") + return fmt.Errorf("iptables not found") + } + defer t.undoIptables() + if err := t.doIptables(); err != nil { + starlog.Errorf("Failed to set iptables:%v\n", err) + return err + } + starlog.Infof("Set iptables success\n") + } + signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) + go t.SynSent() + go t.handleNfResult() + select { + //todo need nf.Stop() + case <-stopSignal: + starlog.Warningf("Received stop signal\n") + + case <-t.ctx.Done(): + starlog.Infoln("TCPKILL Task Finished") + + } + return nil + +} + +func (t *TCPKill) handleNfResult() { + for { + select { + case <-t.ctx.Done(): + return + case info := <-t.requests: + if info == nil { + continue + } + info.p.SetVerdict(info.id, <-info.fin) + } + } +} + +func (t *TCPKill) handleRoute(id uint32, q *nfqueue.Nfqueue, p nfq.Packet) { + if t.requests == nil { + q.SetVerdict(id, nfqueue.NfAccept) + return + } + info, err := t.cap.ParsePacket(p.Packet) + if err != nil { + q.SetVerdict(id, nfqueue.NfAccept) + return + } + fin := make(chan int) + t.requests <- &handler{ + id: id, + p: q, + packet: p.Packet, + attr: p.Attr, + fin: fin, + } + go func() { + fin <- t.handlePacket(info, p) + }() +} + +type handler struct { + id uint32 + p *nfqueue.Nfqueue + packet gopacket.Packet + attr nfqueue.Attribute + fin chan int +} + +func (t *TCPKill) handlePacket(info bcap.PacketInfo, p nfq.Packet) int { + if p.Packet == nil { + return nfqueue.NfAccept + } + tcpLayer := p.Packet.Layer(layers.LayerTypeTCP) + if tcpLayer == nil { + return nfqueue.NfAccept + } + tcp := tcpLayer.(*layers.TCP) + if tcp.SYN && !tcp.ACK { + //starlog.Debugf("SYN packet:%v\n", p.Packet) + return nfqueue.NfAccept + } + if tcp.RST { + starlog.Warningf("RST packet:%v <==> %v\n", info.SrcIP+":"+info.SrcPort, info.DstIP+":"+info.DstPort) + return nfqueue.NfAccept + } + if !t.Match(netm.ConnectionStat{ + Status: "PCAP", + Laddr: netm.Addr{ + IP: info.SrcIP, + Port: uint32(tcp.SrcPort), + }, + Raddr: netm.Addr{ + IP: info.DstIP, + Port: uint32(tcp.DstPort), + }, + }) && !t.Match(netm.ConnectionStat{ + Status: "PCAP", + Raddr: netm.Addr{ + IP: info.SrcIP, + Port: uint32(tcp.SrcPort), + }, + Laddr: netm.Addr{ + IP: info.DstIP, + Port: uint32(tcp.DstPort), + }, + }) { + return nfqueue.NfAccept + } + RealSendRST(info, t.KillType, t.RstNumbers) + return nfqueue.NfAccept +} + +func (t *TCPKill) SynSent() { + for { + time.Sleep(time.Millisecond * 2500) + realConns, err := netm.Connections("tcp") + if err != nil { + continue + } + t.matchConns.Range(func(key, value any) bool { + t.matchConns.Delete(key) + t.matchCount-- + return true + }) + for _, conn := range realConns { + if t.Match(conn) { + t.matchConns.Store(key(conn), conn) + t.matchCount++ + } + } + if t.matchCount == 0 && !t.WaitMode { + starlog.Warningln("No matched connection anymore") + t.stopFn() + return + } + t.matchConns.Range(func(k, v any) bool { + conn := v.(netm.ConnectionStat) + if err := SendSYN(conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), 1127); err != nil { + starlog.Errorf("Failed to send SYN:%v\n", err) + } + if err := SendSYN(conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), 1127); err != nil { + starlog.Errorf("Failed to send SYN:%v\n", err) + } + starlog.Infof("Send SYN to %v <==> %v\n", conn.Laddr.String(), conn.Raddr.String()) + return true + }) + } +} + +func RealSendRST(info bcap.PacketInfo, target string, number int) { + for i := 0; i < number; i++ { + if target == "both" || target == "target" { + seq := uint32(info.TcpPayloads()) + info.TcpSeq() + (uint32(i) * uint32(info.TcpWindow())) + SendRST(info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, seq) + } + if target == "both" || target == "reverse" { + SendRST(info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + } + } +} + +func SendRST(srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, false) + } + return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, false) +} + +func SendSYN(srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(srcIP, srcPort, dstIP, dstPort, seq, true) + } + return sendIPv6(srcIP, srcPort, dstIP, dstPort, seq, true) +} + +func sendIPv4(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + if err != nil { + return err + } + defer syscall.Close(fd) + + dstNetIP := net.ParseIP(dstIP) + iPv4 := layers.IPv4{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil { + return err + } + addr := syscall.SockaddrInet4{ + Port: dPort, + Addr: [4]byte{dstNetIP.To4()[0], dstNetIP.To4()[1], dstNetIP.To4()[2], dstNetIP.To4()[3]}, + } + return syscall.Sendto(fd, buffer.Bytes(), 0, &addr) +} + +func sendIPv6(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + if err != nil { + return err + } + defer syscall.Close(fd) + + dstNetIP := net.ParseIP(dstIP) + iPv6 := layers.IPv6{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 6, + NextHeader: layers.IPProtocolTCP, + HopLimit: 64, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil { + return err + } + addr := syscall.SockaddrInet6{ + Port: dPort, + Addr: [16]byte(dstNetIP.To16()), + } + return syscall.Sendto(fd, buffer.Bytes(), 0, &addr) +} diff --git a/tcpkill/tcpkill_windows.go b/tcpkill/tcpkill_windows.go new file mode 100644 index 0000000..aa73071 --- /dev/null +++ b/tcpkill/tcpkill_windows.go @@ -0,0 +1,390 @@ +//go:build windows && (amd64 || 386) + +package tcpkill + +import ( + "b612.me/bcap" + "b612.me/bcap/libpcap" + "b612.me/starlog" + "context" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + netm "github.com/shirou/gopsutil/v4/net" + "net" + "os" + "os/signal" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +type TCPKill struct { + NFQNums int + AutoIptables bool + BPF string + Eth string + Host string + SrcIP string + SrcPort int + DstIP string + DstPort int + KillType string + RstNumbers int + WaitMode bool + Status string + matchConns sync.Map + matchCount uint + sync.Mutex + cache []string + cap *bcap.Packets + ctx context.Context + stopFn context.CancelFunc + requests chan *handler + pc *libpcap.NetCatch + macCache map[string]net.HardwareAddr +} + +type handler struct { +} + +func (t *TCPKill) presetWindows() { + t.macCache = make(map[string]net.HardwareAddr) + if t.BPF == "" { + if t.DstIP != "" { + t.BPF = fmt.Sprintf("tcp and host %s", t.DstIP) + } else if t.SrcIP != "" { + t.BPF = fmt.Sprintf("tcp and host %s", t.SrcIP) + } else if t.SrcPort != 0 { + t.BPF = fmt.Sprintf("tcp and port %d", t.SrcPort) + } else if t.DstPort != 0 { + t.BPF = fmt.Sprintf("tcp and port %d", t.DstPort) + } else { + t.BPF = "tcp" + } + } + if t.Eth == "" && t.Host == "" { + allDevice, err := pcap.FindAllDevs() + if err != nil { + starlog.Errorf("get pcap devices failed:%v\n", err) + return + } + if t.SrcIP == "" { + defer func() { + t.SrcIP = "" + }() + ip := t.DstIP + if ip == "" { + ip = "223.6.6.6" + } + if strings.Contains(ip, ":") { + ip = "[" + ip + "]" + } + l, err := net.Dial("udp", ip+":53") + if err == nil { + t.SrcIP = l.LocalAddr().(*net.UDPAddr).IP.String() + starlog.Infof("No eth or ip detected,use %s as source ip\n", t.SrcIP) + l.Close() + } else { + starlog.Errorf("Failed to get source ip:%v\n", err) + } + } + for _, v := range allDevice { + if t.SrcIP == "127.0.0.1" && v.Name == "\\Device\\NPF_Loopback" { + t.Eth = v.Name + return + } + for _, addr := range v.Addresses { + if addr.IP.String() == t.SrcIP { + t.Eth = v.Name + return + } + } + } + + } +} + +func (t *TCPKill) Run() error { + var err error + if err = t.PreRun(); err != nil { + return err + } + t.presetWindows() + stopSignal := make(chan os.Signal) + starlog.Noticef("Starting capture\n") + signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) + if t.Eth != "" { + t.pc, err = libpcap.NewCatchEth(t.Eth, t.BPF) + if err != nil { + starlog.Errorf("Failed to create pcap handle:%v\n", err) + return err + } + } + if t.Host != "" { + t.pc, err = libpcap.NewCatch(t.Host, t.BPF) + if err != nil { + starlog.Errorf("Failed to create pcap handle:%v\n", err) + return err + } + } + if t.pc == nil { + starlog.Errorf("No pcap handle\n") + return fmt.Errorf("No pcap handle") + } + t.pc.SetRecall(t.handlePacket) + go func() { + if err := t.pc.Run(); err != nil { + starlog.Errorln(err) + stopSignal <- syscall.SIGKILL + } + }() + go t.SynSent() + select { + //todo need nf.Stop() + case <-stopSignal: + starlog.Warningf("Received stop signal\n") + + case <-t.ctx.Done(): + starlog.Infoln("TCPKILL Task Finished") + + } + return nil + +} + +func (t *TCPKill) SynSent() { + for { + time.Sleep(time.Millisecond * 2500) + realConns, err := netm.Connections("tcp") + if err != nil { + continue + } + t.matchConns.Range(func(key, value any) bool { + t.matchConns.Delete(key) + t.matchCount-- + return true + }) + for _, conn := range realConns { + if t.Match(conn) { + t.matchConns.Store(key(conn), conn) + t.matchCount++ + } + } + if t.matchCount == 0 && !t.WaitMode { + starlog.Warningln("No matched connection anymore") + t.stopFn() + return + } + t.matchConns.Range(func(k, v any) bool { + conn := v.(netm.ConnectionStat) + if t.macCache[conn.Laddr.IP] == nil || t.macCache[conn.Raddr.IP] == nil { + target := conn.Raddr.IP + ":" + strconv.Itoa(int(conn.Raddr.Port)) + if strings.Contains(conn.Raddr.IP, ":") { + target = "[" + conn.Raddr.IP + "]:" + strconv.Itoa(int(conn.Raddr.Port)) + } + starlog.Warningf("No mac address for %v or %v,try send syn to %v\n", conn.Laddr.IP, conn.Raddr.IP, target) + tcpConn, err := net.DialTimeout("tcp", target, time.Millisecond*800) + if err == nil { + tcpConn.(*net.TCPConn).SetLinger(0) + tcpConn.Close() + } + time.Sleep(time.Millisecond * 600) + } + t.Lock() + if err := SendSYN(t.pc.Handle, t.macCache[conn.Laddr.IP], t.macCache[conn.Raddr.IP], conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), 1127); err != nil { + starlog.Errorf("Failed to send SYN:%v\n", err) + } + if err := SendSYN(t.pc.Handle, t.macCache[conn.Raddr.IP], t.macCache[conn.Laddr.IP], conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), 1127); err != nil { + starlog.Errorf("Failed to send SYN:%v\n", err) + } + t.Unlock() + starlog.Infof("Send SYN to %v <==> %v\n", conn.Laddr.String(), conn.Raddr.String()) + return true + }) + } +} + +func (t *TCPKill) handlePacket(p gopacket.Packet) { + t.Lock() + info, err := t.cap.ParsePacket(p) + t.Unlock() + if err != nil { + return + } + tcpLayer := p.Layer(layers.LayerTypeTCP) + if tcpLayer == nil { + return + } + tcp := tcpLayer.(*layers.TCP) + if tcp.SYN && !tcp.ACK { + return + } + t.Lock() + //fmt.Println(info.SrcIP, hex.EncodeToString(info.SrcMac), info.DstIP, hex.EncodeToString(info.DstMac)) + t.macCache[info.SrcIP] = info.SrcMac + t.macCache[info.DstIP] = info.DstMac + t.Unlock() + if tcp.RST { + starlog.Warningf("RST packet:%v <==> %v\n", info.SrcIP+":"+info.SrcPort, info.DstIP+":"+info.DstPort) + return + } + //starlog.Debugf("packet:%v <==> %v\n", info.SrcIP+":"+info.SrcPort, info.DstIP+":"+info.DstPort) + if !t.Match(netm.ConnectionStat{ + Status: "PCAP", + Laddr: netm.Addr{ + IP: info.SrcIP, + Port: uint32(tcp.SrcPort), + }, + Raddr: netm.Addr{ + IP: info.DstIP, + Port: uint32(tcp.DstPort), + }, + }) && !t.Match(netm.ConnectionStat{ + Status: "PCAP", + Raddr: netm.Addr{ + IP: info.SrcIP, + Port: uint32(tcp.SrcPort), + }, + Laddr: netm.Addr{ + IP: info.DstIP, + Port: uint32(tcp.DstPort), + }, + }) { + return + } + starlog.Noticef("Sending RST... %v:%v <==> %v:%v SEQ=%d ACK=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq(), info.TcpAck()) + RealSendRST(t.pc.Handle, info, t.KillType, t.RstNumbers) +} + +func RealSendRST(p *pcap.Handle, info bcap.PacketInfo, target string, number int) { + for i := 0; i < number; i++ { + if target == "both" || target == "target" { + seq := uint32(info.TcpPayloads()) + info.TcpSeq() + (uint32(i) * uint32(info.TcpWindow())) + SendRST(p, info.SrcMac, info.DstMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, seq) + //SendRST(p, info.DstMac, info.SrcMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow()))) + + } + if target == "both" || target == "reverse" { + SendRST(p, info.DstMac, info.SrcMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + //SendRST(p, info.SrcMac, info.DstMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) + } + } +} + +func SendRST(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) + } + return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) +} + +func SendSYN(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { + if net.ParseIP(dstIP).To4() != nil { + return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) + } + return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) +} + +func sendIPv4(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + + dstNetIP := net.ParseIP(dstIP) + eth := layers.Ethernet{ + SrcMAC: srcMac, + DstMAC: dstMac, + EthernetType: layers.EthernetTypeIPv4, + } + iPv4 := layers.IPv4{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + + if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if srcMac == nil && dstMac == nil { + if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil { + return err + } + } else { + if err = gopacket.SerializeLayers(buffer, options, ð, &iPv4, &tcp); err != nil { + return err + } + } + return p.WritePacketData(buffer.Bytes()) +} + +func sendIPv6(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { + dstNetIP := net.ParseIP(dstIP) + eth := layers.Ethernet{ + SrcMAC: srcMac, + DstMAC: dstMac, + EthernetType: layers.EthernetTypeIPv6, + } + iPv6 := layers.IPv6{ + SrcIP: net.ParseIP(srcIP), + DstIP: dstNetIP, + Version: 6, + NextHeader: layers.IPProtocolTCP, + HopLimit: 64, + } + sPort, err := strconv.Atoi(srcPort) + if err != nil { + return err + } + dPort, err := strconv.Atoi(dstPort) + if err != nil { + return err + } + tcp := layers.TCP{ + SrcPort: layers.TCPPort(sPort), + DstPort: layers.TCPPort(dPort), + Seq: seq, + RST: !isSyn, + SYN: isSyn, + } + if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil { + return err + } + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + if srcMac == nil && dstMac == nil { + if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil { + return err + } + } else { + if err = gopacket.SerializeLayers(buffer, options, ð, &iPv6, &tcp); err != nil { + return err + } + } + return p.WritePacketData(buffer.Bytes()) +} diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000..2a0ffce --- /dev/null +++ b/version/version.go @@ -0,0 +1,3 @@ +package version + +var Version = "2.1.0.beta.15"