mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-10-26 13:27:27 +00:00
2586 lines
84 KiB
C
2586 lines
84 KiB
C
/* 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)((PRUint8 *)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 0x010080:
|
|
cs_str = "SSL2/RSA/RC4-128/MD5";
|
|
break;
|
|
case 0x020080:
|
|
cs_str = "SSL2/RSA/RC4-40/MD5";
|
|
break;
|
|
case 0x030080:
|
|
cs_str = "SSL2/RSA/RC2CBC128/MD5";
|
|
break;
|
|
case 0x040080:
|
|
cs_str = "SSL2/RSA/RC2CBC40/MD5";
|
|
break;
|
|
case 0x050080:
|
|
cs_str = "SSL2/RSA/IDEA128CBC/MD5";
|
|
break;
|
|
case 0x060040:
|
|
cs_str = "SSL2/RSA/DES56-CBC/MD5";
|
|
break;
|
|
case 0x0700C0:
|
|
cs_str = "SSL2/RSA/3DES192EDE-CBC/MD5";
|
|
break;
|
|
|
|
case 0x000001:
|
|
cs_str = "SSL3/RSA/NULL/MD5";
|
|
break;
|
|
case 0x000002:
|
|
cs_str = "SSL3/RSA/NULL/SHA";
|
|
break;
|
|
case 0x000003:
|
|
cs_str = "SSL3/RSA/RC4-40/MD5";
|
|
break;
|
|
case 0x000004:
|
|
cs_str = "SSL3/RSA/RC4-128/MD5";
|
|
break;
|
|
case 0x000005:
|
|
cs_str = "SSL3/RSA/RC4-128/SHA";
|
|
break;
|
|
case 0x000006:
|
|
cs_str = "SSL3/RSA/RC2CBC40/MD5";
|
|
break;
|
|
case 0x000007:
|
|
cs_str = "SSL3/RSA/IDEA128CBC/SHA";
|
|
break;
|
|
case 0x000008:
|
|
cs_str = "SSL3/RSA/DES40-CBC/SHA";
|
|
break;
|
|
case 0x000009:
|
|
cs_str = "SSL3/RSA/DES56-CBC/SHA";
|
|
break;
|
|
case 0x00000A:
|
|
cs_str = "SSL3/RSA/3DES192EDE-CBC/SHA";
|
|
break;
|
|
|
|
case 0x00000B:
|
|
cs_str = "SSL3/DH-DSS/DES40-CBC/SHA";
|
|
break;
|
|
case 0x00000C:
|
|
cs_str = "SSL3/DH-DSS/DES56-CBC/SHA";
|
|
break;
|
|
case 0x00000D:
|
|
cs_str = "SSL3/DH-DSS/DES192EDE3CBC/SHA";
|
|
break;
|
|
case 0x00000E:
|
|
cs_str = "SSL3/DH-RSA/DES40-CBC/SHA";
|
|
break;
|
|
case 0x00000F:
|
|
cs_str = "SSL3/DH-RSA/DES56-CBC/SHA";
|
|
break;
|
|
case 0x000010:
|
|
cs_str = "SSL3/DH-RSA/3DES192EDE-CBC/SHA";
|
|
break;
|
|
|
|
case 0x000011:
|
|
cs_str = "SSL3/DHE-DSS/DES40-CBC/SHA";
|
|
break;
|
|
case 0x000012:
|
|
cs_str = "SSL3/DHE-DSS/DES56-CBC/SHA";
|
|
break;
|
|
case 0x000013:
|
|
cs_str = "SSL3/DHE-DSS/DES192EDE3CBC/SHA";
|
|
break;
|
|
case 0x000014:
|
|
cs_str = "SSL3/DHE-RSA/DES40-CBC/SHA";
|
|
break;
|
|
case 0x000015:
|
|
cs_str = "SSL3/DHE-RSA/DES56-CBC/SHA";
|
|
break;
|
|
case 0x000016:
|
|
cs_str = "SSL3/DHE-RSA/3DES192EDE-CBC/SHA";
|
|
break;
|
|
|
|
case 0x000017:
|
|
cs_str = "SSL3/DH-anon/RC4-40/MD5";
|
|
break;
|
|
case 0x000018:
|
|
cs_str = "SSL3/DH-anon/RC4-128/MD5";
|
|
break;
|
|
case 0x000019:
|
|
cs_str = "SSL3/DH-anon/DES40-CBC/SHA";
|
|
break;
|
|
case 0x00001A:
|
|
cs_str = "SSL3/DH-anon/DES56-CBC/SHA";
|
|
break;
|
|
case 0x00001B:
|
|
cs_str = "SSL3/DH-anon/3DES192EDE-CBC/SHA";
|
|
break;
|
|
|
|
case 0x00001C:
|
|
cs_str = "SSL3/FORTEZZA-DMS/NULL/SHA";
|
|
break;
|
|
case 0x00001D:
|
|
cs_str = "SSL3/FORTEZZA-DMS/FORTEZZA-CBC/SHA";
|
|
break;
|
|
case 0x00001E:
|
|
cs_str = "SSL3/FORTEZZA-DMS/RC4-128/SHA";
|
|
break;
|
|
|
|
case 0x00002F:
|
|
cs_str = "TLS/RSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000030:
|
|
cs_str = "TLS/DH-DSS/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000031:
|
|
cs_str = "TLS/DH-RSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000032:
|
|
cs_str = "TLS/DHE-DSS/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000033:
|
|
cs_str = "TLS/DHE-RSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000034:
|
|
cs_str = "TLS/DH-ANON/AES128-CBC/SHA";
|
|
break;
|
|
|
|
case 0x000035:
|
|
cs_str = "TLS/RSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000036:
|
|
cs_str = "TLS/DH-DSS/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000037:
|
|
cs_str = "TLS/DH-RSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000038:
|
|
cs_str = "TLS/DHE-DSS/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000039:
|
|
cs_str = "TLS/DHE-RSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00003A:
|
|
cs_str = "TLS/DH-ANON/AES256-CBC/SHA";
|
|
break;
|
|
|
|
case 0x00003B:
|
|
cs_str = "TLS/RSA/NULL/SHA256";
|
|
break;
|
|
case 0x00003C:
|
|
cs_str = "TLS/RSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00003D:
|
|
cs_str = "TLS/RSA/AES256-CBC/SHA256";
|
|
break;
|
|
case 0x00003E:
|
|
cs_str = "TLS/DH-DSS/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00003F:
|
|
cs_str = "TLS/DH-RSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x000040:
|
|
cs_str = "TLS/DHE-DSS/AES128-CBC/SHA256";
|
|
break;
|
|
|
|
case 0x000041:
|
|
cs_str = "TLS/RSA/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
case 0x000042:
|
|
cs_str = "TLS/DH-DSS/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
case 0x000043:
|
|
cs_str = "TLS/DH-RSA/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
case 0x000044:
|
|
cs_str = "TLS/DHE-DSS/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
case 0x000045:
|
|
cs_str = "TLS/DHE-RSA/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
case 0x000046:
|
|
cs_str = "TLS/DH-ANON/CAMELLIA128-CBC/SHA";
|
|
break;
|
|
|
|
case 0x000060:
|
|
cs_str = "TLS/RSA-EXPORT1024/RC4-56/MD5";
|
|
break;
|
|
case 0x000061:
|
|
cs_str = "TLS/RSA-EXPORT1024/RC2CBC56/MD5";
|
|
break;
|
|
case 0x000062:
|
|
cs_str = "TLS/RSA-EXPORT1024/DES56-CBC/SHA";
|
|
break;
|
|
case 0x000064:
|
|
cs_str = "TLS/RSA-EXPORT1024/RC4-56/SHA";
|
|
break;
|
|
case 0x000063:
|
|
cs_str = "TLS/DHE-DSS_EXPORT1024/DES56-CBC/SHA";
|
|
break;
|
|
case 0x000065:
|
|
cs_str = "TLS/DHE-DSS_EXPORT1024/RC4-56/SHA";
|
|
break;
|
|
case 0x000066:
|
|
cs_str = "TLS/DHE-DSS/RC4-128/SHA";
|
|
break;
|
|
|
|
case 0x000067:
|
|
cs_str = "TLS/DHE-RSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x000068:
|
|
cs_str = "TLS/DH-DSS/AES256-CBC/SHA256";
|
|
break;
|
|
case 0x000069:
|
|
cs_str = "TLS/DH-RSA/AES256-CBC/SHA256";
|
|
break;
|
|
case 0x00006A:
|
|
cs_str = "TLS/DHE-DSS/AES256-CBC/SHA256";
|
|
break;
|
|
case 0x00006B:
|
|
cs_str = "TLS/DHE-RSA/AES256-CBC/SHA256";
|
|
break;
|
|
|
|
case 0x000072:
|
|
cs_str = "TLS/DHE-DSS/3DESEDE-CBC/RMD160";
|
|
break;
|
|
case 0x000073:
|
|
cs_str = "TLS/DHE-DSS/AES128-CBC/RMD160";
|
|
break;
|
|
case 0x000074:
|
|
cs_str = "TLS/DHE-DSS/AES256-CBC/RMD160";
|
|
break;
|
|
|
|
case 0x000079:
|
|
cs_str = "TLS/DHE-RSA/AES256-CBC/RMD160";
|
|
break;
|
|
|
|
case 0x00007C:
|
|
cs_str = "TLS/RSA/3DESEDE-CBC/RMD160";
|
|
break;
|
|
case 0x00007D:
|
|
cs_str = "TLS/RSA/AES128-CBC/RMD160";
|
|
break;
|
|
case 0x00007E:
|
|
cs_str = "TLS/RSA/AES256-CBC/RMD160";
|
|
break;
|
|
|
|
case 0x000080:
|
|
cs_str = "TLS/GOST341094/GOST28147-OFB/GOST28147";
|
|
break;
|
|
case 0x000081:
|
|
cs_str = "TLS/GOST34102001/GOST28147-OFB/GOST28147";
|
|
break;
|
|
case 0x000082:
|
|
cs_str = "TLS/GOST341094/NULL/GOSTR3411";
|
|
break;
|
|
case 0x000083:
|
|
cs_str = "TLS/GOST34102001/NULL/GOSTR3411";
|
|
break;
|
|
|
|
case 0x000084:
|
|
cs_str = "TLS/RSA/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x000085:
|
|
cs_str = "TLS/DH-DSS/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x000086:
|
|
cs_str = "TLS/DH-RSA/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x000087:
|
|
cs_str = "TLS/DHE-DSS/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x000088:
|
|
cs_str = "TLS/DHE-RSA/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x000089:
|
|
cs_str = "TLS/DH-ANON/CAMELLIA256-CBC/SHA";
|
|
break;
|
|
case 0x00008A:
|
|
cs_str = "TLS/PSK/RC4-128/SHA";
|
|
break;
|
|
case 0x00008B:
|
|
cs_str = "TLS/PSK/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00008C:
|
|
cs_str = "TLS/PSK/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00008D:
|
|
cs_str = "TLS/PSK/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00008E:
|
|
cs_str = "TLS/DHE-PSK/RC4-128/SHA";
|
|
break;
|
|
case 0x00008F:
|
|
cs_str = "TLS/DHE-PSK/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x000090:
|
|
cs_str = "TLS/DHE-PSK/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000091:
|
|
cs_str = "TLS/DHE-PSK/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000092:
|
|
cs_str = "TLS/RSA-PSK/RC4-128/SHA";
|
|
break;
|
|
case 0x000093:
|
|
cs_str = "TLS/RSA-PSK/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x000094:
|
|
cs_str = "TLS/RSA-PSK/AES128-CBC/SHA";
|
|
break;
|
|
case 0x000095:
|
|
cs_str = "TLS/RSA-PSK/AES256-CBC/SHA";
|
|
break;
|
|
case 0x000096:
|
|
cs_str = "TLS/RSA/SEED-CBC/SHA";
|
|
break;
|
|
case 0x000097:
|
|
cs_str = "TLS/DH-DSS/SEED-CBC/SHA";
|
|
break;
|
|
case 0x000098:
|
|
cs_str = "TLS/DH-RSA/SEED-CBC/SHA";
|
|
break;
|
|
case 0x000099:
|
|
cs_str = "TLS/DHE-DSS/SEED-CBC/SHA";
|
|
break;
|
|
case 0x00009A:
|
|
cs_str = "TLS/DHE-RSA/SEED-CBC/SHA";
|
|
break;
|
|
case 0x00009B:
|
|
cs_str = "TLS/DH-ANON/SEED-CBC/SHA";
|
|
break;
|
|
case 0x00009C:
|
|
cs_str = "TLS/RSA/AES128-GCM/SHA256";
|
|
break;
|
|
case 0x00009E:
|
|
cs_str = "TLS/DHE-RSA/AES128-GCM/SHA256";
|
|
break;
|
|
|
|
case 0x0000FF:
|
|
cs_str = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
|
|
break;
|
|
case 0x005600:
|
|
cs_str = "TLS_FALLBACK_SCSV";
|
|
break;
|
|
|
|
case 0x00C001:
|
|
cs_str = "TLS/ECDH-ECDSA/NULL/SHA";
|
|
break;
|
|
case 0x00C002:
|
|
cs_str = "TLS/ECDH-ECDSA/RC4-128/SHA";
|
|
break;
|
|
case 0x00C003:
|
|
cs_str = "TLS/ECDH-ECDSA/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00C004:
|
|
cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00C005:
|
|
cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00C006:
|
|
cs_str = "TLS/ECDHE-ECDSA/NULL/SHA";
|
|
break;
|
|
case 0x00C007:
|
|
cs_str = "TLS/ECDHE-ECDSA/RC4-128/SHA";
|
|
break;
|
|
case 0x00C008:
|
|
cs_str = "TLS/ECDHE-ECDSA/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00C009:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00C00A:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00C00B:
|
|
cs_str = "TLS/ECDH-RSA/NULL/SHA";
|
|
break;
|
|
case 0x00C00C:
|
|
cs_str = "TLS/ECDH-RSA/RC4-128/SHA";
|
|
break;
|
|
case 0x00C00D:
|
|
cs_str = "TLS/ECDH-RSA/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00C00E:
|
|
cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00C00F:
|
|
cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00C010:
|
|
cs_str = "TLS/ECDHE-RSA/NULL/SHA";
|
|
break;
|
|
case 0x00C011:
|
|
cs_str = "TLS/ECDHE-RSA/RC4-128/SHA";
|
|
break;
|
|
case 0x00C012:
|
|
cs_str = "TLS/ECDHE-RSA/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00C013:
|
|
cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00C014:
|
|
cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA";
|
|
break;
|
|
case 0x00C015:
|
|
cs_str = "TLS/ECDH-anon/NULL/SHA";
|
|
break;
|
|
case 0x00C016:
|
|
cs_str = "TLS/ECDH-anon/RC4-128/SHA";
|
|
break;
|
|
case 0x00C017:
|
|
cs_str = "TLS/ECDH-anon/3DES-EDE-CBC/SHA";
|
|
break;
|
|
case 0x00C018:
|
|
cs_str = "TLS/ECDH-anon/AES128-CBC/SHA";
|
|
break;
|
|
case 0x00C019:
|
|
cs_str = "TLS/ECDH-anon/AES256-CBC/SHA";
|
|
break;
|
|
|
|
case 0x00C023:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00C024:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES256-CBC/SHA384";
|
|
break;
|
|
case 0x00C025:
|
|
cs_str = "TLS/ECDH-ECDSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00C026:
|
|
cs_str = "TLS/ECDH-ECDSA/AES256-CBC/SHA384";
|
|
break;
|
|
case 0x00C027:
|
|
cs_str = "TLS/ECDHE-RSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00C028:
|
|
cs_str = "TLS/ECDHE-RSA/AES256-CBC/SHA384";
|
|
break;
|
|
case 0x00C029:
|
|
cs_str = "TLS/ECDH-RSA/AES128-CBC/SHA256";
|
|
break;
|
|
case 0x00C02A:
|
|
cs_str = "TLS/ECDH-RSA/AES256-CBC/SHA384";
|
|
break;
|
|
case 0x00C02B:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES128-GCM/SHA256";
|
|
break;
|
|
case 0x00C02C:
|
|
cs_str = "TLS/ECDHE-ECDSA/AES256-GCM/SHA384";
|
|
break;
|
|
case 0x00C02F:
|
|
cs_str = "TLS/ECDHE-RSA/AES128-GCM/SHA256";
|
|
break;
|
|
|
|
case 0x00CCA8:
|
|
cs_str = "TLS/ECDHE-RSA/CHACHA20-POLY1305/SHA256";
|
|
break;
|
|
case 0x00CCA9:
|
|
cs_str = "TLS/ECDHE-ECDSA/CHACHA20-POLY1305/SHA256";
|
|
break;
|
|
case 0x00CCAA:
|
|
cs_str = "TLS/DHE-RSA/CHACHA20-POLY1305/SHA256";
|
|
break;
|
|
|
|
case 0x00FEFF:
|
|
cs_str = "SSL3/RSA-FIPS/3DESEDE-CBC/SHA";
|
|
break;
|
|
case 0x00FEFE:
|
|
cs_str = "SSL3/RSA-FIPS/DES-CBC/SHA";
|
|
break;
|
|
case 0x00FFE1:
|
|
cs_str = "SSL3/RSA-FIPS/DES56-CBC/SHA";
|
|
break;
|
|
case 0x00FFE0:
|
|
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 0xff01:
|
|
ex_name = "renegotiation_info";
|
|
break;
|
|
default:
|
|
sprintf(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] & 0x80) {
|
|
/* 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] & 0x7f) << 8) + lenbuf[1] +
|
|
((lenbuf[0] & 0x80) ? 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 >= 0x20 && j < 0x80) ? 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);
|
|
NSS_Shutdown();
|
|
return 0;
|
|
}
|