Skip to content

Instantly share code, notes, and snippets.

@sotex
Created April 27, 2020 13:38
Show Gist options
  • Save sotex/73201e0c3ee9267ad637a07ee13d2919 to your computer and use it in GitHub Desktop.
Save sotex/73201e0c3ee9267ad637a07ee13d2919 to your computer and use it in GitHub Desktop.
自动更新hosts加速github访问
/*****************************************************************************
文件: 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() {
// 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")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment