K7

K7Blog

须知少年凌云志 曾许人间第一流.
proton
telegram

Continuing from the last time: Scan the reverse proxy Cloudflare's IP to accelerate websites or nodes and create your own CDN acceleration for free.

Preface:#

Before reading this article, please check out: Scan and Proxy Cloudflare's IP to Accelerate Websites or Nodes, Free to Create Your Own CDN Acceleration

This article serves as a follow-up and a solution to common questions. If you only want to accelerate nodes, please directly watch the video below:

If you want to accelerate a website and have encountered some issues that prevent successful acceleration, slow speeds, or inability to access the website, please continue reading.

Possible Issues + Solutions:#

First, you have found some proxy Cloudflare IPs and resolved these IPs through the tutorial in the previous article. When you try to access them, you may encounter certificate errors or access issues. In this case, ignoring the certificate error may lead you to someone else's website.

This indicates that the 443 port of this IP is not proxied to Cloudflare. Generally, the domain for the certificate content on port 443 should be: cloudflare-dns.com

IP Detection:#

I discovered this issue and had AI write a Go code that can filter out nodes with a normal 443 port. Copy the code below to create a Go file, then create an ip.txt and 443.txt. Write the IP nodes you need to check in ip.txt, and the valid IPs will be written to 443.txt.

package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

func main() {
	// Open ip.txt file
	file, err := os.Open("ip.txt")
	if err != nil {
		fmt.Println("Unable to open file:", err)
		return
	}
	defer file.Close()

	// Create a file to write valid IP addresses
	outFile, err := os.OpenFile("443.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("Unable to create file:", err)
		return
	}
	defer outFile.Close()

	// Create a bufio.Scanner to read file content line by line
	scanner := bufio.NewScanner(file)
	tasks := make(chan string, 5) // Concurrent task channel
	results := make(chan string)  // Result channel
	var wg sync.WaitGroup         // Wait for all tasks to complete

	// Start concurrent task processing
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go handleTask(tasks, results, &wg)
	}

	// Read file content line by line and send to concurrent task channel
	for scanner.Scan() {
		ip := scanner.Text()
		tasks <- ip
	}

	// Close concurrent task channel and wait for all tasks to complete
	close(tasks)
	go func() {
		wg.Wait()
		close(results)
	}()

	// Read valid IP addresses from result channel and write to file
	for ip := range results {
		outFile.WriteString(ip + "\n")
	}

	if err := scanner.Err(); err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	fmt.Println("Processing complete, valid IPs have been appended to 443.txt")
}

// Function to handle concurrent tasks
func handleTask(tasks <-chan string, results chan<- string, wg *sync.WaitGroup) {
	defer wg.Done()
	for ip := range tasks {
		// Check if the 443 port of the IP is accessible, timeout set to 5 seconds
		if isPortOpenWithTimeout(ip, "443", 5*time.Second) {
			// If the 443 port is accessible, continue to check if the certificate is from cloudflare-dns.com
			if checkCertificate(ip, "cloudflare-dns.com") {
				// If the certificate is from cloudflare-dns.com, write to result channel
				fmt.Println(ip, "valid")
				results <- ip
			} else {
				fmt.Println(ip, "invalid, certificate is not from cloudflare-dns.com")
			}
		} else {
			fmt.Println(ip, "invalid, 443 port is inaccessible or timed out")
		}
	}
}

// Function to check if the specified IP and port are accessible, with a timeout
func isPortOpenWithTimeout(ip, port string, timeout time.Duration) bool {
	conn, err := net.DialTimeout("tcp", ip+":"+port, timeout)
	if err != nil {
		return false
	}
	defer conn.Close()
	return true
}

// Function to check if the certificate of the specified IP is from the specified domain
func checkCertificate(ip, domain string) bool {
	config := tls.Config{ServerName: domain}
	conn, err := tls.Dial("tcp", ip+":443", &config)
	if err != nil {
		return false
	}
	defer conn.Close()

	// Get the certificate
	certs := conn.ConnectionState().PeerCertificates
	if len(certs) < 1 {
		return false
	}

	// Check if the certificate matches the specified domain
	for _, cert := range certs {
		if strings.Contains(cert.Subject.CommonName, domain) || cert.VerifyHostname(domain) == nil {
			return true
		}
	}
	return false
}

