-
Notifications
You must be signed in to change notification settings - Fork 0
/
domain.go
344 lines (271 loc) · 8.86 KB
/
domain.go
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
// Copyright 2021 Adam Chalkley
//
// https://github.com/atc0005/check-whois
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.
package domain
import (
"errors"
"fmt"
"math"
"strings"
"time"
"github.com/atc0005/go-nagios"
whoisparser "github.com/likexian/whois-parser"
)
// DomainDateLayout is the chosen date layout for displaying
// domain created/expiration date/time values across our application.
const DomainDateLayout string = "2006-01-02 15:04:05 -0700 MST"
// ErrDomainExpired is returned whenever a specified domain has expirem.
var ErrDomainExpired = errors.New("domain has expired")
// ErrDomainExpiring is returned whenever a specified domain is expiring.
var ErrDomainExpiring = errors.New("domain is expiring")
// Metadata represents the details for a specified domain, including the
// parsed WHOIS info, expiration (age) thresholds and parsed date values.
type Metadata struct {
// Results from parsing the WHOIS info. Most fields are plain text values.
WhoisInfo whoisparser.WhoisInfo
// Name is the plaintext label for this domain.
Name string
// ExpirationDate indicates when this domain expires.
ExpirationDate time.Time
// UpdatedDate indicates when the domain WHOIS metadata was last updatem.
UpdatedDate time.Time
// CreatedDate indicates when this domain was created/registerem.
CreatedDate time.Time
// AgeWarningThreshold is the specified age threshold for when domains
// with an expiration less than this value are considered to be in a
// WARNING state.
AgeWarningThreshold time.Time
// AgeCRITICALThreshold is the specified age threshold for when domains
// with an expiration less than this value are considered to be in a
// CRITICAL state.
AgeCriticalThreshold time.Time
}
// ServiceState represents the status label and exit code for a service check.
type ServiceState struct {
// Label maps directly to one of the supported Nagios state labels.
Label string
// ExitCode is the exit or exit status code associated with a Nagios
// service check.
ExitCode int
}
// NewDomain instantiates a new Metadata type from parsed WHOIS data
func NewDomain(whoisInfo whoisparser.WhoisInfo, ageWarning time.Time, ageCritical time.Time) (*Metadata, error) {
// parse expiration date string
// 2028-09-14T04:00:00Z
expirationDate, err := time.Parse(time.RFC3339, whoisInfo.Domain.ExpirationDate)
if err != nil {
return nil, fmt.Errorf(
"failed to parse domain expiration date: %w",
err,
)
}
updatedDate, err := time.Parse(time.RFC3339, whoisInfo.Domain.UpdatedDate)
if err != nil {
return nil, fmt.Errorf(
"failed to parse domain updated date: %w",
err,
)
}
createdDate, err := time.Parse(time.RFC3339, whoisInfo.Domain.ExpirationDate)
if err != nil {
return nil, fmt.Errorf(
"failed to parse domain creation date: %w",
err,
)
}
d := Metadata{
AgeWarningThreshold: ageWarning,
AgeCriticalThreshold: ageCritical,
WhoisInfo: whoisInfo,
Name: whoisInfo.Domain.Domain,
ExpirationDate: expirationDate,
UpdatedDate: updatedDate,
CreatedDate: createdDate,
}
return &d, nil
}
// OneLineCheckSummary generates a one-line summary of the domain WHOIS check
// results for display and notification purposes.
func (m Metadata) OneLineCheckSummary() string {
var summary string
switch {
case m.IsExpired():
summary = fmt.Sprintf(
"%s: %q domain registration EXPIRED %s%s",
m.ServiceState().Label,
m.Name,
FormattedExpiration(m.ExpirationDate),
nagios.CheckOutputEOL,
)
default:
summary = fmt.Sprintf(
"%s: %q domain registration has %s%s",
m.ServiceState().Label,
m.Name,
FormattedExpiration(m.ExpirationDate),
nagios.CheckOutputEOL,
)
}
return summary
}
// Report provides an overview of domain details appropriate for display as
// the LongServiceOutput provided via the web UI or as email or Teams
// notifications.
func (m Metadata) Report() string {
var summary strings.Builder
fmt.Fprintf(
&summary,
"WHOIS metadata for %q domain:%s%s",
m.Name,
nagios.CheckOutputEOL,
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Status: [%s]%s",
strings.Join(m.WhoisInfo.Domain.Status, ", "),
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Creation Date: %v%s",
m.CreatedDate.Format(DomainDateLayout),
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Updated Date: %v%s",
m.UpdatedDate.Format(DomainDateLayout),
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Expiration Date: %v%s",
m.ExpirationDate.Format(DomainDateLayout),
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Registrar Name: %v%s",
m.WhoisInfo.Registrar.Name,
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Registrant Name: %v%s",
m.WhoisInfo.Registrant.Name,
nagios.CheckOutputEOL,
)
fmt.Fprintf(
&summary,
"* Registrant Email: %v%s",
m.WhoisInfo.Registrant.Email,
nagios.CheckOutputEOL,
)
return summary.String()
}
// IsExpired indicates whether the domain expiration date has passem.
func (m Metadata) IsExpired() bool {
return m.ExpirationDate.Before(time.Now())
}
// IsExpiring compares the domain's current expiration date against the
// provided CRITICAL and WARNING thresholds to determine if the domain is
// about to expire.
func (m Metadata) IsExpiring() bool {
switch {
case !m.IsExpired() && m.ExpirationDate.Before(m.AgeCriticalThreshold):
return true
case !m.IsExpired() && m.ExpirationDate.Before(m.AgeWarningThreshold):
return true
}
return false
}
// IsWarningState indicates whether a domain's expiration date has been
// determined to be in a WARNING state. This returns false if the expiration
// date is in an OK or CRITICAL state, true otherwise.
func (m Metadata) IsWarningState() bool {
if !m.IsExpired() &&
m.ExpirationDate.Before(m.AgeWarningThreshold) &&
!m.ExpirationDate.Before(m.AgeCriticalThreshold) {
return true
}
return false
}
// IsCriticalState indicates whether a domain's expiration date has been
// determined to be in a CRITICAL state. This returns false if the ChainStatus
// is in an OK or WARNING state, true otherwise.
func (m Metadata) IsCriticalState() bool {
if m.IsExpired() || m.ExpirationDate.Before(m.AgeCriticalThreshold) {
return true
}
return false
}
// IsOKState indicates whether a domain's expiration date has been determined
// to be in an OK state, without expired or expiring domain registration.
func (m Metadata) IsOKState() bool {
return !m.IsWarningState() && !m.IsCriticalState()
}
// ServiceState returns the appropriate Service Check Status label and exit
// code for the evaluated certificate chain.
func (m Metadata) ServiceState() ServiceState {
var stateLabel string
var stateExitCode int
switch {
case m.IsCriticalState():
stateLabel = nagios.StateCRITICALLabel
stateExitCode = nagios.StateCRITICALExitCode
case m.IsWarningState():
stateLabel = nagios.StateWARNINGLabel
stateExitCode = nagios.StateWARNINGExitCode
case m.IsOKState():
stateLabel = nagios.StateOKLabel
stateExitCode = nagios.StateOKExitCode
default:
stateLabel = nagios.StateUNKNOWNLabel
stateExitCode = nagios.StateUNKNOWNExitCode
}
return ServiceState{
Label: stateLabel,
ExitCode: stateExitCode,
}
}
// FormattedExpiration receives a Time value and converts it to a string
// representing the largest useful whole units of time in days and hours. For
// example, if a domain has 1 year, 2 days and 3 hours remaining until
// expiration, this function will return the string 367d 3h, but if only 3
// hours remain then 3h will be returnem.
func FormattedExpiration(expireTime time.Time) string {
// hoursRemaining := time.Until(certificate.NotAfter)/time.Hour)/24,
timeRemaining := time.Until(expireTime).Hours()
var timeExpired bool
var formattedTimeRemainingStr string
var daysRemainingStr string
var hoursRemainingStr string
// Flip sign back to positive, note that cert is expired for later use
if timeRemaining < 0 {
timeExpired = true
timeRemaining *= -1
}
// Toss remainder so that we only get the whole number of days
daysRemaining := math.Trunc(timeRemaining / 24)
if daysRemaining > 0 {
daysRemainingStr = fmt.Sprintf("%dd", int64(daysRemaining))
}
// Multiply the whole number of days by 24 to get the hours value, then
// subtract from the original number of hours until cert expiration to get
// the number of hours leftover from the days calculation.
hoursRemaining := math.Trunc(timeRemaining - (daysRemaining * 24))
hoursRemainingStr = fmt.Sprintf("%dh", int64(hoursRemaining))
formattedTimeRemainingStr = strings.Join([]string{
daysRemainingStr, hoursRemainingStr}, " ")
switch {
case !timeExpired:
formattedTimeRemainingStr = strings.Join([]string{formattedTimeRemainingStr, "remaining"}, " ")
case timeExpired:
formattedTimeRemainingStr = strings.Join([]string{formattedTimeRemainingStr, "ago"}, " ")
}
return formattedTimeRemainingStr
}