本文介绍两种方便获取主机动态IP的方式(DDNS,IP报告网页),并给出相应的代码实现.

shell脚本获取本机IP,执行上传操作和更新DNS操作.定期执行通过crontab或者systemd等服务.

应用场景

远程访问具有动态IP的公网或内网主机时,如果通过ip进行访问,由于公网IP总是在变化,我们不得不每次去查看新的ip地址,往往这个重复的过程比较麻烦.

远程主机联网的方式有所不同,主要有以下几种情形:

  1. 远程主机是通过PPPoE拨号上网,通常获取到动态的私有网络(内网)地址
  2. 远程主机直接获取到的是动态公网ipv4地址.
  3. 在教育网中通常还能获得动态的公网ipv6地址.

又可以简易地分成两类: 配有公网ip的主机与仅配置内网ip的主机.

内网主机访问方式

  1. 反向隧道

    对于躲在NAT之后的内网主机,比较方便的方式是在内网主机建立到公网主机的反向隧道,命令行建立反向隧道工具有:ssh,ngrok,tmate等,参考我之前的反向隧道的文章. 这些工具往往都能在ip发生改变后自动重建连接.

    缺点是我们需要一台拥有公网ip的主机,并且时刻保持隧道长连接,另外由于远程访问内网主机需要经过这个公网主机中转,速度变慢.

  2. 内网穿透

    通过公网服务器得到内网主机在NAT设备的转码地址,然后可以建立p2p的连接.QQ,TeamViewer即是类似原理.前提是内网容易穿透.

  3. 路由器端口映射

    外网IP和端口映射到内网:在路由器的「转发规则」页面添加外网的端口到内网某主机端口的映射.

本着只要有不断重复的麻烦事就用脚本实现的原则,我们通过一些脚本来方便我们的工作.

DDNS (动态域名IP解析)

IP报告/收集脚本

IP报告脚本

通过linux的ip命令获取到本机的公网ip以及通过网站获取本机的外网ip,然后上传到自建的php服务器上.

脚本使用了本地文件记录前一次变更的ip地址,当ip发生变化才执行网络操作.文件保存在内存文件系统或临时文件中.

reportIP.sh

#!/bin/bash
# __author__ = fyk
# get global ipv6 & ipv4 address,
# note that both ipv6 & ipv4 addr may have more than 1. # grep -v to exclude temporary ipv6 privacy addr.
ip6s=$(ip -6 addr |grep 'global'|grep -v 'tmpaddr'|awk '{print $2}'|sed 's/\/.*//' | uniq)
ip4s_local=$(ip -4 a | grep global |awk '{print $2}' | uniq) # local v4 ips ,public or prive ips that behind NAT
ips_pub=$(curl -s ifconfig.me) # there are lots of websites supplying IP echo services
ip_data=$ips_pub' '${ip6s}' '${ip4s_local}
#echo $ip_data
IP_FILE='/dev/shm/lastip97451' # or in /tmp .etc
ip_data_old=$(cat $IP_FILE 2> /dev/null) # for non-exist file,content is null
if [ "$ip_data" != "$ip_data_old" ];then
echo 'IP changed,push to remote.'
#echo $ip_data > $IP_FILE # update the file
params='k=fyk'
cnt=0
for ip in $ip_data;do
echo $ip
params=$params"&ip$cnt=$ip" # shell will handle & specially,so we first trans & to %26
((cnt=cnt+1))
done
params="n=$cnt&$params"
sever_addr="http://x.makefile.tk/"
#echo ${sever_addr}
curl -G -d "$params" "$sever_addr" else echo 'IP unchanged.'
fi
# TODO:增加断网重连

php收集脚本

提供的服务地址形式是:http://x.makefile.tk/?k=password&n=2&p0=x.x.x.x&p1=x.x.x.x,其中n是ip地址个数,p0,p1,...分别是单独的ip,参数k为了简单地防止一些人捣乱.直接访问http://x.makefile.tk/将能看到上一次保存的ip地址.

下面是index.php代码,通过文件来记录ip地址,不能够并发写入.同shell报告脚本一样可以使用内存文件来加快读写速度.