Another issue is that some IPs can access port 80 and port 443, but the 443 port is blocked by a firewall. In this case, if port 80 cannot be used but port 443 can be used, using such IPs for acceleration will cause http://k7blog.com to be inaccessible and cannot redirect to https. Everyone needs to put in some effort to find IPs where both 80 and 443 are available.

IP Speed Test:#

If you want to use it, you need to tolerate unstable speeds and the possibility of being unavailable at any time, as some people may use the acceleration nodes to run 4k or 8k videos or download tasks. Moreover, some nodes are inherently slow, which means we will experience reverse acceleration. Therefore, I had AI write a Go code that is divided into jpg speed testing and zip speed testing.

Snipaste_2024-04-30_14-42-17

JPG speed test (please use a Windows computer, or modify the corresponding hosts directory yourself):

Change img.a8tool.com and https://img.k7blog.com/i/2024/04/30/loux3b.jpg to your own. The jpg file should be around 5MB or more. Create a dns.txt file in the same directory as the Go program and write the speed test node IPs.

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

// backupHostsFilePath stores the backup path of the hosts file
var backupHostsFilePath = "C:\\Windows\\System32\\drivers\\etc\\hosts.backup"

func main() {
	// Open DNS file
	dnsFile, err := os.Open("dns.txt")
	if err != nil {
		log.Fatalf("Unable to open DNS file: %v", err)
	}
	defer dnsFile.Close()

	// Read IPs from DNS file
	ips, err := ioutil.ReadAll(dnsFile)
	if err != nil {
		log.Fatalf("Unable to read DNS file: %v", err)
	}

	// Create log file
	logFile, err := os.OpenFile("dns.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("Unable to create log file: %v", err)
	}
	defer logFile.Close()

	// Backup original hosts file
	err = backupHostsFile()
	if err != nil {
		log.Fatalf("Failed to backup hosts file: %v", err)
	}

	// Iterate over IPs line by line
	ipList := strings.Split(string(ips), "\n")
	for _, ip := range ipList {
		ip = strings.TrimSpace(ip)
		if ip == "" {
			continue
		}

		// Check if the current IP is already the target IP
		if !checkHostIP("img.k7blog.com", ip) {
			// Modify local hosts file
			if err := modifyHosts("img.k7blog.com", ip); err != nil {
				log.Printf("Failed to modify hosts file: %v", err)
				continue
			}

			// Refresh DNS cache
			err := flushDNSCache()
			if err != nil {
				log.Printf("Failed to refresh DNS cache: %v", err)
			}

			// Wait for a while to ensure the hosts file takes effect
			time.Sleep(5 * time.Second)

			// Check again if the current IP is the target IP
			if !checkHostIP("img.k7blog.com", ip) {
				log.Printf("Domain does not point to target IP: %s", ip)
				continue
			}
		}

		// Delete old image file
		err := deleteFile("downloaded_image.jpg")
		if err != nil {
			log.Printf("Failed to delete old image file: %v", err)
		}

		// Download image
		start := time.Now()
		_, err = downloadFile("https://img.k7blog.com/i/2024/04/30/loux3b.jpg", "downloaded_image.jpg")
		if err != nil {
			log.Printf("Failed to download image: %v", err)
			continue
		}
		duration := time.Since(start)

		// Record download time and speed
		speed := float64(fileSize("downloaded_image.jpg")) / duration.Seconds() / 1024 // KB/s

		// Print log information and write to log file
		logMsg := fmt.Sprintf("IP: %s, Download Time: %v, Download Speed: %.2f KB/s", ip, duration, speed)
		fmt.Println(logMsg)
		if _, err := logFile.WriteString(logMsg + "\n"); err != nil {
			log.Printf("Failed to write to log file: %v", err)
		}

		// Restore original hosts file
		err = restoreHostsFile()
		if err != nil {
			log.Fatalf("Failed to restore hosts file: %v", err)
		}
	}
}

