├── LICENSE ├── README.md ├── dnshjmon.py ├── dnshjmon_dns.conf.sample └── nameservers.conf.sample /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Corelan GCV 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dnshjmon 2 | ======== 3 | 4 | dnshjmon is a free tool to monitor public DNS records and report hijacks 5 | 6 | 7 | Syntax 8 | ------ 9 | python dnshjmon.py [arguments] 10 | 11 | Valid arguments: 12 | 13 | -h : show help 14 | 15 | -d : full path to dns config file. 16 | Defaults to dnshjmon_dns.conf in current folder 17 | 18 | -s : full path to file that contains 22 | DNS server IP addresses 23 | Use this setting to overrule the default behaviour 24 | of using the OS DNS server configuration 25 | ** Note: option -n requires the python-dnspython library ** 26 | (http://www.dnspython.org/) 27 | 28 | -mail : Test e-mail configuration 29 | 30 | 31 | DNS Config file 32 | ---------------- 33 | This is a flat ascii file (dnshjmon_dns.conf) that contains the list with hostnames that need to be checked and the list with valid IPs for that hostname. 34 | 35 | `hostname=ip` 36 | 37 | You can specify multiple IP addresses and/or even use CIDR notation. Simply separate entries with a comma: 38 | 39 | `hostname=127.0.0.1,192.168.0.1/25` 40 | 41 | If you want to exclude a certain IP, prefix it with a dash 42 | 43 | `hostname=127.0.0.1,192.168.0.1/25,-192.168.0.5` 44 | 45 | 46 | SMTP Config file 47 | ---------------- 48 | This file (dnshjmon_smtp.conf) will be created the first time you run dnshjmon.py, using an interactive wizard. 49 | If you want to add additional mailserver configurations or change the existing one, simply edit the conf file. 50 | You can test if the mail configuration works correctly by using the `-mail` argument. 51 | By default, emails will be sent with high-priority and requesting a return-receipt. 52 | 53 | 54 | Custom nameservers 55 | ------------------ 56 | By default, dnshjmon will use the OS DNS configuration. If you want to use specific nameservers, you'll need to install the python-dnspython library. dnshjmon will automatically detect if the library is present and allow you to use the -n option. 57 | Using -n, you can specify the location of a flat ascii file that contains the IP addresses of the nameservers to use. 58 | 59 | 60 | Usage 61 | ----- 62 | 63 | Simply schedule the script as a Cron job or Scheduled Task. 64 | Please note that the script was written and tested against python 2.7. 65 | More info: https://www.corelan.be/index.php/2013/12/29/a-chain-is-only-as-strong-as-its-weakest-link-dns-hijack-monitoring/ 66 | 67 | Disclaimer 68 | ---------- 69 | Use at your own risk. 70 | -------------------------------------------------------------------------------- /dnshjmon.py: -------------------------------------------------------------------------------- 1 | # 2 | # dnshjmon.py 3 | # written by corelanc0d3r 4 | # www.corelan.be 5 | # 6 | 7 | import os 8 | import sys 9 | import smtplib 10 | from email.mime.text import MIMEText 11 | from email.mime.multipart import MIMEMultipart 12 | from email import Encoders 13 | from email.MIMEBase import MIMEBase 14 | import socket 15 | from socket import gethostname 16 | import datetime 17 | 18 | global g_allowExternalResolver 19 | g_allowExternalResolver = False 20 | 21 | try: 22 | import dns.resolver 23 | g_allowExternalResolver = True 24 | except: 25 | pass 26 | 27 | 28 | 29 | # some helper stuff 30 | def getNow(): 31 | return datetime.datetime.now().strftime("%Y%m%d-%H:%M:%S") 32 | 33 | 34 | def showsyntax(args): 35 | print "" 36 | print " Usage: %s [arguments]" % args[0] 37 | print "" 38 | print " Optional arguments:" 39 | print " -h : show help\n" 40 | print " -d : full path to dns config file." 41 | print " Defaults to dnshjmon_dns.conf in current folder\n" 42 | print " -s : full path to file that contains " 46 | print " DNS server IP addresses" 47 | print " Use this setting to overrule the default behaviour" 48 | print " of using the OS DNS server configuration" 49 | if not g_allowExternalResolver: 50 | print " ** Note: option -n requires the python-dnspython library ** " 51 | print " (http://www.dnspython.org/)" 52 | print "" 53 | print " -mail : Test e-mail configuration" 54 | print "" 55 | return 56 | 57 | 58 | def showbanner(): 59 | 60 | print """ 61 | _ __ __ 62 | .--| |.-----..-----.| |--.|__|.--------..-----..-----. 63 | | _ || ||__ --|| || || || _ || | 64 | |_____||__|__||_____||__|__|| ||__|__|__||_____||__|__| 65 | |___| 66 | [ corelanc0d3r - www.corelan.be ] 67 | """ 68 | 69 | return 70 | 71 | 72 | def check_port(host, port): 73 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 74 | try: 75 | s.connect((host, port)) 76 | return True 77 | except: 78 | return False 79 | 80 | 81 | # CIDR to IPv4 list converter, written by Moshe (https://github.com/moshekaplan) 82 | def to_long(ip): 83 | ip = ip.split('.', 4) 84 | return int(ip[0])*(2**24) + int(ip[1])*(2**16) + int(ip[2])*(2**8) + int(ip[3]) 85 | 86 | 87 | def to_dotted_decimal(long_form): 88 | octets = [] 89 | for i in range(4): 90 | octets += [str(long_form % 2**8)] 91 | long_form = long_form >> 8 92 | return '.'.join(octets[::-1]) 93 | 94 | 95 | def cidr_to_ipv4(cidr): 96 | # Takes a CIDR address: 192.168.2.0/24 and returns a list of IP's in it 97 | ip, network_bits = cidr.split('/', 1) 98 | host_bits = 32 - int(network_bits) 99 | # Simplest approach: Turn it into a long, zero out the host bits and then iterate 100 | long_form = to_long(ip) 101 | # zero out the host bits 102 | start = (long_form >> host_bits) << host_bits 103 | for i in xrange(2**host_bits): 104 | yield to_dotted_decimal(start | i) 105 | 106 | def readDNSServerFile(dnsserverfile): 107 | dnsservers = [] 108 | try: 109 | f = open(dnsserverfile,"rb") 110 | content = f.readlines() 111 | f.close() 112 | for dnsline in content: 113 | if not dnsline.startswith('#') and dnsline.replace(" ","") != "": 114 | dnsserver = dnsline.replace("\n","").replace("\r","") 115 | if not dnsserver in dnsservers: 116 | dnsservers.append(dnsserver) 117 | except: 118 | print "[-] Unable to read DNS server file %s" % dnsserverfile 119 | return dnsservers 120 | 121 | 122 | # routine to perform DNS lookups 123 | def checkdns(dnsconfigfile, mailconfigfile, dnsservers): 124 | # get all records to test 125 | useOSDNS = True 126 | print "" 127 | print "[+] Running DNS check" 128 | if len(dnsservers) > 0: 129 | print "[+] Using %d DNS server(s) for queries:" % len(dnsservers) 130 | print " %s" % dnsservers 131 | useOSDNS = False 132 | else: 133 | print "[+] Using OS DNS configuration for queries" 134 | print "\r\nResults:" 135 | print "--------" 136 | toreport = [] 137 | cDNS = DNSConfig(dnsconfigfile) 138 | dnsscope, dnsscope_short = cDNS.getConfig() 139 | dnscnt = 1 140 | for dnscheck in dnsscope: 141 | extramsg = "" 142 | allresults = [] 143 | try: 144 | if useOSDNS: 145 | allresults = [socket.gethostbyname(dnscheck)] 146 | else: 147 | r = dns.resolver.Resolver() 148 | r.nameservers = dnsservers 149 | dnsdata = r.query(dnscheck) 150 | for rdata in dnsdata: 151 | thisresult = rdata.address 152 | if not thisresult in allresults: 153 | allresults.append(thisresult) 154 | extramsg = "(%s) " % allresults 155 | except: 156 | allresults = ["?.?.?.? (unable to resolve)"] 157 | extramsg = "(Error looking up IP)" 158 | siteok = True 159 | for thisresult in allresults: 160 | if not thisresult in dnsscope[dnscheck]: 161 | siteok = False 162 | extramsg = "(%s : Record manipulated?)" % allresults 163 | toreport.append("%s: %s resolves to %s, but it should be %s" % 164 | (getNow(), dnscheck, thisresult, dnsscope_short[dnscheck])) 165 | print "%d. %s - check OK? : %s %s" % (dnscnt,dnscheck, str(siteok).lower(), extramsg) 166 | dnscnt += 1 167 | 168 | print "" 169 | print "[+] Done checking, tested %d sites, reported %d IP mismatches" % \ 170 | (len(dnsscope), len(toreport)) 171 | 172 | if len(toreport) > 0: 173 | print "" 174 | print "*" * 50 175 | print "%d DNS record(s) may have been manipulated:" % len(toreport) 176 | 177 | mailbody = [] 178 | mailbody.append("Hi,") 179 | mailbody.append("") 180 | mailbody.append("dnshjmon.py has detected %d DNS resolution issues:" % len(toreport)) 181 | mailbody.append("") 182 | for tr in toreport: 183 | mailbody.append(tr) 184 | print tr 185 | print "*" * 50 186 | mailbody.append("") 187 | mailbody.append("Report generated with dnshjmon.py - https://github.com/corelan/dnshjmon") 188 | mailhandler = Mailer(mailconfigfile) 189 | mailhandler.sendmail(mailbody) 190 | 191 | return 192 | 193 | 194 | # ----- classes ----- 195 | class MailConfig: 196 | 197 | """ 198 | Class to manage SMTP email config 199 | """ 200 | 201 | serverinfo = {} 202 | 203 | def __init__(self, filename): 204 | self.filename = filename 205 | self.fullpath = os.path.join(os.path.dirname(os.path.realpath(__file__)), filename) 206 | 207 | def configFileExists(self): 208 | return os.path.isfile(self.fullpath) 209 | 210 | def readConfigFile(self): 211 | f = open(self.fullpath, "r") 212 | content = f.readlines() 213 | f.close() 214 | serverdata = {} 215 | thisid = "" 216 | for l in content: 217 | line = l.replace("\n", "").replace("\r", "") 218 | if line.startswith("[") and line.endswith("]"): 219 | # new config 220 | # if we already have a config, save it first 221 | if thisid != "" and len(serverdata) > 0 and not thisid in self.serverinfo: 222 | self.serverinfo[thisid] = serverdata 223 | thisid = line[1:-1] 224 | serverdata = {} 225 | if not line.startswith("#") and len(line) > 0 and "=" in line: 226 | lineparts = line.split("=") 227 | configparam = lineparts[0] 228 | if len(lineparts) > 1 and len(configparam) > 0 and len(line) > len(configparam): 229 | configval = line[len(configparam)+1:] 230 | serverdata[configparam] = configval 231 | # save the last one too 232 | if thisid != "" and len(serverdata) > 0 and not thisid in self.serverinfo: 233 | self.serverinfo[thisid] = serverdata 234 | 235 | return 236 | 237 | def writeConfigFile(self): 238 | filecontent = [] 239 | for configid in self.serverinfo: 240 | thisdata = self.serverinfo[configid] 241 | filecontent.append("[%s]" % str(configid)) 242 | filecontent += thisdata 243 | 244 | f = open(self.fullpath, "wb") 245 | for l in filecontent: 246 | f.write("%s\n" % l) 247 | f.close() 248 | print "[+] Saved new config file" 249 | return 250 | 251 | def initConfigFile(self): 252 | print "[+] Creating a new config file." 253 | i_server = "" 254 | i_port = 25 255 | i_timeout = 300 256 | i_auth = "no" 257 | i_user = "" 258 | i_pass = "" 259 | i_from = "" 260 | i_to = "" 261 | i_tls = "no" 262 | 263 | while True: 264 | i_server = raw_input(' > Enter smtp mail server IP or hostname: ') 265 | if not i_server == "": 266 | break 267 | 268 | while True: 269 | i_port = raw_input(' > Enter mail server port (default: 25): ') 270 | if not str(i_port) == "": 271 | try: 272 | i_port = int(i_port) 273 | break 274 | except: 275 | continue 276 | else: 277 | i_port = 25 278 | break 279 | 280 | while True: 281 | i_from = raw_input(" > Enter 'From' email address: ") 282 | if not i_from == "": 283 | break 284 | 285 | while True: 286 | i_to = raw_input(" > Enter 'To' email address: ") 287 | if not i_to == "": 288 | break 289 | 290 | while True: 291 | i_timeout = raw_input(' > Enter mail server timeout (in seconds, default: 300): ') 292 | if not str(i_timeout) == "": 293 | try: 294 | i_timeout = int(i_timeout) 295 | break 296 | except: 297 | continue 298 | else: 299 | i_timeout = 300 300 | break 301 | 302 | while True: 303 | i_auth = raw_input(' > Does server require authentication? (yes/no, default: no): ') 304 | i_auth = i_auth.lower() 305 | if i_auth == "": 306 | i_auth = "no" 307 | if i_auth in ["yes", "no"]: 308 | break 309 | 310 | if i_auth == "yes": 311 | while True: 312 | i_user = raw_input(' > Username: ') 313 | if not i_user == "": 314 | break 315 | while True: 316 | i_pass = raw_input(' > Password: ') 317 | if not i_pass == "": 318 | break 319 | 320 | while True: 321 | i_tls = raw_input(' > Does server require/support STARTTLS ? (yes/no, default: no): ') 322 | i_tls = i_tls.lower() 323 | if i_tls == "": 324 | i_tls = "no" 325 | if i_tls in ["yes", "no"]: 326 | break 327 | 328 | initserverdata = [] 329 | initserverdata.append("server=%s" % i_server) 330 | initserverdata.append("port=%d" % i_port) 331 | initserverdata.append("from=%s" % i_from) 332 | initserverdata.append("to=%s" % i_to) 333 | initserverdata.append("timeout=%d" % i_timeout) 334 | initserverdata.append("auth=%s" % i_auth) 335 | initserverdata.append("user=%s" % i_user) 336 | initserverdata.append("pass=%s" % i_pass) 337 | initserverdata.append("tls=%s" % i_tls) 338 | 339 | self.serverinfo = {} 340 | self.serverinfo[i_server] = initserverdata 341 | self.writeConfigFile() 342 | return 343 | 344 | 345 | class DNSConfig: 346 | 347 | """ 348 | Class to manage DNS email config 349 | """ 350 | 351 | def __init__(self, configfile): 352 | self.configfile = configfile 353 | return 354 | 355 | def getConfig(self): 356 | configrecords = {} 357 | configrecords_short = {} 358 | f = open(self.configfile, "rb") 359 | contents = f.readlines() 360 | f.close 361 | for thisline in contents: 362 | if not thisline.replace(" ", "") == "" and not thisline.startswith("#"): 363 | thislineparts = thisline.split("=") 364 | if len(thislineparts) == 2: 365 | sitename = thislineparts[0].replace(" ", "") 366 | siteiplist = thislineparts[1].replace("\r", "").replace("\n", "").replace(" ", "") 367 | if len(sitename) > 0 and len(siteiplist) > 0: 368 | siteips = siteiplist.split(',') 369 | # explode if necessary 370 | iplist = [] 371 | # first add IPs 372 | for thisip in siteips: 373 | if not thisip.startswith("-"): 374 | tip = thisip.replace("\\", "/") 375 | if "/" in tip: 376 | cidrlist = cidr_to_ipv4(tip) 377 | for ip in cidrlist: 378 | if not ip in iplist: 379 | iplist.append(ip) 380 | else: 381 | if not tip in iplist: 382 | iplist.append(thisip) 383 | if not sitename in configrecords_short: 384 | configrecords_short[sitename] = [thisip] 385 | else: 386 | configrecords_short[sitename].append(thisip) 387 | # then remove the ones that start with - 388 | for thisip in siteips: 389 | if thisip.startswith("-"): 390 | tip = thisip.replace("\\", "/").replace("-", "") 391 | if "/" in tip: 392 | cidrlist = cidr_to_ipv4(tip) 393 | for ip in cidrlist: 394 | if ip in iplist: 395 | iplist.remove(ip) 396 | else: 397 | if tip in iplist: 398 | iplist.remove(tip) 399 | 400 | # finally store in dictionary 401 | for thisip in iplist: 402 | if not sitename in configrecords: 403 | configrecords[sitename] = [thisip] 404 | else: 405 | configrecords[sitename].append(thisip) 406 | return configrecords, configrecords_short 407 | 408 | 409 | class Mailer: 410 | 411 | """ 412 | Class to handle email notifications 413 | """ 414 | 415 | def __init__(self, smtpconfigfile): 416 | self.server = "127.0.0.1" 417 | self.timeout = 300 418 | self.port = 25 419 | self.to = "root@127.0.0.1" 420 | self.fromaddress = "root@127.0.0.1" 421 | self.login = "" 422 | self.password = "" 423 | self.requirelogin = False 424 | self.usetls = False 425 | 426 | # read the config file 427 | cEmailConfig = MailConfig(smtpconfigfile) 428 | cEmailConfig.readConfigFile() 429 | serverconfigs = cEmailConfig.serverinfo 430 | # connect to the first one that is listening 431 | print "[+] Config file appears to contain %d mail server definitions" % len(serverconfigs) 432 | for mailid in serverconfigs: 433 | thisconfig = serverconfigs[mailid] 434 | if "server" in thisconfig: 435 | self.server = thisconfig["server"] 436 | if "port" in thisconfig: 437 | self.port = int(thisconfig["port"]) 438 | print "[+] Checking if %s:%d is reachable" % (self.server, self.port) 439 | if check_port(self.server, self.port): 440 | # fill out the rest and terminate the loop 441 | print " Yup, port is open" 442 | if "timeout" in thisconfig: 443 | self.timeout = int(thisconfig["timeout"]) 444 | if "auth" in thisconfig: 445 | if thisconfig["auth"] == "yes": 446 | self.requirelogin = True 447 | else: 448 | self.requirelogin = False 449 | if "user" in thisconfig: 450 | self.login = thisconfig["user"] 451 | if "pass" in thisconfig: 452 | self.password = thisconfig["pass"] 453 | if "tls" in thisconfig: 454 | if thisconfig["tls"] == "yes": 455 | self.usetls = True 456 | else: 457 | self.usetls = False 458 | if "to" in thisconfig: 459 | self.to = thisconfig["to"] 460 | if "from" in thisconfig: 461 | self.fromaddress = thisconfig["from"] 462 | break 463 | else: 464 | print " Nope" 465 | return 466 | 467 | def sendmail(self, info, logfile=[], mailsubject="DNS Hijack Monitor Alert"): 468 | msg = MIMEMultipart() 469 | bodytext = "\n".join(x for x in info) 470 | logtext = "\n".join(x for x in logfile) 471 | mailbody = MIMEText(bodytext, 'plain') 472 | msg.attach(mailbody) 473 | 474 | msg['Subject'] = '%s - %s' % (gethostname(), mailsubject) 475 | msg['From'] = self.fromaddress 476 | # uncomment the next line if you don't want return receipts 477 | msg['Disposition-Notification-To'] = self.fromaddress 478 | msg['To'] = self.to 479 | msg['X-Priority'] = '2' 480 | 481 | if len(logfile) > 0: 482 | part = MIMEBase('application', "octet-stream") 483 | part.set_payload(logtext) 484 | Encoders.encode_base64(part) 485 | part.add_header('Content-Disposition', 'attachment; filename="dnshjmon.txt"') 486 | msg.attach(part) 487 | noerror = False 488 | thistimeout = 5 489 | while not noerror: 490 | try: 491 | print "[+] Connecting to %s on port %d" % (self.server, self.port) 492 | s = smtplib.SMTP(self.server, self.port, 'minicase', self.timeout) 493 | print "[+] Connected" 494 | if self.usetls: 495 | print "[+] Issuing STARTTLS" 496 | s.starttls() 497 | print "[+] STARTTLS established" 498 | if self.requirelogin: 499 | print "[+] Authenticating" 500 | s.login(self.login, self.password) 501 | print "[+] Authenticated" 502 | print "[+] Sending email" 503 | s.sendmail(self.to, [self.to], msg.as_string()) 504 | print "[+] Mail sent, disconnecting" 505 | s.quit() 506 | noerror = True 507 | except smtplib.SMTPServerDisconnected as e: 508 | print " ** ERROR, Server disconnected unexpectedly" 509 | print " This is probably okay" 510 | noerror = True 511 | except smtplib.SMTPResponseException as e: 512 | print " ** ERROR Server returned %s : %s" % (str(e.smtp_code), e.smtp_error) 513 | except smtplib.SMTPSenderRefused as e: 514 | print " ** ERROR Sender refused %s : %s" % (str(e.smtp_code), smtp_error) 515 | except smtplib.SMTPRecipientsRefused as e: 516 | print " ** ERROR Recipients refused" 517 | except smtplib.SMTPDataError as e: 518 | print " ** ERROR Server refused to accept the data" 519 | except smtplib.SMTPConnectError as e: 520 | print " ** ERROR establishing connection to server" 521 | except smtplib.SMTPHeloError as e: 522 | print " ** ERROR HELO Error" 523 | #except smtplib.SMTPAUthenticationError as e: 524 | # print " ** ERROR Authentication" 525 | except smtplib.SMTPException as e: 526 | print " ** ERROR Sending email" 527 | except: 528 | print " ** ERROR Unable to send email !" 529 | 530 | if not noerror: 531 | print " I'll try again in %d seconds" % thistimeout 532 | time.sleep(thistimeout) 533 | if thistimeout < 1200: 534 | thistimeout += 5 535 | return 536 | 537 | 538 | # ----- main routine ----- 539 | if __name__ == "__main__": 540 | 541 | mailconfigerror = True 542 | dnsconfigerror = True 543 | workingfolder = os.getcwd() 544 | 545 | dnsconfigfile = os.path.join(workingfolder, "dnshjmon_dns.conf") 546 | mailconfigfile = os.path.join(workingfolder, "dnshjmon_smtp.conf") 547 | dnsserverfile = "" 548 | dnsservers = [] 549 | 550 | showbanner() 551 | 552 | arguments = [] 553 | if len(sys.argv) >= 2: 554 | arguments = sys.argv[1:] 555 | 556 | args = {} 557 | last = "" 558 | for word in arguments: 559 | if (word[0] == '-'): 560 | word = word.lstrip("-") 561 | args[word] = True 562 | last = word 563 | else: 564 | if (last != ""): 565 | if str(args[last]) == "True": 566 | args[last] = word 567 | else: 568 | args[last] = args[last] + " " + word 569 | 570 | if "h" in args: 571 | showsyntax(sys.argv) 572 | sys.exit(0) 573 | 574 | if "d" in args: 575 | if type(args["d"]).__name__.lower() != "bool": 576 | dnsconfigfile = args["d"] 577 | 578 | if "s" in args: 579 | if type(args["s"]).__name__.lower() != "bool": 580 | mailconfigfile = args["s"] 581 | 582 | if "n" in args and g_allowExternalResolver: 583 | if type(args["n"]).__name__.lower() != "bool": 584 | dnsserverfile = args["n"] 585 | if not os.path.isfile(dnsserverfile): 586 | print "[-] DNS server file %s not found, will use OS DNS configuration" % dnsserverfile 587 | dnsserverfile = "" 588 | else: 589 | dnsservers = readDNSServerFile(dnsserverfile) 590 | 591 | if not os.path.isfile(dnsconfigfile): 592 | print "[-] Configuration file %s not found, aborting..." % dnsconfigfile 593 | sys.exit(1) 594 | else: 595 | print "[+] Using dns config file %s" % dnsconfigfile 596 | 597 | 598 | # check email config file 599 | cEmailConfig = MailConfig(mailconfigfile) 600 | if not cEmailConfig.configFileExists(): 601 | print "[-] Oops, email config file %s doesn't exist yet" % mailconfigfile 602 | cEmailConfig.initConfigFile() 603 | else: 604 | print "[+] Using mail config file %s" % mailconfigfile 605 | cEmailConfig.readConfigFile() 606 | 607 | if "mail" in args: 608 | content = [] 609 | mailhandler = Mailer(mailconfigfile) 610 | info = ['dnshjmon.py email test'] 611 | mailhandler.sendmail(info, content, 'Email test') 612 | sys.exit(0) 613 | 614 | checkdns(dnsconfigfile, mailconfigfile, dnsservers) 615 | -------------------------------------------------------------------------------- /dnshjmon_dns.conf.sample: -------------------------------------------------------------------------------- 1 | # syntax: 2 | # hostname=ip 3 | # you can list multiple IPs or CIDR per hostname (separate IPs with comma) 4 | # if you want to exclude an IP or CIDR, simply prefix it with a - 5 | # example: 6 | # www.corelan.be=178.79.152.6/29,-178.79.152.6 -------------------------------------------------------------------------------- /nameservers.conf.sample: -------------------------------------------------------------------------------- 1 | # nameserver configuration file 2 | # note: dnshjmon can only use a custom list of DNS servers 3 | # if the python-dnspython library is installed 4 | 5 | # syntax: 6 | # IP of DNS server to use, one IP per line 7 | # Example: 8 | 8.8.8.8 9 | 208.67.222.222 --------------------------------------------------------------------------------