现在有些DNS解析要收费,国内的几个厂商需要实名制。下面给出golang请求cloudflare修改域名A记录解析的代码。

准备工作:

  1. 在域名购买服务商处,将dns解析服务器改为cloudflare的dns服务器地址
  2. 将域名添加到cloudflare后台,非cloudflare购买的域名也可以添加!
  3. cloudflare后台获取API KEY;获取区域ID,即代码中用到的zone id ; 代码中需要用到
  4. 在后台先为域名设置一条A记录,设置“仅限DNS”

如果准备工作不知道操作的,搜索其他博客,或者在B站搜索,都有教程。

示例代码实现的功能:从wan口获取公网ip地址,与当前dns记录比较,如果相同则不请求接口修改A记录,不同则请求接口。wan获取ip用正则实现,可以根据实际情况修改代码。

package main

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
) // 响应体的json中有其他字段,此处只定义需要的字段
type GetDnsRecords struct {
Result []Result `json:"result"`
Success bool `json:"success"`
} type PutDnsRecordResponse struct {
Result Result `json:"result"`
Errors interface{} `json:"errors"`
Success bool `json:"success"`
} // 单条dns记录
type Result struct {
// dns 记录后台的ID
Id string `json:"id"`
// 一级域名,例如:fuck.com
ZoneName string `json:"zone_name"`
// 具体被解析的域名, 例如:y.fuck.com
Name string `json:"name"`
// 解析到的内容,可能是IP
Content string `json:"content"`
// 解析类型,例如:A
Type string `json:"type"`
} type PutDns struct {
Type string `json:"type"`
Name string `json:"name"`
Content string `json:"content"`
Proxied bool `json:"proxied"`
} func main() {
// 需要解析的域名、cloudflare api key、zone id 写到环境变量
domain := os.Getenv("CLOUDFLARE_DOMAIN")
apiKey := os.Getenv("CLOUDFLARE_API_KEY")
zoneId := os.Getenv("CLOUDFLARE_ZONE_ID")
recordId, lastIp, err := getDnsRecordId(apiKey, zoneId, domain)
if err != nil {
fmt.Println("getDnsRecordId err", err)
return
}
dnsType := "A"
proxied := false
// 从网口获取IP
nowIp := getLocalIP()
if nowIp != lastIp {
_, er := putDnsRecord(zoneId, apiKey, recordId, dnsType, domain, nowIp, proxied)
if er == nil {
fmt.Println("putDnsRecord success ", "lastIp: ", lastIp, "nowIp: ", nowIp)
return
} else {
fmt.Println("putDnsRecord err", er)
return
}
} else {
fmt.Println("ip no change ", "lastIp: ", lastIp, "nowIp: ", nowIp)
return
} } // 执行shell,获取wan口信息正则匹配ip
func getLocalIP() (ip string) {
commandStr := "ifconfig pppoe-wan"
cmd := exec.Command("/bin/bash", "-c", commandStr)
stdout, _ := cmd.StdoutPipe()
if err := cmd.Start(); err != nil {
return ""
}
outBytes, _ := ioutil.ReadAll(stdout)
stdout.Close() if err := cmd.Wait(); err != nil {
fmt.Println("Execute failed when Wait:" + err.Error())
return ""
}
ifconfig := string(outBytes)
pattern := `inet addr:(\d+\.\d+\.\d+\.\d+)`
reg := regexp.MustCompile(pattern)
match := reg.FindString(ifconfig)
if match != "" {
pattern = `\d+\.\d+\.\d+\.\d+`
reg = regexp.MustCompile(pattern)
return reg.FindString(match)
}
return } // 获取DNS记录ID
func getDnsRecordId(apiKey string, zoneId string, domain string ) (recordId string, lastContent string, e error) {
api := "https://api.cloudflare.com/client/v4/zones/%s/dns_records"
api = fmt.Sprintf(api, zoneId)
requestHeader := map[string]string{
// Bearer后有1空格
"Authorization" : "Bearer " + apiKey,
}
code, resBody, _, er := JsonCurl("GET", api, nil, requestHeader)
if er != nil {
e = er
return
}
var dnsRecords GetDnsRecords
err := json.Unmarshal(resBody, &dnsRecords)
if code!= 200 || err != nil || dnsRecords.Success != true {
e = err
return
} for _, value := range dnsRecords.Result {
if value.Name == domain {
recordId = value.Id
lastContent = value.Content
return
}
} return } /*
* @Description: 修改dns
* @param dnsType string A记录传"A"
* @param name string 具体域名
* @param content string 解析到内容例如IP
* @param proxied bool 是否代理传
* @author ""
* @date 2022-05-04 15:30:00
*/
func putDnsRecord(zoneId string, apiKey string, recordId string, dnsType string, name string, content string, proxied bool) (bool,error) {
putDns := PutDns{
Type: dnsType,
Name: name,
Content: content,
Proxied: proxied,
}
api := `https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s`
api = fmt.Sprintf(api, zoneId, recordId)
requestHeader := map[string]string{
// Bearer后有1空格
"Authorization" : "Bearer " + apiKey,
}
_, resBody, _, er := JsonCurl("PUT", api, putDns, requestHeader)
if er != nil {
return false, er
}
var dnsRecords PutDnsRecordResponse
err := json.Unmarshal(resBody, &dnsRecords)
if err != nil {
// 记录日志
return false, err
}
if dnsRecords.Success != true {
return false,errors.New( fmt.Sprintf("resBody: %s, url: %s, apiKey: %s", string(resBody) , api, apiKey))
}
return true, nil
} func JsonCurl(method, url string, body interface{}, headers map[string]string) (code int, data []byte, respHeader http.Header, error error) {
jsonStr, _ := json.Marshal(body)
buffer := bytes.NewBuffer(jsonStr)
request, err := http.NewRequest(method, url, buffer)
if err != nil {
error = err
return
}
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
for key, val := range headers {
request.Header.Set(key, val)
} resp, err := http.DefaultClient.Do(request)
defer resp.Body.Close()
if err != nil {
error = err
return
}
code = resp.StatusCode
data, _ = ioutil.ReadAll(resp.Body)
respHeader = resp.Header
return
} func writeIpToFile(fileName string, content string) error {
f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
return err
} else {
n, _ := f.Seek(0, os.SEEK_END)
_, e := f.WriteAt([]byte(content), n)
defer f.Close()
if e != nil {
return e
}
}
return nil
}

