Skip to content

Commit

Permalink
Merge branch 'jayennis22-customurl'. closes #8 and closes #9
Browse files Browse the repository at this point in the history
  • Loading branch information
Pablo O Vieira committed Dec 16, 2014
2 parents 72b98fc + 5e54758 commit d7578c7
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
Changelog
=========

1.3.0 (2014-12-16)
------------------

- Support for custom DDNS URL via `--url` parameter

1.2.3 (2014-10-10)
------------------

Expand Down
15 changes: 12 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Basic usage of **noipy** command line tool:
.. code-block:: bash
$ noipy -u <your username> -p <your password> -n <your hostname on DDNS provider>
--provider {noip|dyn|duck}
--provider {generic|noip|dyn|duck}
For `DuckDNS provider <https://www.duckdns.org>`_, the command line would look like this:
Expand All @@ -61,9 +61,15 @@ previously stored login information with ``--store`` option.

.. code-block:: bash
$ noipy --hostname <your hostname on DDNS provider> --provider {noip|dyn| duck}
$ noipy --hostname <your hostname on DDNS provider> --provider {generic|noip|dyn| duck}
You can also specify a custom DDNS URL (thanks to @jayennis22):
.. code-block:: bash
$ noipy --hostname <your hostname on DDNS provider> [--provider generic]
--url <custom DDNS URL>
If ``--provider`` option is not informed, **noip** will be used as provider.
It is also possible to inform an IP address other than the machine's current:

Expand All @@ -72,6 +78,9 @@ It is also possible to inform an IP address other than the machine's current:
$ noipy --hostname <your hostname on DDNS provider> 127.0.0.1
If ``--provider`` option is not informed, **generic** will be used as provider.


For details:

.. code-block:: bash
Expand Down
2 changes: 1 addition & 1 deletion noipy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"""

__title__ = "noipy"
__version_info__ = ('1', '2', '3')
__version_info__ = ('1', '3', '0')
__version__ = ".".join(__version_info__)
__author__ = "Pablo O Vieira"
__email__ = "[email protected]"
Expand Down
19 changes: 17 additions & 2 deletions noipy/dnsupdater.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
'noip': 'NoipDnsUpdater',
'dyn': 'DynDnsUpdater',
'duck': 'DuckDnsUpdater',
'generic': 'GenericDnsUpdater',
}

DEFAULT_PLUGIN = 'noip'
DEFAULT_PLUGIN = 'generic'


class DnsUpdaterPlugin(object):
Expand All @@ -29,12 +30,13 @@ class DnsUpdaterPlugin(object):

auth_type = ""

def __init__(self, auth, hostname):
def __init__(self, auth, hostname, options={}):
"""Init plugin with auth information, hostname and IP address.
"""

self._auth = auth
self._hostname = hostname
self._options = options
self.last_status_code = ''

@property
Expand Down Expand Up @@ -167,3 +169,16 @@ def hostname(self):
def _get_base_url(self):
return "https://www.duckdns.org/update?domains={hostname}" \
"&token={token}&ip={ip}"


class GenericDnsUpdater(DnsUpdaterPlugin):
""" Generic DDNS provider plugin - accepts a custom specification for the
DDNS base url
"""

auth_type = "P"

def _get_base_url(self):
return "{url}?hostname={{hostname}}&myip={{ip}}&wildcard=NOCHG" \
"&mx=NOCHG&backmx=NOCHG".format(url=self._options['url'])

28 changes: 26 additions & 2 deletions noipy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
EXECUTION_RESULT_OK = 0
EXECUTION_RESULT_NOK = 1

URL_RE = re.compile(
r'^https?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)

def get_ip():
"""Return the machine external IP.
Expand All @@ -58,7 +67,7 @@ def execute_update(args):

provider_class = getattr(dnsupdater,
dnsupdater.AVAILABLE_PLUGINS.get(args.provider))

updater_options = {}
process_message = None
exec_result = EXECUTION_RESULT_NOK
update_ddns = False
Expand Down Expand Up @@ -113,8 +122,22 @@ def execute_update(args):
"provided via command line or stored with --store " \
"option.\nExecute noipy --help for more details."

if update_ddns and args.provider == 'generic':
if args.url:
if not URL_RE.match(args.url):
process_message = "Malformed url"
exec_result = EXECUTION_RESULT_NOK
update_ddns = False
else:
updater_options['url'] = args.url
else:
process_message = "Must use --url if --provider is 'generic' " \
"(default)"
exec_result = EXECUTION_RESULT_NOK
update_ddns = False

if update_ddns:
updater = provider_class(auth, args.hostname)
updater = provider_class(auth, args.hostname, updater_options)
ip_address = args.ip if args.ip else get_ip()
print("Updating hostname '%s' with IP address %s [provider: '%s']..."
% (args.hostname, ip_address, args.provider))
Expand All @@ -135,6 +158,7 @@ def create_parser():
% dnsupdater.DEFAULT_PLUGIN,
choices=dnsupdater.AVAILABLE_PLUGINS.keys(),
default=dnsupdater.DEFAULT_PLUGIN)
parser.add_argument('--url',help="custom DDNS server address")
parser.add_argument('--store',
help="store DDNS authentication information and "
"update the hostname if it is provided",
Expand Down
19 changes: 19 additions & 0 deletions test/test_noipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ def test_duckdns_plugin(self):
self.assertTrue(status_message.startswith("ERROR:"),
"Status message should be an 'ERROR'")

def test_generic_plugin(self):
cmd_args = ['--provider', 'generic']
args = self.parser.parse_args(cmd_args)
result, status_message = main.execute_update(args)
self.assertTrue(result == main.EXECUTION_RESULT_NOK,
"An error should be flagged when --provider is "
"'generic' and --url is not specified")

cmd_args = ['-u', 'username', '-p', 'password',
'--url', 'https://dynupdate.no-ip.com/nic/update',
'--provider', 'generic',
'-n', 'noipy.no-ip.org', self.test_ip]
args = self.parser.parse_args(cmd_args)
result, status_message = main.execute_update(args)
self.assertTrue(result == main.EXECUTION_RESULT_OK,
"Update with 'No-IP' using generic provider failed.")
self.assertTrue(status_message.startswith("ERROR:"),
"Status message should be an 'ERROR'")


class AuthInfoTest(unittest.TestCase):

Expand Down

0 comments on commit d7578c7

Please sign in to comment.