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

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

html

手動改來改去太麻煩了,因而寫了個 Go 程序來自動更新。node

/*****************************************************************************
文件: 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
}

func main() {
	// 一、獲取本機的公網IP
	myip, err := getPublicIP()
	if err != nil {
		fmt.Println("獲取公網 IP 地址失敗:", err)
		return
	}
	// 二、獲取域名對應的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
	}
	// 三、更新 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("  "))
			// 將映射表內的刪除
			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")
}
相關文章
相關標籤/搜索