wlvalidate 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/wlvalidate.rb +316 -0
  2. 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: []