Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Unified Diff: net/third_party/nss/patches/dtls.patch

Issue 9764001: Add DTLS support to NSS, contributed by Eric Rescorla. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Update AUTHORS Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/third_party/nss/patches/applypatches.sh ('k') | net/third_party/nss/ssl.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/third_party/nss/patches/dtls.patch
===================================================================
--- net/third_party/nss/patches/dtls.patch (revision 0)
+++ net/third_party/nss/patches/dtls.patch (revision 0)
@@ -0,0 +1,3321 @@
+Index: net/third_party/nss/ssl/SSLerrs.h
+===================================================================
+--- net/third_party/nss/ssl/SSLerrs.h (revision 127709)
++++ net/third_party/nss/ssl/SSLerrs.h (working copy)
+@@ -423,3 +423,9 @@
+
+ ER3(SSL_ERROR_RX_UNEXPECTED_CERT_STATUS, (SSL_ERROR_BASE + 121),
+ "SSL received an unexpected Certificate Status handshake message.")
++
++ER3(SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST, (SSL_ERROR_BASE + 122),
++"SSL received a malformed Hello Verify Request handshake message.")
++
++ER3(SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST, (SSL_ERROR_BASE + 123),
++"SSL received an unexpected Hello Verify Request handshake message.")
+Index: net/third_party/nss/ssl/ssl.h
+===================================================================
+--- net/third_party/nss/ssl/ssl.h (revision 127709)
++++ net/third_party/nss/ssl/ssl.h (working copy)
+@@ -80,6 +80,12 @@
+ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd);
+
+ /*
++** Imports fd into DTLS, returning a new socket. Copies DTLS configuration
++** from model.
++*/
++SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
++
++/*
+ ** Enable/disable an ssl mode
+ **
+ ** SSL_SECURITY:
+@@ -942,6 +948,14 @@
+ PRBool *last_handshake_resumed);
+
+ /*
++** How long should we wait before retransmitting the next flight of
++** the DTLS handshake? Returns SECFailure if not DTLS or not in a
++** handshake.
++*/
++SSL_IMPORT SECStatus DTLS_GetTimeout(PRFileDesc *socket,
++ PRIntervalTime *timeout);
++
++/*
+ * Return a boolean that indicates whether the underlying library
+ * will perform as the caller expects.
+ *
+Index: net/third_party/nss/ssl/ssl3gthr.c
+===================================================================
+--- net/third_party/nss/ssl/ssl3gthr.c (revision 127709)
++++ net/third_party/nss/ssl/ssl3gthr.c (working copy)
+@@ -50,7 +50,7 @@
+ *
+ * returns 1 if received a complete SSL3 record.
+ * returns 0 if recv returns EOF
+- * returns -1 if recv returns <0
++ * returns -1 if recv returns < 0
+ * (The error value may have already been set to PR_WOULD_BLOCK_ERROR)
+ *
+ * Caller must hold the recv buf lock.
+@@ -59,7 +59,8 @@
+ * GS_HEADER: waiting for the 5-byte SSL3 record header to come in.
+ * GS_DATA: waiting for the body of the SSL3 record to come in.
+ *
+- * This loop returns when either (a) an error or EOF occurs,
++ * This loop returns when either
++ * (a) an error or EOF occurs,
+ * (b) PR_WOULD_BLOCK_ERROR,
+ * (c) data (entire SSL3 record) has been received.
+ */
+@@ -167,6 +168,125 @@
+ return rv;
+ }
+
++/*
++ * Read in an entire DTLS record.
++ *
++ * Blocks here for blocking sockets, otherwise returns -1 with
++ * PR_WOULD_BLOCK_ERROR when socket would block.
++ *
++ * This is simpler than SSL because we are reading on a datagram socket
++ * and datagrams must contain >=1 complete records.
++ *
++ * returns 1 if received a complete DTLS record.
++ * returns 0 if recv returns EOF
++ * returns -1 if recv returns < 0
++ * (The error value may have already been set to PR_WOULD_BLOCK_ERROR)
++ *
++ * Caller must hold the recv buf lock.
++ *
++ * This loop returns when either
++ * (a) an error or EOF occurs,
++ * (b) PR_WOULD_BLOCK_ERROR,
++ * (c) data (entire DTLS record) has been received.
++ */
++static int
++dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
++{
++ int nb;
++ int err;
++ int rv = 1;
++
++ SSL_TRC(30, ("dtls_GatherData"));
++
++ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
++
++ gs->state = GS_HEADER;
++ gs->offset = 0;
++
++ if (gs->dtlsPacketOffset == gs->dtlsPacket.len) { /* No data left */
++ gs->dtlsPacketOffset = 0;
++ gs->dtlsPacket.len = 0;
++
++ /* Resize to the maximum possible size so we can fit a full datagram */
++ /* This is the max fragment length for an encrypted fragment
++ ** plus the size of the record header.
++ ** This magic constant is copied from ssl3_GatherData, with 5 changed
++ ** to 13 (the size of the record header).
++ */
++ if (gs->dtlsPacket.space < MAX_FRAGMENT_LENGTH + 2048 + 13) {
++ err = sslBuffer_Grow(&gs->dtlsPacket,
++ MAX_FRAGMENT_LENGTH + 2048 + 13);
++ if (err) { /* realloc has set error code to no mem. */
++ return err;
++ }
++ }
++
++ /* recv() needs to read a full datagram at a time */
++ nb = ssl_DefRecv(ss, gs->dtlsPacket.buf, gs->dtlsPacket.space, flags);
++
++ if (nb > 0) {
++ PRINT_BUF(60, (ss, "raw gather data:", gs->dtlsPacket.buf, nb));
++ } else if (nb == 0) {
++ /* EOF */
++ SSL_TRC(30, ("%d: SSL3[%d]: EOF", SSL_GETPID(), ss->fd));
++ rv = 0;
++ return rv;
++ } else /* if (nb < 0) */ {
++ SSL_DBG(("%d: SSL3[%d]: recv error %d", SSL_GETPID(), ss->fd,
++ PR_GetError()));
++ rv = SECFailure;
++ return rv;
++ }
++
++ gs->dtlsPacket.len = nb;
++ }
++
++ /* At this point we should have >=1 complete records lined up in
++ * dtlsPacket. Read off the header.
++ */
++ if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < 13) {
++ SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet "
++ "too short to contain header", SSL_GETPID(), ss->fd));
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
++ gs->dtlsPacketOffset = 0;
++ gs->dtlsPacket.len = 0;
++ rv = SECFailure;
++ return rv;
++ }
++ memcpy(gs->hdr, gs->dtlsPacket.buf + gs->dtlsPacketOffset, 13);
++ gs->dtlsPacketOffset += 13;
++
++ /* Have received SSL3 record header in gs->hdr. */
++ gs->remainder = (gs->hdr[11] << 8) | gs->hdr[12];
++
++ if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < gs->remainder) {
++ SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet too short "
++ "to contain rest of body", SSL_GETPID(), ss->fd));
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
++ gs->dtlsPacketOffset = 0;
++ gs->dtlsPacket.len = 0;
++ rv = SECFailure;
++ return rv;
++ }
++
++ /* OK, we have at least one complete packet, copy into inbuf */
++ if (gs->remainder > gs->inbuf.space) {
++ err = sslBuffer_Grow(&gs->inbuf, gs->remainder);
++ if (err) { /* realloc has set error code to no mem. */
++ return err;
++ }
++ }
++
++ memcpy(gs->inbuf.buf, gs->dtlsPacket.buf + gs->dtlsPacketOffset,
++ gs->remainder);
++ gs->inbuf.len = gs->remainder;
++ gs->offset = gs->remainder;
++ gs->dtlsPacketOffset += gs->remainder;
++ gs->state = GS_INIT;
++
++ return 1;
++}
++
+ /* Gather in a record and when complete, Handle that record.
+ * Repeat this until the handshake is complete,
+ * or until application data is available.
+@@ -190,6 +310,8 @@
+ int rv;
+ PRBool canFalseStart = PR_FALSE;
+
++ SSL_TRC(30, ("ssl3_GatherCompleteHandshake"));
++
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+ do {
+ /* Without this, we may end up wrongly reporting
+@@ -224,7 +346,24 @@
+ rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+ } else {
+ /* bring in the next sslv3 record. */
+- rv = ssl3_GatherData(ss, &ss->gs, flags);
++ if (!IS_DTLS(ss)) {
++ rv = ssl3_GatherData(ss, &ss->gs, flags);
++ } else {
++ rv = dtls_GatherData(ss, &ss->gs, flags);
++
++ /* If we got a would block error, that means that no data was
++ * available, so we check the timer to see if it's time to
++ * retransmit */
++ if (rv == SECFailure &&
++ (PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
++ ssl_GetSSL3HandshakeLock(ss);
++ dtls_CheckTimer(ss);
++ ssl_ReleaseSSL3HandshakeLock(ss);
++ /* Restore the error in case something succeeded */
++ PORT_SetError(PR_WOULD_BLOCK_ERROR);
++ }
++ }
++
+ if (rv <= 0) {
+ return rv;
+ }
+@@ -236,6 +375,20 @@
+ */
+ cText.type = (SSL3ContentType)ss->gs.hdr[0];
+ cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
++
++ if (IS_DTLS(ss)) {
++ int i;
++
++ cText.version = dtls_DTLSVersionToTLSVersion(cText.version);
++ /* DTLS sequence number */
++ cText.seq_num.high = 0; cText.seq_num.low = 0;
++ for (i = 0; i < 4; i++) {
++ cText.seq_num.high <<= 8; cText.seq_num.low <<= 8;
++ cText.seq_num.high |= ss->gs.hdr[3 + i];
++ cText.seq_num.low |= ss->gs.hdr[7 + i];
++ }
++ }
++
+ cText.buf = &ss->gs.inbuf;
+ rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+ }
+Index: net/third_party/nss/ssl/derive.c
+===================================================================
+--- net/third_party/nss/ssl/derive.c (revision 127709)
++++ net/third_party/nss/ssl/derive.c (working copy)
+@@ -583,6 +583,8 @@
+ * arguments were all valid but the slot cannot be bypassed.
+ */
+
++/* XXX Add SSL_CBP_TLS1_1 and test it in protocolmask when setting isTLS. */
++
+ SECStatus
+ SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey,
+ PRUint32 protocolmask, PRUint16 *ciphersuites, int nsuites,
+Index: net/third_party/nss/ssl/sslerr.h
+===================================================================
+--- net/third_party/nss/ssl/sslerr.h (revision 127709)
++++ net/third_party/nss/ssl/sslerr.h (working copy)
+@@ -215,6 +215,9 @@
+
+ SSL_ERROR_RX_UNEXPECTED_CERT_STATUS = (SSL_ERROR_BASE + 121),
+
++SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST = (SSL_ERROR_BASE + 122),
++SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST = (SSL_ERROR_BASE + 123),
++
+ SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
+ } SSLErrorCodes;
+ #endif /* NO_SECURITY_ERROR_ENUM */
+Index: net/third_party/nss/ssl/ssldef.c
+===================================================================
+--- net/third_party/nss/ssl/ssldef.c (revision 127709)
++++ net/third_party/nss/ssl/ssldef.c (working copy)
+@@ -138,6 +138,11 @@
+ return rv;
+ }
+ sent += rv;
++
++ if (IS_DTLS(ss) && (len > sent)) {
++ /* We got a partial write so just return it */
++ return sent;
++ }
+ } while (len > sent);
+ ss->lastWriteBlocked = 0;
+ return sent;
+Index: net/third_party/nss/ssl/sslimpl.h
+===================================================================
+--- net/third_party/nss/ssl/sslimpl.h (revision 127709)
++++ net/third_party/nss/ssl/sslimpl.h (working copy)
+@@ -62,6 +62,7 @@
+ #endif
+ #include "nssrwlk.h"
+ #include "prthread.h"
++#include "prclist.h"
+
+ #include "sslt.h" /* for some formerly private types, now public */
+
+@@ -195,6 +196,10 @@
+
+ #define EXPORT_RSA_KEY_LENGTH 64 /* bytes */
+
++#define INITIAL_DTLS_TIMEOUT_MS 1000 /* Default value from RFC 4347 = 1s*/
++#define MAX_DTLS_TIMEOUT_MS 60000 /* 1 minute */
++#define DTLS_FINISHED_TIMER_MS 120000 /* Time to wait in FINISHED state */
++
+ typedef struct sslBufferStr sslBuffer;
+ typedef struct sslConnectInfoStr sslConnectInfo;
+ typedef struct sslGatherStr sslGather;
+@@ -287,6 +292,8 @@
+ /* Flags interpreted by ssl send functions. */
+ #define ssl_SEND_FLAG_FORCE_INTO_BUFFER 0x40000000
+ #define ssl_SEND_FLAG_NO_BUFFER 0x20000000
++#define ssl_SEND_FLAG_USE_EPOCH 0x10000000 /* DTLS only */
++#define ssl_SEND_FLAG_NO_RETRANSMIT 0x08000000 /* DTLS only */
+ #define ssl_SEND_FLAG_MASK 0x7f000000
+
+ /*
+@@ -448,8 +455,15 @@
+ ** The portion of the SSL record header put here always comes off the wire
+ ** as plaintext, never ciphertext.
+ ** For SSL2, the plaintext portion is two bytes long. For SSl3 it is 5.
++ ** For DTLS it is 13.
+ */
+- unsigned char hdr[5]; /* ssl 2 & 3 */
++ unsigned char hdr[13]; /* ssl 2 & 3 or dtls */
++
++ /* Buffer for DTLS data read off the wire as a single datagram */
++ sslBuffer dtlsPacket;
++
++ /* the start of the buffered DTLS record in dtlsPacket */
++ unsigned int dtlsPacketOffset;
+ };
+
+ /* sslGather.state */
+@@ -521,6 +535,10 @@
+ PRUint32 low;
+ } SSL3SequenceNumber;
+
++typedef PRUint16 DTLSEpoch;
++
++typedef void (*DTLSTimerCb)(sslSocket *);
++
+ #define MAX_MAC_CONTEXT_BYTES 400
+ #define MAX_MAC_CONTEXT_LLONGS (MAX_MAC_CONTEXT_BYTES / 8)
+
+@@ -547,6 +565,20 @@
+ PRUint64 cipher_context[MAX_CIPHER_CONTEXT_LLONGS];
+ } ssl3KeyMaterial;
+
++/* The DTLS anti-replay window. Defined here because we need it in
++ * the cipher spec. Note that this is a ring buffer but left and
++ * right represent the true window, with modular arithmetic used to
++ * map them onto the buffer.
++ */
++#define DTLS_RECVD_RECORDS_WINDOW 1024 /* Packets; approximate
++ * Must be divisible by 8
++ */
++typedef struct DTLSRecvdRecordsStr {
++ unsigned char data[DTLS_RECVD_RECORDS_WINDOW/8];
++ PRUint64 left;
++ PRUint64 right;
++} DTLSRecvdRecords;
++
+ /*
+ ** These are the "specs" in the "ssl3" struct.
+ ** Access to the pointers to these specs, and all the specs' contents
+@@ -582,6 +614,8 @@
+ SECItem srvVirtName; /* for server: name that was negotiated
+ * with a client. For client - is
+ * always set to NULL.*/
++ DTLSEpoch epoch;
++ DTLSRecvdRecords recvdRecords;
+ } ssl3CipherSpec;
+
+ typedef enum { never_cached,
+@@ -777,6 +811,17 @@
+ typedef SECStatus (*sslRestartTarget)(sslSocket *);
+
+ /*
++** A DTLS queued message (potentially to be retransmitted)
++*/
++typedef struct DTLSQueuedMessageStr {
++ PRCList link; /* The linked list link */
++ DTLSEpoch epoch; /* The epoch to use */
++ SSL3ContentType type; /* The message type */
++ unsigned char *data; /* The data */
++ PRUint16 len; /* The data length */
++} DTLSQueuedMessage;
++
++/*
+ ** This is the "hs" member of the "ssl3" struct.
+ ** This entire struct is protected by ssl3HandshakeLock
+ */
+@@ -831,6 +876,30 @@
+ sslRestartTarget restartTarget;
+ /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
+ PRBool cacheSID;
++
++ /* This group of values is used for DTLS */
++ PRUint16 sendMessageSeq; /* The sending message sequence
++ * number */
++ PRCList * lastMessageFlight; /* The last message flight we sent.
++ * This is a pointer because
++ * ssl_FreeSocket relocates the
++ * structure in DEBUG mode, which
++ * messes up the list macros */
++ PRUint16 maxMessageSent; /* The largest message we sent */
++ PRUint16 recvMessageSeq; /* The receiving message sequence
++ * number */
++ sslBuffer recvdFragments; /* The fragments we have received in
++ * a bitmask */
++ PRInt32 recvdHighWater; /* The high water mark for fragments
++ * received. -1 means no reassembly
++ * in progress. */
++ unsigned char cookie[32]; /* The cookie */
++ unsigned char cookieLen; /* The length of the cookie */
++ PRIntervalTime rtTimerStarted; /* When the timer was started */
++ DTLSTimerCb rtTimerCb; /* The function to call on expiry */
++ PRUint32 rtTimeoutMs; /* The length of the current timeout
++ * used for backoff (in ms) */
++ PRUint32 rtRetries; /* The retry counter */
+ } SSL3HandshakeState;
+
+
+@@ -882,11 +951,18 @@
+ */
+ SECItem nextProto;
+ SSLNextProtoState nextProtoState;
++
++ PRUint16 mtu; /* Our estimate of the MTU */
+ };
+
++#define DTLS_MAX_MTU 1500 /* Ethernet MTU but without subtracting the
++ * headers, so slightly larger than expected */
++#define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram)
++
+ typedef struct {
+ SSL3ContentType type;
+ SSL3ProtocolVersion version;
++ SSL3SequenceNumber seq_num; /* DTLS only */
+ sslBuffer * buf;
+ } SSL3Ciphertext;
+
+@@ -1188,6 +1264,9 @@
+ /* True when the current session is a stateless resume. */
+ PRBool statelessResume;
+ TLSExtensionData xtnData;
++
++ /* Whether we are doing stream or datagram mode */
++ SSLProtocolVariant protocolVariant;
+ };
+
+
+@@ -1321,7 +1400,35 @@
+ extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
+
+ extern PRBool ssl3_CanFalseStart(sslSocket *ss);
++extern SECStatus
++ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
++ PRBool isServer,
++ PRBool isDTLS,
++ SSL3ContentType type,
++ const SSL3Opaque * pIn,
++ PRUint32 contentLen,
++ sslBuffer * wrBuf);
++extern PRInt32 ssl3_SendRecord(sslSocket *ss, DTLSEpoch epoch,
++ SSL3ContentType type,
++ const SSL3Opaque* pIn, PRInt32 nIn,
++ PRInt32 flags);
+
++#ifdef NSS_ENABLE_ZLIB
++/*
++ * The DEFLATE algorithm can result in an expansion of 0.1% + 12 bytes. For a
++ * maximum TLS record payload of 2**14 bytes, that's 29 bytes.
++ */
++#define SSL3_COMPRESSION_MAX_EXPANSION 29
++#else /* !NSS_ENABLE_ZLIB */
++#define SSL3_COMPRESSION_MAX_EXPANSION 0
++#endif
++
++/*
++ * make sure there is room in the write buffer for padding and
++ * other compression and cryptographic expansions.
++ */
++#define SSL3_BUFFER_FUDGE 100 + SSL3_COMPRESSION_MAX_EXPANSION
++
+ #define SSL_LOCK_READER(ss) if (ss->recvLock) PZ_Lock(ss->recvLock)
+ #define SSL_UNLOCK_READER(ss) if (ss->recvLock) PZ_Unlock(ss->recvLock)
+ #define SSL_LOCK_WRITER(ss) if (ss->sendLock) PZ_Lock(ss->sendLock)
+@@ -1417,6 +1524,7 @@
+ extern void ssl_FreeSocket(struct sslSocketStr *ssl);
+ extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
+ SSL3AlertDescription desc);
++extern SECStatus ssl3_DecodeError(sslSocket *ss);
+
+ extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket * ss,
+ CERTCertificate * cert,
+@@ -1436,7 +1544,7 @@
+ /*
+ * SSL3 specific routines
+ */
+-SECStatus ssl3_SendClientHello(sslSocket *ss);
++SECStatus ssl3_SendClientHello(sslSocket *ss, PRBool resending);
+
+ /*
+ * input into the SSL3 machinery from the actualy network reading code
+@@ -1531,6 +1639,8 @@
+ unsigned char *cs, int *size);
+
+ extern SECStatus ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache);
++extern SECStatus ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
++ PRUint32 length);
+
+ extern void ssl3_DestroySSL3Info(sslSocket *ss);
+
+@@ -1556,6 +1666,7 @@
+ extern SECStatus ssl3_ComputeCommonKeyHash(PRUint8 * hashBuf,
+ unsigned int bufLen, SSL3Hashes *hashes,
+ PRBool bypassPKCS11);
++extern void ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName);
+ extern SECStatus ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms);
+ extern SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src,
+ PRInt32 bytes);
+@@ -1724,6 +1835,42 @@
+ CERTCertList* list);
+ #endif /* NSS_PLATFORM_CLIENT_AUTH */
+
++/**************** DTLS-specific functions **************/
++extern void dtls_FreeQueuedMessage(DTLSQueuedMessage *msg);
++extern void dtls_FreeQueuedMessages(PRCList *lst);
++extern void dtls_FreeHandshakeMessages(PRCList *lst);
++
++extern SECStatus dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf);
++extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss,
++ SSL3Opaque *b, PRUint32 length);
++extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss);
++extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
++ const SSL3Opaque *pIn, PRInt32 nIn);
++extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
++extern SECStatus dtls_CompressMACEncryptRecord(sslSocket *ss,
++ DTLSEpoch epoch,
++ PRBool use_epoch,
++ SSL3ContentType type,
++ const SSL3Opaque *pIn,
++ PRUint32 contentLen,
++ sslBuffer *wrBuf);
++SECStatus ssl3_DisableNonDTLSSuites(sslSocket * ss);
++extern SECStatus dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb);
++extern SECStatus dtls_RestartTimer(sslSocket *ss, PRBool backoff,
++ DTLSTimerCb cb);
++extern void dtls_CheckTimer(sslSocket *ss);
++extern void dtls_CancelTimer(sslSocket *ss);
++extern void dtls_FinishedTimerCb(sslSocket *ss);
++extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised);
++extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records);
++extern int dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq);
++extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq);
++extern void dtls_RehandshakeCleanup(sslSocket *ss);
++extern SSL3ProtocolVersion
++dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
++extern SSL3ProtocolVersion
++dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
++
+ /********************** misc calls *********************/
+
+ extern int ssl_MapLowLevelError(int hiLevelError);
+Index: net/third_party/nss/ssl/manifest.mn
+===================================================================
+--- net/third_party/nss/ssl/manifest.mn (revision 127709)
++++ net/third_party/nss/ssl/manifest.mn (working copy)
+@@ -51,6 +51,7 @@
+
+ CSRCS = \
+ derive.c \
++ dtls1con.c \
+ prelib.c \
+ ssl3con.c \
+ ssl3gthr.c \
+Index: net/third_party/nss/ssl/ssl3prot.h
+===================================================================
+--- net/third_party/nss/ssl/ssl3prot.h (revision 127709)
++++ net/third_party/nss/ssl/ssl3prot.h (working copy)
+@@ -61,6 +61,9 @@
+
+ #define SSL3_RECORD_HEADER_LENGTH 5
+
++/* SSL3_RECORD_HEADER_LENGTH + epoch/sequence_number */
++#define DTLS_RECORD_HEADER_LENGTH 13
++
+ #define MAX_FRAGMENT_LENGTH 16384
+
+ typedef enum {
+@@ -150,6 +153,7 @@
+ hello_request = 0,
+ client_hello = 1,
+ server_hello = 2,
++ hello_verify_request = 3,
+ new_session_ticket = 4,
+ certificate = 11,
+ server_key_exchange = 12,
+Index: net/third_party/nss/ssl/sslcon.c
+===================================================================
+--- net/third_party/nss/ssl/sslcon.c (revision 127709)
++++ net/third_party/nss/ssl/sslcon.c (working copy)
+@@ -1249,7 +1249,12 @@
+
+ ssl_GetRecvBufLock(ss);
+
+- if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
++ /* The special case DTLS logic is needed here because the SSL/TLS
++ * version wants to auto-detect SSL2 vs. SSL3 on the initial handshake
++ * (ss->version == 0) but with DTLS it gets confused, so we force the
++ * SSL3 version.
++ */
++ if ((ss->version >= SSL_LIBRARY_VERSION_3_0) || IS_DTLS(ss)) {
+ /* Wait for handshake to complete, or application data to arrive. */
+ rv = ssl3_GatherCompleteHandshake(ss, 0);
+ } else {
+@@ -3120,7 +3125,7 @@
+
+ ssl_GetSSL3HandshakeLock(ss);
+ ssl_GetXmitBufLock(ss);
+- rv = ssl3_SendClientHello(ss);
++ rv = ssl3_SendClientHello(ss, PR_FALSE);
+ ssl_ReleaseXmitBufLock(ss);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+
+Index: net/third_party/nss/ssl/sslsecur.c
+===================================================================
+--- net/third_party/nss/ssl/sslsecur.c (revision 127709)
++++ net/third_party/nss/ssl/sslsecur.c (working copy)
+@@ -615,6 +615,7 @@
+ if (!(flags & PR_MSG_PEEK)) {
+ ss->gs.readOffset += amount;
+ }
++ PORT_Assert(ss->gs.readOffset <= ss->gs.writeOffset);
+ rv = amount;
+
+ SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d",
+Index: net/third_party/nss/ssl/sslsock.c
+===================================================================
+--- net/third_party/nss/ssl/sslsock.c (revision 127709)
++++ net/third_party/nss/ssl/sslsock.c (working copy)
+@@ -194,11 +194,20 @@
+ /*
+ * default range of enabled SSL/TLS protocols
+ */
+-static SSLVersionRange versions_defaults = {
++static SSLVersionRange versions_defaults_stream = {
+ SSL_LIBRARY_VERSION_3_0,
+ SSL_LIBRARY_VERSION_TLS_1_0
+ };
+
++static SSLVersionRange versions_defaults_datagram = {
++ SSL_LIBRARY_VERSION_TLS_1_1,
++ SSL_LIBRARY_VERSION_TLS_1_1
++};
++
++#define VERSIONS_DEFAULTS(variant) \
++ (variant == ssl_variant_stream ? &versions_defaults_stream : \
++ &versions_defaults_datagram)
++
+ sslSessionIDLookupFunc ssl_sid_lookup;
+ sslSessionIDCacheFunc ssl_sid_cache;
+ sslSessionIDUncacheFunc ssl_sid_uncache;
+@@ -217,7 +226,7 @@
+ #define LOCKSTATUS_OFFSET 10 /* offset of ENABLED */
+
+ /* forward declarations. */
+-static sslSocket *ssl_NewSocket(PRBool makeLocks);
++static sslSocket *ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant variant);
+ static SECStatus ssl_MakeLocks(sslSocket *ss);
+ static void ssl_SetDefaultsFromEnvironment(void);
+ static PRStatus ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack,
+@@ -281,7 +290,13 @@
+ sslSocket *ss;
+ SECStatus rv;
+
+- ss = ssl_NewSocket((PRBool)(!os->opt.noLocks));
++ /* Not implemented for datagram */
++ if (IS_DTLS(os)) {
++ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++ return NULL;
++ }
++
++ ss = ssl_NewSocket((PRBool)(!os->opt.noLocks), os->protocolVariant);
+ if (ss) {
+ ss->opt = os->opt;
+ ss->opt.useSocks = PR_FALSE;
+@@ -698,6 +713,13 @@
+ break;
+
+ case SSL_ENABLE_TLS:
++ if (IS_DTLS(ss)) {
++ if (on) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ rv = SECFailure; /* not allowed */
++ }
++ break;
++ }
+ ssl_EnableTLS(&ss->vrange, on);
+ ss->preferredCipher = NULL;
+ if (ss->cipherSpecs) {
+@@ -708,6 +730,13 @@
+ break;
+
+ case SSL_ENABLE_SSL3:
++ if (IS_DTLS(ss)) {
++ if (on) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ rv = SECFailure; /* not allowed */
++ }
++ break;
++ }
+ ssl_EnableSSL3(&ss->vrange, on);
+ ss->preferredCipher = NULL;
+ if (ss->cipherSpecs) {
+@@ -718,6 +747,13 @@
+ break;
+
+ case SSL_ENABLE_SSL2:
++ if (IS_DTLS(ss)) {
++ if (on) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ rv = SECFailure; /* not allowed */
++ }
++ break;
++ }
+ ss->opt.enableSSL2 = on;
+ if (on) {
+ ss->opt.v2CompatibleHello = on;
+@@ -743,6 +779,13 @@
+ break;
+
+ case SSL_V2_COMPATIBLE_HELLO:
++ if (IS_DTLS(ss)) {
++ if (on) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ rv = SECFailure; /* not allowed */
++ }
++ break;
++ }
+ ss->opt.v2CompatibleHello = on;
+ if (!on) {
+ ss->opt.enableSSL2 = on;
+@@ -938,10 +981,10 @@
+ case SSL_HANDSHAKE_AS_CLIENT: on = ssl_defaults.handshakeAsClient; break;
+ case SSL_HANDSHAKE_AS_SERVER: on = ssl_defaults.handshakeAsServer; break;
+ case SSL_ENABLE_TLS:
+- on = versions_defaults.max >= SSL_LIBRARY_VERSION_TLS_1_0;
++ on = versions_defaults_stream.max >= SSL_LIBRARY_VERSION_TLS_1_0;
+ break;
+ case SSL_ENABLE_SSL3:
+- on = versions_defaults.min == SSL_LIBRARY_VERSION_3_0;
++ on = versions_defaults_stream.min == SSL_LIBRARY_VERSION_3_0;
+ break;
+ case SSL_ENABLE_SSL2: on = ssl_defaults.enableSSL2; break;
+ case SSL_NO_CACHE: on = ssl_defaults.noCache; break;
+@@ -1034,11 +1077,11 @@
+ break;
+
+ case SSL_ENABLE_TLS:
+- ssl_EnableTLS(&versions_defaults, on);
++ ssl_EnableTLS(&versions_defaults_stream, on);
+ break;
+
+ case SSL_ENABLE_SSL3:
+- ssl_EnableSSL3(&versions_defaults, on);
++ ssl_EnableSSL3(&versions_defaults_stream, on);
+ break;
+
+ case SSL_ENABLE_SSL2:
+@@ -1360,8 +1403,8 @@
+
+
+ /* LOCKS ??? XXX */
+-PRFileDesc *
+-SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
++static PRFileDesc *
++ssl_ImportFD(PRFileDesc *model, PRFileDesc *fd, SSLProtocolVariant variant)
+ {
+ sslSocket * ns = NULL;
+ PRStatus rv;
+@@ -1374,10 +1417,10 @@
+
+ if (model == NULL) {
+ /* Just create a default socket if we're given NULL for the model */
+- ns = ssl_NewSocket((PRBool)(!ssl_defaults.noLocks));
++ ns = ssl_NewSocket((PRBool)(!ssl_defaults.noLocks), variant);
+ } else {
+ sslSocket * ss = ssl_FindSocket(model);
+- if (ss == NULL) {
++ if (ss == NULL || ss->protocolVariant != variant) {
+ SSL_DBG(("%d: SSL[%d]: bad model socket in ssl_ImportFD",
+ SSL_GETPID(), model));
+ return NULL;
+@@ -1403,6 +1446,18 @@
+ return fd;
+ }
+
++PRFileDesc *
++SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
++{
++ return ssl_ImportFD(model, fd, ssl_variant_stream);
++}
++
++PRFileDesc *
++DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd)
++{
++ return ssl_ImportFD(model, fd, ssl_variant_datagram);
++}
++
+ SECStatus
+ SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
+ void *arg)
+@@ -1667,9 +1722,18 @@
+ ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant,
+ SSL3ProtocolVersion version)
+ {
+- return protocolVariant == ssl_variant_stream &&
+- version >= SSL_LIBRARY_VERSION_3_0 &&
+- version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED;
++ switch (protocolVariant) {
++ case ssl_variant_stream:
++ return (version >= SSL_LIBRARY_VERSION_3_0 &&
++ version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
++ case ssl_variant_datagram:
++ return (version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
++ version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
++ default:
++ /* Can't get here */
++ PORT_Assert(PR_FALSE);
++ return PR_FALSE;
++ }
+ }
+
+ /* Returns PR_TRUE if the given version range is valid and
+@@ -1689,13 +1753,24 @@
+ SSL_VersionRangeGetSupported(SSLProtocolVariant protocolVariant,
+ SSLVersionRange *vrange)
+ {
+- if (protocolVariant != ssl_variant_stream || !vrange) {
++ if (!vrange) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+- vrange->min = SSL_LIBRARY_VERSION_3_0;
+- vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
++ switch (protocolVariant) {
++ case ssl_variant_stream:
++ vrange->min = SSL_LIBRARY_VERSION_3_0;
++ vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
++ break;
++ case ssl_variant_datagram:
++ vrange->min = SSL_LIBRARY_VERSION_TLS_1_1;
++ vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
++ break;
++ default:
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ return SECFailure;
++ }
+
+ return SECSuccess;
+ }
+@@ -1704,12 +1779,13 @@
+ SSL_VersionRangeGetDefault(SSLProtocolVariant protocolVariant,
+ SSLVersionRange *vrange)
+ {
+- if (protocolVariant != ssl_variant_stream || !vrange) {
++ if ((protocolVariant != ssl_variant_stream &&
++ protocolVariant != ssl_variant_datagram) || !vrange) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+- *vrange = versions_defaults;
++ *vrange = *VERSIONS_DEFAULTS(protocolVariant);
+
+ return SECSuccess;
+ }
+@@ -1723,7 +1799,7 @@
+ return SECFailure;
+ }
+
+- versions_defaults = *vrange;
++ *VERSIONS_DEFAULTS(protocolVariant) = *vrange;
+
+ return SECSuccess;
+ }
+@@ -2830,7 +2906,7 @@
+ ** Create a newsocket structure for a file descriptor.
+ */
+ static sslSocket *
+-ssl_NewSocket(PRBool makeLocks)
++ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
+ {
+ sslSocket *ss;
+
+@@ -2851,7 +2927,7 @@
+ ss->opt = ssl_defaults;
+ ss->opt.useSocks = PR_FALSE;
+ ss->opt.noLocks = !makeLocks;
+- ss->vrange = versions_defaults;
++ ss->vrange = *VERSIONS_DEFAULTS(protocolVariant);
+
+ ss->peerID = NULL;
+ ss->rTimeout = PR_INTERVAL_NO_TIMEOUT;
+@@ -2907,6 +2983,7 @@
+ PORT_Free(ss);
+ ss = NULL;
+ }
++ ss->protocolVariant = protocolVariant;
+ }
+ return ss;
+ }
+Index: net/third_party/nss/ssl/ssl3con.c
+===================================================================
+--- net/third_party/nss/ssl/ssl3con.c (revision 127709)
++++ net/third_party/nss/ssl/ssl3con.c (working copy)
+@@ -42,6 +42,8 @@
+ * ***** END LICENSE BLOCK ***** */
+ /* $Id: ssl3con.c,v 1.173 2012/03/18 00:31:19 wtc%google.com Exp $ */
+
++/* TODO(ekr): Implement HelloVerifyRequest on server side. OK for now. */
++
+ #include "cert.h"
+ #include "ssl.h"
+ #include "cryptohi.h" /* for DSAU_ stuff */
+@@ -92,6 +94,7 @@
+ static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss,
+ const unsigned char *b,
+ unsigned int l);
++static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
+
+ static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
+ int maxOutputLen, const unsigned char *input,
+@@ -221,22 +224,6 @@
+ #endif /* NSS_ENABLE_ECC */
+ };
+
+-#ifdef NSS_ENABLE_ZLIB
+-/*
+- * The DEFLATE algorithm can result in an expansion of 0.1% + 12 bytes. For a
+- * maximum TLS record payload of 2**14 bytes, that's 29 bytes.
+- */
+-#define SSL3_COMPRESSION_MAX_EXPANSION 29
+-#else /* !NSS_ENABLE_ZLIB */
+-#define SSL3_COMPRESSION_MAX_EXPANSION 0
+-#endif
+-
+-/*
+- * make sure there is room in the write buffer for padding and
+- * other compression and cryptographic expansions.
+- */
+-#define SSL3_BUFFER_FUDGE 100 + SSL3_COMPRESSION_MAX_EXPANSION
+-
+ #define EXPORT_RSA_KEY_LENGTH 64 /* bytes */
+
+
+@@ -517,6 +504,7 @@
+ case hello_request: rv = "hello_request (0)"; break;
+ case client_hello: rv = "client_hello (1)"; break;
+ case server_hello: rv = "server_hello (2)"; break;
++ case hello_verify_request: rv = "hello_verify_request (3)"; break;
+ case certificate: rv = "certificate (11)"; break;
+ case server_key_exchange: rv = "server_key_exchange (12)"; break;
+ case certificate_request: rv = "certificate_request (13)"; break;
+@@ -656,7 +644,7 @@
+ suite->isPresent = PR_FALSE;
+ continue;
+ }
+- cipher_alg=bulk_cipher_defs[cipher_def->bulk_cipher_alg ].calg;
++ cipher_alg = bulk_cipher_defs[cipher_def->bulk_cipher_alg].calg;
+ PORT_Assert( alg2Mech[cipher_alg].calg == cipher_alg);
+ cipher_mech = alg2Mech[cipher_alg].cmech;
+ exchKeyType =
+@@ -1148,7 +1136,7 @@
+ ** ssl3_DestroySSL3Info
+ ** Caller must hold SpecWriteLock.
+ */
+-static void
++void
+ ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
+ {
+ PRBool freeit = (PRBool)(!spec->bypassCiphers);
+@@ -1228,6 +1216,12 @@
+ return SECFailure; /* error code set by ssl_LookupCipherSuiteDef */
+ }
+
++ if (IS_DTLS(ss)) {
++ /* Double-check that we did not pick an RC4 suite */
++ PORT_Assert((suite_def->bulk_cipher_alg != cipher_rc4) &&
++ (suite_def->bulk_cipher_alg != cipher_rc4_40) &&
++ (suite_def->bulk_cipher_alg != cipher_rc4_56));
++ }
+
+ cipher = suite_def->bulk_cipher_alg;
+ kea = suite_def->key_exchange_alg;
+@@ -1754,6 +1748,7 @@
+ ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
+ {
+ ssl3CipherSpec * pwSpec;
++ ssl3CipherSpec * cwSpec;
+ SECStatus rv;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+@@ -1763,6 +1758,7 @@
+ PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
+
+ pwSpec = ss->ssl3.pwSpec;
++ cwSpec = ss->ssl3.cwSpec;
+
+ if (pms || (!pwSpec->msItem.len && !pwSpec->master_secret)) {
+ rv = ssl3_DeriveMasterSecret(ss, pms);
+@@ -1794,7 +1790,32 @@
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ }
++ if (rv != SECSuccess) {
++ goto done;
++ }
+
++ /* Generic behaviors -- common to all crypto methods */
++ if (!IS_DTLS(ss)) {
++ pwSpec->read_seq_num.high = pwSpec->write_seq_num.high = 0;
++ } else {
++ if (cwSpec->epoch == PR_UINT16_MAX) {
++ /* The problem here is that we have rehandshaked too many
++ * times (you are not allowed to wrap the epoch). The
++ * spec says you should be discarding the connection
++ * and start over, so not much we can do here. */
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++ rv = SECFailure;
++ goto done;
++ }
++ /* The sequence number has the high 16 bits as the epoch. */
++ pwSpec->epoch = cwSpec->epoch + 1;
++ pwSpec->read_seq_num.high = pwSpec->write_seq_num.high =
++ pwSpec->epoch << 16;
++
++ dtls_InitRecvdRecords(&pwSpec->recvdRecords);
++ }
++ pwSpec->read_seq_num.low = pwSpec->write_seq_num.low = 0;
++
+ done:
+ ssl_ReleaseSpecWriteLock(ss); /******************************/
+ if (rv != SECSuccess)
+@@ -1834,6 +1855,7 @@
+ ssl3_ComputeRecordMAC(
+ ssl3CipherSpec * spec,
+ PRBool useServerMacKey,
++ PRBool isDTLS,
+ SSL3ContentType type,
+ SSL3ProtocolVersion version,
+ SSL3SequenceNumber seq_num,
+@@ -1871,8 +1893,16 @@
+ isTLS = PR_FALSE;
+ } else {
+ /* New TLS hash includes version. */
+- temp[9] = MSB(version);
+- temp[10] = LSB(version);
++ if (isDTLS) {
++ SSL3ProtocolVersion dtls_version;
++
++ dtls_version = dtls_TLSVersionToDTLSVersion(version);
++ temp[9] = MSB(dtls_version);
++ temp[10] = LSB(dtls_version);
++ } else {
++ temp[9] = MSB(version);
++ temp[10] = LSB(version);
++ }
+ temp[11] = MSB(inputLength);
+ temp[12] = LSB(inputLength);
+ tempLen = 13;
+@@ -2022,9 +2052,10 @@
+ }
+
+ /* Caller must hold the spec read lock. */
+-static SECStatus
++SECStatus
+ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
+ PRBool isServer,
++ PRBool isDTLS,
+ SSL3ContentType type,
+ const SSL3Opaque * pIn,
+ PRUint32 contentLen,
+@@ -2035,10 +2066,12 @@
+ PRUint32 macLen = 0;
+ PRUint32 fragLen;
+ PRUint32 p1Len, p2Len, oddLen = 0;
++ PRUint16 headerLen;
+ int ivLen = 0;
+ int cipherBytes = 0;
+
+ cipher_def = cwSpec->cipher_def;
++ headerLen = isDTLS ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
+
+ if (cipher_def->type == type_block &&
+ cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+@@ -2048,20 +2081,20 @@
+ * record.
+ */
+ ivLen = cipher_def->iv_size;
+- if (ivLen > wrBuf->space - SSL3_RECORD_HEADER_LENGTH) {
++ if (ivLen > wrBuf->space - headerLen) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+- rv = PK11_GenerateRandom(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH, ivLen);
++ rv = PK11_GenerateRandom(wrBuf->buf + headerLen, ivLen);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
+ return rv;
+ }
+ rv = cwSpec->encode( cwSpec->encodeContext,
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH,
++ wrBuf->buf + headerLen,
+ &cipherBytes, /* output and actual outLen */
+ ivLen, /* max outlen */
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH,
++ wrBuf->buf + headerLen,
+ ivLen); /* input and inputLen*/
+ if (rv != SECSuccess || cipherBytes != ivLen) {
+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
+@@ -2073,20 +2106,20 @@
+ int outlen;
+ rv = cwSpec->compressor(
+ cwSpec->compressContext,
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen, &outlen,
+- wrBuf->space - SSL3_RECORD_HEADER_LENGTH - ivLen, pIn, contentLen);
++ wrBuf->buf + headerLen + ivLen, &outlen,
++ wrBuf->space - headerLen - ivLen, pIn, contentLen);
+ if (rv != SECSuccess)
+ return rv;
+- pIn = wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen;
++ pIn = wrBuf->buf + headerLen + ivLen;
+ contentLen = outlen;
+ }
+
+ /*
+ * Add the MAC
+ */
+- rv = ssl3_ComputeRecordMAC( cwSpec, isServer,
++ rv = ssl3_ComputeRecordMAC( cwSpec, isServer, isDTLS,
+ type, cwSpec->version, cwSpec->write_seq_num, pIn, contentLen,
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + contentLen, &macLen);
++ wrBuf->buf + headerLen + ivLen + contentLen, &macLen);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
+ return SECFailure;
+@@ -2113,7 +2146,7 @@
+ PORT_Assert((fragLen % cipher_def->block_size) == 0);
+
+ /* Pad according to TLS rules (also acceptable to SSL3). */
+- pBuf = &wrBuf->buf[SSL3_RECORD_HEADER_LENGTH + ivLen + fragLen - 1];
++ pBuf = &wrBuf->buf[headerLen + ivLen + fragLen - 1];
+ for (i = padding_length + 1; i > 0; --i) {
+ *pBuf-- = padding_length;
+ }
+@@ -2130,13 +2163,12 @@
+ p2Len += oddLen;
+ PORT_Assert( (cipher_def->block_size < 2) || \
+ (p2Len % cipher_def->block_size) == 0);
+- memmove(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
+- pIn + p1Len, oddLen);
++ memmove(wrBuf->buf + headerLen + ivLen + p1Len, pIn + p1Len, oddLen);
+ }
+ if (p1Len > 0) {
+ int cipherBytesPart1 = -1;
+ rv = cwSpec->encode( cwSpec->encodeContext,
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen, /* output */
++ wrBuf->buf + headerLen + ivLen, /* output */
+ &cipherBytesPart1, /* actual outlen */
+ p1Len, /* max outlen */
+ pIn, p1Len); /* input, and inputlen */
+@@ -2150,10 +2182,10 @@
+ if (p2Len > 0) {
+ int cipherBytesPart2 = -1;
+ rv = cwSpec->encode( cwSpec->encodeContext,
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
++ wrBuf->buf + headerLen + ivLen + p1Len,
+ &cipherBytesPart2, /* output and actual outLen */
+ p2Len, /* max outlen */
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
++ wrBuf->buf + headerLen + ivLen + p1Len,
+ p2Len); /* input and inputLen*/
+ PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
+ if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
+@@ -2164,15 +2196,33 @@
+ }
+ PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
+
++ wrBuf->len = cipherBytes + headerLen;
++ wrBuf->buf[0] = type;
++ if (isDTLS) {
++ SSL3ProtocolVersion version;
++
++ version = dtls_TLSVersionToDTLSVersion(cwSpec->version);
++ wrBuf->buf[1] = MSB(version);
++ wrBuf->buf[2] = LSB(version);
++ wrBuf->buf[3] = (unsigned char)(cwSpec->write_seq_num.high >> 24);
++ wrBuf->buf[4] = (unsigned char)(cwSpec->write_seq_num.high >> 16);
++ wrBuf->buf[5] = (unsigned char)(cwSpec->write_seq_num.high >> 8);
++ wrBuf->buf[6] = (unsigned char)(cwSpec->write_seq_num.high >> 0);
++ wrBuf->buf[7] = (unsigned char)(cwSpec->write_seq_num.low >> 24);
++ wrBuf->buf[8] = (unsigned char)(cwSpec->write_seq_num.low >> 16);
++ wrBuf->buf[9] = (unsigned char)(cwSpec->write_seq_num.low >> 8);
++ wrBuf->buf[10] = (unsigned char)(cwSpec->write_seq_num.low >> 0);
++ wrBuf->buf[11] = MSB(cipherBytes);
++ wrBuf->buf[12] = LSB(cipherBytes);
++ } else {
++ wrBuf->buf[1] = MSB(cwSpec->version);
++ wrBuf->buf[2] = LSB(cwSpec->version);
++ wrBuf->buf[3] = MSB(cipherBytes);
++ wrBuf->buf[4] = LSB(cipherBytes);
++ }
++
+ ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
+
+- wrBuf->len = cipherBytes + SSL3_RECORD_HEADER_LENGTH;
+- wrBuf->buf[0] = type;
+- wrBuf->buf[1] = MSB(cwSpec->version);
+- wrBuf->buf[2] = LSB(cwSpec->version);
+- wrBuf->buf[3] = MSB(cipherBytes);
+- wrBuf->buf[4] = LSB(cipherBytes);
+-
+ return SECSuccess;
+ }
+
+@@ -2194,10 +2244,13 @@
+ * ssl_SEND_FLAG_FORCE_INTO_BUFFER
+ * As above, except this suppresses all write attempts, and forces
+ * all ciphertext into the pending ciphertext buffer.
++ * ssl_SEND_FLAG_USE_EPOCH (for DTLS)
++ * Forces the use of the provided epoch
+ *
+ */
+-static PRInt32
++PRInt32
+ ssl3_SendRecord( sslSocket * ss,
++ DTLSEpoch epoch, /* DTLS only */
+ SSL3ContentType type,
+ const SSL3Opaque * pIn, /* input buffer */
+ PRInt32 nIn, /* bytes of input */
+@@ -2269,8 +2322,8 @@
+ sslBuffer secondRecord;
+
+ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
+- ss->sec.isServer, type, pIn, 1,
+- wrBuf);
++ ss->sec.isServer, IS_DTLS(ss),
++ type, pIn, 1, wrBuf);
+ if (rv != SECSuccess)
+ goto spec_locked_loser;
+
+@@ -2282,17 +2335,28 @@
+ secondRecord.space = wrBuf->space - wrBuf->len;
+
+ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
+- ss->sec.isServer, type, pIn + 1,
+- contentLen - 1, &secondRecord);
++ ss->sec.isServer, IS_DTLS(ss),
++ type, pIn + 1, contentLen - 1,
++ &secondRecord);
+ if (rv == SECSuccess) {
+ PRINT_BUF(50, (ss, "send (encrypted) record data [2/2]:",
+ secondRecord.buf, secondRecord.len));
+ wrBuf->len += secondRecord.len;
+ }
+ } else {
+- rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
+- ss->sec.isServer, type, pIn,
+- contentLen, wrBuf);
++ if (!IS_DTLS(ss)) {
++ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
++ ss->sec.isServer,
++ IS_DTLS(ss),
++ type, pIn,
++ contentLen, wrBuf);
++ } else {
++ rv = dtls_CompressMACEncryptRecord(ss, epoch,
++ !!(flags & ssl_SEND_FLAG_USE_EPOCH),
++ type, pIn,
++ contentLen, wrBuf);
++ }
++
+ if (rv == SECSuccess) {
+ PRINT_BUF(50, (ss, "send (encrypted) record data:",
+ wrBuf->buf, wrBuf->len));
+@@ -2350,6 +2414,11 @@
+ }
+ wrBuf->len -= sent;
+ if (wrBuf->len) {
++ if (IS_DTLS(ss)) {
++ /* DTLS just says no in this case. No buffering */
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
++ return SECFailure;
++ }
+ /* now take all the remaining unsent new ciphertext and
+ * append it to the buffer of previously unsent ciphertext.
+ */
+@@ -2378,6 +2447,9 @@
+ PRInt32 discarded = 0;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
++ /* These flags for internal use only */
++ PORT_Assert(!(flags & (ssl_SEND_FLAG_USE_EPOCH |
++ ssl_SEND_FLAG_NO_RETRANSMIT)));
+ if (len < 0 || !in) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return SECFailure;
+@@ -2415,7 +2487,11 @@
+ ssl_GetXmitBufLock(ss);
+ }
+ toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);
+- sent = ssl3_SendRecord(ss, content_application_data,
++ /*
++ * Note that the 0 epoch is OK because flags will never require
++ * its use, as guaranteed by the PORT_Assert above.
++ */
++ sent = ssl3_SendRecord(ss, 0, content_application_data,
+ in + totalSent, toSend, flags);
+ if (sent < 0) {
+ if (totalSent > 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) {
+@@ -2450,10 +2526,15 @@
+ return totalSent + discarded;
+ }
+
+-/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
++/* Attempt to send buffered handshake messages.
+ * This function returns SECSuccess or SECFailure, never SECWouldBlock.
+ * Always set sendBuf.len to 0, even when returning SECFailure.
+ *
++ * Depending on whether we are doing DTLS or not, this either calls
++ *
++ * - ssl3_FlushHandshakeMessages if non-DTLS
++ * - dtls_FlushHandshakeMessages if DTLS
++ *
+ * Called from SSL3_SendAlert(), ssl3_SendChangeCipherSpecs(),
+ * ssl3_AppendHandshake(), ssl3_SendClientHello(),
+ * ssl3_SendHelloRequest(), ssl3_SendServerHelloDone(),
+@@ -2462,6 +2543,22 @@
+ static SECStatus
+ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
+ {
++ if (IS_DTLS(ss)) {
++ return dtls_FlushHandshakeMessages(ss, flags);
++ } else {
++ return ssl3_FlushHandshakeMessages(ss, flags);
++ }
++}
++
++/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
++ * This function returns SECSuccess or SECFailure, never SECWouldBlock.
++ * Always set sendBuf.len to 0, even when returning SECFailure.
++ *
++ * Called from ssl3_FlushHandshake
++ */
++static SECStatus
++ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
++{
+ PRInt32 rv = SECSuccess;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+@@ -2476,7 +2573,7 @@
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ } else {
+- rv = ssl3_SendRecord(ss, content_handshake, ss->sec.ci.sendBuf.buf,
++ rv = ssl3_SendRecord(ss, 0, content_handshake, ss->sec.ci.sendBuf.buf,
+ ss->sec.ci.sendBuf.len, flags);
+ }
+ if (rv < 0) {
+@@ -2593,7 +2690,7 @@
+ rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+ if (rv == SECSuccess) {
+ PRInt32 sent;
+- sent = ssl3_SendRecord(ss, content_alert, bytes, 2,
++ sent = ssl3_SendRecord(ss, 0, content_alert, bytes, 2,
+ desc == no_certificate
+ ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0);
+ rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
+@@ -2667,7 +2764,7 @@
+ /*
+ * Send handshake_Failure alert. Set generic error number.
+ */
+-static SECStatus
++SECStatus
+ ssl3_DecodeError(sslSocket *ss)
+ {
+ (void)SSL3_SendAlert(ss, alert_fatal,
+@@ -2755,7 +2852,8 @@
+ default: error = SSL_ERROR_RX_UNKNOWN_ALERT; break;
+ }
+ if (level == alert_fatal) {
+- ss->sec.uncache(ss->sec.ci.sid);
++ if (!ss->opt.noCache)
++ ss->sec.uncache(ss->sec.ci.sid);
+ if ((ss->ssl3.hs.ws == wait_server_hello) &&
+ (desc == handshake_failure)) {
+ /* XXX This is a hack. We're assuming that any handshake failure
+@@ -2806,17 +2904,22 @@
+ if (rv != SECSuccess) {
+ return rv; /* error code set by ssl3_FlushHandshake */
+ }
+- sent = ssl3_SendRecord(ss, content_change_cipher_spec, &change, 1,
+- ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+- if (sent < 0) {
+- return (SECStatus)sent; /* error code set by ssl3_SendRecord */
++ if (!IS_DTLS(ss)) {
++ sent = ssl3_SendRecord(ss, 0, content_change_cipher_spec, &change, 1,
++ ssl_SEND_FLAG_FORCE_INTO_BUFFER);
++ if (sent < 0) {
++ return (SECStatus)sent; /* error code set by ssl3_SendRecord */
++ }
++ } else {
++ rv = dtls_QueueMessage(ss, content_change_cipher_spec, &change, 1);
++ if (rv != SECSuccess) {
++ return rv;
++ }
+ }
+
+ /* swap the pending and current write specs. */
+ ssl_GetSpecWriteLock(ss); /**************************************/
+ pwSpec = ss->ssl3.pwSpec;
+- pwSpec->write_seq_num.high = 0;
+- pwSpec->write_seq_num.low = 0;
+
+ ss->ssl3.pwSpec = ss->ssl3.cwSpec;
+ ss->ssl3.cwSpec = pwSpec;
+@@ -2829,7 +2932,14 @@
+ * (Both the read and write sides have changed) destroy it.
+ */
+ if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
+- ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE/*freeSrvName*/);
++ if (!IS_DTLS(ss)) {
++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE/*freeSrvName*/);
++ } else {
++ /* With DTLS, we need to set a holddown timer in case the final
++ * message got lost */
++ ss->ssl3.hs.rtTimeoutMs = DTLS_FINISHED_TIMER_MS;
++ dtls_StartTimer(ss, dtls_FinishedTimerCb);
++ }
+ }
+ ssl_ReleaseSpecWriteLock(ss); /**************************************/
+
+@@ -2878,7 +2988,6 @@
+ /* Swap the pending and current read specs. */
+ ssl_GetSpecWriteLock(ss); /*************************************/
+ prSpec = ss->ssl3.prSpec;
+- prSpec->read_seq_num.high = prSpec->read_seq_num.low = 0;
+
+ ss->ssl3.prSpec = ss->ssl3.crSpec;
+ ss->ssl3.crSpec = prSpec;
+@@ -2981,6 +3090,11 @@
+ if (!isDH && pwSpec->master_secret && ss->opt.detectRollBack) {
+ SSL3ProtocolVersion client_version;
+ client_version = pms_version.major << 8 | pms_version.minor;
++
++ if (IS_DTLS(ss)) {
++ client_version = dtls_DTLSVersionToTLSVersion(client_version);
++ }
++
+ if (client_version != ss->clientHelloVersion) {
+ /* Destroy it. Version roll-back detected. */
+ PK11_FreeSymKey(pwSpec->master_secret);
+@@ -3405,6 +3519,17 @@
+ {
+ SECStatus rv;
+
++ /* If we already have a message in place, we need to enqueue it.
++ * This empties the buffer. This is a convenient place to call
++ * dtls_StageHandshakeMessage to mark the message boundary.
++ */
++ if (IS_DTLS(ss)) {
++ rv = dtls_StageHandshakeMessage(ss);
++ if (rv != SECSuccess) {
++ return rv;
++ }
++ }
++
+ SSL_TRC(30,("%d: SSL3[%d]: append handshake header: type %s",
+ SSL_GETPID(), ss->fd, ssl3_DecodeHandshakeType(t)));
+ PRINT_BUF(60, (ss, "MD5 handshake hash:",
+@@ -3417,6 +3542,32 @@
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+ rv = ssl3_AppendHandshakeNumber(ss, length, 3);
++ if (rv != SECSuccess) {
++ return rv; /* error code set by AppendHandshake, if applicable. */
++ }
++
++ if (IS_DTLS(ss)) {
++ /* Note that we make an unfragmented message here. We fragment in the
++ * transmission code, if necessary */
++ rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.sendMessageSeq, 2);
++ if (rv != SECSuccess) {
++ return rv; /* error code set by AppendHandshake, if applicable. */
++ }
++ ss->ssl3.hs.sendMessageSeq++;
++
++ /* 0 is the fragment offset, because it's not fragmented yet */
++ rv = ssl3_AppendHandshakeNumber(ss, 0, 3);
++ if (rv != SECSuccess) {
++ return rv; /* error code set by AppendHandshake, if applicable. */
++ }
++
++ /* Fragment length -- set to the packet length because not fragmented */
++ rv = ssl3_AppendHandshakeNumber(ss, length, 3);
++ if (rv != SECSuccess) {
++ return rv; /* error code set by AppendHandshake, if applicable. */
++ }
++ }
++
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+
+@@ -3823,9 +3974,10 @@
+ /* Called from ssl3_HandleHelloRequest(),
+ * ssl3_RedoHandshake()
+ * ssl2_BeginClientHandshake (when resuming ssl3 session)
++ * dtls_HandleHelloVerifyRequest(with resending=PR_TRUE)
+ */
+ SECStatus
+-ssl3_SendClientHello(sslSocket *ss)
++ssl3_SendClientHello(sslSocket *ss, PRBool resending)
+ {
+ sslSessionID * sid;
+ ssl3CipherSpec * cwSpec;
+@@ -3849,6 +4001,7 @@
+ return rv; /* ssl3_InitState has set the error code. */
+ }
+ ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
++ PORT_Assert(IS_DTLS(ss) || !resending);
+
+ /* We might be starting a session renegotiation in which case we should
+ * clear previous state.
+@@ -4008,6 +4161,10 @@
+ }
+ #endif
+
++ if (IS_DTLS(ss)) {
++ ssl3_DisableNonDTLSSuites(ss);
++ }
++
+ /* how many suites are permitted by policy and user preference? */
+ num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
+ if (!num_suites)
+@@ -4027,6 +4184,9 @@
+ 1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength) +
+ 2 + num_suites*sizeof(ssl3CipherSuite) +
+ 1 + numCompressionMethods + total_exten_len;
++ if (IS_DTLS(ss)) {
++ length += 1 + ss->ssl3.hs.cookieLen;
++ }
+
+ rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
+ if (rv != SECSuccess) {
+@@ -4034,13 +4194,23 @@
+ }
+
+ ss->clientHelloVersion = ss->version;
+- rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
++ if (IS_DTLS(ss)) {
++ PRUint16 version;
++
++ version = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion);
++ rv = ssl3_AppendHandshakeNumber(ss, version, 2);
++ } else {
++ rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
++ }
+ if (rv != SECSuccess) {
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+- rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
+- if (rv != SECSuccess) {
+- return rv; /* err set by GetNewRandom. */
++
++ if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
++ rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
++ if (rv != SECSuccess) {
++ return rv; /* err set by GetNewRandom. */
++ }
+ }
+ rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
+ SSL3_RANDOM_LENGTH);
+@@ -4057,6 +4227,14 @@
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
++ if (IS_DTLS(ss)) {
++ rv = ssl3_AppendHandshakeVariable(
++ ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
++ if (rv != SECSuccess) {
++ return rv; /* err set by ssl3_AppendHandshake* */
++ }
++ }
++
+ rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
+ if (rv != SECSuccess) {
+ return rv; /* err set by ssl3_AppendHandshake* */
+@@ -4180,8 +4358,12 @@
+ ss->sec.ci.sid = NULL;
+ }
+
++ if (IS_DTLS(ss)) {
++ dtls_RehandshakeCleanup(ss);
++ }
++
+ ssl_GetXmitBufLock(ss);
+- rv = ssl3_SendClientHello(ss);
++ rv = ssl3_SendClientHello(ss, PR_FALSE);
+ ssl_ReleaseXmitBufLock(ss);
+
+ return rv;
+@@ -5036,6 +5218,23 @@
+ }
+ version = (SSL3ProtocolVersion)temp;
+
++ if (IS_DTLS(ss)) {
++ /* RFC 4347 required that you verify that the server versions
++ * match (Section 4.2.1) in the HelloVerifyRequest and the
++ * ServerHello.
++ *
++ * RFC 6347 suggests (SHOULD) that servers always use 1.0
++ * in HelloVerifyRequest and allows the versions not to match,
++ * especially when 1.2 is being negotiated.
++ *
++ * Therefore we do not check for matching here.
++ */
++ version = dtls_DTLSVersionToTLSVersion(version);
++ if (version == 0) { /* Insane version number */
++ goto alert_loser;
++ }
++ }
++
+ rv = ssl3_NegotiateVersion(ss, version, PR_FALSE);
+ if (rv != SECSuccess) {
+ desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
+@@ -6264,6 +6463,7 @@
+ SSL3AlertLevel level = alert_fatal;
+ SSL3ProtocolVersion version;
+ SECItem sidBytes = {siBuffer, NULL, 0};
++ SECItem cookieBytes = {siBuffer, NULL, 0};
+ SECItem suites = {siBuffer, NULL, 0};
+ SECItem comps = {siBuffer, NULL, 0};
+ PRBool haveSpecWriteLock = PR_FALSE;
+@@ -6281,6 +6481,20 @@
+ return rv; /* error code is set. */
+ }
+
++ /* Clearing the handshake pointers so that ssl_Do1stHandshake won't
++ * call ssl2_HandleMessage.
++ *
++ * The issue here is that TLS ordinarily starts out in
++ * ssl2_HandleV3HandshakeRecord() because of the backward-compatibility
++ * code paths. That function zeroes these next pointers. But with DTLS,
++ * we don't even try to do the v2 ClientHello so we skip that function
++ * and need to reset these values here.
++ */
++ if (IS_DTLS(ss)) {
++ ss->nextHandshake = 0;
++ ss->securityHandshake = 0;
++ }
++
+ /* We might be starting session renegotiation in which case we should
+ * clear previous state.
+ */
+@@ -6306,10 +6520,22 @@
+ goto alert_loser;
+ }
+
++ if (IS_DTLS(ss)) {
++ dtls_RehandshakeCleanup(ss);
++ }
++
+ tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
+ if (tmp < 0)
+ goto loser; /* malformed, alert already sent */
+- ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
++
++ /* Translate the version */
++ if (IS_DTLS(ss)) {
++ ss->clientHelloVersion = version =
++ dtls_DTLSVersionToTLSVersion((SSL3ProtocolVersion)tmp);
++ } else {
++ ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
++ }
++
+ rv = ssl3_NegotiateVersion(ss, version, PR_TRUE);
+ if (rv != SECSuccess) {
+ desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
+@@ -6331,6 +6557,14 @@
+ goto loser; /* malformed */
+ }
+
++ /* grab the client's cookie, if present. */
++ if (IS_DTLS(ss)) {
++ rv = ssl3_ConsumeHandshakeVariable(ss, &cookieBytes, 1, &b, &length);
++ if (rv != SECSuccess) {
++ goto loser; /* malformed */
++ }
++ }
++
+ /* grab the list of cipher suites. */
+ rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
+ if (rv != SECSuccess) {
+@@ -6479,6 +6713,10 @@
+ ssl3_FilterECCipherSuitesByServerCerts(ss);
+ #endif
+
++ if (IS_DTLS(ss)) {
++ ssl3_DisableNonDTLSSuites(ss);
++ }
++
+ #ifdef PARANOID
+ /* Look for a matching cipher suite. */
+ j = ssl3_config_match_init(ss);
+@@ -7166,17 +7404,28 @@
+ PRUint32 maxBytes = 65535;
+ PRUint32 length;
+ PRInt32 extensions_len = 0;
++ SSL3ProtocolVersion version;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: send server_hello handshake", SSL_GETPID(),
+ ss->fd));
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+- PORT_Assert( MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0));
+
+- if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
+- PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
+- return SECFailure;
++ if (!IS_DTLS(ss)) {
++ PORT_Assert(MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0));
++
++ if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
++ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
++ return SECFailure;
++ }
++ } else {
++ PORT_Assert(MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_DTLS_1_0));
++
++ if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_DTLS_1_0)) {
++ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
++ return SECFailure;
++ }
+ }
+
+ sid = ss->sec.ci.sid;
+@@ -7194,7 +7443,13 @@
+ return rv; /* err set by AppendHandshake. */
+ }
+
+- rv = ssl3_AppendHandshakeNumber(ss, ss->version, 2);
++ if (IS_DTLS(ss)) {
++ version = dtls_TLSVersionToDTLSVersion(ss->version);
++ } else {
++ version = ss->version;
++ }
++
++ rv = ssl3_AppendHandshakeNumber(ss, version, 2);
+ if (rv != SECSuccess) {
+ return rv; /* err set by AppendHandshake. */
+ }
+@@ -7379,11 +7634,8 @@
+ nnames = ca_list->nnames;
+ }
+
+- if (!nnames) {
+- PORT_SetError(SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA);
+- return SECFailure;
+- }
+-
++ /* There used to be a test here to require a CA, but there
++ * are cases where you want to have no CAs offered. */
+ for (i = 0, name = names; i < nnames; i++, name++) {
+ calen += 2 + name->len;
+ }
+@@ -7551,9 +7803,17 @@
+ }
+
+ /* Generate the pre-master secret ... */
+- version.major = MSB(ss->clientHelloVersion);
+- version.minor = LSB(ss->clientHelloVersion);
++ if (IS_DTLS(ss)) {
++ SSL3ProtocolVersion temp;
+
++ temp = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion);
++ version.major = MSB(temp);
++ version.minor = LSB(temp);
++ } else {
++ version.major = MSB(ss->clientHelloVersion);
++ version.minor = LSB(ss->clientHelloVersion);
++ }
++
+ param.data = (unsigned char *)&version;
+ param.len = sizeof version;
+
+@@ -7635,6 +7895,11 @@
+ } else if (ss->opt.detectRollBack) {
+ SSL3ProtocolVersion client_version =
+ (rsaPmsBuf[0] << 8) | rsaPmsBuf[1];
++
++ if (IS_DTLS(ss)) {
++ client_version = dtls_DTLSVersionToTLSVersion(client_version);
++ }
++
+ if (client_version != ss->clientHelloVersion) {
+ /* Version roll-back detected. ensure failure. */
+ rv = PK11_GenerateRandom(rsaPmsBuf, sizeof rsaPmsBuf);
+@@ -8851,6 +9116,10 @@
+ }
+ }
+
++ if (IS_DTLS(ss)) {
++ flags |= ssl_SEND_FLAG_NO_RETRANSMIT;
++ }
++
+ rv = ssl3_SendFinished(ss, flags);
+ if (rv != SECSuccess) {
+ goto xmit_loser; /* err is set. */
+@@ -8980,13 +9249,14 @@
+ * hanshake message.
+ * Caller must hold Handshake and RecvBuf locks.
+ */
+-static SECStatus
++SECStatus
+ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ {
+ SECStatus rv = SECSuccess;
+ SSL3HandshakeType type = ss->ssl3.hs.msg_type;
+ SSL3Hashes hashes; /* computed hashes are put here. */
+ PRUint8 hdr[4];
++ PRUint8 dtlsData[8];
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+@@ -9032,10 +9302,35 @@
+ return rv;
+ }
+ }
+- /* We should not include hello_request messages in the handshake hashes */
+- if (ss->ssl3.hs.msg_type != hello_request) {
++ /* We should not include hello_request and hello_verify_request messages
++ * in the handshake hashes */
++ if ((ss->ssl3.hs.msg_type != hello_request) &&
++ (ss->ssl3.hs.msg_type != hello_verify_request)) {
+ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char*) hdr, 4);
+ if (rv != SECSuccess) return rv; /* err code already set. */
++
++ /* Extra data to simulate a complete DTLS handshake fragment */
++ if (IS_DTLS(ss)) {
++ /* Sequence number */
++ dtlsData[0] = MSB(ss->ssl3.hs.recvMessageSeq);
++ dtlsData[1] = LSB(ss->ssl3.hs.recvMessageSeq);
++
++ /* Fragment offset */
++ dtlsData[2] = 0;
++ dtlsData[3] = 0;
++ dtlsData[4] = 0;
++
++ /* Fragment length */
++ dtlsData[5] = (PRUint8)(length >> 16);
++ dtlsData[6] = (PRUint8)(length >> 8);
++ dtlsData[7] = (PRUint8)(length );
++
++ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char*) dtlsData,
++ sizeof(dtlsData));
++ if (rv != SECSuccess) return rv; /* err code already set. */
++ }
++
++ /* The message body */
+ rv = ssl3_UpdateHandshakeHashes(ss, b, length);
+ if (rv != SECSuccess) return rv; /* err code already set. */
+ }
+@@ -9071,6 +9366,14 @@
+ }
+ rv = ssl3_HandleServerHello(ss, b, length);
+ break;
++ case hello_verify_request:
++ if (!IS_DTLS(ss) || ss->sec.isServer) {
++ (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
++ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST);
++ return SECFailure;
++ }
++ rv = dtls_HandleHelloVerifyRequest(ss, b, length);
++ break;
+ case certificate:
+ if (ss->ssl3.hs.may_get_cert_status) {
+ /* If we might get a CertificateStatus then we want to postpone the
+@@ -9169,6 +9472,12 @@
+ PORT_SetError(SSL_ERROR_RX_UNKNOWN_HANDSHAKE);
+ rv = SECFailure;
+ }
++
++ if (IS_DTLS(ss) && (rv == SECSuccess)) {
++ /* Increment the expected sequence number */
++ ss->ssl3.hs.recvMessageSeq++;
++ }
++
+ return rv;
+ }
+
+@@ -9331,6 +9640,7 @@
+ SSL3Opaque hash[MAX_MAC_LENGTH];
+ sslBuffer *plaintext;
+ sslBuffer temp_buf;
++ PRUint64 dtls_seq_num;
+ unsigned int ivLen = 0;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+@@ -9366,6 +9676,39 @@
+ crSpec = ss->ssl3.crSpec;
+ cipher_def = crSpec->cipher_def;
+
++ /*
++ * DTLS relevance checks:
++ * Note that this code currently ignores all out-of-epoch packets,
++ * which means we lose some in the case of rehandshake +
++ * loss/reordering. Since DTLS is explicitly unreliable, this
++ * seems like a good tradeoff for implementation effort and is
++ * consistent with the guidance of RFC 6347 Sections 4.1 and 4.2.4.1
++ */
++ if (IS_DTLS(ss)) {
++ DTLSEpoch epoch = (cText->seq_num.high >> 16) & 0xffff;
++
++ if (crSpec->epoch != epoch) {
++ ssl_ReleaseSpecReadLock(ss);
++ SSL_DBG(("%d: SSL3[%d]: HandleRecord, received packet "
++ "from irrelevant epoch %d", SSL_GETPID(), ss->fd, epoch));
++ /* Silently drop the packet */
++ databuf->len = 0; /* Needed to ensure data not left around */
++ return SECSuccess;
++ }
++
++ dtls_seq_num = (((PRUint64)(cText->seq_num.high & 0xffff)) << 32) |
++ ((PRUint64)cText->seq_num.low);
++
++ if (dtls_RecordGetRecvd(&crSpec->recvdRecords, dtls_seq_num) != 0) {
++ ssl_ReleaseSpecReadLock(ss);
++ SSL_DBG(("%d: SSL3[%d]: HandleRecord, rejecting "
++ "potentially replayed packet", SSL_GETPID(), ss->fd));
++ /* Silently drop the packet */
++ databuf->len = 0; /* Needed to ensure data not left around */
++ return SECSuccess;
++ }
++ }
++
+ if (cipher_def->type == type_block &&
+ crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+ /* Consume the per-record explicit IV. RFC 4346 Section 6.2.3.2 states
+@@ -9487,7 +9830,8 @@
+ /* compute the MAC */
+ rType = cText->type;
+ rv = ssl3_ComputeRecordMAC( crSpec, (PRBool)(!ss->sec.isServer),
+- rType, cText->version, crSpec->read_seq_num,
++ IS_DTLS(ss), rType, cText->version,
++ IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ plaintext->buf, plaintext->len, hash, &hashBytes);
+ if (rv != SECSuccess) {
+ padIsBad = PR_TRUE; /* really macIsBad */
+@@ -9499,19 +9843,27 @@
+ crSpec->mac_size) != 0) {
+ /* must not hold spec lock when calling SSL3_SendAlert. */
+ ssl_ReleaseSpecReadLock(ss);
+- SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
+- /* always log mac error, in case attacker can read server logs. */
+- PORT_SetError(SSL_ERROR_BAD_MAC_READ);
+
+ SSL_DBG(("%d: SSL3[%d]: mac check failed", SSL_GETPID(), ss->fd));
+
+- return SECFailure;
++ if (!IS_DTLS(ss)) {
++ SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
++ /* always log mac error, in case attacker can read server logs. */
++ PORT_SetError(SSL_ERROR_BAD_MAC_READ);
++ return SECFailure;
++ } else {
++ /* Silently drop the packet */
++ databuf->len = 0; /* Needed to ensure data not left around */
++ return SECSuccess;
++ }
+ }
+
++ if (!IS_DTLS(ss)) {
++ ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
++ } else {
++ dtls_RecordSetRecvd(&crSpec->recvdRecords, dtls_seq_num);
++ }
+
+-
+- ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
+-
+ ssl_ReleaseSpecReadLock(ss); /*****************************************/
+
+ /*
+@@ -9615,7 +9967,11 @@
+ rv = ssl3_HandleAlert(ss, databuf);
+ break;
+ case content_handshake:
+- rv = ssl3_HandleHandshake(ss, databuf);
++ if (!IS_DTLS(ss)) {
++ rv = ssl3_HandleHandshake(ss, databuf);
++ } else {
++ rv = dtls_HandleHandshake(ss, databuf);
++ }
+ break;
+ /*
+ case content_application_data is handled before this switch
+@@ -9675,6 +10031,9 @@
+ spec->read_seq_num.high = 0;
+ spec->read_seq_num.low = 0;
+
++ spec->epoch = 0;
++ dtls_InitRecvdRecords(&spec->recvdRecords);
++
+ spec->version = ss->vrange.max;
+ }
+
+@@ -9716,6 +10075,21 @@
+
+ PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
+
++ if (IS_DTLS(ss)) {
++ ss->ssl3.hs.sendMessageSeq = 0;
++ ss->ssl3.hs.recvMessageSeq = 0;
++ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
++ ss->ssl3.hs.rtRetries = 0;
++
++ /* Have to allocate this because ssl_FreeSocket relocates
++ * this structure in DEBUG mode */
++ if (!(ss->ssl3.hs.lastMessageFlight = PORT_New(PRCList)))
++ return SECFailure;
++ ss->ssl3.hs.recvdHighWater = -1;
++ PR_INIT_CLIST(ss->ssl3.hs.lastMessageFlight);
++ dtls_SetMTU(ss, 0); /* Set the MTU to the highest plateau */
++ }
++
+ rv = ssl3_NewHandshakeHashes(ss);
+ if (rv == SECSuccess) {
+ ss->ssl3.initialized = PR_TRUE;
+@@ -9968,6 +10342,11 @@
+ PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
+ return SECFailure;
+ }
++
++ if (IS_DTLS(ss)) {
++ dtls_RehandshakeCleanup(ss);
++ }
++
+ if (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER) {
+ PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
+ return SECFailure;
+@@ -9982,7 +10361,7 @@
+
+ /* start off a new handshake. */
+ rv = (ss->sec.isServer) ? ssl3_SendHelloRequest(ss)
+- : ssl3_SendClientHello(ss);
++ : ssl3_SendClientHello(ss, PR_FALSE);
+
+ ssl_ReleaseXmitBufLock(ss); /**************************************/
+ return rv;
+@@ -10042,6 +10421,17 @@
+ ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
+ ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
+
++ /* Destroy the DTLS data */
++ if (IS_DTLS(ss)) {
++ if (ss->ssl3.hs.lastMessageFlight) {
++ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight);
++ PORT_Free(ss->ssl3.hs.lastMessageFlight);
++ }
++ if (ss->ssl3.hs.recvdFragments.buf) {
++ PORT_Free(ss->ssl3.hs.recvdFragments.buf);
++ }
++ }
++
+ ss->ssl3.initialized = PR_FALSE;
+
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+Index: net/third_party/nss/ssl/sslgathr.c
+===================================================================
+--- net/third_party/nss/ssl/sslgathr.c (revision 127709)
++++ net/third_party/nss/ssl/sslgathr.c (working copy)
+@@ -434,6 +434,8 @@
+ gs->state = GS_INIT;
+ gs->writeOffset = 0;
+ gs->readOffset = 0;
++ gs->dtlsPacketOffset = 0;
++ gs->dtlsPacket.len = 0;
+ status = sslBuffer_Grow(&gs->buf, 4096);
+ return status;
+ }
+@@ -445,6 +447,7 @@
+ if (gs) { /* the PORT_*Free functions check for NULL pointers. */
+ PORT_ZFree(gs->buf.buf, gs->buf.space);
+ PORT_Free(gs->inbuf.buf);
++ PORT_Free(gs->dtlsPacket.buf);
+ }
+ }
+
+Index: net/third_party/nss/ssl/dtls1con.c
+===================================================================
+--- net/third_party/nss/ssl/dtls1con.c (revision 0)
++++ net/third_party/nss/ssl/dtls1con.c (revision 0)
+@@ -0,0 +1,1163 @@
++/*
++ * DTLS Protocol
++ *
++ * ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is the Netscape security libraries.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1994-2000
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ * Eric Rescorla <ekr@rtfm.com>
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++/* $Id: $ */
++
++#include "ssl.h"
++#include "sslimpl.h"
++#include "sslproto.h"
++
++#ifndef PR_ARRAY_SIZE
++#define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
++#endif
++
++static SECStatus dtls_TransmitMessageFlight(sslSocket *ss);
++static void dtls_RetransmitTimerExpiredCb(sslSocket *ss);
++static SECStatus dtls_SendSavedWriteData(sslSocket *ss);
++
++/* -28 adjusts for the IP/UDP header */
++static const PRUint16 COMMON_MTU_VALUES[] = {
++ 1500 - 28, /* Ethernet MTU */
++ 1280 - 28, /* IPv6 minimum MTU */
++ 576 - 28, /* Common assumption */
++ 256 - 28 /* We're in serious trouble now */
++};
++
++#define DTLS_COOKIE_BYTES 32
++
++/* List copied from ssl3con.c:cipherSuites */
++static const ssl3CipherSuite nonDTLSSuites[] = {
++#ifdef NSS_ENABLE_ECC
++ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
++ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
++#endif /* NSS_ENABLE_ECC */
++ TLS_DHE_DSS_WITH_RC4_128_SHA,
++#ifdef NSS_ENABLE_ECC
++ TLS_ECDH_RSA_WITH_RC4_128_SHA,
++ TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
++#endif /* NSS_ENABLE_ECC */
++ SSL_RSA_WITH_RC4_128_MD5,
++ SSL_RSA_WITH_RC4_128_SHA,
++ TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
++ SSL_RSA_EXPORT_WITH_RC4_40_MD5,
++ 0 /* End of list marker */
++};
++
++/* Map back and forth between TLS and DTLS versions in wire format.
++ * Mapping table is:
++ *
++ * TLS DTLS
++ * 1.1 (0302) 1.0 (feff)
++ */
++SSL3ProtocolVersion
++dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv)
++{
++ /* Anything other than TLS 1.1 is an error, so return
++ * the invalid version ffff. */
++ if (tlsv != SSL_LIBRARY_VERSION_TLS_1_1)
++ return 0xffff;
++
++ return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
++}
++
++/* Map known DTLS versions to known TLS versions.
++ * - Invalid versions (< 1.0) return a version of 0
++ * - Versions > known return a version one higher than we know of
++ * to accomodate a theoretically newer version */
++SSL3ProtocolVersion
++dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv)
++{
++ if (MSB(dtlsv) == 0xff) {
++ return 0;
++ }
++
++ if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE)
++ return SSL_LIBRARY_VERSION_TLS_1_1;
++
++ /* Return a fictional higher version than we know of */
++ return SSL_LIBRARY_VERSION_TLS_1_1 + 1;
++}
++
++/* On this socket, Disable non-DTLS cipher suites in the argument's list */
++SECStatus
++ssl3_DisableNonDTLSSuites(sslSocket * ss)
++{
++ const ssl3CipherSuite * suite;
++
++ for (suite = nonDTLSSuites; *suite; ++suite) {
++ SECStatus rv = ssl3_CipherPrefSet(ss, *suite, PR_FALSE);
++
++ PORT_Assert(rv == SECSuccess); /* else is coding error */
++ }
++ return SECSuccess;
++}
++
++/* Allocate a DTLSQueuedMessage.
++ *
++ * Called from dtls_QueueMessage()
++ */
++static DTLSQueuedMessage *
++dtls_AllocQueuedMessage(PRUint16 epoch, SSL3ContentType type,
++ const unsigned char *data, PRUint32 len)
++{
++ DTLSQueuedMessage *msg = NULL;
++
++ msg = PORT_ZAlloc(sizeof(DTLSQueuedMessage));
++ if (!msg)
++ return NULL;
++
++ msg->data = PORT_Alloc(len);
++ if (!msg->data) {
++ PORT_Free(msg);
++ return NULL;
++ }
++ PORT_Memcpy(msg->data, data, len);
++
++ msg->len = len;
++ msg->epoch = epoch;
++ msg->type = type;
++
++ return msg;
++}
++
++/*
++ * Free a handshake message
++ *
++ * Called from dtls_FreeHandshakeMessages()
++ */
++static void
++dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg)
++{
++ if (!msg)
++ return;
++
++ PORT_ZFree(msg->data, msg->len);
++ PORT_Free(msg);
++}
++
++/*
++ * Free a list of handshake messages
++ *
++ * Called from:
++ * dtls_HandleHandshake()
++ * ssl3_DestroySSL3Info()
++ */
++void
++dtls_FreeHandshakeMessages(PRCList *list)
++{
++ PRCList *cur_p;
++
++ while (!PR_CLIST_IS_EMPTY(list)) {
++ cur_p = PR_LIST_TAIL(list);
++ PR_REMOVE_LINK(cur_p);
++ dtls_FreeHandshakeMessage((DTLSQueuedMessage *)cur_p);
++ }
++}
++
++/* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record.
++ * origBuf is the decrypted ssl record content and is expected to contain
++ * complete handshake records
++ * Caller must hold the handshake and RecvBuf locks.
++ *
++ * Note that this code uses msg_len for two purposes:
++ *
++ * (1) To pass the length to ssl3_HandleHandshakeMessage()
++ * (2) To carry the length of a message currently being reassembled
++ *
++ * However, unlike ssl3_HandleHandshake(), it is not used to carry
++ * the state of reassembly (i.e., whether one is in progress). That
++ * is carried in recvdHighWater and recvdFragments.
++ */
++#define OFFSET_BYTE(o) (o/8)
++#define OFFSET_MASK(o) (1 << (o%8))
++
++SECStatus
++dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
++{
++ /* XXX OK for now.
++ * This doesn't work properly with asynchronous certificate validation.
++ * because that returns a WOULDBLOCK error. The current DTLS
++ * applications do not need asynchronous validation, but in the
++ * future we will need to add this.
++ */
++ sslBuffer buf = *origBuf;
++ SECStatus rv = SECSuccess;
++
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++
++ while (buf.len > 0) {
++ PRUint8 type;
++ PRUint32 message_length;
++ PRUint16 message_seq;
++ PRUint32 fragment_offset;
++ PRUint32 fragment_length;
++ PRUint32 offset;
++
++ if (buf.len < 12) {
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
++ rv = SECFailure;
++ break;
++ }
++
++ /* Parse the header */
++ type = buf.buf[0];
++ message_length = (buf.buf[1] << 16) | (buf.buf[2] << 8) | buf.buf[3];
++ message_seq = (buf.buf[4] << 8) | buf.buf[5];
++ fragment_offset = (buf.buf[6] << 16) | (buf.buf[7] << 8) | buf.buf[8];
++ fragment_length = (buf.buf[9] << 16) | (buf.buf[10] << 8) | buf.buf[11];
++
++#define MAX_HANDSHAKE_MSG_LEN 0x1ffff /* 128k - 1 */
++ if (message_length > MAX_HANDSHAKE_MSG_LEN) {
++ (void)ssl3_DecodeError(ss);
++ PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
++ return SECFailure;
++ }
++#undef MAX_HANDSHAKE_MSG_LEN
++
++ buf.buf += 12;
++ buf.len -= 12;
++
++ /* This fragment must be complete */
++ if (buf.len < fragment_length) {
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
++ rv = SECFailure;
++ break;
++ }
++
++ /* Sanity check the packet contents */
++ if ((fragment_length + fragment_offset) > message_length) {
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
++ rv = SECFailure;
++ break;
++ }
++
++ /* There are three ways we could not be ready for this packet.
++ *
++ * 1. It's a partial next message.
++ * 2. It's a partial or complete message beyond the next
++ * 3. It's a message we've already seen
++ *
++ * If it's the complete next message we accept it right away.
++ * This is the common case for short messages
++ */
++ if ((message_seq == ss->ssl3.hs.recvMessageSeq)
++ && (fragment_offset == 0)
++ && (fragment_length == message_length)) {
++ /* Complete next message. Process immediately */
++ ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
++ ss->ssl3.hs.msg_len = message_length;
++
++ /* At this point we are advancing our state machine, so
++ * we can free our last flight of messages */
++ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight);
++ ss->ssl3.hs.recvdHighWater = -1;
++ dtls_CancelTimer(ss);
++
++ /* Reset the timer to the initial value if the retry counter
++ * is 0, per Sec. 4.2.4.1 */
++ if (ss->ssl3.hs.rtRetries == 0) {
++ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
++ }
++
++ rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len);
++ if (rv == SECFailure) {
++ /* Do not attempt to process rest of messages in this record */
++ break;
++ }
++ } else {
++ if (message_seq < ss->ssl3.hs.recvMessageSeq) {
++ /* Case 3: we do an immediate retransmit if we're
++ * in a waiting state*/
++ if (ss->ssl3.hs.rtTimerCb == NULL) {
++ /* Ignore */
++ } else if (ss->ssl3.hs.rtTimerCb ==
++ dtls_RetransmitTimerExpiredCb) {
++ SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected",
++ SSL_GETPID(), ss->fd));
++ /* Check to see if we retransmitted recently. If so,
++ * suppress the triggered retransmit. This avoids
++ * retransmit wars after packet loss.
++ * This is not in RFC 5346 but should be
++ */
++ if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
++ (ss->ssl3.hs.rtTimeoutMs / 4)) {
++ SSL_TRC(30,
++ ("%d: SSL3[%d]: Shortcutting retransmit timer",
++ SSL_GETPID(), ss->fd));
++
++ /* Cancel the timer and call the CB,
++ * which re-arms the timer */
++ dtls_CancelTimer(ss);
++ dtls_RetransmitTimerExpiredCb(ss);
++ rv = SECSuccess;
++ break;
++ } else {
++ SSL_TRC(30,
++ ("%d: SSL3[%d]: We just retransmitted. Ignoring.",
++ SSL_GETPID(), ss->fd));
++ rv = SECSuccess;
++ break;
++ }
++ } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) {
++ /* Retransmit the messages and re-arm the timer
++ * Note that we are not backing off the timer here.
++ * The spec isn't clear and my reasoning is that this
++ * may be a re-ordered packet rather than slowness,
++ * so let's be aggressive. */
++ dtls_CancelTimer(ss);
++ rv = dtls_TransmitMessageFlight(ss);
++ if (rv == SECSuccess) {
++ rv = dtls_StartTimer(ss, dtls_FinishedTimerCb);
++ }
++ if (rv != SECSuccess)
++ return rv;
++ break;
++ }
++ } else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
++ /* Case 2
++ *
++ * Ignore this message. This means we don't handle out of
++ * order complete messages that well, but we're still
++ * compliant and this probably does not happen often
++ *
++ * XXX OK for now. Maybe do something smarter at some point?
++ */
++ } else {
++ /* Case 1
++ *
++ * Buffer the fragment for reassembly
++ */
++ /* Make room for the message */
++ if (ss->ssl3.hs.recvdHighWater == -1) {
++ PRUint32 map_length = OFFSET_BYTE(message_length) + 1;
++
++ rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length);
++ if (rv != SECSuccess)
++ break;
++ /* Make room for the fragment map */
++ rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments,
++ map_length);
++ if (rv != SECSuccess)
++ break;
++
++ /* Reset the reassembly map */
++ ss->ssl3.hs.recvdHighWater = 0;
++ PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0,
++ ss->ssl3.hs.recvdFragments.space);
++ ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
++ ss->ssl3.hs.msg_len = message_length;
++ }
++
++ /* If we have a message length mismatch, abandon the reassembly
++ * in progress and hope that the next retransmit will give us
++ * something sane
++ */
++ if (message_length != ss->ssl3.hs.msg_len) {
++ ss->ssl3.hs.recvdHighWater = -1;
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
++ rv = SECFailure;
++ break;
++ }
++
++ /* Now copy this fragment into the buffer */
++ PORT_Assert((fragment_offset + fragment_length) <=
++ ss->ssl3.hs.msg_body.space);
++ PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset,
++ buf.buf, fragment_length);
++
++ /* This logic is a bit tricky. We have two values for
++ * reassembly state:
++ *
++ * - recvdHighWater contains the highest contiguous number of
++ * bytes received
++ * - recvdFragments contains a bitmask of packets received
++ * above recvdHighWater
++ *
++ * This avoids having to fill in the bitmask in the common
++ * case of adjacent fragments received in sequence
++ */
++ if (fragment_offset <= ss->ssl3.hs.recvdHighWater) {
++ /* Either this is the adjacent fragment or an overlapping
++ * fragment */
++ ss->ssl3.hs.recvdHighWater = fragment_offset +
++ fragment_length;
++ } else {
++ for (offset = fragment_offset;
++ offset < fragment_offset + fragment_length;
++ offset++) {
++ ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] |=
++ OFFSET_MASK(offset);
++ }
++ }
++
++ /* Now figure out the new high water mark if appropriate */
++ for (offset = ss->ssl3.hs.recvdHighWater;
++ offset < ss->ssl3.hs.msg_len; offset++) {
++ /* Note that this loop is not efficient, since it counts
++ * bit by bit. If we have a lot of out-of-order packets,
++ * we should optimize this */
++ if (ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] &
++ OFFSET_MASK(offset)) {
++ ss->ssl3.hs.recvdHighWater++;
++ } else {
++ break;
++ }
++ }
++
++ /* If we have all the bytes, then we are good to go */
++ if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) {
++ ss->ssl3.hs.recvdHighWater = -1;
++
++ rv = ssl3_HandleHandshakeMessage(ss,
++ ss->ssl3.hs.msg_body.buf,
++ ss->ssl3.hs.msg_len);
++ if (rv == SECFailure)
++ break; /* Skip rest of record */
++
++ /* At this point we are advancing our state machine, so
++ * we can free our last flight of messages */
++ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight);
++ dtls_CancelTimer(ss);
++
++ /* If there have been no retries this time, reset the
++ * timer value to the default per Section 4.2.4.1 */
++ if (ss->ssl3.hs.rtRetries == 0) {
++ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
++ }
++ }
++ }
++ }
++
++ buf.buf += fragment_length;
++ buf.len -= fragment_length;
++ }
++
++ origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */
++
++ /* XXX OK for now. In future handle rv == SECWouldBlock safely in order
++ * to deal with asynchronous certificate verification */
++ return rv;
++}
++
++/* Enqueue a message (either handshake or CCS)
++ *
++ * Called from:
++ * dtls_StageHandshakeMessage()
++ * ssl3_SendChangeCipherSpecs()
++ */
++SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
++ const SSL3Opaque *pIn, PRInt32 nIn)
++{
++ SECStatus rv = SECSuccess;
++ DTLSQueuedMessage *msg = NULL;
++
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
++
++ msg = dtls_AllocQueuedMessage(ss->ssl3.cwSpec->epoch, type, pIn, nIn);
++
++ if (!msg) {
++ PORT_SetError(SEC_ERROR_NO_MEMORY);
++ rv = SECFailure;
++ } else {
++ PR_APPEND_LINK(&msg->link, ss->ssl3.hs.lastMessageFlight);
++ }
++
++ return rv;
++}
++
++/* Add DTLS handshake message to the pending queue
++ * Empty the sendBuf buffer.
++ * This function returns SECSuccess or SECFailure, never SECWouldBlock.
++ * Always set sendBuf.len to 0, even when returning SECFailure.
++ *
++ * Called from:
++ * ssl3_AppendHandshakeHeader()
++ * dtls_FlushHandshake()
++ */
++SECStatus
++dtls_StageHandshakeMessage(sslSocket *ss)
++{
++ SECStatus rv = SECSuccess;
++
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
++
++ /* This function is sometimes called when no data is actually to
++ * be staged, so just return SECSuccess. */
++ if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len)
++ return rv;
++
++ rv = dtls_QueueMessage(ss, content_handshake,
++ ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len);
++
++ /* Whether we succeeded or failed, toss the old handshake data. */
++ ss->sec.ci.sendBuf.len = 0;
++ return rv;
++}
++
++/* Enqueue the handshake message in sendBuf (if any) and then
++ * transmit the resulting flight of handshake messages.
++ *
++ * Called from:
++ * ssl3_FlushHandshake()
++ */
++SECStatus
++dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
++{
++ SECStatus rv = SECSuccess;
++
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
++
++ rv = dtls_StageHandshakeMessage(ss);
++ if (rv != SECSuccess)
++ return rv;
++
++ if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) {
++ rv = dtls_TransmitMessageFlight(ss);
++ if (rv != SECSuccess)
++ return rv;
++
++ if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) {
++ ss->ssl3.hs.rtRetries = 0;
++ rv = dtls_StartTimer(ss, dtls_RetransmitTimerExpiredCb);
++ }
++ }
++
++ return rv;
++}
++
++/* The callback for when the retransmit timer expires
++ *
++ * Called from:
++ * dtls_CheckTimer()
++ * dtls_HandleHandshake()
++ */
++static void
++dtls_RetransmitTimerExpiredCb(sslSocket *ss)
++{
++ SECStatus rv = SECFailure;
++
++ ss->ssl3.hs.rtRetries++;
++
++ if (!(ss->ssl3.hs.rtRetries % 3)) {
++ /* If one of the messages was potentially greater than > MTU,
++ * then downgrade. Do this every time we have retransmitted a
++ * message twice, per RFC 6347 Sec. 4.1.1 */
++ dtls_SetMTU(ss, ss->ssl3.hs.maxMessageSent - 1);
++ }
++
++ rv = dtls_TransmitMessageFlight(ss);
++ if (rv == SECSuccess) {
++
++ /* Re-arm the timer */
++ rv = dtls_RestartTimer(ss, PR_TRUE, dtls_RetransmitTimerExpiredCb);
++ }
++
++ if (rv == SECFailure) {
++ /* XXX OK for now. In future maybe signal the stack that we couldn't
++ * transmit. For now, let the read handle any real network errors */
++ }
++}
++
++/* Transmit a flight of handshake messages, stuffing them
++ * into as few records as seems reasonable
++ *
++ * Called from:
++ * dtls_FlushHandshake()
++ * dtls_RetransmitTimerExpiredCb()
++ */
++static SECStatus
++dtls_TransmitMessageFlight(sslSocket *ss)
++{
++ SECStatus rv = SECSuccess;
++ PRCList *msg_p;
++ PRUint16 room_left = ss->ssl3.mtu;
++ PRInt32 sent;
++
++ ssl_GetXmitBufLock(ss);
++ ssl_GetSpecReadLock(ss);
++
++ /* DTLS does not buffer its handshake messages in
++ * ss->pendingBuf, but rather in the lastMessageFlight
++ * structure. This is just a sanity check that
++ * some programming error hasn't inadvertantly
++ * stuffed something in ss->pendingBuf
++ */
++ PORT_Assert(!ss->pendingBuf.len);
++ for (msg_p = PR_LIST_HEAD(ss->ssl3.hs.lastMessageFlight);
++ msg_p != ss->ssl3.hs.lastMessageFlight;
++ msg_p = PR_NEXT_LINK(msg_p)) {
++ DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p;
++
++ /* The logic here is:
++ *
++ * 1. If this is a message that will not fit into the remaining
++ * space, then flush.
++ * 2. If the message will now fit into the remaining space,
++ * encrypt, buffer, and loop.
++ * 3. If the message will not fit, then fragment.
++ *
++ * At the end of the function, flush.
++ */
++ if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) {
++ /* The message will not fit into the remaining space, so flush */
++ rv = dtls_SendSavedWriteData(ss);
++ if (rv != SECSuccess)
++ break;
++
++ room_left = ss->ssl3.mtu;
++ }
++
++ if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) {
++ /* The message will fit, so encrypt and then continue with the
++ * next packet */
++ sent = ssl3_SendRecord(ss, msg->epoch, msg->type,
++ msg->data, msg->len,
++ ssl_SEND_FLAG_FORCE_INTO_BUFFER |
++ ssl_SEND_FLAG_USE_EPOCH);
++ if (sent != msg->len) {
++ rv = SECFailure;
++ if (sent != -1) {
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++ }
++ break;
++ }
++
++ room_left = ss->ssl3.mtu - ss->pendingBuf.len;
++ } else {
++ /* The message will not fit, so fragment.
++ *
++ * XXX OK for now. Arrange to coalesce the last fragment
++ * of this message with the next message if possible.
++ * That would be more efficient.
++ */
++ PRUint32 fragment_offset = 0;
++ unsigned char fragment[DTLS_MAX_MTU]; /* >= than largest
++ * plausible MTU */
++
++ /* Assert that we have already flushed */
++ PORT_Assert(room_left == ss->ssl3.mtu);
++
++ /* Case 3: We now need to fragment this message
++ * DTLS only supports fragmenting handshaking messages */
++ PORT_Assert(msg->type == content_handshake);
++
++ /* The headers consume 12 bytes so the smalles possible
++ * message (i.e., an empty one) is 12 bytes
++ */
++ PORT_Assert(msg->len >= 12);
++
++ while ((fragment_offset + 12) < msg->len) {
++ PRUint32 fragment_len;
++ const unsigned char *content = msg->data + 12;
++ PRUint32 content_len = msg->len - 12;
++
++ /* The reason we use 8 here is that that's the length of
++ * the new DTLS data that we add to the header */
++ fragment_len = PR_MIN(room_left - (SSL3_BUFFER_FUDGE + 8),
++ content_len - fragment_offset);
++ PORT_Assert(fragment_len < DTLS_MAX_MTU - 12);
++ /* Make totally sure that we are within the buffer.
++ * Note that the only way that fragment len could get
++ * adjusted here is if
++ *
++ * (a) we are in release mode so the PORT_Assert is compiled out
++ * (b) either the MTU table is inconsistent with DTLS_MAX_MTU
++ * or ss->ssl3.mtu has become corrupt.
++ */
++ fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12);
++
++ /* Construct an appropriate-sized fragment */
++ /* Type, length, sequence */
++ PORT_Memcpy(fragment, msg->data, 6);
++
++ /* Offset */
++ fragment[6] = (fragment_offset >> 16) & 0xff;
++ fragment[7] = (fragment_offset >> 8) & 0xff;
++ fragment[8] = (fragment_offset) & 0xff;
++
++ /* Fragment length */
++ fragment[9] = (fragment_len >> 16) & 0xff;
++ fragment[10] = (fragment_len >> 8) & 0xff;
++ fragment[11] = (fragment_len) & 0xff;
++
++ PORT_Memcpy(fragment + 12, content + fragment_offset,
++ fragment_len);
++
++ /*
++ * Send the record. We do this in two stages
++ * 1. Encrypt
++ */
++ sent = ssl3_SendRecord(ss, msg->epoch, msg->type,
++ fragment, fragment_len + 12,
++ ssl_SEND_FLAG_FORCE_INTO_BUFFER |
++ ssl_SEND_FLAG_USE_EPOCH);
++ if (sent != (fragment_len + 12)) {
++ rv = SECFailure;
++ if (sent != -1) {
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++ }
++ break;
++ }
++
++ /* 2. Flush */
++ rv = dtls_SendSavedWriteData(ss);
++ if (rv != SECSuccess)
++ break;
++
++ fragment_offset += fragment_len;
++ }
++ }
++ }
++
++ /* Finally, we need to flush */
++ if (rv == SECSuccess)
++ rv = dtls_SendSavedWriteData(ss);
++
++ /* Give up the locks */
++ ssl_ReleaseSpecReadLock(ss);
++ ssl_ReleaseXmitBufLock(ss);
++
++ return rv;
++}
++
++/* Flush the data in the pendingBuf and update the max message sent
++ * so we can adjust the MTU estimate if we need to.
++ * Wrapper for ssl_SendSavedWriteData.
++ *
++ * Called from dtls_TransmitMessageFlight()
++ */
++static
++SECStatus dtls_SendSavedWriteData(sslSocket *ss)
++{
++ PRInt32 sent;
++
++ sent = ssl_SendSavedWriteData(ss);
++ if (sent < 0)
++ return SECFailure;
++
++ /* We should always have complete writes b/c datagram sockets
++ * don't really block */
++ if (ss->pendingBuf.len > 0) {
++ ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE);
++ return SECFailure;
++ }
++
++ /* Update the largest message sent so we can adjust the MTU
++ * estimate if necessary */
++ if (sent > ss->ssl3.hs.maxMessageSent)
++ ss->ssl3.hs.maxMessageSent = sent;
++
++ return SECSuccess;
++}
++
++/* Compress, MAC, encrypt a DTLS record. Allows specification of
++ * the epoch using epoch value. If use_epoch is PR_TRUE then
++ * we use the provided epoch. If use_epoch is PR_FALSE then
++ * whatever the current value is in effect is used.
++ *
++ * Called from ssl3_SendRecord()
++ */
++SECStatus
++dtls_CompressMACEncryptRecord(sslSocket * ss,
++ DTLSEpoch epoch,
++ PRBool use_epoch,
++ SSL3ContentType type,
++ const SSL3Opaque * pIn,
++ PRUint32 contentLen,
++ sslBuffer * wrBuf)
++{
++ SECStatus rv = SECFailure;
++ ssl3CipherSpec * cwSpec;
++
++ ssl_GetSpecReadLock(ss); /********************************/
++
++ /* The reason for this switch-hitting code is that we might have
++ * a flight of records spanning an epoch boundary, e.g.,
++ *
++ * ClientKeyExchange (epoch = 0)
++ * ChangeCipherSpec (epoch = 0)
++ * Finished (epoch = 1)
++ *
++ * Thus, each record needs a different cipher spec. The information
++ * about which epoch to use is carried with the record.
++ */
++ if (use_epoch) {
++ if (ss->ssl3.cwSpec->epoch == epoch)
++ cwSpec = ss->ssl3.cwSpec;
++ else if (ss->ssl3.pwSpec->epoch == epoch)
++ cwSpec = ss->ssl3.pwSpec;
++ else
++ cwSpec = NULL;
++ } else {
++ cwSpec = ss->ssl3.cwSpec;
++ }
++
++ if (cwSpec) {
++ rv = ssl3_CompressMACEncryptRecord(cwSpec, ss->sec.isServer, PR_TRUE,
++ type, pIn, contentLen, wrBuf);
++ } else {
++ PR_NOT_REACHED("Couldn't find a cipher spec matching epoch");
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++ }
++ ssl_ReleaseSpecReadLock(ss); /************************************/
++
++ return rv;
++}
++
++/* Start a timer
++ *
++ * Called from:
++ * dtls_HandleHandshake()
++ * dtls_FlushHAndshake()
++ * dtls_RestartTimer()
++ */
++SECStatus
++dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb)
++{
++ PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
++
++ ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
++ ss->ssl3.hs.rtTimerCb = cb;
++
++ return SECSuccess;
++}
++
++/* Restart a timer with optional backoff
++ *
++ * Called from dtls_RetransmitTimerExpiredCb()
++ */
++SECStatus
++dtls_RestartTimer(sslSocket *ss, PRBool backoff, DTLSTimerCb cb)
++{
++ if (backoff) {
++ ss->ssl3.hs.rtTimeoutMs *= 2;
++ if (ss->ssl3.hs.rtTimeoutMs > MAX_DTLS_TIMEOUT_MS)
++ ss->ssl3.hs.rtTimeoutMs = MAX_DTLS_TIMEOUT_MS;
++ }
++
++ return dtls_StartTimer(ss, cb);
++}
++
++/* Cancel a pending timer
++ *
++ * Called from:
++ * dtls_HandleHandshake()
++ * dtls_CheckTimer()
++ */
++void
++dtls_CancelTimer(sslSocket *ss)
++{
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
++
++ ss->ssl3.hs.rtTimerCb = NULL;
++}
++
++/* Check the pending timer and fire the callback if it expired
++ *
++ * Called from ssl3_GatherCompleteHandshake()
++ */
++void
++dtls_CheckTimer(sslSocket *ss)
++{
++ if (!ss->ssl3.hs.rtTimerCb)
++ return;
++
++ if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
++ PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) {
++ /* Timer has expired */
++ DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb;
++
++ /* Cancel the timer so that we can call the CB safely */
++ dtls_CancelTimer(ss);
++
++ /* Now call the CB */
++ cb(ss);
++ }
++}
++
++/* The callback to fire when the holddown timer for the Finished
++ * message expires and we can delete it
++ *
++ * Called from dtls_CheckTimer()
++ */
++void
++dtls_FinishedTimerCb(sslSocket *ss)
++{
++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
++}
++
++/* Cancel the Finished hold-down timer and destroy the
++ * pending cipher spec. Note that this means that
++ * successive rehandshakes will fail if the Finished is
++ * lost.
++ *
++ * XXX OK for now. Figure out how to handle the combination
++ * of Finished lost and rehandshake
++ */
++void
++dtls_RehandshakeCleanup(sslSocket *ss)
++{
++ dtls_CancelTimer(ss);
++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
++ ss->ssl3.hs.sendMessageSeq = 0;
++}
++
++/* Set the MTU to the next step less than or equal to the
++ * advertised value. Also used to downgrade the MTU by
++ * doing dtls_SetMTU(ss, biggest packet set).
++ *
++ * Passing 0 means set this to the largest MTU known
++ * (effectively resetting the PMTU backoff value).
++ *
++ * Called by:
++ * ssl3_InitState()
++ * dtls_RetransmitTimerExpiredCb()
++ */
++void
++dtls_SetMTU(sslSocket *ss, PRUint16 advertised)
++{
++ int i;
++
++ if (advertised == 0) {
++ ss->ssl3.mtu = COMMON_MTU_VALUES[0];
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
++ return;
++ }
++
++ for (i = 0; i < PR_ARRAY_SIZE(COMMON_MTU_VALUES); i++) {
++ if (COMMON_MTU_VALUES[i] <= advertised) {
++ ss->ssl3.mtu = COMMON_MTU_VALUES[i];
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
++ return;
++ }
++ }
++
++ /* Fallback */
++ ss->ssl3.mtu = COMMON_MTU_VALUES[PR_ARRAY_SIZE(COMMON_MTU_VALUES)-1];
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
++}
++
++/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a
++ * DTLS hello_verify_request
++ * Caller must hold Handshake and RecvBuf locks.
++ */
++SECStatus
++dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
++{
++ int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST;
++ SECStatus rv;
++ PRInt32 temp;
++ SECItem cookie = {siBuffer, NULL, 0};
++ SSL3AlertDescription desc = illegal_parameter;
++
++ SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake",
++ SSL_GETPID(), ss->fd));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++
++ if (ss->ssl3.hs.ws != wait_server_hello) {
++ errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST;
++ desc = unexpected_message;
++ goto alert_loser;
++ }
++
++ /* The version */
++ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
++ if (temp < 0) {
++ goto loser; /* alert has been sent */
++ }
++
++ if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) {
++ /* Note: this will need adjustment for DTLS 1.2 per Section 4.2.1 */
++ goto alert_loser;
++ }
++
++ /* The cookie */
++ rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length);
++ if (rv != SECSuccess) {
++ goto loser; /* alert has been sent */
++ }
++ if (cookie.len > DTLS_COOKIE_BYTES) {
++ desc = decode_error;
++ goto alert_loser; /* malformed. */
++ }
++
++ PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len);
++ ss->ssl3.hs.cookieLen = cookie.len;
++
++
++ ssl_GetXmitBufLock(ss); /*******************************/
++
++ /* Now re-send the client hello */
++ rv = ssl3_SendClientHello(ss, PR_TRUE);
++
++ ssl_ReleaseXmitBufLock(ss); /*******************************/
++
++ if (rv == SECSuccess)
++ return rv;
++
++alert_loser:
++ (void)SSL3_SendAlert(ss, alert_fatal, desc);
++
++loser:
++ errCode = ssl_MapLowLevelError(errCode);
++ return SECFailure;
++}
++
++/* Initialize the DTLS anti-replay window
++ *
++ * Called from:
++ * ssl3_SetupPendingCipherSpec()
++ * ssl3_InitCipherSpec()
++ */
++void
++dtls_InitRecvdRecords(DTLSRecvdRecords *records)
++{
++ PORT_Memset(records->data, 0, sizeof(records->data));
++ records->left = 0;
++ records->right = DTLS_RECVD_RECORDS_WINDOW - 1;
++}
++
++/*
++ * Has this DTLS record been received? Return values are:
++ * -1 -- out of range to the left
++ * 0 -- not received yet
++ * 1 -- replay
++ *
++ * Called from: dtls_HandleRecord()
++ */
++int
++dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq)
++{
++ PRUint64 offset;
++
++ /* Out of range to the left */
++ if (seq < records->left) {
++ return -1;
++ }
++
++ /* Out of range to the right; since we advance the window on
++ * receipt, that means that this packet has not been received
++ * yet */
++ if (seq > records->right)
++ return 0;
++
++ offset = seq % DTLS_RECVD_RECORDS_WINDOW;
++
++ return !!(records->data[offset / 8] & (1 << (offset % 8)));
++}
++
++/* Update the DTLS anti-replay window
++ *
++ * Called from ssl3_HandleRecord()
++ */
++void
++dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq)
++{
++ PRUint64 offset;
++
++ if (seq < records->left)
++ return;
++
++ if (seq > records->right) {
++ PRUint64 new_left;
++ PRUint64 new_right;
++ PRUint64 right;
++
++ /* Slide to the right; this is the tricky part
++ *
++ * 1. new_top is set to have room for seq, on the
++ * next byte boundary by setting the right 8
++ * bits of seq
++ * 2. new_left is set to compensate.
++ * 3. Zero all bits between top and new_top. Since
++ * this is a ring, this zeroes everything as-yet
++ * unseen. Because we always operate on byte
++ * boundaries, we can zero one byte at a time
++ */
++ new_right = seq | 0x07;
++ new_left = (new_right - DTLS_RECVD_RECORDS_WINDOW) + 1;
++
++ for (right = records->right + 8; right <= new_right; right += 8) {
++ offset = right % DTLS_RECVD_RECORDS_WINDOW;
++ records->data[offset / 8] = 0;
++ }
++
++ records->right = new_right;
++ records->left = new_left;
++ }
++
++ offset = seq % DTLS_RECVD_RECORDS_WINDOW;
++
++ records->data[offset / 8] |= (1 << (offset % 8));
++}
++
++SECStatus
++DTLS_GetTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
++{
++ sslSocket * ss = NULL;
++ PRIntervalTime elapsed;
++ PRIntervalTime desired;
++
++ ss = ssl_FindSocket(socket);
++
++ if (!ss)
++ return SECFailure;
++
++ if (!IS_DTLS(ss))
++ return SECFailure;
++
++ if (!ss->ssl3.hs.rtTimerCb)
++ return SECFailure;
++
++ elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted;
++ desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs);
++ if (elapsed > desired) {
++ /* Timer expired */
++ *timeout = PR_INTERVAL_NO_WAIT;
++ } else {
++ *timeout = desired - elapsed;
++ }
++
++ return SECSuccess;
++}
+
+Property changes on: net/third_party/nss/ssl/dtls1con.c
+___________________________________________________________________
+Added: svn:eol-style
+ + LF
+
+Index: net/third_party/nss/ssl/sslproto.h
+===================================================================
+--- net/third_party/nss/ssl/sslproto.h (revision 127709)
++++ net/third_party/nss/ssl/sslproto.h (working copy)
+@@ -49,10 +49,15 @@
+ #define SSL_LIBRARY_VERSION_3_0 0x0300
+ #define SSL_LIBRARY_VERSION_TLS_1_0 0x0301
+ #define SSL_LIBRARY_VERSION_TLS_1_1 0x0302
++/* Note: this is the internal format, not the wire format */
++#define SSL_LIBRARY_VERSION_DTLS_1_0 0x0302
+
+ /* deprecated old name */
+ #define SSL_LIBRARY_VERSION_3_1_TLS SSL_LIBRARY_VERSION_TLS_1_0
+
++/* The DTLS version used in the spec */
++#define SSL_LIBRARY_VERSION_DTLS_1_0_WIRE ((~0x0100) & 0xffff)
++
+ /* Header lengths of some of the messages */
+ #define SSL_HL_ERROR_HBYTES 3
+ #define SSL_HL_CLIENT_HELLO_HBYTES 9
+Index: net/third_party/nss/ssl/sslt.h
+===================================================================
+--- net/third_party/nss/ssl/sslt.h (revision 127709)
++++ net/third_party/nss/ssl/sslt.h (working copy)
+@@ -190,7 +190,8 @@
+ } SSLCipherSuiteInfo;
+
+ typedef enum {
+- ssl_variant_stream = 0
++ ssl_variant_stream = 0,
++ ssl_variant_datagram = 1
+ } SSLProtocolVariant;
+
+ typedef struct SSLVersionRangeStr {
« no previous file with comments | « net/third_party/nss/patches/applypatches.sh ('k') | net/third_party/nss/ssl.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698