From e0b30cc6e82113491261c872a371a2fa0b4db3ba Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Thu, 4 Dec 2025 16:52:55 +0100 Subject: append the query string from parsed uri Check the UriUriA object, and if query string exists append it to the new_url. Only appends the query part, fragments are still not appended Function redir parses the new location header value using the uriParseUriA function already, which populates the query field. This field was already being printed, but it was not being appended to the new_url during its construction. Redirection chain of check_curl --onredirect=follow now mimics the chain of check_http --onredirect=follow. Tested on the url: mail.google.com/chat --- plugins/check_curl.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index e3e514ff..ca6357a7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -761,7 +761,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } /* compose new path */ - /* TODO: handle fragments and query part of URL */ + /* TODO: handle fragments of URL */ char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; @@ -772,6 +772,25 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } } + /* missing components have null,null in their UriTextRangeA + * add query parameters if they exist. + */ + if (uri.query.first && uri.query.afterLast){ + // Ensure we have space for '?' + query_str + '\0' ahead of time, instead of calling strncat twice + size_t current_len = strlen(new_url); + size_t remaining_space = DEFAULT_BUFFER_SIZE - current_len - 1; + + const char* query_str = uri_string(uri.query, buf, DEFAULT_BUFFER_SIZE); + size_t query_str_len = strlen(query_str); + + if (remaining_space >= query_str_len + 1) { + strcat(new_url, "?"); + strcat(new_url, query_str); + }else{ + die(STATE_UNKNOWN, _("HTTP UNKNOWN - No space to add query part of size %d to the buffer, buffer has remaining size %d"), query_str_len , current_len ); + } + } + if (working_state.serverPort == new_port && !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && (working_state.host_name && -- cgit v1.2.3-74-g34f1 From f7df5579ab1d029eeaad785a6d016884c8f36c7d Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 9 Dec 2025 00:11:20 +0100 Subject: check_curl add tests for uri field parsing plugins/tests/check_curl.t forks and runs a http(s) server that responds to specific uri endpoints. Added another endpoint under /redirect_with_increment with dynamic redirection points. This endpoint will parse different parts of the uri that come after the path: parameters, query and the fragment. If applicable, seperate elements within each field are parsed into key/value pairs. value is incremented in redirected URI. Tests if check_url redirection logic retains different parts of the url when parsing the uri and building the new redirected URL. Current tests show that it ignores the fragment part. --- plugins/tests/check_curl.t | 194 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index 52c5ad1c..bc0101a2 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -20,6 +20,11 @@ use Test::More; use NPTest; use FindBin qw($Bin); +use URI; +use URI::QueryParam; +use HTTP::Daemon; +use HTTP::Daemon::SSL; + $ENV{'LC_TIME'} = "C"; my $common_tests = 75; @@ -186,6 +191,149 @@ sub run_server { $c->send_response('moved to /redirect2'); } elsif ($r->url->path eq "/redir_timeout") { $c->send_redirect( "/timeout" ); + } elsif ($r->url->path =~ m{^/redirect_with_increment}) { + # ://:@:/;?# + # Find every parameter, query , and fragment keys and increment them + + my $content = ""; + + # Use URI to help with query/fragment; parse path params manually. + my $original_url = $r->url->as_string; + $content .= " original_url: ${original_url}\n"; + my $uri = URI->new($original_url); + $content .= " uri: ${uri}\n"; + + my $path = $uri->path // ''; + my $query = $uri->query // ''; + my $fragment = $uri->fragment // ''; + + $content .= " path: ${path}\n"; + $content .= " query: ${query}\n"; + $content .= " fragment: ${fragment}\n"; + + # Sets and returns the scheme-specific part of the $uri (everything between the scheme and the fragment) as an escaped string. + #my $opaque = $uri->opaque; + #$content .= " opaque: ${opaque}\n"; + + # group 1 is captured: anything that is not '/' : ([^/]*) + # / matches the / directly + # group 2 is captured: anything : (.*) + #my ($before_slash, $after_slash) = $opaque =~ m{^/([^/]*)/(.*)$}; + #$before_slash //= ''; + #$after_slash //= ''; + #$content .= " before_slash: ${before_slash}\n"; + #$content .= " after_slash: ${after_slash}\n"; + + # split the uri part and parameters. uri package cannot do this + # group 1 is captured: anything without a semicolon: ([^;]) + # group 2 is uncaptured: (?:;(.*))? + # (? )? prevents the capture + # in between the ';' matches the first ever semicolon + # group3 is captured: any character stirng : (.*) + my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?}; + $before_params //= ''; + $params //= ''; + $content .= " before_params: ${before_params}\n"; + $content .= " params: ${params}\n"; + my @parameter_pairs; + if (defined $params && length $params) { + for my $p (split /;/, $params) { + my ($key,$value) = split /=/, $p, 2; + $value //= ''; + push @parameter_pairs, [ $key, $value ]; + $content .= " parameter: ${key} -> ${value}\n"; + } + } + + # query parameters are offered directly from the library + my @query_form = $uri->query_form; + my @query_parameter_pairs; + while (@query_form) { + my $key = shift @query_form; + my $value = shift @query_form; + $value //= ''; # there can be valueless keys + push @query_parameter_pairs, [ $key, $value ]; + $content .= " query: ${key} -> ${value}\n"; + } + + # fragment: try to split into key=value pairs on ';' or '&' if present + my @fragment_pairs; + my $fragment_seperator = ''; + if ($fragment ne '') { + $fragment_seperator = ($fragment =~ /&/ ? '&' : ';'); + for my $f (split /[&;]/, $fragment) { + next unless length $f; + my ($key,$value) = split /=/, $f, 2; + $value //= ''; + push @fragment_pairs, [ $key, $value ]; + $content .= " fragment: ${key} -> ${value}\n"; + } + } + + # helper to increment value + my $increment = sub { + my ($v) = @_; + return $v if !defined $v || $v eq ''; + # numeric integer + if ($v =~ /^-?\d+$/) { + return $v + 1; + } + # otherwise -> increment as if its an ascii character + # sed replacement syntax, but the $& holds the matched character + if (length($v)) { + (my $new_v = $v) =~ s/./chr(ord($&) + 1)/ge; + return $new_v; + } + }; + + # increment values in pairs + for my $pair (@parameter_pairs) { + $pair->[1] = $increment->($pair->[1]); + $content .= " parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; + } + for my $pair (@query_parameter_pairs) { + $pair->[1] = $increment->($pair->[1]); + $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; + } + for my $pair (@fragment_pairs) { + $pair->[1] = $increment->($pair->[1]); + $content .= " fragment new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; + } + + # rebuild strings + my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs); + $content .= " new_parameter_str: ${new_parameter_str}\n"; + + # library can rebuild from an array + my @new_query_form; + for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] } + + my $new_fragment_str = ''; + if (@fragment_pairs) { + $new_fragment_str = join($fragment_seperator, map { $_->[0] . '=' . $_->[1] } @fragment_pairs); + } + $content .= " new_fragment_str: ${new_fragment_str}\n"; + + # construct new URI using the library + my $new_uri = URI->new(''); + $new_uri->path( $before_params . ($new_parameter_str ? ';' . $new_parameter_str : '') ); + $new_uri->query_form( \@new_query_form ) if @new_query_form; + $new_uri->fragment( $new_fragment_str ) if $new_fragment_str ne ''; + $content .= " new_uri: ${new_uri}\n"; + + # Redirect until fail_count or redirect_count reaches 3 + if ($new_uri =~ /fail_count=3/){ + $c->send_error(HTTP::Status->RC_FORBIDDEN, "fail count reached 3, url path:" . $r->url->path ); + } elsif ($new_uri =~ /redirect_count=3/){ + $c->send_response(HTTP::Response->new( 200, 'OK', undef , $content )); + } elsif ($new_uri =~ /location_redirect_count=3/){ + $c->send_basic_header(302); + $c->send_header("Location", "$new_uri" ); + $c->send_crlf; + $c->send_response("$content \n moved to $new_uri"); + } else { + $c->send_redirect( $new_uri->as_string, 301, $content ); + } } elsif ($r->url->path eq "/timeout") { # Keep $c from being destroyed, but prevent severe leaks unshift @persist, $c; @@ -215,7 +363,7 @@ sub run_server { return($chunk); })); } else { - $c->send_error(HTTP::Status->RC_FORBIDDEN); + $c->send_error(HTTP::Status->RC_FORBIDDEN, "unknown url path:" . $r->url->path ); } $c->close; } @@ -482,6 +630,50 @@ sub run_common_tests { is( $result->return_code, 0, $cmd); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); + # Redirect with increment tests. These are for checking if the url parameters, query parameters and fragment are parsed. + # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects. + # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them. + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 1, $cmd); + like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3: ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 1, $cmd); + like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*;p1=3;p2=cd\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*\?qp1=12&qp2=mn*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*\?qp0=2&qp1=3&qp2=4&qp3=5&qp4=6&qp5=7*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output ); + + $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $result = NPTest->testCmd( "$cmd" ); + is( $result->return_code, 0, $cmd); + like( $result->output, '/.*#f1=vguv*/', "Output correct, parsed and incremented fragment f1 : ".$result->output ); + # These tests may block # stickyport - on full urlS port is set back to 80 otherwise $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; -- cgit v1.2.3-74-g34f1 From feeade08178ed4874207c90b43e411d986d10cb9 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Wed, 10 Dec 2025 17:09:46 +0100 Subject: fix typos --- plugins/tests/check_curl.t | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'plugins') diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index bc0101a2..af01c97e 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -229,7 +229,7 @@ sub run_server { # group 2 is uncaptured: (?:;(.*))? # (? )? prevents the capture # in between the ';' matches the first ever semicolon - # group3 is captured: any character stirng : (.*) + # group3 is captured: any character string : (.*) my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?}; $before_params //= ''; $params //= ''; @@ -258,9 +258,9 @@ sub run_server { # fragment: try to split into key=value pairs on ';' or '&' if present my @fragment_pairs; - my $fragment_seperator = ''; + my $fragment_separator = ''; if ($fragment ne '') { - $fragment_seperator = ($fragment =~ /&/ ? '&' : ';'); + $fragment_separator = ($fragment =~ /&/ ? '&' : ';'); for my $f (split /[&;]/, $fragment) { next unless length $f; my ($key,$value) = split /=/, $f, 2; @@ -310,7 +310,7 @@ sub run_server { my $new_fragment_str = ''; if (@fragment_pairs) { - $new_fragment_str = join($fragment_seperator, map { $_->[0] . '=' . $_->[1] } @fragment_pairs); + $new_fragment_str = join($fragment_separator, map { $_->[0] . '=' . $_->[1] } @fragment_pairs); } $content .= " new_fragment_str: ${new_fragment_str}\n"; -- cgit v1.2.3-74-g34f1 From 737af667a212f7058143b34770f7f0af70aa8d9b Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Thu, 11 Dec 2025 10:05:25 +0100 Subject: clairfy new check_curl tests use the parameters in the last redirected URI that that server returns HTTP OK to. matches the incrementation count of redirection_count from 0 to 3, as they also get incremented three times alongside it. add comments about what is happening in the test cases, no need to understand the endpoint completely --- plugins/tests/check_curl.t | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'plugins') diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index af01c97e..374c74b3 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -224,12 +224,13 @@ sub run_server { #$content .= " before_slash: ${before_slash}\n"; #$content .= " after_slash: ${after_slash}\n"; - # split the uri part and parameters. uri package cannot do this - # group 1 is captured: anything without a semicolon: ([^;]) - # group 2 is uncaptured: (?:;(.*))? - # (? )? prevents the capture - # in between the ';' matches the first ever semicolon + # split the URI part and parameters. URI package cannot do this + # group 1 is captured: anything without a semicolon: ([^;]*) + # group 2 is uncaptured: (?:;(.*))? + # (?: ... )? prevents capturing the parameter section + # inside group 2, ';' matches the first ever semicolon # group3 is captured: any character string : (.*) + # \? matches an actual ? mark, which starts the query parameters my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?}; $before_params //= ''; $params //= ''; @@ -639,40 +640,53 @@ sub run_common_tests { is( $result->return_code, 1, $cmd); like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output ); + # redirect_count=0 is parsed as a parameter and incremented. When it goes up to 3, the redirection returns HTTP OK $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3, and returned OK: ".$result->output ); + # location_redirect_count=0 goes up to 3, which uses the HTTP 302 style of redirection with 'Location' header $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); + # fail_count parameter may also go up to 3, which returns a HTTP 403 $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 1, $cmd); like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output ); + # redirect_count=0, p1=1 , p2=ab => redirect_count=1, p1=2 , p2=bc => redirect_count=2, p1=3 , p2=cd => redirect_count=3 , p1=4 , p2=de + # Last visited URI returns HTTP OK instead of redirect, and the one before that contains the new_uri in its content $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*;p1=3;p2=cd\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output ); + like( $result->output, '/.*redirect_count=3;p1=4;p2=de\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); + # Same incrementation as before, uses the query parameters that come after the first '?' : qp1 and qp2 $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*\?qp1=12&qp2=mn*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output ); + like( $result->output, '/.*\?qp1=13&qp2=no*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); + # Check if the query parameter order is kept intact $cmd = "$command -p $port_http -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*\?qp0=2&qp1=3&qp2=4&qp3=5&qp4=6&qp5=7*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output ); + like( $result->output, '/.*\?qp0=3&qp1=4&qp2=5&qp3=6&qp4=7&qp5=8*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); + # The fragment is a single item, and it should be kept during redirections as well. + # Increase the chars in strings. 'test' => 'uftu' => 'vguv' => 'whvw' $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*#f1=vguv*/', "Output correct, parsed and incremented fragment f1 : ".$result->output ); + like( $result->output, '/.*#f1=whvw*/', "Output correct, parsed and incremented fragment f1 : ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); # These tests may block # stickyport - on full urlS port is set back to 80 otherwise -- cgit v1.2.3-74-g34f1 From f8db90c206e777a21ff69a301406a11ca627034c Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Fri, 12 Dec 2025 11:16:09 +0100 Subject: check_curl redirection test improvements previously, the fragment was sent in the request from client, and the server would parse and increment its value. the incremented value would be set in the redirected URI. this does not work as fragments are meaningless to servers and clients like check_curl strip them in their GET request. rewrite the fragment handling . if client sends a URI parameter with 'fragment' as its key, the server will set its value for its redirected URI. it will come up both as a parameter and the fragment at the end. use this new logic to rewrite the fragment redirection test. remove -p $http_port argument on tests for this endpoint, which was making https tests fail. correct the common test count from 75 to 95, as there are 20 total test assertions in the 8 times it uses the new endpoint. remove unused code on that endpoint as well --- plugins/tests/check_curl.t | 66 ++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 46 deletions(-) (limited to 'plugins') diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index 374c74b3..248eb4c5 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -27,7 +27,7 @@ use HTTP::Daemon::SSL; $ENV{'LC_TIME'} = "C"; -my $common_tests = 75; +my $common_tests = 95; my $ssl_only_tests = 8; # Check that all dependent modules are available eval "use HTTP::Daemon 6.01;"; @@ -211,19 +211,6 @@ sub run_server { $content .= " query: ${query}\n"; $content .= " fragment: ${fragment}\n"; - # Sets and returns the scheme-specific part of the $uri (everything between the scheme and the fragment) as an escaped string. - #my $opaque = $uri->opaque; - #$content .= " opaque: ${opaque}\n"; - - # group 1 is captured: anything that is not '/' : ([^/]*) - # / matches the / directly - # group 2 is captured: anything : (.*) - #my ($before_slash, $after_slash) = $opaque =~ m{^/([^/]*)/(.*)$}; - #$before_slash //= ''; - #$after_slash //= ''; - #$content .= " before_slash: ${before_slash}\n"; - #$content .= " after_slash: ${after_slash}\n"; - # split the URI part and parameters. URI package cannot do this # group 1 is captured: anything without a semicolon: ([^;]*) # group 2 is uncaptured: (?:;(.*))? @@ -257,20 +244,6 @@ sub run_server { $content .= " query: ${key} -> ${value}\n"; } - # fragment: try to split into key=value pairs on ';' or '&' if present - my @fragment_pairs; - my $fragment_separator = ''; - if ($fragment ne '') { - $fragment_separator = ($fragment =~ /&/ ? '&' : ';'); - for my $f (split /[&;]/, $fragment) { - next unless length $f; - my ($key,$value) = split /=/, $f, 2; - $value //= ''; - push @fragment_pairs, [ $key, $value ]; - $content .= " fragment: ${key} -> ${value}\n"; - } - } - # helper to increment value my $increment = sub { my ($v) = @_; @@ -296,10 +269,6 @@ sub run_server { $pair->[1] = $increment->($pair->[1]); $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; } - for my $pair (@fragment_pairs) { - $pair->[1] = $increment->($pair->[1]); - $content .= " fragment new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; - } # rebuild strings my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs); @@ -309,9 +278,13 @@ sub run_server { my @new_query_form; for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] } - my $new_fragment_str = ''; - if (@fragment_pairs) { - $new_fragment_str = join($fragment_separator, map { $_->[0] . '=' . $_->[1] } @fragment_pairs); + my $new_fragment_str = ''; + for my $pair (@parameter_pairs) { + my $key = $pair->[0]; + my $value = $pair->[1]; + if ($key eq "fragment") { + $new_fragment_str = $value + } } $content .= " new_fragment_str: ${new_fragment_str}\n"; @@ -635,57 +608,58 @@ sub run_common_tests { # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects. # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them. - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 1, $cmd); like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output ); # redirect_count=0 is parsed as a parameter and incremented. When it goes up to 3, the redirection returns HTTP OK - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3, and returned OK: ".$result->output ); # location_redirect_count=0 goes up to 3, which uses the HTTP 302 style of redirection with 'Location' header - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); # fail_count parameter may also go up to 3, which returns a HTTP 403 - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 1, $cmd); like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output ); # redirect_count=0, p1=1 , p2=ab => redirect_count=1, p1=2 , p2=bc => redirect_count=2, p1=3 , p2=cd => redirect_count=3 , p1=4 , p2=de # Last visited URI returns HTTP OK instead of redirect, and the one before that contains the new_uri in its content - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*redirect_count=3;p1=4;p2=de\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output ); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); # Same incrementation as before, uses the query parameters that come after the first '?' : qp1 and qp2 - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*\?qp1=13&qp2=no*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output ); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); # Check if the query parameter order is kept intact - $cmd = "$command -p $port_http -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv"; + $cmd = "$command -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); like( $result->output, '/.*\?qp0=3&qp1=4&qp2=5&qp3=6&qp4=7&qp5=8*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output ); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); - # The fragment is a single item, and it should be kept during redirections as well. - # Increase the chars in strings. 'test' => 'uftu' => 'vguv' => 'whvw' - $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; + # The fragment is passed as another parameter. + # During the server redirects the fragment will be set to its value, if such a key is present. + # 'ebiil' => 'fcjjm' => 'gdkkn' => 'hello' + $cmd = "$command -u '/redirect_with_increment/path1/path2;redirect_count=0;fragment=ebiil?qp1=0' --onredirect=follow -vvv"; $result = NPTest->testCmd( "$cmd" ); is( $result->return_code, 0, $cmd); - like( $result->output, '/.*#f1=whvw*/', "Output correct, parsed and incremented fragment f1 : ".$result->output ); + like( $result->output, '/.*redirect_count=3;fragment=hello\?qp1=3#hello*/', "Output correct, fragments are specified by server and followed by check_curl: ".$result->output ); like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); # These tests may block -- cgit v1.2.3-74-g34f1 From d36bf51baf2338cc7f49601f679548960a8299c0 Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Tue, 23 Dec 2025 15:54:27 +0000 Subject: fix types for most curl_easy_setopt parameters according to https://curl.se/libcurl/c/curl_easy_setopt.html, parameters are either a long, a function pointer, an object pointer or a curl_off_t, depending on what the option expects; curl 8.16 checks and warns about these. --- plugins/check_curl.c | 20 ++++++++++---------- plugins/check_curl.d/check_curl_helpers.c | 30 ++++++++++++++++-------------- plugins/check_curl.d/check_curl_helpers.h | 2 +- plugins/check_curl.d/config.h | 6 +++--- 4 files changed, 30 insertions(+), 28 deletions(-) (limited to 'plugins') diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 0aff8b40..a1fefa3a 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -92,16 +92,16 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth); + long redir_depth); typedef struct { - int redir_depth; + long redir_depth; check_curl_working_state working_state; int error_code; check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state); + long redir_depth, check_curl_working_state working_state); static void print_help(void); void print_usage(void); @@ -198,7 +198,7 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { #endif /* HAVE_SSL */ mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth) { + long redir_depth) { // ======================= // Initialisation for curl @@ -441,19 +441,19 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { - printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + printf(_("* curl LIBINFO_REDIRECT_COUNT is %ld\n"), redir_depth); } mp_subcheck sc_redir_depth = mp_subcheck_init(); if (redir_depth > config.max_depth) { xasprintf(&sc_redir_depth.output, - "maximum redirection depth %d exceeded in libcurl", + "maximum redirection depth %ld exceeded in libcurl", config.max_depth); sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); return sc_result; } - xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", + xasprintf(&sc_redir_depth.output, "redirection depth %ld (of a maximum %ld)", redir_depth, config.max_depth); mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); @@ -653,7 +653,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state) { + long redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -678,7 +678,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %ld exceeded - %s\n"), config.max_depth, location); } @@ -1400,7 +1400,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); + printf(_("* Set SSL/TLS version to %ld\n"), result.config.curl_config.ssl_version); } if (!specify_port) { result.config.initial_config.serverPort = HTTPS_PORT; diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index e4e7bef6..7be781fc 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -19,7 +19,7 @@ bool add_sslctx_verify_fun = false; check_curl_configure_curl_wrapper check_curl_configure_curl(const check_curl_static_curl_config config, check_curl_working_state working_state, bool check_cert, - bool on_redirect_dependent, int follow_method, int max_depth) { + bool on_redirect_dependent, int follow_method, long max_depth) { check_curl_configure_curl_wrapper result = { .errorcode = OK, .curl_state = @@ -57,7 +57,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, result.curl_state.curl_easy_initialized = true; if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L), "CURLOPT_VERBOSE"); } @@ -214,10 +214,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, if (working_state.http_method) { if (!strcmp(working_state.http_method, "POST")) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1L), "CURLOPT_POST"); } else if (!strcmp(working_state.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1L), "CURLOPT_UPLOAD"); } else { handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_CUSTOMREQUEST, @@ -300,10 +300,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1L), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2L), "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks @@ -311,10 +311,10 @@ check_curl_configure_curl(const check_curl_static_curl_config config, * to be less tolerant about ssl verfications */ handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0L), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0L), "CURLOPT_SSL_VERIFYHOST"); } @@ -438,7 +438,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, if (on_redirect_dependent) { if (follow_method == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1L), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! @@ -474,7 +474,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, } /* no-body */ if (working_state.no_body) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1L), "CURLOPT_NOBODY"); } @@ -796,15 +796,17 @@ mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const ((float)last_modified) / (60 * 60 * 24)); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { - xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"), + (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60, + (int)last_modified % 60); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } } else { // TODO is this the OK case? time_t last_modified = (srv_data - doc_data); - xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + xasprintf(&sc_document_dates.output, _("Last modified %lld:%02d:%02d ago"), + (long long)last_modified / (60 * 60), (int)(last_modified / 60) % 60, + (int)last_modified % 60); sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); } } diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index e7b80f7e..e77b763b 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -80,7 +80,7 @@ check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_cu check_curl_working_state working_state, bool check_cert, bool on_redirect_dependent, - int follow_method, int max_depth); + int follow_method, long max_depth); void handle_curl_option_return_code(CURLcode res, const char *option); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index f51b2ee9..61067d46 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -57,10 +57,10 @@ typedef struct { bool haproxy_protocol; long socket_timeout; sa_family_t sin_family; - int curl_http_version; + long curl_http_version; char **http_opt_headers; size_t http_opt_headers_count; - int ssl_version; + long ssl_version; char *client_cert; char *client_privkey; char *ca_cert; @@ -76,7 +76,7 @@ typedef struct { check_curl_working_state initial_config; check_curl_static_curl_config curl_config; - int max_depth; + long max_depth; int followmethod; int followsticky; -- cgit v1.2.3-74-g34f1 From ae6fcfde85d04a2333f69a134ac7f3e2f51e1fe6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Fri, 2 Jan 2026 13:42:00 +0100 Subject: Add lib math to check_snmp dependencies --- plugins/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d098fa8a..fa159f4a 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -164,7 +164,7 @@ check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c check_snmp_LDADD = $(BASEOBJS) -check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` +check_snmp_LDFLAGS = $(AM_LDFLAGS) -lm `net-snmp-config --libs` check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` check_smtp_LDADD = $(SSLOBJS) check_ssh_LDADD = $(NETLIBS) -- cgit v1.2.3-74-g34f1