-
Notifications
You must be signed in to change notification settings - Fork 0
/
dnsMxGrapher.py
149 lines (105 loc) · 4.9 KB
/
dnsMxGrapher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import dns.resolver
import networkx as nx
import sys
def main():
domainFilename = 'NA'
isWriteGraphml = False
graphmlOutputFilename = 'NA'
isWriteCsv = False
csvOutputFilename = 'NA'
#At minimum we need a domain file as input
if '-domains' in sys.argv:
domainFilename = sys.argv[sys.argv.index('-domains') + 1]
else:
printUsage()
sys.exit('Need domain list filename. Ex: ' + sys.argv[0] + ' domains.txt');
#Check if graphml output is desired
if '-graphml' in sys.argv:
isWriteGraphml = True
graphmlOutputFilename = sys.argv[sys.argv.index('-graphml') + 1]
#Check if csv output is desired
if '-csv' in sys.argv:
isWriteCsv = True
csvOutputFilename = sys.argv[sys.argv.index('-csv') + 1]
print('Getting MX data: Started')
domainToMxSetMap = getMxMapping(domainFilename)
print('Getting MX data: Done')
if isWriteGraphml:
print('Writing graphml file')
diGraph = getDirectedGraph(domainToMxSetMap)
convertToGraphmlLines(diGraph, graphmlOutputFilename)
if isWriteCsv:
print('Writing csv file')
csvLines = convertToCsvLines(domainToMxSetMap)
writeLinesToFile(csvOutputFilename, csvLines)
def printUsage():
print(sys.argv[0] + ' Usage')
print("\tRead MX domains and print | " + sys.argv[0] + ' -domains filename')
print("\tWrite output as graphml | " + sys.argv[0] + ' -domains filename -graphml outputFilename')
print("\tWrite output as csv | " + sys.argv[0] + ' -domains filename -csv outputFilename')
print("\tWrite output as graphml and csv | " + sys.argv[0] + ' -domains filename -graphml outputFilename -csv outputFilename')
print()
def writeLinesToFile(filename, lines):
outputFile = open(filename,'w')
for line in lines:
outputFile.write(line + '\n')
outputFile.close()
def convertToGraphmlLines(directedGraph, graphmlFilename):
nx.write_graphml(directedGraph, graphmlFilename)
def convertToCsvLines(domainToMxSetMap):
lines = []
lines.append('domain,mxDomain')
for domain,mxSet in domainToMxSetMap.items():
for mxDomain in mxSet:
lines.append(domain + ',' + mxDomain)
return lines
def getDirectedGraph(domainToMxSetMap):
DG=nx.DiGraph()
#Add nodes
domainSet = set()
domainKeys = sorted(domainToMxSetMap.keys())
domainSet.update(domainKeys)
#MX domain nodes
for domain,mxSet in domainToMxSetMap.items():
domainSet.update(mxSet)
for domain in domainSet:
nodeType = 'domain' if domain in domainKeys else 'mxDomain'
DG.add_node(domain, type=nodeType, label=domain)
#Add edges
for domain,mxSet in domainToMxSetMap.items():
for mxDomain in mxSet:
DG.add_edge(domain,mxDomain)
return DG
def getMxMapping(domainsFilename):
domainToMxSetMap = {}
#Open file containing domain names
with open(domainsFilename) as f:
for domain in f:
#Remove any whitespace from the raw line
domain = domain.strip()
print('Querying for MX record for domain: ' + domain)
#Use a set to derive unique entries without reinventing the wheel
mxRecordSet = set()
#Query for DNS MX records. Loop over each Name object.
try:
for mxData in dns.resolver.query(domain, 'MX'):
#Only split and use records that look like a domain name
if '.' in mxData.exchange.to_text(True):
#Split into TLD and name. Goal here is to remove specific subdomains
splitMxExchange = mxData.exchange.split(3)
#Some domains are all uppercase. Some lower. Let's make
#them the same here
mxDomain = splitMxExchange[1].to_text(True).lower()
#Add to the set. Will write to the file later after using the set
#to derive only unique entries
mxRecordSet.add(mxDomain)
else:
print('Could not determine if this was a valid domain name. Found: ' + mxData.exchange.to_text(True));
domainToMxSetMap[domain] = mxRecordSet;
except KeyError:
print('Could not get MX record for: ' + domain + '. Received: KeyError');
except dns.resolver.NoAnswer:
print('Could not get MX record for: ' + domain + '. Received: NoAnswer');
return domainToMxSetMap
if __name__== "__main__":
main()