更新hosts加速 github 訪問方案 程序代碼


參考:
國內加速訪問Github的辦法,超級簡單 - 擴展迷Extfans的文章 - 知乎
https://zhuanlan.zhihu.com/p/65154116

手動改來改去太麻煩了,於是寫了個 Go 程序來自動更新。

/*****************************************************************************
文件: github_hosts_speed.go
描述: 獲取 github 相關域名的 ip 地址
作者: solym ymwh@foxmail.com
版本: 2020.04.27
日期: 2020年4月27日
歷史:
*****************************************************************************/

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"

	json "github.com/json-iterator/go"
)

func httpGetRequest(url string) ([]byte, error) {
	// 創建一個請求
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	// 設置頭,模擬火狐瀏覽器
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0")
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Pragma", "no-cache")
	req.Header.Set("Cache-Control", "no-cache")

	// 執行請求結果
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	// 獲取返回結果
	data, err := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 || err != nil {
		return nil, fmt.Errorf("請求失敗:[%d] %v", resp.StatusCode, err)
	}
	return data, nil
}

func getPublicIP() (string, error) {
	// 通過訪問 http://pv.sohu.com/cityjson 來獲取
	//   var returnCitySN = {"cip": "111.196.240.67", "cid": "110000", "cname": "北京市"};

	data, err := httpGetRequest("http://pv.sohu.com/cityjson")
	if err != nil {
		return "", err
	}
	if i := bytes.IndexByte(data, '{'); i > 0 {
		data = data[i:]
		any := json.Get(data, "cip")
		return any.ToString(), nil
	} else {
		return "", fmt.Errorf("格式不正確:%s", string(data))
	}
}

func getIPAddressOfDomain(domain, myip string) (string, error) {
	/*
		// 通過 119.29.29.29 的 接口獲取最合適的 IP
		url := "http://119.29.29.29/d?dn=" + domain + "&ip=" + myip

		data, err := httpGetRequest(url)
		if err != nil {
			return "", err
		}
		// 可能會有多個 IP 返回,只要第一個
		// 185.199.109.154;185.199.110.154;185.199.111.154;185.199.108.154
		if i := bytes.IndexByte(data, ';'); i > 6 {
			data = data[0:i]
		}
		return string(data), nil
	*/

	// 通過 https://github.com.ipaddress.com/gist.github.com 獲取
	primarydomain := domain
	if domainpart := strings.Split(primarydomain, "."); len(domainpart) > 2 {
		primarydomain = domainpart[len(domainpart)-2] + "." + domainpart[len(domainpart)-1]
	}
	// if d1 := strings.LastIndex(primarydomain, "."); d1 > 0 {
	// 	if d1 := strings.LastIndex(primarydomain[:d1-1], "."); d1 > 0 {
	// 		primarydomain = primarydomain[d1+1:]
	// 	}
	// }
	url := "https://" + primarydomain + ".ipaddress.com/" + domain

	data, err := httpGetRequest(url)
	if err != nil {
		return "", err
	}
	// <a href="https://www.ipaddress.com/ipv4/140.82.112.4">140.82.112.4</a>
	sep := []byte("<a href=\"https://www.ipaddress.com/ipv4/")
	if i := bytes.Index(data, sep); i > 0 {
		data = data[i+len(sep):]
		if j := bytes.Index(data, []byte("\">")); j > 0 {
			return string(data[:j]), nil
		}
	}
	return "", fmt.Errorf("獲取IP失敗:%s", domain)
}

