/* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* ssltap . c
*
* Version 1 . 0 : Frederick Roeber : 11 June 1997
* Version 2 . 0 : Steve Parkinson : 13 November 1997
* Version 3 . 0 : Nelson Bolyard : 22 July 1998
* Version 3 . 1 : Nelson Bolyard : 24 May 1999
*
* changes in version 2 . 0 :
* Uses NSPR20
* Shows structure of SSL negotiation , if enabled .
*
* This " proxies " a socket connection ( like a socks tunnel ) , but displays the
* data is it flies by .
*
* In the code , the ' client ' socket is the one on the client side of the
* proxy , and the server socket is on the server side .
*
*/
#include "nspr.h"
#include "plstr.h"
#include "secutil.h"
#include <memory.h>
/* for memcpy, etc. */
#include <string.h>
#include <time.h>
#include "plgetopt.h"
#include "nss.h"
#include "cert.h"
#include "sslproto.h"
#include "ocsp.h"
#include "ocspti.h" /* internals for pretty-printing routines *only* */
struct _DataBufferList;
struct _DataBuffer;
typedef struct _DataBufferList {
struct _DataBuffer *first, *last;
unsigned int size;
int isEncrypted;
unsigned char *msgBuf;
unsigned int msgBufOffset;
unsigned int msgBufSize;
unsigned int hMACsize;
} DataBufferList;
typedef struct _DataBuffer {
unsigned char *buffer;
int length;
int offset;
/* offset of first good byte */
struct _DataBuffer *next;
} DataBuffer;
struct sslhandshake {
PRUint8 type;
PRUint32 length;
};
typedef struct _SSLRecord {
PRUint8 type;
PRUint8 ver_maj, ver_min;
PRUint8 length[
2 ];
} SSLRecord;
typedef struct _ClientHelloV2 {
PRUint8 length[
2 ];
PRUint8 type;
PRUint8 version[
2 ];
PRUint8 cslength[
2 ];
PRUint8 sidlength[
2 ];
PRUint8 rndlength[
2 ];
PRUint8 csuites[
1 ];
} ClientHelloV2;
typedef struct _ServerHelloV2 {
PRUint8 length[
2 ];
PRUint8 type;
PRUint8 sidhit;
PRUint8 certtype;
PRUint8 version[
2 ];
PRUint8 certlength[
2 ];
PRUint8 cslength[
2 ];
PRUint8 cidlength[
2 ];
} ServerHelloV2;
typedef struct _ClientMasterKeyV2 {
PRUint8 length[
2 ];
PRUint8 type;
PRUint8 cipherkind[
3 ];
PRUint8 clearkey[
2 ];
PRUint8 secretkey[
2 ];
} ClientMasterKeyV2;
/* forward declaration */
void showErr(
const char *msg);
#define TAPBUFSIZ
16384
#define DEFPORT
1924
#include <ctype.h>
const char *progName;
int hexparse =
0 ;
int sslparse =
0 ;
int sslhexparse =
0 ;
int looparound =
0 ;
int fancy =
0 ;
int isV2Session =
0 ;
int currentcipher =
0 ;
DataBufferList clientstream, serverstream;
#define PR_FPUTS(x) PR_fprintf(PR_STDOUT, x)
#define GET_SHORT(x) ((PRUint16)(((PRUint16)((PRUint8 *)x)[
0 ]) <<
8 ) + ((PRUint16)((PRUin
t8 *)x)[1 ]))
#define GET_24(x) ((PRUint32)((((PRUint32)((PRUint8 *)x)[0 ]) << 16 ) + \
(((PRUint32)((PRUint8 *)x)[1 ]) << 8 ) + \
(((PRUint32)((PRUint8 *)x)[2 ]) << 0 )))
#define GET_32(x) ((PRUint32)((((PRUint32)((PRUint8 *)x)[0 ]) << 24 ) + \
(((PRUint32)((PRUint8 *)x)[1 ]) << 16 ) + \
(((PRUint32)((PRUint8 *)x)[2 ]) << 8 ) + \
(((PRUint32)((PRUint8 *)x)[3 ]) << 0 )))
void print_hex(int amt, unsigned char *buf);
void read_stream_bytes(unsigned char *d, DataBufferList *db, int length);
void
myhalt(int dblsize, int collectedsize)
{
PR_fprintf(PR_STDERR, "HALTED\n" );
PR_ASSERT(dblsize == collectedsize);
exit (13 );
}
const char *
get_error_text(int error)
{
switch (error) {
case PR_IO_TIMEOUT_ERROR:
return "Timeout" ;
break ;
case PR_CONNECT_REFUSED_ERROR:
return "Connection refused" ;
break ;
case PR_NETWORK_UNREACHABLE_ERROR:
return "Network unreachable" ;
break ;
case PR_BAD_ADDRESS_ERROR:
return "Bad address" ;
break ;
case PR_CONNECT_RESET_ERROR:
return "Connection reset" ;
break ;
case PR_PIPE_ERROR:
return "Pipe error" ;
break ;
}
return "" ;
}
void
check_integrity(DataBufferList *dbl)
{
DataBuffer *db;
int i;
db = dbl->first;
i = 0 ;
while (db) {
i += db->length - db->offset;
db = db->next;
}
if (i != dbl->size) {
myhalt(dbl->size, i);
}
}
/* Free's the DataBuffer at the head of the list and returns the pointer
* to the new head of the list .
*/
DataBuffer *
free_head(DataBufferList *dbl)
{
DataBuffer *db = dbl->first;
PR_ASSERT(db->offset >= db->length);
if (db->offset >= db->length) {
dbl->first = db->next;
if (dbl->first == NULL) {
dbl->last = NULL;
}
PORT_Free(db->buffer);
PORT_Free(db);
db = dbl->first;
}
return db;
}
void
read_stream_bytes(unsigned char *d, DataBufferList *dbl, int length)
{
int copied = 0 ;
DataBuffer *db = dbl->first;
if (!db) {
PR_fprintf(PR_STDERR, "assert failed - dbl->first is null\n" );
exit (8 );
}
while (length) {
int toCopy;
/* find the number of bytes to copy from the head buffer */
/* if there's too many in this buffer, then only copy 'length' */
toCopy = PR_MIN(db->length - db->offset, length);
memcpy(d + copied, db->buffer + db->offset, toCopy);
copied += toCopy;
db->offset += toCopy;
length -= toCopy;
dbl->size -= toCopy;
/* if we emptied the head buffer */
if (db->offset >= db->length) {
db = free_head(dbl);
}
}
check_integrity(dbl);
}
void
flush_stream(DataBufferList *dbl)
{
DataBuffer *db = dbl->first;
check_integrity(dbl);
while (db) {
db->offset = db->length;
db = free_head(dbl);
}
dbl->size = 0 ;
check_integrity(dbl);
if (dbl->msgBuf) {
PORT_Free(dbl->msgBuf);
dbl->msgBuf = NULL;
}
dbl->msgBufOffset = 0 ;
dbl->msgBufSize = 0 ;
dbl->hMACsize = 0 ;
}
const char *
V2CipherString(int cs_int)
{
char *cs_str;
cs_str = NULL;
switch (cs_int) {
case 0 x010080:
cs_str = "SSL2/RSA/RC4-128/MD5" ;
break ;
case 0 x020080:
cs_str = "SSL2/RSA/RC4-40/MD5" ;
break ;
case 0 x030080:
cs_str = "SSL2/RSA/RC2CBC128/MD5" ;
break ;
case 0 x040080:
cs_str = "SSL2/RSA/RC2CBC40/MD5" ;
break ;
case 0 x050080:
cs_str = "SSL2/RSA/IDEA128CBC/MD5" ;
break ;
case 0 x060040:
cs_str = "SSL2/RSA/DES56-CBC/MD5" ;
break ;
case 0 x0700C0:
cs_str = "SSL2/RSA/3DES192EDE-CBC/MD5" ;
break ;
case 0 x000001:
cs_str = "SSL3/RSA/NULL/MD5" ;
break ;
case 0 x000002:
cs_str = "SSL3/RSA/NULL/SHA" ;
break ;
case 0 x000003:
cs_str = "SSL3/RSA/RC4-40/MD5" ;
break ;
case 0 x000004:
cs_str = "SSL3/RSA/RC4-128/MD5" ;
break ;
case 0 x000005:
cs_str = "SSL3/RSA/RC4-128/SHA" ;
break ;
case 0 x000006:
cs_str = "SSL3/RSA/RC2CBC40/MD5" ;
break ;
case 0 x000007:
cs_str = "SSL3/RSA/IDEA128CBC/SHA" ;
break ;
case 0 x000008:
cs_str = "SSL3/RSA/DES40-CBC/SHA" ;
break ;
case 0 x000009:
cs_str = "SSL3/RSA/DES56-CBC/SHA" ;
break ;
case 0 x00000A:
cs_str = "SSL3/RSA/3DES192EDE-CBC/SHA" ;
break ;
case 0 x00000B:
cs_str = "SSL3/DH-DSS/DES40-CBC/SHA" ;
break ;
case 0 x00000C:
cs_str = "SSL3/DH-DSS/DES56-CBC/SHA" ;
break ;
case 0 x00000D:
cs_str = "SSL3/DH-DSS/DES192EDE3CBC/SHA" ;
break ;
case 0 x00000E:
cs_str = "SSL3/DH-RSA/DES40-CBC/SHA" ;
break ;
case 0 x00000F:
cs_str = "SSL3/DH-RSA/DES56-CBC/SHA" ;
break ;
case 0 x000010:
cs_str = "SSL3/DH-RSA/3DES192EDE-CBC/SHA" ;
break ;
case 0 x000011:
cs_str = "SSL3/DHE-DSS/DES40-CBC/SHA" ;
break ;
case 0 x000012:
cs_str = "SSL3/DHE-DSS/DES56-CBC/SHA" ;
break ;
case 0 x000013:
cs_str = "SSL3/DHE-DSS/DES192EDE3CBC/SHA" ;
break ;
case 0 x000014:
cs_str = "SSL3/DHE-RSA/DES40-CBC/SHA" ;
break ;
case 0 x000015:
cs_str = "SSL3/DHE-RSA/DES56-CBC/SHA" ;
break ;
case 0 x000016:
cs_str = "SSL3/DHE-RSA/3DES192EDE-CBC/SHA" ;
break ;
case 0 x000017:
cs_str = "SSL3/DH-anon/RC4-40/MD5" ;
break ;
case 0 x000018:
cs_str = "SSL3/DH-anon/RC4-128/MD5" ;
break ;
case 0 x000019:
cs_str = "SSL3/DH-anon/DES40-CBC/SHA" ;
break ;
case 0 x00001A:
cs_str = "SSL3/DH-anon/DES56-CBC/SHA" ;
break ;
case 0 x00001B:
cs_str = "SSL3/DH-anon/3DES192EDE-CBC/SHA" ;
break ;
case 0 x00001C:
cs_str = "SSL3/FORTEZZA-DMS/NULL/SHA" ;
break ;
case 0 x00001D:
cs_str = "SSL3/FORTEZZA-DMS/FORTEZZA-CBC/SHA" ;
break ;
case 0 x00001E:
cs_str = "SSL3/FORTEZZA-DMS/RC4-128/SHA" ;
break ;
case 0 x00002F:
cs_str = "TLS/RSA/AES128-CBC/SHA" ;
break ;
case 0 x000030:
cs_str = "TLS/DH-DSS/AES128-CBC/SHA" ;
break ;
case 0 x000031:
cs_str = "TLS/DH-RSA/AES128-CBC/SHA" ;
break ;
case 0 x000032:
cs_str = "TLS/DHE-DSS/AES128-CBC/SHA" ;
break ;
case 0 x000033:
cs_str = "TLS/DHE-RSA/AES128-CBC/SHA" ;
break ;
case 0 x000034:
cs_str = "TLS/DH-ANON/AES128-CBC/SHA" ;
break ;
case 0 x000035:
cs_str = "TLS/RSA/AES256-CBC/SHA" ;
break ;
case 0 x000036:
cs_str = "TLS/DH-DSS/AES256-CBC/SHA" ;
break ;
case 0 x000037:
cs_str = "TLS/DH-RSA/AES256-CBC/SHA" ;
break ;
case 0 x000038:
cs_str = "TLS/DHE-DSS/AES256-CBC/SHA" ;
break ;
case 0 x000039:
cs_str = "TLS/DHE-RSA/AES256-CBC/SHA" ;
break ;
case 0 x00003A:
cs_str = "TLS/DH-ANON/AES256-CBC/SHA" ;
break ;
case 0 x00003B:
cs_str = "TLS/RSA/NULL/SHA256" ;
break ;
case 0 x00003C:
cs_str = "TLS/RSA/AES128-CBC/SHA256" ;
break ;
case 0 x00003D:
cs_str = "TLS/RSA/AES256-CBC/SHA256" ;
break ;
case 0 x00003E:
cs_str = "TLS/DH-DSS/AES128-CBC/SHA256" ;
break ;
case 0 x00003F:
cs_str = "TLS/DH-RSA/AES128-CBC/SHA256" ;
break ;
case 0 x000040:
cs_str = "TLS/DHE-DSS/AES128-CBC/SHA256" ;
break ;
case 0 x000041:
cs_str = "TLS/RSA/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000042:
cs_str = "TLS/DH-DSS/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000043:
cs_str = "TLS/DH-RSA/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000044:
cs_str = "TLS/DHE-DSS/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000045:
cs_str = "TLS/DHE-RSA/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000046:
cs_str = "TLS/DH-ANON/CAMELLIA128-CBC/SHA" ;
break ;
case 0 x000060:
cs_str = "TLS/RSA-EXPORT1024/RC4-56/MD5" ;
break ;
case 0 x000061:
cs_str = "TLS/RSA-EXPORT1024/RC2CBC56/MD5" ;
break ;
case 0 x000062:
cs_str = "TLS/RSA-EXPORT1024/DES56-CBC/SHA" ;
break ;
case 0 x000064:
cs_str = "TLS/RSA-EXPORT1024/RC4-56/SHA" ;
break ;
case 0 x000063:
cs_str = "TLS/DHE-DSS_EXPORT1024/DES56-CBC/SHA" ;
break ;
case 0 x000065:
cs_str = "TLS/DHE-DSS_EXPORT1024/RC4-56/SHA" ;
break ;
case 0 x000066:
cs_str = "TLS/DHE-DSS/RC4-128/SHA" ;
break ;
case 0 x000067:
cs_str = "TLS/DHE-RSA/AES128-CBC/SHA256" ;
break ;
case 0 x000068:
cs_str = "TLS/DH-DSS/AES256-CBC/SHA256" ;
break ;
case 0 x000069:
cs_str = "TLS/DH-RSA/AES256-CBC/SHA256" ;
break ;
case 0 x00006A:
cs_str = "TLS/DHE-DSS/AES256-CBC/SHA256" ;
break ;
case 0 x00006B:
cs_str = "TLS/DHE-RSA/AES256-CBC/SHA256" ;
break ;
case 0 x000072:
cs_str = "TLS/DHE-DSS/3DESEDE-CBC/RMD160" ;
break ;
case 0 x000073:
cs_str = "TLS/DHE-DSS/AES128-CBC/RMD160" ;
break ;
case 0 x000074:
cs_str = "TLS/DHE-DSS/AES256-CBC/RMD160" ;
break ;
case 0 x000079:
cs_str = "TLS/DHE-RSA/AES256-CBC/RMD160" ;
break ;
case 0 x00007C:
cs_str = "TLS/RSA/3DESEDE-CBC/RMD160" ;
break ;
case 0 x00007D:
cs_str = "TLS/RSA/AES128-CBC/RMD160" ;
break ;
case 0 x00007E:
cs_str = "TLS/RSA/AES256-CBC/RMD160" ;
break ;
case 0 x000080:
cs_str = "TLS/GOST341094/GOST28147-OFB/GOST28147" ;
break ;
case 0 x000081:
cs_str = "TLS/GOST34102001/GOST28147-OFB/GOST28147" ;
break ;
case 0 x000082:
cs_str = "TLS/GOST341094/NULL/GOSTR3411" ;
break ;
case 0 x000083:
cs_str = "TLS/GOST34102001/NULL/GOSTR3411" ;
break ;
case 0 x000084:
cs_str = "TLS/RSA/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x000085:
cs_str = "TLS/DH-DSS/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x000086:
cs_str = "TLS/DH-RSA/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x000087:
cs_str = "TLS/DHE-DSS/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x000088:
cs_str = "TLS/DHE-RSA/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x000089:
cs_str = "TLS/DH-ANON/CAMELLIA256-CBC/SHA" ;
break ;
case 0 x00008A:
cs_str = "TLS/PSK/RC4-128/SHA" ;
break ;
case 0 x00008B:
cs_str = "TLS/PSK/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00008C:
cs_str = "TLS/PSK/AES128-CBC/SHA" ;
break ;
case 0 x00008D:
cs_str = "TLS/PSK/AES256-CBC/SHA" ;
break ;
case 0 x00008E:
cs_str = "TLS/DHE-PSK/RC4-128/SHA" ;
break ;
case 0 x00008F:
cs_str = "TLS/DHE-PSK/3DES-EDE-CBC/SHA" ;
break ;
case 0 x000090:
cs_str = "TLS/DHE-PSK/AES128-CBC/SHA" ;
break ;
case 0 x000091:
cs_str = "TLS/DHE-PSK/AES256-CBC/SHA" ;
break ;
case 0 x000092:
cs_str = "TLS/RSA-PSK/RC4-128/SHA" ;
break ;
case 0 x000093:
cs_str = "TLS/RSA-PSK/3DES-EDE-CBC/SHA" ;
break ;
case 0 x000094:
cs_str = "TLS/RSA-PSK/AES128-CBC/SHA" ;
break ;
case 0 x000095:
cs_str = "TLS/RSA-PSK/AES256-CBC/SHA" ;
break ;
case 0 x000096:
cs_str = "TLS/RSA/SEED-CBC/SHA" ;
break ;
case 0 x000097:
cs_str = "TLS/DH-DSS/SEED-CBC/SHA" ;
break ;
case 0 x000098:
cs_str = "TLS/DH-RSA/SEED-CBC/SHA" ;
break ;
case 0 x000099:
cs_str = "TLS/DHE-DSS/SEED-CBC/SHA" ;
break ;
case 0 x00009A:
cs_str = "TLS/DHE-RSA/SEED-CBC/SHA" ;
break ;
case 0 x00009B:
cs_str = "TLS/DH-ANON/SEED-CBC/SHA" ;
break ;
case 0 x00009C:
cs_str = "TLS/RSA/AES128-GCM/SHA256" ;
break ;
case 0 x00009E:
cs_str = "TLS/DHE-RSA/AES128-GCM/SHA256" ;
break ;
case 0 x0000FF:
cs_str = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" ;
break ;
case 0 x005600:
cs_str = "TLS_FALLBACK_SCSV" ;
break ;
case 0 x00C001:
cs_str = "TLS/ECDH-ECDSA/NULL/SHA" ;
break ;
case 0 x00C002:
cs_str = "TLS/ECDH-ECDSA/RC4-128/SHA" ;
break ;
case 0 x00C003:
cs_str = "TLS/ECDH-ECDSA/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00C004:
cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA" ;
break ;
case 0 x00C005:
cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA" ;
break ;
case 0 x00C006:
cs_str = "TLS/ECDHE-ECDSA/NULL/SHA" ;
break ;
case 0 x00C007:
cs_str = "TLS/ECDHE-ECDSA/RC4-128/SHA" ;
break ;
case 0 x00C008:
cs_str = "TLS/ECDHE-ECDSA/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00C009:
cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA" ;
break ;
case 0 x00C00A:
cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA" ;
break ;
case 0 x00C00B:
cs_str = "TLS/ECDH-RSA/NULL/SHA" ;
break ;
case 0 x00C00C:
cs_str = "TLS/ECDH-RSA/RC4-128/SHA" ;
break ;
case 0 x00C00D:
cs_str = "TLS/ECDH-RSA/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00C00E:
cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA" ;
break ;
case 0 x00C00F:
cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA" ;
break ;
case 0 x00C010:
cs_str = "TLS/ECDHE-RSA/NULL/SHA" ;
break ;
case 0 x00C011:
cs_str = "TLS/ECDHE-RSA/RC4-128/SHA" ;
break ;
case 0 x00C012:
cs_str = "TLS/ECDHE-RSA/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00C013:
cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA" ;
break ;
case 0 x00C014:
cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA" ;
break ;
case 0 x00C015:
cs_str = "TLS/ECDH-anon/NULL/SHA" ;
break ;
case 0 x00C016:
cs_str = "TLS/ECDH-anon/RC4-128/SHA" ;
break ;
case 0 x00C017:
cs_str = "TLS/ECDH-anon/3DES-EDE-CBC/SHA" ;
break ;
case 0 x00C018:
cs_str = "TLS/ECDH-anon/AES128-CBC/SHA" ;
break ;
case 0 x00C019:
cs_str = "TLS/ECDH-anon/AES256-CBC/SHA" ;
break ;
case 0 x00C023:
cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA256" ;
break ;
case 0 x00C024:
cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA384" ;
break ;
case 0 x00C025:
cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA256" ;
break ;
case 0 x00C026:
cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA384" ;
break ;
case 0 x00C027:
cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA256" ;
break ;
case 0 x00C028:
cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA384" ;
break ;
case 0 x00C029:
cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA256" ;
break ;
case 0 x00C02A:
cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA384" ;
break ;
case 0 x00C02B:
cs_str = "TLS/ECDHE-ECDSA/AES128-GCM/SHA256" ;
break ;
case 0 x00C02C:
cs_str = "TLS/ECDHE-ECDSA/AES256-GCM/SHA384" ;
break ;
case 0 x00C02F:
cs_str = "TLS/ECDHE-RSA/AES128-GCM/SHA256" ;
break ;
case 0 x00CCA8:
cs_str = "TLS/ECDHE-RSA/CHACHA20-POLY1305/SHA256" ;
break ;
case 0 x00CCA9:
cs_str = "TLS/ECDHE-ECDSA/CHACHA20-POLY1305/SHA256" ;
break ;
case 0 x00CCAA:
cs_str = "TLS/DHE-RSA/CHACHA20-POLY1305/SHA256" ;
break ;
case 0 x00FEFF:
cs_str = "SSL3/RSA-FIPS/3DESEDE-CBC/SHA" ;
break ;
case 0 x00FEFE:
cs_str = "SSL3/RSA-FIPS/DES-CBC/SHA" ;
break ;
case 0 x00FFE1:
cs_str = "SSL3/RSA-FIPS/DES56-CBC/SHA" ;
break ;
case 0 x00FFE0:
cs_str = "SSL3/RSA-FIPS/3DES192EDE-CBC/SHA" ;
break ;
/* the string literal is broken up to avoid trigraphs */
default :
cs_str = "????"
"/????????"
"/?????????"
"/???" ;
break ;
}
return cs_str;
}
const char *
CompressionMethodString(int cm_int)
{
char *cm_str;
cm_str = NULL;
switch (cm_int) {
case 0 :
cm_str = "NULL" ;
break ;
case 1 :
cm_str = "DEFLATE" ;
break ; /* RFC 3749 */
case 64 :
cm_str = "LZS" ;
break ; /* RFC 3943 */
default :
cm_str = "???" ;
break ;
}
return cm_str;
}
const char *
helloExtensionNameString(int ex_num)
{
const char *ex_name = NULL;
static char buf[10 ];
switch (ex_num) {
case 0 :
ex_name = "server_name" ;
break ;
case 1 :
ex_name = "max_fragment_length" ;
break ;
case 2 :
ex_name = "client_certificate_url" ;
break ;
case 3 :
ex_name = "trusted_ca_keys" ;
break ;
case 4 :
ex_name = "truncated_hmac" ;
break ;
case 5 :
ex_name = "status_request" ;
break ;
case 10 :
ex_name = "elliptic_curves" ;
break ;
case 11 :
ex_name = "ec_point_formats" ;
break ;
case 13 :
ex_name = "signature_algorithms" ;
break ;
case 35 :
ex_name = "session_ticket" ;
break ;
case 0 xff01:
ex_name = "renegotiation_info" ;
break ;
default :
snprintf(buf, sizeof (buf), "%d" , ex_num);
ex_name = (const char *)buf;
break ;
}
return ex_name;
}
static int
isNULLmac(int cs_int)
{
return (cs_int == TLS_NULL_WITH_NULL_NULL);
}
static int
isNULLcipher(int cs_int)
{
return ((cs_int == TLS_RSA_WITH_NULL_MD5) ||
(cs_int == TLS_RSA_WITH_NULL_SHA) ||
(cs_int == SSL_FORTEZZA_DMS_WITH_NULL_SHA) ||
(cs_int == TLS_ECDH_ECDSA_WITH_NULL_SHA) ||
(cs_int == TLS_ECDHE_ECDSA_WITH_NULL_SHA) ||
(cs_int == TLS_ECDH_RSA_WITH_NULL_SHA) ||
(cs_int == TLS_ECDHE_RSA_WITH_NULL_SHA));
}
void
partial_packet(int thispacket, int size, int needed)
{
PR_fprintf(PR_STDOUT, "(%u bytes" , thispacket);
if (thispacket < needed) {
PR_fprintf(PR_STDOUT, ", making %u" , size);
}
PR_fprintf(PR_STDOUT, " of %u" , needed);
if (size > needed) {
PR_fprintf(PR_STDOUT, ", with %u left over" , size - needed);
}
PR_fprintf(PR_STDOUT, ")\n" );
}
char *
get_time_string(void )
{
char *cp;
char *eol;
time_t tt;
time(&tt);
cp = ctime(&tt);
eol = strchr(cp, '\n' );
if (eol)
*eol = 0 ;
return cp;
}
void
print_sslv2(DataBufferList *s, unsigned char *recordBuf, unsigned int recordLen)
{
ClientHelloV2 *chv2;
ServerHelloV2 *shv2;
unsigned char *pos;
unsigned int p;
unsigned int q;
PRUint32 len;
chv2 = (ClientHelloV2 *)recordBuf;
shv2 = (ServerHelloV2 *)recordBuf;
if (s->isEncrypted) {
PR_fprintf(PR_STDOUT, " [ssl2] Encrypted {...}\n" );
return ;
}
PR_fprintf(PR_STDOUT, " [%s]" , get_time_string());
switch (chv2->type) {
case 1 :
PR_fprintf(PR_STDOUT, " [ssl2] ClientHelloV2 {\n" );
PR_fprintf(PR_STDOUT, " version = {0x%02x, 0x%02x}\n" ,
(PRUint32)chv2->version[0 ], (PRUint32)chv2->version[1 ]);
PR_fprintf(PR_STDOUT, " cipher-specs-length = %d (0x%02x)\n" ,
(PRUint32)(GET_SHORT((chv2->cslength))),
(PRUint32)(GET_SHORT((chv2->cslength))));
PR_fprintf(PR_STDOUT, " sid-length = %d (0x%02x)\n" ,
(PRUint32)(GET_SHORT((chv2->sidlength))),
(PRUint32)(GET_SHORT((chv2->sidlength))));
PR_fprintf(PR_STDOUT, " challenge-length = %d (0x%02x)\n" ,
(PRUint32)(GET_SHORT((chv2->rndlength))),
(PRUint32)(GET_SHORT((chv2->rndlength))));
PR_fprintf(PR_STDOUT, " cipher-suites = { \n" );
for (p =
0 ;
p < (PRUint32)GET_SHORT((chv2->cslength)); p += 3 ) {
PRUint32 cs_int = GET_24((&chv2->csuites[p]));
const char *cs_str =
V2CipherString(cs_int);
PR_fprintf(PR_STDOUT, " (0x%06x) %s\n" ,
cs_int, cs_str);
}
q = p;
PR_fprintf(PR_STDOUT, " }\n" );
if (GET_SHORT((chv2->sidlength))) {
PR_fprintf(PR_STDOUT, " session-id = { " );
for (p = 0 ;
p < (PRUint32)GET_SHORT((chv2->sidlength)); p += 2 ) {
PR_fprintf(PR_STDOUT, "0x%04x " , (PRUint32)(GET_SHORT((&chv2->csuites[p + q]))));
}
}
q += p;
PR_fprintf(PR_STDOUT, "}\n" );
if (GET_SHORT((chv2->rndlength))) {
PR_fprintf(PR_STDOUT, " challenge = { " );
for (p = 0 ;
p < (PRUint32)GET_SHORT((chv2->rndlength)); p += 2 ) {
PR_fprintf(PR_STDOUT, "0x%04x " , (PRUint32)(GET_SHORT((&chv2->csuites[p + q]))));
}
PR_fprintf(PR_STDOUT, "}\n" );
}
PR_fprintf(PR_STDOUT, "}\n" );
break ;
/* end of V2 CLientHello Parsing */
case 2 : /* Client Master Key */
{
const char *cs_str =
NULL;
PRUint32 cs_int =
0 ;
ClientMasterKeyV2 *cmkv2;
cmkv2 = (ClientMasterKeyV2 *)chv2;
isV2Session = 1 ;
PR_fprintf(PR_STDOUT, " [ssl2] ClientMasterKeyV2 { \n" );
cs_int = GET_24(&cmkv2->cipherkind[0 ]);
cs_str = V2CipherString(cs_int);
PR_fprintf(PR_STDOUT, " cipher-spec-chosen = (0x%06x) %s\n" ,
cs_int, cs_str);
PR_fprintf(PR_STDOUT, " clear-portion = %d bits\n" ,
8 *
(PRUint32)(GET_SHORT((cmkv2->clearkey))));
PR_fprintf(PR_STDOUT, " }\n" );
clientstream.isEncrypted = 1 ;
serverstream.isEncrypted = 1 ;
} break ;
case 3 :
PR_fprintf(PR_STDOUT, " [ssl2] Client Finished V2 {...}\n" );
isV2Session = 1 ;
break ;
case 4 : /* V2 Server Hello */
isV2Session = 1 ;
PR_fprintf(PR_STDOUT, " [ssl2] ServerHelloV2 {\n" );
PR_fprintf(PR_STDOUT, " sid hit = {0x%02x}\n" ,
(PRUintn)shv2->sidhit);
PR_fprintf(PR_STDOUT, " version = {0x%02x, 0x%02x}\n" ,
(PRUint32)shv2->version[0 ], (PRUint32)shv2->version[1 ]);
PR_fprintf(PR_STDOUT, " cipher-specs-length = %d (0x%02x)\n" ,
(PRUint32)(GET_SHORT((shv2->cslength))),
(PRUint32)(GET_SHORT((shv2->cslength))));
PR_fprintf(PR_STDOUT, " sid-length = %d (0x%02x)\n" ,
(PRUint32)(GET_SHORT((shv2->cidlength))),
(PRUint32)(GET_SHORT((shv2->cidlength))));
pos = (unsigned char *)shv2;
pos += 2 ; /* skip length header */
pos += 11 ; /* position pointer to Certificate data area */
q = GET_SHORT(&shv2->certlength);
if (q > recordLen) {
goto eosh;
}
pos += q; /* skip certificate */
PR_fprintf(PR_STDOUT, " cipher-suites = { " );
len = GET_SHORT((shv2->cslength));
for (p = 0 ; p < len; p += 3 ) {
PRUint32 cs_int = GET_24((pos + p));
const char *cs_str =
V2CipherString(cs_int);
PR_fprintf(PR_STDOUT, "\n " );
PR_fprintf(PR_STDOUT, "(0x%06x) %s" , cs_int, cs_str);
}
pos += len;
PR_fprintf(PR_STDOUT, " }\n" ); /* End of cipher suites */
len = (PRUint32)GET_SHORT((shv2->cidlength));
if (len) {
PR_fprintf(PR_STDOUT, " connection-id = { " );
for (p =
0 ;
p < len; p += 2 ) {
PR_fprintf(PR_STDOUT, "0x%04x " , (PRUint32)(GET_SHORT((pos + p))));
}
PR_fprintf(PR_STDOUT, " }\n" ); /* End of connection id */
}
eosh:
PR_fprintf(PR_STDOUT, "\n }\n" ); /* end of ServerHelloV2 */
if (shv2->sidhit) {
clientstream.isEncrypted =
1 ;
serverstream.isEncrypted =
1 ;
}
break ;
case 5 :
PR_fprintf(PR_STDOUT, " [ssl2] Server Verify V2 {...}\n" );
isV2Session = 1 ;
break ;
case 6 :
PR_fprintf(PR_STDOUT, " [ssl2] Server Finished V2 {...}\n" );
isV2Session = 1 ;
break ;
case 7 :
PR_fprintf(PR_STDOUT, " [ssl2] Request Certificate V2 {...}\n" );
isV2Session = 1 ;
break ;
case 8 :
PR_fprintf(PR_STDOUT, " [ssl2] Client Certificate V2 {...}\n" );
isV2Session = 1 ;
break ;
default :
PR_fprintf(PR_STDOUT, " [ssl2] UnknownType 0x%02x {...}\n" ,
(PRUint32)chv2->type);
break ;
}
}
unsigned int
print_hello_extension(unsigned char *hsdata,
unsigned int length,
unsigned int pos)
{
/* pretty print extensions, if any */
if (pos < length) {
int exListLen = GET_SHORT((hsdata + pos));
pos += 2 ;
PR_fprintf(PR_STDOUT,
" extensions[%d] = {\n" , exListLen);
while (exListLen > 0 && pos < length) {
int exLen;
int exType = GET_SHORT((hsdata + pos));
pos += 2 ;
exLen = GET_SHORT((hsdata + pos));
pos += 2 ;
/* dump the extension */
PR_fprintf(PR_STDOUT,
" extension type %s, length [%d]" ,
helloExtensionNameString(exType), exLen);
if (exLen > 0 ) {
PR_fprintf(PR_STDOUT, " = {\n" );
print_hex(exLen, hsdata + pos);
PR_fprintf(PR_STDOUT, " }\n" );
} else {
PR_fprintf(PR_STDOUT, "\n" );
}
pos += exLen;
exListLen -= 2 + exLen;
}
PR_fprintf(PR_STDOUT, " }\n" );
}
return pos;
}
/*
* Note this must match ( exactly ) the enumeration ocspResponseStatus .
*/
static char *responseStatusNames[] = {
"successful (Response has valid confirmations)" ,
"malformedRequest (Illegal confirmation request)" ,
"internalError (Internal error in issuer)" ,
"tryLater (Try again later)" ,
"unused ((4) is not used)" ,
"sigRequired (Must sign the request)" ,
"unauthorized (Request unauthorized)" ,
};
static void
print_ocsp_cert_id(FILE *out_file, CERTOCSPCertID *cert_id, int level)
{
SECU_Indent(out_file, level);
fprintf(out_file, "Cert ID:\n" );
level++;
/*
SECU_PrintAlgorithmID ( out_file , & ( cert_id - > hashAlgorithm ) ,
" Hash Algorithm " , level ) ;
SECU_PrintAsHex ( out_file , & ( cert_id - > issuerNameHash ) ,
" Issuer Name Hash " , level ) ;
SECU_PrintAsHex ( out_file , & ( cert_id - > issuerKeyHash ) ,
" Issuer Key Hash " , level ) ;
*/
SECU_PrintInteger(out_file, &(cert_id->serialNumber),
"Serial Number" , level);
/* XXX lookup the cert; if found, print something nice (nickname?) */
}
static void
print_ocsp_version(FILE *out_file, SECItem *version, int level)
{
if (version->len > 0 ) {
SECU_PrintInteger(out_file, version, "Version" , level);
} else {
SECU_Indent(out_file, level);
fprintf(out_file, "Version: DEFAULT\n" );
}
}
static void
print_responder_id(FILE *out_file, ocspResponderID *responderID, int level)
{
SECU_Indent(out_file, level);
fprintf(out_file, "Responder ID " );
switch (responderID->responderIDType) {
case ocspResponderID_byName:
fprintf(out_file, "(byName):\n" );
SECU_PrintName(out_file, &(responderID->responderIDValue.name),
"Name" , level + 1 );
break ;
case ocspResponderID_byKey:
fprintf(out_file, "(byKey):\n" );
SECU_PrintAsHex(out_file, &(responderID->responderIDValue.keyHash),
"Key Hash" , level + 1 );
break ;
default :
fprintf(out_file, "Unrecognized Responder ID Type\n" );
break ;
}
}
static void
print_ocsp_extensions(FILE *out_file, CERTCertExtension **extensions,
char *msg, int level)
{
if (extensions) {
SECU_PrintExtensions(out_file, extensions, msg, level);
} else {
SECU_Indent(out_file, level);
fprintf(out_file, "No %s\n" , msg);
}
}
static void
print_revoked_info(FILE *out_file, ocspRevokedInfo *revoked_info, int level)
{
SECU_PrintGeneralizedTime(out_file, &(revoked_info->revocationTime),
"Revocation Time" , level);
if (revoked_info->revocationReason != NULL) {
SECU_PrintAsHex(out_file, revoked_info->revocationReason,
"Revocation Reason" , level);
} else {
SECU_Indent(out_file, level);
fprintf(out_file, "No Revocation Reason.\n" );
}
}
static void
print_cert_status(FILE *out_file, ocspCertStatus *status, int level)
{
SECU_Indent(out_file, level);
fprintf(out_file, "Status: " );
switch (status->certStatusType) {
case ocspCertStatus_good:
fprintf(out_file, "Cert is good.\n" );
break ;
case ocspCertStatus_revoked:
fprintf(out_file, "Cert has been revoked.\n" );
print_revoked_info(out_file, status->certStatusInfo.revokedInfo,
level + 1 );
break ;
case ocspCertStatus_unknown:
fprintf(out_file, "Cert is unknown to responder.\n" );
break ;
default :
fprintf(out_file, "Unrecognized status.\n" );
break ;
}
}
static void
print_single_response(FILE *out_file, CERTOCSPSingleResponse *single,
int level)
{
print_ocsp_cert_id(out_file, single->certID, level);
print_cert_status(out_file, single->certStatus, level);
SECU_PrintGeneralizedTime(out_file, &(single->thisUpdate),
"This Update" , level);
if (single->nextUpdate != NULL) {
SECU_PrintGeneralizedTime(out_file, single->nextUpdate,
"Next Update" , level);
} else {
SECU_Indent(out_file, level);
fprintf(out_file, "No Next Update\n" );
}
print_ocsp_extensions(out_file, single->singleExtensions,
"Single Response Extensions" , level);
}
static void
print_response_data(FILE *out_file, ocspResponseData *responseData, int level)
{
SECU_Indent(out_file, level);
fprintf(out_file, "Response Data:\n" );
level++;
print_ocsp_version(out_file, &(responseData->version), level);
print_responder_id(out_file, responseData->responderID, level);
SECU_PrintGeneralizedTime(out_file, &(responseData->producedAt),
"Produced At" , level);
if (responseData->responses != NULL) {
int i;
for (i = 0 ; responseData->responses[i] != NULL; i++) {
SECU_Indent(out_file, level);
fprintf(out_file, "Response %d:\n" , i);
print_single_response(out_file, responseData->responses[i],
level + 1 );
}
} else {
fprintf(out_file, "Response list is empty.\n" );
}
print_ocsp_extensions(out_file, responseData->responseExtensions,
"Response Extensions" , level);
}
static void
print_basic_response(FILE *out_file, ocspBasicOCSPResponse *basic, int level)
{
SECU_Indent(out_file, level);
fprintf(out_file, "Basic OCSP Response:\n" );
level++;
print_response_data(out_file, basic->tbsResponseData, level);
}
static void
print_status_response(SECItem *data)
{
int level = 2 ;
CERTOCSPResponse *response;
response = CERT_DecodeOCSPResponse(data);
if (!response) {
SECU_Indent(stdout, level);
fprintf(stdout, "unable to decode certificate_status\n" );
return ;
}
SECU_Indent(stdout, level);
if (response->statusValue >= ocspResponse_min &&
response->statusValue <= ocspResponse_max) {
fprintf(stdout, "Response Status: %s\n" ,
responseStatusNames[response->statusValue]);
} else {
fprintf(stdout,
"Response Status: other (Status value %d out of defined range)\n" ,
(int )response->statusValue);
}
if (response->statusValue == ocspResponse_successful) {
ocspResponseBytes *responseBytes = response->responseBytes;
PORT_Assert(responseBytes != NULL);
level++;
SECU_PrintObjectID(stdout, &(responseBytes->responseType),
"Response Type" , level);
switch (response->responseBytes->responseTypeTag) {
case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
print_basic_response(stdout,
responseBytes->decodedResponse.basic,
level);
break ;
default :
SECU_Indent(stdout, level);
fprintf(stdout, "Unknown response syntax\n" );
break ;
}
} else {
SECU_Indent(stdout, level);
fprintf(stdout, "Unsuccessful response, no more information.\n" );
}
CERT_DestroyOCSPResponse(response);
}
/* In the case of renegotiation, handshakes that occur in an already MAC'ed
* channel , by the time of this call , the caller has already removed the MAC
* from input recordLen . The only MAC ' ed record that will get here with its
* MAC intact ( not removed ) is the first Finished message on the connection .
*/
void
print_ssl3_handshake(unsigned char *recordBuf,
unsigned int recordLen,
SSLRecord *sr,
DataBufferList *s)
{
struct sslhandshake sslh;
unsigned char *hsdata;
unsigned int offset = 0 ;
PR_fprintf(PR_STDOUT, " handshake {\n" );
if (s->msgBufOffset && s->msgBuf) {
/* append recordBuf to msgBuf, then use msgBuf */
if (s->msgBufOffset + recordLen > s->msgBufSize) {
int newSize = s->msgBufOffset + recordLen;
unsigned char *newBuf = PORT_Realloc(s->msgBuf, newSize);
if (!newBuf) {
PR_ASSERT(newBuf);
showErr("Realloc failed" );
exit (10 );
}
s->msgBuf = newBuf;
s->msgBufSize = newSize;
}
memcpy(s->msgBuf + s->msgBufOffset, recordBuf, recordLen);
s->msgBufOffset += recordLen;
recordLen = s->msgBufOffset;
recordBuf = s->msgBuf;
}
while (offset + 4 <= recordLen) {
sslh.type = recordBuf[offset];
sslh.length = GET_24(recordBuf + offset + 1 );
if (offset + 4 + sslh.length > recordLen)
break ;
/* finally have a complete message */
if (sslhexparse)
print_hex(4 , recordBuf + offset);
hsdata = &recordBuf[offset + 4 ];
PR_fprintf(PR_STDOUT, " type = %d (" , sslh.type);
switch (sslh.type) {
case 0 :
PR_FPUTS("hello_request)\n" );
break ;
case 1 :
PR_FPUTS("client_hello)\n" );
break ;
case 2 :
PR_FPUTS("server_hello)\n" );
break ;
case 4 :
PR_FPUTS("new_session_ticket)\n" );
break ;
case 11 :
PR_FPUTS("certificate)\n" );
break ;
case 12 :
PR_FPUTS("server_key_exchange)\n" );
break ;
case 13 :
PR_FPUTS("certificate_request)\n" );
break ;
case 14 :
PR_FPUTS("server_hello_done)\n" );
break ;
case 15 :
PR_FPUTS("certificate_verify)\n" );
break ;
case 16 :
PR_FPUTS("client_key_exchange)\n" );
break ;
case 20 :
PR_FPUTS("finished)\n" );
break ;
case 22 :
PR_FPUTS("certificate_status)\n" );
break ;
default :
PR_FPUTS("unknown)\n" );
break ;
}
PR_fprintf(PR_STDOUT, " length = %d (0x%06x)\n" , sslh.length, sslh.length);
switch (sslh.type) {
case 0 : /* hello_request */ /* not much to show here. */
break ;
case 1 : /* client hello */
switch (sr->ver_maj) {
case 3 : /* ssl version 3 */
{
unsigned int pos;
int w;
PR_fprintf(PR_STDOUT, " ClientHelloV3 {\n" );
PR_fprintf(PR_STDOUT, " client_version = {%d, %d}\n" ,
(PRUint8)hsdata[0 ], (PRUint8)hsdata[1 ]);
PR_fprintf(PR_STDOUT, " random = {...}\n" );
if (sslhexparse)
print_hex(32 , &hsdata[2 ]);
/* pretty print Session ID */
{
int sidlength =
(int )hsdata[2 + 32 ];
PR_fprintf(PR_STDOUT, " session ID = {\n" );
PR_fprintf(PR_STDOUT, " length = %d\n" , sidlength);
PR_fprintf(PR_STDOUT, " contents = {...}\n" );
if (sslhexparse)
print_hex(sidlength, &hsdata[2 + 32 + 1 ]);
PR_fprintf(PR_STDOUT, " }\n" );
pos =
2 +
32 +
1 +
sidlength;
}
/* pretty print cipher suites */
{
int csuitelength =
GET_SHORT((hsdata + pos));
PR_fprintf(PR_STDOUT, " cipher_suites[%d] = {\n" ,
csuitelength /
2 );
if (csuitelength %
2 ) {
PR_fprintf(PR_STDOUT,
"*error in protocol - csuitelength shouldn't be odd*\n" );
}
for (w =
0 ;
w <
csuitelength;
w += 2 ) {
PRUint32 cs_int =
GET_SHORT((hsdata + pos + 2 + w));
const char *cs_str =
V2CipherString(cs_int);
PR_fprintf(PR_STDOUT,
" (0x%04x) %s\n" , cs_int, cs_str);
}
pos +=
2 +
csuitelength;
PR_fprintf(PR_STDOUT, " }\n" );
}
/* pretty print compression methods */
{
int complength =
hsdata[pos];
PR_fprintf(PR_STDOUT, " compression[%d] = {\n" ,
complength);
for (w =
0 ;
w <
complength;
w++) {
PRUint32 cm_int =
hsdata[pos + 1 + w];
const char *cm_str =
CompressionMethodString(cm_int);
PR_fprintf(PR_STDOUT,
" (%02x) %s\n" , cm_int, cm_str);
}
pos +=
1 +
complength;
PR_fprintf(PR_STDOUT, " }\n" );
}
/* pretty print extensions, if any */
pos =
print_hello_extension(hsdata, sslh.length, pos);
PR_fprintf(PR_STDOUT, " }\n" );
} /* end of ssl version 3 */
break ;
default :
PR_fprintf(PR_STDOUT, " UNDEFINED VERSION %d.%d {...}\n" ,
sr->ver_maj, sr->ver_min);
if (sslhexparse)
print_hex(sslh.length, hsdata);
break ;
} /* end of switch sr->ver_maj */
break ;
case 2 : /* server hello */
{
unsigned int sidlength, pos;
PR_fprintf(PR_STDOUT, " ServerHello {\n" );
PR_fprintf(PR_STDOUT, " server_version = {%d, %d}\n" ,
(PRUint8)hsdata[0 ], (PRUint8)hsdata[1 ]);
PR_fprintf(PR_STDOUT, " random = {...}\n" );
if (sslhexparse)
print_hex(32 , &hsdata[2 ]);
PR_fprintf(PR_STDOUT, " session ID = {\n" );
sidlength = (int )hsdata[2 +
32 ];
PR_fprintf(PR_STDOUT, " length = %d\n" , sidlength);
PR_fprintf(PR_STDOUT, " contents = {...}\n" );
if (sslhexparse)
print_hex(sidlength, &hsdata[2 + 32 + 1 ]);
PR_fprintf(PR_STDOUT, " }\n" );
pos = 2 +
32 + 1 +
sidlength;
/* pretty print chosen cipher suite */
{
PRUint32 cs_int = GET_SHORT((hsdata + pos));
const char *cs_str =
V2CipherString(cs_int);
PR_fprintf(PR_STDOUT, " cipher_suite = (0x%04x) %s\n" ,
cs_int, cs_str);
currentcipher =
cs_int;
pos +=
2 ;
}
/* pretty print chosen compression method */
{
PRUint32 cm_int = hsdata[pos++];
const char *cm_str =
CompressionMethodString(cm_int);
PR_fprintf(PR_STDOUT, " compression method = (%02x) %s\n" ,
cm_int, cm_str);
}
/* pretty print extensions, if any */
pos = print_hello_extension(hsdata, sslh.length, pos);
PR_fprintf(PR_STDOUT, " }\n" );
} break ;
case 4 : /* new session ticket */
{
PRUint32 lifetimehint;
PRUint16 ticketlength;
char lifetime[32 ];
lifetimehint = GET_32(hsdata);
if (lifetimehint) {
PRExplodedTime et;
PRTime t =
lifetimehint;
t *=
PR_USEC_PER_SEC;
PR_ExplodeTime(t, PR_GMTParameters, &et);
/* use HTTP Cookie header's date format */
PR_FormatTimeUSEnglish(lifetime, sizeof lifetime,
"%a, %d-%b-%Y %H:%M:%S GMT" , &et);
} else {
/* 0 means the lifetime of the ticket is unspecified */
strcpy(lifetime, "unspecified" );
}
ticketlength = GET_SHORT((hsdata +
4 ));
PR_fprintf(PR_STDOUT, " NewSessionTicket {\n" );
PR_fprintf(PR_STDOUT, " ticket_lifetime_hint = %s\n" ,
lifetime);
PR_fprintf(PR_STDOUT, " ticket = {\n" );
PR_fprintf(PR_STDOUT, " length = %d\n" , ticketlength);
PR_fprintf(PR_STDOUT, " contents = {...}\n" );
if (sslhexparse)
print_hex(ticketlength, &hsdata[4 + 2 ]);
PR_fprintf(PR_STDOUT, " }\n" );
PR_fprintf(PR_STDOUT, " }\n" );
} break ;
case 11 : /* certificate */
{
PRFileDesc *cfd;
int pos;
int certslength;
int certlength;
int certbytesread = 0 ;
static int certFileNumber;
char certFileName[20 ];
PR_fprintf(PR_STDOUT, " CertificateChain {\n" );
certslength = GET_24(hsdata);
PR_fprintf(PR_STDOUT, " chainlength = %d (0x%04x)\n" ,
certslength, certslength);
pos = 3 ;
while (certbytesread < certslength) {
certlength =
GET_24((hsdata + pos));
pos +=
3 ;
PR_fprintf(PR_STDOUT, " Certificate {\n" );
PR_fprintf(PR_STDOUT, " size = %d (0x%04x)\n" ,
certlength, certlength);
certbytesread +=
certlength + 3 ;
if (certbytesread <=
certslength) {
PR_snprintf(certFileName, sizeof certFileName, "cert.%03d" ,
++certFileNumber);
cfd =
PR_Open(certFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664 );
if (!cfd) {
PR_fprintf(PR_STDOUT,
" data = { couldn't save file '%s' }\n" ,
certFileName);
} else {
PR_Write(cfd, (hsdata + pos),
certlength);
PR_fprintf(PR_STDOUT,
" data = { saved in file '%s' }\n" ,
certFileName);
PR_Close(cfd);
}
}
PR_fprintf(PR_STDOUT, " }\n" );
pos += certlength;
}
PR_fprintf(PR_STDOUT, " }\n" );
} break ;
case 12 : /* server_key_exchange */
if (sslhexparse)
print_hex(sslh.length, hsdata);
break ;
case 13 : /* certificate request */
{
unsigned int pos = 0 ;
int w, reqLength;
PR_fprintf(PR_STDOUT, " CertificateRequest {\n" );
/* pretty print requested certificate types */
reqLength = hsdata[pos];
PR_fprintf(PR_STDOUT, " certificate types[%d] = {" ,
reqLength);
for (w =
0 ;
w < reqLength; w++) {
PR_fprintf(PR_STDOUT, " %02x" , hsdata[pos + 1 + w]);
}
pos += 1 + reqLength;
PR_fprintf(PR_STDOUT, " }\n" );
/* pretty print CA names, if any */
if (pos < sslh.length) {
int exListLen =
GET_SHORT((hsdata + pos));
pos += 2 ;
PR_fprintf(PR_STDOUT,
" certificate_authorities[%d] = {\n" ,
exListLen);
while (exListLen >
0 &&
pos < sslh.length) {
char *ca_name;
SECItem it;
int dnLen = GET_SHORT((hsdata +
pos));
pos += 2 ;
/* dump the CA name */
it.type =
siBuffer;
it.data =
hsdata + pos;
it.len =
dnLen;
ca_name =
CERT_DerNameToAscii(&it);
if (ca_name) {
PR_fprintf(PR_STDOUT, " %s\n" , ca_name);
PORT_Free(ca_name);
} else {
PR_fprintf(PR_STDOUT,
" distinguished name [%d]" , dnLen);
if (dnLen >
0 &&
sslhexparse) {
PR_fprintf(PR_STDOUT, " = {\n" );
print_hex(dnLen, hsdata + pos);
PR_fprintf(PR_STDOUT, " }\n" );
} else {
PR_fprintf(PR_STDOUT, "\n" );
}
}
pos +=
dnLen;
exListLen -=
2 + dnLen;
}
PR_fprintf(PR_STDOUT, " }\n" );
}
PR_fprintf(PR_STDOUT, " }\n" );
} break ;
case 14 : /* server_hello_done */ /* not much to show here. */
break ;
case 15 : /* certificate_verify */
if (sslhexparse)
print_hex(sslh.length, hsdata);
break ;
case 16 : /* client key exchange */
{
PR_fprintf(PR_STDOUT, " ClientKeyExchange {\n" );
PR_fprintf(PR_STDOUT, " message = {...}\n" );
PR_fprintf(PR_STDOUT, " }\n" );
} break ;
case 20 : /* finished */
PR_fprintf(PR_STDOUT, " Finished {\n" );
PR_fprintf(PR_STDOUT, " verify_data = {...}\n" );
if (sslhexparse)
print_hex(sslh.length, hsdata);
PR_fprintf(PR_STDOUT, " }\n" );
if (!isNULLmac(currentcipher) &&
!s->hMACsize) {
/* To calculate the size of MAC, we subtract the number of known
* bytes of message from the number of remaining bytes in the
* record . This assumes that this is the first record on the
* connection to have a MAC , and that the sender has not put another
* message after the finished message in the handshake record .
* This is only correct for the first transition from unMACed to
* MACed . If the connection switches from one cipher suite to
* another one with a different MAC , this logic will not track that
* change correctly .
*/
s->hMACsize =
recordLen - (sslh.length + 4 );
sslh.length +=
s->hMACsize; /* skip over the MAC data */
}
break ;
case 22 : /* certificate_status */
{
SECItem data;
PRFileDesc *ofd;
static int ocspFileNumber;
char ocspFileName[20 ];
/* skip 4 bytes with handshake numbers, as in ssl3_HandleCertificateStatus */
data.type = siBuffer;
data.data = hsdata + 4 ;
data.len = sslh.length - 4 ;
print_status_response(&data);
PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d" ,
++ocspFileNumber);
ofd = PR_Open(ocspFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0664 );
if (!ofd) {
PR_fprintf(PR_STDOUT,
" data = { couldn't save file '%s' }\n" ,
ocspFileName);
} else {
PR_Write(ofd, data.data, data.len);
PR_fprintf(PR_STDOUT,
" data = { saved in file '%s' }\n" ,
ocspFileName);
PR_Close(ofd);
}
} break ;
default : {
PR_fprintf(PR_STDOUT, " UNKNOWN MESSAGE TYPE %d [%d] {\n" ,
sslh.type, sslh.length);
if (sslhexparse)
print_hex(sslh.length, hsdata);
PR_fprintf(PR_STDOUT, " }\n" );
}
} /* end of switch sslh.type */
offset += sslh.length + 4 ;
} /* while */
if (offset < recordLen) { /* stuff left over */
unsigned int newMsgLen = recordLen - offset;
if (!s->msgBuf) {
s->msgBuf = PORT_Alloc(newMsgLen);
if (!s->msgBuf) {
PR_ASSERT(s->msgBuf);
showErr("Malloc failed" );
exit (11 );
}
s->msgBufSize = newMsgLen;
memcpy(s->msgBuf, recordBuf + offset, newMsgLen);
} else if (newMsgLen > s->msgBufSize) {
unsigned char *newBuf = PORT_Realloc(s->msgBuf, newMsgLen);
if (!newBuf) {
PR_ASSERT(newBuf);
showErr("Realloc failed" );
exit (12 );
}
s->msgBuf = newBuf;
s->msgBufSize = newMsgLen;
} else if (offset || s->msgBuf != recordBuf) {
memmove(s->msgBuf, recordBuf + offset, newMsgLen);
}
s->msgBufOffset = newMsgLen;
PR_fprintf(PR_STDOUT, " [incomplete handshake message]\n" );
} else {
s->msgBufOffset = 0 ;
}
PR_fprintf(PR_STDOUT, " }\n" );
}
void
print_ssl(DataBufferList *s, int length, unsigned char *buffer)
{
/* -------------------------------------------------------- */
/* first, create a new buffer object for this piece of data. */
DataBuffer *db;
if (s->size == 0 && length > 0 && buffer[0 ] >= 32 && buffer[0 ] < 128 ) {
/* Not an SSL record, treat entire buffer as plaintext */
PR_Write(PR_STDOUT, buffer, length);
return ;
}
check_integrity(s);
db = PR_NEW(struct _DataBuffer);
if (!db) {
return ;
}
db->buffer = (unsigned char *)PORT_Alloc(length);
db->length = length;
db->offset = 0 ;
memcpy(db->buffer, buffer, length);
db->next = NULL;
/* now, add it to the stream */
if (s->last != NULL)
s->last->next = db;
s->last = db;
s->size += length;
if (s->first == NULL)
s->first = db;
check_integrity(s);
/*------------------------------------------------------- */
/* now we look at the stream to see if we have enough data to
decode */
while (s->size > 0 ) {
unsigned char *recordBuf = NULL;
SSLRecord sr;
unsigned recordLen;
unsigned recordsize;
check_integrity(s);
if (s->first == NULL) {
PR_fprintf(PR_STDOUT, "ERROR: s->first is null\n" );
exit (9 );
}
/* in the case of an SSL 2 client-hello */
/* will have the high-bit set, whereas an SSL 3 client-hello will not */
/* SSL2 can also send records that begin with the high bit clear.
* This code will incorrectly handle them . XXX
*/
if (isV2Session || s->first->buffer[s->first->offset] & 0 x80) {
/* it's an SSL 2 packet */
unsigned char lenbuf[3 ];
/* first, we check if there's enough data for it to be an SSL2-type
* record. What a pain.*/
if (s->size < sizeof lenbuf) {
partial_packet(length, s->size, sizeof lenbuf);
return ;
}
/* read the first two bytes off the stream. */
read_stream_bytes(lenbuf, s, sizeof (lenbuf));
recordLen = ((unsigned int )(lenbuf[0 ] & 0 x7f) << 8 ) + lenbuf[1 ] +
((lenbuf[0 ] & 0 x80) ? 2 : 3 );
PR_fprintf(PR_STDOUT, "recordLen = %u bytes\n" , recordLen);
/* put 'em back on the head of the stream. */
db = PR_NEW(struct _DataBuffer);
db->length = sizeof lenbuf;
db->buffer = (unsigned char *)PORT_Alloc(db->length);
db->offset = 0 ;
memcpy(db->buffer, lenbuf, sizeof lenbuf);
db->next = s->first;
s->first = db;
if (s->last == NULL)
s->last = db;
s->size += db->length;
/* if there wasn't enough, go back for more. */
if (s->size < recordLen) {
check_integrity(s);
partial_packet(length, s->size, recordLen);
return ;
}
partial_packet(length, s->size, recordLen);
/* read in the whole record. */
recordBuf = PORT_Alloc(recordLen);
read_stream_bytes(recordBuf, s, recordLen);
print_sslv2(s, recordBuf, recordLen);
PR_FREEIF(recordBuf);
check_integrity(s);
continue ;
}
/***********************************************************/
/* It's SSL v3 */
/***********************************************************/
check_integrity(s);
if (s->size < sizeof sr) {
partial_packet(length, s->size, sizeof (SSLRecord));
return ;
}
read_stream_bytes((unsigned char *)&sr, s, sizeof sr);
/* we have read the stream bytes. Look at the length of
the ssl record . If we don ' t have enough data to satisfy this
request , then put the bytes we just took back at the head
of the queue */
recordsize = GET_SHORT(sr.length);
if (recordsize > s->size) {
db = PR_NEW(struct _DataBuffer);
db->length = sizeof sr;
db->buffer = (unsigned char *)PORT_Alloc(db->length);
db->offset = 0 ;
memcpy(db->buffer, &sr, sizeof sr);
db->next = s->first;
/* now, add it back on to the head of the stream */
s->first = db;
if (s->last == NULL)
s->last = db;
s->size += db->length;
check_integrity(s);
partial_packet(length, s->size, recordsize);
return ;
}
partial_packet(length, s->size, recordsize);
PR_fprintf(PR_STDOUT, "SSLRecord { [%s]\n" , get_time_string());
if (sslhexparse) {
print_hex(5 , (unsigned char *)&sr);
}
check_integrity(s);
PR_fprintf(PR_STDOUT, " type = %d (" , sr.type);
switch (sr.type) {
case 20 :
PR_fprintf(PR_STDOUT, "change_cipher_spec)\n" );
break ;
case 21 :
PR_fprintf(PR_STDOUT, "alert)\n" );
break ;
case 22 :
PR_fprintf(PR_STDOUT, "handshake)\n" );
break ;
case 23 :
PR_fprintf(PR_STDOUT, "application_data)\n" );
break ;
default :
PR_fprintf(PR_STDOUT, "unknown)\n" );
break ;
}
PR_fprintf(PR_STDOUT, " version = { %d,%d }\n" ,
(PRUint32)sr.ver_maj, (PRUint32)sr.ver_min);
PR_fprintf(PR_STDOUT, " length = %d (0x%x)\n" ,
(PRUint32)GET_SHORT(sr.length), (PRUint32)GET_SHORT(sr.length));
recordLen = recordsize;
PR_ASSERT(s->size >= recordLen);
if (s->size >= recordLen) {
recordBuf = (unsigned char *)PORT_Alloc(recordLen);
read_stream_bytes(recordBuf, s, recordLen);
if (s->isEncrypted) {
PR_fprintf(PR_STDOUT, " < encrypted >\n" );
} else { /* not encrypted */
switch (sr.type) {
case 20 : /* change_cipher_spec */
if (sslhexparse)
print_hex(recordLen - s->hMACsize, recordBuf);
/* mark to say we can only dump hex form now on
* if it is not one on a null cipher */
s->isEncrypted =
isNULLcipher(currentcipher) ? 0 : 1 ;
break ;
case 21 : /* alert */
switch (recordBuf[0 ]) {
case 1 :
PR_fprintf(PR_STDOUT, " warning: " );
break ;
case 2 :
PR_fprintf(PR_STDOUT, " fatal: " );
break ;
default :
PR_fprintf(PR_STDOUT, " unknown level %d: " , recordBuf[0 ]);
break ;
}
switch (recordBuf[1 ]) {
case 0 :
PR_FPUTS("close_notify\n" );
break ;
case 10 :
PR_FPUTS("unexpected_message\n" );
break ;
case 20 :
PR_FPUTS("bad_record_mac\n" );
break ;
case 21 :
PR_FPUTS("decryption_failed\n" );
break ;
case 22 :
PR_FPUTS("record_overflow\n" );
break ;
case 30 :
PR_FPUTS("decompression_failure\n" );
break ;
case 40 :
PR_FPUTS("handshake_failure\n" );
break ;
case 41 :
PR_FPUTS("no_certificate\n" );
break ;
case 42 :
PR_FPUTS("bad_certificate\n" );
break ;
case 43 :
PR_FPUTS("unsupported_certificate\n" );
break ;
case 44 :
PR_FPUTS("certificate_revoked\n" );
break ;
case 45 :
PR_FPUTS("certificate_expired\n" );
break ;
case 46 :
PR_FPUTS("certificate_unknown\n" );
break ;
case 47 :
PR_FPUTS("illegal_parameter\n" );
break ;
case 48 :
PR_FPUTS("unknown_ca\n" );
break ;
case 49 :
PR_FPUTS("access_denied\n" );
break ;
case 50 :
PR_FPUTS("decode_error\n" );
break ;
case 51 :
PR_FPUTS("decrypt_error\n" );
break ;
case 60 :
PR_FPUTS("export_restriction\n" );
break ;
case 70 :
PR_FPUTS("protocol_version\n" );
break ;
case 71 :
PR_FPUTS("insufficient_security\n" );
break ;
case 80 :
PR_FPUTS("internal_error\n" );
break ;
case 90 :
PR_FPUTS("user_canceled\n" );
break ;
case 100 :
PR_FPUTS("no_renegotiation\n" );
break ;
case 110 :
PR_FPUTS("unsupported_extension\n" );
break ;
case 111 :
PR_FPUTS("certificate_unobtainable\n" );
break ;
case 112 :
PR_FPUTS("unrecognized_name\n" );
break ;
case 113 :
PR_FPUTS("bad_certificate_status_response\n" );
break ;
case 114 :
PR_FPUTS("bad_certificate_hash_value\n" );
break ;
default :
PR_fprintf(PR_STDOUT, "unknown alert %d\n" , recordBuf[1 ]);
break ;
}
if (sslhexparse)
print_hex(recordLen - s->hMACsize, recordBuf);
break ;
case 22 : /* handshake */
print_ssl3_handshake(recordBuf, recordLen - s->hMACsize,
&sr, s);
break ;
case 23 : /* application data */
print_hex(recordLen -
s->hMACsize,
recordBuf);
break ;
default :
print_hex(recordLen -
s->hMACsize,
recordBuf);
break ;
}
if (s->hMACsize) {
PR_fprintf(PR_STDOUT, " MAC = {...}\n" );
if (sslhexparse) {
unsigned char *offset =
recordBuf + (recordLen - s->hMACsize);
print_hex(s->hMACsize, offset);
}
}
} /* not encrypted */
}
PR_fprintf(PR_STDOUT, "}\n" );
PR_FREEIF(recordBuf);
check_integrity(s);
}
}
void
print_hex(int amt, unsigned char *buf)
{
int i, j, k;
char t[20 ];
static char string[5000 ];
for (i = 0 ; i < amt; i++) {
t[1 ] = 0 ;
if (i % 16 == 0 ) { /* if we are at the beginning of a line */
PR_fprintf(PR_STDOUT, "%4x:" , i); /* print the line number */
strcpy(string, "" );
}
if (i % 4 == 0 ) {
PR_fprintf(PR_STDOUT, " " );
}
j = buf[i];
t[0 ] = (j >= 0 x20 && j < 0 x80) ? j : '.' ;
if (fancy) {
switch (t[0 ]) {
case '<' :
strcpy(t, "<" );
break ;
case '>' :
strcpy(t, ">" );
break ;
case '&' :
strcpy(t, "&" );
break ;
}
}
strcat(string, t);
PR_fprintf(PR_STDOUT, "%02x " , (PRUint8)buf[i]);
/* if we've reached the end of the line - add the string */
if (i % 16 == 15 )
PR_fprintf(PR_STDOUT, " | %s\n" , string);
}
/* we reached the end of the buffer,*/
/* do we have buffer left over? */
j = i % 16 ;
if (j > 0 ) {
for (k = 0 ; k < (16 -
j);
k++) {
/* print additional space after every four bytes */
if ((k + j) % 4 == 0 ) {
PR_fprintf(PR_STDOUT, " " );
}
PR_fprintf(PR_STDOUT, " " );
}
PR_fprintf(PR_STDOUT, " | %s\n" , string);
}
}
void
Usage(void )
{
PR_fprintf(PR_STDERR, "Usage: ssltap [-vhfsxl] [-p port] hostname:port\n" );
PR_fprintf(PR_STDERR, " -v [prints version string]\n" );
PR_fprintf(PR_STDERR, " -h [outputs hex instead of ASCII]\n" );
PR_fprintf(PR_STDERR, " -f [turn on Fancy HTML coloring]\n" );
PR_fprintf(PR_STDERR, " -s [turn on SSL decoding]\n" );
PR_fprintf(PR_STDERR, " -x [turn on extra SSL hex dumps]\n" );
PR_fprintf(PR_STDERR, " -p port [specify rendezvous port (default 1924)]\n" );
PR_fprintf(PR_STDERR, " -l [loop - continue to wait for more connections]\n" );
}
void
showErr(const char *msg)
{
PRErrorCode err = PR_GetError();
const char *errString;
if (err == PR_UNKNOWN_ERROR)
err = PR_CONNECT_RESET_ERROR; /* bug in NSPR. */
errString = SECU_Strerror(err);
if (!errString)
errString = "(no text available)" ;
PR_fprintf(PR_STDERR, "%s: Error %d: %s: %s" , progName, err, errString, msg);
}
int
main(int argc, char *argv[])
{
char *hostname = NULL;
PRUint16 rendport = DEFPORT, port;
PRAddrInfo *ai;
void *iter;
PRStatus r;
PRNetAddr na_client, na_server, na_rend;
PRFileDesc *s_server, *s_client, *s_rend; /*rendezvous */
int c_count = 0 ;
PLOptState *optstate;
PLOptStatus status;
SECStatus rv;
progName = argv[0 ];
optstate = PL_CreateOptState(argc, argv, "fxhslp:" );
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case 'f' :
fancy++;
break ;
case 'h' :
hexparse++;
break ;
case 's' :
sslparse++;
break ;
case 'x' :
sslhexparse++;
break ;
case 'l' :
looparound++;
break ;
case 'p' :
rendport =
atoi(optstate->value);
break ;
case '\0' :
hostname =
PL_strdup(optstate->value);
}
}
if (status == PL_OPT_BAD)
Usage();
if (fancy) {
if (!hexparse && !sslparse) {
PR_fprintf(PR_STDERR,
"Note: use of -f without -s or -h not recommended, \n"
"as the output looks a little strange. It may be useful, however\n" );
}
}
if (!hostname)
Usage(), exit (2 );
{
char *colon = (char *)strchr(hostname, ':' );
if (!colon) {
PR_fprintf(PR_STDERR,
"You must specify the host AND port you wish to connect to\n" );
Usage(), exit (3 );
}
port = atoi(&colon[1 ]);
*colon = '\0' ;
if (port == 0 ) {
PR_fprintf(PR_STDERR, "Port must be a nonzero number.\n" );
exit (4 );
}
}
/* find the 'server' IP address so we don't have to look it up later */
if (fancy) {
PR_fprintf(PR_STDOUT, "<HTML><HEAD><TITLE>SSLTAP output</TITLE></HEAD>\n" );
PR_fprintf(PR_STDOUT, "<BODY><PRE>\n" );
}
PR_fprintf(PR_STDERR, "Looking up \" %s\"...\n" , hostname);
ai = PR_GetAddrInfoByName(hostname, PR_AF_UNSPEC, PR_AI_ADDRCONFIG);
if (!ai) {
showErr("Host Name lookup failed\n" );
exit (5 );
}
iter = NULL;
iter = PR_EnumerateAddrInfo(iter, ai, port, &na_server);
/* set up the port which the client will connect to */
r = PR_InitializeNetAddr(PR_IpAddrAny, rendport, &na_rend);
if (r == PR_FAILURE) {
PR_fprintf(PR_STDERR,
"PR_InitializeNetAddr(,%d,) failed with error %d\n" , PR_GetError());
exit (0 );
}
rv = NSS_NoDB_Init("" );
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR,
"NSS_NoDB_Init() failed with error %d\n" , PR_GetError());
exit (5 );
}
s_rend = PR_NewTCPSocket();
if (!s_rend) {
showErr("Couldn't create socket\n" );
exit (6 );
}
if (PR_Bind(s_rend, &na_rend)) {
PR_fprintf(PR_STDERR, "Couldn't bind to port %d (error %d)\n" , rendport, PR_GetError());
exit (-1 );
}
if (PR_Listen(s_rend, 5 )) {
showErr("Couldn't listen\n" );
exit (-1 );
}
PR_fprintf(PR_STDERR, "Proxy socket ready and listening\n" );
do { /* accept one connection and process it. */
PRPollDesc pds[2 ];
s_client = PR_Accept(s_rend, &na_client, PR_SecondsToInterval(3600 ));
if (s_client == NULL) {
showErr("accept timed out\n" );
exit (7 );
}
s_server = PR_OpenTCPSocket(na_server.raw.family);
if (s_server == NULL) {
showErr("couldn't open new socket to connect to server \n" );
exit (8 );
}
r = PR_Connect(s_server, &na_server, PR_SecondsToInterval(5 ));
if (r == PR_FAILURE) {
showErr("Couldn't connect\n" );
return -1 ;
}
if (looparound) {
if (fancy)
PR_fprintf(PR_STDOUT, "<p><HR><H2>" );
PR_fprintf(PR_STDOUT, "Connection #%d [%s]\n" , c_count + 1 ,
get_time_string());
if (fancy)
PR_fprintf(PR_STDOUT, "</H2>" );
}
PR_fprintf(PR_STDOUT, "Connected to %s:%d\n" , hostname, port);
#define PD_C 0
#define PD_S 1
pds[PD_C].fd = s_client;
pds[PD_S].fd = s_server;
pds[PD_C].in_flags = PR_POLL_READ;
pds[PD_S].in_flags = PR_POLL_READ;
/* make sure the new connections don't start out encrypted. */
clientstream.isEncrypted = 0 ;
serverstream.isEncrypted = 0 ;
isV2Session = 0 ;
while ((pds[PD_C].in_flags & PR_POLL_READ) != 0 ||
(pds[PD_S].in_flags & PR_POLL_READ) != 0 ) { /* Handle all messages on the connection */
PRInt32 amt;
PRInt32 wrote;
unsigned char buffer[TAPBUFSIZ];
amt = PR_Poll(pds, 2 , PR_INTERVAL_NO_TIMEOUT);
if (amt <= 0 ) {
if (amt)
showErr("PR_Poll failed.\n" );
else
showErr("PR_Poll timed out.\n" );
break ;
}
if (pds[PD_C].out_flags & PR_POLL_EXCEPT) {
showErr("Exception on client-side socket.\n" );
break ;
}
if (pds[PD_S].out_flags & PR_POLL_EXCEPT) {
showErr("Exception on server-side socket.\n" );
break ;
}
/* read data, copy it to stdout, and write to other socket */
if ((pds[PD_C].in_flags & PR_POLL_READ) != 0 &&
(pds[PD_C].out_flags & PR_POLL_READ) != 0 ) {
amt = PR_Read(s_client, buffer, sizeof (buffer));
if (amt < 0 ) {
showErr("Client socket read failed.\n" );
break ;
}
if (amt == 0 ) {
PR_fprintf(PR_STDOUT, "Read EOF on Client socket. [%s]\n" ,
get_time_string());
pds[PD_C].in_flags &= ~PR_POLL_READ;
PR_Shutdown(s_server, PR_SHUTDOWN_SEND);
continue ;
}
PR_fprintf(PR_STDOUT, "--> [\n" );
if (fancy)
PR_fprintf(PR_STDOUT, "<font color=blue>" );
if (hexparse)
print_hex(amt, buffer);
if (sslparse)
print_ssl(&clientstream, amt, buffer);
if (!hexparse && !sslparse)
PR_Write(PR_STDOUT, buffer, amt);
if (fancy)
PR_fprintf(PR_STDOUT, "</font>" );
PR_fprintf(PR_STDOUT, "]\n" );
wrote = PR_Write(s_server, buffer, amt);
if (wrote != amt) {
if (wrote < 0 ) {
showErr("Write to server socket failed.\n" );
break ;
} else {
PR_fprintf(PR_STDERR, "Short write to server socket!\n" );
}
}
} /* end of read from client socket. */
/* read data, copy it to stdout, and write to other socket */
if ((pds[PD_S].in_flags & PR_POLL_READ) != 0 &&
(pds[PD_S].out_flags & PR_POLL_READ) != 0 ) {
amt = PR_Read(s_server, buffer, sizeof (buffer));
if (amt < 0 ) {
showErr("error on server-side socket.\n" );
break ;
}
if (amt == 0 ) {
PR_fprintf(PR_STDOUT, "Read EOF on Server socket. [%s]\n" ,
get_time_string());
pds[PD_S].in_flags &= ~PR_POLL_READ;
PR_Shutdown(s_client, PR_SHUTDOWN_SEND);
continue ;
}
PR_fprintf(PR_STDOUT, "<-- [\n" );
if (fancy)
PR_fprintf(PR_STDOUT, "<font color=red>" );
if (hexparse)
print_hex(amt, (unsigned char *)buffer);
if (sslparse)
print_ssl(&serverstream, amt, (unsigned char *)buffer);
if (!hexparse && !sslparse)
PR_Write(PR_STDOUT, buffer, amt);
if (fancy)
PR_fprintf(PR_STDOUT, "</font>" );
PR_fprintf(PR_STDOUT, "]\n" );
wrote = PR_Write(s_client, buffer, amt);
if (wrote != amt) {
if (wrote < 0 ) {
showErr("Write to client socket failed.\n" );
break ;
} else {
PR_fprintf(PR_STDERR, "Short write to client socket!\n" );
}
}
} /* end of read from server socket. */
/* Loop, handle next message. */
} /* handle messages during a connection loop */
PR_Close(s_client);
PR_Close(s_server);
flush_stream(&clientstream);
flush_stream(&serverstream);
/* Connection is closed, so reset the current cipher */
currentcipher = 0 ;
c_count++;
PR_fprintf(PR_STDERR, "Connection %d Complete [%s]\n" , c_count,
get_time_string());
} while (looparound); /* accept connection and process it. */
PR_Close(s_rend);
if (NSS_Shutdown() != SECSuccess) {
return 1 ;
}
return 0 ;
}
Messung V0.5 in Prozent C=100 H=97 G=98
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.47Angebot
¤
*Eine klare Vorstellung vom Zielzustand