Commit f9915f512c for openssl.org

commit f9915f512c2fc5ce7c992a336dc5e9ca8e036236
Author: Shreenidhi Shedi <yesshedi@gmail.com>
Date:   Thu Jul 9 16:23:57 2026 +0530

    apps/tsget: replace CURLOPT_UPLOAD with CURLOPT_POST

    Switch from the UPLOAD+CUSTOMREQUEST+read-callback approach to
    POSTFIELDS, eliminating the read_body/write_body callbacks and the
    INFILE/INFILESIZE/FILE options. Rename get_timestamp to send_request
    with a simpler interface that writes directly to the output file via
    CURLOPT_WRITEDATA.

    Assisted-by: Claude:claude-sonnet-4-6
    Signed-off-by: Shreenidhi Shedi <yesshedi@gmail.com>

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    MergeDate: Fri Aug  7 13:29:36 2026
    (Merged from https://github.com/openssl/openssl/pull/31445)

diff --git a/apps/tsget.in b/apps/tsget.in
index 0bd35dbfb4..4fab476e5f 100644
--- a/apps/tsget.in
+++ b/apps/tsget.in
@@ -15,27 +15,6 @@ use Net::Curl::Easy qw(:constants);

 use vars qw(%options);

-# Callback for reading the body.
-sub read_body {
-    my ($easy, $maxlength, $state) = @_;
-    my $return_data = "";
-    my $data_len = length ${$state->{data}};
-    if ($state->{bytes} < $data_len) {
-        $data_len = $data_len - $state->{bytes};
-        $data_len = $maxlength if $data_len > $maxlength;
-        $return_data = substr ${$state->{data}}, $state->{bytes}, $data_len;
-        $state->{bytes} += $data_len;
-    }
-    return $return_data;
-}
-
-# Callback for writing the body into a variable.
-sub write_body {
-    my ($easy, $data, $pointer) = @_;
-    ${$pointer} .= $data;
-    return length($data);
-}
-
 # Initialise a new Curl object.
 sub create_curl {
     my $url = shift;
@@ -49,17 +28,9 @@ sub create_curl {
     $curl->setopt(CURLOPT_USERAGENT,
         "OpenTSA tsget.pl/openssl-{- $config{full_version} -}");

-    # Options for POST method.
-    $curl->setopt(CURLOPT_UPLOAD, 1);
-    $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST");
     $curl->setopt(CURLOPT_HTTPHEADER,
         ["Content-Type: application/timestamp-query",
         "Accept: application/timestamp-reply,application/timestamp-response"]);
-    $curl->setopt(CURLOPT_READFUNCTION, \&read_body);
-    $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[1]); });
-
-    # Options for getting the result.
-    $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body);

     # SSL related options.
     $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM");
@@ -79,36 +50,41 @@ sub create_curl {
     return $curl;
 }

-# Send a request and returns the body back.
-sub get_timestamp {
-    my $curl = shift;
-    my $body = shift;
-    my $ts_body;
-
-    # Options for POST method.
-    $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0});
-    $curl->setopt(CURLOPT_INFILESIZE, length(${$body}));
-
-    # Options for getting the result.
-    $curl->setopt(CURLOPT_FILE, \$ts_body);
-
-    # Send the request...
-    my $error_string;
-    eval { $curl->perform(); };
-    if ($@) {
-        my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE);
-        $error_string = "could not get timestamp";
-        $error_string .= ", http code: $http_code" unless $http_code == 0;
-        $error_string .= ": $@";
-    } else {
-        my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE);
-        if (lc($ct) ne "application/timestamp-reply"
-            && lc($ct) ne "application/timestamp-response") {
-            $error_string = "unexpected content type returned: $ct";
-        }
+# Send a request, writing the response to the given filehandle.
+# Returns an error string on network/protocol failure, undef on success.
+sub send_request {
+    my ($curl, $body, $out_fh) = @_;
+
+    $curl->setopt(CURLOPT_POST, 1);
+    $curl->setopt(CURLOPT_POSTFIELDS, $body);
+    $curl->setopt(CURLOPT_POSTFIELDSIZE, length($body));
+    $curl->setopt(CURLOPT_WRITEDATA, $out_fh);
+
+    my $ok = eval { $curl->perform(); 1; };
+
+    if (!$ok) {
+        my $http_code = eval { $curl->getinfo(CURLINFO_HTTP_CODE) } // 0;
+        my $curl_err  = eval { $curl->error() } // "";
+        my $err = "could not get timestamp";
+        $err .= ", http code: $http_code" if $http_code != 0;
+        $err .= " ($curl_err)" if length($curl_err);
+        return $err;
     }
-    return ($ts_body, $error_string);

+    my $downloaded = $curl->getinfo(CURLINFO_SIZE_DOWNLOAD);
+    if (!defined($downloaded) || $downloaded == 0) {
+        return "empty response received";
+    }
+
+    my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE);
+    if (!defined($ct)
+            || (lc($ct) ne "application/timestamp-reply"
+                && lc($ct) ne "application/timestamp-response")) {
+        return "unexpected content type returned: "
+            . (defined($ct) ? $ct : "(none)");
+    }
+
+    return undef;
 }

 # Print usage information and exists.
@@ -172,23 +148,10 @@ REQUEST: foreach (@ARGV) {
     # Send request.
     STDERR->printflush("sending request") if $options{v};

-    my ($ts_body, $error) = get_timestamp $curl, \$body;
+    my $error = send_request($curl, $body, $output);
     if (defined($error)) {
         die "$input: fatal error: $error\n";
     }
     STDERR->printflush(", reply received") if $options{v};
-
-    # Write response.
-    if ($output eq "-") {
-        # Write to STDOUT.
-        print $ts_body;
-    } else {
-        # Write to file.
-        open OUTPUT, ">", $output
-            or warn("$output: could not open output file: $!\n"), next REQUEST;
-        print OUTPUT $ts_body;
-        close OUTPUT
-            or warn("$output: could not close output file: $!\n"), next REQUEST;
-    }
     STDERR->printflush(", $output written.\n") if $options{v};
 }