The ZTE routers cheap piece of shit routers™ usually dished out by ISPs (such as the ones in Indonesia) that have an astonishing lack of features: no SNMP, no bridge mode, bizarre wireless coupled with hardware, etc… Since SNMP did not fit the plastic allure of router in general, there is no way to programatically extract the external IP address.
Instead, Selenium and PhantomJS are going to be used in order to retrieve the IP address and update the CloudFlare zone.
aptitude install selenium
Download the script in the code section, configure the script by editing the parameters in the configuration section at the top and then place it in, say /etc/cron/cron.hourly
. The script is created such that it will not return any output in case the CloudFlare name already resolves to the ZTE external address. However, the script will announce when the IP has been updated or in case there are any errors in attempting to do so.
#!/bin/sh ########################################################################### ## Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 ## ## Please see: http://www.gnu.org/licenses/gpl.html for legal details, ## ## rights of fair usage, the disclaimer and warranty conditions. ## ########################################################################### ########################################################################### ## CONFIGURATION ## ########################################################################### # ZTE router IP or hostname ZTE_LOCAL_ADDRESS='192.168.0.1' # ZTE router username ZTE_USERNAME='sm5lty' # ZTE router password ZTE_PASSWORD='goM0k1' # Set this to the cloudflare zone name. DOMAIN='remoz.org' # Set this to the A record name. RECORD_NAME='forward.remoz.org' # Set this to your account e-mail address. EMAIL='vic@remoz.org' # Set this to your CloudFlare API key. API_KEY='f5fdb672c54bb7d72dbeec35e822d90ce14e1' ########################################################################### ## INTERNALS ## ########################################################################### LOCK_FILE='/var/lock/zte-cloudflare' # Acquire a lock. if mkdir $LOCK_FILE 2>/dev/null; then trap '{ rm -rf $LOCK_FILE; }' KILL QUIT TERM EXIT INT HUP else exit 0 fi # Retrieve the external address. ZTE_EXTERNAL_ADDRRESS=`cat << EOF | /usr/bin/env python - $ZTE_LOCAL_ADDRESS $ZTE_USERNAME $ZTE_PASSWORD # -*- coding: utf-8 -*- # imports and packages from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.support import expected_conditions as EC from contextlib import contextmanager import unittest, time, re, time, os, sys, random, signal # the tool takes as parameter: # - the IP address of the ZTE router # - the username # - the password if len(sys.argv) != 4: print "Syntax: " + sys.argv[0] + " " + "<IP>" + " " + "<username>" + " " + "<password>" sys.exit(1) zte = sys.argv[1] usr = sys.argv[2] pwd = sys.argv[3] try: # initialize PhantomJS wd = webdriver.PhantomJS(service_log_path='/var/ghostdriver.log') wd.get("http://" + zte) wd.find_element_by_id("Frm_Username").click() wd.find_element_by_id("Frm_Username").clear() wd.find_element_by_id("Frm_Username").send_keys(usr) wd.find_element_by_id("Frm_Password").click() wd.find_element_by_id("Frm_Password").clear() wd.find_element_by_id("Frm_Password").send_keys(pwd) wd.find_element_by_id("LoginId").click() wd.switch_to_frame("mainFrame") wd.find_element_by_xpath("//div[3]/div[1]/div[1]/table/tbody/tr/td/table/tbody/tr[1]/td[2]/font").click() print wd.find_element_by_xpath("/html/body/div[3]/div[1]/div[3]/table/tbody/tr[5]/td[2]").text except: # Tough luck! #print "Unexpected error:", sys.exc_info()[0] pass finally: wd.close() wd.service.process.send_signal(signal.SIGTERM) wd.quit() EOF ` #echo $ZTE_EXTERNAL_ADDRRESS #exit 0 if [ -z $ZTE_EXTERNAL_ADDRRESS ]; then echo "Unable to retrieve Airport external address." echo "Please check that the Airport has SNMP enabled." exit 1 fi FLARE=$(curl -s https://www.cloudflare.com/api_json.html \ -d 'a=rec_load_all' \ -d "tkn=$API_KEY" \ -d "email=$EMAIL" \ -d "z=$DOMAIN") if [ -z $FLARE ]; then echo "Unable to retrieve CloudFlare zone." echo "Please check the API key." exit 1 fi ZONE_ADDRESS=`echo $FLARE | sed -E "s/.*,\"name\":\"$RECORD_NAME\",[^}]*?,\"type\":\"A\",[^}]*?,\"content\":\"([0-9\.]+?)\",.*/\1/"` if [ -z $ZONE_ADDRESS ]; then echo "Unable to retrieve the CloudFlare zone IP address." exit 1 fi # if the ZTE external address matches the zone address terminate if [ $ZTE_EXTERNAL_ADDRRESS = $ZONE_ADDRESS ]; then exit 0 fi RECORD_ID=`echo $FLARE | sed -E "s/.*\"rec_id\":\"([0-9]+?)\",[^}]*?,\"name\":\"$RECORD_NAME\",[^}]*?,\"type\":\"A\",.*/\1/"` if [ -z $RECORD_ID ]; then echo "Unable to retrieve CloudFlare record ID for the zone." exit 1 fi FLARE=$(curl -s https://www.cloudflare.com/api_json.html \ -d 'a=rec_edit' \ -d "tkn=$API_KEY" \ -d "id=$RECORD_ID" \ -d "email=$EMAIL" \ -d "z=$DOMAIN" \ -d 'type=A' \ -d "name=$RECORD_NAME" \ -d "content=$ZTE_EXTERNAL_ADDRRESS" \ -d 'service_mode=0' \ -d 'ttl=1') RESULT=`echo $FLARE | sed -E "s/.*,\"result\":\"([^\"]*)\",.*/\1/"` if [ $RESULT = "success" ]; then echo "$RECORD_NAME now points to $ZTE_EXTERNAL_ADDRRESS" exit 0 fi exit 1