func main() {
	// 1、獲取本機的公網IP
	myip, err := getPublicIP()
	if err != nil {
		fmt.Println("獲取公網 IP 地址失敗:", err)
		return
	}
	// 2、獲取域名對應的IP地址
	domainlist := []string{
		"github.global.ssl.fastly.net",
		"github.com",
		"assets-cdn.github.com",
		"documentcloud.github.com",
		"gist.github.com",
		"help.github.com",
		"nodeload.github.com",
		"raw.github.com",
		"status.github.com",
		"training.github.com",
		"www.github.com",
		"avatars0.githubusercontent.com",
		"avatars1.githubusercontent.com",
		"github.githubassets.com",
		"github-production-release-asset-2e65be.s3.amazonaws.com",
		"api.github.com",
		"user-images.githubusercontent.com",
		"raw.githubusercontent.com",
		"gist.githubusercontent.com",
		"camo.githubusercontent.com",
		"avatars2.githubusercontent.com",
		"avatars3.githubusercontent.com",
		"avatars4.githubusercontent.com",
		"avatars5.githubusercontent.com",
		"avatars6.githubusercontent.com",
		"avatars8.githubusercontent.com",
		"github.io",
		"avatars7.githubusercontent.com",
	}
	// 域名IP映射表
	domaintoipaddr := make(map[string]string)
	for _, domain := range domainlist {
		ipaddr, err := getIPAddressOfDomain(domain, myip)
		if err != nil {
			fmt.Println("錯誤:", err, domain)
			continue
		}
		fmt.Println(ipaddr, domain)
		domaintoipaddr[domain] = ipaddr
	}
	// 3、更新 hosts 文件
	hostspath := "/etc/hosts"
	if runtime.GOOS == "windows" {
		// 這里應該先通過環境變量 windir 或 SystemRoot 獲取路徑
		hostspath = "C:/Windows/System32/drivers/etc/hosts"
	}
	// 讀取 hosts 文件
	hostsdata, err := ioutil.ReadFile(hostspath)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 逐行讀取並更新
	hostsreader := bufio.NewReader(bytes.NewReader(hostsdata))
	hostsbuffer := bytes.NewBuffer(nil)
	hostswriter := bufio.NewWriter(hostsbuffer)

	line, err := hostsreader.ReadSlice('\n')
	for ; err == nil; line, err = hostsreader.ReadSlice('\n') {
		// 分割為多個部分
		fields := bytes.Fields(line)
		// 可能是 # xxx xxxx
		//       127.0.0.1 localhost # xxxxx
		// 等多種形勢
		if len(fields) < 2 || fields[0][0] == byte('#') {
			hostswriter.Write(line)
			continue
		}
		// 如果域名存在,則替換
		domain := string(fields[1])
		if ipaddr, ok := domaintoipaddr[domain]; ok {
			fields[0] = []byte(ipaddr)
			line = bytes.Join(fields, []byte("  "))
			line = append(line, byte('\n'))
			// 將映射表內的刪除
			delete(domaintoipaddr, domain)
		}
		hostswriter.Write(line)
	}
	// 將不存在 Hosts 中的加入
	hostswriter.WriteString("\n# github speed\n")
	for domain := range domaintoipaddr {
		hostswriter.WriteString(fmt.Sprintln(domaintoipaddr[domain], domain))
	}
	hostswriter.Flush()
	// 寫入到 hosts 文件,這里應該需要管理員權限
	err = ioutil.WriteFile(hostspath, hostsbuffer.Bytes(), os.ModePerm)
	if err != nil {
		fmt.Println("寫入更新失敗:", err)
		return
	}

	// 還需要刷新下 DNS 緩存,這里就直接給個提示好了
	fmt.Println("更新 hosts 文件完成,需要刷新 DNS 緩存請參考下面命令")
	fmt.Println("Windows 請執行命令: ipconfig /flushdns")
	fmt.Println("Linux   請執行命令: sudo service nscd restart")
	fmt.Println("               或: sudo service restart")
	fmt.Println("               或: sudo systemctl restart network")
	fmt.Println("               或: sudo /etc/init.d/networking restart")
	fmt.Println("MacOS   請執行命令: sudo killall -HUP mDNSResponder")
	fmt.Println("         Yosemite: sudo discoveryutil udnsflushcaches")
	fmt.Println("     Snow Leopard: sudo dscacheutil -flushcache")
	fmt.Println("Leopard and below: sudo lookupd -flushcache")
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM