OpenSSL-Dev'ers -
Finding the #ifndef WINDOWS in apps/s_client.c prevented me from
using the otherwise-oh-so-complete feature set of this application
on my OS 'of choice' I decided that, instead of learning to work
around or through the peculiarities and special feeding of WINSOCK,
it would be a far, far more interesting exercise to code up a simple
in-line client that 'knew' HTTP, at least in a rudimentary fashion,
after the model of the also-oh-so-useful MKS Toolkit 'web' command.
Hence the enclosed utility, gratuitously cloned from the s_client.c
and s_socket.c sources with a few comments of my own sprinkled
lightly here and there. See the verbose, self-serving comment after
the OpenSSL Copyright if you're interested in more details. As always,
comments/insights/flaming onions from the dev team and others are
always welcome (well, virtual onions anyway).
TT
/* sweb.c */
/* Written by Tom Titchener ([EMAIL PROTECTED]) for the OpenSSL
* project 1999.
*/
/* ====================================================================
* Copyright (c) 1999 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* ([EMAIL PROTECTED]). This product includes software written by Tim
* Hudson ([EMAIL PROTECTED]).
*
*/
/* This application was created by copying source from the OpenSSL
* files apps/s_client.c and apps/s_socket.c. It implements a simple
* SSL client with many of the same options as the s_client app, with
* the difference that this client sends a single HTTP command, and
* displays the HTTP command results, whereas s_client operates in a
* loop and functions as an SSL pipe, echoing input to the server and
* returning output from the server. The application is modeled after
* the MKS Toolkit "web" command, which performs the same functions
* (and more) over a TCP connection (but not over an SSL connection).
*
* I think of the utility as a hybrid combining features (and source)
* from s_client and command-line (and conception) from the MKS Toolkit
* "web" command. We use it at CertCo to script SSL interactions with
* our web servers. I believe it also could serve as a convenient
* example of an OpenSSL client, demonstrating the basic API and the
* many ways the programmer can customize the SSL connection.
*
* To that end, I've added short in-line comments describing the purpose
* and behavior of the APIs invoked below. Finally, the utility provides
* a client that can be used on Windows, where WinSock peculiarities
* (i.e. (if I understand them properly) the fact that all fds in the
* arguments must be for sockets) don't allow the select() loop design
* of the s_client application. Note that this application has been
* developed, debugged, and tested on Windows/NT only.
*
* The easiest way to build this application is to copy sweb.c to the
* apps directory, create a new line "TEST=sweb.c", regenerate the
* makefiles (for Windows/NT), and then build everything.
*
* Note: the delimiter for specifying multiple headers is the new-line,
* which means you can't use the DOS shell to specify an input argument
* with multiple headers. With a unix-like shell, you can use the
* single-quotes to capture the newlines in the input argument.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#ifdef WIN32
#include <winsock.h>
#endif
#include "bio.h"
#include "asn1.h"
#include "x509.h"
#include "ssl.h"
#include "err.h"
#include "pem.h"
#ifndef BUFSIZ
#define BUFSIZ 1024
#endif
#define HTTP_VERSION "HTTP/1.0"
/* Passphrase for secret key, value from -p command-line argument.
* Needs global scope to be used by pass_cb callback.
*/
static char* pass=NULL;
/* Error code output from verify callback verify_cb. Should be
* checked after SSL_connect() failure. See verify_cb.
*/
static int verify_error=X509_V_OK;
/* Ignore errors verifying certificates down to this deep in the
* chain. For this depth and deeper, set verify_error to "chain
* too long" and return 0 so x509_vfy.c:internal_verify() stops
* processing the chain. Set from value for command-line switch
* -v. See verify_cb.
*/
static int verify_depth=0;
#ifdef WIN32
#define get_last_socket_error() WSAGetLastError()
#else
#define get_last_socket_error() errno
#endif
#ifdef WIN32
static struct WSAData wsa_state;
static void sock_cleanup() {
WSACancelBlockingCall();
WSACleanup();
}
#endif
/* Debug callback tracks SSL connection states and errors.
* Initialized by SSL_CTX_set_info_callback. Invoked when
* -d argument value is >= 3. Copied from apps/s_cb.c.
*/
static void info_cb(SSL *s,int where,int ret) {
char *str="undefined";
int w=where& ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT) str="SSL_connect";
else if (w & SSL_ST_ACCEPT) str="SSL_accept";
if (where & SSL_CB_LOOP)
fprintf(stderr,"%s:%s\n",str,SSL_state_string_long(s));
else if (where & SSL_CB_ALERT) {
str=(where & SSL_CB_READ)?"read":"write";
fprintf(stderr,"SSL3 alert %s:%s:%s\n",
str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
}
else if (where & SSL_CB_EXIT) {
if (ret == 0)
fprintf(stderr,"%s:failed in %s\n",str,SSL_state_string_long(s));
else if (ret < 0)
fprintf(stderr,"%s:error in %s\n",str,SSL_state_string_long(s));
}
}
/* Debug callback tracks raw SSL buffers. Initialized by
* BIO_set_callback(). Invoked when debug level is >= 4.
* Copied from apps/s_cb.c. IMHOP, BIO piggybacking like
* this is one of Eric's coolest SW designs.
*/
static long bio_dump_cb(BIO *bio, int cmd, char *argp,
int argi, long argl, long ret) {
BIO *out;
out=(BIO *)BIO_get_callback_arg(bio);
if (out == NULL) return(ret);
if (cmd == (BIO_CB_READ|BIO_CB_RETURN)) {
BIO_printf(out,"read from %08X [%08lX] (%d bytes => %ld (0x%X))\n",
bio,argp,argi,ret,ret);
BIO_dump(out,argp,(int)ret);
return(ret);
}
else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN)) {
BIO_printf(out,"write to %08X [%08lX] (%d bytes => %ld (0x%X))\n",
bio,argp,argi,ret,ret);
BIO_dump(out,argp,(int)ret);
}
return(ret);
}
/* Verify certificate callback. Gets invoked by crypto/x509/x509_vfy.c:
* internal_verify() after it checks the signature and the time of
* the certificate. Copied from apps/s_cb.c. Configured through
* verify_depth and verify_error, initialized in main(). Doesn't
* really do much other than print verify errors to the screen and
* ignore errors down to verify_depth. Mainly an example of how
* OpenSSL let's your app have a say in certificate validating.
* At this point OpenSSL has checked the signature and dates are
* good, so you don't need to do that. One thing you could do
* here would be to verify the certificate status with an on-line
* service, e.g. via OCSP.
*/
static int verify_cb(int ok, X509_STORE_CTX *ctx) {
char buf[256];
X509 *err_cert;
int err,depth;
BIO* bio_err;
if ((bio_err=BIO_new(BIO_s_file())) == NULL)
return(0);
BIO_set_fp(bio_err,stderr,BIO_NOCLOSE);
err_cert=X509_STORE_CTX_get_current_cert(ctx);
err=X509_STORE_CTX_get_error(ctx);
depth=X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256);
BIO_printf(bio_err,"depth=%d %s\n",depth,buf);
if (!ok) {
BIO_printf(bio_err,"verify error:num=%d:%s\n",err,
X509_verify_cert_error_string(err));
if (depth > verify_depth)
verify_error=X509_V_ERR_CERT_CHAIN_TOO_LONG;
else {
ok=1;
verify_error=X509_V_OK;
}
}
switch (ctx->error) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,256);
BIO_printf(bio_err,"issuer= %s\n",buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
BIO_printf(bio_err,"notBefore=");
ASN1_TIME_print(bio_err,X509_get_notBefore(ctx->current_cert));
BIO_printf(bio_err,"\n");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
BIO_printf(bio_err,"notAfter=");
ASN1_TIME_print(bio_err,X509_get_notAfter(ctx->current_cert));
BIO_printf(bio_err,"\n");
break;
}
BIO_printf(bio_err,"verify return:%d\n",ok);
BIO_free(bio_err);
return(ok);
}
/* Passphrase callback. Initialized by SSL_CTX_set_default_passwd_cb.
* Used by various functions in ssl/ssl_rsa.c. Just copies passphrase
* initialized by main() to return buffer. If you don't specify this
* and the RSA key is encrypted, the PEM libraries prompt for it at the
* terminal.
*/
static int pass_cb(char *buf,int len, int verify) {
int i;
if (pass == NULL) return(0);
i=strlen(pass);
i=(i > len)?len:i;
memcpy(buf,pass,i);
return(i);
}
/* Who am I? What am I doing here? */
static char *usage[]={
"Usage: sweb [-d <1..4>] [-v <0..level>] [-h header] [-c cert] [-k key]\n",
" [-p pass] [-s <1..4>] ...\n",
" ... get url\n",
" ... put url file\n",
" ... post url file\n",
"SSL_CIPHER, SSL_CA_PATH, SSL_CA_FILE environment variables\n",
NULL
};
/* One humongous, in-line straight pass through SSL and (rudimentary)
* HTTP request/response processing. Almost all code copied from
* apps/s_client.c (although the comments are mine:)).
*/
int main(int argc, char **argv) {
FILE* fp;
STACK* sk;
X509_NAME *xn;
SSL* con=NULL;
SSL_CIPHER *sc;
X509 *peer=NULL;
unsigned long a;
SSL_CTX *ctx=NULL;
struct stat stbuf;
struct hostent* he;
unsigned int in[4];
unsigned char ip[4];
SSL_METHOD *meth=NULL;
struct sockaddr_in sa;
BIO* sbio=NULL,* ebio,* dbio;
int s,i,j,l,v=0, d=0, z=3, get=0, put=0, post=0, port=443, badops=0;
char* head=NULL,* cert=NULL,* key=NULL,* file=NULL,* url=NULL,* cmd=NULL,
* ciph=NULL,* targ=NULL,* cafile=NULL,* capath=NULL,* p,* q,* m,** pp,
host[BUFSIZ], msg[BUFSIZ], buf[BUFSIZ];
argc--; argv++;
while (argc >= 1) {
if (strcmp(*argv,"-c") == 0) {
if (--argc < 1) goto bad;
cert=*(++argv);
} else if (strcmp(*argv,"-h") == 0) {
if (--argc < 1) goto bad;
head=*(++argv);
} else if (strcmp(*argv,"-p") == 0) {
if (--argc < 1) goto bad;
pass=*(++argv);
} else if (strcmp(*argv,"-s") == 0) {
if (--argc < 1) goto bad;
z=atoi(*(++argv));
if (z < 1 || z > 4) {
fprintf(stderr,"sweb: -s value %d not 1, 2, 3, or 4\n", z);
badops++;
}
} else if (strcmp(*argv,"-k") == 0) {
if (--argc < 1) goto bad;
key=*(++argv);
} else if (strcmp(*argv,"-v") == 0) {
if (--argc < 1) goto bad;
v=verify_depth=atoi(*(++argv));
} else if (strcmp(*argv,"-d") == 0) {
if (--argc < 1) goto bad;
d=atoi(*(++argv));
} else if (stricmp(*argv,"get") == 0) {
if (--argc < 1) goto bad;
url=p=q=*(++argv);
cmd="GET";
get=1;
} else if (stricmp(*argv,"put") == 0) {
if (--argc < 1) goto bad;
url=*(++argv);
if (--argc < 1) goto bad;
file=*(++argv);
put=1;
cmd="PUT";
} else if (stricmp(*argv,"post") == 0) {
if (--argc < 1) goto bad;
url=*(++argv);
if (--argc < 1) goto bad;
file=*(++argv);
post=1;
cmd="POST";
} else {
bad:
fprintf(stderr,"sweb: Unknown option %s\n",*argv);
badops=1;
break;
}
argc--;
argv++;
}
switch (put + get + post) {
case 0:
fprintf(stderr,"sweb: Missing required argument put, get, or post\n");
badops++;
break;
case 1:
break;
default:
fprintf(stderr,"sweb: Too many operations, choose on of put, get, or post\n");
badops++;
break;
}
if (badops) {
for (pp=usage; (*pp != NULL); pp++)
fprintf(stderr,*pp);
goto err;
}
/* No key file? Try pointing at cert file. With PEM you can simply
* concatenate cert and keys and libraries will find correct header.
*/
if (key == NULL)
key=cert;
/* Connect to server. */
/* Isolate host name and optional port from URL: https://<host[:port]>/foo.
* With no :port specifier, we default to 443.
*/
if (strnicmp(url,"https://",8)) {
fprintf(stderr,"sweb: url \"%s\": not a valid https hostname\n", url);
goto err;
}
p=url+8;
if (!(q=strchr(p,'/'))) {
fprintf(stderr,"sweb: url \"%s\": not a valid https hostname\n", url);
goto err;
}
memcpy(host,p,q-p);
host[q-p]='\0';
targ=q;
if ((q=strchr(host,':'))) {
port=atoi(q+1);
q='\0';
}
#ifdef WIN32
#ifdef SIGINT
signal(SIGINT,(void (*)(int))sock_cleanup);
#endif
memset(&wsa_state,0,sizeof(wsa_state));
if (WSAStartup(0x0101,&wsa_state)!=0) {
i=WSAGetLastError();
fprintf(stderr,"sweb: Unable to start WINSOCK, error code=%d\n", i);
goto err;
}
#endif
/* Get IP address for host from URL. Allow for dot format IP address. */
if (sscanf(host,"%d.%d.%d.%d",&(in[0]),&(in[1]),&(in[2]),&(in[3])) == 4) {
for (i=0; i<4; i++)
if (in[i] > 255) {
fprintf(stderr,"sweb: Invalid IP address %s\n", host);
goto err;
}
ip[0]=in[0];ip[1]=in[1];ip[2]=in[2];ip[3]=in[3];
}
else {
if (!(he=gethostbyname(host))) {
fprintf(stderr,"sweb: gethostbyname failure\n");
goto err;
}
if ((short)he->h_addrtype != AF_INET) {
fprintf(stderr,"sweb: gethostbyname addr is not AF_INET\n");
goto err;
}
ip[0]=he->h_addr_list[0][0];ip[1]=he->h_addr_list[0][1];
ip[2]=he->h_addr_list[0][2];ip[3]=he->h_addr_list[0][3];
}
if (d >= 1)
fprintf(stdout,"sweb: host: %s, ip: %d.%d.%d.%d, port: %d\n",
host, ip[0], ip[1], ip[2], ip[3], port);
/* Get a socket and connect to a server at the host and port number
* from the URL.
*/
memset((char *)&sa,0,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_port=htons((unsigned short)port);
a=(unsigned long)((unsigned long)ip[0]<<24L)|
((unsigned long)ip[1]<<16L)|
((unsigned long)ip[2]<< 8L)|
((unsigned long)ip[3]);
sa.sin_addr.s_addr=htonl(a);
if ((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET) {
perror("socket");
goto err;
}
i=0;
if ((i=setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i))) < 0) {
perror("keepalive");
goto err;
}
if (connect(s,(struct sockaddr *)&sa,sizeof(sa)) == -1) {
perror("connect");
goto err;
}
/* Initialize OpenSSL system. */
/* Initialize cipher and digest subsystems according to OpenSSL
* compile-time definitions (NO_RSA, NO_IDEA, NO_SHA1, NO_DSA, etc.).
* If your application suddenly starts complaining that it doesn't
* recognize OIDs like md5WithRSAEncryption etc., it's generally
* because you forgot to call this function.
*/
SSLeay_add_all_algorithms();
/* Make sure ERR_error_string() in ERR_print_errors() and its
* brethren show you nice shiny text instead of dull old numbers.
*/
SSL_load_error_strings();
/* Create and configure SSL_CTX. */
/* Initialize protocol version, default is ssl3, can choose 23, 2,
* or TLSv1 according to value for -s command-line argument.
* SSL_CTX_new() does not set a default for NULL SSL_METHOD*
* argument. You must supply one of these methods.
*/
switch (z) {
case 1:
meth=SSLv23_client_method();
break;
case 2:
meth=SSLv2_client_method();
break;
case 3:
meth=SSLv3_client_method();
break;
case 4:
meth=TLSv1_client_method();
break;
}
if (!(ctx=SSL_CTX_new(meth))) {
fprintf(stderr, "sweb: Cannot create new SSL context\n");
ERR_print_errors_fp(stderr);
goto err;
}
/* Info callback just prints SSL states and errors. */
if (d >= 3)
SSL_CTX_set_info_callback(ctx,info_cb);
/* Verify callback prints errors, ignores 'em up to verify_depth. */
if (v)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
/* Initialize certificate store. Used by ssl/s[2|3]_[clnt|srvr].c,
* which call ssl/ssl_cert.c:ssl_verify_cert_chain(), which calls
* crypto/x509/x509_vfy.c:X509_verify_cert(), which calls
* crypto/x509/x509_lu.c:X509_STORE_get_by_subject(). On Windows,
* SSL_CA_FILE contains concatenated CA certs. On UNIX, SSL_CA_PATH
* points a a directory with symlinks to the certs where the symlink
* names are hash values from the target certs. If you want to
* see verify_cb() happily telling you about the certs in the chain
* with no errors, point SSL_CA_FILE and SSL_CA_PATH at a file with
* the cert(s) for the CA(s) that signed the server's cert.
*/
cafile = getenv("SSL_CA_FILE");
capath = getenv("SSL_CA_PATH");
if (cafile || capath) {
if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
fprintf(stderr,"sweb: Cannot load verify locations %s and %s\n",
cafile, capath);
ERR_print_errors_fp(stderr);
}
if (d >= 1)
fprintf(stderr,"using ca file %s/%s\n", capath, cafile);
}
SSL_CTX_set_default_verify_paths(ctx);
/* Point the SSL_CTX at a certificate and key file (if provided).
* That way, if the server asks for a certificate, the client will
* have one to send (and a key to generate the certificate verify
* message). Call SSL_CTX_check_private_key to make sure the key
* in the certificate matches they key in the private key file.
*/
if (cert && (SSL_CTX_use_certificate_file(ctx,cert,SSL_FILETYPE_PEM) <= 0)) {
fprintf(stderr,"sweb: Unable to get certificate from '%s'\n",cert);
ERR_print_errors_fp(stderr);
goto err;
}
if (pass)
SSL_CTX_set_default_passwd_cb(ctx,pass_cb);
if (key && (SSL_CTX_use_PrivateKey_file(ctx,key,SSL_FILETYPE_PEM) <= 0)) {
fprintf(stderr,"sweb: Unable to get private key from '%s'\n",key);
ERR_print_errors_fp(stderr);
goto err;
}
if ((cert || key) && !SSL_CTX_check_private_key(ctx)) {
fprintf(stderr,"sweb: Private key does not match certificate public key\n");
goto err;
}
/* Create and configure SSL connection. */
if (!(con=(SSL *)SSL_new(ctx))) {
fprintf(stderr, "sweb: Cannot create new SSL connection\n");
ERR_print_errors_fp(stderr);
goto err;
}
/* Use OpenSSL ciphers -v to see the cipher strings and their SSL
* versions, key exchange, authentication, encryption, and message
* digest algorithms, and key length restrictions. See the OpenSSL
* for the syntax to combine ciphers, e.g. !SSLv2:RC4-MD5:RC4-SHA.
* If you don't specify anything, you get the same as "DEFAULT", which
* means "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP".
*/
if ((ciph=getenv("SSL_CIPHER")))
if (!SSL_CTX_set_cipher_list(ctx,ciph)) {
fprintf(stderr, "sweb: Cannot use cipher list %s\n", ciph);
goto err;
}
/* Ordinarily, just decorate the SSL connection with the socket file
* descriptor. But for super-cool shazaam debugging, build your
* own socket BIO, decorate it with a BIO debugging callback, and
* presto, see a dump of the bytes as they fly. See ssl/ssl_lib.c
* for details.
*/
if (d < 4)
SSL_set_fd(con,s);
else {
if (!(sbio=BIO_new_socket(s,BIO_NOCLOSE))) {
fprintf(stderr, "sweb: Cannot create new socket BIO\n");
ERR_print_errors_fp(stderr);
goto err;
}
SSL_set_bio(con,sbio,sbio);
con->debug=1;
dbio=BIO_new_fp(stdout,BIO_NOCLOSE);
BIO_set_callback(sbio,bio_dump_cb);
BIO_set_callback_arg(sbio,dbio);
}
/* Initialize the state of the connection so the first i/o operation
* knows to do the SSL connect. Strictly speaking, this not necessary,
* as this code goes ahead and calls SSL_connect() anyway (so I can
* put the connect tracing stuff in one handy spot). But look closely
* in ssl/s_client.c for a call to SSL_connect. You won't find one.
*/
SSL_set_connect_state(con);
/* Now that we've finally finished customizing the context and the
* connection, go ahead and see if it works. This function call
* invokes all the SSL connection handshake and key exchange logic,
* (which is why there's so much to report on after it completes).
*/
l = SSL_connect(con);
switch (SSL_get_error(con,l)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_SYSCALL:
if ((l != 0) && (i = get_last_socket_error()))
fprintf(stderr,"sweb: Write errno=%d\n", i);
/* fall through */
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SSL:
default:
ERR_print_errors_fp(stderr);
goto err;
break;
}
/* Report on what happened now that we've successfully connected. */
if (d >= 3) {
if ((ebio=BIO_new(BIO_s_file())) == NULL) {
fprintf(stderr,"sweb: Cannot create new BIO\n");
ERR_print_errors_fp(stderr);
goto err;
}
BIO_set_fp(ebio,stderr,BIO_NOCLOSE);
if ((sk=SSL_get_peer_cert_chain(con)) != NULL) {
BIO_printf(ebio,"---\nCertificate chain\n");
for (i=0; i<sk_num(sk); i++) {
X509_NAME_oneline(X509_get_subject_name((X509*)sk_value(sk,i)),buf,BUFSIZ);
BIO_printf(ebio,"%2d s:%s\n",i,buf);
X509_NAME_oneline(X509_get_issuer_name((X509 *)sk_value(sk,i)),buf,BUFSIZ);
BIO_printf(ebio," i:%s\n",buf);
}
}
BIO_printf(ebio,"---\n");
if ((peer=SSL_get_peer_certificate(con)) != NULL) {
BIO_printf(ebio,"Server certificate\n");
PEM_write_bio_X509(ebio,peer);
X509_NAME_oneline(X509_get_subject_name(peer),buf,BUFSIZ);
BIO_printf(ebio,"subject=%s\n",buf);
X509_NAME_oneline(X509_get_issuer_name(peer),buf,BUFSIZ);
BIO_printf(ebio,"issuer=%s\n",buf);
}
else
BIO_printf(ebio,"no peer certificate available\n");
if (((sk=SSL_get_client_CA_list(con)) != NULL) && (sk_num(sk) > 0)) {
BIO_printf(ebio,"---\nAcceptable client certificate CA names\n");
for (i=0; i<sk_num(sk); i++) {
xn=(X509_NAME *)sk_value(sk,i);
X509_NAME_oneline(xn,buf,sizeof(buf));
BIO_write(ebio,buf,strlen(buf));
BIO_write(ebio,"\n",1);
}
}
else {
BIO_printf(ebio,"---\nNo client certificate CA names sent\n");
}
if ((p=SSL_get_shared_ciphers(con,buf,BUFSIZ)) != NULL) {
BIO_printf(ebio,"---\nCiphers common between both SSL endpoints:\n");
j=i=0;
while (*p) {
if (*p != ':') {
BIO_write(ebio,p,1);j++;
} else {
BIO_write(ebio," ",15-j%25);i++;j=0;
BIO_write(ebio,((i%3)?" ":"\n"),1);
}
p++;
}
BIO_write(ebio,"\n",1);
}
BIO_printf(ebio,
"---\nSSL handshake has read %ld bytes and written %ld bytes\n",
BIO_number_read(SSL_get_rbio(con)),
BIO_number_written(SSL_get_wbio(con)));
BIO_printf(ebio,((con->hit)?"---\nReused, ":"---\nNew, "));
sc=SSL_get_current_cipher(con);
BIO_printf(ebio,"%s, Cipher is %s\n",
SSL_CIPHER_get_version(sc),SSL_CIPHER_get_name(sc));
if (peer != NULL) {
EVP_PKEY *pktmp;
pktmp = X509_get_pubkey(peer);
BIO_printf(ebio,"Server public key is %d bit\n", EVP_PKEY_bits(pktmp));
EVP_PKEY_free(pktmp);
}
SSL_SESSION_print(ebio,SSL_get_session(con));
BIO_printf(ebio,"---\n");
if (peer != NULL)
X509_free(peer);
BIO_free(ebio);
}
/* Prepare HTTP message: request line first. */
sprintf(msg, "%s %s %s\r\n", cmd, targ, HTTP_VERSION);
/* HTTP request headers, new-line delimited. */
m=&msg[strlen(msg)];
if ((p=head)) {
while ((q=strchr(p,'\n'))) {
sprintf(m, "%.*s\r\n", q-p, p);
m=&msg[strlen(msg)];
p=q+1;
}
sprintf(m, "%s\r\n", p);
m=&msg[strlen(msg)];
}
/* Post needs content-length. */
if (post) {
if (stat(file, &stbuf) < 0) {
perror(file);
fprintf(stderr, "sweb: Cannot stat file %s\n", file);
goto err;
}
l = stbuf.st_size;
sprintf(m, "Content-Length: %d\r\n", l);
m=&msg[strlen(msg)];
}
/* Empty line separates request and headers from body. */
sprintf(m, "\r\n");
m=&msg[strlen(msg)];
if (d >= 2)
fprintf(stderr,"write: %s\n", msg);
l=SSL_write(con,msg,strlen(msg));
switch (SSL_get_error(con,l)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_SYSCALL:
if ((l != 0) && (i = get_last_socket_error()))
fprintf(stderr,"sweb: Write errno=%d\n", i);
/* fall through */
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SSL:
default:
ERR_print_errors_fp(stderr);
goto err;
break;
}
/* For post, send body. */
if (post) {
/* On Windows, mode must be "rb", else amount you send
* is less than length returned by stat for Content-Length
* header.
*/
if (!(fp = fopen(file, "rb"))) {
perror(file);
fprintf(stderr, "sweb: Cannot open file %s\n", file);
goto err;
}
while ((l=fread(buf,1,sizeof buf,fp)) > 0) {
if (d >= 2)
fprintf(stderr,"write %d bytes: %.*s\n", l, l, buf);
l=SSL_write(con,buf,l);
switch (SSL_get_error(con,l)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_SYSCALL:
if ((l != 0) && (i = get_last_socket_error()))
fprintf(stderr,"sweb: Write errno=%d\n", i);
/* fall through */
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SSL:
default:
ERR_print_errors_fp(stderr);
goto err;
}
}
fclose(fp);
if (l < 0) {
perror(file);
fprintf(stderr, "sweb: Cannot read from file %s\n", file);
goto err;
}
}
/* Get and display response. */
while ((l = SSL_read(con, buf, sizeof buf)) > 0) {
fwrite(buf, l, 1, stdout);
}
switch (SSL_get_error(con,l)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_SYSCALL:
if ((i = get_last_socket_error()))
fprintf(stderr,"sweb: Read errno=%d\n", i);
/* fall through */
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SSL:
ERR_print_errors_fp(stderr);
goto err;
}
/* Connection cleanup */
SSL_shutdown(con);
SSL_free(con);
/* Context cleanup */
SSL_CTX_free(ctx);
if (sbio)
BIO_free(sbio);
return 1;
err:
return 0;
}
______________________________________________________________________
OpenSSL Project http://www.openssl.org
Development Mailing List [EMAIL PROTECTED]
Automated List Manager [EMAIL PROTECTED]