最新文章

  1. linux笔记
  2. 谈一谈前端多容器(多webview平台)处理方案
  3. Centos7中systemctl命令详解
  4. MySQL做练习时总结的一些知识点
  5. 解决问题:centos虚拟机安装好nginx,本机无法访问
  6. codeforces 430A Points and Segments (easy)(理解能力有待提高……)
  7. JavaScript高级---桥模式设计
  8. Delphi 调用批处理
  9. 洛谷 P1162 填涂颜色
  10. Git应用—03分支管理和冲突解决(转载)
  11. SpringMvc 文件上传注意事项
  12. Ubuntu14.04下 安装p4c
  13. SpringCloud入门之Maven系统安装及配置
  14. Python学习--Selenium模块学习(2)
  15. odoo返写数据
  16. 《Lua程序设计》第3章 表达式 学习笔记
  17. Linux服务器修改文件句柄数和用户最大进程数限制
  18. selenium - switch_to.frame()- 内嵌表单的切换
  19. CloudStack 虚拟机控制台报错
  20. 武汉Uber优步司机奖励政策(1月11日~1月17日)

热门文章

  1. Linux 下查看mysql 加载的配置文件, 并且解决报错 “this is incompatible with sql_mode=only_full_group_by”
  2. 蓝牙mesh组网实践(手机配网例程配合wch mesh手机app的使用)
  3. HCIP-ICT实战进阶06-BGP基础
  4. java spring 理解
  5. (已解决)nginx+php 上传文件大小设置。
  6. vs MFC c++ rc文件的dilog打不开,显示加载失败
  7. ctfshow web入门 命令执行 web29-36
  8. 解决手机点击包含a、button标签时出现背景色问题
  9. MybatisPlus #{param}和${param}的用法详解
  10. php curl方法封装