<html>
<body>
<?php
if ( !function_exists('sys_get_temp_dir')) {
function sys_get_temp_dir() {
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
$tempfile=tempnam(uniqid(rand(),TRUE),'');
if (file_exists($tempfile)) {
unlink($tempfile);
return realpath(dirname($tempfile));
}
}
}
?> <?php
//echo "QUERY_STRING: " . $_SERVER['QUERY_STRING'];
//echo "<br>";
//$temp_file = tempnam(sys_get_temp_dir(), 'Tux');
//$temp_file = sys_get_temp_dir() . 'ip97845';
//$temp_file = '/dev/shm/ip97845';//seems to be frequently erased by cloud host.
$temp_file = 'ip97845';
//if has param of n,then save ip to file
if(isset($_GET['n'])){
$n = $_GET['n'];
if(isset($_GET['k'])){
$key = $_GET['k'];//for simple security
if($key == 'password'){
$myfile = fopen($temp_file, "w") or die("Unable to open file!");
for ($x=0; $x<$n; $x++) {
$ip_idx = 'ip' . $x;
$line = $ip_idx . "=$_GET[$ip_idx]<br>";
echo $line;
fwrite($myfile, $line);
}
fclose($myfile);
echo 'save ip ok!<br>' ;
}else echo 'key error';
}else echo 'no key error'; }else{ // read from file
$myfile = fopen($temp_file, "r") or die("Unable to open file!");
echo fread($myfile,filesize($temp_file));
fclose($myfile);
} echo "<br><br>";
echo "Your INFO:<br>";
echo "IP: " . $_SERVER['REMOTE_ADDR'];
echo "<br>";
echo "UA: " . $_SERVER['HTTP_USER_AGENT'];
echo "<br>"; ?> </body>
</html>

对于这种web应用,使用网络上各种php建站即可.

公用DNS服务

准备:购买公网域名,域名设置DNS解析服务为Dnspod或CloudFlare.本文的代码使用CloudFlare的API动态修改DNS记录.

思路是修改IP报告脚本,将更新的IP更新的公共的DNS服务上.

ipv6-dns.sh 代码:

#!/bin/bash
# __author__ = fyk
# get global ipv6 & ipv4 address,
# note that both ipv6 & ipv4 addr may have more than 1. # get variables in dns.conf which includes cloudflare info
# source dns.conf
if [ -z "$1" ] ;then
echo 'please specify conf file'
exit 0
else
source $1
fi # grep -v to exclude temporary ipv6 privacy addr.
ip6s=$(ip -6 addr |grep 'global'|grep -v 'tmpaddr'|awk '{print $2}'|sed 's/\/.*//')
#for my own needs,i only use ipv6
#ip4s=$(ip -4 a | grep global |awk '{print $2}')
for ip in $ip6s;do
ip_data=$ip
break # only use first one
done
#ip_data=${ip6s}' '${ip4s}
#echo $ip_data API_URL="https://api.cloudflare.com/client/v4"
CURL="curl -s \
-H Content-Type:application/json \
-H X-Auth-Key:$AUTH_KEY \
-H X-Auth-Email:$AUTH_EMAIL " update_dns(){
UPDATE_DATA=$(cat << EOF
{ "type": "AAAA",
"name": "$DOMAIN_NAME",
"content": "$2",
"proxied": false }
EOF
)
#"ttl": 1, # let it be Automatic
echo "update dns: $DOMAIN_NAME -> $2"
$CURL -X PUT "$API_URL/zones/$ZONE_ID/dns_records/$1" -d "$UPDATE_DATA" > /tmp/cloudflare-ddns.json
}
# get current IP
get_dns_ip(){
RECS=$($CURL "$API_URL/zones/$ZONE_ID/dns_records?name=$DOMAIN_NAME")
IP=$(echo "$RECS" | sed -e 's/[{}]/\n/g' | sed -e 's/,/\n/g' | grep '"content":"' | cut -d'"' -f4)
echo $IP
} IP_FILE='/dev/shm/lastip9745' # or in /tmp .etc
ip_data_old=$(cat $IP_FILE 2> /dev/null) # for non-exist file,content is null
if [ "$ip_data" == "$ip_data_old" ];then
echo 'IP unchanged.'
exit 0
fi
echo 'IP changed,push to remote.'
if [ -z "$REC_ID" ] ; then
RECS=$($CURL "$API_URL/zones/$ZONE_ID/dns_records?name=$DOMAIN_NAME")
echo $RECS
REC_ID=$(echo "$RECS" | sed -e 's/[{}]/\n/g' | sed -e 's/,/\n/g' | grep '"id":"' | cut -d'"' -f4)
echo "REC_ID=$REC_ID"
fi
update_dns "$REC_ID" "$ip_data"
cur_ip=$(get_dns_ip)
if [ "$cur_ip"=="$ip_data" ];then
echo $ip_data > $IP_FILE # update the file
else
echo 'update dns failed.'
fi

