星路

追寻那一缕星光,在漆黑夜晚前行

0%

为pfSense的DDNS增加腾讯云的实现

前几天刚换了电信宽带,终于有了公网IP,摆脱了移动的大内网,打通家里网络的方案又多几种选择。以前用过花生壳之类的免费DDNS,是挺方便的,但得用服务商的二级域名,没自己的域名用起来自由。家里的路由用的pfSense有DDNS服务,但不支持腾讯云。想着pfSense是开源的,之前申请Let’s encrypt泛域名证书的时候又调试过腾讯云的API了,改造改造难度应该不大,虽然没研究过pfSense的代码,但还是有信心约估个半天可以完成的。于是乎开工:读代码、定位功能模块还算顺利,但调试还是掉坑了,为了把php的调试日志调出来昨晚研究到近2点,最后不得不放弃。今天早上单独调试好模块后,再三确认嵌入的代码,运行后还好能正常工作了,哪天有空再回头研究调试的事情吧。


1.准备

1)工作流程分析

配置DDNS服务->pfSense监测WANIP变化->调用API修改”A”记录->最后通过域名解析服务器获取最新IP

2)需要改造内容

  • 前台的编辑页面:增加腾讯云类型的选项、显示相关的输入选项
  • 后台的处理逻辑:增加腾讯云类型的处理分支、相关的更新记录方法

3)复用之前的腾讯云云解析接口

文章传送门QcloudDns类下载


2.过程

2.1. 前端页面

  • 修改/usr/local/www/services_dyndns_edit.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 增加qcloud类型客户端的需要显示的选项
    function setVisible(service) {
    switch (service) {
    # ……省略……
    case "qcloud":
    hideGroupInput('domainname', false); # 域名
    hideInput('resultmatch', true);
    hideInput('updateurl', true);
    hideInput('requestif', true);
    hideCheckbox('curl_ipresolve_v4', true);
    hideCheckbox('curl_ssl_verifypeer', true);
    hideInput('host', false); # 主机名
    hideInput('mx', false); # mx值
    hideCheckbox('wildcard', true);
    hideCheckbox('proxied', true);
    hideInput('zoneid', true);
    hideInput('ttl', false); # ttl值
    break;
    # ……省略……
    }
    }

    Tips

    该功能函数作用是根据不同类型的客户端,在页面显示不同的配置选项。另外用户名、密码选项默认显示,不需要设置。

