Created
July 24, 2023 18:01
-
-
Save charSLee013/8558d265914a5d8af26ca6687f4756c9 to your computer and use it in GitHub Desktop.
Websocket with SSL/TLS checker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"context" | |
"crypto/rand" | |
"encoding/base64" | |
"errors" | |
"flag" | |
"fmt" | |
"log" | |
"net" | |
"net/http" | |
"os" | |
"runtime" | |
"runtime/pprof" | |
"strings" | |
"sync" | |
"time" | |
_ "net/http/pprof" | |
) | |
var ( | |
concurrent chan struct{} // concurrent request limiter | |
ip, port, file, host string | |
num int | |
debug bool | |
headers http.Header | |
ips chan string | |
) | |
func main() { | |
// Parse command line flags | |
flag.StringVar(&ip, "ip", "", "Target IP address") | |
flag.StringVar(&port, "port", "", "Target port") | |
flag.StringVar(&file, "file", "", "File containing IP addresses") | |
flag.IntVar(&num, "num", 200, "Number of concurrent requests") | |
flag.StringVar(&host, "host", "", "Target domain name") | |
flag.BoolVar(&debug, "debug", false, "Enable debug mode") | |
flag.Parse() | |
// Enable debugging if specified | |
if debug {package main | |
import ( | |
"bufio" | |
"context" | |
"crypto/rand" | |
"encoding/base64" | |
"errors" | |
"flag" | |
"fmt" | |
"log" | |
"net" | |
"net/http" | |
"os" | |
"runtime" | |
"runtime/pprof" | |
"strings" | |
"sync" | |
"time" | |
_ "net/http/pprof" | |
) | |
var ( | |
concurrent chan struct{} // concurrent request limiter | |
ip, port, file, host string | |
num int | |
debug bool | |
headers http.Header | |
ips chan string | |
) | |
func main() { | |
// Parse command line flags | |
flag.StringVar(&ip, "ip", "", "Target IP address") | |
flag.StringVar(&port, "port", "", "Target port") | |
flag.StringVar(&file, "file", "", "File containing IP addresses") | |
flag.IntVar(&num, "num", 200, "Number of concurrent requests") | |
flag.StringVar(&host, "host", "", "Target domain name") | |
flag.BoolVar(&debug, "debug", false, "Enable debug mode") | |
flag.Parse() | |
// Enable debugging if specified | |
if debug { | |
log.Println("Enable debug model") | |
go func() { log.Println(http.ListenAndServe("127.0.0.1:30080", nil)) }() | |
// Start CPU profiling | |
f, err := os.OpenFile("cpu.prof", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer f.Close() | |
if err := pprof.StartCPUProfile(f); err != nil { | |
log.Fatal(err) | |
} | |
defer pprof.StopCPUProfile() | |
// Write heap profile | |
f, err = os.OpenFile("mem.prof", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer f.Close() | |
runtime.GC() // get up-to-date statistics | |
if err := pprof.WriteHeapProfile(f); err != nil { | |
log.Fatal(err) | |
} | |
} | |
// Initialize concurrent request limiter | |
concurrent = make(chan struct{}, num) | |
for i := 0; i < num; i++ { | |
concurrent <- struct{}{} | |
} | |
ips = make(chan string, num) | |
// Validate required parameters | |
if ip == "" && file == "" { | |
fmt.Println("Please specify the IP address or provide a file containing IP addresses.") | |
fmt.Println("Usage: go run main.go -ip <IP> -port <Port> -file <FileName> -num <Number> -host <HostName>") | |
return | |
} | |
if host == "" { | |
fmt.Println("Please specify the target domain name.") | |
fmt.Println("Usage: go run main.go -ip <IP> -port <Port> -file <FileName> -num <Number> -host <HostName>") | |
return | |
} | |
// Construct request headers | |
headers = http.Header{ | |
"Cache-Control": {"no-cache"}, | |
"Pragma": {"no-cache"}, | |
"Accept-Encoding": {"gzip, deflate, br"}, | |
"Accept-Language": {"zh-CN;q=0.9,zh;"}, | |
"User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Wine; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/115.0.1901.183"}, | |
"Sec-WebSocket-Extensions": {"permessage-deflate; client_max_window_bits"}, | |
"Sec-WebSocket-Version": {"13"}, | |
"Connection": {"Upgrade"}, | |
"Upgrade": {"websocket"}, | |
"Host": {host}, | |
} | |
key, err := generateKey() | |
if err != nil { | |
log.Fatalln("Failed to generate key:", err) | |
} | |
headers.Set("Sec-WebSocket-Key", key) | |
var wg sync.WaitGroup // Wait group for concurrent requests | |
// executable task | |
go func() { | |
for { | |
addr := <-ips | |
wg.Add(1) | |
go func(addr string) { | |
defer func() { | |
concurrent <- struct{}{} | |
}() | |
defer wg.Done() | |
sendRequest(addr) | |
}(addr) | |
} | |
}() | |
if file != "" { | |
// wait for scan done | |
wg.Add(1) | |
f, err := os.Open(file) | |
if err != nil { | |
// Skip file if error | |
goto check_ip | |
} | |
defer f.Close() | |
scanner := bufio.NewScanner(f) | |
var ip string | |
for scanner.Scan() { | |
ip = strings.TrimSpace(scanner.Text()) | |
if ip != "" { | |
err := readIPsFromString(ip) | |
if err != nil { | |
continue | |
} | |
} | |
ip = "" | |
runtime.GC() | |
} | |
// scan done | |
wg.Done() | |
} | |
check_ip: | |
if ip != "" { | |
wg.Add(1) | |
go func() { | |
defer wg.Done() | |
sendRequest(ip) | |
}() | |
} | |
wg.Wait() // Wait for all requests to complete | |
fmt.Println("All requests completed.") | |
} | |
// Generate HTTP client with customized transport | |
func generateClient(host, ip, port string) http.Client { | |
// Set timeouts | |
dialer := &net.Dialer{ | |
Timeout: 10 * time.Second, | |
KeepAlive: 10 * time.Second, | |
DualStack: true, | |
} | |
transport := &http.Transport{ | |
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { | |
if addr == host+":"+port { | |
addr = ip + ":" + port // Override host with target IP | |
} | |
return dialer.DialContext(ctx, network, addr) | |
}, | |
MaxIdleConns: 100, | |
IdleConnTimeout: 10 * time.Second, | |
TLSHandshakeTimeout: 5 * time.Second, | |
ExpectContinueTimeout: 3 * time.Second, | |
} | |
client := &http.Client{ | |
Transport: transport, | |
CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
// Inherit headers from last request | |
req.Header = via[len(via)-1].Header | |
return nil | |
}, | |
} | |
return *client | |
} | |
// Generate random key for WebSocket handshake | |
func generateKey() (string, error) { | |
key := make([]byte, 16) | |
_, err := rand.Read(key) | |
if err != nil { | |
return "", err | |
} | |
return base64.StdEncoding.EncodeToString(key), nil | |
} | |
// Parse IP address strings | |
func readIPsFromString(ip string) error { | |
if strings.Contains(ip, "/") { | |
err := expandCIDR(ip) | |
if err != nil { | |
return err | |
} | |
} else if isValidIP(ip) { | |
// Limit concurrency | |
<-concurrent | |
ips <- ip | |
} else { | |
return errors.New("Invalid IP address") | |
} | |
return nil | |
} | |
// Validate IP address | |
func isValidIP(ip string) bool { | |
myIP := net.ParseIP(ip) | |
return myIP != nil | |
} | |
// Expand CIDR to list of IPs | |
func expandCIDR(cidr string) error { | |
ip, ipnet, err := net.ParseCIDR(cidr) | |
if err != nil { | |
return err | |
} | |
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { | |
<-concurrent | |
ips <- ip.String() | |
} | |
return nil | |
} | |
// Increment IP address | |
func inc(ip net.IP) { | |
for j := len(ip) - 1; j >= 0; j-- { | |
ip[j]++ | |
if ip[j] > 0 { | |
break | |
} | |
} | |
} | |
// Send WebSocket handshake request | |
func sendRequest(ip string) { | |
req, _ := http.NewRequest("GET", "https://"+host+":"+port, nil) | |
req.Header = headers | |
client := generateClient(host, ip, port) | |
resp, err := client.Do(req) | |
if err != nil { | |
return | |
} | |
if resp.StatusCode == http.StatusSwitchingProtocols { | |
fmt.Println(ip) // Print IP if handshake successful | |
} | |
_ = resp.Body.Close() | |
} | |
log.Println("Enable debug model") | |
go func() { log.Println(http.ListenAndServe("127.0.0.1:30080", nil)) }() | |
// Start CPU profiling | |
f, err := os.OpenFile("cpu.prof", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer f.Close() | |
if err := pprof.StartCPUProfile(f); err != nil { | |
log.Fatal(err) | |
} | |
defer pprof.StopCPUProfile() | |
// Write heap profile | |
f, err = os.OpenFile("mem.prof", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer f.Close() | |
runtime.GC() // get up-to-date statistics | |
if err := pprof.WriteHeapProfile(f); err != nil { | |
log.Fatal(err) | |
} | |
} | |
// Initialize concurrent request limiter | |
concurrent = make(chan struct{}, num) | |
for i := 0; i < num; i++ { | |
concurrent <- struct{}{} | |
} | |
ips = make(chan string, num) | |
// Validate required parameters | |
if ip == "" && file == "" { | |
fmt.Println("Please specify the IP address or provide a file containing IP addresses.") | |
fmt.Println("Usage: go run main.go -ip <IP> -port <Port> -file <FileName> -num <Number> -host <HostName>") | |
return | |
} | |
if host == "" { | |
fmt.Println("Please specify the target domain name.") | |
fmt.Println("Usage: go run main.go -ip <IP> -port <Port> -file <FileName> -num <Number> -host <HostName>") | |
return | |
} | |
// Construct request headers | |
headers = http.Header{ | |
"Cache-Control": {"no-cache"}, | |
"Pragma": {"no-cache"}, | |
"Accept-Encoding": {"gzip, deflate, br"}, | |
"Accept-Language": {"zh-CN;q=0.9,zh;"}, | |
"User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Wine; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/115.0.1901.183"}, | |
"Sec-WebSocket-Extensions": {"permessage-deflate; client_max_window_bits"}, | |
"Sec-WebSocket-Version": {"13"}, | |
"Connection": {"Upgrade"}, | |
"Upgrade": {"websocket"}, | |
"Host": {host}, | |
} | |
key, err := generateKey() | |
if err != nil { | |
log.Fatalln("Failed to generate key:", err) | |
} | |
headers.Set("Sec-WebSocket-Key", key) | |
var wg sync.WaitGroup // Wait group for concurrent requests | |
// executable task | |
go func() { | |
for { | |
addr := <-ips | |
wg.Add(1) | |
go func(addr string) { | |
defer func() { | |
concurrent <- struct{}{} | |
}() | |
defer wg.Done() | |
sendRequest(addr) | |
}(addr) | |
} | |
}() | |
if file != "" { | |
// wait for scan done | |
wg.Add(1) | |
f, err := os.Open(file) | |
if err != nil { | |
// Skip file if error | |
goto check_ip | |
} | |
defer f.Close() | |
scanner := bufio.NewScanner(f) | |
var ip string | |
for scanner.Scan() { | |
ip = strings.TrimSpace(scanner.Text()) | |
if ip != "" { | |
err := readIPsFromString(ip) | |
if err != nil { | |
continue | |
} | |
} | |
ip = "" | |
runtime.GC() | |
} | |
// scan done | |
wg.Done() | |
} | |
check_ip: | |
if ip != "" { | |
wg.Add(1) | |
go func() { | |
defer wg.Done() | |
sendRequest(ip) | |
}() | |
} | |
wg.Wait() // Wait for all requests to complete | |
fmt.Println("All requests completed.") | |
} | |
// Generate HTTP client with customized transport | |
func generateClient(host, ip, port string) http.Client { | |
// Set timeouts | |
dialer := &net.Dialer{ | |
Timeout: 10 * time.Second, | |
KeepAlive: 10 * time.Second, | |
DualStack: true, | |
} | |
transport := &http.Transport{ | |
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { | |
if addr == host+":"+port { | |
addr = ip + ":" + port // Override host with target IP | |
} | |
return dialer.DialContext(ctx, network, addr) | |
}, | |
MaxIdleConns: 100, | |
IdleConnTimeout: 10 * time.Second, | |
TLSHandshakeTimeout: 5 * time.Second, | |
ExpectContinueTimeout: 3 * time.Second, | |
} | |
client := &http.Client{ | |
Transport: transport, | |
CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
// Inherit headers from last request | |
req.Header = via[len(via)-1].Header | |
return nil | |
}, | |
} | |
return *client | |
} | |
// Generate random key for WebSocket handshake | |
func generateKey() (string, error) { | |
key := make([]byte, 16) | |
_, err := rand.Read(key) | |
if err != nil { | |
return "", err | |
} | |
return base64.StdEncoding.EncodeToString(key), nil | |
} | |
// Parse IP address strings | |
func readIPsFromString(ip string) error { | |
if strings.Contains(ip, "/") { | |
err := expandCIDR(ip) | |
if err != nil { | |
return err | |
} | |
} else if isValidIP(ip) { | |
// Limit concurrency | |
<-concurrent | |
ips <- ip | |
} else { | |
return errors.New("Invalid IP address") | |
} | |
return nil | |
} | |
// Validate IP address | |
func isValidIP(ip string) bool { | |
myIP := net.ParseIP(ip) | |
return myIP != nil | |
} | |
// Expand CIDR to list of IPs | |
func expandCIDR(cidr string) error { | |
ip, ipnet, err := net.ParseCIDR(cidr) | |
if err != nil { | |
return err | |
} | |
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { | |
<-concurrent | |
ips <- ip.String() | |
} | |
return nil | |
} | |
// Increment IP address | |
func inc(ip net.IP) { | |
for j := len(ip) - 1; j >= 0; j-- { | |
ip[j]++ | |
if ip[j] > 0 { | |
break | |
} | |
} | |
} | |
// Send WebSocket handshake request | |
func sendRequest(ip string) { | |
req, _ := http.NewRequest("GET", "https://"+host+":"+port, nil) | |
req.Header = headers | |
client := generateClient(host, ip, port) | |
resp, err := client.Do(req) | |
if err != nil { | |
return | |
} | |
if resp.StatusCode == http.StatusSwitchingProtocols { | |
fmt.Println(ip) // Print IP if handshake successful | |
} | |
_ = resp.Body.Close() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment