opennic-set.py 9.91 KB
Newer Older
1 2 3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

vince committed
4 5
import re
import os
6
import sys
vince committed
7
import shutil
8 9 10 11
import platform
import argparse
import subprocess
import urllib2
12 13 14 15 16
import json
import datetime
import socket
import string
import time
vince committed
17 18 19

"""
Use dns servers of the open-nic project.
20 21
Get the nearest dns servers and insert them into your configuration.

22 23 24
usage (Debian): sudo python opennic-set.py
usage (Mageia): $ su
              : # python opennic-set.py
25 26
Requirements (on Debian):
apt-get install resolvconf # installed in Ubuntu
27 28 29 30
Requirements (on Mageia):
# urpmi resolvconf
Full information: https://github.com/vindarel/open-nic 
This version is heavly modified by kensan: kensan@kensan.it
31

32 33 34 35 36 37 38 39 40
Mageia Installation:
put in /etc/rc.d/rc.local the follow line:
/usr/bin/python /home/sandro/bin/opennic-set.py > /home/sandro/bin/log/opennic.log&
where "sandro" is your username and bin is your script folder (bin/log is your log folder)
execute:
# python opennic-set.py
verify that is all fine and then reboot and shutdown the linux-box.
(router may be on, so shutdown for test)
At restart check Internet and ~/bin/log/opennic.log.
vince committed
41
"""
42

43
DIR_RESOLVCONF = "/etc/resolvconf/resolv.conf.d/"
44 45
DEBIAN_MAGEIA_CONF = DIR_RESOLVCONF + "head"
RESOLVCONF = "/sbin/resolvconf"
46

47 48 49 50 51
#Default DNS server
DNS_SET_NUM = 3 # Number of set of Default DNS
DEFAULT_DNS = [i for i in range(DNS_SET_NUM)]
DEFAULT_DATA_DNS = [i for i in range(DNS_SET_NUM)]
DEFAULT_DNS_NUM = [i for i in range(DNS_SET_NUM)]
52

53 54 55 56
#SET 1 of DNS
DEFAULT_DNS[0] = ["193.70.192.25", "193.70.192.15",] #DNS Infostrada without 404 redirect (DNS hijacking)
DEFAULT_DATA_DNS[0] = ["DNS1 Wind without 404 redirect (DNS hijacking)", "DNS2 Wind without 404 redirect (DNS hijacking)"]
DEFAULT_DNS_NUM[0] = 2
57

58 59 60 61
#SET 2 of DNS
DEFAULT_DNS[1] = ["8.8.8.8", "8.8.4.4",] #DNS google
DEFAULT_DATA_DNS[1] = ["DNS1 google", "DNS2 google"]
DEFAULT_DNS_NUM[1] = 2
62

63 64 65 66
#SET 3 of DNS
DEFAULT_DNS[2] = ["208.67.222.222", "208.67.220.220", "208.67.222.220", "208.67.220.222"] #DNS openDNS
DEFAULT_DATA_DNS[2] = ["resolver1.opendns.com", "resolver2.opendns.com", "resolver3.opendns.com", "resolver4.opendns.com"]
DEFAULT_DNS_NUM[2] = 4
67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
#Datas for testing Internet vai IP or via domain name
TEST_NUM = 4
PING_IP = [i for i in range(TEST_NUM)]
TEST_INTERNET_DOMAIN = [i for i in range(TEST_NUM)]
# domain always up
TEST_INTERNET_DOMAIN[0] = "startpage.com"
TEST_INTERNET_DOMAIN[1] = "google.com"
TEST_INTERNET_DOMAIN[2] = "opendns.com"
TEST_INTERNET_DOMAIN[3] = "kensan.it"
# IP always up
PING_IP[0] = "193.70.192.25" #Wind Italy DNS
PING_IP[1] = "4.2.2.2" #It belongs to Level 3 and is anycast
PING_IP[2] = "208.67.222.222" #OpenDNS DNS
PING_IP[3] = "85.37.17.51" #Telecom Italy DNS

BACK_SUFFIX = ".opennicset-back"
NUMERO_DNS_OPENNIC = 4 # Number of openiNIC DNS to put in Resolv.conf text file and used for resolv domain name into ip
DNS_API_OPENNIC = "https://api.opennicproject.org/geoip/?jsonp&res="+str(NUMERO_DNS_OPENNIC)+"&ip=&nearest"
86

87 88
now = datetime.datetime.now()
LOCALTIME = now.strftime("%Y-%m-%d %H:%M:%S")
89

90 91 92 93
	
def getDnsList():
    """Gets your nearest openNIC servers from API
    (http://www.opennicproject.org/nearest-servers/).
94 95
    returns a list of IP addresses.
    """
96 97 98
    dns_list = [i for i in range(NUMERO_DNS_OPENNIC)]
    dns_data_list = [i for i in range(NUMERO_DNS_OPENNIC)]
    url = DNS_API_OPENNIC
99
    print "Connecting to %s..." % (url,)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    # get DNS opennic from Internet
    try :
      response = urllib2.urlopen(url)
      data = response.read()
      data = re.sub(r'([a-zA-Z_0-9\.]*\()|(\);?$)','',data) #transform jsonp into json
      data = json.loads(data)
    except :
      print "Error The OpenNIC DNS list is not accessible: www.opennicproject.org site down"
      return 0,0,0
    # load DNS opennic in array
    numero_dns = 0
    for i in range(NUMERO_DNS_OPENNIC):
      try:
	ip = data[i]['ip']
	valid_ip = is_valid_ip(ip)
      except:
	valid_ip = False
      if valid_ip:
	status = test(ip, "Warning: ip "+ip+" openNIC DNS down", 0)
	if status == "up":
	  dns_list[i] = ip
	  try:
	    loc = data[i]['loc']
	  except:
	    loc = "Unknown"
	  try:
	    stat = data[i]['stat']
	  except:
	    stat = "Unknown"
	  dns_data_list[i] = "OpenNIC - " + "loc: "+loc+", stat: "+stat+", "+LOCALTIME
	  numero_dns += 1
    return dns_list, dns_data_list, numero_dns
132

133 134 135 136 137 138 139

def is_valid_ip(ip):
    # check valid IPv4, to add valid IPv6
    m = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip)
    return bool(m) and all(map(lambda n: 0 <= int(n) <= 255, m.groups()))
  
  
140 141 142 143 144 145 146
def updateResolvconf():
    """resolvconf -u
    """
    print "Updating configuration…",
    subprocess.Popen(['resolvconf', '-u'])
    print " done."

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
def test_domain(message):
    for i in range(TEST_NUM):
      if test(TEST_INTERNET_DOMAIN[i], message, 1) == "up":
	return "up"
    return "down"


def test_ip(message):
    for i in range(TEST_NUM):
      if test(PING_IP[i], message, 1) == "up":
	return "up"
    return "down"


def test(domain_or_ip, error_message, step_number):
    """	Tests if we can access Internet
	domain_or_ip is google.com or 8.8.8.8
	step_number is the number of times that domain is pinging
	and it cost al least time_number seconds or more
166
    """
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    i = 0
    while True:
      if ping(domain_or_ip) == "up":
	break
      i += 1
      if step_number != 0: time.sleep(1)
      if i >= step_number:
	      print error_message
	      return "down"
    return "up"


def ping(domain_or_ip):
    #ping one time domain or ip like google.com or 8.8.8.8
    #and then check the response...
182
    try:
183 184 185 186
      output = subprocess.check_output("ping -c 1 " + domain_or_ip, shell=True)
      print "Ping OK - " + domain_or_ip
      #hostname, 'is up!'
      return "up"
187
    except:
188 189
      #hostname, 'is down!'
      return "down"
190 191


192
def editConf(dns_list, dns_data_list, numero_dns, conf):
193 194
    """Writes the given dns servers to the given file.
    """
195
    back = conf + BACK_SUFFIX
196
    if os.path.exists(conf):
197 198
        if not os.path.exists(back):
            shutil.copyfile(conf, back)
199
    else:
200 201 202 203
        print "%s file not found. Did you install resolvconf ?" % (conf,)

    w = "".join("nameserver " + dns_list[i] + " # " + dns_data_list[i] + "\n" for i in range(numero_dns))
    