2.2. 后端处理

  • 修改/etc/inc/services.inc

    1
    2
    3
    4
    5
    # DYNDNS_PROVIDER_VALUES、DYNDNS_PROVIDER_DESCRIPTIONS两个字符串常量的定义中,加入qcloud。注意要加入分隔符,并且保证位置序号一致。
    # ……省略……
    define('DYNDNS_PROVIDER_VALUES', 'all-inkl azure azurev6 citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 digitalocean dnsexit dnsimple dnsmadeeasy dnsomatic dreamhost dreamhost-v6 duiadns duiadns-v6 dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns freedns-v6 glesys godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover loopia namecheap noip noip-free ods opendns ovh-dynhost qcloud route53 route53-v6 selfhost spdyn spdyn-v6 zoneedit');
    define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,Azure DNS,Azure DNS (v6),City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),DigitalOcean,DNSexit,DNSimple,DNS Made Easy,DNS-O-Matic,DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,freeDNS (v6),GleSYS,GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Loopia,Namecheap,No-IP,No-IP (free),ODS.org,OpenDNS,OVH DynHOST,qcloud,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),ZoneEdit');
    # ……省略……

    Tips

    services_dyndns_edit.php页面的根据这两个常量生成的类型列表。

  • 修改/etc/inc/dyndns.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 第一个switch ($this->_dnsService),在'azurev6'分支前加入'qcloud'分支
    function updatedns{
    # ……省略……
    switch ($this->_dnsService){
    # ……省略……
    case 'qcloud': # 增加这个即可,会执行$this->_update();
    case 'azurev6':
    $this->_update();
    if ($this->_dnsDummyUpdateDone == true) {
    // If a dummy update was needed, then sleep a while and do the update again to put the proper address back.
    // Some providers (e.g. No-IP free accounts) need to have at least 1 address change every month.
    // If the address has not changed recently, or the user did "Force Update", then the code does
    // a dummy address change for providers like this.
    sleep(10);
    $this->_update();
    }
    break;
    # ……省略……
    }
    # ……省略……
    }

    Tips

    该处判断服务并出发更新操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     # 第二个switch ($this->_dnsService),加入'qcloud'分支,位置在'default'之前即可。
    function _update() {
    # ……省略……
    switch ($this->_dnsService){
    # ……省略……
    case 'qcloud':
    /* tencent cloud dyndns */
    $secretId = $this->_dnsUser; #
    $secretKey = $this->_dnsPass; #
    $domain = $this->_dnsDomain; #
    $subDomain = $this->_dnsHost; #
    $recordType = 'A';
    $ip = $this->_dnsIP;
    $ttl = $this->_dnsTTL;
    $mx = $this->_dnsMX;
    $obj = new QcloudDns($secretId, $secretKey);
    $data = $obj->ListRecords($domain);
    $is_newRecord = true;
    if($data["code"] == 0){
    $data = $data["data"]["records"];
    if (is_array($data)) {
    foreach ($data as $v) {
    if ($v["name"] == $subDomain) {
    $recordId = $v["id"];
    $result = $obj->UpdateRecord($domain, $recordId, $subDomain, $recordType, $recordLine = '默认', $ip, $ttl = 600, $mx = 0);
    $is_newRecord = false;
    }
    }
    if($is_newRecord){
    $result = $obj->CreateRecord($domain, $subDomain, $recordType, $recordLine = '默认', $ip, $ttl = 600, $mx = 0);
    }
    $this->_checkStatus(0, $result, null);
    }
    }else{
    $this->_checkStatus(0, $data,null);
    }
    break;
    default:
    break;
    }
    # ……省略……
    }
    # ……省略……

    Tips

    该处实现获取腾讯云API配置信息、更新IP操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 第三个switch ($this->_dnsService),加入'qcloud'分支,位置在'default'之前即可。
    function _checkStatus($ch, $data, $header) {
    # ……省略……
    switch ($this->_dnsService){
    # ……省略……
    case 'qcloud':
    $data = json_encode($data);
    if (preg_match("/Success/i", $data)) {
    $status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
    $successful_update = true;
    } else {
    $status = $status_intro . "(" . gettext("Unknown Response") . ")";
    log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
    $this->_debug($data);
    }
    break;
    # ……省略……
    }
    # ……省略……
    }

    Tips

    该处根据腾讯云返回的信息判断是否更新成功并输出日志。为了简单点,这里不对错误原因一一分析。

    1
    2
    3
    4
    5
    6
    # 将所有的if($this->_dnsService != 'ods')判断语句,加入'qcloud'类型
    # ……省略……
    if (!in_array($this->_dnsService, array('ods','qcloud'))){
    # ……省略……
    }
    # ……省略……

    Tips

    原来代码'ods'类型的服务使用特殊的处理流程,跳过通用的curl构造处理流程。新加的'qcloud'直接使用QcloudDns类方法,也不需要用到原来的curl处理流程,所以也加进忽略列表。

    1
    2
    3
    4
    # 最后加入QcloudDns类定义代码
    class QcloudDns {
    # ……省略……
    }

    补充

    也可以做个文件引用。


3.小结

  • php的配置文件php.ini由/etc/rc.php_ini_setup脚本生成,每次重启会覆盖。
  • sshd的配置文件sshd_config由/etc/sshd脚本生成,每次重启会覆盖;secureCRT7版本不支持默认的钥匙交换协议,可以在配置文件增加旧方法,简单点就升级secureCRT版本。