| Index: net/third_party/nss/ssl/sslnonce.c
|
| diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c
|
| index 28ad36435ccf1e3f8c6581a63fb7d5f63c0fbb4e..99591cc1488ce1e2875ca82fda456e38cc01bb30 100644
|
| --- a/net/third_party/nss/ssl/sslnonce.c
|
| +++ b/net/third_party/nss/ssl/sslnonce.c
|
| @@ -1,5 +1,5 @@
|
| -/*
|
| - * This file implements the CLIENT Session ID cache.
|
| +/*
|
| + * This file implements the CLIENT Session ID cache.
|
| *
|
| * This Source Code Form is subject to the terms of the Mozilla Public
|
| * License, v. 2.0. If a copy of the MPL was not distributed with this
|
| @@ -22,18 +22,18 @@ PRUint32 ssl_sid_timeout = 100;
|
| PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
|
|
|
| static sslSessionID *cache = NULL;
|
| -static PZLock * cacheLock = NULL;
|
| +static PZLock *cacheLock = NULL;
|
|
|
| /* sids can be in one of 4 states:
|
| *
|
| - * never_cached, created, but not yet put into cache.
|
| - * in_client_cache, in the client cache's linked list.
|
| - * in_server_cache, entry came from the server's cache file.
|
| - * invalid_cache has been removed from the cache.
|
| + * never_cached, created, but not yet put into cache.
|
| + * in_client_cache, in the client cache's linked list.
|
| + * in_server_cache, entry came from the server's cache file.
|
| + * invalid_cache has been removed from the cache.
|
| */
|
|
|
| -#define LOCK_CACHE lock_cache()
|
| -#define UNLOCK_CACHE PZ_Unlock(cacheLock)
|
| +#define LOCK_CACHE lock_cache()
|
| +#define UNLOCK_CACHE PZ_Unlock(cacheLock)
|
|
|
| static PRCallOnceType lockOnce;
|
|
|
| @@ -41,7 +41,7 @@ static PRCallOnceType lockOnce;
|
| * the session cache locks on shutdown and resets them to their initial
|
| * state. */
|
| static SECStatus
|
| -FreeSessionCacheLocks(void* appData, void* nssData)
|
| +FreeSessionCacheLocks(void *appData, void *nssData)
|
| {
|
| static const PRCallOnceType pristineCallOnce;
|
| SECStatus rv;
|
| @@ -92,11 +92,12 @@ InitSessionCacheLocks(void)
|
| }
|
|
|
| SECStatus
|
| -ssl_InitSessionCacheLocks(void)
|
| +ssl_InitSessionCacheLocks()
|
| {
|
| return (PR_SUCCESS ==
|
| - PR_CallOnce(&lockOnce, InitSessionCacheLocks)) ?
|
| - SECSuccess : SECFailure;
|
| + PR_CallOnce(&lockOnce, InitSessionCacheLocks))
|
| + ? SECSuccess
|
| + : SECFailure;
|
| }
|
|
|
| static void
|
| @@ -118,8 +119,8 @@ ssl_DestroySID(sslSessionID *sid)
|
| PORT_Assert(sid->cached != in_client_cache);
|
|
|
| if (sid->version < SSL_LIBRARY_VERSION_3_0) {
|
| - SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
|
| - SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
|
| + SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
|
| + SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
|
| } else {
|
| if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
|
| SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
|
| @@ -128,26 +129,26 @@ ssl_DestroySID(sslSessionID *sid)
|
| if (sid->u.ssl3.srvName.data) {
|
| SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
|
| }
|
| - if (sid->u.ssl3.originalHandshakeHash.data) {
|
| - SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
|
| - }
|
| if (sid->u.ssl3.signedCertTimestamps.data) {
|
| SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
|
| }
|
| + if (sid->u.ssl3.originalHandshakeHash.data) {
|
| + SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
|
| + }
|
|
|
| if (sid->u.ssl3.lock) {
|
| - NSSRWLock_Destroy(sid->u.ssl3.lock);
|
| + PR_DestroyRWLock(sid->u.ssl3.lock);
|
| }
|
| }
|
|
|
| if (sid->peerID != NULL)
|
| - PORT_Free((void *)sid->peerID); /* CONST */
|
| + PORT_Free((void *)sid->peerID); /* CONST */
|
|
|
| if (sid->urlSvrName != NULL)
|
| - PORT_Free((void *)sid->urlSvrName); /* CONST */
|
| + PORT_Free((void *)sid->urlSvrName); /* CONST */
|
|
|
| - if ( sid->peerCert ) {
|
| - CERT_DestroyCertificate(sid->peerCert);
|
| + if (sid->peerCert) {
|
| + CERT_DestroyCertificate(sid->peerCert);
|
| }
|
| for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
|
| CERT_DestroyCertificate(sid->peerCertChain[i]);
|
| @@ -156,33 +157,33 @@ ssl_DestroySID(sslSessionID *sid)
|
| SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
|
| }
|
|
|
| - if ( sid->localCert ) {
|
| - CERT_DestroyCertificate(sid->localCert);
|
| + if (sid->localCert) {
|
| + CERT_DestroyCertificate(sid->localCert);
|
| }
|
| -
|
| +
|
| PORT_ZFree(sid, sizeof(sslSessionID));
|
| }
|
|
|
| /* BEWARE: This function gets called for both client and server SIDs !!
|
| - * Decrement reference count, and
|
| - * free sid if ref count is zero, and sid is not in the cache.
|
| - * Does NOT remove from the cache first.
|
| + * Decrement reference count, and
|
| + * free sid if ref count is zero, and sid is not in the cache.
|
| + * Does NOT remove from the cache first.
|
| * If the sid is still in the cache, it is left there until next time
|
| * the cache list is traversed.
|
| */
|
| -static void
|
| +static void
|
| ssl_FreeLockedSID(sslSessionID *sid)
|
| {
|
| PORT_Assert(sid->references >= 1);
|
| if (--sid->references == 0) {
|
| - ssl_DestroySID(sid);
|
| + ssl_DestroySID(sid);
|
| }
|
| }
|
|
|
| /* BEWARE: This function gets called for both client and server SIDs !!
|
| - * Decrement reference count, and
|
| - * free sid if ref count is zero, and sid is not in the cache.
|
| - * Does NOT remove from the cache first.
|
| + * Decrement reference count, and
|
| + * free sid if ref count is zero, and sid is not in the cache.
|
| + * Does NOT remove from the cache first.
|
| * These locks are necessary because the sid _might_ be in the cache list.
|
| */
|
| void
|
| @@ -202,57 +203,54 @@ ssl_FreeSID(sslSessionID *sid)
|
| */
|
|
|
| sslSessionID *
|
| -ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
|
| - const char * urlSvrName)
|
| +ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
|
| + const char *urlSvrName)
|
| {
|
| sslSessionID **sidp;
|
| - sslSessionID * sid;
|
| - PRUint32 now;
|
| + sslSessionID *sid;
|
| + PRUint32 now;
|
|
|
| if (!urlSvrName)
|
| - return NULL;
|
| + return NULL;
|
| now = ssl_Time();
|
| LOCK_CACHE;
|
| sidp = &cache;
|
| while ((sid = *sidp) != 0) {
|
| - PORT_Assert(sid->cached == in_client_cache);
|
| - PORT_Assert(sid->references >= 1);
|
| -
|
| - SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
|
| -
|
| - if (sid->expirationTime < now) {
|
| - /*
|
| - ** This session-id timed out.
|
| - ** Don't even care who it belongs to, blow it out of our cache.
|
| - */
|
| - SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
|
| - now - sid->creationTime, sid->references));
|
| -
|
| - *sidp = sid->next; /* delink it from the list. */
|
| - sid->cached = invalid_cache; /* mark not on list. */
|
| - ssl_FreeLockedSID(sid); /* drop ref count, free. */
|
| - } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
|
| - (sid->port == port) && /* server port matches */
|
| - /* proxy (peerID) matches */
|
| - (((peerID == NULL) && (sid->peerID == NULL)) ||
|
| - ((peerID != NULL) && (sid->peerID != NULL) &&
|
| - PORT_Strcmp(sid->peerID, peerID) == 0)) &&
|
| - /* is cacheable */
|
| - (sid->version < SSL_LIBRARY_VERSION_3_0 ||
|
| - sid->u.ssl3.keys.resumable) &&
|
| - /* server hostname matches. */
|
| - (sid->urlSvrName != NULL) &&
|
| - ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
|
| - ((sid->peerCert != NULL) && (SECSuccess ==
|
| - CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
|
| - ) {
|
| - /* Hit */
|
| - sid->lastAccessTime = now;
|
| - sid->references++;
|
| - break;
|
| - } else {
|
| - sidp = &sid->next;
|
| - }
|
| + PORT_Assert(sid->cached == in_client_cache);
|
| + PORT_Assert(sid->references >= 1);
|
| +
|
| + SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
|
| +
|
| + if (sid->expirationTime < now) {
|
| + /*
|
| + ** This session-id timed out.
|
| + ** Don't even care who it belongs to, blow it out of our cache.
|
| + */
|
| + SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
|
| + now - sid->creationTime, sid->references));
|
| +
|
| + *sidp = sid->next; /* delink it from the list. */
|
| + sid->cached = invalid_cache; /* mark not on list. */
|
| + ssl_FreeLockedSID(sid); /* drop ref count, free. */
|
| + } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
|
| + (sid->port == port) && /* server port matches */
|
| + /* proxy (peerID) matches */
|
| + (((peerID == NULL) && (sid->peerID == NULL)) ||
|
| + ((peerID != NULL) && (sid->peerID != NULL) &&
|
| + PORT_Strcmp(sid->peerID, peerID) == 0)) &&
|
| + /* is cacheable */
|
| + (sid->version < SSL_LIBRARY_VERSION_3_0 ||
|
| + sid->u.ssl3.keys.resumable) &&
|
| + /* server hostname matches. */
|
| + (sid->urlSvrName != NULL) &&
|
| + (0 == PORT_Strcmp(urlSvrName, sid->urlSvrName))) {
|
| + /* Hit */
|
| + sid->lastAccessTime = now;
|
| + sid->references++;
|
| + break;
|
| + } else {
|
| + sidp = &sid->next;
|
| + }
|
| }
|
| UNLOCK_CACHE;
|
| return sid;
|
| @@ -262,19 +260,19 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
|
| ** Add an sid to the cache or return a previously cached entry to the cache.
|
| ** Although this is static, it is called via ss->sec.cache().
|
| */
|
| -static void
|
| +static void
|
| CacheSID(sslSessionID *sid)
|
| {
|
| - PRUint32 expirationPeriod;
|
| + PRUint32 expirationPeriod;
|
|
|
| PORT_Assert(sid->cached == never_cached);
|
|
|
| SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
|
| - "time=%x cached=%d",
|
| - sid, sid->cached, sid->addr.pr_s6_addr32[0],
|
| - sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
|
| - sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
|
| - sid->cached));
|
| + "time=%x cached=%d",
|
| + sid, sid->cached, sid->addr.pr_s6_addr32[0],
|
| + sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
|
| + sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
|
| + sid->cached));
|
|
|
| if (!sid->urlSvrName) {
|
| /* don't cache this SID because it can never be matched */
|
| @@ -283,41 +281,41 @@ CacheSID(sslSessionID *sid)
|
|
|
| /* XXX should be different trace for version 2 vs. version 3 */
|
| if (sid->version < SSL_LIBRARY_VERSION_3_0) {
|
| - expirationPeriod = ssl_sid_timeout;
|
| - PRINT_BUF(8, (0, "sessionID:",
|
| - sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
|
| - PRINT_BUF(8, (0, "masterKey:",
|
| - sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
|
| - PRINT_BUF(8, (0, "cipherArg:",
|
| - sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
|
| + expirationPeriod = ssl_sid_timeout;
|
| + PRINT_BUF(8, (0, "sessionID:",
|
| + sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
|
| + PRINT_BUF(8, (0, "masterKey:",
|
| + sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
|
| + PRINT_BUF(8, (0, "cipherArg:",
|
| + sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
|
| } else {
|
| - if (sid->u.ssl3.sessionIDLength == 0 &&
|
| - sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
|
| - return;
|
| -
|
| - /* Client generates the SessionID if this was a stateless resume. */
|
| - if (sid->u.ssl3.sessionIDLength == 0) {
|
| - SECStatus rv;
|
| - rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
|
| - SSL3_SESSIONID_BYTES);
|
| - if (rv != SECSuccess)
|
| - return;
|
| - sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
|
| - }
|
| - expirationPeriod = ssl3_sid_timeout;
|
| - PRINT_BUF(8, (0, "sessionID:",
|
| - sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
|
| -
|
| - sid->u.ssl3.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
|
| - if (!sid->u.ssl3.lock) {
|
| - return;
|
| - }
|
| + if (sid->u.ssl3.sessionIDLength == 0 &&
|
| + sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
|
| + return;
|
| +
|
| + /* Client generates the SessionID if this was a stateless resume. */
|
| + if (sid->u.ssl3.sessionIDLength == 0) {
|
| + SECStatus rv;
|
| + rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
|
| + SSL3_SESSIONID_BYTES);
|
| + if (rv != SECSuccess)
|
| + return;
|
| + sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
|
| + }
|
| + expirationPeriod = ssl3_sid_timeout;
|
| + PRINT_BUF(8, (0, "sessionID:",
|
| + sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
|
| +
|
| + sid->u.ssl3.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, NULL);
|
| + if (!sid->u.ssl3.lock) {
|
| + return;
|
| + }
|
| }
|
| PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
|
| if (!sid->creationTime)
|
| - sid->lastAccessTime = sid->creationTime = ssl_Time();
|
| + sid->lastAccessTime = sid->creationTime = ssl_Time();
|
| if (!sid->expirationTime)
|
| - sid->expirationTime = sid->creationTime + expirationPeriod;
|
| + sid->expirationTime = sid->creationTime + expirationPeriod;
|
|
|
| /*
|
| * Put sid into the cache. Bump reference count to indicate that
|
| @@ -327,12 +325,12 @@ CacheSID(sslSessionID *sid)
|
| LOCK_CACHE;
|
| sid->references++;
|
| sid->cached = in_client_cache;
|
| - sid->next = cache;
|
| - cache = sid;
|
| + sid->next = cache;
|
| + cache = sid;
|
| UNLOCK_CACHE;
|
| }
|
|
|
| -/*
|
| +/*
|
| * If sid "zap" is in the cache,
|
| * removes sid from cache, and decrements reference count.
|
| * Caller must hold cache lock.
|
| @@ -344,43 +342,43 @@ UncacheSID(sslSessionID *zap)
|
| sslSessionID *sid;
|
|
|
| if (zap->cached != in_client_cache) {
|
| - return;
|
| + return;
|
| }
|
|
|
| - SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
|
| - "time=%x cipher=%d",
|
| - zap, zap->cached, zap->addr.pr_s6_addr32[0],
|
| - zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
|
| - zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
|
| - zap->u.ssl2.cipherType));
|
| + SSL_TRC(8, ("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
|
| + "time=%x cipher=%d",
|
| + zap, zap->cached, zap->addr.pr_s6_addr32[0],
|
| + zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
|
| + zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
|
| + zap->u.ssl2.cipherType));
|
| if (zap->version < SSL_LIBRARY_VERSION_3_0) {
|
| - PRINT_BUF(8, (0, "sessionID:",
|
| - zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
|
| - PRINT_BUF(8, (0, "masterKey:",
|
| - zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
|
| - PRINT_BUF(8, (0, "cipherArg:",
|
| - zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
|
| + PRINT_BUF(8, (0, "sessionID:",
|
| + zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
|
| + PRINT_BUF(8, (0, "masterKey:",
|
| + zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
|
| + PRINT_BUF(8, (0, "cipherArg:",
|
| + zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
|
| }
|
|
|
| /* See if it's in the cache, if so nuke it */
|
| while ((sid = *sidp) != 0) {
|
| - if (sid == zap) {
|
| - /*
|
| - ** Bingo. Reduce reference count by one so that when
|
| - ** everyone is done with the sid we can free it up.
|
| - */
|
| - *sidp = zap->next;
|
| - zap->cached = invalid_cache;
|
| - ssl_FreeLockedSID(zap);
|
| - return;
|
| - }
|
| - sidp = &sid->next;
|
| + if (sid == zap) {
|
| + /*
|
| + ** Bingo. Reduce reference count by one so that when
|
| + ** everyone is done with the sid we can free it up.
|
| + */
|
| + *sidp = zap->next;
|
| + zap->cached = invalid_cache;
|
| + ssl_FreeLockedSID(zap);
|
| + return;
|
| + }
|
| + sidp = &sid->next;
|
| }
|
| }
|
|
|
| /* If sid "zap" is in the cache,
|
| * removes sid from cache, and decrements reference count.
|
| - * Although this function is static, it is called externally via
|
| + * Although this function is static, it is called externally via
|
| * ss->sec.uncache().
|
| */
|
| static void
|
| @@ -389,19 +387,18 @@ LockAndUncacheSID(sslSessionID *zap)
|
| LOCK_CACHE;
|
| UncacheSID(zap);
|
| UNLOCK_CACHE;
|
| -
|
| }
|
|
|
| /* choose client or server cache functions for this sslsocket. */
|
| -void
|
| +void
|
| ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
|
| {
|
| if (sec->isServer) {
|
| - sec->cache = ssl_sid_cache;
|
| - sec->uncache = ssl_sid_uncache;
|
| + sec->cache = ssl_sid_cache;
|
| + sec->uncache = ssl_sid_uncache;
|
| } else {
|
| - sec->cache = CacheSID;
|
| - sec->uncache = LockAndUncacheSID;
|
| + sec->cache = CacheSID;
|
| + sec->uncache = LockAndUncacheSID;
|
| }
|
| }
|
|
|
| @@ -410,8 +407,8 @@ void
|
| SSL_ClearSessionCache(void)
|
| {
|
| LOCK_CACHE;
|
| - while(cache != NULL)
|
| - UncacheSID(cache);
|
| + while (cache != NULL)
|
| + UncacheSID(cache);
|
| UNLOCK_CACHE;
|
| }
|
|
|
| @@ -421,7 +418,7 @@ ssl_Time(void)
|
| {
|
| PRUint32 myTime;
|
| #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
|
| - myTime = time(NULL); /* accurate until the year 2038. */
|
| + myTime = time(NULL); /* accurate until the year 2038. */
|
| #else
|
| /* portable, but possibly slower */
|
| PRTime now;
|
| @@ -450,11 +447,11 @@ ssl3_SetSIDSessionTicket(sslSessionID *sid,
|
| * yet, so no locking is needed.
|
| */
|
| if (sid->u.ssl3.lock) {
|
| - NSSRWLock_LockWrite(sid->u.ssl3.lock);
|
| - if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
|
| - SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
|
| - PR_FALSE);
|
| - }
|
| + PR_RWLock_Wlock(sid->u.ssl3.lock);
|
| + if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
|
| + SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
|
| + PR_FALSE);
|
| + }
|
| }
|
|
|
| PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
|
| @@ -465,6 +462,6 @@ ssl3_SetSIDSessionTicket(sslSessionID *sid,
|
| newSessionTicket->ticket.len = 0;
|
|
|
| if (sid->u.ssl3.lock) {
|
| - NSSRWLock_UnlockWrite(sid->u.ssl3.lock);
|
| + PR_RWLock_Unlock(sid->u.ssl3.lock);
|
| }
|
| }
|
|
|