wlvalidate 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/wlvalidate.rb +316 -0
- metadata +45 -0
data/lib/wlvalidate.rb
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Results of the below calls will be returned in the following format
|
4
|
+
(TYPE, RESULT, DATA and EXT_DATA are return values in each hash line)
|
5
|
+
+=======+===========+===================+===========++========+=================+======================+
|
6
|
+
[ TYPE | RESULT | DATA | EXT_DATA ][ Status | Return Type | Return on all paths? ]
|
7
|
+
+=======+===========+===================+===========++========+=================+======================+
|
8
|
+
| A | PASS/FAIL | (none) | (none) || Done | Array of hashes | Yes |
|
9
|
+
| SPF | PASS/FAIL | SPF record | Fail type || Done | Hash | Yes |
|
10
|
+
| CNAME | PASS/FAIL | CNAME | (none) || Done | Hash | Yes |
|
11
|
+
| DKIM1 | PASS/FAIL | (none) | CNAME/TXT || Done | Hash | Yes |
|
12
|
+
| DKIM2 | PASS/FAIL | (none) | CNAME/TXT || Done | Hash | Yes |
|
13
|
+
| MX | INFO | MX Hostname | Priority || Done | Array of hashes | |
|
14
|
+
+-------+-----------+-------------------+-----------++--------+-----------------+----------------------+
|
15
|
+
| ALL | (multi) | (multi) | (multi) || Done | Array of hashes | (n/a) |
|
16
|
+
+-------+-----------+-------------------+-----------++--------+-----------------+----------------------+
|
17
|
+
|
18
|
+
TO DO:
|
19
|
+
- Add master/slave DNS option
|
20
|
+
- Validate hashes for Ruby
|
21
|
+
|
22
|
+
=end
|
23
|
+
|
24
|
+
# Must have dnsruby gem installed for this to work
|
25
|
+
require 'dnsruby'
|
26
|
+
include Dnsruby
|
27
|
+
|
28
|
+
module WLValidate
|
29
|
+
class Validate
|
30
|
+
def initialize(attributes=nil)
|
31
|
+
@intDNSTimeout = 5
|
32
|
+
@intDNSIPLoops = 5
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.ALL(strWL, strDomain)
|
36
|
+
# PURPOSE: Execute all of the below methods as a single call
|
37
|
+
# RETURNS: Returns the combined array of hashes as returned by each function
|
38
|
+
# NOTES: Need to encapsulate all functions in a begin/capture/end to catch anything odd
|
39
|
+
|
40
|
+
arrayReturns = Array.new
|
41
|
+
|
42
|
+
hashSPF = self.SPF(strWL, strDomain)
|
43
|
+
hashCNAME = self.CNAME(strWL, strDomain)
|
44
|
+
hashA = self.A(strWL, strDomain)
|
45
|
+
hashDKIM1 = self.DKIM1(strWL, strDomain)
|
46
|
+
hashDKIM2 = self.DKIM2(strWL, strDomain)
|
47
|
+
arrayMX = self.MX(strWL, strDomain)
|
48
|
+
|
49
|
+
# array.push hashes, and array.concat arrays
|
50
|
+
arrayReturns.push(hashSPF)
|
51
|
+
arrayReturns.push(hashCNAME)
|
52
|
+
arrayReturns.concat(hashA)
|
53
|
+
arrayReturns.push(hashDKIM1)
|
54
|
+
arrayReturns.push(hashDKIM2)
|
55
|
+
arrayReturns.concat(arrayMX)
|
56
|
+
|
57
|
+
return arrayReturns
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.MX(strWL, strDomain)
|
61
|
+
# PURPOSE: Loop through all <domain> MX records
|
62
|
+
# RETURNS: Array of hashes
|
63
|
+
hashTemp = Hash.new
|
64
|
+
arrayReturn = Array.new
|
65
|
+
arrayMX = Array.new
|
66
|
+
|
67
|
+
if ((strWL == "") || (strDomain == ""))
|
68
|
+
hashReturn = { "type" => "MX", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more parameters are missing!" }
|
69
|
+
end
|
70
|
+
|
71
|
+
Dnsruby::DNS.open {|dns|
|
72
|
+
begin
|
73
|
+
arrayMX = dns.getresources(strDomain, Dnsruby::Types.MX)
|
74
|
+
if (arrayMX.count > 0)
|
75
|
+
arrayMX.sort!
|
76
|
+
arrayMX.each do |r|
|
77
|
+
myExchange = r.exchange.to_s
|
78
|
+
myPreference = r.preference.to_s
|
79
|
+
hashTemp = { "type" => "MX", "result" => "INFO", "data" => myExchange, "ext_data" => myPreference }
|
80
|
+
arrayReturn.push(hashTemp)
|
81
|
+
hashTemp = {}
|
82
|
+
end
|
83
|
+
else
|
84
|
+
hashTemp = { "type" => "MX", "result" => "FAIL", "data" => "No records", "ext_data" => "No MX records exist for this domain" }
|
85
|
+
arrayReturn.push(hashTemp)
|
86
|
+
hashTemp = {}
|
87
|
+
end
|
88
|
+
rescue
|
89
|
+
hashTemp = { "type" => "MX", "result" => "ERROR", "data" => "Unable to resolve", "ext_data" => "Unable to resolve MX records for domain " + strDomain }
|
90
|
+
arrayReturn.push(hashTemp)
|
91
|
+
hashTemp = {}
|
92
|
+
end
|
93
|
+
}
|
94
|
+
if arrayReturn.empty?
|
95
|
+
hashReturn = { "type" => "MX", "result" => "FAIL", "data" => "No data", "ext_data" => "No MX records in domain" }
|
96
|
+
arrayReturn.push(hashReturn)
|
97
|
+
hashReturn = {}
|
98
|
+
end
|
99
|
+
return arrayReturn
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.A(strWL, strDomain)
|
103
|
+
# PURPOSE: Loop through all oN.<wl>.<domain>.<tlds> to get IPs, then also do reverse DNS checks
|
104
|
+
# RETURNS: Hash - see above for definition
|
105
|
+
|
106
|
+
hashReturn = Hash.new
|
107
|
+
arrayReturn = Array.new
|
108
|
+
|
109
|
+
if ((strWL == "") || (strDomain == ""))
|
110
|
+
hashReturn = { "type" => "A", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more expected paramters are missing!" }
|
111
|
+
end
|
112
|
+
|
113
|
+
i = 1
|
114
|
+
intDNSIPLoops = 11
|
115
|
+
while i < intDNSIPLoops do
|
116
|
+
begin
|
117
|
+
tmpHost = "o" + i.to_s + "." + strWL + "." + strDomain
|
118
|
+
tmpIP = Dnsruby::Resolv.getaddress(tmpHost)
|
119
|
+
resolvedHost = Dnsruby::Resolv.getname(tmpIP)
|
120
|
+
if tmpHost == resolvedHost
|
121
|
+
hashReturn = { "type" => "A", "result" => "PASS", "data" => tmpHost, "ext_data" => tmpIP }
|
122
|
+
arrayReturn.push(hashReturn)
|
123
|
+
hashReturn = {}
|
124
|
+
end
|
125
|
+
rescue Dnsruby::ResolvTimeout
|
126
|
+
hashReturn = { "type" => "A", "result" => "ERROR", "data" => tmpHost, "ext_data" => "Timed out while attempting rDNS lookup" }
|
127
|
+
arrayReturn.push(hashReturn)
|
128
|
+
hashReturn = {}
|
129
|
+
rescue
|
130
|
+
#hashReturn = { "type" => "A", "result" => "ERROR", "data" => tmpHost, "ext_data" => "Unable to resolve rDNS for host" }
|
131
|
+
#arrayReturn.push(hashReturn)
|
132
|
+
#hashReturn = {}
|
133
|
+
end
|
134
|
+
i += 1
|
135
|
+
end
|
136
|
+
if arrayReturn.empty?
|
137
|
+
hashReturn = { "type" => "A", "result" => "FAIL", "data" => "No data", "ext_data" => "No SendGrid IPs" }
|
138
|
+
arrayReturn.push(hashReturn)
|
139
|
+
hashReturn = {}
|
140
|
+
end
|
141
|
+
|
142
|
+
return arrayReturn
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.CNAME(strWL, strDomain)
|
146
|
+
# PURPOSE: Get CNAME record and make sure it's mapped to 'sendgrid.net'
|
147
|
+
# RETURNS: Hash - See above for definition
|
148
|
+
|
149
|
+
hashReturn = Hash.new
|
150
|
+
boolFoundMatch = false
|
151
|
+
|
152
|
+
if ((strWL == "") || (strDomain == ""))
|
153
|
+
hashReturn = { "type" => "CNAME", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more expected paramters are missing!" }
|
154
|
+
end
|
155
|
+
|
156
|
+
Dnsruby::DNS.open {|dns|
|
157
|
+
begin
|
158
|
+
strHost = strWL + "." + strDomain
|
159
|
+
arrayCNAME = dns.getresources(strHost, Dnsruby::Types.CNAME)
|
160
|
+
arrayCNAME.each do |r|
|
161
|
+
if boolFoundMatch == false
|
162
|
+
tmpCNAME = r.domainname.to_s
|
163
|
+
if tmpCNAME.match %r{sendgrid.net}
|
164
|
+
hashReturn = { "type" => "CNAME", "result" => "PASS", "data" => strHost, "ext_data" => tmpCNAME }
|
165
|
+
boolFoundMatch = true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
if hashReturn.empty?
|
169
|
+
hashReturn = { "type" => "CNAME", "result" => "FAIL", "data" => "No data", "ext_data" => "No matching CNAME records found" }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
rescue Dnsruby::ResolvTimeout
|
173
|
+
hashReturn = { "type" => "CNAME", "result" => "ERROR", "data" => strHost, "ext_data" => "Timed out while attempting rDNS lookup" }
|
174
|
+
rescue
|
175
|
+
hashReturn = { "type" => "CNAME", "result" => "ERROR", "data" => strHost, "ext_data" => "Unable to resolve host, or query timed out" }
|
176
|
+
end
|
177
|
+
}
|
178
|
+
return hashReturn
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.DKIM1(strWL, strDomain)
|
182
|
+
# PURPOSE: Get TXT/CNAME record for smtpapi._domainkey.<domain>.<tlds>
|
183
|
+
# RETURNS: Hash - See above for definition
|
184
|
+
|
185
|
+
hashReturn = Hash.new
|
186
|
+
boolFoundMatch = false
|
187
|
+
strHost = "smtpapi._domainkey." + strDomain
|
188
|
+
strDKIM = "k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtW5iwpXVPiH5FzJ7Nrl8USzuY9zqqzjE0D1r04xDN6qwziDnmgcFNNfMewVKN2D1O+2J9N14hRprzByFwfQW76yojh54Xu3uSbQ3JP0A7k8o8GutRF8zbFUA8n0ZH2y0cIEjMliXY4W4LwPA7m4q0ObmvSjhd63O9d8z1XkUBwIDAQAB"
|
189
|
+
strDKIM_k = "rsa"
|
190
|
+
strDKIM_t = "s"
|
191
|
+
strDKIM_p = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtW5iwpXVPiH5FzJ7Nrl8USzuY9zqqzjE0D1r04xDN6qwziDnmgcFNNfMewVKN2D1O+2J9N14hRprzByFwfQW76yojh54Xu3uSbQ3JP0A7k8o8GutRF8zbFUA8n0ZH2y0cIEjMliXY4W4LwPA7m4q0ObmvSjhd63O9d8z1XkUBwIDAQAB"
|
192
|
+
|
193
|
+
if ((strWL == "") || (strDomain == ""))
|
194
|
+
hashReturn = { "type" => "DKIM1", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more expected paramters are missing!" }
|
195
|
+
end
|
196
|
+
|
197
|
+
Dnsruby::DNS.open {|dns|
|
198
|
+
begin
|
199
|
+
arrayDKIM1 = dns.getresources(strHost, Dnsruby::Types.TXT)
|
200
|
+
arrayDKIM1.each do |r|
|
201
|
+
if boolFoundMatch == false
|
202
|
+
puts "DKIM data: #{r.data}"
|
203
|
+
r.data.split(" ").each {|k|
|
204
|
+
puts "#{k}"
|
205
|
+
}
|
206
|
+
puts "r.name.to_s => #{r.name.to_s}"
|
207
|
+
if (r.name.to_s == "dkim.sendgrid.net")
|
208
|
+
hashReturn = { "type" => "DKIM1", "result" => "PASS", "data" => r.name.to_s, "ext_data" => "cname" }
|
209
|
+
boolFoundMatch = true
|
210
|
+
elsif ( (r.name.to_s == strHost) && (r.data.split(" ")[0].match %r{#{Regexp.escape(strDKIM_k)}}) && (r.data.split(" ")[1].match %r{#{Regexp.escape(strDKIM_t)}}) && (r.data.split(" ")[2].match %r{#{Regexp.escape(strDKIM_p)}}) )
|
211
|
+
hashReturn = { "type" => "DKIM1", "result" => "PASS", "data" => r.name.to_s, "ext_data" => "txt" }
|
212
|
+
boolFoundMatch = true
|
213
|
+
else
|
214
|
+
hashReturn = { "type" => "DKIM1", "result" => "FAIL", "data" => strHost, "ext_data" => "No DKIM record found as either CNAME or TXT" }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
if hashReturn.empty?
|
219
|
+
hashReturn = { "type" => "DKIM2", "result" => "FAIL", "data" => strHost, "ext_data" => "No matching DKIM/TXT records found" }
|
220
|
+
end
|
221
|
+
rescue
|
222
|
+
hashReturn = { "type" => "DKIM1", "result" => "ERROR", "data" => strHost, "ext_data" => "Unable to resolve host, or query timed out" }
|
223
|
+
end
|
224
|
+
}
|
225
|
+
return hashReturn
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.DKIM2(strWL, strDomain)
|
229
|
+
# PURPOSE: Get TXT/CNAME record for smtpapi._domainkey.<wl>.<domain>.<tlds>
|
230
|
+
# RETURNS: Hash - See above for definition
|
231
|
+
|
232
|
+
hashReturn = Hash.new
|
233
|
+
boolFoundMatch = false
|
234
|
+
strHost = "smtpapi._domainkey." + strWL + "." + strDomain
|
235
|
+
strDKIM = "k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtW5iwpXVPiH5FzJ7Nrl8USzuY9zqqzjE0D1r04xDN6qwziDnmgcFNNfMewVKN2D1O+2J9N14hRprzByFwfQW76yojh54Xu3uSbQ3JP0A7k8o8GutRF8zbFUA8n0ZH2y0cIEjMliXY4W4LwPA7m4q0ObmvSjhd63O9d8z1XkUBwIDAQAB"
|
236
|
+
|
237
|
+
if ((strWL == "") || (strDomain == ""))
|
238
|
+
hashReturn = { "type" => "DKIM2", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more expected paramters are missing!" }
|
239
|
+
end
|
240
|
+
|
241
|
+
Dnsruby::DNS.open {|dns|
|
242
|
+
begin
|
243
|
+
arrayDKIM2 = dns.getresources(strHost, Dnsruby::Types.TXT)
|
244
|
+
arrayDKIM2.each do |r|
|
245
|
+
if boolFoundMatch == false
|
246
|
+
if (r.name.to_s == "dkim.sendgrid.net")
|
247
|
+
hashReturn = { "type" => "DKIM2", "result" => "PASS", "data" => r.name.to_s, "ext_data" => "cname" }
|
248
|
+
boolFoundMatch = true
|
249
|
+
elsif ((r.name.to_s == strHost) && (r.data == strDKIM))
|
250
|
+
hashReturn = { "type" => "DKIM2", "result" => "PASS", "data" => r.name.to_s, "ext_data" => "txt" }
|
251
|
+
boolFoundMatch = true
|
252
|
+
else
|
253
|
+
hashReturn = { "type" => "DKIM2", "result" => "FAIL", "data" => strHost, "ext_data" => "No DKIM record found as either CNAME or TXT" }
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
if hashReturn.empty?
|
258
|
+
hashReturn = { "type" => "DKIM2", "result" => "FAIL", "data" => strHost, "ext_data" => "No matching DKIM/TXT records found" }
|
259
|
+
end
|
260
|
+
rescue
|
261
|
+
hashReturn = { "type" => "DKIM2", "result" => "ERROR", "data" => strHost, "ext_data" => "Unable to resolve host, or query timed out" }
|
262
|
+
end
|
263
|
+
}
|
264
|
+
return hashReturn
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.SPF(strWL, strDomain)
|
268
|
+
# PURPOSE: Get TXT record and make sure it has include:sendgrid.net
|
269
|
+
# RETURNS: Hash - See above for definition
|
270
|
+
|
271
|
+
hashReturn = Hash.new
|
272
|
+
boolFoundMatch = false
|
273
|
+
|
274
|
+
if ((strWL == "") || (strDomain == ""))
|
275
|
+
hashReturn = { "type" => "SPF", "result" => "ERROR", "data" => "Missing parameters", "ext_data" => "One or more expected paramters are missing!" }
|
276
|
+
end
|
277
|
+
|
278
|
+
Dnsruby::DNS.open {|dns|
|
279
|
+
begin
|
280
|
+
arrayTXT = dns.getresources(strDomain, Dnsruby::Types.TXT)
|
281
|
+
if arrayTXT.to_s != "[]"
|
282
|
+
arrayTXT.each do |r|
|
283
|
+
if boolFoundMatch == false
|
284
|
+
rTXT = r.strings.to_s
|
285
|
+
if ((rTXT.match('v=spf1')) && (rTXT.match('include:sendgrid.net')))
|
286
|
+
if rTXT.match %r{~al}
|
287
|
+
hashReturn = { "type" => "SPF", "result" => "PASS", "data" => rTXT, "ext_data" => "softfail" }
|
288
|
+
elsif rTXT.match %r{-al}
|
289
|
+
hashReturn = { "type" => "SPF", "result" => "PASS", "data" => rTXT, "ext_data" => "fail" }
|
290
|
+
elsif rTXT =~/^+al/
|
291
|
+
hashReturn = { "type" => "SPF", "result" => "PASS", "data" => rTXT, "ext_data" => "pass" }
|
292
|
+
elsif rTXT.match %r{\?al}
|
293
|
+
hashReturn = { "type" => "SPF", "result" => "PASS", "data" => rTXT, "ext_data" => "neutral" }
|
294
|
+
else
|
295
|
+
hashReturn = { "type" => "SPF", "result" => "PASS", "data" => rTXT, "ext_data" => "" }
|
296
|
+
end
|
297
|
+
boolFoundMatch = true
|
298
|
+
else
|
299
|
+
hashReturn = { "type" => "SPF", "result" => "FAIL", "data" => "No record", "ext_data" => "The requested SPF record was not found" }
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
if hashReturn.empty?
|
304
|
+
hashReturn = { "type" => "SPF", "result" => "FAIL", "data" => "No record", "ext_data" => "No matching DKIM/TXT records found" }
|
305
|
+
end
|
306
|
+
else
|
307
|
+
hashReturn = { "type" => "SPF", "result" => "FAIL", "data" => "No record", "ext_data" => "No TXT records found for domain" }
|
308
|
+
end
|
309
|
+
rescue
|
310
|
+
hashReturn = { "type" => "SPF", "result" => "ERROR", "data" => "No record", "ext_data" => "The requested SPF record was not found" }
|
311
|
+
end
|
312
|
+
}
|
313
|
+
return hashReturn
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wlvalidate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jayson M. Sperling
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: SendGrid Rails Gem to validate White Label settings
|
15
|
+
email: jayson.sperling@sendgrid.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/wlvalidate.rb
|
21
|
+
homepage: http://dev.tluw.net/sg/wlvalidate
|
22
|
+
licenses: []
|
23
|
+
post_install_message:
|
24
|
+
rdoc_options: []
|
25
|
+
require_paths:
|
26
|
+
- lib
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 1.8.23
|
42
|
+
signing_key:
|
43
|
+
specification_version: 3
|
44
|
+
summary: SendGrid White Label Validator
|
45
|
+
test_files: []
|