Commit f663788138ee017d728988252a0ace41cbdf5813
1 parent
e568ed64
Sanitized HTTP Error codes
Implemented consistent output (normally provide an HTTP response when usual things go wrong) Hid detailed error information in debug info so that end users cannot see this
Showing
1 changed file
with
53 additions
and
43 deletions
dyndns.pl
... | ... | @@ -117,19 +117,37 @@ sub DNS_get($;$) { |
117 | 117 | } |
118 | 118 | } |
119 | 119 | |
120 | +############################## | |
121 | +# Initialize CGI, determine whether debugging is on and declaire fail method | |
122 | +my $cgi = CGI->new; | |
123 | +my $debug = ($AllowDebugKey cmp 'off') && (($AllowDebugKey eq '') || ($AllowDebugKey eq $cgi->param('debug')) ); | |
124 | +my $SE = 'System Error'; | |
125 | +my $CE = 'Configuration Error'; | |
126 | +my $PE = 'Required parameter missing'; | |
127 | +sub fail($$;$) { | |
128 | + my ($errormsg, $debugmsg, $exitcode) = @_; | |
129 | + print $debug . "\n"; | |
130 | + print $cgi->header(-status=>$exitcode || 503, -type=>'text/plain'), | |
131 | + "ERROR - $errormsg" . ($debug ? ": $debugmsg\n" : "\n"); | |
132 | + exit 0; | |
133 | +} | |
134 | + | |
135 | + | |
120 | 136 | # Check whether the hostname provided is actually a CNAME, returns the host |
121 | 137 | # it points to in case of a CNAME or the original hostname if it is not. |
122 | -sub expand_CNAME($;$) { | |
123 | - my ($host, @found) = @_; | |
124 | - if(my $cname=DNS_get($host, 'CNAME')) { | |
138 | +sub expand_CNAME($) { | |
139 | + my ($host) = @_; | |
140 | + my @found; | |
141 | + while(my $cname = DNS_get($host, 'CNAME')) { | |
125 | 142 | push(@found, $host); |
126 | - die $found[0]."has > $ExpandCNAMEs level deep CNAME chain, aborting!\n" | |
143 | + $host = $cname; | |
144 | + fail("DNS CNAME Not supported", "$found[0] points to a CNAME but this is not supported", 400) | |
145 | + unless($ExpandCNAMEs); | |
146 | + fail("DNS CNAME Chain Error", "$found[0] has > $ExpandCNAMEs level deep CNAME chain", 400) | |
127 | 147 | unless($#found < $ExpandCNAMEs); |
128 | - foreach my $r (@found) { | |
129 | - die "found CNAME loop for $host, aborting!\n" if($cname eq $r); | |
148 | + foreach my $r (@found) { | |
149 | + fail("DNS CNAME Loop", "found CNAME loop for $found[0]", 400) if($cname eq $r); | |
130 | 150 | } |
131 | - | |
132 | - return &expand_CNAME($cname, @found); | |
133 | 151 | } |
134 | 152 | return $host; |
135 | 153 | } |
... | ... | @@ -145,11 +163,8 @@ sub get_authinfo($$) { |
145 | 163 | || (($AuthMode eq 'both') ? $StaticSigner : undef)); |
146 | 164 | |
147 | 165 | # Ensure we have a value for signer and key, otherwise abort the processing |
148 | - if($signer eq '' or $key eq '') { | |
149 | - print $cgi->header(-status=>400, -type=>'text/plain'), | |
150 | - "ERROR - No/incomplete authentication information provided\n"; | |
151 | - exit | |
152 | - } | |
166 | + fail($PE, "No/incomplete authentication information provided", 400) | |
167 | + if($signer eq '' or $key eq ''); | |
153 | 168 | |
154 | 169 | # and return the values |
155 | 170 | return($signer, $key); |
... | ... | @@ -195,7 +210,7 @@ sub DNS_Update($$$$$$$) { |
195 | 210 | $response->header->rcode . $debugmessage); |
196 | 211 | } else { |
197 | 212 | # REFUSED, FORMERR |
198 | - return (400, "ERROR - DNS update for $dnshost failed: " . | |
213 | + return (403, "ERROR - DNS update for $dnshost failed: " . | |
199 | 214 | $response->header->rcode . $debugmessage); |
200 | 215 | } |
201 | 216 | } else { |
... | ... | @@ -207,8 +222,8 @@ sub DNS_Update($$$$$$$) { |
207 | 222 | |
208 | 223 | ############################## |
209 | 224 | # Handlers for the different requests |
210 | -sub handle_update($$$$$$) { | |
211 | - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
225 | +sub handle_update($$$$$) { | |
226 | + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
212 | 227 | my ($signer, $key) = get_authinfo($cgi, $host); |
213 | 228 | |
214 | 229 | # perform the action |
... | ... | @@ -236,8 +251,8 @@ sub handle_update($$$$$$) { |
236 | 251 | |
237 | 252 | } |
238 | 253 | |
239 | -sub handle_expire($$$$$$) { | |
240 | - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
254 | +sub handle_expire($$$$$) { | |
255 | + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
241 | 256 | my ($signer, $key) = get_authinfo($cgi, $host); |
242 | 257 | |
243 | 258 | my $debugmsg = ($debug) ? "\n" : ''; |
... | ... | @@ -271,8 +286,8 @@ sub handle_expire($$$$$$) { |
271 | 286 | } |
272 | 287 | } |
273 | 288 | |
274 | -sub handle_view($$$$$$) { | |
275 | - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
289 | +sub handle_view($$$$$) { | |
290 | + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
276 | 291 | my $title = "DynDNS Updater - $host"; |
277 | 292 | print $cgi->header(-status=>200), |
278 | 293 | $cgi->start_html(-title => $title), |
... | ... | @@ -295,8 +310,8 @@ sub handle_view($$$$$$) { |
295 | 310 | print $cgi->end_html(); |
296 | 311 | } |
297 | 312 | |
298 | -sub handle_list($$$$$$) { | |
299 | - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
313 | +sub handle_list($$$$$) { | |
314 | + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_; | |
300 | 315 | my $title = "DynDNS Updater - $dnsdomain"; |
301 | 316 | |
302 | 317 | print $cgi->header(-status=>200), |
... | ... | @@ -322,7 +337,6 @@ sub handle_list($$$$$$) { |
322 | 337 | print $cgi->end_html(); |
323 | 338 | } |
324 | 339 | |
325 | -my $cgi = CGI->new; | |
326 | 340 | |
327 | 341 | ############################## |
328 | 342 | # Load configuration, if desired |
... | ... | @@ -346,36 +360,36 @@ if ($ConfigFile cmp 'ignore') { |
346 | 360 | my $dst = $CONFIG{$key}; # get destination for value |
347 | 361 | if (ref $dst eq 'SCALAR') { $$dst = $value; } # store scalar value |
348 | 362 | elsif (ref $dst eq 'CODE') { &$dst($value); } # call setter function |
349 | - elsif (ref $dst cmp 'ARRAY') { die "Invalid $key in $CFGFile\n"; } | |
350 | - else { @$dst = split(/\s*,\s*/, $value); } # split and store array | |
363 | + elsif (ref $dst cmp 'ARRAY') { fail($SE, "Invalid config: $key"); } | |
364 | + else { @$dst = split(/\s*,\s*/, $value); } # split and store array | |
351 | 365 | } |
352 | 366 | close CONFIG; |
353 | 367 | } elsif ($ConfigFile eq 'required') { |
354 | - die "unable to load configuration from $CFGFile: $!, aborting\n" | |
368 | + fail($SE, "unable to load configuration from $CFGFile: $!"); | |
355 | 369 | } |
356 | 370 | } |
357 | 371 | |
372 | + | |
358 | 373 | ############################## |
359 | 374 | # Validate Configuration |
360 | -my $CE = 'Configuration Error:'; | |
361 | -die "$CE ConfigFile must be optional, required or ignore, not '$ConfigFile'\n" | |
375 | +fail($CE, "ConfigFile must be optional, required or ignore, not '$ConfigFile'") | |
362 | 376 | unless $ConfigFile=~/optional|required|ignore/; |
363 | -die "$CE AuthMode '$AuthMode' is unsupported must be remote, static or both\n" | |
377 | +fail($CE, "AuthMode '$AuthMode' is unsupported must be remote, static or both") | |
364 | 378 | unless $AuthMode=~/remote|static|both/; |
365 | -die "$CE StaticSigner must be set for \$AuthMode '$AuthMode'\n" | |
379 | +fail($CE, "StaticSigner must be set for \$AuthMode '$AuthMode'") | |
366 | 380 | unless ($StaticSigner or $AuthMode eq 'remote'); |
367 | -die "$CE StaticKey must be set for \$AuthMode '$AuthMode'\n" | |
381 | +fail($CE, "StaticKey must be set for \$AuthMode '$AuthMode'") | |
368 | 382 | unless ($StaticKey or $AuthMode eq 'remote'); |
369 | -die "$CE RequireRR is set to unsupported type '$RequireRR'\n" | |
383 | +fail($CE, "RequireRR is set to unsupported type '$RequireRR'") | |
370 | 384 | if ($RequireRR and not $DNS_label{$RequireRR}); |
371 | -die "$CE RecordTTL '$RecordTTL' is not supported\n" | |
385 | +fail($CE, "RecordTTL '$RecordTTL' is not supported") | |
372 | 386 | unless ($RecordTTL=~/^\d+[smhw]?$/); |
373 | -die "$CE ExpireAfter '$ExpireAfter' is not supported\n" | |
387 | +fail($CE, "ExpireAfter '$ExpireAfter' is not supported") | |
374 | 388 | unless ($ExpireAfter=~/^\d+[smhw]?$/); |
375 | -die "$CE UpdateTXT must be set when \$ExpireAfter is set\n" | |
389 | +fail($CE, "UpdateTXT must be set when \$ExpireAfter is set") | |
376 | 390 | if($ExpireAfter and not $UpdateTXT); |
377 | 391 | foreach my $rrtype (@ReplaceRR) { |
378 | - die "$CE ReplaceRR contains unsupported type '$rrtype'\n" | |
392 | + fail($CE, "ReplaceRR contains unsupported type '$rrtype'") | |
379 | 393 | unless exists $DNS_label{$rrtype}; |
380 | 394 | } |
381 | 395 | |
... | ... | @@ -385,7 +399,6 @@ foreach my $rrtype (@ReplaceRR) { |
385 | 399 | my $mode = $cgi->path_info || $cgi->param('mode') || 'view'; |
386 | 400 | $mode=~s/^\/([^\/]+)(\/(.*))?/$1/; |
387 | 401 | my $host = $cgi->param('host') || $3; |
388 | -my $debug = ($AllowDebugKey eq 'off') ? 0 : ($AllowDebugKey eq ($cgi->param('debug') || '')); | |
389 | 402 | |
390 | 403 | |
391 | 404 | ############################## |
... | ... | @@ -398,16 +411,13 @@ my %handlers = ( |
398 | 411 | expire => \&handle_expire, |
399 | 412 | ); |
400 | 413 | if($host eq '' and $mode cmp 'list' and $mode cmp 'expire') { |
401 | - print $cgi->header(-status=>400, -type=>'text/plain'), | |
402 | - "ERROR - No host name to act on specified\n"; | |
414 | + fail($PE, "No host name to act on specified", 400); | |
403 | 415 | } elsif(my $handler = $handlers{$mode}) { |
404 | 416 | # Replace provided host with that of a CNAME it points to and determine domain |
405 | 417 | my $dnshost = ($host) ? expand_CNAME($host) : undef; |
406 | 418 | my $dnsdomain = $cgi->param('domain') || ($dnshost=~/\.(.*)$/)[0]; |
407 | - $handler->($cgi, $mode, $host, $dnshost, $dnsdomain, $debug); | |
419 | + $handler->($mode, $host, $dnshost, $dnsdomain, $debug); | |
408 | 420 | } else { |
409 | - print $cgi->header(-status=>($cgi->path_info) ? 404 : 400, | |
410 | - -type=>'text/plain'), | |
411 | - "ERROR - File Not Found / Invalid Mode '$mode' specified\n"; | |
421 | + fail("File Not Found", "Invalid Mode '$mode' specified", 404); | |
412 | 422 | } |
413 | 423 | |
... | ... |