1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 10# 11# This software is licensed as described in the file COPYING, which 12# you should have received as part of this distribution. The terms 13# are also available at https://curl.se/docs/copyright.html. 14# 15# You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# copies of the Software, and permit persons to whom the Software is 17# furnished to do so, under the terms of the COPYING file. 18# 19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# KIND, either express or implied. 21# 22# SPDX-License-Identifier: curl 23# 24#*************************************************************************** 25 26# This is the HTTPS, FTPS, POP3S, IMAPS, SMTPS, server used for curl test 27# harness. Actually just a layer that runs stunnel properly using the 28# non-secure test harness servers. 29 30BEGIN { 31 push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'}); 32 push(@INC, "."); 33} 34 35use strict; 36use warnings; 37use Cwd; 38use Cwd 'abs_path'; 39 40use serverhelp qw( 41 server_pidfilename 42 server_logfilename 43 ); 44 45use pathhelp; 46 47my $stunnel = "stunnel"; 48 49my $verbose=0; # set to 1 for debugging 50 51my $accept_port = 8991; # just our default, weird enough 52my $target_port = 8999; # default test http-server port 53 54my $stuncert; 55 56my $ver_major; 57my $ver_minor; 58my $fips_support; 59my $stunnel_version; 60my $tstunnel_windows; 61my $socketopt; 62my $cmd; 63 64my $pidfile; # stunnel pid file 65my $logfile; # stunnel log file 66my $loglevel = 5; # stunnel log level 67my $ipvnum = 4; # default IP version of stunneled server 68my $idnum = 1; # default stunneled server instance number 69my $proto = 'https'; # default secure server protocol 70my $conffile; # stunnel configuration file 71my $capath; # certificate chain PEM folder 72my $certfile; # certificate chain PEM file 73 74#*************************************************************************** 75# stunnel requires full path specification for several files. 76# 77my $path = getcwd(); 78my $srcdir = $path; 79my $logdir = $path .'/log'; 80 81#*************************************************************************** 82# Signal handler to remove our stunnel 4.00 and newer configuration file. 83# 84sub exit_signal_handler { 85 my $signame = shift; 86 local $!; # preserve errno 87 local $?; # preserve exit status 88 unlink($conffile) if($conffile && (-f $conffile)); 89 exit; 90} 91 92#*************************************************************************** 93# Process command line options 94# 95while(@ARGV) { 96 if($ARGV[0] eq '--verbose') { 97 $verbose = 1; 98 } 99 elsif($ARGV[0] eq '--proto') { 100 if($ARGV[1]) { 101 $proto = $ARGV[1]; 102 shift @ARGV; 103 } 104 } 105 elsif($ARGV[0] eq '--accept') { 106 if($ARGV[1]) { 107 if($ARGV[1] =~ /^(\d+)$/) { 108 $accept_port = $1; 109 shift @ARGV; 110 } 111 } 112 } 113 elsif($ARGV[0] eq '--connect') { 114 if($ARGV[1]) { 115 if($ARGV[1] =~ /^(\d+)$/) { 116 $target_port = $1; 117 shift @ARGV; 118 } 119 } 120 } 121 elsif($ARGV[0] eq '--stunnel') { 122 if($ARGV[1]) { 123 if($ARGV[1] =~ /^([\w\/]+)$/) { 124 $stunnel = $ARGV[1]; 125 } 126 else { 127 $stunnel = "\"". $ARGV[1] ."\""; 128 } 129 shift @ARGV; 130 } 131 } 132 elsif($ARGV[0] eq '--srcdir') { 133 if($ARGV[1]) { 134 $srcdir = $ARGV[1]; 135 shift @ARGV; 136 } 137 } 138 elsif($ARGV[0] eq '--certfile') { 139 if($ARGV[1]) { 140 $stuncert = $ARGV[1]; 141 shift @ARGV; 142 } 143 } 144 elsif($ARGV[0] eq '--id') { 145 if($ARGV[1]) { 146 if($ARGV[1] =~ /^(\d+)$/) { 147 $idnum = $1 if($1 > 0); 148 shift @ARGV; 149 } 150 } 151 } 152 elsif($ARGV[0] eq '--ipv4') { 153 $ipvnum = 4; 154 } 155 elsif($ARGV[0] eq '--ipv6') { 156 $ipvnum = 6; 157 } 158 elsif($ARGV[0] eq '--pidfile') { 159 if($ARGV[1]) { 160 $pidfile = "$path/". $ARGV[1]; 161 shift @ARGV; 162 } 163 } 164 elsif($ARGV[0] eq '--logfile') { 165 if($ARGV[1]) { 166 $logfile = "$path/". $ARGV[1]; 167 shift @ARGV; 168 } 169 } 170 else { 171 print STDERR "\nWarning: secureserver.pl unknown parameter: $ARGV[0]\n"; 172 } 173 shift @ARGV; 174} 175 176#*************************************************************************** 177# Initialize command line option dependent variables 178# 179if(!$pidfile) { 180 $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum); 181} 182if(!$logfile) { 183 $logfile = server_logfilename($logdir, $proto, $ipvnum, $idnum); 184} 185 186$conffile = "$path/${proto}_stunnel.conf"; 187 188$capath = abs_path($path); 189$certfile = "$srcdir/". ($stuncert?"certs/$stuncert":"stunnel.pem"); 190$certfile = abs_path($certfile); 191 192my $ssltext = uc($proto) ." SSL/TLS:"; 193 194#*************************************************************************** 195# Find out version info for the given stunnel binary 196# 197foreach my $veropt (('-version', '-V')) { 198 foreach my $verstr (qx($stunnel $veropt 2>&1)) { 199 if($verstr =~ /^stunnel (\d+)\.(\d+) on /) { 200 $ver_major = $1; 201 $ver_minor = $2; 202 } 203 elsif($verstr =~ /^sslVersion.*fips *= *yes/) { 204 # the fips option causes an error if stunnel doesn't support it 205 $fips_support = 1; 206 last 207 } 208 } 209 last if($ver_major); 210} 211if((!$ver_major) || (!$ver_minor)) { 212 if(-x "$stunnel" && ! -d "$stunnel") { 213 print "$ssltext Unknown stunnel version\n"; 214 } 215 else { 216 print "$ssltext No stunnel\n"; 217 } 218 exit 1; 219} 220$stunnel_version = (100*$ver_major) + $ver_minor; 221 222#*************************************************************************** 223# Verify minimum stunnel required version 224# 225if($stunnel_version < 310) { 226 print "$ssltext Unsupported stunnel version $ver_major.$ver_minor\n"; 227 exit 1; 228} 229 230#*************************************************************************** 231# Find out if we are running on Windows using the tstunnel binary 232# 233if($stunnel =~ /tstunnel(\.exe)?"?$/) { 234 $tstunnel_windows = 1; 235 236 # convert Cygwin/MinGW paths to Win32 format 237 $capath = pathhelp::sys_native_abs_path($capath); 238 $certfile = pathhelp::sys_native_abs_path($certfile); 239} 240 241#*************************************************************************** 242# Build command to execute for stunnel 3.X versions 243# 244if($stunnel_version < 400) { 245 if($stunnel_version >= 319) { 246 $socketopt = "-O a:SO_REUSEADDR=1"; 247 } 248 $cmd = "$stunnel -p $certfile -P $pidfile "; 249 $cmd .= "-d $accept_port -r $target_port -f -D $loglevel "; 250 $cmd .= ($socketopt) ? "$socketopt " : ""; 251 $cmd .= ">$logfile 2>&1"; 252 if($verbose) { 253 print uc($proto) ." server (stunnel $ver_major.$ver_minor)\n"; 254 print "cmd: $cmd\n"; 255 print "pem cert file: $certfile\n"; 256 print "pid file: $pidfile\n"; 257 print "log file: $logfile\n"; 258 print "log level: $loglevel\n"; 259 print "listen on port: $accept_port\n"; 260 print "connect to port: $target_port\n"; 261 } 262} 263 264#*************************************************************************** 265# Build command to execute for stunnel 4.00 and newer 266# 267if($stunnel_version >= 400) { 268 $socketopt = "a:SO_REUSEADDR=1"; 269 if(($stunnel_version >= 534) && $tstunnel_windows) { 270 # SO_EXCLUSIVEADDRUSE is on by default on Vista or newer, 271 # but does not work together with SO_REUSEADDR being on. 272 $socketopt .= "\nsocket = a:SO_EXCLUSIVEADDRUSE=0"; 273 } 274 $cmd = "$stunnel $conffile "; 275 $cmd .= ">$logfile 2>&1"; 276 # setup signal handler 277 $SIG{INT} = \&exit_signal_handler; 278 $SIG{TERM} = \&exit_signal_handler; 279 # stunnel configuration file 280 if(open(STUNCONF, ">$conffile")) { 281 print STUNCONF "CApath = $capath\n"; 282 print STUNCONF "cert = $certfile\n"; 283 print STUNCONF "debug = $loglevel\n"; 284 print STUNCONF "socket = $socketopt\n"; 285 if($fips_support) { 286 # disable fips in case OpenSSL doesn't support it 287 print STUNCONF "fips = no\n"; 288 } 289 if(!$tstunnel_windows) { 290 # do not use Linux-specific options on Windows 291 print STUNCONF "output = $logfile\n"; 292 print STUNCONF "pid = $pidfile\n"; 293 print STUNCONF "foreground = yes\n"; 294 } 295 print STUNCONF "\n"; 296 print STUNCONF "[curltest]\n"; 297 print STUNCONF "accept = $accept_port\n"; 298 print STUNCONF "connect = $target_port\n"; 299 if(!close(STUNCONF)) { 300 print "$ssltext Error closing file $conffile\n"; 301 exit 1; 302 } 303 } 304 else { 305 print "$ssltext Error writing file $conffile\n"; 306 exit 1; 307 } 308 if($verbose) { 309 print uc($proto) ." server (stunnel $ver_major.$ver_minor)\n"; 310 print "cmd: $cmd\n"; 311 print "CApath = $capath\n"; 312 print "cert = $certfile\n"; 313 print "debug = $loglevel\n"; 314 print "socket = $socketopt\n"; 315 if($fips_support) { 316 print "fips = no\n"; 317 } 318 if(!$tstunnel_windows) { 319 print "pid = $pidfile\n"; 320 print "output = $logfile\n"; 321 print "foreground = yes\n"; 322 } 323 print "\n"; 324 print "[curltest]\n"; 325 print "accept = $accept_port\n"; 326 print "connect = $target_port\n"; 327 } 328} 329 330#*************************************************************************** 331# Set file permissions on certificate pem file. 332# 333chmod(0600, $certfile) if(-f $certfile); 334print STDERR "RUN: $cmd\n" if($verbose); 335 336#*************************************************************************** 337# Run tstunnel on Windows. 338# 339if($tstunnel_windows) { 340 # Fake pidfile for tstunnel on Windows. 341 if(open(OUT, ">$pidfile")) { 342 print OUT $$ . "\n"; 343 close(OUT); 344 } 345 346 # Flush output. 347 $| = 1; 348 349 # Put an "exec" in front of the command so that the child process 350 # keeps this child's process ID by being tied to the spawned shell. 351 exec("exec $cmd") || die "Can't exec() $cmd: $!"; 352 # exec() will create a new process, but ties the existence of the 353 # new process to the parent waiting perl.exe and sh.exe processes. 354 355 # exec() should never return back here to this process. We protect 356 # ourselves by calling die() just in case something goes really bad. 357 die "error: exec() has returned"; 358} 359 360#*************************************************************************** 361# Run stunnel. 362# 363my $rc = system($cmd); 364 365$rc >>= 8; 366 367unlink($conffile) if($conffile && -f $conffile); 368 369exit $rc; 370