更新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('
')
	for ; err == nil; line, err = hostsreader.ReadSlice('
') {
		// 分割为多个部分
		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('
'))
			// 将映射表内的删除
			delete(domaintoipaddr, domain)
		}
		hostswriter.Write(line)
	}
	// 将不存在 Hosts 中的加入
	hostswriter.WriteString("
# github speed
")
	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")
}
原文地址:https://www.cnblogs.com/oloroso/p/12789641.html