Skip to content

Commit 18d51f0

Browse files
committed
feature: IPv6 support
Requests, checks, and updates the both, IPv4 and IPv6, addresses when available. Uses ip4only.me, ip6only.me, and 8.8.8.8 Google DNS resolver
1 parent 2483cb6 commit 18d51f0

File tree

7 files changed

+73
-13
lines changed

7 files changed

+73
-13
lines changed

noipy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"""
1010

1111
__title__ = "noipy"
12-
__version_info__ = ('1', '5', '3')
12+
__version_info__ = ('1', '5', '4')
1313
__version__ = ".".join(__version_info__)
1414
__author__ = "Pablo O Vieira"
1515
__email__ = "noipy@pv8.io"

noipy/utils.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,23 @@
55
# Copyright (c) 2013 Pablo O Vieira (povieira)
66
# See README.rst and LICENSE for details.
77

8+
try:
9+
from StringIO import StringIO
10+
except ImportError:
11+
from io import StringIO
12+
813
import socket
914

15+
import dns.resolver
1016
import requests
1117

1218
HTTPBIN_URL = "https://httpbin.org/ip"
1319

20+
IP4ONLY_URL = "http://ip4only.me/api"
21+
IP6ONLY_URL = "http://ip6only.me/api"
22+
23+
COMMON_DNS = "8.8.8.8"
24+
1425
try:
1526
input = raw_input
1627
except NameError:
@@ -22,19 +33,57 @@ def read_input(message):
2233

2334

2435
def get_ip():
25-
"""Return machine's origin IP address.
36+
"""Return machine's origin IP address(es).
2637
"""
38+
lst = []
39+
try:
40+
r = requests.get(IP4ONLY_URL)
41+
if r.status_code == 200:
42+
lst.append(r.text.split(',')[1])
43+
except requests.exceptions.ConnectionError:
44+
pass
2745
try:
28-
r = requests.get(HTTPBIN_URL)
29-
return r.json()['origin'] if r.status_code == 200 else None
46+
r = requests.get(IP6ONLY_URL)
47+
if r.status_code == 200:
48+
lst.append(r.text.split(',')[1])
3049
except requests.exceptions.ConnectionError:
50+
pass
51+
if not lst:
52+
try:
53+
r = requests.get(HTTPBIN_URL)
54+
if r.status_code == 200:
55+
lst.append(r.json()['origin'])
56+
except requests.exceptions.ConnectionError:
57+
pass
58+
if not lst:
3159
return None
60+
return ','.join(lst)
3261

3362

3463
def get_dns_ip(dnsname):
35-
"""Return machine's current IP address in DNS.
64+
"""Return machine's current IP address(es) in DNS.
3665
"""
66+
resolver = dns.resolver.Resolver(StringIO("nameserver %s" % COMMON_DNS))
67+
68+
try:
69+
resolve = resolver.resolve
70+
except AttributeError:
71+
resolve = resolver.query
72+
73+
lst = []
74+
try:
75+
lst += [a.address for a in resolve(dnsname, 'A')]
76+
except dns.exception.DNSException:
77+
pass
3778
try:
38-
return socket.gethostbyname(dnsname)
39-
except socket.error:
79+
lst += [a.address for a in resolve(dnsname, 'AAAA')]
80+
except dns.exception.DNSException:
81+
pass
82+
if not lst:
83+
try:
84+
lst.append(socket.gethostbyname(dnsname))
85+
except socket.error:
86+
pass
87+
if not lst:
4088
return None
89+
return ','.join(lst)

requirements-dev.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
flake8==3.7.9
44
tox==3.20.0
55
pytest==4.6.9
6+
coverage
7+
IPy

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
dnspython
12
requests==2.22.0

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
install_requires = [
1515
"requests>=2.0",
16+
"dnspython",
1617
]
1718

1819
if sys.version_info[:2] < (2, 7):

test/test_noipy.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
# Copyright (c) 2013 Pablo O Vieira (povieira)
66
# See README.rst and LICENSE for details.
77

8+
from IPy import IP
9+
810
import getpass
911
import os
10-
import re
1112
import shutil
1213
import unittest
1314

@@ -32,12 +33,18 @@ def tearDown(self):
3233
shutil.rmtree(self.test_dir)
3334

3435
def test_get_ip(self):
35-
ip = utils.get_ip()
36+
ips = utils.get_ip()
3637

37-
self.assertTrue(re.match(VALID_IP_REGEX, ip), "get_ip() failed.")
38+
for ip in ips.split(','):
39+
try:
40+
IP(ip)
41+
except Exception as ex:
42+
self.assertIsNone(ex, "get_ip() failed.")
3843

3944
# monkey patch for testing (forcing ConnectionError)
4045
utils.HTTPBIN_URL = "http://example.nothing"
46+
utils.IP4ONLY_URL = "http://example.nothing"
47+
utils.IP6ONLY_URL = "http://example.nothing"
4148

4249
ip = utils.get_ip()
4350
self.assertTrue(ip is None, "get_ip() should return None. IP=%s" % ip)
@@ -56,7 +63,7 @@ class PluginsTest(unittest.TestCase):
5663

5764
def setUp(self):
5865
self.parser = main.create_parser()
59-
self.test_ip = "10.1.2.3"
66+
self.test_ip = "10.1.2.3,2004::1:2:3:4"
6067

6168
def tearDown(self):
6269
pass
@@ -168,7 +175,7 @@ class AuthInfoTest(unittest.TestCase):
168175

169176
def setUp(self):
170177
self.parser = main.create_parser()
171-
self.test_ip = "10.1.2.3"
178+
self.test_ip = "10.1.2.3,2004::1:2:3:4"
172179
self.test_dir = os.path.join(os.path.expanduser("~"), "noipy_test")
173180

174181
def tearDown(self):

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ envlist = py{26,27,35,36,37},pypy,pypy3,pep8
55
passenv = CI TRAVIS_BUILD_ID TRAVIS TRAVIS_BRANCH TRAVIS_JOB_NUMBER TRAVIS_PULL_REQUEST TRAVIS_JOB_ID TRAVIS_REPO_SLUG TRAVIS_COMMIT
66
deps =
77
-rrequirements-dev.txt
8-
py{26,27,35,36,37},pypy,pypy3: coverage
8+
py{26,27,35,36,37},pypy,pypy3: coverage IPy
99

1010
commands =
1111
python --version

0 commit comments

Comments
 (0)