Mixe for Privacy and Anonymity in the Internet
CALastMixB.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2006, The JAP-Team 
00003  * All rights reserved.
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions are met:
00006  *
00007  *   - Redistributions of source code must retain the above copyright notice, 
00008  *     this list of conditions and the following disclaimer.
00009  *
00010  *   - Redistributions in binary form must reproduce the above copyright
00011  *     notice, this list of conditions and the following disclaimer in the
00012  *     documentation and/or other materials provided with the distribution.
00013  *
00014  *   - Neither the name of the University of Technology Dresden, Germany nor
00015  *     the names of its contributors may be used to endorse or promote
00016  *     products derived from this software without specific prior written
00017  *     permission. 
00018  *
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00022  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00023  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
00024  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE
00031  */
00032 #include "../StdAfx.h"
00033 #ifndef ONLY_LOCAL_PROXY
00034 #include "CALastMixB.hpp"
00035 #include "typedefsb.hpp"
00036 #include "CALastMixBChannelList.hpp"
00037 #include "../CASingleSocketGroup.hpp"
00038 #include "../CAPool.hpp"
00039 #include "../CACmdLnOptions.hpp"
00040 #include "../CAUtil.hpp"
00041 
00042 #ifdef HAVE_EPOLL
00043   #include "../CASocketGroupEpoll.hpp"
00044 #endif
00045 
00046 CALastMixB::CALastMixB() {
00047   m_pChainTable = NULL;
00048   m_pChannelTable = NULL;
00049 }
00050 
00051 void CALastMixB::reconfigureMix() {
00052   #ifdef DELAY_CHANNELS
00053     CAMsg::printMsg(LOG_DEBUG, "CALastMixB: Set new resources limitation parameters.\n");
00054     if (m_pChainTable != NULL) {
00055       m_pChainTable->setDelayParameters(pglobalOptions->getDelayChannelUnlimitTraffic(), pglobalOptions->getDelayChannelBucketGrow(), pglobalOptions->getDelayChannelBucketGrowIntervall());
00056     }
00057   #endif
00058 }
00059 
00060 
00061 SINT32 CALastMixB::loop() {
00062 #ifdef NEW_MIX_TYPE
00063   /* should only be compiled, if TypeB mixes are used */
00064   m_pChainTable = new CAChainTable();
00065   m_pChannelTable = new CALastMixBChannelList();
00066   #ifdef DELAY_CHANNELS
00067     m_pChainTable->setDelayParameters(pglobalOptions->getDelayChannelUnlimitTraffic(), pglobalOptions->getDelayChannelBucketGrow(), pglobalOptions->getDelayChannelBucketGrowIntervall());
00068   #endif  
00069 
00070   #ifdef HAVE_EPOLL
00071     CASocketGroupEpoll* psocketgroupCacheRead = new CASocketGroupEpoll(false);
00072     CASocketGroupEpoll* psocketgroupCacheWrite = new CASocketGroupEpoll(true);
00073   #else
00074     CASocketGroup* psocketgroupCacheRead = new CASocketGroup(false);
00075     CASocketGroup* psocketgroupCacheWrite = new CASocketGroup(true);
00076   #endif
00077 
00078   UINT8 rsaOutputBuffer[RSA_SIZE];
00079   m_logUploadedPackets = 0;
00080   m_logDownloadedPackets = 0;
00081   set64((UINT64&)m_logUploadedBytes,(UINT32)0);
00082   set64((UINT64&)m_logDownloadedBytes,(UINT32)0);
00083 
00084   /* start logging */
00085   CAThread* pLogThread = new CAThread((UINT8*)"CALastMixB - LogLoop");
00086   pLogThread->setMainLoop(lm_loopLog);
00087   pLogThread->start(this);
00088 
00089   /* initialize some pointers */
00090   tQueueEntry* currentQueueEntry = new tQueueEntry;
00091   MIXPACKET* currentMixPacket = &(currentQueueEntry->packet);
00092   t_upstreamChainCell* pChainCell = (t_upstreamChainCell*)(currentMixPacket->data);
00093   
00094   #ifdef LOG_CHAIN_STATISTICS
00095     CAMsg::printMsg(LOG_DEBUG, "Chain log format is: Chain-ID, Chain duration [micros], Upload (bytes), Download (bytes), Packets from user, Packets to user\n"); 
00096   #endif
00097 
00098   while(!m_bRestart) {
00099     /* begin of the mix-loop */
00100     bool bAktiv = false;
00101 
00102 // Step 1a: reading from previous Mix --> now in separate thread
00103 // Step 1b: processing MixPackets from previous mix
00104 
00105     if (m_pQueueReadFromMix->getSize() >= sizeof(tQueueEntry)) {
00106       /* there is something to do in upstream-direction */
00107       bAktiv = true;
00108       UINT32 chains = m_pChainTable->getSize();
00109       /* limit the number of processed upstream-packets depending on the
00110        * number of currently forwarded data-chains
00111        */
00112       for (UINT32 k = 0; (k < (chains + 1)) && (m_pQueueReadFromMix->getSize() >= sizeof(tQueueEntry)); k++) {
00113         UINT32 readBytes = sizeof(tQueueEntry);
00114         m_pQueueReadFromMix->get((UINT8*)currentQueueEntry, &readBytes);
00115         #ifdef LOG_PACKET_TIMES
00116           getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_start_OP);
00117         #endif
00118         if ((currentMixPacket->channel > 0) && (currentMixPacket->channel < 256)) {
00119           /* it's a control-channel packet */
00120           m_pMuxInControlChannelDispatcher->proccessMixPacket(currentMixPacket);
00121           /* process the next packet */
00122           continue;
00123         }
00124         /* a data-channel packet received */
00125         m_logUploadedPackets++;
00126         if (currentMixPacket->flags & CHANNEL_DUMMY) {
00127           /* it's only a dummy-packet -> ignore it */
00128           continue;
00129         }
00130         t_lastMixBChannelListEntry* pChannelListEntry = m_pChannelTable->get(currentMixPacket->channel);
00131         if (pChannelListEntry == NULL) {
00132           /* it's a new channel */
00133           #ifdef _DEBUG1
00134             CAMsg::printMsg(LOG_DEBUG, "New channel from previous Mix!\n");
00135           #endif                
00136           m_pRSA->decrypt(currentMixPacket->data, rsaOutputBuffer);
00137           #ifdef REPLAY_DETECTION
00138             if (m_pReplayDB->insert(rsaOutputBuffer) != E_SUCCESS) {
00139               /* we know such a packet already */
00140               CAMsg::printMsg(LOG_INFO, "Replay: Duplicate packet ignored.\n");
00141               /* currently we have to send at least a CHANNEL-CLOSE -> reuse
00142                * our buffers for the response
00143                */
00144               getRandom(currentMixPacket->data, DATA_SIZE);
00145               currentMixPacket->flags = CHANNEL_CLOSE;
00146               #ifdef LOG_PACKET_TIMES
00147                 /* set invalid packet time for the response */
00148                 setZero64(currentQueueEntry->timestamp_proccessing_start);
00149               #endif
00150               m_pQueueSendToMix->add(currentMixPacket, sizeof(tQueueEntry));
00151               m_logDownloadedPackets++;
00152               /* process the next packet */
00153               continue;
00154             }
00155           #endif
00156           /* copy the RSA output-buffer back in the mix-packet (without the
00157            * symmetric key -> decrypted data will start at the data-pointer
00158            * of the packet)
00159            */
00160           memcpy(currentMixPacket->data, rsaOutputBuffer + KEY_SIZE, RSA_SIZE - KEY_SIZE);
00161           /* initialize the channel-cipher */
00162           CASymCipher* channelCipher = new CASymCipher();
00163           channelCipher->setKey(rsaOutputBuffer);
00164           /* uncrypt the packet (because the symmetric key at the begin is
00165            * removed, we have to pull out the decrypted data)
00166            */
00167           channelCipher->crypt1((currentMixPacket->data) + RSA_SIZE, (currentMixPacket->data) + RSA_SIZE - KEY_SIZE, DATA_SIZE - RSA_SIZE);
00168           #ifdef LOG_PACKET_TIMES
00169             getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_end_OP);
00170           #endif
00171           UINT16 lengthAndFlags = ntohs(pChainCell->lengthAndFlags);
00172           UINT16 payloadLength = lengthAndFlags & CHAINFLAG_LENGTH_MASK;
00173           if (lengthAndFlags & CHAINFLAG_NEW_CHAIN) {
00174             #ifdef _DEBUG1
00175               CAMsg::printMsg(LOG_DEBUG, "Creating new chain.\n");
00176             #endif
00177             CAChain* currentChain = m_pChainTable->createEntry();
00178             if (currentChain == NULL) {
00179               /* we are unable to create a new chain (maximum number of
00180                * chains - defined in MAX_POLLFD - is reached)
00181                */
00182               CAMsg::printMsg(LOG_INFO, "Unable to create more than %u chains - dropped new chain.\n", MAX_POLLFD);
00183               delete channelCipher;
00184               channelCipher = NULL;
00185               /* currently we have to send at least a CHANNEL-CLOSE -> reuse
00186                * our buffers for the response
00187                */
00188               getRandom(currentMixPacket->data, DATA_SIZE);
00189               currentMixPacket->flags = CHANNEL_CLOSE;
00190               #ifdef LOG_PACKET_TIMES
00191                 /* set invalid packet time for the response */
00192                 setZero64(pQueueEntry->timestamp_proccessing_start);
00193               #endif
00194               m_pQueueSendToMix->add(currentMixPacket, sizeof(tQueueEntry));
00195               m_logDownloadedPackets++;
00196               /* process the next packet */
00197               continue;
00198             }
00199             currentChain->addChannel(m_pChannelTable->add(currentMixPacket->channel, channelCipher, currentChain), ((lengthAndFlags & CHAINFLAG_FAST_RESPONSE) == CHAINFLAG_FAST_RESPONSE));
00200             /* Attention: The type-field is handled as part of the payload-
00201              *            data --> to get the useable payload we have to
00202              *            subtract the size of the type-field.
00203              */
00204             payloadLength = payloadLength - 1;
00205             if (payloadLength <= MAX_FIRST_UPSTREAM_CHAINCELL_PAYLOAD) {
00206               /* it's a valid new chain */
00207               CASocket* tmpSocket = new CASocket;
00208               CACacheLoadBalancing* pLoadBalancing = m_pCacheLB;
00209               if (pChainCell->firstCell.type == MIX_PAYLOAD_SOCKS) {
00210                 pLoadBalancing = m_pSocksLB;
00211               }
00212               SINT32 errorCode = E_UNKNOWN;
00213               /* build a new connection to one of the known proxy-servers */
00214               for (UINT32 count=0; count < pLoadBalancing->getElementCount(); count++) {
00215                 tmpSocket->create();
00216                 tmpSocket->setRecvBuff(50000);
00217                 tmpSocket->setSendBuff(5000);
00218                 errorCode = tmpSocket->connect(*pLoadBalancing->get(), LAST_MIX_TO_PROXY_CONNECT_TIMEOUT);
00219                 if (errorCode == E_SUCCESS) {
00220                   break;
00221                 }
00222                 tmpSocket->close();
00223               }  
00224               if (errorCode != E_SUCCESS) {
00225                 /* could not connect to any proxy */
00226                 CAMsg::printMsg(LOG_DEBUG,"Cannot connect to Squid!\n");
00227                 delete tmpSocket;
00228                 tmpSocket = NULL;
00229                 /* close the chain immediately */
00230                 currentChain->signalConnectionError();
00231               }
00232               else {
00233                 /* we have a connection to a proxy */
00234                 #ifdef LOG_CHAIN_STATISTICS
00235                   currentChain->setSocket(tmpSocket, 1, payloadLength);
00236                 #else
00237                   currentChain->setSocket(tmpSocket);
00238                 #endif
00239                 #ifdef _DEBUG1
00240                   /* log the first 30 byte of the chain-data */
00241                   UINT8 c = pChainCell->firstCell.data[30];
00242                   /* make a temporary string-cut after 30 byte */
00243                   pChainCell->firstCell.data[30] = 0;
00244                   CAMsg::printMsg(LOG_DEBUG, "Try sending data to Squid: %s\n", pChainCell->firstCell.data);
00245                   pChainCell->firstCell.data[30] = c;
00246                 #endif
00247                 #ifdef LOG_CRIME
00248                   if (checkCrime(pChainCell->firstCell.data, payloadLength)) {
00249                     /* we've captured a stupid gangsta, who sent a suspected 
00250                      * webaddress completely in the first packet
00251                      */
00252                     UINT8 crimeBuff[MAX_FIRST_UPSTREAM_CHAINCELL_PAYLOAD + 1];
00253                     /* ensure that there is a trailing 0 -> use one byte more
00254                      * than necessary for the plain data
00255                      */
00256                     memset(crimeBuff, 0, MAX_FIRST_UPSTREAM_CHAINCELL_PAYLOAD + 1);
00257                     memcpy(crimeBuff, pChainCell->firstCell.data, payloadLength);
00258                     /* for compatibility with the default mix-implementation
00259                      * we will send an extra-packet on the channel with a
00260                      * crime-signal (without using the channel-cipher)
00261                      */
00262                     tQueueEntry oSigCrimeQueueEntry;
00263                     memset(&oSigCrimeQueueEntry, 0, sizeof(tQueueEntry));
00264                     UINT32 id = m_pMuxIn->sigCrime(currentMixPacket->channel, &oSigCrimeQueueEntry.packet);
00265                     m_pQueueSendToMix->add(&oSigCrimeQueueEntry, sizeof(tQueueEntry));
00266                     m_logDownloadedPackets++;
00267                     int log = LOG_ENCRYPTED;
00268                     if (!pglobalOptions->isEncryptedLogEnabled()) {
00269                       log = LOG_CRIT;
00270                       CAMsg::printMsg(log,"Crime detected -- ID: %u -- Content: \n%s\n", id, crimeBuff);
00271                     }
00272                   }
00273                 #endif
00274                 if (tmpSocket->sendTimeOut(pChainCell->firstCell.data, payloadLength, LAST_MIX_TO_PROXY_SEND_TIMEOUT) == SOCKET_ERROR) {
00275                   #ifdef _DEBUG
00276                     CAMsg::printMsg(LOG_DEBUG,"Error sending data to Squid!\n");
00277                   #endif
00278                   currentChain->signalConnectionError();
00279                 }
00280                 else {
00281                   tmpSocket->setNonBlocking(true);
00282                   currentChain->addToSocketGroup(psocketgroupCacheRead);
00283                   #ifdef LOG_PACKET_TIMES
00284                     getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_end);
00285                     m_pLogPacketStats->addToTimeingStats(*currentQueueEntry, CHANNEL_OPEN, true);
00286                   #endif
00287                   if (lengthAndFlags & CHAINFLAG_STREAM_CLOSED) {
00288                     /* close upstream (after sending data) */
00289                     currentChain->closeUpstream();
00290                   }
00291                 }
00292               }
00293             }
00294             else {
00295               /* invalid packet length */
00296               currentChain->signalConnectionError();
00297             }
00298           }
00299           else {
00300             /* new-chain flag is not set */
00301             CAChain* currentChain = m_pChainTable->getEntry(pChainCell->sequelCell.chainId);
00302             if (currentChain != NULL) {
00303               #ifdef _DEBUG1
00304                 CAMsg::printMsg(LOG_DEBUG, "Continue existent chain.\n");
00305               #endif
00306               /* we've found the specified chain in the table */
00307               currentChain->addChannel(m_pChannelTable->add(currentMixPacket->channel, channelCipher, currentChain), ((lengthAndFlags & CHAINFLAG_FAST_RESPONSE) == CHAINFLAG_FAST_RESPONSE));
00308               if (payloadLength <= MAX_SEQUEL_UPSTREAM_CHAINCELL_PAYLOAD) {
00309                 /* payload-length is valid */
00310                 if (payloadLength > 0) {
00311                   currentChain->addDataToUpstreamQueue(pChainCell->sequelCell.data, payloadLength);
00312                   currentChain->addToSocketGroup(psocketgroupCacheWrite);
00313                 }
00314                 #ifdef LOG_CHAIN_STATISTICS
00315                 /* also add empty packets to the queue (will do nothing, but
00316                  * adds the received packet to the statistics)
00317                  */
00318                 else {
00319                   currentChain->addDataToUpstreamQueue(pChainCell->sequelCell.data, payloadLength);
00320                 }
00321                 #endif
00322                 #ifdef LOG_PACKET_TIMES
00323                   getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_end);
00324                   m_pLogPacketStats->addToTimeingStats(*currentQueueEntry, CHANNEL_DATA, true);
00325                 #endif
00326                 if (lengthAndFlags & CHAINFLAG_STREAM_CLOSED) {
00327                   /* close upstream (after sending data) */
00328                   currentChain->closeUpstream();
00329                 }
00330               }
00331               else {
00332                 /* invalid payload-length */
00333                 currentChain->signalConnectionError();
00334               }
00335             }
00336             else {
00337               #ifdef _DEBUG1
00338                 CAMsg::printMsg(LOG_DEBUG, "Unknown chain - cannot continue chain.\n");
00339               #endif
00340               /* we don't know a chain with the specified ID -> create a
00341                * dummy-chain and signal an unknown-chain-error
00342                */
00343               currentChain = m_pChainTable->createEntry();
00344               if (currentChain == NULL) {
00345                 /* we are unable to create a new chain (maximum number of
00346                  * chains - defined in MAX_POLLFD - is reached)
00347                  */
00348                 CAMsg::printMsg(LOG_INFO, "Unable to create more than %u chains - cannot send 'unknown chain' response.\n", MAX_POLLFD);
00349                 delete channelCipher;
00350                 channelCipher = NULL;
00351                 /* currently we have to send at least a CHANNEL-CLOSE -> reuse
00352                  * our buffers for the response
00353                  */
00354                 getRandom(currentMixPacket->data, DATA_SIZE);
00355                 currentMixPacket->flags = CHANNEL_CLOSE;
00356                 #ifdef LOG_PACKET_TIMES
00357                   /* set invalid packet time for the response */
00358                   setZero64(pQueueEntry->timestamp_proccessing_start);
00359                 #endif
00360                 m_pQueueSendToMix->add(currentMixPacket, sizeof(tQueueEntry));
00361                 m_logDownloadedPackets++;
00362                 /* process the next packet */
00363                 continue;
00364               }
00365               currentChain->addChannel(m_pChannelTable->add(currentMixPacket->channel, channelCipher, currentChain), ((lengthAndFlags & CHAINFLAG_FAST_RESPONSE) == CHAINFLAG_FAST_RESPONSE));
00366               currentChain->signalUnknownChain();
00367             }
00368           }
00369         }
00370         else {
00371           /* it's not the first channel-packet -> currently only one upstream-
00372            * packet is allowed -> ignore this one
00373            */
00374           CAMsg::printMsg(LOG_INFO, "Received more than one packet on a channel.\n");
00375         }
00376       }
00377     }
00378 
00379 //end Step 1
00380 
00381 //Step 2 Sending to Cache...
00382 
00383     /* check for chains which have data in the upstream-queue (only those
00384      * chains are in the socket-group) and having also a send-ready socket
00385      */
00386     SINT32 sendReadySockets = psocketgroupCacheWrite->select(0);
00387     if (sendReadySockets > 0) {
00388       bAktiv=true;
00389       #ifdef HAVE_EPOLL
00390         CAChain* currentChain = (CAChain*)psocketgroupCacheWrite->getFirstSignaledSocketData();
00391         while (currentChain != NULL) {
00392           add64((UINT64&)m_logUploadedBytes, currentChain->sendUpstreamData(MIXPACKET_SIZE, psocketgroupCacheWrite));
00393           currentChain = (CAChain*)(psocketgroupCacheWrite->getNextSignaledSocketData());
00394         }
00395       #else
00396         CAChain* currentChain = m_pChainTable->getFirstEntry();
00397         while ((currentChain != NULL) && (sendReadySockets > 0)) {
00398           if (currentChain->isSignaledInSocketGroup(psocketgroupCacheWrite)) {
00399             sendReadySockets--;
00400             add64((UINT64&)m_logUploadedBytes, currentChain->sendUpstreamData(MIXPACKET_SIZE, psocketgroupCacheWrite));
00401           }
00402           currentChain = m_pChainTable->getNextEntry();
00403         }
00404       #endif
00405     }
00406 
00407 //End Step 2
00408 
00409 //Step 3 Reading from Cache....
00410 
00411     #define MAX_MIXIN_SEND_QUEUE_SIZE 1000000
00412     psocketgroupCacheRead->select(0);
00413     if (m_pQueueSendToMix->getSize() < MAX_MIXIN_SEND_QUEUE_SIZE) {
00414       /* we are able to send data to the previos mix -> ask every chain
00415        * whether we can process something
00416        */
00417       CAChain* currentChain = m_pChainTable->getFirstEntry(); 
00418       while (currentChain != NULL) {
00419         #ifdef LOG_PACKET_TIMES
00420           /* timestamps are only meaningful, if a packet is created and sent,
00421            * in the other case they will be overwritten by the next chain
00422            */
00423           getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_start);
00424           set64(currentQueueEntry->timestamp_proccessing_start_OP, currentQueueEntry->timestamp_proccessing_start);
00425         #endif
00426         UINT32 processedBytes;
00427         SINT32 status = currentChain->processDownstream(psocketgroupCacheRead, currentMixPacket, &processedBytes);
00428         add64((UINT64&)m_logDownloadedBytes, processedBytes);
00429         if ((status == 0) || (status == 2)) {
00430           /* there was a packet created -> send it */
00431           #ifdef LOG_PACKET_TIMES
00432             getcurrentTimeMicros(currentQueueEntry->timestamp_proccessing_end_OP);
00433           #endif
00434           m_pQueueSendToMix->add(currentMixPacket, sizeof(tQueueEntry)); 
00435           m_logDownloadedPackets++;
00436         }
00437         if ((status == 2) || (status == 3)) {
00438           /* chain can be removed from the table */
00439           m_pChainTable->deleteEntry(currentChain->getChainId());
00440         }
00441         currentChain = m_pChainTable->getNextEntry();
00442       }
00443     }
00444 
00445 //end Step 3
00446 
00447 //Step 4 Writing to previous Mix
00448 // Now in a separate Thread!
00449 //end step 4
00450 
00451     if (!bAktiv) {
00452       /* there was no data to process in upstream and downstream direction
00453        * -> avoid senseless looping and sleep some time
00454        */
00455       msSleep(100);
00456     }
00457     /* go again to the begin */
00458   }
00459   
00460   /* we have leaved the mix-loop */
00461   CAMsg::printMsg(LOG_CRIT, "Seams that we are restarting now!\n");
00462   m_bRestart=true;
00463   m_pMuxIn->close();
00464   /* write some bytes to the queue (ensure that m_pthreadSendToMix will stop)
00465    */
00466   UINT8 b[sizeof(tQueueEntry)+1];
00467   m_pQueueSendToMix->add(b, sizeof(tQueueEntry)+1);
00468   CAMsg::printMsg(LOG_CRIT, "Wait for LoopSendToMix...\n");
00469   /* will not join if queue is empty (because thread is waiting)!!! */
00470   m_pthreadSendToMix->join(); 
00471   m_bRunLog = false;
00472   CAMsg::printMsg(LOG_CRIT, "Wait for LoopReadFromMix...\n");
00473   m_pthreadReadFromMix->join();
00474   #ifdef LOG_PACKET_TIMES
00475     CAMsg::printMsg(LOG_CRIT, "Wait for LoopLogPacketStats to terminate...\n");
00476     m_pLogPacketStats->stop();
00477   #endif
00478   /* delete the tables (will also remove all entries) */
00479   delete m_pChainTable;
00480   m_pChainTable = NULL;
00481   delete m_pChannelTable;
00482   m_pChannelTable = NULL;
00483   delete currentQueueEntry;
00484   currentQueueEntry = NULL;
00485   pLogThread->join();
00486   delete pLogThread;
00487   pLogThread = NULL;
00488   delete psocketgroupCacheWrite;
00489   psocketgroupCacheWrite = NULL;
00490   delete psocketgroupCacheRead;
00491   psocketgroupCacheRead = NULL;
00492 #endif //NEW_MIX_TYPE
00493   return E_UNKNOWN;
00494 }
00495 #endif //ONLY_LOCAL_PROXY