脚本中通过source dns.conf读取了配置信息:

# this is Cloudflare api info for DDNS
# !!do not leave space around =
AUTH_EMAIL=<cloudflare-auth-email>
#This is your *Global API Key* under Cloudflare account settings
AUTH_KEY=<cloudflare-auth-key>
#Zone ID:can be find out there: <https://www.cloudflare.com/a/overview/>
ZONE_ID=<DNS Zone>
#your sub domain name
DOMAIN_NAME="ip.example.com"

具体API使用方法查阅https://api.cloudflare.com

自建DNS服务

准备:外网服务器B,搭建bind9服务用来提供DNS服务

借助于IP报告/收集脚本,在服务器B上不断更新域名解析.

客户端机器C,手动设置DNS服务地址为B的IP.这种方式的优点是域名想怎么写就怎么写.

示意图:

通过DNS服务可以实现与著名的花生壳相类似的服务,而且成本低,“自主、可控”:) 。

关于域名解析ttl

TTL是英语Time-To-Live的简称,意思为一条域名解析记录在DNS服务器中的存留时间。当各地的DNS服务器接受到解析请求时,就会向域名指定的NS服务器发出解析请求从而获得解析记录;在获得这个记录之后,记录会在DNS服务器中保存一段时间,这段时间内如果再接到这个域名的解析请求,DNS服务器将不再向NS服务器发出请求,而是直接返回刚才获得的记录;而这个记录在DNS服务器上保留的时间,就是TTL值。

如果域名的IP经常变更,那么减小TTL的值,如果很少改变,调大成几个小时都行.

将TTL设为1,表示'Automatic',如Cloudflare的DNS会在约5分钟内push出去.

IP地址变更事件通知

得到网络变化的方式有多种:

  1. C语言使用linux下的rtnetlink的NETLINK_ROUTE socket,监听之后会收到消息.要求写的程序一直在运行.参考.
  2. 如果网络管理器使用的Gnome的NetworkManager,那么会通过D-Bus广播事件(浏览器等常通过这种方式切换在线/离线模式).
  3. Debian系统中网络接口up或down时会执行/etc/network下的相关脚本.
  4. ifplugd 当网线被拔掉或接入时会执行相应脚本.

如果网络是使用NetworkManager(Ubuntu等系统默认的网络管理器)进行DHCP获取动态IP,比较方便的方式是将我们的脚本添加到事件响应脚本中. 参考man手册,在/etc/NetworkManager/dispatcher.d中添加脚本.

#!/bin/bash
# put this script in /etc/NetworkManager/dispatcher.d IF=$1
STATUS=$2 case "$STATUS" in
down)
#logger -s "NM Script down $IF triggered"
;;
dhcp6-change|up) #
#if [ $IP6_NUM_ADDRESSES > 0 ];then
# echo $IP6_ADDRESS_0 //0,1,2,...
#fi
# msg logged to /va/log/syslog
logger "IP6_ADDRESS_0 = $IP6_ADDRESS_0"
/path/to/ipv6-dns.sh /path/to/dns.conf 2>&1 > /dev/null
*)
;;
esac

cron定时执行

执行crontab -e将会编辑用户的crontab文件,其创建/tmp下的临时文件进行编辑,保存后将会提交到系统目录下(/var/spool/cron),这种设计方式类似visudo,目的是先检查用户的输入,防止错误的输入带来的破坏.系统重启后/tmp下的文件会删除,而crontab不会丢失.

