1e71b7053SJung-uk Kim#!{- $config{HASHBANGPERL} -} 2*b077aed3SPierre Pronchery# Copyright 2002-2021 The OpenSSL Project Authors. All Rights Reserved. 3e71b7053SJung-uk Kim# Copyright (c) 2002 The OpenTSA Project. All rights reserved. 4e71b7053SJung-uk Kim# 5*b077aed3SPierre Pronchery# Licensed under the Apache License 2.0 (the "License"). You may not use 6e71b7053SJung-uk Kim# this file except in compliance with the License. You can obtain a copy 7e71b7053SJung-uk Kim# in the file LICENSE in the source distribution or at 8e71b7053SJung-uk Kim# https://www.openssl.org/source/license.html 9e71b7053SJung-uk Kim 10e71b7053SJung-uk Kimuse strict; 11e71b7053SJung-uk Kimuse IO::Handle; 12e71b7053SJung-uk Kimuse Getopt::Std; 13e71b7053SJung-uk Kimuse File::Basename; 14e71b7053SJung-uk Kimuse WWW::Curl::Easy; 15e71b7053SJung-uk Kim 16e71b7053SJung-uk Kimuse vars qw(%options); 17e71b7053SJung-uk Kim 18e71b7053SJung-uk Kim# Callback for reading the body. 19e71b7053SJung-uk Kimsub read_body { 20e71b7053SJung-uk Kim my ($maxlength, $state) = @_; 21e71b7053SJung-uk Kim my $return_data = ""; 22e71b7053SJung-uk Kim my $data_len = length ${$state->{data}}; 23e71b7053SJung-uk Kim if ($state->{bytes} < $data_len) { 24e71b7053SJung-uk Kim $data_len = $data_len - $state->{bytes}; 25e71b7053SJung-uk Kim $data_len = $maxlength if $data_len > $maxlength; 26e71b7053SJung-uk Kim $return_data = substr ${$state->{data}}, $state->{bytes}, $data_len; 27e71b7053SJung-uk Kim $state->{bytes} += $data_len; 28e71b7053SJung-uk Kim } 29e71b7053SJung-uk Kim return $return_data; 30e71b7053SJung-uk Kim} 31e71b7053SJung-uk Kim 32e71b7053SJung-uk Kim# Callback for writing the body into a variable. 33e71b7053SJung-uk Kimsub write_body { 34e71b7053SJung-uk Kim my ($data, $pointer) = @_; 35e71b7053SJung-uk Kim ${$pointer} .= $data; 36e71b7053SJung-uk Kim return length($data); 37e71b7053SJung-uk Kim} 38e71b7053SJung-uk Kim 39e71b7053SJung-uk Kim# Initialise a new Curl object. 40e71b7053SJung-uk Kimsub create_curl { 41e71b7053SJung-uk Kim my $url = shift; 42e71b7053SJung-uk Kim 43e71b7053SJung-uk Kim # Create Curl object. 44e71b7053SJung-uk Kim my $curl = WWW::Curl::Easy::new(); 45e71b7053SJung-uk Kim 46e71b7053SJung-uk Kim # Error-handling related options. 47e71b7053SJung-uk Kim $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d}; 48e71b7053SJung-uk Kim $curl->setopt(CURLOPT_FAILONERROR, 1); 49e71b7053SJung-uk Kim $curl->setopt(CURLOPT_USERAGENT, 50*b077aed3SPierre Pronchery "OpenTSA tsget.pl/openssl-{- $config{full_version} -}"); 51e71b7053SJung-uk Kim 52e71b7053SJung-uk Kim # Options for POST method. 53e71b7053SJung-uk Kim $curl->setopt(CURLOPT_UPLOAD, 1); 54e71b7053SJung-uk Kim $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST"); 55e71b7053SJung-uk Kim $curl->setopt(CURLOPT_HTTPHEADER, 56e71b7053SJung-uk Kim ["Content-Type: application/timestamp-query", 57e71b7053SJung-uk Kim "Accept: application/timestamp-reply,application/timestamp-response"]); 58e71b7053SJung-uk Kim $curl->setopt(CURLOPT_READFUNCTION, \&read_body); 59e71b7053SJung-uk Kim $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[0]); }); 60e71b7053SJung-uk Kim 61e71b7053SJung-uk Kim # Options for getting the result. 62e71b7053SJung-uk Kim $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body); 63e71b7053SJung-uk Kim 64e71b7053SJung-uk Kim # SSL related options. 65e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM"); 66e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSL_VERIFYPEER, 1); # Verify server's certificate. 67e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSL_VERIFYHOST, 2); # Check server's CN. 68e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSLKEY, $options{k}) if defined($options{k}); 69e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSLKEYPASSWD, $options{p}) if defined($options{p}); 70e71b7053SJung-uk Kim $curl->setopt(CURLOPT_SSLCERT, $options{c}) if defined($options{c}); 71e71b7053SJung-uk Kim $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C}); 72e71b7053SJung-uk Kim $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P}); 73e71b7053SJung-uk Kim $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r}); 74e71b7053SJung-uk Kim $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g}); 75e71b7053SJung-uk Kim 76e71b7053SJung-uk Kim # Setting destination. 77e71b7053SJung-uk Kim $curl->setopt(CURLOPT_URL, $url); 78e71b7053SJung-uk Kim 79e71b7053SJung-uk Kim return $curl; 80e71b7053SJung-uk Kim} 81e71b7053SJung-uk Kim 82e71b7053SJung-uk Kim# Send a request and returns the body back. 83e71b7053SJung-uk Kimsub get_timestamp { 84e71b7053SJung-uk Kim my $curl = shift; 85e71b7053SJung-uk Kim my $body = shift; 86e71b7053SJung-uk Kim my $ts_body; 87e71b7053SJung-uk Kim local $::error_buf; 88e71b7053SJung-uk Kim 89e71b7053SJung-uk Kim # Error-handling related options. 90e71b7053SJung-uk Kim $curl->setopt(CURLOPT_ERRORBUFFER, "::error_buf"); 91e71b7053SJung-uk Kim 92e71b7053SJung-uk Kim # Options for POST method. 93e71b7053SJung-uk Kim $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0}); 94e71b7053SJung-uk Kim $curl->setopt(CURLOPT_INFILESIZE, length(${$body})); 95e71b7053SJung-uk Kim 96e71b7053SJung-uk Kim # Options for getting the result. 97e71b7053SJung-uk Kim $curl->setopt(CURLOPT_FILE, \$ts_body); 98e71b7053SJung-uk Kim 99e71b7053SJung-uk Kim # Send the request... 100e71b7053SJung-uk Kim my $error_code = $curl->perform(); 101e71b7053SJung-uk Kim my $error_string; 102e71b7053SJung-uk Kim if ($error_code != 0) { 103e71b7053SJung-uk Kim my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE); 104e71b7053SJung-uk Kim $error_string = "could not get timestamp"; 105e71b7053SJung-uk Kim $error_string .= ", http code: $http_code" unless $http_code == 0; 106e71b7053SJung-uk Kim $error_string .= ", curl code: $error_code"; 107e71b7053SJung-uk Kim $error_string .= " ($::error_buf)" if defined($::error_buf); 108e71b7053SJung-uk Kim } else { 109e71b7053SJung-uk Kim my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE); 110e71b7053SJung-uk Kim if (lc($ct) ne "application/timestamp-reply" 111e71b7053SJung-uk Kim && lc($ct) ne "application/timestamp-response") { 112e71b7053SJung-uk Kim $error_string = "unexpected content type returned: $ct"; 113e71b7053SJung-uk Kim } 114e71b7053SJung-uk Kim } 115e71b7053SJung-uk Kim return ($ts_body, $error_string); 116e71b7053SJung-uk Kim 117e71b7053SJung-uk Kim} 118e71b7053SJung-uk Kim 119e71b7053SJung-uk Kim# Print usage information and exists. 120e71b7053SJung-uk Kimsub usage { 121e71b7053SJung-uk Kim 122e71b7053SJung-uk Kim print STDERR "usage: $0 -h <server_url> [-e <extension>] [-o <output>] "; 123e71b7053SJung-uk Kim print STDERR "[-v] [-d] [-k <private_key.pem>] [-p <key_password>] "; 124e71b7053SJung-uk Kim print STDERR "[-c <client_cert.pem>] [-C <CA_certs.pem>] [-P <CA_path>] "; 125e71b7053SJung-uk Kim print STDERR "[-r <file:file...>] [-g <EGD_socket>] [<request>]...\n"; 126e71b7053SJung-uk Kim exit 1; 127e71b7053SJung-uk Kim} 128e71b7053SJung-uk Kim 129e71b7053SJung-uk Kim# ---------------------------------------------------------------------- 130e71b7053SJung-uk Kim# Main program 131e71b7053SJung-uk Kim# ---------------------------------------------------------------------- 132e71b7053SJung-uk Kim 133e71b7053SJung-uk Kim# Getting command-line options (default comes from TSGET environment variable). 134e71b7053SJung-uk Kimmy $getopt_arg = "h:e:o:vdk:p:c:C:P:r:g:"; 135e71b7053SJung-uk Kimif (exists $ENV{TSGET}) { 136e71b7053SJung-uk Kim my @old_argv = @ARGV; 137e71b7053SJung-uk Kim @ARGV = split /\s+/, $ENV{TSGET}; 138e71b7053SJung-uk Kim getopts($getopt_arg, \%options) or usage; 139e71b7053SJung-uk Kim @ARGV = @old_argv; 140e71b7053SJung-uk Kim} 141e71b7053SJung-uk Kimgetopts($getopt_arg, \%options) or usage; 142e71b7053SJung-uk Kim 143e71b7053SJung-uk Kim# Checking argument consistency. 144e71b7053SJung-uk Kimif (!exists($options{h}) || (@ARGV == 0 && !exists($options{o})) 145e71b7053SJung-uk Kim || (@ARGV > 1 && exists($options{o}))) { 146e71b7053SJung-uk Kim print STDERR "Inconsistent command line options.\n"; 147e71b7053SJung-uk Kim usage; 148e71b7053SJung-uk Kim} 149e71b7053SJung-uk Kim# Setting defaults. 150e71b7053SJung-uk Kim@ARGV = ("-") unless @ARGV != 0; 151e71b7053SJung-uk Kim$options{e} = ".tsr" unless defined($options{e}); 152e71b7053SJung-uk Kim 153e71b7053SJung-uk Kim# Processing requests. 154e71b7053SJung-uk Kimmy $curl = create_curl $options{h}; 155e71b7053SJung-uk Kimundef $/; # For reading whole files. 156e71b7053SJung-uk KimREQUEST: foreach (@ARGV) { 157e71b7053SJung-uk Kim my $input = $_; 158e71b7053SJung-uk Kim my ($base, $path) = fileparse($input, '\.[^.]*'); 159e71b7053SJung-uk Kim my $output_base = $base . $options{e}; 160e71b7053SJung-uk Kim my $output = defined($options{o}) ? $options{o} : $path . $output_base; 161e71b7053SJung-uk Kim 162e71b7053SJung-uk Kim STDERR->printflush("$input: ") if $options{v}; 163e71b7053SJung-uk Kim # Read request. 164e71b7053SJung-uk Kim my $body; 165e71b7053SJung-uk Kim if ($input eq "-") { 166e71b7053SJung-uk Kim # Read the request from STDIN; 167e71b7053SJung-uk Kim $body = <STDIN>; 168e71b7053SJung-uk Kim } else { 169e71b7053SJung-uk Kim # Read the request from file. 170e71b7053SJung-uk Kim open INPUT, "<" . $input 171e71b7053SJung-uk Kim or warn("$input: could not open input file: $!\n"), next REQUEST; 172e71b7053SJung-uk Kim $body = <INPUT>; 173e71b7053SJung-uk Kim close INPUT 174e71b7053SJung-uk Kim or warn("$input: could not close input file: $!\n"), next REQUEST; 175e71b7053SJung-uk Kim } 176e71b7053SJung-uk Kim 177e71b7053SJung-uk Kim # Send request. 178e71b7053SJung-uk Kim STDERR->printflush("sending request") if $options{v}; 179e71b7053SJung-uk Kim 180e71b7053SJung-uk Kim my ($ts_body, $error) = get_timestamp $curl, \$body; 181e71b7053SJung-uk Kim if (defined($error)) { 182e71b7053SJung-uk Kim die "$input: fatal error: $error\n"; 183e71b7053SJung-uk Kim } 184e71b7053SJung-uk Kim STDERR->printflush(", reply received") if $options{v}; 185e71b7053SJung-uk Kim 186e71b7053SJung-uk Kim # Write response. 187e71b7053SJung-uk Kim if ($output eq "-") { 188e71b7053SJung-uk Kim # Write to STDOUT. 189e71b7053SJung-uk Kim print $ts_body; 190e71b7053SJung-uk Kim } else { 191e71b7053SJung-uk Kim # Write to file. 192e71b7053SJung-uk Kim open OUTPUT, ">", $output 193e71b7053SJung-uk Kim or warn("$output: could not open output file: $!\n"), next REQUEST; 194e71b7053SJung-uk Kim print OUTPUT $ts_body; 195e71b7053SJung-uk Kim close OUTPUT 196e71b7053SJung-uk Kim or warn("$output: could not close output file: $!\n"), next REQUEST; 197e71b7053SJung-uk Kim } 198e71b7053SJung-uk Kim STDERR->printflush(", $output written.\n") if $options{v}; 199e71b7053SJung-uk Kim} 200e71b7053SJung-uk Kim$curl->cleanup(); 201