204 205
    with open(conf, 'r') as f:
        lines = f.readlines()
206 207 208 209
        headstring = ""
	for l in lines:
	  if not l.startswith("nameserver"):
	    headstring += l
210

211
    head = headstring + w
212

213 214 215 216 217
    with open(conf, 'w') as f:
        print "we  put the lines:\n" +  w + "\nto " + conf
        print "We saved the original configuration file, so if you notice any connection problem, you can still put it back with the --undo option."
        f.write(head)
        
218 219

def main(*args):
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
  
    CONF = DEBIAN_MAGEIA_CONF
    print "Local current time :", LOCALTIME
    print
    # waiting: Internet is up?
    # waiting timeout step_number in seconds
    step_number = 120
    for wait in range(step_number/TEST_NUM):
      status0 = test_ip("Warning: not connected to Internet")
      if status0 == "up":
	break
    if status0 == "down":
      print
      print "ERROR: Internet is down, please check your connection"
      print
      exit(1)
    print "Internet is UP!"
    # ok, Internet is up, is DNS server working?
    status1 = test_domain("Warning: Server DNS down")
    if status1 == "down":
      print "Server DNS is down, use default server DNS"
      #SET DNS number 1,2,3,..
      for i in range(DNS_SET_NUM):
	print
	print "Set Default DNS server number ", i+1
	editConf(DEFAULT_DNS[i], DEFAULT_DATA_DNS[i], DEFAULT_DNS_NUM[i], CONF)
	updateResolvconf()
	# is DNS up?
	status2 = test_domain("Warning: Server DNS down")
	if status2 == "down":
	  print "Warning: Default DNS server is down. Try the next..."
	else:
	  break
      # everyone default DNS server are down? or anyone is up?
      print
      if status2 == "up":
	print "Default DNS server setted and working!"
	print
      else:
	print "ERROR: NO WEB - DNS all DOWN: check your pc"
	print
	exit(1)
    else: #status1 == "up"
      print "DNS is UP!"
      print
      dns_list, dns_data_list, numero_dns = getDnsList()
      if numero_dns != 0:
	editConf(dns_list, dns_data_list, numero_dns, CONF)
	updateResolvconf()
	# ok, Internet is up, is OpenNIC DNS server working?
	status3 = test_domain("Warning: Server DNS OpenNIC down")
	if status3 == "up":
	  print
	  print "OpenNIC DNS server Setted and working!"
	  print
	else:
	  print
	  print "ERROR: NO WEB - OpenNIC DNS DOWN: check your pc"
	  print
	  exit(1)
      else:
	print
	print "WARNING: we couldn't get the nearest opennic servers from you."
	print "         Stay with old servers."
	print
vince committed
285

286 287 288 289 290 291 292 293 294 295 296
def testOpennic():
    """Tests if we can access Internet via domain names
    """
    print "Testing… ",
    status = test_domain("Warning: Server DNS down")
    if status == "down":
      print "test FAILED: we can't access domain URL like google.com"
      return 1
    else: #status == "up"
      print "test SUCCESSFUL: we can access domain URL like google.com"
      return 0
vince committed
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
def undo(conf_dir):
    """Put back the original configuration files."""
    files = ["tail", "head"]
    has_backup = False
    for f in files:
        f_path = conf_dir + f
        back = conf_dir + f + BACK_SUFFIX
        if os.path.exists(back):
            has_backup = True
            shutil.copyfile(back, f_path)
            print "backup of %s restored." % (f_path,)
            os.remove(back)

    if not has_backup:
        print "no backup found in %s" % (conf_dir,)

    return 0

316 317 318 319
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Use an Opennic DNS server. Usage: sudo python opennic-set.py")
    parser.add_argument("-t", "--test", action='store_true',
                    help="test if we can access an opennic's TLD")
320 321
    parser.add_argument("-u", "--undo", action='store_true',
                    help="put back the configuration files you had before starting playing with opennic-set.")
vince committed
322

323 324 325
    args = parser.parse_args()
    if args.test:
        exit(testOpennic())
vince committed
326

327 328 329 330
    if args.undo:
        exit(undo(DIR_RESOLVCONF))
    else:
        exit(main(sys.argv[1:]))