wlvalidate 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|