也可以使用自定义的crontab文件导入到系统任务中:`crontab /path/to/cronfile

文件内容如下,注意使用绝对路径:

0 */1 * * * /home/s05/fyk/ip/ipv6-dns.sh /home/s05/fyk/ip/dns.conf

cron job的执行命令情况可以在/var/log/syslog中看到.

使用logger 'msg'可以将msg记录到syslog文件中.

crontab无需重启会立即生效.

邮件通知

发送邮件.适合于ip更新不太频繁的情形,通过代码发送邮件的代码很简便,Python,Java等语言均有方便的实现.

More

本文代码地址: https://github.com/makefile/CharmScript/tree/master/DDNS

其它资源:

动态IP解析的更多相关文章

  1. 更新TP-LINK路由器的外网IP到花生壳动态IP解析

    ------------------------------------------------------------------------------- 以下内容可能还是存在问题,等之后有时间再 ...

  2. 域名动态解析到动态IP

    一般宽带用户的IP都是动态IP,重连之后IP可能会发生变化. 如果想在其他地方连接家里的设备,或者在家中搭建服务器,就会受到影响. 现在提供一种动态解析域名的方式,只要检测到IP的变化,那么就调用阿里 ...

  3. Linux 静态IP动态IP设置

    1.设置动态IP ifconfig eth0 192.168.1.12 设置后立即生效,重启机器后就无效了 2.设置静态IP 编辑文件 /etc/sysconfig/network-scripts/i ...

  4. 动态IP或无公网IP时外网訪问内网ORACLE数据库

    ORACLE数据库是应用最多的一个数据库.一般项目应用.将ORACLE部署在内网,内网调用,及运维都仅仅能是内网完毕. 假设ORACLE主机或所在局域网没有固定公网IP,又想在外网对ORACLE进行訪 ...

  5. 动态IP下群晖搭建DDNS服务

    转载地址:https://www.zimrilink.com/share/dsm_aliddns_server.html 通过阿里云API(php)搭建出DDNS动态域名解析服务器;不同的是本文的方法 ...

  6. nodejs爬虫如何设置动态ip以及userAgent

    nodejs爬虫如何设置动态ip以及userAgent 转https://blog.csdn.net/u014374031/article/details/78833765 前言 在写nodejs爬虫 ...

  7. 2014-10-2 bug更新5 ecshop和ectouch解决动态ip登录超时和购物车清空问题

    有客户说登陆网站后台操作的时候,会时不时的掉一下,要重新登陆才能继续操作,自动登出的频率快和时间短,针对这个问题是因为: 购物车问题原因的产生是因为动态IP的SESSEION机制导致很多在公司或者其他 ...

  8. 实体ip 虚拟ip 固定ip 动态ip

    实体 IP:在网络的世界里,为了要辨识每一部计算机的位置,因此有了计算机 IP 位址的定义.一个 IP 就好似一个门牌!例如,你要去微软的网站的话,就要去『 207.46.197.101 』这个 IP ...

  9. IP工具类-自己动手做个ip解析器

    IP工具类-自己动手做个ip解析器 一.资料准备 导入依赖包:

随机推荐

  1. 如何将ASP.NET-WebApi发布到IIS6.0上(转)

    关于"如何将ASP.NET-WebApi发布到IIS6.0上"的这方面的学习,一开始项目组长让我们接触的时候,我的心情是这样的 哇呜.jpg 当时真的是一脸懵逼啊,对于刚接触asp ...

  2. 201521123051《Java程序设计》第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...

  3. 201521044091 《Java学习笔记》 第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结.注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖面 ...

  4. 201521123013 《Java程序设计》第2周学习总结

    1. 本章学习总结 1.final声明,一旦赋值不能被改变.移位运算符的应用,负数右移高位补"1",正数右移高位补"1".利用移位运算,按位与运算可以方便得出整 ...

  5. 201521123004 《Java程序设计》第2周学习总结

    本周学习总结 ① String类的对象是不可变(immutable)的 String对象创建之后不能再进行修改 -->StringBuilder ② 字符串的拼接(使用"+" ...

  6. 201521123080《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 流(Stream): Stream是从起源(source)到接收(sink)的有序数据 按照流向分可以分为输入 ...

  7. java程序设计----学生基本信息管理系统

    1. 团队课程设计博客链接 http://www.cnblogs.com/hyy786030686/p/7063890.html 2. 个人负责模块或任务说明 --登录界面的设计 --用户名和密码验证 ...

  8. idea使用转载【别人的专栏】

    维C果糖

  9. 从JS和jQuery浅谈DOM操作,当我们在获取时,究竟获取了什么

    0.写在前面的话 自己对前端的东西一直不是很熟,现在开始要想办法从前端各个地方去获取想要的属性值的时候,也基本是在网上现炒现卖,几周下来,发现自己还是迷迷糊糊,可以算是一无所获. 所以就抽时间,把这一 ...

  10. activiti07- Task

    任务 用户任务: 用户任务,用来对那些需要人参与完成的工作进行建模.当流程执行到这样的用户任务时,会在被分配到该任务的用户或用户组的任务列表中创建新的任务. 用户任务中可以包含描述.事实上,任何BPM ...