// Modify local hosts file
func modifyHosts(hostname, ip string) error {
	hostsFilePath := "C:\\Windows\\System32\\drivers\\etc\\hosts"
	hostsFile, err := os.OpenFile(hostsFilePath, os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer hostsFile.Close()

	_, err = hostsFile.WriteString(fmt.Sprintf("%s %s\n", ip, hostname))
	if err != nil {
		return err
	}

	return nil
}

// Download file
func downloadFile(url, filename string) (int64, error) {
	resp, err := http.Get(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	file, err := os.Create(filename)
	if err != nil {
		return 0, err
	}
	defer file.Close()

	size, err := io.Copy(file, resp.Body)
	if err != nil {
		return 0, err
	}

	return size, nil
}

// Get file size
func fileSize(filename string) int64 {
	info, err := os.Stat(filename)
	if err != nil {
		return 0
	}
	return info.Size()
}

// Backup hosts file
func backupHostsFile() error {
	src, err := os.Open("C:\\Windows\\System32\\drivers\\etc\\hosts")
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.Create(backupHostsFilePath)
	if err != nil {
		return err
	}
	defer dst.Close()

	_, err = io.Copy(dst, src)
	if err != nil {
		return err
	}

	return nil
}

// Restore hosts file
func restoreHostsFile() error {
	src, err := os.Open(backupHostsFilePath)
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.Create("C:\\Windows\\System32\\drivers\\etc\\hosts")
	if err != nil {
		return err
	}
	defer dst.Close()

	_, err = io.Copy(dst, src)
	if err != nil {
		return err
	}

	return nil
}

// Refresh DNS cache
func flushDNSCache() error {
	cmd := exec.Command("ipconfig", "/flushdns")
	err := cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

// Delete file
func deleteFile(filename string) error {
	err := os.Remove(filename)
	if err != nil {
		return err
	}
	return nil
}

// Check if the domain points to the target IP
func checkHostIP(hostname, expectedIP string) bool {
	addrs, err := net.LookupIP(hostname)
	if err != nil {
		log.Printf("Unable to resolve domain: %s", err)
		return false
	}
	for _, addr := range addrs {
		if addr.String() == expectedIP {
			return true
		}
	}
	return false
}

Zip speed test (please use a Windows computer, or modify the corresponding hosts directory yourself):

Change img.a8tool.com and https://img.k7blog.com/i/2024i.zip to your own. The zip file should be around 10MB or more. Create a dns.txt file in the same directory as the Go program and write the speed test node IPs.

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

// backupHostsFilePath stores the backup path of the hosts file
var backupHostsFilePath = "C:\\Windows\\System32\\drivers\\etc\\hosts.backup"

func main() {
	// Open DNS file
	dnsFile, err := os.Open("dns.txt")
	if err != nil {
		log.Fatalf("Unable to open DNS file: %v", err)
	}
	defer dnsFile.Close()

	// Read IPs from DNS file
	ips, err := ioutil.ReadAll(dnsFile)
	if err != nil {
		log.Fatalf("Unable to read DNS file: %v", err)
	}

	// Create log file
	logFile, err := os.OpenFile("dns.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("Unable to create log file: %v", err)
	}
	defer logFile.Close()

	// Backup original hosts file
	err = backupHostsFile()
	if err != nil {
		log.Fatalf("Failed to backup hosts file: %v", err)
	}

	// Iterate over IPs line by line
	ipList := strings.Split(string(ips), "\n")
	for _, ip := range ipList {
		ip = strings.TrimSpace(ip)
		if ip == "" {
			continue
		}

		// Check if the current IP is already the target IP
		if !checkHostIP("img.k7blog.com", ip) {
			// Modify local hosts file
			if err := modifyHosts("img.k7blog.com", ip); err != nil {
				log.Printf("Failed to modify hosts file: %v", err)
				continue
			}

			// Refresh DNS cache
			err := flushDNSCache()
			if err != nil {
				log.Printf("Failed to refresh DNS cache: %v", err)
			}

			// Wait for a while to ensure the hosts file takes effect
			time.Sleep(5 * time.Second)

			// Check again if the current IP is the target IP
			if !checkHostIP("img.k7blog.com", ip) {
				log.Printf("Domain does not point to target IP: %s", ip)
				continue
			}
		}

		// Delete old zip file
		err := deleteFile("downloaded_file.zip")
		if err != nil {
			log.Printf("Failed to delete old zip file: %v", err)
		}

		// Download zip file
		start := time.Now()
		size, err := downloadFile("https://img.k7blog.com/i/2024i.zip", "downloaded_file.zip")
		if err != nil {
			log.Printf("Failed to download zip file: %v", err)
			continue
		}
		duration := time.Since(start)

		// Calculate download speed
		speed := float64(size) / duration.Seconds() / 1024 // KB/s

		// Print log information and write to log file
		logMsg := fmt.Sprintf("IP: %s, Download Time: %v, Download Speed: %.2f KB/s", ip, duration, speed)
		fmt.Println(logMsg)
		if _, err := logFile.WriteString(logMsg + "\n"); err != nil {
			log.Printf("Failed to write to log file: %v", err)
		}

		// Restore original hosts file
		err = restoreHostsFile()
		if err != nil {
			log.Fatalf("Failed to restore hosts file: %v", err)
		}
	}
}

// Modify local hosts file
func modifyHosts(hostname, ip string) error {
	hostsFilePath := "C:\\Windows\\System32\\drivers\\etc\\hosts"
	hostsFile, err := os.OpenFile(hostsFilePath, os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer hostsFile.Close()

	_, err = hostsFile.WriteString(fmt.Sprintf("%s %s\n", ip, hostname))
	if err != nil {
		return err
	}

	return nil
}

// Download file
func downloadFile(url, filename string) (int64, error) {
	resp, err := http.Get(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	file, err := os.Create(filename)
	if err != nil {
		return 0, err
	}
	defer file.Close()

	size, err := io.Copy(file, resp.Body)
	if err != nil {
		return 0, err
	}

	return size, nil
}

// Backup hosts file
func backupHostsFile() error {
	src, err := os.Open("C:\\Windows\\System32\\drivers\\etc\\hosts")
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.Create(backupHostsFilePath)
	if err != nil {
		return err
	}
	defer dst.Close()

	_, err = io.Copy(dst, src)
	if err != nil {
		return err
	}

	return nil
}

// Restore hosts file
func restoreHostsFile() error {
	src, err := os.Open(backupHostsFilePath)
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.Create("C:\\Windows\\System32\\drivers\\etc\\hosts")
	if err != nil {
		return err
	}
	defer dst.Close()

	_, err = io.Copy(dst, src)
	if err != nil {
		return err
	}

	return nil
}

// Refresh DNS cache
func flushDNSCache() error {
	cmd := exec.Command("ipconfig", "/flushdns")
	err := cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

// Delete file
func deleteFile(filename string) error {
	err := os.Remove(filename)
	if err != nil {
		return err
	}
	return nil
}

// Check if the domain points to the target IP
func checkHostIP(hostname, expectedIP string) bool {
	addrs, err := net.LookupIP(hostname)
	if err != nil {
		log.Printf("Unable to resolve domain: %s", err)
		return false
	}
	for _, addr := range addrs {
		if addr.String() == expectedIP {
			return true
		}
	}
	return false
}

Of course, you can also use the code without modification, and it will still test a basic speed, but modifying it to your own will reflect the actual speed. It is recommended to use a domestic network without a proxy for testing, as you need acceleration to mainland China, and this way you can also know what is unavailable in mainland China.

In the future, when I have time, I will look for AI or other solutions to create a failover method, such as directly deleting resolutions for unavailable nodes. Lastly, I want to remind everyone that you should not directly resolve the accelerated domain to the node IP, because if you change the resolution, you will need to re-verify the hostname. Instead, just add a new resolution that only resolves the node IP, and then add the accelerated domain to this resolution.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.