summaryrefslogtreecommitdiffstats
path: root/security/nss/cmd/ssltap
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/cmd/ssltap')
-rw-r--r--security/nss/cmd/ssltap/Makefile51
-rw-r--r--security/nss/cmd/ssltap/manifest.mn21
-rw-r--r--security/nss/cmd/ssltap/ssltap-manual.html170
-rw-r--r--security/nss/cmd/ssltap/ssltap.c2584
-rw-r--r--security/nss/cmd/ssltap/ssltap.gyp25
5 files changed, 2851 insertions, 0 deletions
diff --git a/security/nss/cmd/ssltap/Makefile b/security/nss/cmd/ssltap/Makefile
new file mode 100644
index 0000000000..bc7d35df4e
--- /dev/null
+++ b/security/nss/cmd/ssltap/Makefile
@@ -0,0 +1,51 @@
+#! gmake
+#
+# 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/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+# Since ssltap doesn't use any of NSS, we'll skip NSS's link libs,
+# and just link with NSPR.
+#
+include ../platlibs.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+
+-include ../platrules.mk
+
diff --git a/security/nss/cmd/ssltap/manifest.mn b/security/nss/cmd/ssltap/manifest.mn
new file mode 100644
index 0000000000..92302a1b8b
--- /dev/null
+++ b/security/nss/cmd/ssltap/manifest.mn
@@ -0,0 +1,21 @@
+#
+# 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/.
+
+CORE_DEPTH = ../..
+
+# MODULE public and private header directories are implicitly REQUIRED.
+MODULE = nss
+
+CSRCS = ssltap.c \
+ $(NULL)
+
+PROGRAM = ssltap
+
+REQUIRES = seccmd dbm
+
+PACKAGE_FILES = ssltap-manual.html licence.doc ssltap.exe
+
+ARCHIVE_NAME = ssltap
+
diff --git a/security/nss/cmd/ssltap/ssltap-manual.html b/security/nss/cmd/ssltap/ssltap-manual.html
new file mode 100644
index 0000000000..619c93f8cd
--- /dev/null
+++ b/security/nss/cmd/ssltap/ssltap-manual.html
@@ -0,0 +1,170 @@
+<HTML>
+<!-- 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/. -->
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.05 [en] (WinNT; U) [Netscape]">
+ <META NAME="Author" CONTENT="Steve Parkinson">
+ <TITLE>SSLTap - manual</TITLE>
+</HEAD>
+<BODY>
+
+<H1>
+SSLTap Manual page</H1>
+
+<H3>
+Summary</H3>
+A command-line proxy which is SSL-aware. It snoops on TCP connections,
+and displays the data going by, including SSL records and handshaking&nbsp;
+if the connection is SSL.
+<H3>
+Synopsis</H3>
+<TT>ssltap [-vhfsxl] [-p port] hostname:port</TT>
+
+<P><TT>&nbsp;&nbsp; -v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [prints version string]</TT>
+<BR><TT>&nbsp;&nbsp; -h&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [outputs hex instead
+of ASCII]</TT>
+<BR><TT>&nbsp;&nbsp; -f&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [turn on Fancy HTML
+coloring]</TT>
+<BR><TT>&nbsp;&nbsp; -s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [turn on SSL decoding]</TT>
+<BR><TT>&nbsp;&nbsp; -x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [turn on extra SSL
+hex dumps]</TT>
+<BR><TT>&nbsp;&nbsp; -p port [specify rendezvous port (default 1924)]</TT>
+<BR><TT>&nbsp;&nbsp; -l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [loop - continue
+to wait for more connections]</TT>
+<H3>
+Description</H3>
+SSLTap opens a socket on a rendezvous port, and waits for an incoming connection
+(client side). Once this connection arrives, SSLTap makes another connection
+to hostname:port (server side). It passes any data sent by the client to
+the server, and vice versa. However, SSLTap will also display the data
+to the console. It can do this for plain HTTP connections, or any TCP protocol.
+However, SSLTap can also work with SSL streams, as detailed below.
+
+<P>Let's assume your development machine is called 'intercept'. The simplest
+usage of SSLTap is to run the command <TT>'ssltap www.netscape.com:80'</TT>
+on intercept. The program will wait for an incoming connection on port
+1924. Next you would want to go to your browser, and enter the URL http://intercept:1924.
+The page retrieved by the browser will actually be gotten from the server
+at www.netscape.com, but will go via SSLTap.
+
+<P>Data sent from the client to the server is surrounded by a '--> [ ]'
+symbol, and data sent from the server to the client, a '&lt;---[&nbsp;
+]' symbol.
+
+<P>You'll notice that the page retrieved with this example looks incomplete.
+This is because SSLTap by default closes down after the first connection
+is complete, so the browser is not able to load images. To make the SSLTap
+continue to accept connections, switch on looping mode with the -l option.
+
+<P>You can change the default rendezvous port to something else with the
+-p option.
+
+<P>The remaining options change the way the output is produced.
+
+<P>The -f option prints 'fancy' output - in colored HTML. Data sent from
+the client to the server is in blue. The server's reply is in red. This
+is designed so you can load the output up into a browser. When used with
+looping mode, the different connections are separated with horizontal lines.
+
+<P>-x will turn on HEX printing. Instead of being output as ascii, the
+data is shown as Hex, like this:
+<UL><TT>&lt;-- [</TT>
+<BR><TT>&nbsp;&nbsp; 0: 56 d5 16 3e&nbsp; a1 6b b1 4a&nbsp; 8f 67 c4 d7&nbsp;
+21 2f 6f dd&nbsp; | V..>.k.J.g..!/o.</TT>
+<BR><TT>&nbsp; 10: bb 22 c4 75&nbsp; 8c f4 ce 28&nbsp; 16 a6 20 aa&nbsp;
+fb 9a 59 a1&nbsp; | .".u...(.. ...Y.</TT>
+<BR><TT>&nbsp; 20: 51 91 14 d2&nbsp; fc 9f a7 ea&nbsp; 4d 9c f7 3a&nbsp;
+9d 83 62 4a&nbsp; | Q.......M..:..bJ</TT>
+<BR><TT>]</TT>
+<BR>&nbsp;</UL>
+
+<H4>
+SSL Parse mode</H4>
+The following options deal with SSL connections.
+<UL>-s will turn on SSL parsing. (SSLTap doesn't automatically detect SSL
+sessions.)
+<BR>-x will turn on extra SSL hexdumps. Mostly, if SSL can decode the data,
+it doesn't display the hex.</UL>
+The following SSL3 Data structures are parsed: Handshake, ClientHello,
+ServerHello, CertificateChain, Certificate. In addition, SSL2 ClientHello,
+ServerHello, ClientMasterKey are also partly parsed. NO DECRYPTION IS PERFORMED
+ON THE DATA. SSLTAP CANNOT DECRYPT the data.
+
+<P>If a certificate chain is detected, DER-encoded certificates will be
+saved into files in the current directory called 'cert.0x' where x is the
+sequence number of the certificate.
+<BR>&nbsp;
+<H3>
+Operation Hints</H3>
+Often, you'll find that the server certificate does not get transferred,
+or other parts of the handshake do not happen. This is because the browser
+is taking advantage of session-id-reuse (using the handshake results from
+a previous session). If you restart the browser, it'll clear the session
+id cache.
+
+<P>If you run the ssltap on a different machine that the ssl server you're
+trying to connect to, the browser will complain that the host name you're
+trying to connect to is different to the certificate, but it will still
+let you connect, after showing you a dialog.
+<H3>
+Bugs</H3>
+Please contact <A HREF="mailto:ssltap-support@netscape.com">ssltap-support@netscape.com</A>
+for bug reports.
+<H3>
+History</H3>
+2.1 - First public release (March 1998)
+<BR>&nbsp;
+<H3>
+Other</H3>
+For reference, here is a table of some well-known port numbers:
+<BR>&nbsp;
+<TABLE BORDER=2 >
+<TR>
+<TD>HTTP</TD>
+
+<TD>80</TD>
+</TR>
+
+<TR>
+<TD>SMTP</TD>
+
+<TD>25</TD>
+</TR>
+
+<TR>
+<TD>HTTPS</TD>
+
+<TD>443</TD>
+</TR>
+
+<TR>
+<TD>FTP</TD>
+
+<TD>21</TD>
+</TR>
+
+<TR>
+<TD>IMAPS</TD>
+
+<TD>993</TD>
+</TR>
+
+<TR>
+<TD>NNTP</TD>
+
+<TD>119</TD>
+</TR>
+
+<TR>
+<TD>NNTPS</TD>
+
+<TD>563</TD>
+</TR>
+</TABLE>
+&nbsp;
+
+<P>&nbsp;
+</BODY>
+</HTML>
diff --git a/security/nss/cmd/ssltap/ssltap.c b/security/nss/cmd/ssltap/ssltap.c
new file mode 100644
index 0000000000..0a9ec11eec
--- /dev/null
+++ b/security/nss/cmd/ssltap/ssltap.c
@@ -0,0 +1,2584 @@
+/* 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:
+ 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] & 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, "&lt;");
+ break;
+ case '>':
+ strcpy(t, "&gt;");
+ break;
+ case '&':
+ strcpy(t, "&amp;");
+ 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;
+}
diff --git a/security/nss/cmd/ssltap/ssltap.gyp b/security/nss/cmd/ssltap/ssltap.gyp
new file mode 100644
index 0000000000..9743ac262c
--- /dev/null
+++ b/security/nss/cmd/ssltap/ssltap.gyp
@@ -0,0 +1,25 @@
+# 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/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi',
+ '../../cmd/platlibs.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'ssltap',
+ 'type': 'executable',
+ 'sources': [
+ 'ssltap.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:dbm_exports',
+ '<(DEPTH)/exports.gyp:nss_exports'
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+} \ No newline at end of file