mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			9705 lines
		
	
	
	
		
			367 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			9705 lines
		
	
	
	
		
			367 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * SRT - Secure, Reliable, Transport
 | 
						|
 * Copyright (c) 2018 Haivision Systems Inc.
 | 
						|
 *
 | 
						|
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
 | 
						|
All rights reserved.
 | 
						|
 | 
						|
Redistribution and use in source and binary forms, with or without
 | 
						|
modification, are permitted provided that the following conditions are
 | 
						|
met:
 | 
						|
 | 
						|
* Redistributions of source code must retain the above
 | 
						|
  copyright notice, this list of conditions and the
 | 
						|
  following disclaimer.
 | 
						|
 | 
						|
* Redistributions in binary form must reproduce the
 | 
						|
  above copyright notice, this list of conditions
 | 
						|
  and the following disclaimer in the documentation
 | 
						|
  and/or other materials provided with the distribution.
 | 
						|
 | 
						|
* Neither the name of the University of Illinois
 | 
						|
  nor the names of its contributors may be used to
 | 
						|
  endorse or promote products derived from this
 | 
						|
  software without specific prior written permission.
 | 
						|
 | 
						|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
						|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
						|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
						|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
						|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
						|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
						|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
						|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
						|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
						|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
						|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
*****************************************************************************/
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
written by
 | 
						|
   Yunhong Gu, last updated 02/28/2012
 | 
						|
modified by
 | 
						|
   Haivision Systems Inc.
 | 
						|
*****************************************************************************/
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
#include <unistd.h>
 | 
						|
#include <netdb.h>
 | 
						|
#include <arpa/inet.h>
 | 
						|
#include <cerrno>
 | 
						|
#include <cstring>
 | 
						|
#include <cstdlib>
 | 
						|
#else
 | 
						|
#include <winsock2.h>
 | 
						|
#include <ws2tcpip.h>
 | 
						|
#endif
 | 
						|
#include <cmath>
 | 
						|
#include <sstream>
 | 
						|
#include "srt.h"
 | 
						|
#include "queue.h"
 | 
						|
#include "core.h"
 | 
						|
#include "logging.h"
 | 
						|
#include "crypto.h"
 | 
						|
#include "logging_api.h" // Required due to containing extern srt_logger_config
 | 
						|
 | 
						|
// Again, just in case when some "smart guy" provided such a global macro
 | 
						|
#ifdef min
 | 
						|
#undef min
 | 
						|
#endif
 | 
						|
#ifdef max
 | 
						|
#undef max
 | 
						|
#endif
 | 
						|
 | 
						|
using namespace std;
 | 
						|
 | 
						|
namespace srt_logging
 | 
						|
{
 | 
						|
 | 
						|
struct AllFaOn
 | 
						|
{
 | 
						|
    LogConfig::fa_bitset_t allfa;
 | 
						|
 | 
						|
    AllFaOn()
 | 
						|
    {
 | 
						|
        //        allfa.set(SRT_LOGFA_BSTATS, true);
 | 
						|
        allfa.set(SRT_LOGFA_CONTROL, true);
 | 
						|
        allfa.set(SRT_LOGFA_DATA, true);
 | 
						|
        allfa.set(SRT_LOGFA_TSBPD, true);
 | 
						|
        allfa.set(SRT_LOGFA_REXMIT, true);
 | 
						|
        allfa.set(SRT_LOGFA_CONGEST, true);
 | 
						|
#if ENABLE_HAICRYPT_LOGGING
 | 
						|
        allfa.set(SRT_LOGFA_HAICRYPT, true);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
} logger_fa_all;
 | 
						|
 | 
						|
} // namespace srt_logging
 | 
						|
 | 
						|
// We need it outside the namespace to preserve the global name.
 | 
						|
// It's a part of "hidden API" (used by applications)
 | 
						|
SRT_API srt_logging::LogConfig srt_logger_config(srt_logging::logger_fa_all.allfa);
 | 
						|
 | 
						|
namespace srt_logging
 | 
						|
{
 | 
						|
 | 
						|
Logger glog(SRT_LOGFA_GENERAL, srt_logger_config, "SRT.g");
 | 
						|
// Unused. If not found useful, maybe reuse for another FA.
 | 
						|
// Logger blog(SRT_LOGFA_BSTATS, srt_logger_config, "SRT.b");
 | 
						|
Logger mglog(SRT_LOGFA_CONTROL, srt_logger_config, "SRT.c");
 | 
						|
Logger dlog(SRT_LOGFA_DATA, srt_logger_config, "SRT.d");
 | 
						|
Logger tslog(SRT_LOGFA_TSBPD, srt_logger_config, "SRT.t");
 | 
						|
Logger rxlog(SRT_LOGFA_REXMIT, srt_logger_config, "SRT.r");
 | 
						|
Logger cclog(SRT_LOGFA_CONGEST, srt_logger_config, "SRT.cc");
 | 
						|
 | 
						|
} // namespace srt_logging
 | 
						|
 | 
						|
using namespace srt_logging;
 | 
						|
 | 
						|
CUDTUnited CUDT::s_UDTUnited;
 | 
						|
 | 
						|
const SRTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK;
 | 
						|
const int       UDT::ERROR        = CUDT::ERROR;
 | 
						|
 | 
						|
// SRT Version constants
 | 
						|
#define SRT_VERSION_UNK     0
 | 
						|
#define SRT_VERSION_MAJ1    0x010000            /* Version 1 major */
 | 
						|
#define SRT_VERSION_MAJ(v) (0xFF0000 & (v))     /* Major number ensuring backward compatibility */
 | 
						|
#define SRT_VERSION_MIN(v) (0x00FF00 & (v))
 | 
						|
#define SRT_VERSION_PCH(v) (0x0000FF & (v))
 | 
						|
 | 
						|
// NOTE: SRT_VERSION is primarily defined in the build file.
 | 
						|
const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION);
 | 
						|
 | 
						|
//#define SRT_CMD_HSREQ       1           /* SRT Handshake Request (sender) */
 | 
						|
#define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */
 | 
						|
#define SRT_CMD_HSREQ_SZ 12   /* Current version packet size */
 | 
						|
#if SRT_CMD_HSREQ_SZ > SRT_CMD_MAXSZ
 | 
						|
#error SRT_CMD_MAXSZ too small
 | 
						|
#endif
 | 
						|
/*      Handshake Request (Network Order)
 | 
						|
        0[31..0]:   SRT version     SRT_DEF_VERSION
 | 
						|
        1[31..0]:   Options         0 [ | SRT_OPT_TSBPDSND ][ | SRT_OPT_HAICRYPT ]
 | 
						|
        2[31..16]:  TsbPD resv      0
 | 
						|
        2[15..0]:   TsbPD delay     [0..60000] msec
 | 
						|
*/
 | 
						|
 | 
						|
//#define SRT_CMD_HSRSP       2           /* SRT Handshake Response (receiver) */
 | 
						|
#define SRT_CMD_HSRSP_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */
 | 
						|
#define SRT_CMD_HSRSP_SZ 12   /* Current version packet size */
 | 
						|
#if SRT_CMD_HSRSP_SZ > SRT_CMD_MAXSZ
 | 
						|
#error SRT_CMD_MAXSZ too small
 | 
						|
#endif
 | 
						|
/*      Handshake Response (Network Order)
 | 
						|
        0[31..0]:   SRT version     SRT_DEF_VERSION
 | 
						|
        1[31..0]:   Options         0 [ | SRT_OPT_TSBPDRCV [| SRT_OPT_TLPKTDROP ]][ | SRT_OPT_HAICRYPT]
 | 
						|
                                      [ | SRT_OPT_NAKREPORT ] [ | SRT_OPT_REXMITFLG ]
 | 
						|
        2[31..16]:  TsbPD resv      0
 | 
						|
        2[15..0]:   TsbPD delay     [0..60000] msec
 | 
						|
*/
 | 
						|
 | 
						|
void CUDT::construct()
 | 
						|
{
 | 
						|
    m_pSndBuffer           = NULL;
 | 
						|
    m_pRcvBuffer           = NULL;
 | 
						|
    m_pSndLossList         = NULL;
 | 
						|
    m_pRcvLossList         = NULL;
 | 
						|
    m_iReorderTolerance    = 0;
 | 
						|
    m_iMaxReorderTolerance = 0; // Sensible optimal value is 10, 0 preserves old behavior
 | 
						|
    m_iConsecEarlyDelivery = 0; // how many times so far the packet considered lost has been received before TTL expires
 | 
						|
    m_iConsecOrderedDelivery = 0;
 | 
						|
 | 
						|
    m_pSndQueue = NULL;
 | 
						|
    m_pRcvQueue = NULL;
 | 
						|
    m_pPeerAddr = NULL;
 | 
						|
    m_pSNode    = NULL;
 | 
						|
    m_pRNode    = NULL;
 | 
						|
 | 
						|
    m_ullSndHsLastTime_us = 0;
 | 
						|
    m_iSndHsRetryCnt      = SRT_MAX_HSRETRY + 1; // Will be reset to 0 for HSv5, this value is important for HSv4
 | 
						|
 | 
						|
    // Initial status
 | 
						|
    m_bOpened             = false;
 | 
						|
    m_bListening          = false;
 | 
						|
    m_bConnecting         = false;
 | 
						|
    m_bConnected          = false;
 | 
						|
    m_bClosing            = false;
 | 
						|
    m_bShutdown           = false;
 | 
						|
    m_bBroken             = false;
 | 
						|
    m_bPeerHealth         = true;
 | 
						|
    m_RejectReason        = SRT_REJ_UNKNOWN;
 | 
						|
    m_ullLingerExpiration = 0;
 | 
						|
    m_llLastReqTime       = 0;
 | 
						|
 | 
						|
    m_lSrtVersion            = SRT_DEF_VERSION;
 | 
						|
    m_lPeerSrtVersion        = 0; // not defined until connected.
 | 
						|
    m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1;
 | 
						|
 | 
						|
    m_iTsbPdDelay_ms     = 0;
 | 
						|
    m_iPeerTsbPdDelay_ms = 0;
 | 
						|
 | 
						|
    m_bPeerTsbPd         = false;
 | 
						|
    m_iPeerTsbPdDelay_ms = 0;
 | 
						|
    m_bTsbPd             = false;
 | 
						|
    m_bTsbPdAckWakeup    = false;
 | 
						|
    m_bPeerTLPktDrop     = false;
 | 
						|
 | 
						|
    m_uKmRefreshRatePkt = 0;
 | 
						|
    m_uKmPreAnnouncePkt = 0;
 | 
						|
 | 
						|
    // Initilize mutex and condition variables
 | 
						|
    initSynch();
 | 
						|
}
 | 
						|
 | 
						|
CUDT::CUDT()
 | 
						|
{
 | 
						|
    construct();
 | 
						|
 | 
						|
    (void)SRT_DEF_VERSION;
 | 
						|
 | 
						|
    // Default UDT configurations
 | 
						|
    m_iMSS            = 1500;
 | 
						|
    m_bSynSending     = true;
 | 
						|
    m_bSynRecving     = true;
 | 
						|
    m_iFlightFlagSize = 25600;
 | 
						|
    m_iSndBufSize     = 8192;
 | 
						|
    m_iRcvBufSize = 8192; // Rcv buffer MUST NOT be bigger than Flight Flag size
 | 
						|
 | 
						|
    // Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option
 | 
						|
    // for other modes.
 | 
						|
    m_Linger.l_onoff  = 0;
 | 
						|
    m_Linger.l_linger = 0;
 | 
						|
    m_iUDPSndBufSize  = 65536;
 | 
						|
    m_iUDPRcvBufSize  = m_iRcvBufSize * m_iMSS;
 | 
						|
    m_iSockType       = UDT_DGRAM;
 | 
						|
    m_iIPversion      = AF_INET;
 | 
						|
    m_bRendezvous     = false;
 | 
						|
#ifdef SRT_ENABLE_CONNTIMEO
 | 
						|
    m_iConnTimeOut = 3000;
 | 
						|
#endif
 | 
						|
    m_iSndTimeOut = -1;
 | 
						|
    m_iRcvTimeOut = -1;
 | 
						|
    m_bReuseAddr  = true;
 | 
						|
    m_llMaxBW     = -1;
 | 
						|
#ifdef SRT_ENABLE_IPOPTS
 | 
						|
    m_iIpTTL = -1;
 | 
						|
    m_iIpToS = -1;
 | 
						|
#endif
 | 
						|
    m_CryptoSecret.len = 0;
 | 
						|
    m_iSndCryptoKeyLen = 0;
 | 
						|
    // Cfg
 | 
						|
    m_bDataSender           = false; // Sender only if true: does not recv data
 | 
						|
    m_bOPT_TsbPd            = true;  // Enable TsbPd on sender
 | 
						|
    m_iOPT_TsbPdDelay       = SRT_LIVE_DEF_LATENCY_MS;
 | 
						|
    m_iOPT_PeerTsbPdDelay   = 0; // Peer's TsbPd delay as receiver (here is its minimum value, if used)
 | 
						|
    m_bOPT_TLPktDrop        = true;
 | 
						|
    m_iOPT_SndDropDelay     = 0;
 | 
						|
    m_bOPT_StrictEncryption = true;
 | 
						|
    m_iOPT_PeerIdleTimeout  = COMM_RESPONSE_TIMEOUT_MS;
 | 
						|
    m_bTLPktDrop            = true; // Too-late Packet Drop
 | 
						|
    m_bMessageAPI           = true;
 | 
						|
    m_zOPT_ExpPayloadSize   = SRT_LIVE_DEF_PLSIZE;
 | 
						|
    m_iIpV6Only             = -1;
 | 
						|
    // Runtime
 | 
						|
    m_bRcvNakReport             = true; // Receiver's Periodic NAK Reports
 | 
						|
    m_llInputBW                 = 0;    // Application provided input bandwidth (internal input rate sampling == 0)
 | 
						|
    m_iOverheadBW               = 25;   // Percent above input stream rate (applies if m_llMaxBW == 0)
 | 
						|
    m_OPT_PktFilterConfigString = "";
 | 
						|
 | 
						|
    m_pCache = NULL;
 | 
						|
 | 
						|
    // Default congctl is "live".
 | 
						|
    // Available builtin congctl: "file".
 | 
						|
    // Other congctls can be registerred.
 | 
						|
 | 
						|
    // Note that 'select' returns false if there's no such congctl.
 | 
						|
    // If so, congctl becomes unselected. Calling 'configure' on an
 | 
						|
    // unselected congctl results in exception.
 | 
						|
    m_CongCtl.select("live");
 | 
						|
}
 | 
						|
 | 
						|
CUDT::CUDT(const CUDT &ancestor)
 | 
						|
{
 | 
						|
    construct();
 | 
						|
 | 
						|
    // XXX Consider all below fields (except m_bReuseAddr) to be put
 | 
						|
    // into a separate class for easier copying.
 | 
						|
 | 
						|
    // Default UDT configurations
 | 
						|
    m_iMSS            = ancestor.m_iMSS;
 | 
						|
    m_bSynSending     = ancestor.m_bSynSending;
 | 
						|
    m_bSynRecving     = ancestor.m_bSynRecving;
 | 
						|
    m_iFlightFlagSize = ancestor.m_iFlightFlagSize;
 | 
						|
    m_iSndBufSize     = ancestor.m_iSndBufSize;
 | 
						|
    m_iRcvBufSize     = ancestor.m_iRcvBufSize;
 | 
						|
    m_Linger          = ancestor.m_Linger;
 | 
						|
    m_iUDPSndBufSize  = ancestor.m_iUDPSndBufSize;
 | 
						|
    m_iUDPRcvBufSize  = ancestor.m_iUDPRcvBufSize;
 | 
						|
    m_iSockType       = ancestor.m_iSockType;
 | 
						|
    m_iIPversion      = ancestor.m_iIPversion;
 | 
						|
    m_bRendezvous     = ancestor.m_bRendezvous;
 | 
						|
#ifdef SRT_ENABLE_CONNTIMEO
 | 
						|
    m_iConnTimeOut = ancestor.m_iConnTimeOut;
 | 
						|
#endif
 | 
						|
    m_iSndTimeOut = ancestor.m_iSndTimeOut;
 | 
						|
    m_iRcvTimeOut = ancestor.m_iRcvTimeOut;
 | 
						|
    m_bReuseAddr  = true; // this must be true, because all accepted sockets share the same port with the listener
 | 
						|
    m_llMaxBW     = ancestor.m_llMaxBW;
 | 
						|
#ifdef SRT_ENABLE_IPOPTS
 | 
						|
    m_iIpTTL = ancestor.m_iIpTTL;
 | 
						|
    m_iIpToS = ancestor.m_iIpToS;
 | 
						|
#endif
 | 
						|
    m_llInputBW             = ancestor.m_llInputBW;
 | 
						|
    m_iOverheadBW           = ancestor.m_iOverheadBW;
 | 
						|
    m_bDataSender           = ancestor.m_bDataSender;
 | 
						|
    m_bOPT_TsbPd            = ancestor.m_bOPT_TsbPd;
 | 
						|
    m_iOPT_TsbPdDelay       = ancestor.m_iOPT_TsbPdDelay;
 | 
						|
    m_iOPT_PeerTsbPdDelay   = ancestor.m_iOPT_PeerTsbPdDelay;
 | 
						|
    m_bOPT_TLPktDrop        = ancestor.m_bOPT_TLPktDrop;
 | 
						|
    m_iOPT_SndDropDelay     = ancestor.m_iOPT_SndDropDelay;
 | 
						|
    m_bOPT_StrictEncryption = ancestor.m_bOPT_StrictEncryption;
 | 
						|
    m_iOPT_PeerIdleTimeout  = ancestor.m_iOPT_PeerIdleTimeout;
 | 
						|
    m_zOPT_ExpPayloadSize   = ancestor.m_zOPT_ExpPayloadSize;
 | 
						|
    m_bTLPktDrop            = ancestor.m_bTLPktDrop;
 | 
						|
    m_bMessageAPI           = ancestor.m_bMessageAPI;
 | 
						|
    m_iIpV6Only             = ancestor.m_iIpV6Only;
 | 
						|
    m_iReorderTolerance     = ancestor.m_iMaxReorderTolerance;  // Initialize with maximum value
 | 
						|
    m_iMaxReorderTolerance  = ancestor.m_iMaxReorderTolerance;
 | 
						|
    // Runtime
 | 
						|
    m_bRcvNakReport             = ancestor.m_bRcvNakReport;
 | 
						|
    m_OPT_PktFilterConfigString = ancestor.m_OPT_PktFilterConfigString;
 | 
						|
 | 
						|
    m_CryptoSecret     = ancestor.m_CryptoSecret;
 | 
						|
    m_iSndCryptoKeyLen = ancestor.m_iSndCryptoKeyLen;
 | 
						|
 | 
						|
    m_uKmRefreshRatePkt = ancestor.m_uKmRefreshRatePkt;
 | 
						|
    m_uKmPreAnnouncePkt = ancestor.m_uKmPreAnnouncePkt;
 | 
						|
 | 
						|
    m_pCache = ancestor.m_pCache;
 | 
						|
 | 
						|
    // SrtCongestion's copy constructor copies the selection,
 | 
						|
    // but not the underlying congctl object. After
 | 
						|
    // copy-constructed, the 'configure' must be called on it again.
 | 
						|
    m_CongCtl = ancestor.m_CongCtl;
 | 
						|
}
 | 
						|
 | 
						|
CUDT::~CUDT()
 | 
						|
{
 | 
						|
    // release mutex/condtion variables
 | 
						|
    destroySynch();
 | 
						|
 | 
						|
    // Wipeout critical data
 | 
						|
    memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret));
 | 
						|
 | 
						|
    // destroy the data structures
 | 
						|
    delete m_pSndBuffer;
 | 
						|
    delete m_pRcvBuffer;
 | 
						|
    delete m_pSndLossList;
 | 
						|
    delete m_pRcvLossList;
 | 
						|
    delete m_pPeerAddr;
 | 
						|
    delete m_pSNode;
 | 
						|
    delete m_pRNode;
 | 
						|
}
 | 
						|
 | 
						|
// This function is to make it possible for both C and C++
 | 
						|
// API to accept both bool and int types for boolean options.
 | 
						|
// (it's not that C couldn't use <stdbool.h>, it's that people
 | 
						|
// often forget to use correct type).
 | 
						|
static bool bool_int_value(const void *optval, int optlen)
 | 
						|
{
 | 
						|
    if (optlen == sizeof(bool))
 | 
						|
    {
 | 
						|
        return *(bool *)optval;
 | 
						|
    }
 | 
						|
 | 
						|
    if (optlen == sizeof(int))
 | 
						|
    {
 | 
						|
        return 0 != *(int *)optval; // 0!= is a windows warning-killer int-to-bool conversion
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::setOpt(SRT_SOCKOPT optName, const void *optval, int optlen)
 | 
						|
{
 | 
						|
    if (m_bBroken || m_bClosing)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
    CGuard sendguard(m_SendLock);
 | 
						|
    CGuard recvguard(m_RecvLock);
 | 
						|
 | 
						|
    switch (optName)
 | 
						|
    {
 | 
						|
    case SRTO_MSS:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
 | 
						|
        if (*(int *)optval < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize))
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        m_iMSS = *(int *)optval;
 | 
						|
 | 
						|
        // Packet size cannot be greater than UDP buffer size
 | 
						|
        if (m_iMSS > m_iUDPSndBufSize)
 | 
						|
            m_iMSS = m_iUDPSndBufSize;
 | 
						|
        if (m_iMSS > m_iUDPRcvBufSize)
 | 
						|
            m_iMSS = m_iUDPRcvBufSize;
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDSYN:
 | 
						|
        m_bSynSending = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVSYN:
 | 
						|
        m_bSynRecving = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_FC:
 | 
						|
        if (m_bConnecting || m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        if (*(int *)optval < 1)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL);
 | 
						|
 | 
						|
        // Mimimum recv flight flag size is 32 packets
 | 
						|
        if (*(int *)optval > 32)
 | 
						|
            m_iFlightFlagSize = *(int *)optval;
 | 
						|
        else
 | 
						|
            m_iFlightFlagSize = 32;
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDBUF:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
 | 
						|
        if (*(int *)optval <= 0)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        m_iSndBufSize = *(int *)optval / (m_iMSS - CPacket::UDP_HDR_SIZE);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVBUF:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
 | 
						|
        if (*(int *)optval <= 0)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        {
 | 
						|
            // This weird cast through int is required because
 | 
						|
            // API requires 'int', and internals require 'size_t';
 | 
						|
            // their size is different on 64-bit systems.
 | 
						|
            size_t val = size_t(*(int *)optval);
 | 
						|
 | 
						|
            // Mimimum recv buffer size is 32 packets
 | 
						|
            size_t mssin_size = m_iMSS - CPacket::UDP_HDR_SIZE;
 | 
						|
 | 
						|
            // XXX This magic 32 deserves some constant
 | 
						|
            if (val > mssin_size * 32)
 | 
						|
                m_iRcvBufSize = val / mssin_size;
 | 
						|
            else
 | 
						|
                m_iRcvBufSize = 32;
 | 
						|
 | 
						|
            // recv buffer MUST not be greater than FC size
 | 
						|
            if (m_iRcvBufSize > m_iFlightFlagSize)
 | 
						|
                m_iRcvBufSize = m_iFlightFlagSize;
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_LINGER:
 | 
						|
        m_Linger = *(linger *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_UDP_SNDBUF:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
 | 
						|
        m_iUDPSndBufSize = *(int *)optval;
 | 
						|
 | 
						|
        if (m_iUDPSndBufSize < m_iMSS)
 | 
						|
            m_iUDPSndBufSize = m_iMSS;
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_UDP_RCVBUF:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
 | 
						|
        m_iUDPRcvBufSize = *(int *)optval;
 | 
						|
 | 
						|
        if (m_iUDPRcvBufSize < m_iMSS)
 | 
						|
            m_iUDPRcvBufSize = m_iMSS;
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RENDEZVOUS:
 | 
						|
        if (m_bConnecting || m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
        m_bRendezvous = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDTIMEO:
 | 
						|
        m_iSndTimeOut = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVTIMEO:
 | 
						|
        m_iRcvTimeOut = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_REUSEADDR:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
        m_bReuseAddr = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_MAXBW:
 | 
						|
        m_llMaxBW = *(int64_t *)optval;
 | 
						|
 | 
						|
        // This can be done on both connected and unconnected socket.
 | 
						|
        // When not connected, this will do nothing, however this
 | 
						|
        // event will be repeated just after connecting anyway.
 | 
						|
        if (m_bConnected)
 | 
						|
            updateCC(TEV_INIT, TEV_INIT_RESET);
 | 
						|
        break;
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_IPOPTS
 | 
						|
    case SRTO_IPTTL:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
        if (!(*(int *)optval == -1) && !((*(int *)optval >= 1) && (*(int *)optval <= 255)))
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
        m_iIpTTL = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_IPTOS:
 | 
						|
        if (m_bOpened)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
 | 
						|
        m_iIpToS = *(int *)optval;
 | 
						|
        break;
 | 
						|
#endif
 | 
						|
 | 
						|
    case SRTO_INPUTBW:
 | 
						|
        m_llInputBW = *(int64_t *)optval;
 | 
						|
        // (only if connected; if not, then the value
 | 
						|
        // from m_iOverheadBW will be used initially)
 | 
						|
        if (m_bConnected)
 | 
						|
            updateCC(TEV_INIT, TEV_INIT_INPUTBW);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_OHEADBW:
 | 
						|
        if ((*(int *)optval < 5) || (*(int *)optval > 100))
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
        m_iOverheadBW = *(int *)optval;
 | 
						|
 | 
						|
        // Changed overhead BW, so spread the change
 | 
						|
        // (only if connected; if not, then the value
 | 
						|
        // from m_iOverheadBW will be used initially)
 | 
						|
        if (m_bConnected)
 | 
						|
            updateCC(TEV_INIT, TEV_INIT_OHEADBW);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SENDER:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_bDataSender = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_TSBPDMODE:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_bOPT_TsbPd = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_LATENCY:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_iOPT_TsbPdDelay     = *(int *)optval;
 | 
						|
        m_iOPT_PeerTsbPdDelay = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVLATENCY:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_iOPT_TsbPdDelay = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PEERLATENCY:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_iOPT_PeerTsbPdDelay = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_TLPKTDROP:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_bOPT_TLPktDrop = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDDROPDELAY:
 | 
						|
        // Surprise: you may be connected to alter this option.
 | 
						|
        // The application may manipulate this option on sender while transmitting.
 | 
						|
        m_iOPT_SndDropDelay = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PASSPHRASE:
 | 
						|
        // For consistency, throw exception when connected,
 | 
						|
        // no matter if otherwise the password can be set.
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_ENCRYPTION
 | 
						|
        // Password must be 10-80 characters.
 | 
						|
        // Or it can be empty to clear the password.
 | 
						|
        if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ))
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        memset(&m_CryptoSecret, 0, sizeof(m_CryptoSecret));
 | 
						|
        m_CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE;
 | 
						|
        m_CryptoSecret.len = (optlen <= (int)sizeof(m_CryptoSecret.str) ? optlen : (int)sizeof(m_CryptoSecret.str));
 | 
						|
        memcpy(m_CryptoSecret.str, optval, m_CryptoSecret.len);
 | 
						|
#else
 | 
						|
        if (optlen == 0)
 | 
						|
            break;
 | 
						|
 | 
						|
        LOGC(mglog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
#endif
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PBKEYLEN:
 | 
						|
    case _DEPRECATED_SRTO_SNDPBKEYLEN:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
#ifdef SRT_ENABLE_ENCRYPTION
 | 
						|
        {
 | 
						|
            int v          = *(int *)optval;
 | 
						|
            int allowed[4] = {
 | 
						|
                0,  // Default value, if this results for initiator, defaults to 16. See below.
 | 
						|
                16, // AES-128
 | 
						|
                24, // AES-192
 | 
						|
                32  // AES-256
 | 
						|
            };
 | 
						|
            int *allowed_end = allowed + 4;
 | 
						|
            if (find(allowed, allowed_end, v) == allowed_end)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error,
 | 
						|
                     log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32");
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
            }
 | 
						|
 | 
						|
            // Note: This works a little different in HSv4 and HSv5.
 | 
						|
 | 
						|
            // HSv4:
 | 
						|
            // The party that is set SRTO_SENDER will send KMREQ, and it will
 | 
						|
            // use default value 16, if SRTO_PBKEYLEN is the default value 0.
 | 
						|
            // The responder that receives KMRSP has nothing to say about
 | 
						|
            // PBKEYLEN anyway and it will take the length of the key from
 | 
						|
            // the initiator (sender) as a good deal.
 | 
						|
            //
 | 
						|
            // HSv5:
 | 
						|
            // The initiator (independently on the sender) will send KMREQ,
 | 
						|
            // and as it should be the sender to decide about the PBKEYLEN.
 | 
						|
            // Your application should do the following then:
 | 
						|
            // 1. The sender should set PBKEYLEN to the required value.
 | 
						|
            // 2. If the sender is initiator, it will create the key using
 | 
						|
            //    its preset PBKEYLEN (or default 16, if not set) and the
 | 
						|
            //    receiver-responder will take it as a good deal.
 | 
						|
            // 3. Leave the PBKEYLEN value on the receiver as default 0.
 | 
						|
            // 4. If sender is responder, it should then advertise the PBKEYLEN
 | 
						|
            //    value in the initial handshake messages (URQ_INDUCTION if
 | 
						|
            //    listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case
 | 
						|
            //    of rendezvous, as it is the matter of luck who of them will
 | 
						|
            //    eventually become the initiator). This way the receiver
 | 
						|
            //    being an initiator will set m_iSndCryptoKeyLen before setting
 | 
						|
            //    up KMREQ for sending to the sender-responder.
 | 
						|
            //
 | 
						|
            // Note that in HSv5 if both sides set PBKEYLEN, the responder
 | 
						|
            // wins, unless the initiator is a sender (the effective PBKEYLEN
 | 
						|
            // will be the one advertised by the responder). If none sets,
 | 
						|
            // PBKEYLEN will default to 16.
 | 
						|
 | 
						|
            m_iSndCryptoKeyLen = v;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        LOGC(mglog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
#endif
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_NAKREPORT:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_bRcvNakReport = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_CONNTIMEO
 | 
						|
    case SRTO_CONNTIMEO:
 | 
						|
        m_iConnTimeOut = *(int *)optval;
 | 
						|
        break;
 | 
						|
#endif
 | 
						|
 | 
						|
    case SRTO_LOSSMAXTTL:
 | 
						|
        m_iMaxReorderTolerance = *(int *)optval;
 | 
						|
        if (!m_bConnected)
 | 
						|
            m_iReorderTolerance = m_iMaxReorderTolerance;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_VERSION:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_lSrtVersion = *(uint32_t *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_MINVERSION:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_lMinimumPeerSrtVersion = *(uint32_t *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_STREAMID:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        if (size_t(optlen) > MAX_SID_LENGTH)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        m_sStreamName.assign((const char *)optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_CONGESTION:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        {
 | 
						|
            string val;
 | 
						|
            if (optlen == -1)
 | 
						|
                val = (const char *)optval;
 | 
						|
            else
 | 
						|
                val.assign((const char *)optval, optlen);
 | 
						|
 | 
						|
            // Translate alias
 | 
						|
            if (val == "vod")
 | 
						|
                val = "file";
 | 
						|
 | 
						|
            bool res = m_CongCtl.select(val);
 | 
						|
            if (!res)
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_MESSAGEAPI:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        m_bMessageAPI = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PAYLOADSIZE:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        if (*(int *)optval > SRT_LIVE_MAX_PLSIZE)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU.");
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_OPT_PktFilterConfigString != "")
 | 
						|
        {
 | 
						|
            // This means that the filter might have been installed before,
 | 
						|
            // and the fix to the maximum payload size was already applied.
 | 
						|
            // This needs to be checked now.
 | 
						|
            SrtFilterConfig fc;
 | 
						|
            if (!ParseFilterConfig(m_OPT_PktFilterConfigString, fc))
 | 
						|
            {
 | 
						|
                // Break silently. This should not happen
 | 
						|
                LOGC(mglog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed");
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
            }
 | 
						|
 | 
						|
            size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size;
 | 
						|
            if (m_zOPT_ExpPayloadSize > efc_max_payload_size)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error,
 | 
						|
                     log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size
 | 
						|
                         << " required for packet filter header");
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        m_zOPT_ExpPayloadSize = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_TRANSTYPE:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        // XXX Note that here the configuration for SRTT_LIVE
 | 
						|
        // is the same as DEFAULT VALUES for these fields set
 | 
						|
        // in CUDT::CUDT.
 | 
						|
        switch (*(SRT_TRANSTYPE *)optval)
 | 
						|
        {
 | 
						|
        case SRTT_LIVE:
 | 
						|
            // Default live options:
 | 
						|
            // - tsbpd: on
 | 
						|
            // - latency: 120ms
 | 
						|
            // - linger: off
 | 
						|
            // - congctl: live
 | 
						|
            // - extraction method: message (reading call extracts one message)
 | 
						|
            m_bOPT_TsbPd          = true;
 | 
						|
            m_iOPT_TsbPdDelay     = SRT_LIVE_DEF_LATENCY_MS;
 | 
						|
            m_iOPT_PeerTsbPdDelay = 0;
 | 
						|
            m_bOPT_TLPktDrop      = true;
 | 
						|
            m_iOPT_SndDropDelay   = 0;
 | 
						|
            m_bMessageAPI         = true;
 | 
						|
            m_bRcvNakReport       = true;
 | 
						|
            m_zOPT_ExpPayloadSize = SRT_LIVE_DEF_PLSIZE;
 | 
						|
            m_Linger.l_onoff      = 0;
 | 
						|
            m_Linger.l_linger     = 0;
 | 
						|
            m_CongCtl.select("live");
 | 
						|
            break;
 | 
						|
 | 
						|
        case SRTT_FILE:
 | 
						|
            // File transfer mode:
 | 
						|
            // - tsbpd: off
 | 
						|
            // - latency: 0
 | 
						|
            // - linger: 2 minutes (180s)
 | 
						|
            // - congctl: file (original UDT congestion control)
 | 
						|
            // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer)
 | 
						|
            m_bOPT_TsbPd          = false;
 | 
						|
            m_iOPT_TsbPdDelay     = 0;
 | 
						|
            m_iOPT_PeerTsbPdDelay = 0;
 | 
						|
            m_bOPT_TLPktDrop      = false;
 | 
						|
            m_iOPT_SndDropDelay   = -1;
 | 
						|
            m_bMessageAPI         = false;
 | 
						|
            m_bRcvNakReport       = false;
 | 
						|
            m_zOPT_ExpPayloadSize = 0; // use maximum
 | 
						|
            m_Linger.l_onoff      = 1;
 | 
						|
            m_Linger.l_linger     = 180; // 2 minutes
 | 
						|
            m_CongCtl.select("file");
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_KMREFRESHRATE:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        // If you first change the KMREFRESHRATE, KMPREANNOUNCE
 | 
						|
        // will be set to the maximum allowed value
 | 
						|
        m_uKmRefreshRatePkt = *(int *)optval;
 | 
						|
        if (m_uKmPreAnnouncePkt == 0 || m_uKmPreAnnouncePkt > (m_uKmRefreshRatePkt - 1) / 2)
 | 
						|
        {
 | 
						|
            m_uKmPreAnnouncePkt = (m_uKmRefreshRatePkt - 1) / 2;
 | 
						|
            LOGC(mglog.Warn,
 | 
						|
                 log << "SRTO_KMREFRESHRATE=0x" << hex << m_uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x"
 | 
						|
                     << hex << m_uKmPreAnnouncePkt);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_KMPREANNOUNCE:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        {
 | 
						|
            int val   = *(int *)optval;
 | 
						|
            int kmref = m_uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_uKmRefreshRatePkt;
 | 
						|
            if (val > (kmref - 1) / 2)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error,
 | 
						|
                     log << "SRTO_KMPREANNOUNCE=0x" << hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2)
 | 
						|
                         << " - OPTION REJECTED.");
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
            }
 | 
						|
 | 
						|
            m_uKmPreAnnouncePkt = val;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_ENFORCEDENCRYPTION:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        m_bOPT_StrictEncryption = bool_int_value(optval, optlen);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PEERIDLETIMEO:
 | 
						|
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
        m_iOPT_PeerIdleTimeout = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_IPV6ONLY:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        m_iIpV6Only = *(int *)optval;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PACKETFILTER:
 | 
						|
        if (m_bConnected)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
        {
 | 
						|
            string arg((char *)optval, optlen);
 | 
						|
            // Parse the configuration string prematurely
 | 
						|
            SrtFilterConfig fc;
 | 
						|
            if (!ParseFilterConfig(arg, fc))
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error,
 | 
						|
                     log << "SRTO_FILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. "
 | 
						|
                            "FILTERTYPE ("
 | 
						|
                         << fc.type << ") must be installed (or builtin)");
 | 
						|
                throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
            }
 | 
						|
 | 
						|
            size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size;
 | 
						|
            if (m_zOPT_ExpPayloadSize > efc_max_payload_size)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Warn,
 | 
						|
                     log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to "
 | 
						|
                         << efc_max_payload_size << " bytes");
 | 
						|
                m_zOPT_ExpPayloadSize = efc_max_payload_size;
 | 
						|
            }
 | 
						|
 | 
						|
            m_OPT_PktFilterConfigString = arg;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen)
 | 
						|
{
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    switch (optName)
 | 
						|
    {
 | 
						|
    case SRTO_MSS:
 | 
						|
        *(int *)optval = m_iMSS;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDSYN:
 | 
						|
        *(bool *)optval = m_bSynSending;
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVSYN:
 | 
						|
        *(bool *)optval = m_bSynRecving;
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_ISN:
 | 
						|
        *(int *)optval = m_iISN;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_FC:
 | 
						|
        *(int *)optval = m_iFlightFlagSize;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDBUF:
 | 
						|
        *(int *)optval = m_iSndBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE);
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVBUF:
 | 
						|
        *(int *)optval = m_iRcvBufSize * (m_iMSS - CPacket::UDP_HDR_SIZE);
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_LINGER:
 | 
						|
        if (optlen < (int)(sizeof(linger)))
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        *(linger *)optval = m_Linger;
 | 
						|
        optlen            = sizeof(linger);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_UDP_SNDBUF:
 | 
						|
        *(int *)optval = m_iUDPSndBufSize;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_UDP_RCVBUF:
 | 
						|
        *(int *)optval = m_iUDPRcvBufSize;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RENDEZVOUS:
 | 
						|
        *(bool *)optval = m_bRendezvous;
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDTIMEO:
 | 
						|
        *(int *)optval = m_iSndTimeOut;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVTIMEO:
 | 
						|
        *(int *)optval = m_iRcvTimeOut;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_REUSEADDR:
 | 
						|
        *(bool *)optval = m_bReuseAddr;
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_MAXBW:
 | 
						|
        *(int64_t *)optval = m_llMaxBW;
 | 
						|
        optlen             = sizeof(int64_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_STATE:
 | 
						|
        *(int32_t *)optval = s_UDTUnited.getStatus(m_SocketID);
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_EVENT:
 | 
						|
    {
 | 
						|
        int32_t event = 0;
 | 
						|
        if (m_bBroken)
 | 
						|
            event |= UDT_EPOLL_ERR;
 | 
						|
        else
 | 
						|
        {
 | 
						|
            CGuard::enterCS(m_RecvLock);
 | 
						|
            if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady())
 | 
						|
                event |= UDT_EPOLL_IN;
 | 
						|
            CGuard::leaveCS(m_RecvLock);
 | 
						|
            if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()))
 | 
						|
                event |= UDT_EPOLL_OUT;
 | 
						|
        }
 | 
						|
        *(int32_t *)optval = event;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case SRTO_SNDDATA:
 | 
						|
        if (m_pSndBuffer)
 | 
						|
            *(int32_t *)optval = m_pSndBuffer->getCurrBufSize();
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = 0;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVDATA:
 | 
						|
        if (m_pRcvBuffer)
 | 
						|
        {
 | 
						|
            CGuard::enterCS(m_RecvLock);
 | 
						|
            *(int32_t *)optval = m_pRcvBuffer->getRcvDataSize();
 | 
						|
            CGuard::leaveCS(m_RecvLock);
 | 
						|
        }
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = 0;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_IPOPTS
 | 
						|
    case SRTO_IPTTL:
 | 
						|
        if (m_bOpened)
 | 
						|
            *(int32_t *)optval = m_pSndQueue->getIpTTL();
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = m_iIpTTL;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_IPTOS:
 | 
						|
        if (m_bOpened)
 | 
						|
            *(int32_t *)optval = m_pSndQueue->getIpToS();
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = m_iIpToS;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
#endif
 | 
						|
 | 
						|
    case SRTO_SENDER:
 | 
						|
        *(int32_t *)optval = m_bDataSender;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_TSBPDMODE:
 | 
						|
        *(int32_t *)optval = m_bOPT_TsbPd;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_LATENCY:
 | 
						|
    case SRTO_RCVLATENCY:
 | 
						|
        *(int32_t *)optval = m_iTsbPdDelay_ms;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PEERLATENCY:
 | 
						|
        *(int32_t *)optval = m_iPeerTsbPdDelay_ms;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_TLPKTDROP:
 | 
						|
        *(int32_t *)optval = m_bTLPktDrop;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDDROPDELAY:
 | 
						|
        *(int32_t *)optval = m_iOPT_SndDropDelay;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PBKEYLEN:
 | 
						|
        if (m_pCryptoControl)
 | 
						|
            *(int32_t *)optval = m_pCryptoControl->KeyLen(); // Running Key length.
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = m_iSndCryptoKeyLen; // May be 0.
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_KMSTATE:
 | 
						|
        if (!m_pCryptoControl)
 | 
						|
            *(int32_t *)optval = SRT_KM_S_UNSECURED;
 | 
						|
        else if (m_bDataSender)
 | 
						|
            *(int32_t *)optval = m_pCryptoControl->m_SndKmState;
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = m_pCryptoControl->m_RcvKmState;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_SNDKMSTATE: // State imposed by Agent depending on PW and KMX
 | 
						|
        if (m_pCryptoControl)
 | 
						|
            *(int32_t *)optval = m_pCryptoControl->m_SndKmState;
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = SRT_KM_S_UNSECURED;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_RCVKMSTATE: // State returned by Peer as informed during KMX
 | 
						|
        if (m_pCryptoControl)
 | 
						|
            *(int32_t *)optval = m_pCryptoControl->m_RcvKmState;
 | 
						|
        else
 | 
						|
            *(int32_t *)optval = SRT_KM_S_UNSECURED;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_LOSSMAXTTL:
 | 
						|
        *(int32_t*)optval = m_iMaxReorderTolerance;
 | 
						|
        optlen = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_NAKREPORT:
 | 
						|
        *(bool *)optval = m_bRcvNakReport;
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_VERSION:
 | 
						|
        *(int32_t *)optval = m_lSrtVersion;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PEERVERSION:
 | 
						|
        *(int32_t *)optval = m_lPeerSrtVersion;
 | 
						|
        optlen             = sizeof(int32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_CONNTIMEO
 | 
						|
    case SRTO_CONNTIMEO:
 | 
						|
        *(int *)optval = m_iConnTimeOut;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
#endif
 | 
						|
 | 
						|
    case SRTO_MINVERSION:
 | 
						|
        *(uint32_t *)optval = m_lMinimumPeerSrtVersion;
 | 
						|
        optlen              = sizeof(uint32_t);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_STREAMID:
 | 
						|
        if (size_t(optlen) < m_sStreamName.size() + 1)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        strcpy((char *)optval, m_sStreamName.c_str());
 | 
						|
        optlen = m_sStreamName.size();
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_CONGESTION:
 | 
						|
    {
 | 
						|
        string tt = m_CongCtl.selected_name();
 | 
						|
        strcpy((char *)optval, tt.c_str());
 | 
						|
        optlen = tt.size();
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
    case SRTO_MESSAGEAPI:
 | 
						|
        optlen          = sizeof(bool);
 | 
						|
        *(bool *)optval = m_bMessageAPI;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PAYLOADSIZE:
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        *(int *)optval = m_zOPT_ExpPayloadSize;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_ENFORCEDENCRYPTION:
 | 
						|
        optlen             = sizeof(int32_t); // also with TSBPDMODE and SENDER
 | 
						|
        *(int32_t *)optval = m_bOPT_StrictEncryption;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_IPV6ONLY:
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        *(int *)optval = m_iIpV6Only;
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PEERIDLETIMEO:
 | 
						|
        *(int *)optval = m_iOPT_PeerIdleTimeout;
 | 
						|
        optlen         = sizeof(int);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRTO_PACKETFILTER:
 | 
						|
        if (size_t(optlen) < m_OPT_PktFilterConfigString.size() + 1)
 | 
						|
            throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
 | 
						|
        strcpy((char *)optval, m_OPT_PktFilterConfigString.c_str());
 | 
						|
        optlen = m_OPT_PktFilterConfigString.size();
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid)
 | 
						|
{
 | 
						|
    CUDT *that = getUDTHandle(u);
 | 
						|
    if (!that)
 | 
						|
        return false;
 | 
						|
 | 
						|
    if (sid.size() > MAX_SID_LENGTH)
 | 
						|
        return false;
 | 
						|
 | 
						|
    if (that->m_bConnected)
 | 
						|
        return false;
 | 
						|
 | 
						|
    that->m_sStreamName = sid;
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
std::string CUDT::getstreamid(SRTSOCKET u)
 | 
						|
{
 | 
						|
    CUDT *that = getUDTHandle(u);
 | 
						|
    if (!that)
 | 
						|
        return "";
 | 
						|
 | 
						|
    return that->m_sStreamName;
 | 
						|
}
 | 
						|
 | 
						|
// XXX REFACTOR: Make common code for CUDT constructor and clearData,
 | 
						|
// possibly using CUDT::construct.
 | 
						|
void CUDT::clearData()
 | 
						|
{
 | 
						|
    // Initial sequence number, loss, acknowledgement, etc.
 | 
						|
    int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE;
 | 
						|
 | 
						|
    m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
    m_iEXPCount  = 1;
 | 
						|
    m_iBandwidth = 1; // pkts/sec
 | 
						|
    // XXX use some constant for this 16
 | 
						|
    m_iDeliveryRate     = 16;
 | 
						|
    m_iByteDeliveryRate = 16 * m_iMaxSRTPayloadSize;
 | 
						|
    m_iAckSeqNo         = 0;
 | 
						|
    m_ullLastAckTime_tk = 0;
 | 
						|
 | 
						|
    // trace information
 | 
						|
    CGuard::enterCS(m_StatsLock);
 | 
						|
    m_stats.startTime = CTimer::getTime();
 | 
						|
    m_stats.sentTotal = m_stats.recvTotal = m_stats.sndLossTotal = m_stats.rcvLossTotal = m_stats.retransTotal =
 | 
						|
        m_stats.sentACKTotal = m_stats.recvACKTotal = m_stats.sentNAKTotal = m_stats.recvNAKTotal = 0;
 | 
						|
    m_stats.lastSampleTime                                                                        = CTimer::getTime();
 | 
						|
    m_stats.traceSent = m_stats.traceRecv = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans =
 | 
						|
        m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0;
 | 
						|
    m_stats.traceRcvRetrans                                                   = 0;
 | 
						|
    m_stats.traceReorderDistance                                              = 0;
 | 
						|
    m_stats.traceBelatedTime                                                  = 0.0;
 | 
						|
    m_stats.traceRcvBelated                                                   = 0;
 | 
						|
 | 
						|
    m_stats.sndDropTotal = 0;
 | 
						|
    m_stats.traceSndDrop = 0;
 | 
						|
    m_stats.rcvDropTotal = 0;
 | 
						|
    m_stats.traceRcvDrop = 0;
 | 
						|
 | 
						|
    m_stats.m_rcvUndecryptTotal = 0;
 | 
						|
    m_stats.traceRcvUndecrypt   = 0;
 | 
						|
 | 
						|
    m_stats.bytesSentTotal    = 0;
 | 
						|
    m_stats.bytesRecvTotal    = 0;
 | 
						|
    m_stats.bytesRetransTotal = 0;
 | 
						|
    m_stats.traceBytesSent    = 0;
 | 
						|
    m_stats.traceBytesRecv    = 0;
 | 
						|
    m_stats.sndFilterExtra    = 0;
 | 
						|
    m_stats.rcvFilterExtra    = 0;
 | 
						|
    m_stats.rcvFilterSupply   = 0;
 | 
						|
    m_stats.rcvFilterLoss     = 0;
 | 
						|
 | 
						|
    m_stats.traceBytesRetrans = 0;
 | 
						|
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
 | 
						|
    m_stats.traceRcvBytesLoss = 0;
 | 
						|
#endif
 | 
						|
    m_stats.sndBytesDropTotal        = 0;
 | 
						|
    m_stats.rcvBytesDropTotal        = 0;
 | 
						|
    m_stats.traceSndBytesDrop        = 0;
 | 
						|
    m_stats.traceRcvBytesDrop        = 0;
 | 
						|
    m_stats.m_rcvBytesUndecryptTotal = 0;
 | 
						|
    m_stats.traceRcvBytesUndecrypt   = 0;
 | 
						|
 | 
						|
    m_stats.sndDuration = m_stats.m_sndDurationTotal = 0;
 | 
						|
    CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
    // Resetting these data because this happens when agent isn't connected.
 | 
						|
    m_bPeerTsbPd         = false;
 | 
						|
    m_iPeerTsbPdDelay_ms = 0;
 | 
						|
 | 
						|
    m_bTsbPd         = m_bOPT_TsbPd; // Take the values from user-configurable options
 | 
						|
    m_iTsbPdDelay_ms = m_iOPT_TsbPdDelay;
 | 
						|
    m_bTLPktDrop     = m_bOPT_TLPktDrop;
 | 
						|
    m_bPeerTLPktDrop = false;
 | 
						|
 | 
						|
    m_bPeerNakReport = false;
 | 
						|
 | 
						|
    m_bPeerRexmitFlag = false;
 | 
						|
 | 
						|
    m_RdvState            = CHandShake::RDV_INVALID;
 | 
						|
    m_ullRcvPeerStartTime = 0;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::open()
 | 
						|
{
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    clearData();
 | 
						|
 | 
						|
    // structures for queue
 | 
						|
    if (m_pSNode == NULL)
 | 
						|
        m_pSNode = new CSNode;
 | 
						|
    m_pSNode->m_pUDT           = this;
 | 
						|
    m_pSNode->m_llTimeStamp_tk = 1;
 | 
						|
    m_pSNode->m_iHeapLoc       = -1;
 | 
						|
 | 
						|
    if (m_pRNode == NULL)
 | 
						|
        m_pRNode = new CRNode;
 | 
						|
    m_pRNode->m_pUDT           = this;
 | 
						|
    m_pRNode->m_llTimeStamp_tk = 1;
 | 
						|
    m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL;
 | 
						|
    m_pRNode->m_bOnList                   = false;
 | 
						|
 | 
						|
    m_iRTT            = 10 * COMM_SYN_INTERVAL_US;
 | 
						|
    m_iRTTVar         = m_iRTT >> 1;
 | 
						|
    m_ullCPUFrequency = CTimer::getCPUFrequency();
 | 
						|
 | 
						|
    // set minimum NAK and EXP timeout to 300ms
 | 
						|
    /*
 | 
						|
       XXX This code is blocked because the value of
 | 
						|
       m_ullMinNakInt_tk will be overwritten again in setupCC.
 | 
						|
       And in setupCC it will have an opportunity to make the
 | 
						|
       value overridden according to the statements in the SrtCongestion.
 | 
						|
 | 
						|
 #ifdef SRT_ENABLE_NAKREPORT
 | 
						|
    if (m_bRcvNakReport)
 | 
						|
       m_ullMinNakInt_tk = m_iMinNakInterval_us * m_ullCPUFrequency;
 | 
						|
    else
 | 
						|
 #endif
 | 
						|
 */
 | 
						|
    // Set up timers
 | 
						|
    m_ullMinNakInt_tk = 300000 * m_ullCPUFrequency;
 | 
						|
    m_ullMinExpInt_tk = 300000 * m_ullCPUFrequency;
 | 
						|
 | 
						|
    m_ullACKInt_tk = COMM_SYN_INTERVAL_US * m_ullCPUFrequency;
 | 
						|
    m_ullNAKInt_tk = m_ullMinNakInt_tk;
 | 
						|
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
    m_ullLastRspTime_tk    = currtime_tk;
 | 
						|
    m_ullNextACKTime_tk    = currtime_tk + m_ullACKInt_tk;
 | 
						|
    m_ullNextNAKTime_tk    = currtime_tk + m_ullNAKInt_tk;
 | 
						|
    m_ullLastRspAckTime_tk = currtime_tk;
 | 
						|
    m_ullLastSndTime_tk    = currtime_tk;
 | 
						|
    m_iReXmitCount         = 1;
 | 
						|
 | 
						|
    m_iPktCount      = 0;
 | 
						|
    m_iLightACKCount = 1;
 | 
						|
 | 
						|
    m_ullTargetTime_tk = 0;
 | 
						|
    m_ullTimeDiff_tk   = 0;
 | 
						|
 | 
						|
    // Now UDT is opened.
 | 
						|
    m_bOpened = true;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::setListenState()
 | 
						|
{
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    if (!m_bOpened)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
 | 
						|
 | 
						|
    if (m_bConnecting || m_bConnected)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
    // listen can be called more than once
 | 
						|
    if (m_bListening)
 | 
						|
        return;
 | 
						|
 | 
						|
    // if there is already another socket listening on the same port
 | 
						|
    if (m_pRcvQueue->setListener(this) < 0)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_BUSY, 0);
 | 
						|
 | 
						|
    m_bListening = true;
 | 
						|
}
 | 
						|
 | 
						|
size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int hs_version)
 | 
						|
{
 | 
						|
    if (srtlen < SRT_HS__SIZE)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Fatal,
 | 
						|
             log << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS__SIZE << ")");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    srtlen = SRT_HS__SIZE; // We use only that much space.
 | 
						|
 | 
						|
    memset(srtdata, 0, sizeof(uint32_t) * srtlen);
 | 
						|
    /* Current version (1.x.x) SRT handshake */
 | 
						|
    srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */
 | 
						|
    srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities();
 | 
						|
 | 
						|
    switch (msgtype)
 | 
						|
    {
 | 
						|
    case SRT_CMD_HSREQ:
 | 
						|
        return fillSrtHandshake_HSREQ(srtdata, srtlen, hs_version);
 | 
						|
    case SRT_CMD_HSRSP:
 | 
						|
        return fillSrtHandshake_HSRSP(srtdata, srtlen, hs_version);
 | 
						|
    default:
 | 
						|
        LOGC(mglog.Fatal, log << "IPE: createSrtHandshake/sendSrtMsg called with value " << msgtype);
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version)
 | 
						|
{
 | 
						|
    // INITIATOR sends HSREQ.
 | 
						|
 | 
						|
    // The TSBPD(SND|RCV) options are being set only if the TSBPD is set in the current agent.
 | 
						|
    // The agent has a decisive power only in the range of RECEIVING the data, however it can
 | 
						|
    // also influence the peer's latency. If agent doesn't set TSBPD mode, it doesn't send any
 | 
						|
    // latency flags, although the peer might still want to do Rx with TSBPD. When agent sets
 | 
						|
    // TsbPd mode, it defines latency values for Rx (itself) and Tx (peer's Rx). If peer does
 | 
						|
    // not set TsbPd mode, it will simply ignore the proposed latency (PeerTsbPdDelay), although
 | 
						|
    // if it has received the Rx latency as well, it must honor it and respond accordingly
 | 
						|
    // (the latter is only in case of HSv5 and bidirectional connection).
 | 
						|
    if (m_bOPT_TsbPd)
 | 
						|
    {
 | 
						|
        m_iTsbPdDelay_ms     = m_iOPT_TsbPdDelay;
 | 
						|
        m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay;
 | 
						|
        /*
 | 
						|
         * Sent data is real-time, use Time-based Packet Delivery,
 | 
						|
         * set option bit and configured delay
 | 
						|
         */
 | 
						|
        srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND;
 | 
						|
 | 
						|
        if (hs_version < CUDT::HS_VERSION_SRT1)
 | 
						|
        {
 | 
						|
            // HSv4 - this uses only one value.
 | 
						|
            srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // HSv5 - this will be understood only since this version when this exists.
 | 
						|
            srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms);
 | 
						|
 | 
						|
            m_bTsbPd = true;
 | 
						|
            // And in the reverse direction.
 | 
						|
            srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV;
 | 
						|
            srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms);
 | 
						|
 | 
						|
            // This wasn't there for HSv4, this setting is only for the receiver.
 | 
						|
            // HSv5 is bidirectional, so every party is a receiver.
 | 
						|
 | 
						|
            if (m_bTLPktDrop)
 | 
						|
                srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bRcvNakReport)
 | 
						|
        srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT;
 | 
						|
 | 
						|
    // I support SRT_OPT_REXMITFLG. Do you?
 | 
						|
    srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG;
 | 
						|
 | 
						|
    // Declare the API used. The flag is set for "stream" API because
 | 
						|
    // the older versions will never set this flag, but all old SRT versions use message API.
 | 
						|
    if (!m_bMessageAPI)
 | 
						|
        srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY])
 | 
						|
              << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS["
 | 
						|
              << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]");
 | 
						|
 | 
						|
    return 3;
 | 
						|
}
 | 
						|
 | 
						|
size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version)
 | 
						|
{
 | 
						|
    // Setting m_ullRcvPeerStartTime is done in processSrtMsg_HSREQ(), so
 | 
						|
    // this condition will be skipped only if this function is called without
 | 
						|
    // getting first received HSREQ. Doesn't look possible in both HSv4 and HSv5.
 | 
						|
    if (m_ullRcvPeerStartTime != 0)
 | 
						|
    {
 | 
						|
        // If Agent doesn't set TSBPD, it will not set the TSBPD flag back to the Peer.
 | 
						|
        // The peer doesn't have be disturbed by it anyway.
 | 
						|
        if (m_bTsbPd)
 | 
						|
        {
 | 
						|
            /*
 | 
						|
             * We got and transposed peer start time (HandShake request timestamp),
 | 
						|
             * we can support Timestamp-based Packet Delivery
 | 
						|
             */
 | 
						|
            srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV;
 | 
						|
 | 
						|
            if (hs_version < HS_VERSION_SRT1)
 | 
						|
            {
 | 
						|
                // HSv4 - this uses only one value
 | 
						|
                srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // HSv5 - this puts "agent's" latency into RCV field and "peer's" -
 | 
						|
                // into SND field.
 | 
						|
                srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "HSRSP/snd: TSBPD off, NOT responding TSBPDRCV flag.");
 | 
						|
        }
 | 
						|
 | 
						|
        // Hsv5, only when peer has declared TSBPD mode.
 | 
						|
        // The flag was already set, and the value already "maximized" in processSrtMsg_HSREQ().
 | 
						|
        if (m_bPeerTsbPd && hs_version >= HS_VERSION_SRT1)
 | 
						|
        {
 | 
						|
            // HSv5 is bidirectional - so send the TSBPDSND flag, and place also the
 | 
						|
            // peer's latency into SND field.
 | 
						|
            srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND;
 | 
						|
            srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms);
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "HSRSP/snd: HSv" << (hs_version == CUDT::HS_VERSION_UDT4 ? 4 : 5)
 | 
						|
                      << " with peer TSBPD=" << (m_bPeerTsbPd ? "on" : "off") << " - NOT responding TSBPDSND");
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_bTLPktDrop)
 | 
						|
            srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        LOGC(mglog.Fatal, log << "IPE: fillSrtHandshake_HSRSP: m_ullRcvPeerStartTime NOT SET!");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bRcvNakReport)
 | 
						|
    {
 | 
						|
        // HSv5: Note that this setting is independent on the value of
 | 
						|
        // m_bPeerNakReport, which represent this setting in the peer.
 | 
						|
 | 
						|
        srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT;
 | 
						|
        /*
 | 
						|
         * NAK Report is so efficient at controlling bandwidth that sender TLPktDrop
 | 
						|
         * is not needed. SRT 1.0.5 to 1.0.7 sender TLPktDrop combined with SRT 1.0
 | 
						|
         * Timestamp-Based Packet Delivery was not well implemented and could drop
 | 
						|
         * big I-Frame tail before sending once on low latency setups.
 | 
						|
         * Disabling TLPktDrop in the receiver SRT Handshake Reply prevents the sender
 | 
						|
         * from enabling Too-Late Packet Drop.
 | 
						|
         */
 | 
						|
        if (m_lPeerSrtVersion <= SrtVersion(1, 0, 7))
 | 
						|
            srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_lSrtVersion >= SrtVersion(1, 2, 0))
 | 
						|
    {
 | 
						|
        if (!m_bPeerRexmitFlag)
 | 
						|
        {
 | 
						|
            // Peer does not request to use rexmit flag, if so,
 | 
						|
            // we won't use as well.
 | 
						|
            HLOGC(mglog.Debug, log << "HSRSP/snd: AGENT understands REXMIT flag, but PEER DOES NOT. NOT setting.");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Request that the rexmit bit be used as a part of msgno.
 | 
						|
            srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG;
 | 
						|
            HLOGF(mglog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too.");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Since this is now in the code, it can occur only in case when you change the
 | 
						|
        // version specification in the build configuration.
 | 
						|
        HLOGF(mglog.Debug, "HSRSP/snd: AGENT DOES NOT UNDERSTAND REXMIT flag");
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY])
 | 
						|
              << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS["
 | 
						|
              << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]");
 | 
						|
 | 
						|
    return 3;
 | 
						|
}
 | 
						|
 | 
						|
size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size)
 | 
						|
{
 | 
						|
    size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion());
 | 
						|
    HLOGF(mglog.Debug,
 | 
						|
          "CMD:%s(%d) Len:%d Version: %s Flags: %08X (%s) sdelay:%d",
 | 
						|
          MessageTypeStr(UMSG_EXT, cmd).c_str(),
 | 
						|
          cmd,
 | 
						|
          (int)(srtlen * sizeof(int32_t)),
 | 
						|
          SrtVersionString(srtdata[SRT_HS_VERSION]).c_str(),
 | 
						|
          srtdata[SRT_HS_FLAGS],
 | 
						|
          SrtFlagString(srtdata[SRT_HS_FLAGS]).c_str(),
 | 
						|
          srtdata[SRT_HS_LATENCY]);
 | 
						|
 | 
						|
    return srtlen;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, int srtlen_in)
 | 
						|
{
 | 
						|
    CPacket srtpkt;
 | 
						|
    int32_t srtcmd = (int32_t)cmd;
 | 
						|
 | 
						|
    static const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ / sizeof(int32_t);
 | 
						|
 | 
						|
    // This is in order to issue a compile error if the SRT_CMD_MAXSZ is
 | 
						|
    // too small to keep all the data. As this is "static const", declaring
 | 
						|
    // an array of such specified size in C++ isn't considered VLA.
 | 
						|
    static const int SRTDATA_SIZE = SRTDATA_MAXSIZE >= SRT_HS__SIZE ? SRTDATA_MAXSIZE : -1;
 | 
						|
 | 
						|
    // This will be effectively larger than SRT_HS__SIZE, but it will be also used
 | 
						|
    // for incoming data. We have a guarantee that it won't be larger than SRTDATA_MAXSIZE.
 | 
						|
    uint32_t srtdata[SRTDATA_SIZE];
 | 
						|
 | 
						|
    int srtlen = 0;
 | 
						|
 | 
						|
    if (cmd == SRT_CMD_REJECT)
 | 
						|
    {
 | 
						|
        // This is a value returned by processSrtMsg underlying layer, potentially
 | 
						|
        // to be reported here. Should this happen, just send a rejection message.
 | 
						|
        cmd                     = SRT_CMD_HSRSP;
 | 
						|
        srtdata[SRT_HS_VERSION] = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (cmd)
 | 
						|
    {
 | 
						|
    case SRT_CMD_HSREQ:
 | 
						|
    case SRT_CMD_HSRSP:
 | 
						|
        srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_SIZE);
 | 
						|
        break;
 | 
						|
 | 
						|
    case SRT_CMD_KMREQ: // Sender
 | 
						|
    case SRT_CMD_KMRSP: // Receiver
 | 
						|
        srtlen = srtlen_in;
 | 
						|
        /* Msg already in network order
 | 
						|
         * But CChannel:sendto will swap again (assuming 32-bit fields)
 | 
						|
         * Pre-swap to cancel it.
 | 
						|
         */
 | 
						|
        HtoNLA(srtdata, srtdata_in, srtlen);
 | 
						|
        m_pCryptoControl->updateKmState(cmd, srtlen); // <-- THIS function can't be moved to CUDT
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        LOGF(mglog.Error, "sndSrtMsg: cmd=%d unsupported", cmd);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (srtlen > 0)
 | 
						|
    {
 | 
						|
        /* srtpkt.pack will set message data in network order */
 | 
						|
        srtpkt.pack(UMSG_EXT, &srtcmd, srtdata, srtlen * sizeof(int32_t));
 | 
						|
        addressAndSend(srtpkt);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// PREREQUISITE:
 | 
						|
// pkt must be set the buffer and configured for UMSG_HANDSHAKE.
 | 
						|
// Note that this function replaces also serialization for the HSv4.
 | 
						|
bool CUDT::createSrtHandshake(ref_t<CPacket>    r_pkt,
 | 
						|
                              ref_t<CHandShake> r_hs,
 | 
						|
                              int               srths_cmd,
 | 
						|
                              int               srtkm_cmd,
 | 
						|
                              const uint32_t *  kmdata,
 | 
						|
                              size_t            kmdata_wordsize /* IN WORDS, NOT BYTES!!! */)
 | 
						|
{
 | 
						|
    CPacket &   pkt = *r_pkt;
 | 
						|
    CHandShake &hs  = *r_hs;
 | 
						|
 | 
						|
    // This function might be called before the opposite version was recognized.
 | 
						|
    // Check if the version is exactly 4 because this means that the peer has already
 | 
						|
    // sent something - asynchronously, and usually in rendezvous - and we already know
 | 
						|
    // that the peer is version 4. In this case, agent must behave as HSv4, til the end.
 | 
						|
    if (m_ConnRes.m_iVersion == HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        hs.m_iVersion = HS_VERSION_UDT4;
 | 
						|
        hs.m_iType    = UDT_DGRAM;
 | 
						|
        if (hs.m_extension)
 | 
						|
        {
 | 
						|
            // Should be impossible
 | 
						|
            LOGC(mglog.Error, log << "createSrtHandshake: IPE: EXTENSION SET WHEN peer reports version 4 - fixing...");
 | 
						|
            hs.m_extension = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        hs.m_iType = 0; // Prepare it for flags
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "createSrtHandshake: buf size=" << pkt.getLength() << " hsx=" << MessageTypeStr(UMSG_EXT, srths_cmd)
 | 
						|
              << " kmx=" << MessageTypeStr(UMSG_EXT, srtkm_cmd) << " kmdata_wordsize=" << kmdata_wordsize
 | 
						|
              << " version=" << hs.m_iVersion);
 | 
						|
 | 
						|
    // Once you are certain that the version is HSv5, set the enc type flags
 | 
						|
    // to advertise pbkeylen. Otherwise make sure that the old interpretation
 | 
						|
    // will correctly pick up the type field. PBKEYLEN should be advertized
 | 
						|
    // regardless of what URQ stage the handshake is (note that in case of rendezvous
 | 
						|
    // CONCLUSION might be the FIRST MESSAGE EVER RECEIVED by a party).
 | 
						|
    if (hs.m_iVersion > HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        // Check if there was a failure to receie HSREQ before trying to craft HSRSP.
 | 
						|
        // If fillSrtHandshake_HSRSP catches the condition of m_ullRcvPeerStartTime == 0,
 | 
						|
        // it will return size 0, which will mess up with further extension procedures;
 | 
						|
        // PREVENT THIS HERE.
 | 
						|
        if (hs.m_iReqType == URQ_CONCLUSION && srths_cmd == SRT_CMD_HSRSP && m_ullRcvPeerStartTime == 0)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "createSrtHandshake: IPE (non-fatal): Attempting to craft HSRSP without received HSREQ. "
 | 
						|
                        "BLOCKING extensions.");
 | 
						|
            hs.m_extension = false;
 | 
						|
        }
 | 
						|
 | 
						|
        // The situation when this function is called without requested extensions
 | 
						|
        // is URQ_CONCLUSION in rendezvous mode in some of the transitions.
 | 
						|
        // In this case for version 5 just clear the m_iType field, as it has
 | 
						|
        // different meaning in HSv5 and contains extension flags.
 | 
						|
        //
 | 
						|
        // Keep 0 in the SRT_HSTYPE_HSFLAGS field, but still advertise PBKEYLEN
 | 
						|
        // in the SRT_HSTYPE_ENCFLAGS field.
 | 
						|
        hs.m_iType                  = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_iSndCryptoKeyLen);
 | 
						|
        bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0;
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "createSrtHandshake: " << (whether ? "" : "NOT ")
 | 
						|
                  << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen);
 | 
						|
 | 
						|
        // Note: This is required only when sending a HS message without SRT extensions.
 | 
						|
        // When this is to be sent with SRT extensions, then KMREQ will be attached here
 | 
						|
        // and the PBKEYLEN will be extracted from it. If this is going to attach KMRSP
 | 
						|
        // here, it's already too late (it should've been advertised before getting the first
 | 
						|
        // handshake message with KMREQ).
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        hs.m_iType = UDT_DGRAM;
 | 
						|
    }
 | 
						|
 | 
						|
    // values > URQ_CONCLUSION include also error types
 | 
						|
    // if (hs.m_iVersion == HS_VERSION_UDT4 || hs.m_iReqType > URQ_CONCLUSION) <--- This condition was checked b4 and
 | 
						|
    // it's only valid for caller-listener mode
 | 
						|
    if (!hs.m_extension)
 | 
						|
    {
 | 
						|
        // Serialize only the basic handshake, if this is predicted for
 | 
						|
        // Hsv4 peer or this is URQ_INDUCTION or URQ_WAVEAHAND.
 | 
						|
        size_t hs_size = pkt.getLength();
 | 
						|
        hs.store_to(pkt.m_pcData, Ref(hs_size));
 | 
						|
        pkt.setLength(hs_size);
 | 
						|
        HLOGC(mglog.Debug, log << "createSrtHandshake: (no ext) size=" << hs_size << " data: " << hs.show());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Sanity check, applies to HSv5 only cases.
 | 
						|
    if (srths_cmd == SRT_CMD_HSREQ && m_SrtHsSide == HSD_RESPONDER)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_IPE;
 | 
						|
        LOGC(mglog.Fatal, log << "IPE: SRT_CMD_HSREQ was requested to be sent in HSv5 by an INITIATOR side!");
 | 
						|
        return false; // should cause rejection
 | 
						|
    }
 | 
						|
 | 
						|
    string logext = "HSX";
 | 
						|
 | 
						|
    bool have_kmreq   = false;
 | 
						|
    bool have_sid     = false;
 | 
						|
    bool have_congctl = false;
 | 
						|
    bool have_filter  = false;
 | 
						|
 | 
						|
    // Install the SRT extensions
 | 
						|
    hs.m_iType |= CHandShake::HS_EXT_HSREQ;
 | 
						|
 | 
						|
    if (srths_cmd == SRT_CMD_HSREQ)
 | 
						|
    {
 | 
						|
        if (m_sStreamName != "")
 | 
						|
        {
 | 
						|
            have_sid = true;
 | 
						|
            hs.m_iType |= CHandShake::HS_EXT_CONFIG;
 | 
						|
            logext += ",SID";
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // If this is a response, we have also information
 | 
						|
    // on the peer. If Peer is NOT filter capable, don't
 | 
						|
    // put filter config, even if agent is capable.
 | 
						|
    bool peer_filter_capable = true;
 | 
						|
    if (srths_cmd == SRT_CMD_HSRSP)
 | 
						|
    {
 | 
						|
        if (m_sPeerPktFilterConfigString != "")
 | 
						|
        {
 | 
						|
            peer_filter_capable = true;
 | 
						|
        }
 | 
						|
        else if (IsSet(m_lPeerSrtFlags, SRT_OPT_FILTERCAP))
 | 
						|
        {
 | 
						|
            peer_filter_capable = true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            peer_filter_capable = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Now, if this is INITIATOR, then it has its
 | 
						|
    // filter config already set, if configured, otherwise
 | 
						|
    // it should not attach the filter config extension.
 | 
						|
 | 
						|
    // If this is a RESPONDER, then it has already received
 | 
						|
    // the filter config string from the peer and therefore
 | 
						|
    // possibly confronted with the contents of m_OPT_FECConfigString,
 | 
						|
    // and if it decided to go with filter, it will be nonempty.
 | 
						|
    if (peer_filter_capable && m_OPT_PktFilterConfigString != "")
 | 
						|
    {
 | 
						|
        have_filter = true;
 | 
						|
        hs.m_iType |= CHandShake::HS_EXT_CONFIG;
 | 
						|
        logext += ",filter";
 | 
						|
    }
 | 
						|
 | 
						|
    string sm = m_CongCtl.selected_name();
 | 
						|
    if (sm != "" && sm != "live")
 | 
						|
    {
 | 
						|
        have_congctl = true;
 | 
						|
        hs.m_iType |= CHandShake::HS_EXT_CONFIG;
 | 
						|
        logext += ",CONGCTL";
 | 
						|
    }
 | 
						|
 | 
						|
    // Prevent adding KMRSP only in case when BOTH:
 | 
						|
    // - Agent has set no password
 | 
						|
    // - no KMREQ has arrived from Peer
 | 
						|
    // KMRSP must be always sent when:
 | 
						|
    // - Agent set a password, Peer did not send KMREQ: Agent sets snd=NOSECRET.
 | 
						|
    // - Agent set no password, but Peer sent KMREQ: Ageng sets rcv=NOSECRET.
 | 
						|
    if (m_CryptoSecret.len > 0 || kmdata_wordsize > 0)
 | 
						|
    {
 | 
						|
        have_kmreq = true;
 | 
						|
        hs.m_iType |= CHandShake::HS_EXT_KMREQ;
 | 
						|
        logext += ",KMX";
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "createSrtHandshake: (ext: " << logext << ") data: " << hs.show());
 | 
						|
 | 
						|
    // NOTE: The HSREQ is practically always required, although may happen
 | 
						|
    // in future that CONCLUSION can be sent multiple times for a separate
 | 
						|
    // stream encryption support, and this way it won't enclose HSREQ.
 | 
						|
    // Also, KMREQ may occur multiple times.
 | 
						|
 | 
						|
    // So, initially store the UDT legacy handshake.
 | 
						|
    size_t hs_size = pkt.getLength(), total_ra_size = (hs_size / sizeof(uint32_t)); // Maximum size of data
 | 
						|
    hs.store_to(pkt.m_pcData, Ref(hs_size));                                        // hs_size is updated
 | 
						|
 | 
						|
    size_t ra_size = hs_size / sizeof(int32_t);
 | 
						|
 | 
						|
    // Now attach the SRT handshake for HSREQ
 | 
						|
    size_t    offset = ra_size;
 | 
						|
    uint32_t *p      = reinterpret_cast<uint32_t *>(pkt.m_pcData);
 | 
						|
    // NOTE: since this point, ra_size has a size in int32_t elements, NOT BYTES.
 | 
						|
 | 
						|
    // The first 4-byte item is the CMD/LENGTH spec.
 | 
						|
    uint32_t *pcmdspec = p + offset; // Remember the location to be filled later, when we know the length
 | 
						|
    ++offset;
 | 
						|
 | 
						|
    // Now use the original function to store the actual SRT_HS data
 | 
						|
    // ra_size after that
 | 
						|
    // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements.
 | 
						|
    // WILL BE CHANGED HERE.
 | 
						|
    ra_size   = fillSrtHandshake(p + offset, total_ra_size - offset, srths_cmd, HS_VERSION_SRT1);
 | 
						|
    *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size
 | 
						|
              << " space left: " << (total_ra_size - offset));
 | 
						|
 | 
						|
    if (have_sid)
 | 
						|
    {
 | 
						|
        // Use only in REQ phase and only if stream name is set
 | 
						|
        offset += ra_size;
 | 
						|
        pcmdspec = p + offset;
 | 
						|
        ++offset;
 | 
						|
 | 
						|
        // Now prepare the string with 4-byte alignment. The string size is limited
 | 
						|
        // to half the payload size. Just a sanity check to not pack too much into
 | 
						|
        // the conclusion packet.
 | 
						|
        size_t size_limit = m_iMaxSRTPayloadSize / 2;
 | 
						|
 | 
						|
        if (m_sStreamName.size() >= size_limit)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "createSrtHandshake: stream id too long, limited to " << (size_limit - 1) << " bytes");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        size_t wordsize         = (m_sStreamName.size() + 3) / 4;
 | 
						|
        size_t aligned_bytesize = wordsize * 4;
 | 
						|
 | 
						|
        memset(p + offset, 0, aligned_bytesize);
 | 
						|
        memcpy(p + offset, m_sStreamName.data(), m_sStreamName.size());
 | 
						|
        // Preswap to little endian (in place due to possible padding zeros)
 | 
						|
        HtoILA((uint32_t *)(p + offset), (uint32_t *)(p + offset), wordsize);
 | 
						|
 | 
						|
        ra_size   = wordsize;
 | 
						|
        *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_SID) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "createSrtHandshake: after SID [" << m_sStreamName << "] length=" << m_sStreamName.size()
 | 
						|
                  << " alignedln=" << aligned_bytesize << ": offset=" << offset << " SID size=" << ra_size
 | 
						|
                  << " space left: " << (total_ra_size - offset));
 | 
						|
    }
 | 
						|
 | 
						|
    if (have_congctl)
 | 
						|
    {
 | 
						|
        // Pass the congctl to the other side as informational.
 | 
						|
        // The other side should reject connection if it uses a different congctl.
 | 
						|
        // The other side should also respond with the congctl it uses, if its non-default (for backward compatibility).
 | 
						|
 | 
						|
        // XXX Consider change the congctl settings in the listener socket to "adaptive"
 | 
						|
        // congctl and also "adaptive" value of CUDT::m_bMessageAPI so that the caller
 | 
						|
        // may ask for whatever kind of transmission it wants, or select transmission
 | 
						|
        // type differently for different connections, however with the same listener.
 | 
						|
 | 
						|
        offset += ra_size;
 | 
						|
        pcmdspec = p + offset;
 | 
						|
        ++offset;
 | 
						|
 | 
						|
        size_t wordsize         = (sm.size() + 3) / 4;
 | 
						|
        size_t aligned_bytesize = wordsize * 4;
 | 
						|
 | 
						|
        memset(p + offset, 0, aligned_bytesize);
 | 
						|
 | 
						|
        memcpy(p + offset, sm.data(), sm.size());
 | 
						|
        // Preswap to little endian (in place due to possible padding zeros)
 | 
						|
        HtoILA((uint32_t *)(p + offset), (uint32_t *)(p + offset), wordsize);
 | 
						|
 | 
						|
        ra_size   = wordsize;
 | 
						|
        *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_CONGESTION) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size()
 | 
						|
                  << " alignedln=" << aligned_bytesize << ": offset=" << offset << " CONGCTL size=" << ra_size
 | 
						|
                  << " space left: " << (total_ra_size - offset));
 | 
						|
    }
 | 
						|
 | 
						|
    if (have_filter)
 | 
						|
    {
 | 
						|
        offset += ra_size;
 | 
						|
        pcmdspec = p + offset;
 | 
						|
        ++offset;
 | 
						|
 | 
						|
        size_t wordsize         = (m_OPT_PktFilterConfigString.size() + 3) / 4;
 | 
						|
        size_t aligned_bytesize = wordsize * 4;
 | 
						|
 | 
						|
        memset(p + offset, 0, aligned_bytesize);
 | 
						|
        memcpy(p + offset, m_OPT_PktFilterConfigString.data(), m_OPT_PktFilterConfigString.size());
 | 
						|
 | 
						|
        ra_size   = wordsize;
 | 
						|
        *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_FILTER) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "createSrtHandshake: after filter [" << m_OPT_PktFilterConfigString << "] length="
 | 
						|
                  << m_OPT_PktFilterConfigString.size() << " alignedln=" << aligned_bytesize << ": offset=" << offset
 | 
						|
                  << " filter size=" << ra_size << " space left: " << (total_ra_size - offset));
 | 
						|
    }
 | 
						|
 | 
						|
    // When encryption turned on
 | 
						|
    if (have_kmreq)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "createSrtHandshake: "
 | 
						|
                  << (m_CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION"));
 | 
						|
        if (srtkm_cmd == SRT_CMD_KMREQ)
 | 
						|
        {
 | 
						|
            bool have_any_keys = false;
 | 
						|
            for (size_t ki = 0; ki < 2; ++ki)
 | 
						|
            {
 | 
						|
                // Skip those that have expired
 | 
						|
                if (!m_pCryptoControl->getKmMsg_needSend(ki, false))
 | 
						|
                    continue;
 | 
						|
 | 
						|
                m_pCryptoControl->getKmMsg_markSent(ki, false);
 | 
						|
 | 
						|
                offset += ra_size;
 | 
						|
 | 
						|
                size_t msglen = m_pCryptoControl->getKmMsg_size(ki);
 | 
						|
                // Make ra_size back in element unit
 | 
						|
                // Add one extra word if the size isn't aligned to 32-bit.
 | 
						|
                ra_size = (msglen / sizeof(uint32_t)) + (msglen % sizeof(uint32_t) ? 1 : 0);
 | 
						|
 | 
						|
                // Store the CMD + SIZE in the next field
 | 
						|
                *(p + offset) = HS_CMDSPEC_CMD::wrap(srtkm_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
                ++offset;
 | 
						|
 | 
						|
                // Copy the key - do the endian inversion because another endian inversion
 | 
						|
                // will be done for every control message before sending, and this KM message
 | 
						|
                // is ALREADY in network order.
 | 
						|
                const uint32_t *keydata = reinterpret_cast<const uint32_t *>(m_pCryptoControl->getKmMsg_data(ki));
 | 
						|
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "createSrtHandshake: KMREQ: adding key #" << ki << " length=" << ra_size
 | 
						|
                          << " words (KmMsg_size=" << msglen << ")");
 | 
						|
                // XXX INSECURE ": [" << FormatBinaryString((uint8_t*)keydata, msglen) << "]";
 | 
						|
 | 
						|
                // Yes, I know HtoNLA and NtoHLA do exactly the same operation, but I want
 | 
						|
                // to be clear about the true intention.
 | 
						|
                NtoHLA(p + offset, keydata, ra_size);
 | 
						|
                have_any_keys = true;
 | 
						|
            }
 | 
						|
 | 
						|
            if (!have_any_keys)
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_IPE;
 | 
						|
                LOGC(mglog.Error, log << "createSrtHandshake: IPE: all keys have expired, no KM to send.");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (srtkm_cmd == SRT_CMD_KMRSP)
 | 
						|
        {
 | 
						|
            uint32_t        failure_kmrsp[] = {SRT_KM_S_UNSECURED};
 | 
						|
            const uint32_t *keydata         = 0;
 | 
						|
 | 
						|
            // Shift the starting point with the value of previously added block,
 | 
						|
            // to start with the new one.
 | 
						|
            offset += ra_size;
 | 
						|
 | 
						|
            if (kmdata_wordsize == 0)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error,
 | 
						|
                     log << "createSrtHandshake: Agent has PW, but Peer sent no KMREQ. Sending error KMRSP response");
 | 
						|
                ra_size = 1;
 | 
						|
                keydata = failure_kmrsp;
 | 
						|
 | 
						|
                // Update the KM state as well
 | 
						|
                m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET;  // Agent has PW, but Peer won't decrypt
 | 
						|
                m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Peer won't encrypt as well.
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (!kmdata)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_IPE;
 | 
						|
                    LOGC(mglog.Fatal, log << "createSrtHandshake: IPE: srtkm_cmd=SRT_CMD_KMRSP and no kmdata!");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                ra_size = kmdata_wordsize;
 | 
						|
                keydata = reinterpret_cast<const uint32_t *>(kmdata);
 | 
						|
            }
 | 
						|
 | 
						|
            *(p + offset) = HS_CMDSPEC_CMD::wrap(srtkm_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size);
 | 
						|
            ++offset; // Once cell, containting CMD spec and size
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "createSrtHandshake: KMRSP: applying returned key length="
 | 
						|
                      << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata,
 | 
						|
                                   // kmdata_wordsize*sizeof(uint32_t)) << "]";
 | 
						|
 | 
						|
            NtoHLA(p + offset, keydata, ra_size);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_IPE;
 | 
						|
            LOGC(mglog.Fatal, log << "createSrtHandshake: IPE: wrong value of srtkm_cmd: " << srtkm_cmd);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // ra_size + offset has a value in element unit.
 | 
						|
    // Switch it again to byte unit.
 | 
						|
    pkt.setLength((ra_size + offset) * sizeof(int32_t));
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "createSrtHandshake: filled HSv5 handshake flags: " << CHandShake::ExtensionFlagStr(hs.m_iType)
 | 
						|
              << " length: " << pkt.getLength() << " bytes");
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
FindExtensionBlock(uint32_t *begin, size_t total_length, ref_t<size_t> r_out_len, ref_t<uint32_t *> r_next_block)
 | 
						|
{
 | 
						|
    // Check if there's anything to process
 | 
						|
    if (total_length == 0)
 | 
						|
    {
 | 
						|
        *r_next_block = NULL;
 | 
						|
        *r_out_len    = 0;
 | 
						|
        return SRT_CMD_NONE;
 | 
						|
    }
 | 
						|
 | 
						|
    size_t &   out_len    = *r_out_len;
 | 
						|
    uint32_t *&next_block = *r_next_block;
 | 
						|
    // This function extracts the block command from the block and its length.
 | 
						|
    // The command value is returned as a function result.
 | 
						|
    // The size of that command block is stored into out_len.
 | 
						|
    // The beginning of the prospective next block is stored in next_block.
 | 
						|
 | 
						|
    // The caller must be aware that:
 | 
						|
    // - exactly one element holds the block header (cmd+size), so the actual data are after this one.
 | 
						|
    // - the returned size is the number of uint32_t elements since that first data element
 | 
						|
    // - the remaining size should be manually calculated as total_length - 1 - out_len, or
 | 
						|
    // simply, as next_block - begin.
 | 
						|
 | 
						|
    // Note that if the total_length is too short to extract the whole block, it will return
 | 
						|
    // SRT_CMD_NONE. Note that total_length includes this first CMDSPEC word.
 | 
						|
    //
 | 
						|
    // When SRT_CMD_NONE is returned, it means that nothing has been extracted and nothing else
 | 
						|
    // can be further extracted from this block.
 | 
						|
 | 
						|
    int    cmd  = HS_CMDSPEC_CMD::unwrap(*begin);
 | 
						|
    size_t size = HS_CMDSPEC_SIZE::unwrap(*begin);
 | 
						|
 | 
						|
    if (size + 1 > total_length)
 | 
						|
        return SRT_CMD_NONE;
 | 
						|
 | 
						|
    out_len = size;
 | 
						|
 | 
						|
    if (total_length == size + 1)
 | 
						|
        next_block = NULL;
 | 
						|
    else
 | 
						|
        next_block = begin + 1 + size;
 | 
						|
 | 
						|
    return cmd;
 | 
						|
}
 | 
						|
 | 
						|
static inline bool NextExtensionBlock(ref_t<uint32_t *> begin, uint32_t *next, ref_t<size_t> length)
 | 
						|
{
 | 
						|
    if (!next)
 | 
						|
        return false;
 | 
						|
 | 
						|
    *length = *length - (next - *begin);
 | 
						|
    *begin  = next;
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::processSrtMsg(const CPacket *ctrlpkt)
 | 
						|
{
 | 
						|
    uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData;
 | 
						|
    size_t    len     = ctrlpkt->getLength();
 | 
						|
    int       etype   = ctrlpkt->getExtendedType();
 | 
						|
    uint32_t  ts      = ctrlpkt->m_iTimeStamp;
 | 
						|
 | 
						|
    int res = SRT_CMD_NONE;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "Dispatching message type=" << etype << " data length=" << (len / sizeof(int32_t)));
 | 
						|
    switch (etype)
 | 
						|
    {
 | 
						|
    case SRT_CMD_HSREQ:
 | 
						|
    {
 | 
						|
        res = processSrtMsg_HSREQ(srtdata, len, ts, CUDT::HS_VERSION_UDT4);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    case SRT_CMD_HSRSP:
 | 
						|
    {
 | 
						|
        res = processSrtMsg_HSRSP(srtdata, len, ts, CUDT::HS_VERSION_UDT4);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    case SRT_CMD_KMREQ:
 | 
						|
        // Special case when the data need to be processed here
 | 
						|
        // and the appropriate message must be constructed for sending.
 | 
						|
        // No further processing required
 | 
						|
        {
 | 
						|
            uint32_t srtdata_out[SRTDATA_MAXSIZE];
 | 
						|
            size_t   len_out = 0;
 | 
						|
            res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, srtdata_out, Ref(len_out), CUDT::HS_VERSION_UDT4);
 | 
						|
            if (res == SRT_CMD_KMRSP)
 | 
						|
            {
 | 
						|
                if (len_out == 1)
 | 
						|
                {
 | 
						|
                    if (m_bOPT_StrictEncryption)
 | 
						|
                    {
 | 
						|
                        LOGC(mglog.Error,
 | 
						|
                             log << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0]))
 | 
						|
                                 << " - rejecting per strict encryption");
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
                    HLOGC(mglog.Debug,
 | 
						|
                          log << "MKREQ -> KMRSP FAILURE state: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])));
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    HLOGC(mglog.Debug, log << "KMREQ -> requested to send KMRSP length=" << len_out);
 | 
						|
                }
 | 
						|
                sendSrtMsg(SRT_CMD_KMRSP, srtdata_out, len_out);
 | 
						|
            }
 | 
						|
            // XXX Dead code. processSrtMsg_KMREQ now doesn't return any other value now.
 | 
						|
            // Please review later.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error, log << "KMREQ failed to process the request - ignoring");
 | 
						|
            }
 | 
						|
 | 
						|
            return true; // already done what's necessary
 | 
						|
        }
 | 
						|
 | 
						|
    case SRT_CMD_KMRSP:
 | 
						|
    {
 | 
						|
        // KMRSP doesn't expect any following action
 | 
						|
        m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, CUDT::HS_VERSION_UDT4);
 | 
						|
        return true; // nothing to do
 | 
						|
    }
 | 
						|
 | 
						|
    default:
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (res == SRT_CMD_NONE)
 | 
						|
        return true;
 | 
						|
 | 
						|
    // Send the message that the message handler requested.
 | 
						|
    sendSrtMsg(res);
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t len, uint32_t ts, int hsv)
 | 
						|
{
 | 
						|
    // Set this start time in the beginning, regardless as to whether TSBPD is being
 | 
						|
    // used or not. This must be done in the Initiator as well as Responder.
 | 
						|
 | 
						|
    /*
 | 
						|
     * Compute peer StartTime in our time reference
 | 
						|
     * This takes time zone, time drift into account.
 | 
						|
     * Also includes current packet transit time (rtt/2)
 | 
						|
     */
 | 
						|
#if 0 // Debug PeerStartTime if not 1st HS packet
 | 
						|
    {
 | 
						|
        uint64_t oldPeerStartTime = m_ullRcvPeerStartTime;
 | 
						|
        m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts);
 | 
						|
        if (oldPeerStartTime) {
 | 
						|
            LOGC(mglog.Note, log << "rcvSrtMsg: 2nd PeerStartTime diff=" <<  
 | 
						|
                    (m_ullRcvPeerStartTime - oldPeerStartTime) << " usec");
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
#else
 | 
						|
    m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts);
 | 
						|
#endif
 | 
						|
 | 
						|
    // Prepare the initial runtime values of latency basing on the option values.
 | 
						|
    // They are going to get the value fixed HERE.
 | 
						|
    m_iTsbPdDelay_ms     = m_iOPT_TsbPdDelay;
 | 
						|
    m_iPeerTsbPdDelay_ms = m_iOPT_PeerTsbPdDelay;
 | 
						|
 | 
						|
    if (len < SRT_CMD_HSREQ_MINSZ)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        /* Packet smaller than minimum compatible packet size */
 | 
						|
        LOGF(mglog.Error, "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " invalid", SRT_CMD_HSREQ, len);
 | 
						|
        return SRT_CMD_NONE;
 | 
						|
    }
 | 
						|
 | 
						|
    LOGF(mglog.Note,
 | 
						|
         "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " vers=0x%x opts=0x%x delay=%d",
 | 
						|
         SRT_CMD_HSREQ,
 | 
						|
         len,
 | 
						|
         srtdata[SRT_HS_VERSION],
 | 
						|
         srtdata[SRT_HS_FLAGS],
 | 
						|
         SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]));
 | 
						|
 | 
						|
    m_lPeerSrtVersion = srtdata[SRT_HS_VERSION];
 | 
						|
    m_lPeerSrtFlags   = srtdata[SRT_HS_FLAGS];
 | 
						|
 | 
						|
    if (hsv == CUDT::HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        if (m_lPeerSrtVersion >= SRT_VERSION_FEAT_HSv5)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HSREQ/rcv: With HSv4 version >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5)
 | 
						|
                     << " is not acceptable.");
 | 
						|
            return SRT_CMD_REJECT;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (m_lPeerSrtVersion < SRT_VERSION_FEAT_HSv5)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HSREQ/rcv: With HSv5 version must be >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) << " .");
 | 
						|
            return SRT_CMD_REJECT;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Check also if the version satisfies the minimum required version
 | 
						|
    if (m_lPeerSrtVersion < m_lMinimumPeerSrtVersion)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_VERSION;
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_lPeerSrtVersion)
 | 
						|
                 << " is too old for requested: " << SrtVersionString(m_lMinimumPeerSrtVersion) << " - REJECTING");
 | 
						|
        return SRT_CMD_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_lPeerSrtVersion) << " Flags: " << m_lPeerSrtFlags
 | 
						|
              << "(" << SrtFlagString(m_lPeerSrtFlags) << ")");
 | 
						|
 | 
						|
    m_bPeerRexmitFlag = IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG);
 | 
						|
    HLOGF(mglog.Debug, "HSREQ/rcv: peer %s REXMIT flag", m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND");
 | 
						|
 | 
						|
    // Check if both use the same API type. Reject if not.
 | 
						|
    bool peer_message_api = !IsSet(m_lPeerSrtFlags, SRT_OPT_STREAM);
 | 
						|
    if (peer_message_api != m_bMessageAPI)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_MESSAGEAPI;
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "HSREQ/rcv: Agent uses " << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, but the Peer declares "
 | 
						|
                 << (peer_message_api ? "MESSAGE" : "STREAM") << " API. Not compatible transmission type, rejecting.");
 | 
						|
        return SRT_CMD_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    if (len < SRT_HS_LATENCY + 1)
 | 
						|
    {
 | 
						|
        // 3 is the size when containing VERSION, FLAGS and LATENCY. Less size
 | 
						|
        // makes it contain only the first two. Let's make it acceptable, as long
 | 
						|
        // as the latency flags aren't set.
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND) || IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV))
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, but TSBPD flags are set. Rejecting.");
 | 
						|
            return SRT_CMD_REJECT;
 | 
						|
        }
 | 
						|
 | 
						|
        LOGC(mglog.Warn, log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, not getting any TSBPD settings.");
 | 
						|
        // Don't process any further settings in this case. Turn off TSBPD, just for a case.
 | 
						|
        m_bTsbPd     = false;
 | 
						|
        m_bPeerTsbPd = false;
 | 
						|
        return SRT_CMD_HSRSP;
 | 
						|
    }
 | 
						|
 | 
						|
    uint32_t latencystr = srtdata[SRT_HS_LATENCY];
 | 
						|
 | 
						|
    if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND))
 | 
						|
    {
 | 
						|
        // TimeStamp-based Packet Delivery feature enabled
 | 
						|
        if (!m_bTsbPd)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Warn, log << "HSREQ/rcv: Agent did not set rcv-TSBPD - ignoring proposed latency from peer");
 | 
						|
 | 
						|
            // Note: also don't set the peer TSBPD flag HERE because
 | 
						|
            // - in HSv4 it will be a sender, so it doesn't matter anyway
 | 
						|
            // - in HSv5 if it's going to receive, the TSBPDRCV flag will define it.
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            int peer_decl_latency;
 | 
						|
            if (hsv < CUDT::HS_VERSION_SRT1)
 | 
						|
            {
 | 
						|
                // In HSv4 there is only one value and this is the latency
 | 
						|
                // that the sender peer proposes for the agent.
 | 
						|
                peer_decl_latency = SRT_HS_LATENCY_LEG::unwrap(latencystr);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // In HSv5 there are latency declared for sending and receiving separately.
 | 
						|
 | 
						|
                // SRT_HS_LATENCY_SND is the value that the peer proposes to be the
 | 
						|
                // value used by agent when receiving data. We take this as a local latency value.
 | 
						|
                peer_decl_latency = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]);
 | 
						|
            }
 | 
						|
 | 
						|
            // Use the maximum latency out of latency from our settings and the latency
 | 
						|
            // "proposed" by the peer.
 | 
						|
            int maxdelay = std::max(m_iTsbPdDelay_ms, peer_decl_latency);
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "HSREQ/rcv: LOCAL/RCV LATENCY: Agent:" << m_iTsbPdDelay_ms << " Peer:" << peer_decl_latency
 | 
						|
                      << "  Selecting:" << maxdelay);
 | 
						|
            m_iTsbPdDelay_ms = maxdelay;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        std::string how_about_agent = m_bTsbPd ? "BUT AGENT DOES" : "and nor does Agent";
 | 
						|
        HLOGC(mglog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for sending - " << how_about_agent);
 | 
						|
    }
 | 
						|
 | 
						|
    // This happens when the HSv5 RESPONDER receives the HSREQ message; it declares
 | 
						|
    // that the peer INITIATOR will receive the data and informs about its predefined
 | 
						|
    // latency. We need to maximize this with our setting of the peer's latency and
 | 
						|
    // record as peer's latency, which will be then sent back with HSRSP.
 | 
						|
    if (hsv > CUDT::HS_VERSION_UDT4 && IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV))
 | 
						|
    {
 | 
						|
        // So, PEER uses TSBPD, set the flag.
 | 
						|
        // NOTE: it doesn't matter, if AGENT uses TSBPD.
 | 
						|
        m_bPeerTsbPd = true;
 | 
						|
 | 
						|
        // SRT_HS_LATENCY_RCV is the value that the peer declares as to be
 | 
						|
        // used by it when receiving data. We take this as a peer's value,
 | 
						|
        // and select the maximum of this one and our proposed latency for the peer.
 | 
						|
        int peer_decl_latency = SRT_HS_LATENCY_RCV::unwrap(latencystr);
 | 
						|
        int maxdelay          = std::max(m_iPeerTsbPdDelay_ms, peer_decl_latency);
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "HSREQ/rcv: PEER/RCV LATENCY: Agent:" << m_iPeerTsbPdDelay_ms << " Peer:" << peer_decl_latency
 | 
						|
                  << " Selecting:" << maxdelay);
 | 
						|
        m_iPeerTsbPdDelay_ms = maxdelay;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        std::string how_about_agent = m_bTsbPd ? "BUT AGENT DOES" : "and nor does Agent";
 | 
						|
        HLOGC(mglog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for receiving - " << how_about_agent);
 | 
						|
    }
 | 
						|
 | 
						|
    if (hsv > CUDT::HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        // This is HSv5, do the same things as required for the sending party in HSv4,
 | 
						|
        // as in HSv5 this can also be a sender.
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP))
 | 
						|
        {
 | 
						|
            // Too late packets dropping feature supported
 | 
						|
            m_bPeerTLPktDrop = true;
 | 
						|
        }
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT))
 | 
						|
        {
 | 
						|
            // Peer will send Periodic NAK Reports
 | 
						|
            m_bPeerNakReport = true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return SRT_CMD_HSRSP;
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t len, uint32_t ts, int hsv)
 | 
						|
{
 | 
						|
    // XXX Check for mis-version
 | 
						|
    // With HSv4 we accept only version less than 1.2.0
 | 
						|
    if (hsv == CUDT::HS_VERSION_UDT4 && srtdata[SRT_HS_VERSION] >= SRT_VERSION_FEAT_HSv5)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Error, log << "HSRSP/rcv: With HSv4 version >= 1.2.0 is not acceptable.");
 | 
						|
        return SRT_CMD_NONE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (len < SRT_CMD_HSRSP_MINSZ)
 | 
						|
    {
 | 
						|
        /* Packet smaller than minimum compatible packet size */
 | 
						|
        LOGF(mglog.Error, "HSRSP/rcv: cmd=%d(HSRSP) len=%" PRIzu " invalid", SRT_CMD_HSRSP, len);
 | 
						|
        return SRT_CMD_NONE;
 | 
						|
    }
 | 
						|
 | 
						|
    // Set this start time in the beginning, regardless as to whether TSBPD is being
 | 
						|
    // used or not. This must be done in the Initiator as well as Responder. In case when
 | 
						|
    // agent is sender only (HSv4) this value simply won't be used.
 | 
						|
 | 
						|
    /*
 | 
						|
     * Compute peer StartTime in our time reference
 | 
						|
     * This takes time zone, time drift into account.
 | 
						|
     * Also includes current packet transit time (rtt/2)
 | 
						|
     */
 | 
						|
#if 0 // Debug PeerStartTime if not 1st HS packet
 | 
						|
    {
 | 
						|
        uint64_t oldPeerStartTime = m_ullRcvPeerStartTime;
 | 
						|
        m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts);
 | 
						|
        if (oldPeerStartTime) {
 | 
						|
            LOGC(mglog.Note, log << "rcvSrtMsg: 2nd PeerStartTime diff=" <<  
 | 
						|
                    (m_ullRcvPeerStartTime - oldPeerStartTime) << " usec");
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
#else
 | 
						|
    m_ullRcvPeerStartTime = CTimer::getTime() - (uint64_t)((uint32_t)ts);
 | 
						|
#endif
 | 
						|
 | 
						|
    m_lPeerSrtVersion = srtdata[SRT_HS_VERSION];
 | 
						|
    m_lPeerSrtFlags   = srtdata[SRT_HS_FLAGS];
 | 
						|
 | 
						|
    HLOGF(mglog.Debug,
 | 
						|
          "HSRSP/rcv: Version: %s Flags: SND:%08X (%s)",
 | 
						|
          SrtVersionString(m_lPeerSrtVersion).c_str(),
 | 
						|
          m_lPeerSrtFlags,
 | 
						|
          SrtFlagString(m_lPeerSrtFlags).c_str());
 | 
						|
 | 
						|
    if (hsv == CUDT::HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        // The old HSv4 way: extract just one value and put it under peer.
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV))
 | 
						|
        {
 | 
						|
            // TsbPd feature enabled
 | 
						|
            m_bPeerTsbPd         = true;
 | 
						|
            m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_LEG::unwrap(srtdata[SRT_HS_LATENCY]);
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms
 | 
						|
                      << " (Agent: declared:" << m_iTsbPdDelay_ms << " rcv:" << m_iTsbPdDelay_ms << ")");
 | 
						|
        }
 | 
						|
        // TSBPDSND isn't set in HSv4 by the RESPONDER, because HSv4 RESPONDER is always RECEIVER.
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // HSv5 way: extract the receiver latency and sender latency, if used.
 | 
						|
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDRCV))
 | 
						|
        {
 | 
						|
            // TsbPd feature enabled
 | 
						|
            m_bPeerTsbPd         = true;
 | 
						|
            m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]);
 | 
						|
            HLOGC(mglog.Debug, log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << "ms");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "HSRSP/rcv: Peer (responder) DOES NOT USE latency");
 | 
						|
        }
 | 
						|
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_TSBPDSND))
 | 
						|
        {
 | 
						|
            if (!m_bTsbPd)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Warn,
 | 
						|
                     log << "HSRSP/rcv: BUG? Peer (responder) declares sending latency, but Agent turned off TSBPD.");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Take this value as a good deal. In case when the Peer did not "correct" the latency
 | 
						|
                // because it has TSBPD turned off, just stay with the present value defined in options.
 | 
						|
                m_iTsbPdDelay_ms = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]);
 | 
						|
                HLOGC(mglog.Debug, log << "HSRSP/rcv: LATENCY Agent/rcv: " << m_iTsbPdDelay_ms << "ms");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((m_lSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_lPeerSrtFlags, SRT_OPT_TLPKTDROP))
 | 
						|
    {
 | 
						|
        // Too late packets dropping feature supported
 | 
						|
        m_bPeerTLPktDrop = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((m_lSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_lPeerSrtFlags, SRT_OPT_NAKREPORT))
 | 
						|
    {
 | 
						|
        // Peer will send Periodic NAK Reports
 | 
						|
        m_bPeerNakReport = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_lSrtVersion >= SrtVersion(1, 2, 0))
 | 
						|
    {
 | 
						|
        if (IsSet(m_lPeerSrtFlags, SRT_OPT_REXMITFLG))
 | 
						|
        {
 | 
						|
            // Peer will use REXMIT flag in packet retransmission.
 | 
						|
            m_bPeerRexmitFlag = true;
 | 
						|
            HLOGP(mglog.Debug, "HSRSP/rcv: 1.2.0+ Agent understands REXMIT flag and so does peer.");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGP(mglog.Debug, "HSRSP/rcv: Agent understands REXMIT flag, but PEER DOES NOT");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGF(mglog.Debug, "HSRSP/rcv: <1.2.0 Agent DOESN'T understand REXMIT flag");
 | 
						|
    }
 | 
						|
 | 
						|
    handshakeDone();
 | 
						|
 | 
						|
    return SRT_CMD_NONE;
 | 
						|
}
 | 
						|
 | 
						|
// This function is called only when the URQ_CONCLUSION handshake has been received from the peer.
 | 
						|
bool CUDT::interpretSrtHandshake(const CHandShake &hs,
 | 
						|
                                 const CPacket &   hspkt,
 | 
						|
                                 uint32_t *out_data SRT_ATR_UNUSED,
 | 
						|
                                 size_t *           out_len)
 | 
						|
{
 | 
						|
    // Initialize out_len to 0 to handle the unencrypted case
 | 
						|
    if (out_len)
 | 
						|
        *out_len = 0;
 | 
						|
 | 
						|
    // The version=0 statement as rejection is used only since HSv5.
 | 
						|
    // The HSv4 sends the AGREEMENT handshake message with version=0, do not misinterpret it.
 | 
						|
    if (m_ConnRes.m_iVersion > HS_VERSION_UDT4 && hs.m_iVersion == 0)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_PEER;
 | 
						|
        LOGC(mglog.Error, log << "HS VERSION = 0, meaning the handshake has been rejected.");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (hs.m_iVersion < HS_VERSION_SRT1)
 | 
						|
        return true; // do nothing
 | 
						|
 | 
						|
    // Anyway, check if the handshake contains any extra data.
 | 
						|
    if (hspkt.getLength() <= CHandShake::m_iContentSize)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        // This would mean that the handshake was at least HSv5, but somehow no extras were added.
 | 
						|
        // Dismiss it then, however this has to be logged.
 | 
						|
        LOGC(mglog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension found!");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // We still believe it should work, let's check the flags.
 | 
						|
    int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType);
 | 
						|
    if (ext_flags == 0)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        LOGC(mglog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension flags are set!");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "HS VERSION=" << hs.m_iVersion << " EXTENSIONS: " << CHandShake::ExtensionFlagStr(ext_flags));
 | 
						|
 | 
						|
    // Ok, now find the beginning of an int32_t array that follows the UDT handshake.
 | 
						|
    uint32_t *p    = reinterpret_cast<uint32_t *>(hspkt.m_pcData + CHandShake::m_iContentSize);
 | 
						|
    size_t    size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0
 | 
						|
 | 
						|
    if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting HSREQ/RSP type extension");
 | 
						|
        uint32_t *begin    = p;
 | 
						|
        uint32_t *next     = 0;
 | 
						|
        size_t    length   = size / sizeof(uint32_t);
 | 
						|
        size_t    blocklen = 0;
 | 
						|
 | 
						|
        for (;;) // this is ONE SHOT LOOP
 | 
						|
        {
 | 
						|
            int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next));
 | 
						|
 | 
						|
            size_t bytelen = blocklen * sizeof(uint32_t);
 | 
						|
 | 
						|
            if (cmd == SRT_CMD_HSREQ)
 | 
						|
            {
 | 
						|
                // Set is the size as it should, then give it for interpretation for
 | 
						|
                // the proper function.
 | 
						|
                if (blocklen < SRT_HS__SIZE)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "HS-ext HSREQ found but invalid size: " << bytelen << " (expected: " << SRT_HS__SIZE
 | 
						|
                             << ")");
 | 
						|
                    return false; // don't interpret
 | 
						|
                }
 | 
						|
 | 
						|
                int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1);
 | 
						|
                // Interpreted? Then it should be responded with SRT_CMD_HSRSP.
 | 
						|
                if (rescmd != SRT_CMD_HSRSP)
 | 
						|
                {
 | 
						|
                    // m_RejectReason already set
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "interpretSrtHandshake: process HSREQ returned unexpected value " << rescmd);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                handshakeDone();
 | 
						|
                updateAfterSrtHandshake(SRT_CMD_HSREQ, HS_VERSION_SRT1);
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_HSRSP)
 | 
						|
            {
 | 
						|
                // Set is the size as it should, then give it for interpretation for
 | 
						|
                // the proper function.
 | 
						|
                if (blocklen < SRT_HS__SIZE)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "HS-ext HSRSP found but invalid size: " << bytelen << " (expected: " << SRT_HS__SIZE
 | 
						|
                             << ")");
 | 
						|
 | 
						|
                    return false; // don't interpret
 | 
						|
                }
 | 
						|
 | 
						|
                int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1);
 | 
						|
                // Interpreted? Then it should be responded with SRT_CMD_NONE.
 | 
						|
                // (nothing to be responded for HSRSP, unless there was some kinda problem)
 | 
						|
                if (rescmd != SRT_CMD_NONE)
 | 
						|
                {
 | 
						|
                    // Just formally; the current code doesn't seem to return anything else.
 | 
						|
                    m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "interpretSrtHandshake: process HSRSP returned unexpected value " << rescmd);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                handshakeDone();
 | 
						|
                updateAfterSrtHandshake(SRT_CMD_HSRSP, HS_VERSION_SRT1);
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_NONE)
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                LOGC(mglog.Error, log << "interpretSrtHandshake: no HSREQ/HSRSP block found in the handshake msg!");
 | 
						|
                // This means that there can be no more processing done by FindExtensionBlock().
 | 
						|
                // And we haven't found what we need - otherwise one of the above cases would pass
 | 
						|
                // and lead to exit this loop immediately.
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Any other kind of message extracted. Search on.
 | 
						|
                length -= (next - begin);
 | 
						|
                begin = next;
 | 
						|
                if (begin)
 | 
						|
                    continue;
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "interpretSrtHandshake: HSREQ done, checking KMREQ");
 | 
						|
 | 
						|
    // Now check the encrypted
 | 
						|
 | 
						|
    bool encrypted = false;
 | 
						|
 | 
						|
    if (IsSet(ext_flags, CHandShake::HS_EXT_KMREQ))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting KMREQ/RSP type extension");
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_ENCRYPTION
 | 
						|
        if (!m_pCryptoControl->hasPassphrase())
 | 
						|
        {
 | 
						|
            if (m_bOPT_StrictEncryption)
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_UNSECURE;
 | 
						|
                LOGC(
 | 
						|
                    mglog.Error,
 | 
						|
                    log << "HS KMREQ: Peer declares encryption, but agent does not - rejecting per strict requirement");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HS KMREQ: Peer declares encryption, but agent does not - still allowing connection.");
 | 
						|
 | 
						|
            // Still allow for connection, and allow Agent to send unencrypted stream to the peer.
 | 
						|
            // Also normally allow the key to be processed; worst case it will send the failure response.
 | 
						|
        }
 | 
						|
 | 
						|
        uint32_t *begin    = p;
 | 
						|
        uint32_t *next     = 0;
 | 
						|
        size_t    length   = size / sizeof(uint32_t);
 | 
						|
        size_t    blocklen = 0;
 | 
						|
 | 
						|
        for (;;) // This is one shot loop, unless REPEATED by 'continue'.
 | 
						|
        {
 | 
						|
            int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next));
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd));
 | 
						|
 | 
						|
            size_t bytelen = blocklen * sizeof(uint32_t);
 | 
						|
            if (cmd == SRT_CMD_KMREQ)
 | 
						|
            {
 | 
						|
                if (!out_data || !out_len)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_IPE;
 | 
						|
                    LOGC(mglog.Fatal, log << "IPE: HS/KMREQ extracted without passing target buffer!");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                int res =
 | 
						|
                    m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, out_data, Ref(*out_len), HS_VERSION_SRT1);
 | 
						|
                if (res != SRT_CMD_KMRSP)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_IPE;
 | 
						|
                    // Something went wrong.
 | 
						|
                    HLOGC(mglog.Debug,
 | 
						|
                          log << "interpretSrtHandshake: IPE/EPE KMREQ processing failed - returned " << res);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                if (*out_len == 1)
 | 
						|
                {
 | 
						|
                    // This means that there was an abnormal encryption situation occurred.
 | 
						|
                    // This is inacceptable in case of strict encryption.
 | 
						|
                    if (m_bOPT_StrictEncryption)
 | 
						|
                    {
 | 
						|
                        if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADSECRET)
 | 
						|
                        {
 | 
						|
                            m_RejectReason = SRT_REJ_BADSECRET;
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            m_RejectReason = SRT_REJ_UNSECURE;
 | 
						|
                        }
 | 
						|
                        LOGC(mglog.Error,
 | 
						|
                             log << "interpretSrtHandshake: KMREQ result abnornal - rejecting per strict encryption");
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                encrypted = true;
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_KMRSP)
 | 
						|
            {
 | 
						|
                int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1);
 | 
						|
                if (m_bOPT_StrictEncryption && res == -1)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_UNSECURE;
 | 
						|
                    LOGC(mglog.Error, log << "KMRSP failed - rejecting connection as per strict encryption.");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                encrypted = true;
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_NONE)
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                LOGC(mglog.Error, log << "HS KMREQ expected - none found!");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd));
 | 
						|
                if (NextExtensionBlock(Ref(begin), next, Ref(length)))
 | 
						|
                    continue;
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
#else
 | 
						|
        // When encryption is not enabled at compile time, behave as if encryption wasn't set,
 | 
						|
        // so accordingly to StrictEncryption flag.
 | 
						|
 | 
						|
        if (m_bOPT_StrictEncryption)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_UNSECURE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - rejecting "
 | 
						|
                        "per strict requirement");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - still allowing "
 | 
						|
                    "connection.");
 | 
						|
        encrypted = true;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    bool   have_congctl = false;
 | 
						|
    bool   have_filter  = false;
 | 
						|
    string agsm         = m_CongCtl.selected_name();
 | 
						|
    if (agsm == "")
 | 
						|
    {
 | 
						|
        agsm = "live";
 | 
						|
        m_CongCtl.select("live");
 | 
						|
    }
 | 
						|
 | 
						|
    if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "interpretSrtHandshake: extracting various CONFIG extensions");
 | 
						|
 | 
						|
        uint32_t *begin    = p;
 | 
						|
        uint32_t *next     = 0;
 | 
						|
        size_t    length   = size / sizeof(uint32_t);
 | 
						|
        size_t    blocklen = 0;
 | 
						|
 | 
						|
        for (;;) // This is one shot loop, unless REPEATED by 'continue'.
 | 
						|
        {
 | 
						|
            int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next));
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd));
 | 
						|
 | 
						|
            const size_t bytelen = blocklen * sizeof(uint32_t);
 | 
						|
            if (cmd == SRT_CMD_SID)
 | 
						|
            {
 | 
						|
                if (!bytelen || bytelen > MAX_SID_LENGTH)
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH
 | 
						|
                             << " - PROTOCOL ERROR, REJECTING");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                // Copied through a cleared array. This is because the length is aligned to 4
 | 
						|
                // where the padding is filled by zero bytes. For the case when the string is
 | 
						|
                // exactly of a 4-divisible length, we make a big array with maximum allowed size
 | 
						|
                // filled with zeros. Copying to this array should then copy either only the valid
 | 
						|
                // characters of the string (if the lenght is divisible by 4), or the string with
 | 
						|
                // padding zeros. In all these cases in the resulting array we should have all
 | 
						|
                // subsequent characters of the string plus at least one '\0' at the end. This will
 | 
						|
                // make it a perfect NUL-terminated string, to be used to initialize a string.
 | 
						|
                char target[MAX_SID_LENGTH + 1];
 | 
						|
                memset(target, 0, MAX_SID_LENGTH + 1);
 | 
						|
                memcpy(target, begin + 1, bytelen);
 | 
						|
 | 
						|
                // Un-swap on big endian machines
 | 
						|
                ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
 | 
						|
 | 
						|
                m_sStreamName = target;
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "CONNECTOR'S REQUESTED SID [" << m_sStreamName << "] (bytelen=" << bytelen
 | 
						|
                          << " blocklen=" << blocklen << ")");
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_CONGESTION)
 | 
						|
            {
 | 
						|
                if (have_congctl)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
                    LOGC(mglog.Error, log << "CONGCTL BLOCK REPEATED!");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                if (!bytelen || bytelen > MAX_SID_LENGTH)
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "interpretSrtHandshake: CONGESTION-control type length " << bytelen << " is 0 or > "
 | 
						|
                             << +MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                // Declare that congctl has been received
 | 
						|
                have_congctl = true;
 | 
						|
 | 
						|
                char target[MAX_SID_LENGTH + 1];
 | 
						|
                memset(target, 0, MAX_SID_LENGTH + 1);
 | 
						|
                memcpy(target, begin + 1, bytelen);
 | 
						|
                // Un-swap on big endian machines
 | 
						|
                ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
 | 
						|
 | 
						|
                string sm = target;
 | 
						|
 | 
						|
                // As the congctl has been declared by the peer,
 | 
						|
                // check if your congctl is compatible.
 | 
						|
                // sm cannot be empty, but the agent's sm can be empty meaning live.
 | 
						|
                if (sm != agsm)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_CONGESTION;
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "PEER'S CONGCTL '" << sm << "' does not match AGENT'S CONGCTL '" << agsm << "'");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "CONNECTOR'S CONGCTL [" << sm << "] (bytelen=" << bytelen << " blocklen=" << blocklen
 | 
						|
                          << ")");
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_FILTER)
 | 
						|
            {
 | 
						|
                if (have_filter)
 | 
						|
                {
 | 
						|
                    m_RejectReason = SRT_REJ_FILTER;
 | 
						|
                    LOGC(mglog.Error, log << "FILTER BLOCK REPEATED!");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                // Declare that filter has been received
 | 
						|
                have_filter = true;
 | 
						|
 | 
						|
                // XXX This is the maximum string, but filter config
 | 
						|
                // shall be normally limited somehow, especially if used
 | 
						|
                // together with SID!
 | 
						|
                char target[MAX_SID_LENGTH + 1];
 | 
						|
                memset(target, 0, MAX_SID_LENGTH + 1);
 | 
						|
                memcpy(target, begin + 1, bytelen);
 | 
						|
                string fltcfg = target;
 | 
						|
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "PEER'S FILTER CONFIG [" << fltcfg << "] (bytelen=" << bytelen << " blocklen=" << blocklen
 | 
						|
                          << ")");
 | 
						|
 | 
						|
                if (!checkApplyFilterConfig(fltcfg))
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Error, log << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_NONE)
 | 
						|
            {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Found some block that is not interesting here. Skip this and get the next one.
 | 
						|
                HLOGC(mglog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd));
 | 
						|
            }
 | 
						|
 | 
						|
            if (!NextExtensionBlock(Ref(begin), next, Ref(length)))
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Post-checks
 | 
						|
    // Check if peer declared encryption
 | 
						|
    if (!encrypted && m_CryptoSecret.len > 0)
 | 
						|
    {
 | 
						|
        if (m_bOPT_StrictEncryption)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_UNSECURE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "HS EXT: Agent declares encryption, but Peer does not - rejecting connection per strict "
 | 
						|
                        "requirement.");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "HS EXT: Agent declares encryption, but Peer does not (Agent can still receive unencrypted packets "
 | 
						|
                    "from Peer).");
 | 
						|
 | 
						|
        // This is required so that the sender is still allowed to send data, when encryption is required,
 | 
						|
        // just this will be for waste because the receiver won't decrypt them anyway.
 | 
						|
        m_pCryptoControl->createFakeSndContext();
 | 
						|
        m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET;  // Because Peer did not send KMX, though Agent has pw
 | 
						|
        m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Because Peer has no PW, as has sent no KMREQ.
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // If agent has set some nondefault congctl, then congctl is expected from the peer.
 | 
						|
    if (agsm != "live" && !have_congctl)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_CONGESTION;
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "HS EXT: Agent uses '" << agsm << "' congctl, but peer DID NOT DECLARE congctl (assuming 'live').");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Ok, finished, for now.
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::checkApplyFilterConfig(const std::string &confstr)
 | 
						|
{
 | 
						|
    SrtFilterConfig cfg;
 | 
						|
    if (!ParseFilterConfig(confstr, cfg))
 | 
						|
        return false;
 | 
						|
 | 
						|
    // Now extract the type, if present, and
 | 
						|
    // check if you have this type of corrector available.
 | 
						|
    if (!PacketFilter::correctConfig(cfg))
 | 
						|
        return false;
 | 
						|
 | 
						|
    // Now parse your own string, if you have it.
 | 
						|
    if (m_OPT_PktFilterConfigString != "")
 | 
						|
    {
 | 
						|
        // - for rendezvous, both must be exactly the same, or only one side specified.
 | 
						|
        if (m_bRendezvous && m_OPT_PktFilterConfigString != confstr)
 | 
						|
        {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        SrtFilterConfig mycfg;
 | 
						|
        if (!ParseFilterConfig(m_OPT_PktFilterConfigString, mycfg))
 | 
						|
            return false;
 | 
						|
 | 
						|
        // Check only if both have set a filter of the same type.
 | 
						|
        if (mycfg.type != cfg.type)
 | 
						|
            return false;
 | 
						|
 | 
						|
        // If so, then:
 | 
						|
        // - for caller-listener configuration, accept the listener version.
 | 
						|
        if (m_SrtHsSide == HSD_INITIATOR)
 | 
						|
        {
 | 
						|
            // This is a caller, this should apply all parameters received
 | 
						|
            // from the listener, forcefully.
 | 
						|
            for (map<string, string>::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x)
 | 
						|
            {
 | 
						|
                mycfg.parameters[x->first] = x->second;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // On a listener, only apply those that you haven't set
 | 
						|
            for (map<string, string>::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x)
 | 
						|
            {
 | 
						|
                if (!mycfg.parameters.count(x->first))
 | 
						|
                    mycfg.parameters[x->first] = x->second;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "checkApplyFilterConfig: param: LOCAL: " << Printable(mycfg.parameters)
 | 
						|
                  << " FORGN: " << Printable(cfg.parameters));
 | 
						|
 | 
						|
        ostringstream myos;
 | 
						|
        myos << mycfg.type;
 | 
						|
        for (map<string, string>::iterator x = mycfg.parameters.begin(); x != mycfg.parameters.end(); ++x)
 | 
						|
        {
 | 
						|
            myos << "," << x->first << ":" << x->second;
 | 
						|
        }
 | 
						|
 | 
						|
        m_OPT_PktFilterConfigString = myos.str();
 | 
						|
 | 
						|
        HLOGC(mglog.Debug, log << "checkApplyFilterConfig: Effective config: " << m_OPT_PktFilterConfigString);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Take the foreign configuration as a good deal.
 | 
						|
        HLOGC(mglog.Debug, log << "checkApplyFilterConfig: Good deal config: " << m_OPT_PktFilterConfigString);
 | 
						|
        m_OPT_PktFilterConfigString = confstr;
 | 
						|
    }
 | 
						|
 | 
						|
    size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size;
 | 
						|
    if (m_zOPT_ExpPayloadSize > efc_max_payload_size)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Warn,
 | 
						|
             log << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to "
 | 
						|
                 << efc_max_payload_size << " bytes");
 | 
						|
        m_zOPT_ExpPayloadSize = efc_max_payload_size;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::startConnect(const sockaddr *serv_addr, int32_t forced_isn)
 | 
						|
{
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "startConnect: -> " << SockaddrToString(serv_addr) << "...");
 | 
						|
 | 
						|
    if (!m_bOpened)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
 | 
						|
 | 
						|
    if (m_bListening)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
    if (m_bConnecting || m_bConnected)
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
 | 
						|
 | 
						|
    // record peer/server address
 | 
						|
    delete m_pPeerAddr;
 | 
						|
    m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr *)new sockaddr_in : (sockaddr *)new sockaddr_in6;
 | 
						|
    memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6));
 | 
						|
 | 
						|
    // register this socket in the rendezvous queue
 | 
						|
    // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this
 | 
						|
    // function
 | 
						|
#ifdef SRT_ENABLE_CONNTIMEO
 | 
						|
    uint64_t ttl = m_iConnTimeOut * uint64_t(1000);
 | 
						|
#else
 | 
						|
    uint64_t ttl = 3000000;
 | 
						|
#endif
 | 
						|
    // XXX DEBUG
 | 
						|
    // ttl = 0x1000000000000000;
 | 
						|
    // XXX
 | 
						|
    if (m_bRendezvous)
 | 
						|
        ttl *= 10;
 | 
						|
    ttl += CTimer::getTime();
 | 
						|
    m_pRcvQueue->registerConnector(m_SocketID, this, m_iIPversion, serv_addr, ttl);
 | 
						|
 | 
						|
    // The m_iType is used in the INDUCTION for nothing. This value is only regarded
 | 
						|
    // in CONCLUSION handshake, however this must be created after the handshake version
 | 
						|
    // is already known. UDT_DGRAM is the value that was the only valid in the old SRT
 | 
						|
    // with HSv4 (it supported only live transmission), for HSv5 it will be changed to
 | 
						|
    // handle handshake extension flags.
 | 
						|
    m_ConnReq.m_iType = UDT_DGRAM;
 | 
						|
 | 
						|
    // This is my current configuration
 | 
						|
    if (m_bRendezvous)
 | 
						|
    {
 | 
						|
        // For rendezvous, use version 5 in the waveahand and the cookie.
 | 
						|
        // In case when you get the version 4 waveahand, simply switch to
 | 
						|
        // the legacy HSv4 rendezvous and this time send version 4 CONCLUSION.
 | 
						|
 | 
						|
        // The HSv4 client simply won't check the version nor the cookie and it
 | 
						|
        // will be sending its waveahands with version 4. Only when the party
 | 
						|
        // has sent version 5 waveahand should the agent continue with HSv5
 | 
						|
        // rendezvous.
 | 
						|
        m_ConnReq.m_iVersion = HS_VERSION_SRT1;
 | 
						|
        // m_ConnReq.m_iVersion = HS_VERSION_UDT4; // <--- Change in order to do regression test.
 | 
						|
        m_ConnReq.m_iReqType = URQ_WAVEAHAND;
 | 
						|
        m_ConnReq.m_iCookie  = bake(serv_addr);
 | 
						|
 | 
						|
        // This will be also passed to a HSv4 rendezvous, but fortunately the old
 | 
						|
        // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION.
 | 
						|
        m_ConnReq.m_iType           = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_iSndCryptoKeyLen);
 | 
						|
        bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0;
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "startConnect (rnd): " << (whether ? "" : "NOT ")
 | 
						|
                  << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen);
 | 
						|
        m_RdvState  = CHandShake::RDV_WAVING;
 | 
						|
        m_SrtHsSide = HSD_DRAW; // initially not resolved.
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // For caller-listener configuration, set the version 4 for INDUCTION
 | 
						|
        // due to a serious problem in UDT code being also in the older SRT versions:
 | 
						|
        // the listener peer simply sents the EXACT COPY of the caller's induction
 | 
						|
        // handshake, except the cookie, which means that when the caller sents version 5,
 | 
						|
        // the listener will respond with version 5, which is a false information. Therefore
 | 
						|
        // HSv5 clients MUST send HS_VERSION_UDT4 from the caller, regardless of currently
 | 
						|
        // supported handshake version.
 | 
						|
        //
 | 
						|
        // The HSv5 listener should only respond with INDUCTION with m_iVersion == HS_VERSION_SRT1.
 | 
						|
        m_ConnReq.m_iVersion = HS_VERSION_UDT4;
 | 
						|
        m_ConnReq.m_iReqType = URQ_INDUCTION;
 | 
						|
        m_ConnReq.m_iCookie  = 0;
 | 
						|
        m_RdvState           = CHandShake::RDV_INVALID;
 | 
						|
    }
 | 
						|
 | 
						|
    m_ConnReq.m_iMSS            = m_iMSS;
 | 
						|
    m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize;
 | 
						|
    m_ConnReq.m_iID             = m_SocketID;
 | 
						|
    CIPAddress::ntop(serv_addr, m_ConnReq.m_piPeerIP, m_iIPversion);
 | 
						|
 | 
						|
    if (forced_isn == 0)
 | 
						|
    {
 | 
						|
        // Random Initial Sequence Number (normal mode)
 | 
						|
        srand((unsigned int)CTimer::getTime());
 | 
						|
        m_iISN = m_ConnReq.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Predefined ISN (for debug purposes)
 | 
						|
        m_iISN = m_ConnReq.m_iISN = forced_isn;
 | 
						|
    }
 | 
						|
 | 
						|
    m_iLastDecSeq        = m_iISN - 1;
 | 
						|
    m_iSndLastAck        = m_iISN;
 | 
						|
    m_iSndLastDataAck    = m_iISN;
 | 
						|
    m_iSndLastFullAck    = m_iISN;
 | 
						|
    m_iSndCurrSeqNo      = m_iISN - 1;
 | 
						|
    m_iSndLastAck2       = m_iISN;
 | 
						|
    m_ullSndLastAck2Time = CTimer::getTime();
 | 
						|
 | 
						|
    // Inform the server my configurations.
 | 
						|
    CPacket reqpkt;
 | 
						|
    reqpkt.setControl(UMSG_HANDSHAKE);
 | 
						|
    reqpkt.allocate(m_iMaxSRTPayloadSize);
 | 
						|
    // XXX NOTE: Now the memory for the payload part is allocated automatically,
 | 
						|
    // and such allocated memory is also automatically deallocated in the
 | 
						|
    // destructor. If you use CPacket::allocate, remember that you must not:
 | 
						|
    // - delete this memory
 | 
						|
    // - assign to m_pcData.
 | 
						|
    // If you use only manual assignment to m_pCData, this is then manual
 | 
						|
    // allocation and so it won't be deallocated in the destructor.
 | 
						|
    //
 | 
						|
    // (Desired would be to disallow modification of m_pcData outside the
 | 
						|
    // control of methods.)
 | 
						|
 | 
						|
    // ID = 0, connection request
 | 
						|
    reqpkt.m_iID = 0;
 | 
						|
 | 
						|
    size_t hs_size = m_iMaxSRTPayloadSize;
 | 
						|
    m_ConnReq.store_to(reqpkt.m_pcData, Ref(hs_size));
 | 
						|
 | 
						|
    // Note that CPacket::allocate() sets also the size
 | 
						|
    // to the size of the allocated buffer, which not
 | 
						|
    // necessarily is to be the size of the data.
 | 
						|
    reqpkt.setLength(hs_size);
 | 
						|
 | 
						|
    uint64_t now        = CTimer::getTime();
 | 
						|
    reqpkt.m_iTimeStamp = int32_t(now - m_stats.startTime);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (" << now << "). SENDING HS: " << m_ConnReq.show());
 | 
						|
 | 
						|
    /*
 | 
						|
     * Race condition if non-block connect response thread scheduled before we set m_bConnecting to true?
 | 
						|
     * Connect response will be ignored and connecting will wait until timeout.
 | 
						|
     * Maybe m_ConnectionLock handling problem? Not used in CUDT::connect(const CPacket& response)
 | 
						|
     */
 | 
						|
    m_llLastReqTime = now;
 | 
						|
    m_bConnecting   = true;
 | 
						|
    m_pSndQueue->sendto(serv_addr, reqpkt);
 | 
						|
 | 
						|
    //
 | 
						|
    ///
 | 
						|
    ////  ---> CONTINUE TO: <PEER>.CUDT::processConnectRequest()
 | 
						|
    ///        (Take the part under condition: hs.m_iReqType == URQ_INDUCTION)
 | 
						|
    ////  <--- RETURN WHEN: m_pSndQueue->sendto() is called.
 | 
						|
    ////  .... SKIP UNTIL m_pRcvQueue->recvfrom() HERE....
 | 
						|
    ////       (the first "sendto" will not be called due to being too early)
 | 
						|
    ///
 | 
						|
    //
 | 
						|
 | 
						|
    // asynchronous connect, return immediately
 | 
						|
    if (!m_bSynRecving)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << CONID() << "startConnect: ASYNC MODE DETECTED. Deferring the process to RcvQ:worker");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Wait for the negotiated configurations from the peer side.
 | 
						|
 | 
						|
    // This packet only prepares the storage where we will read the
 | 
						|
    // next incoming packet.
 | 
						|
    CPacket response;
 | 
						|
    response.setControl(UMSG_HANDSHAKE);
 | 
						|
    response.allocate(m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
    CUDTException  e;
 | 
						|
    EConnectStatus cst = CONN_CONTINUE;
 | 
						|
 | 
						|
    while (!m_bClosing)
 | 
						|
    {
 | 
						|
        int64_t tdiff = CTimer::getTime() - m_llLastReqTime;
 | 
						|
        // avoid sending too many requests, at most 1 request per 250ms
 | 
						|
 | 
						|
        // SHORT VERSION:
 | 
						|
        // The immediate first run of this loop WILL SKIP THIS PART, so
 | 
						|
        // the processing really begins AFTER THIS CONDITION.
 | 
						|
        //
 | 
						|
        // Note that some procedures inside may set m_llLastReqTime to 0,
 | 
						|
        // which will result of this condition to trigger immediately in
 | 
						|
        // the next iteration.
 | 
						|
        if (tdiff > 250000)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "startConnect: LOOP: time to send (" << tdiff << " > 250000). size=" << reqpkt.getLength());
 | 
						|
 | 
						|
            if (m_bRendezvous)
 | 
						|
                reqpkt.m_iID = m_ConnRes.m_iID;
 | 
						|
 | 
						|
            now = CTimer::getTime();
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
            {
 | 
						|
                CHandShake debughs;
 | 
						|
                debughs.load_from(reqpkt.m_pcData, reqpkt.getLength());
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << CONID() << "startConnect: REQ-TIME HIGH (" << now
 | 
						|
                          << "). cont/sending HS to peer: " << debughs.show());
 | 
						|
            }
 | 
						|
#endif
 | 
						|
 | 
						|
            m_llLastReqTime     = now;
 | 
						|
            reqpkt.m_iTimeStamp = int32_t(now - m_stats.startTime);
 | 
						|
            m_pSndQueue->sendto(serv_addr, reqpkt);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "startConnect: LOOP: too early to send - " << tdiff << " < 250000");
 | 
						|
        }
 | 
						|
 | 
						|
        cst = CONN_CONTINUE;
 | 
						|
        response.setLength(m_iMaxSRTPayloadSize);
 | 
						|
        if (m_pRcvQueue->recvfrom(m_SocketID, Ref(response)) > 0)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "startConnect: got response for connect request");
 | 
						|
            cst = processConnectResponse(response, &e, true /*synchro*/);
 | 
						|
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "startConnect: response processing result: " << ConnectStatusStr(cst));
 | 
						|
 | 
						|
            // Expected is that:
 | 
						|
            // - the peer responded with URQ_INDUCTION + cookie. This above function
 | 
						|
            //   should check that and craft the URQ_CONCLUSION handshake, in which
 | 
						|
            //   case this function returns CONN_CONTINUE. As an extra action taken
 | 
						|
            //   for that case, we set the SECURING mode if encryption requested,
 | 
						|
            //   and serialize again the handshake, possibly together with HS extension
 | 
						|
            //   blocks, if HSv5 peer responded. The serialized handshake will be then
 | 
						|
            //   sent again, as the loop is repeated.
 | 
						|
            // - the peer responded with URQ_CONCLUSION. This handshake was accepted
 | 
						|
            //   as a connection, and for >= HSv5 the HS extension blocks have been
 | 
						|
            //   also read and interpreted. In this case this function returns:
 | 
						|
            //   - CONN_ACCEPT, if everything was correct - break this loop and return normally
 | 
						|
            //   - CONN_REJECT in case of any problems with the delivered handshake
 | 
						|
            //     (incorrect data or data conflict) - throw error exception
 | 
						|
            // - the peer responded with any of URQ_ERROR_*.  - throw error exception
 | 
						|
            //
 | 
						|
            // The error exception should make the API connect() function fail, if blocking
 | 
						|
            // or mark the failure for that socket in epoll, if non-blocking.
 | 
						|
 | 
						|
            if (cst == CONN_RENDEZVOUS)
 | 
						|
            {
 | 
						|
                // When this function returned CONN_RENDEZVOUS, this requires
 | 
						|
                // very special processing for the Rendezvous-v5 algorithm. This MAY
 | 
						|
                // involve also preparing a new handshake form, also interpreting the
 | 
						|
                // SRT handshake extension and crafting SRT handshake extension for the
 | 
						|
                // peer, which should be next sent. When this function returns CONN_CONTINUE,
 | 
						|
                // it means that it has done all that was required, however none of the below
 | 
						|
                // things has to be done (this function will do it by itself if needed).
 | 
						|
                // Otherwise the handshake rolling can be interrupted and considered complete.
 | 
						|
                cst = processRendezvous(Ref(reqpkt), response, serv_addr, true /*synchro*/, RST_OK);
 | 
						|
                if (cst == CONN_CONTINUE)
 | 
						|
                    continue;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (cst == CONN_REJECT)
 | 
						|
                sendCtrl(UMSG_SHUTDOWN);
 | 
						|
 | 
						|
            if (cst != CONN_CONTINUE && cst != CONN_CONFUSED)
 | 
						|
                break; // --> OUTSIDE-LOOP
 | 
						|
 | 
						|
            // IMPORTANT
 | 
						|
            // [[using assert(m_pCryptoControl != nullptr)]];
 | 
						|
 | 
						|
            // new request/response should be sent out immediately on receving a response
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "startConnect: SYNC CONNECTION STATUS:" << ConnectStatusStr(cst) << ", REQ-TIME: LOW.");
 | 
						|
            m_llLastReqTime = 0;
 | 
						|
 | 
						|
            // Now serialize the handshake again to the existing buffer so that it's
 | 
						|
            // then sent later in this loop.
 | 
						|
 | 
						|
            // First, set the size back to the original size, m_iMaxSRTPayloadSize because
 | 
						|
            // this is the size of the originally allocated space. It might have been
 | 
						|
            // shrunk by serializing the INDUCTION handshake (which was required before
 | 
						|
            // sending this packet to the output queue) and therefore be too
 | 
						|
            // small to store the CONCLUSION handshake (with HSv5 extensions).
 | 
						|
            reqpkt.setLength(m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
            HLOGC(mglog.Debug, log << "startConnect: creating HS CONCLUSION: buffer size=" << reqpkt.getLength());
 | 
						|
 | 
						|
            // NOTE: BUGFIX: SERIALIZE AGAIN.
 | 
						|
            // The original UDT code didn't do it, so it was theoretically
 | 
						|
            // turned into conclusion, but was sending still the original
 | 
						|
            // induction handshake challenge message. It was working only
 | 
						|
            // thanks to that simultaneously there were being sent handshake
 | 
						|
            // messages from a separate thread (CSndQueue::worker) from
 | 
						|
            // RendezvousQueue, this time serialized properly, which caused
 | 
						|
            // that with blocking mode there was a kinda initial "drunk
 | 
						|
            // passenger with taxi driver talk" until the RendezvousQueue sends
 | 
						|
            // (when "the time comes") the right CONCLUSION handshake
 | 
						|
            // challenge message.
 | 
						|
            //
 | 
						|
            // Now that this is fixed, the handshake messages from RendezvousQueue
 | 
						|
            // are sent only when there is a rendezvous mode or non-blocking mode.
 | 
						|
            if (!createSrtHandshake(Ref(reqpkt), Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0))
 | 
						|
            {
 | 
						|
                LOGC(mglog.Error, log << "createSrtHandshake failed - REJECTING.");
 | 
						|
                cst = CONN_REJECT;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            // These last 2 parameters designate the buffer, which is in use only for SRT_CMD_KMRSP.
 | 
						|
            // If m_ConnReq.m_iVersion == HS_VERSION_UDT4, this function will do nothing,
 | 
						|
            // except just serializing the UDT handshake.
 | 
						|
            // The trick is that the HS challenge is with version HS_VERSION_UDT4, but the
 | 
						|
            // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable.
 | 
						|
        }
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst));
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
        // Non-fatal assertion
 | 
						|
        if (cst == CONN_REJECT) // Might be returned by processRendezvous
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "startConnect: IPE: cst=REJECT NOT EXPECTED HERE, the loop should've been interrupted!");
 | 
						|
            break;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
        if (CTimer::getTime() > ttl)
 | 
						|
        {
 | 
						|
            // timeout
 | 
						|
            e = CUDTException(MJ_SETUP, MN_TIMEOUT, 0);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // <--- OUTSIDE-LOOP
 | 
						|
    // Here will fall the break when not CONN_CONTINUE.
 | 
						|
    // CONN_RENDEZVOUS is handled by processRendezvous.
 | 
						|
    // CONN_ACCEPT will skip this and pass on.
 | 
						|
    if (cst == CONN_REJECT)
 | 
						|
    {
 | 
						|
        e = CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (e.getErrorCode() == 0)
 | 
						|
    {
 | 
						|
        if (m_bClosing)                                    // if the socket is closed before connection...
 | 
						|
            e = CUDTException(MJ_SETUP);                   // XXX NO MN ?
 | 
						|
        else if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) // connection request rejected
 | 
						|
        {
 | 
						|
            m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType);
 | 
						|
            e              = CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
        }
 | 
						|
        else if ((!m_bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check
 | 
						|
            e = CUDTException(MJ_SETUP, MN_SECURITY, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (e.getErrorCode() != 0)
 | 
						|
    {
 | 
						|
        m_bConnecting = false;
 | 
						|
        // The process is to be abnormally terminated, remove the connector
 | 
						|
        // now because most likely no other processing part has done anything with it.
 | 
						|
        m_pRcvQueue->removeConnector(m_SocketID);
 | 
						|
        throw e;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "startConnect: handshake exchange succeeded");
 | 
						|
 | 
						|
    // Parameters at the end.
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "startConnect: END. Parameters:"
 | 
						|
                 " mss="
 | 
						|
              << m_iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize()
 | 
						|
              << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth);
 | 
						|
}
 | 
						|
 | 
						|
// Asynchronous connection
 | 
						|
EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT
 | 
						|
{
 | 
						|
    EConnectStatus cst = CONN_CONTINUE;
 | 
						|
    CUDTException  e;
 | 
						|
 | 
						|
    CGuard cg(m_ConnectionLock); // FIX
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "processAsyncConnectResponse: got response for connect request, processing");
 | 
						|
    cst = processConnectResponse(pkt, &e, false);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "processAsyncConnectResponse: response processing result: " << ConnectStatusStr(cst)
 | 
						|
              << "REQ-TIME LOW to enforce immediate response");
 | 
						|
    m_llLastReqTime = 0;
 | 
						|
 | 
						|
    return cst;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::processAsyncConnectRequest(EReadStatus     rst,
 | 
						|
                                      EConnectStatus  cst,
 | 
						|
                                      const CPacket & response,
 | 
						|
                                      const sockaddr *serv_addr)
 | 
						|
{
 | 
						|
    // IMPORTANT!
 | 
						|
 | 
						|
    // This function is called, still asynchronously, but in the order
 | 
						|
    // of call just after the call to the above processAsyncConnectResponse.
 | 
						|
    // This should have got the original value returned from
 | 
						|
    // processConnectResponse through processAsyncConnectResponse.
 | 
						|
 | 
						|
    CPacket request;
 | 
						|
    request.setControl(UMSG_HANDSHAKE);
 | 
						|
    request.allocate(m_iMaxSRTPayloadSize);
 | 
						|
    uint64_t now         = CTimer::getTime();
 | 
						|
    request.m_iTimeStamp = int(now - m_stats.startTime);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "processAsyncConnectRequest: REQ-TIME: HIGH (" << now << "). Should prevent too quick responses.");
 | 
						|
    m_llLastReqTime = now;
 | 
						|
    // ID = 0, connection request
 | 
						|
    request.m_iID = !m_bRendezvous ? 0 : m_ConnRes.m_iID;
 | 
						|
 | 
						|
    bool status = true;
 | 
						|
 | 
						|
    if (cst == CONN_RENDEZVOUS)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processAsyncConnectRequest: passing to processRendezvous");
 | 
						|
        cst = processRendezvous(Ref(request), response, serv_addr, false /*asynchro*/, rst);
 | 
						|
        if (cst == CONN_ACCEPT)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "processAsyncConnectRequest: processRendezvous completed the process and responded by itself. "
 | 
						|
                         "Done.");
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        if (cst != CONN_CONTINUE)
 | 
						|
        {
 | 
						|
            // processRendezvous already set the reject reason
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "processAsyncConnectRequest: REJECT reported from processRendezvous, not processing further.");
 | 
						|
            status = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (cst == CONN_REJECT)
 | 
						|
    {
 | 
						|
        // m_RejectReason already set at worker_ProcessAddressedPacket.
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "processAsyncConnectRequest: REJECT reported from HS processing, not processing further.");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // (this procedure will be also run for HSv4 rendezvous)
 | 
						|
        HLOGC(mglog.Debug, log << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength());
 | 
						|
        if (!createSrtHandshake(Ref(request), Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0))
 | 
						|
        {
 | 
						|
            // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired".
 | 
						|
            LOGC(mglog.Error, log << "IPE: processAsyncConnectRequest: createSrtHandshake failed, dismissing.");
 | 
						|
            status = false;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType)
 | 
						|
                      << " to socket " << request.m_iID << " size=" << request.getLength());
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!status)
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
        /* XXX Shouldn't it send a single response packet for the rejection?
 | 
						|
        // Set the version to 0 as "handshake rejection" status and serialize it
 | 
						|
        CHandShake zhs;
 | 
						|
        size_t size = request.getLength();
 | 
						|
        zhs.store_to(request.m_pcData, Ref(size));
 | 
						|
        request.setLength(size);
 | 
						|
        */
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "processAsyncConnectRequest: sending request packet, setting REQ-TIME HIGH.");
 | 
						|
    m_llLastReqTime = CTimer::getTime();
 | 
						|
    m_pSndQueue->sendto(serv_addr, request);
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::cookieContest()
 | 
						|
{
 | 
						|
    if (m_SrtHsSide != HSD_DRAW)
 | 
						|
        return;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie);
 | 
						|
 | 
						|
    if (m_ConnReq.m_iCookie == 0 || m_ConnRes.m_iCookie == 0)
 | 
						|
    {
 | 
						|
        // Note that it's virtually impossible that Agent's cookie is not ready, this
 | 
						|
        // shall be considered IPE.
 | 
						|
        // Not all cookies are ready, don't start the contest.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // INITIATOR/RESPONDER role is resolved by COOKIE CONTEST.
 | 
						|
    //
 | 
						|
    // The cookie contest must be repeated every time because it
 | 
						|
    // may change the state at some point.
 | 
						|
    int better_cookie = m_ConnReq.m_iCookie - m_ConnRes.m_iCookie;
 | 
						|
 | 
						|
    if (better_cookie > 0)
 | 
						|
    {
 | 
						|
        m_SrtHsSide = HSD_INITIATOR;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (better_cookie < 0)
 | 
						|
    {
 | 
						|
        m_SrtHsSide = HSD_RESPONDER;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // DRAW! The only way to continue would be to force the
 | 
						|
    // cookies to be regenerated and to start over. But it's
 | 
						|
    // not worth a shot - this is an extremely rare case.
 | 
						|
    // This can simply do reject so that it can be started again.
 | 
						|
 | 
						|
    // Pretend then that the cookie contest wasn't done so that
 | 
						|
    // it's done again. Cookies are baked every time anew, however
 | 
						|
    // the successful initial contest remains valid no matter how
 | 
						|
    // cookies will change.
 | 
						|
 | 
						|
    m_SrtHsSide = HSD_DRAW;
 | 
						|
}
 | 
						|
 | 
						|
EConnectStatus CUDT::processRendezvous(
 | 
						|
    ref_t<CPacket> reqpkt, const CPacket &response, const sockaddr *serv_addr, bool synchro, EReadStatus rst)
 | 
						|
{
 | 
						|
    if (m_RdvState == CHandShake::RDV_CONNECTED)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processRendezvous: already in CONNECTED state.");
 | 
						|
        return CONN_ACCEPT;
 | 
						|
    }
 | 
						|
 | 
						|
    uint32_t kmdata[SRTDATA_MAXSIZE];
 | 
						|
    size_t   kmdatasize = SRTDATA_MAXSIZE;
 | 
						|
    CPacket &rpkt       = *reqpkt;
 | 
						|
 | 
						|
    cookieContest();
 | 
						|
 | 
						|
    // We know that the other side was contacted and the other side has sent
 | 
						|
    // the handshake message - we know then both cookies. If it's a draw, it's
 | 
						|
    // a very rare case of creating identical cookies.
 | 
						|
    if (m_SrtHsSide == HSD_DRAW)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_RDVCOOKIE;
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "COOKIE CONTEST UNRESOLVED: can't assign connection roles, please wait another minute.");
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    UDTRequestType rsp_type = URQ_FAILURE_TYPES; // just to track uninitialized errors
 | 
						|
 | 
						|
    // We can assume that the Handshake packet received here as 'response'
 | 
						|
    // is already serialized in m_ConnRes. Check extra flags that are meaningful
 | 
						|
    // for further processing here.
 | 
						|
 | 
						|
    int  ext_flags       = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
 | 
						|
    bool needs_extension = ext_flags != 0; // Initial value: received HS has extensions.
 | 
						|
    bool needs_hsrsp;
 | 
						|
    rendezvousSwitchState(Ref(rsp_type), Ref(needs_extension), Ref(needs_hsrsp));
 | 
						|
    if (rsp_type > URQ_FAILURE_TYPES)
 | 
						|
    {
 | 
						|
        m_RejectReason = RejectReasonForURQ(rsp_type);
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processRendezvous: rejecting due to switch-state response: " << RequestTypeStr(rsp_type));
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
    checkUpdateCryptoKeyLen("processRendezvous", m_ConnRes.m_iType);
 | 
						|
 | 
						|
    // We have three possibilities here as it comes to HSREQ extensions:
 | 
						|
 | 
						|
    // 1. The agent is loser in attention state, it sends EMPTY conclusion (without extensions)
 | 
						|
    // 2. The agent is loser in initiated state, it interprets incoming HSREQ and creates HSRSP
 | 
						|
    // 3. The agent is winner in attention or fine state, it sends HSREQ extension
 | 
						|
    m_ConnReq.m_iReqType  = rsp_type;
 | 
						|
    m_ConnReq.m_extension = needs_extension;
 | 
						|
 | 
						|
    // This must be done before prepareConnectionObjects().
 | 
						|
    applyResponseSettings();
 | 
						|
 | 
						|
    // This must be done before interpreting and creating HSv5 extensions.
 | 
						|
    if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0))
 | 
						|
    {
 | 
						|
        // m_RejectReason already handled
 | 
						|
        HLOGC(mglog.Debug, log << "processRendezvous: rejecting due to problems in prepareConnectionObjects.");
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    // Case 2.
 | 
						|
    if (needs_hsrsp)
 | 
						|
    {
 | 
						|
        // This means that we have received HSREQ extension with the handshake, so we need to interpret
 | 
						|
        // it and craft the response.
 | 
						|
        if (rst == RST_OK)
 | 
						|
        {
 | 
						|
            // We have JUST RECEIVED packet in this session (not that this is called as periodic update).
 | 
						|
            // Sanity check
 | 
						|
            m_llLastReqTime = 0;
 | 
						|
            if (response.getLength() == size_t(-1))
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_IPE;
 | 
						|
                LOGC(mglog.Fatal,
 | 
						|
                     log << "IPE: rst=RST_OK, but the packet has set -1 length - REJECTING (REQ-TIME: LOW)");
 | 
						|
                return CONN_REJECT;
 | 
						|
            }
 | 
						|
 | 
						|
            if (!interpretSrtHandshake(m_ConnRes, response, kmdata, &kmdatasize))
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW.");
 | 
						|
                return CONN_REJECT;
 | 
						|
            }
 | 
						|
 | 
						|
            // Pass on, inform about the shortened response-waiting period.
 | 
						|
            HLOGC(mglog.Debug, log << "processRendezvous: setting REQ-TIME: LOW. Forced to respond immediately.");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // If the last CONCLUSION message didn't contain the KMX extension, there's
 | 
						|
            // no key recorded yet, so it can't be extracted. Mark this kmdatasize empty though.
 | 
						|
            int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
 | 
						|
            if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ))
 | 
						|
            {
 | 
						|
                // This is a periodic handshake update, so you need to extract the KM data from the
 | 
						|
                // first message, provided that it is there.
 | 
						|
                size_t msgsize = m_pCryptoControl->getKmMsg_size(0);
 | 
						|
                if (msgsize == 0)
 | 
						|
                {
 | 
						|
                    switch (m_pCryptoControl->m_RcvKmState)
 | 
						|
                    {
 | 
						|
                        // If the KMX process ended up with a failure, the KMX is not recorded.
 | 
						|
                        // In this case as the KMRSP answer the "failure status" should be crafted.
 | 
						|
                    case SRT_KM_S_NOSECRET:
 | 
						|
                    case SRT_KM_S_BADSECRET:
 | 
						|
                    {
 | 
						|
                        HLOGC(mglog.Debug,
 | 
						|
                              log << "processRendezvous: No KMX recorded, status = NOSECRET. Respond with NOSECRET.");
 | 
						|
 | 
						|
                        // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case,
 | 
						|
                        // that is, copy the NOSECRET code into KMX message.
 | 
						|
                        memcpy(kmdata, &m_pCryptoControl->m_RcvKmState, sizeof(int32_t));
 | 
						|
                        kmdatasize = 1;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                    default:
 | 
						|
                        // Remaining values:
 | 
						|
                        // UNSECURED: should not fall here at alll
 | 
						|
                        // SECURING: should not happen in HSv5
 | 
						|
                        // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0)
 | 
						|
                        {
 | 
						|
                            m_RejectReason = SRT_REJ_IPE;
 | 
						|
                            // Remaining situations:
 | 
						|
                            // - password only on this site: shouldn't be considered to be sent to a no-password site
 | 
						|
                            LOGC(mglog.Error,
 | 
						|
                                 log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV="
 | 
						|
                                     << KmStateStr(m_pCryptoControl->m_RcvKmState)
 | 
						|
                                     << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState));
 | 
						|
                            return CONN_REJECT;
 | 
						|
                        }
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    kmdatasize = msgsize / 4;
 | 
						|
                    if (msgsize > kmdatasize * 4)
 | 
						|
                    {
 | 
						|
                        // Sanity check
 | 
						|
                        LOGC(mglog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize);
 | 
						|
                        memset(kmdata + (kmdatasize * 4), 0, msgsize - (kmdatasize * 4));
 | 
						|
                        ++kmdatasize;
 | 
						|
                    }
 | 
						|
 | 
						|
                    HLOGC(mglog.Debug,
 | 
						|
                          log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size="
 | 
						|
                              << kmdatasize);
 | 
						|
                    memcpy(kmdata, m_pCryptoControl->getKmMsg_data(0), msgsize);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP");
 | 
						|
                kmdatasize = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // No matter the value of needs_extension, the extension is always needed
 | 
						|
        // when HSREQ was interpreted (to store HSRSP extension).
 | 
						|
        m_ConnReq.m_extension = true;
 | 
						|
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processRendezvous: HSREQ extension ok, creating HSRSP response. kmdatasize=" << kmdatasize);
 | 
						|
 | 
						|
        rpkt.setLength(m_iMaxSRTPayloadSize);
 | 
						|
        if (!createSrtHandshake(reqpkt, Ref(m_ConnReq), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize))
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "processRendezvous: rejecting due to problems in createSrtHandshake. REQ-TIME: LOW");
 | 
						|
            m_llLastReqTime = 0;
 | 
						|
            return CONN_REJECT;
 | 
						|
        }
 | 
						|
 | 
						|
        // This means that it has received URQ_CONCLUSION with HSREQ, agent is then in RDV_FINE
 | 
						|
        // state, it sends here URQ_CONCLUSION with HSREQ/KMREQ extensions and it awaits URQ_AGREEMENT.
 | 
						|
        return CONN_CONTINUE;
 | 
						|
    }
 | 
						|
 | 
						|
    // Special case: if URQ_AGREEMENT is to be sent, when this side is INITIATOR,
 | 
						|
    // then it must have received HSRSP, so it must interpret it. Otherwise it would
 | 
						|
    // end up with URQ_DONE, which means that it is the other side to interpret HSRSP.
 | 
						|
    if (m_SrtHsSide == HSD_INITIATOR && m_ConnReq.m_iReqType == URQ_AGREEMENT)
 | 
						|
    {
 | 
						|
        // The same is done in CUDT::postConnect(), however this section will
 | 
						|
        // not be done in case of rendezvous. The section in postConnect() is
 | 
						|
        // predicted to run only in regular CALLER handling.
 | 
						|
 | 
						|
        if (rst != RST_OK || response.getLength() == size_t(-1))
 | 
						|
        {
 | 
						|
            // Actually the -1 length would be an IPE, but it's likely that this was reported already.
 | 
						|
            HLOGC(
 | 
						|
                mglog.Debug,
 | 
						|
                log << "processRendezvous: no INCOMING packet, NOT interpreting extensions (relying on exising data)");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension");
 | 
						|
            if (!interpretSrtHandshake(m_ConnRes, response, 0, 0))
 | 
						|
            {
 | 
						|
                // m_RejectReason is already set, so set the reqtype accordingly
 | 
						|
                m_ConnReq.m_iReqType = URQFailure(m_RejectReason);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // This should be false, make a kinda assert here.
 | 
						|
        if (needs_extension)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Fatal, log << "IPE: INITIATOR responding AGREEMENT should declare no extensions to HS");
 | 
						|
            m_ConnReq.m_extension = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "processRendezvous: COOKIES Agent/Peer: " << m_ConnReq.m_iCookie << "/"
 | 
						|
              << m_ConnRes.m_iCookie << " HSD:" << (m_SrtHsSide == HSD_INITIATOR ? "initiator" : "responder")
 | 
						|
              << " STATE:" << CHandShake::RdvStateStr(m_RdvState) << " ...");
 | 
						|
 | 
						|
    if (rsp_type == URQ_DONE)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "... WON'T SEND any response, both sides considered connected");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "... WILL SEND " << RequestTypeStr(rsp_type) << " " << (m_ConnReq.m_extension ? "with" : "without")
 | 
						|
                  << " SRT HS extensions");
 | 
						|
    }
 | 
						|
 | 
						|
    // This marks the information for the serializer that
 | 
						|
    // the SRT handshake extension is required.
 | 
						|
    // Rest of the data will be filled together with
 | 
						|
    // serialization.
 | 
						|
    m_ConnReq.m_extension = needs_extension;
 | 
						|
 | 
						|
    rpkt.setLength(m_iMaxSRTPayloadSize);
 | 
						|
    if (m_RdvState == CHandShake::RDV_CONNECTED)
 | 
						|
    {
 | 
						|
        // When synchro=false, don't lock a mutex for rendezvous queue.
 | 
						|
        // This is required when this function is called in the
 | 
						|
        // receive queue worker thread - it would lock itself.
 | 
						|
        int cst = postConnect(response, true, 0, synchro);
 | 
						|
        if (cst == CONN_REJECT)
 | 
						|
        {
 | 
						|
            // m_RejectReason already set
 | 
						|
            HLOGC(mglog.Debug, log << "processRendezvous: rejecting due to problems in postConnect.");
 | 
						|
            return CONN_REJECT;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // URQ_DONE or URQ_AGREEMENT can be the result if the state is RDV_CONNECTED.
 | 
						|
    // If URQ_DONE, then there's nothing to be done, when URQ_AGREEMENT then return
 | 
						|
    // CONN_CONTINUE to make the caller send again the contents if the packet buffer,
 | 
						|
    // this time with URQ_AGREEMENT message, but still consider yourself connected.
 | 
						|
    if (rsp_type == URQ_DONE)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processRendezvous: rsp=DONE, reporting ACCEPT (nothing to respond)");
 | 
						|
        return CONN_ACCEPT;
 | 
						|
    }
 | 
						|
 | 
						|
    // createSrtHandshake moved here because if the above conditions are satisfied,
 | 
						|
    // no response is going to be send, so nothing needs to be "created".
 | 
						|
 | 
						|
    // needs_extension here distinguishes between cases 1 and 3.
 | 
						|
    // NOTE: in case when interpretSrtHandshake was run under the conditions above (to interpret HSRSP),
 | 
						|
    // then createSrtHandshake below will create only empty AGREEMENT message.
 | 
						|
    if (!createSrtHandshake(reqpkt, Ref(m_ConnReq), SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0))
 | 
						|
    {
 | 
						|
        // m_RejectReason already set
 | 
						|
        LOGC(mglog.Error, log << "createSrtHandshake failed (IPE?), connection rejected. REQ-TIME: LOW");
 | 
						|
        m_llLastReqTime = 0;
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    if (rsp_type == URQ_AGREEMENT && m_RdvState == CHandShake::RDV_CONNECTED)
 | 
						|
    {
 | 
						|
        // We are using our own serialization method (not the one called after
 | 
						|
        // processConnectResponse, this is skipped in case when this function
 | 
						|
        // is called), so we can also send this immediately. Agreement must be
 | 
						|
        // sent just once and the party must switch into CONNECTED state - in
 | 
						|
        // contrast to CONCLUSION messages, which should be sent in loop repeatedly.
 | 
						|
        //
 | 
						|
        // Even though in theory the AGREEMENT message sent just once may miss
 | 
						|
        // the target (as normal thing in UDP), this is little probable to happen,
 | 
						|
        // and this doesn't matter much because even if the other party doesn't
 | 
						|
        // get AGREEMENT, but will get payload or KEEPALIVE messages, it will
 | 
						|
        // turn into connected state as well. The AGREEMENT is rather kinda
 | 
						|
        // catalyzer here and may turn the entity on the right track faster. When
 | 
						|
        // AGREEMENT is missed, it may have kinda initial tearing.
 | 
						|
 | 
						|
        const uint64_t now = CTimer::getTime();
 | 
						|
        m_llLastReqTime    = now;
 | 
						|
        rpkt.m_iTimeStamp  = int32_t(now - m_stats.startTime);
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processRendezvous: rsp=AGREEMENT, reporting ACCEPT and sending just this one, REQ-TIME HIGH ("
 | 
						|
                  << now << ").");
 | 
						|
 | 
						|
        m_pSndQueue->sendto(serv_addr, rpkt);
 | 
						|
 | 
						|
        return CONN_ACCEPT;
 | 
						|
    }
 | 
						|
 | 
						|
    if (rst == RST_OK)
 | 
						|
    {
 | 
						|
        // the request time must be updated so that the next handshake can be sent out immediately
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processRendezvous: rsp=" << RequestTypeStr(m_ConnReq.m_iReqType)
 | 
						|
                  << " REQ-TIME: LOW to send immediately, consider yourself conencted");
 | 
						|
        m_llLastReqTime = 0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processRendezvous: REQ-TIME: remains previous value, consider yourself connected");
 | 
						|
    }
 | 
						|
    return CONN_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
EConnectStatus CUDT::processConnectResponse(const CPacket &response, CUDTException *eout, bool synchro) ATR_NOEXCEPT
 | 
						|
{
 | 
						|
    // NOTE: ASSUMED LOCK ON: m_ConnectionLock.
 | 
						|
 | 
						|
    // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0.
 | 
						|
    // Returned values:
 | 
						|
    // - CONN_REJECT: there was some error when processing the response, connection should be rejected
 | 
						|
    // - CONN_ACCEPT: the handshake is done and finished correctly
 | 
						|
    // - CONN_CONTINUE: the induction handshake has been processed correctly, and expects CONCLUSION handshake
 | 
						|
 | 
						|
    if (!m_bConnecting)
 | 
						|
        return CONN_REJECT;
 | 
						|
 | 
						|
    // This is required in HSv5 rendezvous, in which it should send the URQ_AGREEMENT message to
 | 
						|
    // the peer, however switch to connected state.
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "processConnectResponse: TYPE:"
 | 
						|
              << (response.isControl() ? MessageTypeStr(response.getType(), response.getExtendedType())
 | 
						|
                                       : string("DATA")));
 | 
						|
    // ConnectStatus res = CONN_REJECT; // used later for status - must be declared here due to goto POST_CONNECT.
 | 
						|
 | 
						|
    // For HSv4, the data sender is INITIATOR, and the data receiver is RESPONDER,
 | 
						|
    // regardless of the connecting side affiliation. This will be changed for HSv5.
 | 
						|
    bool          bidirectional = false;
 | 
						|
    HandshakeSide hsd           = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER;
 | 
						|
    // (defined here due to 'goto' below).
 | 
						|
 | 
						|
    // SRT peer may send the SRT handshake private message (type 0x7fff) before a keep-alive.
 | 
						|
 | 
						|
    // This condition is checked when the current agent is trying to do connect() in rendezvous mode,
 | 
						|
    // but the peer was faster to send a handshake packet earlier. This makes it continue with connecting
 | 
						|
    // process if the peer is already behaving as if the connection was already established.
 | 
						|
 | 
						|
    // This value will check either the initial value, which is less than SRT1, or
 | 
						|
    // the value previously loaded to m_ConnReq during the previous handshake response.
 | 
						|
    // For the initial form this value should not be checked.
 | 
						|
    bool hsv5 = m_ConnRes.m_iVersion >= HS_VERSION_SRT1;
 | 
						|
 | 
						|
    if (m_bRendezvous &&
 | 
						|
        (m_RdvState == CHandShake::RDV_CONNECTED   // somehow Rendezvous-v5 switched it to CONNECTED.
 | 
						|
         || !response.isControl()                  // WAS A PAYLOAD PACKET.
 | 
						|
         || (response.getType() == UMSG_KEEPALIVE) // OR WAS A UMSG_KEEPALIVE message.
 | 
						|
         || (response.getType() == UMSG_EXT) // OR WAS a CONTROL packet of some extended type (i.e. any SRT specific)
 | 
						|
         )
 | 
						|
        // This may happen if this is an initial state in which the socket type was not yet set.
 | 
						|
        // If this is a field that holds the response handshake record from the peer, this means that it wasn't received
 | 
						|
        // yet. HSv5: added version check because in HSv5 the m_iType field has different meaning and it may be 0 in
 | 
						|
        // case when the handshake does not carry SRT extensions.
 | 
						|
        && (hsv5 || m_ConnRes.m_iType != UDT_UNDEFINED))
 | 
						|
    {
 | 
						|
        // a data packet or a keep-alive packet comes, which means the peer side is already connected
 | 
						|
        // in this situation, the previously recorded response will be used
 | 
						|
        // In HSv5 this situation is theoretically possible if this party has missed the URQ_AGREEMENT message.
 | 
						|
        HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: already connected - pinning in");
 | 
						|
        if (hsv5)
 | 
						|
        {
 | 
						|
            m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
        }
 | 
						|
 | 
						|
        return postConnect(response, hsv5, eout, synchro);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!response.isControl(UMSG_HANDSHAKE))
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        if (!response.isControl())
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error, log << CONID() << "processConnectResponse: received DATA while HANDSHAKE expected");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << CONID()
 | 
						|
                     << "processConnectResponse: CONFUSED: expected UMSG_HANDSHAKE as connection not yet established, "
 | 
						|
                        "got: "
 | 
						|
                     << MessageTypeStr(response.getType(), response.getExtendedType()));
 | 
						|
        }
 | 
						|
        return CONN_CONFUSED;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_ConnRes.load_from(response.m_pcData, response.getLength()) == -1)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        // Handshake data were too small to reach the Handshake structure. Reject.
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << CONID()
 | 
						|
                 << "processConnectResponse: HANDSHAKE data buffer too small - possible blueboxing. Rejecting.");
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show());
 | 
						|
    if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES)
 | 
						|
    {
 | 
						|
        m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType);
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    if (size_t(m_ConnRes.m_iMSS) > CPacket::ETH_MAX_MTU_SIZE)
 | 
						|
    {
 | 
						|
        // Yes, we do abort to prevent buffer overrun. Set your MSS correctly
 | 
						|
        // and you'll avoid problems.
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        LOGC(mglog.Fatal, log << "MSS size " << m_iMSS << "exceeds MTU size!");
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    // (see createCrypter() call below)
 | 
						|
    //
 | 
						|
    // The CCryptoControl attached object must be created early
 | 
						|
    // because it will be required to create a conclusion handshake in HSv5
 | 
						|
    //
 | 
						|
    if (m_bRendezvous)
 | 
						|
    {
 | 
						|
        // SANITY CHECK: A rendezvous socket should reject any caller requests (it's not a listener)
 | 
						|
        if (m_ConnRes.m_iReqType == URQ_INDUCTION)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << CONID()
 | 
						|
                     << "processConnectResponse: Rendezvous-point received INDUCTION handshake (expected WAVEAHAND). "
 | 
						|
                        "Rejecting.");
 | 
						|
            return CONN_REJECT;
 | 
						|
        }
 | 
						|
 | 
						|
        // The procedure for version 5 is completely different and changes the states
 | 
						|
        // differently, so the old code will still maintain HSv4 the old way.
 | 
						|
 | 
						|
        if (m_ConnRes.m_iVersion > HS_VERSION_UDT4)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv5 DETECTED.");
 | 
						|
            return CONN_RENDEZVOUS; // --> will continue in CUDT::processRendezvous().
 | 
						|
        }
 | 
						|
 | 
						|
        HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendsezvous HSv4 DETECTED.");
 | 
						|
        // So, here it has either received URQ_WAVEAHAND handshake message (while it should be in URQ_WAVEAHAND itself)
 | 
						|
        // or it has received URQ_CONCLUSION/URQ_AGREEMENT message while this box has already sent URQ_WAVEAHAND to the
 | 
						|
        // peer, and DID NOT send the URQ_CONCLUSION yet.
 | 
						|
 | 
						|
        if (m_ConnReq.m_iReqType == URQ_WAVEAHAND || m_ConnRes.m_iReqType == URQ_WAVEAHAND)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "processConnectResponse: REQ-TIME LOW. got HS RDV. Agent state:"
 | 
						|
                      << RequestTypeStr(m_ConnReq.m_iReqType) << " Peer HS:" << m_ConnRes.show());
 | 
						|
 | 
						|
            // Here we could have received WAVEAHAND or CONCLUSION.
 | 
						|
            // For HSv4 simply switch to CONCLUSION for the sake of further handshake rolling.
 | 
						|
            // For HSv5, make the cookie contest and basing on this decide, which party
 | 
						|
            // should provide the HSREQ/KMREQ attachment.
 | 
						|
 | 
						|
            if (!createCrypter(hsd, false /* unidirectional */))
 | 
						|
            {
 | 
						|
                m_RejectReason       = SRT_REJ_RESOURCE;
 | 
						|
                m_ConnReq.m_iReqType = URQFailure(SRT_REJ_RESOURCE);
 | 
						|
                // the request time must be updated so that the next handshake can be sent out immediately.
 | 
						|
                m_llLastReqTime = 0;
 | 
						|
                return CONN_REJECT;
 | 
						|
            }
 | 
						|
 | 
						|
            m_ConnReq.m_iReqType = URQ_CONCLUSION;
 | 
						|
            // the request time must be updated so that the next handshake can be sent out immediately.
 | 
						|
            m_llLastReqTime = 0;
 | 
						|
            return CONN_CONTINUE;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv4 PAST waveahand");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // set cookie
 | 
						|
        if (m_ConnRes.m_iReqType == URQ_INDUCTION)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "processConnectResponse: REQ-TIME LOW; got INDUCTION HS response (cookie:" << hex
 | 
						|
                      << m_ConnRes.m_iCookie << " version:" << dec << m_ConnRes.m_iVersion
 | 
						|
                      << "), sending CONCLUSION HS with this cookie");
 | 
						|
 | 
						|
            m_ConnReq.m_iCookie  = m_ConnRes.m_iCookie;
 | 
						|
            m_ConnReq.m_iReqType = URQ_CONCLUSION;
 | 
						|
 | 
						|
            // Here test if the LISTENER has responded with version HS_VERSION_SRT1,
 | 
						|
            // it means that it is HSv5 capable. It can still accept the HSv4 handshake.
 | 
						|
            if (m_ConnRes.m_iVersion > HS_VERSION_UDT4)
 | 
						|
            {
 | 
						|
                int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
 | 
						|
 | 
						|
                if (hs_flags != SrtHSRequest::SRT_MAGIC_CODE)
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Warn, log << "processConnectResponse: Listener HSv5 did not set the SRT_MAGIC_CODE");
 | 
						|
                }
 | 
						|
 | 
						|
                checkUpdateCryptoKeyLen("processConnectResponse", m_ConnRes.m_iType);
 | 
						|
 | 
						|
                // This will catch HS_VERSION_SRT1 and any newer.
 | 
						|
                // Set your highest version.
 | 
						|
                m_ConnReq.m_iVersion = HS_VERSION_SRT1;
 | 
						|
                // CONTROVERSIAL: use 0 as m_iType according to the meaning in HSv5.
 | 
						|
                // The HSv4 client might not understand it, which means that agent
 | 
						|
                // must switch itself to HSv4 rendezvous, and this time iType sould
 | 
						|
                // be set to UDT_DGRAM value.
 | 
						|
                m_ConnReq.m_iType = 0;
 | 
						|
 | 
						|
                // This marks the information for the serializer that
 | 
						|
                // the SRT handshake extension is required.
 | 
						|
                // Rest of the data will be filled together with
 | 
						|
                // serialization.
 | 
						|
                m_ConnReq.m_extension = true;
 | 
						|
 | 
						|
                // For HSv5, the caller is INITIATOR and the listener is RESPONDER.
 | 
						|
                // The m_bDataSender value should be completely ignored and the
 | 
						|
                // connection is always bidirectional.
 | 
						|
                bidirectional = true;
 | 
						|
                hsd           = HSD_INITIATOR;
 | 
						|
            }
 | 
						|
            m_llLastReqTime = 0;
 | 
						|
            if (!createCrypter(hsd, bidirectional))
 | 
						|
            {
 | 
						|
                m_RejectReason = SRT_REJ_RESOURCE;
 | 
						|
                return CONN_REJECT;
 | 
						|
            }
 | 
						|
            // NOTE: This setup sets URQ_CONCLUSION and appropriate data in the handshake structure.
 | 
						|
            // The full handshake to be sent will be filled back in the caller function -- CUDT::startConnect().
 | 
						|
            return CONN_CONTINUE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return postConnect(response, false, eout, synchro);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::applyResponseSettings()
 | 
						|
{
 | 
						|
    // Re-configure according to the negotiated values.
 | 
						|
    m_iMSS               = m_ConnRes.m_iMSS;
 | 
						|
    m_iFlowWindowSize    = m_ConnRes.m_iFlightFlagSize;
 | 
						|
    int udpsize          = m_iMSS - CPacket::UDP_HDR_SIZE;
 | 
						|
    m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE;
 | 
						|
    m_iPeerISN           = m_ConnRes.m_iISN;
 | 
						|
    m_iRcvLastAck        = m_ConnRes.m_iISN;
 | 
						|
#ifdef ENABLE_LOGGING
 | 
						|
    m_iDebugPrevLastAck = m_iRcvLastAck;
 | 
						|
#endif
 | 
						|
    m_iRcvLastSkipAck  = m_iRcvLastAck;
 | 
						|
    m_iRcvLastAckAck   = m_ConnRes.m_iISN;
 | 
						|
    m_iRcvCurrSeqNo    = m_ConnRes.m_iISN - 1;
 | 
						|
    m_iRcvCurrPhySeqNo = m_ConnRes.m_iISN - 1;
 | 
						|
    m_PeerID           = m_ConnRes.m_iID;
 | 
						|
    memcpy(m_piSelfIP, m_ConnRes.m_piPeerIP, 16);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize
 | 
						|
              << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " isn=" << m_ConnRes.m_iISN
 | 
						|
              << " peerID=" << m_ConnRes.m_iID);
 | 
						|
}
 | 
						|
 | 
						|
EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout, bool synchro)
 | 
						|
{
 | 
						|
    if (m_ConnRes.m_iVersion < HS_VERSION_SRT1)
 | 
						|
        m_ullRcvPeerStartTime = 0; // will be set correctly in SRT HS.
 | 
						|
 | 
						|
    // This procedure isn't being executed in rendezvous because
 | 
						|
    // in rendezvous it's completed before calling this function.
 | 
						|
    if (!rendezvous)
 | 
						|
    {
 | 
						|
        // NOTE: THIS function must be called before calling prepareConnectionObjects.
 | 
						|
        // The reason why it's not part of prepareConnectionObjects is that the activities
 | 
						|
        // done there are done SIMILAR way in acceptAndRespond, which also calls this
 | 
						|
        // function. In fact, prepareConnectionObjects() represents the code that was
 | 
						|
        // done separately in processConnectResponse() and acceptAndRespond(), so this way
 | 
						|
        // this code is now common. Now acceptAndRespond() does "manually" something similar
 | 
						|
        // to applyResponseSettings(), just a little bit differently. This SHOULD be made
 | 
						|
        // common as a part of refactoring job, just needs a bit more time.
 | 
						|
        //
 | 
						|
        // Currently just this function must be called always BEFORE prepareConnectionObjects
 | 
						|
        // everywhere except acceptAndRespond().
 | 
						|
        applyResponseSettings();
 | 
						|
 | 
						|
        // This will actually be done also in rendezvous HSv4,
 | 
						|
        // however in this case the HSREQ extension will not be attached,
 | 
						|
        // so it will simply go the "old way".
 | 
						|
        bool ok = prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout);
 | 
						|
        // May happen that 'response' contains a data packet that was sent in rendezvous mode.
 | 
						|
        // In this situation the interpretation of handshake was already done earlier.
 | 
						|
        if (ok && response.isControl())
 | 
						|
        {
 | 
						|
            ok = interpretSrtHandshake(m_ConnRes, response, 0, 0);
 | 
						|
            if (!ok && eout)
 | 
						|
            {
 | 
						|
                *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (!ok) // m_RejectReason already set
 | 
						|
            return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    CInfoBlock ib;
 | 
						|
    ib.m_iIPversion = m_iIPversion;
 | 
						|
    CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP);
 | 
						|
    if (m_pCache->lookup(&ib) >= 0)
 | 
						|
    {
 | 
						|
        m_iRTT       = ib.m_iRTT;
 | 
						|
        m_iBandwidth = ib.m_iBandwidth;
 | 
						|
    }
 | 
						|
 | 
						|
    SRT_REJECT_REASON rr = setupCC();
 | 
						|
    if (rr != SRT_REJ_UNKNOWN)
 | 
						|
    {
 | 
						|
        m_RejectReason = rr;
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    // And, I am connected too.
 | 
						|
    m_bConnecting = false;
 | 
						|
    m_bConnected  = true;
 | 
						|
 | 
						|
    // register this socket for receiving data packets
 | 
						|
    m_pRNode->m_bOnList = true;
 | 
						|
    m_pRcvQueue->setNewEntry(this);
 | 
						|
 | 
						|
    // XXX Problem around CONN_CONFUSED!
 | 
						|
    // If some too-eager packets were received from a listener
 | 
						|
    // that thinks it's connected, but his last handshake was missed,
 | 
						|
    // they are collected by CRcvQueue::storePkt. The removeConnector
 | 
						|
    // function will want to delete them all, so it would be nice
 | 
						|
    // if these packets can be re-delivered. Of course the listener
 | 
						|
    // should be prepared to resend them (as every packet can be lost
 | 
						|
    // on UDP), but it's kinda overkill when we have them already and
 | 
						|
    // can dispatch them.
 | 
						|
 | 
						|
    // Remove from rendezvous queue (in this particular case it's
 | 
						|
    // actually removing the socket that undergoes asynchronous HS processing).
 | 
						|
    // Removing at THIS point because since when setNewEntry is called,
 | 
						|
    // the next iteration in the CRcvQueue::worker loop will be dispatching
 | 
						|
    // packets normally, as within-connection, so the "connector" won't
 | 
						|
    // play any role since this time.
 | 
						|
    // The connector, however, must stay alive until the setNewEntry is called
 | 
						|
    // because otherwise the packets that are coming for this socket before the
 | 
						|
    // connection process is complete will be rejected as "attack", instead of
 | 
						|
    // being enqueued for later pickup from the queue.
 | 
						|
    m_pRcvQueue->removeConnector(m_SocketID, synchro);
 | 
						|
 | 
						|
    // acknowledge the management module.
 | 
						|
    CUDTSocket* s = s_UDTUnited.locate(m_SocketID);
 | 
						|
    if (!s)
 | 
						|
    {
 | 
						|
        if (eout)
 | 
						|
        {
 | 
						|
            *eout = CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
 | 
						|
        }
 | 
						|
 | 
						|
        m_RejectReason = SRT_REJ_CLOSE;
 | 
						|
        return CONN_REJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    // copy address information of local node
 | 
						|
    // the local port must be correctly assigned BEFORE CUDT::startConnect(),
 | 
						|
    // otherwise if startConnect() fails, the multiplexer cannot be located
 | 
						|
    // by garbage collection and will cause leak
 | 
						|
    s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr);
 | 
						|
    CIPAddress::pton(s->m_pSelfAddr, s->m_pUDT->m_piSelfIP, s->m_iIPversion);
 | 
						|
 | 
						|
    s->m_Status = SRTS_CONNECTED;
 | 
						|
 | 
						|
    // acknowledde any waiting epolls to write
 | 
						|
    s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
 | 
						|
 | 
						|
    LOGC(mglog.Note, log << "Connection established to: " << SockaddrToString(m_pPeerAddr));
 | 
						|
 | 
						|
    return CONN_ACCEPT;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield)
 | 
						|
{
 | 
						|
    int enc_flags = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(typefield);
 | 
						|
 | 
						|
    // potentially 0-7 values are possible.
 | 
						|
    // When 0, don't change anything - it should rely on the value 0.
 | 
						|
    // When 1, 5, 6, 7, this is kinda internal error - ignore.
 | 
						|
    if (enc_flags >= 2 && enc_flags <= 4) // 2 = 128, 3 = 192, 4 = 256
 | 
						|
    {
 | 
						|
        int rcv_pbkeylen = SrtHSRequest::SRT_PBKEYLEN_BITS::wrap(enc_flags);
 | 
						|
        if (m_iSndCryptoKeyLen == 0)
 | 
						|
        {
 | 
						|
            m_iSndCryptoKeyLen = rcv_pbkeylen;
 | 
						|
            HLOGC(mglog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " << m_iSndCryptoKeyLen);
 | 
						|
        }
 | 
						|
        else if (m_iSndCryptoKeyLen != rcv_pbkeylen)
 | 
						|
        {
 | 
						|
            // Conflict. Use SRTO_SENDER flag to check if this side should accept
 | 
						|
            // the enforcement, otherwise simply let it win.
 | 
						|
            if (!m_bDataSender)
 | 
						|
            {
 | 
						|
                LOGC(mglog.Warn,
 | 
						|
                     log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_iSndCryptoKeyLen << " by "
 | 
						|
                         << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)");
 | 
						|
                m_iSndCryptoKeyLen = rcv_pbkeylen;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                LOGC(mglog.Warn,
 | 
						|
                     log << loghdr << ": PBKEYLEN conflict - keep " << m_iSndCryptoKeyLen
 | 
						|
                         << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (enc_flags != 0)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Error, log << loghdr << ": IPE: enc_flags outside allowed 2, 3, 4: " << enc_flags);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << loghdr << ": No encryption flags found in type field: " << typefield);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Rendezvous
 | 
						|
void CUDT::rendezvousSwitchState(ref_t<UDTRequestType> rsptype, ref_t<bool> needs_extension, ref_t<bool> needs_hsrsp)
 | 
						|
{
 | 
						|
    UDTRequestType req           = m_ConnRes.m_iReqType;
 | 
						|
    int            hs_flags      = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
 | 
						|
    bool           has_extension = !!hs_flags; // it holds flags, if no flags, there are no extensions.
 | 
						|
 | 
						|
    const HandshakeSide &hsd = m_SrtHsSide;
 | 
						|
    // Note important possibilities that are considered here:
 | 
						|
 | 
						|
    // 1. The serial arrangement. This happens when one party has missed the
 | 
						|
    // URQ_WAVEAHAND message, it sent its own URQ_WAVEAHAND message, and then the
 | 
						|
    // firstmost message it received from the peer is URQ_CONCLUSION, as a response
 | 
						|
    // for agent's URQ_WAVEAHAND.
 | 
						|
    //
 | 
						|
    // In this case, Agent switches to RDV_FINE state and Peer switches to RDV_ATTENTION state.
 | 
						|
    //
 | 
						|
    // 2. The parallel arrangement. This happens when the URQ_WAVEAHAND message sent
 | 
						|
    // by both parties are almost in a perfect synch (a rare, but possible case). In this
 | 
						|
    // case, both parties receive one another's URQ_WAVEAHAND message and both switch to
 | 
						|
    // RDV_ATTENTION state.
 | 
						|
    //
 | 
						|
    // It's not possible to predict neither which arrangement will happen, or which
 | 
						|
    // party will be RDV_FINE in case when the serial arrangement has happened. What
 | 
						|
    // will actually happen will depend on random conditions.
 | 
						|
    //
 | 
						|
    // No matter this randomity, we have a limited number of possible conditions:
 | 
						|
    //
 | 
						|
    // Stating that "agent" is the party that has received the URQ_WAVEAHAND in whatever
 | 
						|
    // arrangement, we are certain, that "agent" switched to RDV_ATTENTION, and peer:
 | 
						|
    //
 | 
						|
    // - switched to RDV_ATTENTION state (so, both are in the same state independently)
 | 
						|
    // - switched to RDV_FINE state (so, the message interchange is actually more-less sequenced)
 | 
						|
    //
 | 
						|
    // In particular, there's no possibility of a situation that both are in RDV_FINE state
 | 
						|
    // because the agent can switch to RDV_FINE state only if it received URQ_CONCLUSION from
 | 
						|
    // the peer, while the peer could not send URQ_CONCLUSION without switching off RDV_WAVING
 | 
						|
    // (actually to RDV_ATTENTION). There's also no exit to RDV_FINE from RDV_ATTENTION.
 | 
						|
 | 
						|
    // DEFAULT STATEMENT: don't attach extensions to URQ_CONCLUSION, neither HSREQ nor HSRSP.
 | 
						|
    *needs_extension = false;
 | 
						|
    *needs_hsrsp     = false;
 | 
						|
 | 
						|
    string reason;
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "rendezvousSwitchState: HS: " << m_ConnRes.show());
 | 
						|
 | 
						|
    struct LogAtTheEnd
 | 
						|
    {
 | 
						|
        CHandShake::RendezvousState        ost;
 | 
						|
        UDTRequestType                     orq;
 | 
						|
        const CHandShake::RendezvousState &nst;
 | 
						|
        const UDTRequestType &             nrq;
 | 
						|
        bool &                             needext;
 | 
						|
        bool &                             needrsp;
 | 
						|
        string &                           reason;
 | 
						|
 | 
						|
        ~LogAtTheEnd()
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "rendezvousSwitchState: STATE[" << CHandShake::RdvStateStr(ost) << "->"
 | 
						|
                      << CHandShake::RdvStateStr(nst) << "] REQTYPE[" << RequestTypeStr(orq) << "->"
 | 
						|
                      << RequestTypeStr(nrq) << "] "
 | 
						|
                      << "ext:" << (needext ? (needrsp ? "HSRSP" : "HSREQ") : "NONE")
 | 
						|
                      << (reason == "" ? string() : "reason:" + reason));
 | 
						|
        }
 | 
						|
    } l_logend = {m_RdvState, req, m_RdvState, *rsptype, *needs_extension, *needs_hsrsp, reason};
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    switch (m_RdvState)
 | 
						|
    {
 | 
						|
    case CHandShake::RDV_INVALID:
 | 
						|
        return;
 | 
						|
 | 
						|
    case CHandShake::RDV_WAVING:
 | 
						|
    {
 | 
						|
        if (req == URQ_WAVEAHAND)
 | 
						|
        {
 | 
						|
            m_RdvState = CHandShake::RDV_ATTENTION;
 | 
						|
 | 
						|
            // NOTE: if this->isWinner(), attach HSREQ
 | 
						|
            *rsptype = URQ_CONCLUSION;
 | 
						|
            if (hsd == HSD_INITIATOR)
 | 
						|
                *needs_extension = true;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (req == URQ_CONCLUSION)
 | 
						|
        {
 | 
						|
            m_RdvState = CHandShake::RDV_FINE;
 | 
						|
            *rsptype   = URQ_CONCLUSION;
 | 
						|
 | 
						|
            *needs_extension = true; // (see below - this needs to craft either HSREQ or HSRSP)
 | 
						|
            // if this->isWinner(), then craft HSREQ for that response.
 | 
						|
            // if this->isLoser(), then this packet should bring HSREQ, so craft HSRSP for the response.
 | 
						|
            if (hsd == HSD_RESPONDER)
 | 
						|
                *needs_hsrsp = true;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
        reason = "WAVING -> WAVEAHAND or CONCLUSION";
 | 
						|
        break;
 | 
						|
 | 
						|
    case CHandShake::RDV_ATTENTION:
 | 
						|
    {
 | 
						|
        if (req == URQ_WAVEAHAND)
 | 
						|
        {
 | 
						|
            // This is only possible if the URQ_CONCLUSION sent to the peer
 | 
						|
            // was lost on track. The peer is then simply unaware that the
 | 
						|
            // agent has switched to ATTENTION state and continues sending
 | 
						|
            // waveahands. In this case, just remain in ATTENTION state and
 | 
						|
            // retry with URQ_CONCLUSION, as normally.
 | 
						|
            *rsptype = URQ_CONCLUSION;
 | 
						|
            if (hsd == HSD_INITIATOR)
 | 
						|
                *needs_extension = true;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (req == URQ_CONCLUSION)
 | 
						|
        {
 | 
						|
            // We have two possibilities here:
 | 
						|
            //
 | 
						|
            // WINNER (HSD_INITIATOR): send URQ_AGREEMENT
 | 
						|
            if (hsd == HSD_INITIATOR)
 | 
						|
            {
 | 
						|
                // WINNER should get a response with HSRSP, otherwise this is kinda empty conclusion.
 | 
						|
                // If no HSRSP attached, stay in this state.
 | 
						|
                if (hs_flags == 0)
 | 
						|
                {
 | 
						|
                    HLOGC(
 | 
						|
                        mglog.Debug,
 | 
						|
                        log << "rendezvousSwitchState: "
 | 
						|
                               "{INITIATOR}[ATTENTION] awaits CONCLUSION+HSRSP, got CONCLUSION, remain in [ATTENTION]");
 | 
						|
                    *rsptype         = URQ_CONCLUSION;
 | 
						|
                    *needs_extension = true; // If you expect to receive HSRSP, continue sending HSREQ
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
                *rsptype   = URQ_AGREEMENT;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // LOSER (HSD_RESPONDER): send URQ_CONCLUSION and attach HSRSP extension, then expect URQ_AGREEMENT
 | 
						|
            if (hsd == HSD_RESPONDER)
 | 
						|
            {
 | 
						|
                // If no HSREQ attached, stay in this state.
 | 
						|
                // (Although this seems completely impossible).
 | 
						|
                if (hs_flags == 0)
 | 
						|
                {
 | 
						|
                    LOGC(
 | 
						|
                        mglog.Warn,
 | 
						|
                        log << "rendezvousSwitchState: (IPE!)"
 | 
						|
                               "{RESPONDER}[ATTENTION] awaits CONCLUSION+HSREQ, got CONCLUSION, remain in [ATTENTION]");
 | 
						|
                    *rsptype         = URQ_CONCLUSION;
 | 
						|
                    *needs_extension = false; // If you received WITHOUT extensions, respond WITHOUT extensions (wait
 | 
						|
                                              // for the right message)
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                m_RdvState       = CHandShake::RDV_INITIATED;
 | 
						|
                *rsptype         = URQ_CONCLUSION;
 | 
						|
                *needs_extension = true;
 | 
						|
                *needs_hsrsp     = true;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            LOGC(mglog.Error, log << "RENDEZVOUS COOKIE DRAW! Cannot resolve to a valid state.");
 | 
						|
            // Fallback for cookie draw
 | 
						|
            m_RdvState = CHandShake::RDV_INVALID;
 | 
						|
            *rsptype   = URQFailure(SRT_REJ_RDVCOOKIE);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (req == URQ_AGREEMENT)
 | 
						|
        {
 | 
						|
            // This means that the peer has received our URQ_CONCLUSION, but
 | 
						|
            // the agent missed the peer's URQ_CONCLUSION (received only initial
 | 
						|
            // URQ_WAVEAHAND).
 | 
						|
            if (hsd == HSD_INITIATOR)
 | 
						|
            {
 | 
						|
                // In this case the missed URQ_CONCLUSION was sent without extensions,
 | 
						|
                // whereas the peer received our URQ_CONCLUSION with HSREQ, and therefore
 | 
						|
                // it sent URQ_AGREEMENT already with HSRSP. This isn't a problem for
 | 
						|
                // us, we can go on with it, especially that the peer is already switched
 | 
						|
                // into CHandShake::RDV_CONNECTED state.
 | 
						|
                m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
 | 
						|
                // Both sides are connected, no need to send anything anymore.
 | 
						|
                *rsptype = URQ_DONE;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            if (hsd == HSD_RESPONDER)
 | 
						|
            {
 | 
						|
                // In this case the missed URQ_CONCLUSION was sent with extensions, so
 | 
						|
                // we have to request this once again. Send URQ_CONCLUSION in order to
 | 
						|
                // inform the other party that we need the conclusion message once again.
 | 
						|
                // The ATTENTION state should be maintained.
 | 
						|
                *rsptype         = URQ_CONCLUSION;
 | 
						|
                *needs_extension = true;
 | 
						|
                *needs_hsrsp     = true;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
        reason = "ATTENTION -> WAVEAHAND(conclusion), CONCLUSION(agreement/conclusion), AGREEMENT (done/conclusion)";
 | 
						|
        break;
 | 
						|
 | 
						|
    case CHandShake::RDV_FINE:
 | 
						|
    {
 | 
						|
        // In FINE state we can't receive URQ_WAVEAHAND because if the peer has already
 | 
						|
        // sent URQ_CONCLUSION, it's already in CHandShake::RDV_ATTENTION, and in this state it can
 | 
						|
        // only send URQ_CONCLUSION, whereas when it isn't in CHandShake::RDV_ATTENTION, it couldn't
 | 
						|
        // have sent URQ_CONCLUSION, and if it didn't, the agent wouldn't be in CHandShake::RDV_FINE state.
 | 
						|
 | 
						|
        if (req == URQ_CONCLUSION)
 | 
						|
        {
 | 
						|
            // There's only one case when it should receive CONCLUSION in FINE state:
 | 
						|
            // When it's the winner. If so, it should then contain HSREQ extension.
 | 
						|
            // In case of loser, it shouldn't receive CONCLUSION at all - it should
 | 
						|
            // receive AGREEMENT.
 | 
						|
 | 
						|
            // The winner case, received CONCLUSION + HSRSP - switch to CONNECTED and send AGREEMENT.
 | 
						|
            // So, check first if HAS EXTENSION
 | 
						|
 | 
						|
            bool correct_switch = false;
 | 
						|
            if (hsd == HSD_INITIATOR && !has_extension)
 | 
						|
            {
 | 
						|
                // Received REPEATED empty conclusion that has initially switched it into FINE state.
 | 
						|
                // To exit FINE state we need the CONCLUSION message with HSRSP.
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "rendezvousSwitchState: {INITIATOR}[FINE] <CONCLUSION without HSRSP. Stay in [FINE], "
 | 
						|
                             "await CONCLUSION+HSRSP");
 | 
						|
            }
 | 
						|
            else if (hsd == HSD_RESPONDER)
 | 
						|
            {
 | 
						|
                // In FINE state the RESPONDER expects only to be sent AGREEMENT.
 | 
						|
                // It has previously received CONCLUSION in WAVING state and this has switched
 | 
						|
                // it to FINE state. That CONCLUSION message should have contained extension,
 | 
						|
                // so if this is a repeated CONCLUSION+HSREQ, it should be responded with
 | 
						|
                // CONCLUSION+HSRSP.
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "rendezvousSwitchState: {RESPONDER}[FINE] <CONCLUSION. Stay in [FINE], await AGREEMENT");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                correct_switch = true;
 | 
						|
            }
 | 
						|
 | 
						|
            if (!correct_switch)
 | 
						|
            {
 | 
						|
                *rsptype = URQ_CONCLUSION;
 | 
						|
                // initiator should send HSREQ, responder HSRSP,
 | 
						|
                // in both cases extension is needed
 | 
						|
                *needs_extension = true;
 | 
						|
                *needs_hsrsp     = hsd == HSD_RESPONDER;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
            *rsptype   = URQ_AGREEMENT;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (req == URQ_AGREEMENT)
 | 
						|
        {
 | 
						|
            // The loser case, the agreement was sent in response to conclusion that
 | 
						|
            // already carried over the HSRSP extension.
 | 
						|
 | 
						|
            // There's a theoretical case when URQ_AGREEMENT can be received in case of
 | 
						|
            // parallel arrangement, while the agent is already in CHandShake::RDV_CONNECTED state.
 | 
						|
            // This will be dispatched in the main loop and discarded.
 | 
						|
 | 
						|
            m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
            *rsptype   = URQ_DONE;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
        reason = "FINE -> CONCLUSION(agreement), AGREEMENT(done)";
 | 
						|
        break;
 | 
						|
    case CHandShake::RDV_INITIATED:
 | 
						|
    {
 | 
						|
        // In this state we just wait for URQ_AGREEMENT, which should cause it to
 | 
						|
        // switch to CONNECTED. No response required.
 | 
						|
        if (req == URQ_AGREEMENT)
 | 
						|
        {
 | 
						|
            // No matter in which state we'd be, just switch to connected.
 | 
						|
            if (m_RdvState == CHandShake::RDV_CONNECTED)
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug, log << "<-- AGREEMENT: already connected");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug, log << "<-- AGREEMENT: switched to connected");
 | 
						|
            }
 | 
						|
            m_RdvState = CHandShake::RDV_CONNECTED;
 | 
						|
            *rsptype   = URQ_DONE;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (req == URQ_CONCLUSION)
 | 
						|
        {
 | 
						|
            // Receiving conclusion in this state means that the other party
 | 
						|
            // didn't get our conclusion, so send it again, the same as when
 | 
						|
            // exiting the ATTENTION state.
 | 
						|
            *rsptype = URQ_CONCLUSION;
 | 
						|
            if (hsd == HSD_RESPONDER)
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "rendezvousSwitchState: "
 | 
						|
                             "{RESPONDER}[INITIATED] awaits AGREEMENT, "
 | 
						|
                             "got CONCLUSION, sending CONCLUSION+HSRSP");
 | 
						|
                *needs_extension = true;
 | 
						|
                *needs_hsrsp     = true;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Loser, initiated? This may only happen in parallel arrangement, where
 | 
						|
            // the agent exchanges empty conclusion messages with the peer, simultaneously
 | 
						|
            // exchanging HSREQ-HSRSP conclusion messages. Check if THIS message contained
 | 
						|
            // HSREQ, and set responding HSRSP in that case.
 | 
						|
            if (hs_flags == 0)
 | 
						|
            {
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "rendezvousSwitchState: "
 | 
						|
                             "{INITIATOR}[INITIATED] awaits AGREEMENT, "
 | 
						|
                             "got empty CONCLUSION, STILL RESPONDING CONCLUSION+HSRSP");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "rendezvousSwitchState: "
 | 
						|
                             "{INITIATOR}[INITIATED] awaits AGREEMENT, "
 | 
						|
                             "got CONCLUSION+HSREQ, responding CONCLUSION+HSRSP");
 | 
						|
            }
 | 
						|
            *needs_extension = true;
 | 
						|
            *needs_hsrsp     = true;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
        reason = "INITIATED -> AGREEMENT(done)";
 | 
						|
        break;
 | 
						|
 | 
						|
    case CHandShake::RDV_CONNECTED:
 | 
						|
        // Do nothing. This theoretically should never happen.
 | 
						|
        *rsptype = URQ_DONE;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "rendezvousSwitchState: INVALID STATE TRANSITION, result: INVALID");
 | 
						|
    // All others are treated as errors
 | 
						|
    m_RdvState = CHandShake::RDV_WAVING;
 | 
						|
    *rsptype   = URQFailure(SRT_REJ_ROGUE);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Timestamp-based Packet Delivery (TsbPd) thread
 | 
						|
 * This thread runs only if TsbPd mode is enabled
 | 
						|
 * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay.
 | 
						|
 */
 | 
						|
void *CUDT::tsbpd(void *param)
 | 
						|
{
 | 
						|
    CUDT *self = (CUDT *)param;
 | 
						|
 | 
						|
    THREAD_STATE_INIT("SRT:TsbPd");
 | 
						|
 | 
						|
    CGuard::enterCS(self->m_RecvLock);
 | 
						|
    self->m_bTsbPdAckWakeup = true;
 | 
						|
    while (!self->m_bClosing)
 | 
						|
    {
 | 
						|
        int32_t  current_pkt_seq = 0;
 | 
						|
        uint64_t tsbpdtime       = 0;
 | 
						|
        bool     rxready         = false;
 | 
						|
 | 
						|
        CGuard::enterCS(self->m_RcvBufferLock);
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
 | 
						|
        self->m_pRcvBuffer->updRcvAvgDataSize(CTimer::getTime());
 | 
						|
#endif
 | 
						|
 | 
						|
        if (self->m_bTLPktDrop)
 | 
						|
        {
 | 
						|
            int32_t skiptoseqno = -1;
 | 
						|
            bool passack = true; // Get next packet to wait for even if not acked
 | 
						|
 | 
						|
            rxready = self->m_pRcvBuffer->getRcvFirstMsg(
 | 
						|
                Ref(tsbpdtime), Ref(passack), Ref(skiptoseqno), Ref(current_pkt_seq));
 | 
						|
 | 
						|
            HLOGC(tslog.Debug,
 | 
						|
                  log << boolalpha << "NEXT PKT CHECK: rdy=" << rxready << " passack=" << passack << " skipto=%"
 | 
						|
                      << skiptoseqno << " current=%" << current_pkt_seq << " buf-base=%" << self->m_iRcvLastSkipAck);
 | 
						|
            /*
 | 
						|
             * VALUES RETURNED:
 | 
						|
             *
 | 
						|
             * rxready:     if true, packet at head of queue ready to play
 | 
						|
             * tsbpdtime:   timestamp of packet at head of queue, ready or not. 0 if none.
 | 
						|
             * passack:     if true, ready head of queue not yet acknowledged
 | 
						|
             * skiptoseqno: sequence number of packet at head of queue if ready to play but
 | 
						|
             *              some preceeding packets are missing (need to be skipped). -1 if none.
 | 
						|
             */
 | 
						|
            if (rxready)
 | 
						|
            {
 | 
						|
                /* Packet ready to play according to time stamp but... */
 | 
						|
                int seqlen = CSeqNo::seqoff(self->m_iRcvLastSkipAck, skiptoseqno);
 | 
						|
 | 
						|
                if (skiptoseqno != -1 && seqlen > 0)
 | 
						|
                {
 | 
						|
                    /*
 | 
						|
                     * skiptoseqno != -1,
 | 
						|
                     * packet ready to play but preceeded by missing packets (hole).
 | 
						|
                     */
 | 
						|
 | 
						|
                    /* Update drop/skip stats */
 | 
						|
                    CGuard::enterCS(self->m_StatsLock);
 | 
						|
                    self->m_stats.rcvDropTotal += seqlen;
 | 
						|
                    self->m_stats.traceRcvDrop += seqlen;
 | 
						|
                    /* Estimate dropped/skipped bytes from average payload */
 | 
						|
                    int avgpayloadsz = self->m_pRcvBuffer->getRcvAvgPayloadSize();
 | 
						|
                    self->m_stats.rcvBytesDropTotal += seqlen * avgpayloadsz;
 | 
						|
                    self->m_stats.traceRcvBytesDrop += seqlen * avgpayloadsz;
 | 
						|
                    CGuard::leaveCS(self->m_StatsLock);
 | 
						|
 | 
						|
                    self->dropFromLossLists(self->m_iRcvLastSkipAck,
 | 
						|
                                            CSeqNo::decseq(skiptoseqno)); // remove(from,to-inclusive)
 | 
						|
                    self->m_pRcvBuffer->skipData(seqlen);
 | 
						|
 | 
						|
                    self->m_iRcvLastSkipAck = skiptoseqno;
 | 
						|
 | 
						|
#if ENABLE_LOGGING
 | 
						|
                    int64_t timediff = 0;
 | 
						|
                    if (tsbpdtime)
 | 
						|
                        timediff = int64_t(tsbpdtime) - int64_t(CTimer::getTime());
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
                    HLOGC(tslog.Debug,
 | 
						|
                          log << self->CONID() << "tsbpd: DROPSEQ: up to seq=" << CSeqNo::decseq(skiptoseqno) << " ("
 | 
						|
                              << seqlen << " packets) playable at " << FormatTime(tsbpdtime) << " delayed "
 | 
						|
                              << (timediff / 1000) << "." << (timediff % 1000) << " ms");
 | 
						|
#endif
 | 
						|
                    LOGC(dlog.Debug, log << "RCV-DROPPED packet delay=" << (timediff / 1000) << "ms");
 | 
						|
#endif
 | 
						|
 | 
						|
                    tsbpdtime = 0; // Next sent ack will unblock
 | 
						|
                    rxready   = false;
 | 
						|
                }
 | 
						|
                else if (passack)
 | 
						|
                {
 | 
						|
                    /* Packets ready to play but not yet acknowledged (should happen within 10ms) */
 | 
						|
                    rxready   = false;
 | 
						|
                    tsbpdtime = 0; // Next sent ack will unblock
 | 
						|
                }                  /* else packet ready to play */
 | 
						|
            }                      /* else packets not ready to play */
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            rxready = self->m_pRcvBuffer->isRcvDataReady(Ref(tsbpdtime), Ref(current_pkt_seq));
 | 
						|
        }
 | 
						|
        CGuard::leaveCS(self->m_RcvBufferLock);
 | 
						|
 | 
						|
        if (rxready)
 | 
						|
        {
 | 
						|
            HLOGC(tslog.Debug,
 | 
						|
                  log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << current_pkt_seq << " (belated "
 | 
						|
                      << ((CTimer::getTime() - tsbpdtime) / 1000.0) << "ms)");
 | 
						|
            /*
 | 
						|
             * There are packets ready to be delivered
 | 
						|
             * signal a waiting "recv" call if there is any data available
 | 
						|
             */
 | 
						|
            if (self->m_bSynRecving)
 | 
						|
            {
 | 
						|
                pthread_cond_signal(&self->m_RecvDataCond);
 | 
						|
            }
 | 
						|
            /*
 | 
						|
             * Set EPOLL_IN to wakeup any thread waiting on epoll
 | 
						|
             */
 | 
						|
            self->s_UDTUnited.m_EPoll.update_events(self->m_SocketID, self->m_sPollID, UDT_EPOLL_IN, true);
 | 
						|
            CTimer::triggerEvent();
 | 
						|
            tsbpdtime = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        if (tsbpdtime != 0)
 | 
						|
        {
 | 
						|
            int64_t timediff = int64_t(tsbpdtime) - int64_t(CTimer::getTime());
 | 
						|
            /*
 | 
						|
             * Buffer at head of queue is not ready to play.
 | 
						|
             * Schedule wakeup when it will be.
 | 
						|
             */
 | 
						|
            self->m_bTsbPdAckWakeup = false;
 | 
						|
            THREAD_PAUSED();
 | 
						|
            HLOGC(tslog.Debug,
 | 
						|
                  log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << current_pkt_seq
 | 
						|
                      << " T=" << FormatTime(tsbpdtime) << " - waiting " << (timediff / 1000.0) << "ms");
 | 
						|
            CTimer::condTimedWaitUS(&self->m_RcvTsbPdCond, &self->m_RecvLock, timediff);
 | 
						|
            THREAD_RESUMED();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            /*
 | 
						|
             * We have just signaled epoll; or
 | 
						|
             * receive queue is empty; or
 | 
						|
             * next buffer to deliver is not in receive queue (missing packet in sequence).
 | 
						|
             *
 | 
						|
             * Block until woken up by one of the following event:
 | 
						|
             * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time
 | 
						|
             * if any)
 | 
						|
             * - New buffers ACKed
 | 
						|
             * - Closing the connection
 | 
						|
             */
 | 
						|
            HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack");
 | 
						|
            self->m_bTsbPdAckWakeup = true;
 | 
						|
            THREAD_PAUSED();
 | 
						|
            pthread_cond_wait(&self->m_RcvTsbPdCond, &self->m_RecvLock);
 | 
						|
            THREAD_RESUMED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    CGuard::leaveCS(self->m_RecvLock);
 | 
						|
    THREAD_EXIT();
 | 
						|
    HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING");
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout)
 | 
						|
{
 | 
						|
    // This will be lazily created due to being the common
 | 
						|
    // code with HSv5 rendezvous, in which this will be run
 | 
						|
    // in a little bit "randomly selected" moment, but must
 | 
						|
    // be run once in the whole connection process.
 | 
						|
    if (m_pSndBuffer)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "prepareConnectionObjects: (lazy) already created.");
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool bidirectional = false;
 | 
						|
    if (hs.m_iVersion > HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        bidirectional = true; // HSv5 is always bidirectional
 | 
						|
    }
 | 
						|
 | 
						|
    // HSD_DRAW is received only if this side is listener.
 | 
						|
    // If this side is caller with HSv5, HSD_INITIATOR should be passed.
 | 
						|
    // If this is a rendezvous connection with HSv5, the handshake role
 | 
						|
    // is taken from m_SrtHsSide field.
 | 
						|
    if (hsd == HSD_DRAW)
 | 
						|
    {
 | 
						|
        if (bidirectional)
 | 
						|
        {
 | 
						|
            hsd = HSD_RESPONDER; // In HSv5, listener is always RESPONDER and caller always INITIATOR.
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            hsd = m_bDataSender ? HSD_INITIATOR : HSD_RESPONDER;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    try
 | 
						|
    {
 | 
						|
        m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize);
 | 
						|
        m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize);
 | 
						|
        // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space.
 | 
						|
        m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2);
 | 
						|
        m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize);
 | 
						|
    }
 | 
						|
    catch (...)
 | 
						|
    {
 | 
						|
        // Simply reject.
 | 
						|
        if (eout)
 | 
						|
        {
 | 
						|
            *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
 | 
						|
        }
 | 
						|
        m_RejectReason = SRT_REJ_RESOURCE;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!createCrypter(hsd, bidirectional)) // Make sure CC is created (lazy)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_RESOURCE;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::acceptAndRespond(const sockaddr *peer, CHandShake *hs, const CPacket &hspkt)
 | 
						|
{
 | 
						|
    HLOGC(mglog.Debug, log << "acceptAndRespond: setting up data according to handshake");
 | 
						|
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    m_ullRcvPeerStartTime = 0; // will be set correctly at SRT HS
 | 
						|
 | 
						|
    // Uses the smaller MSS between the peers
 | 
						|
    if (hs->m_iMSS > m_iMSS)
 | 
						|
        hs->m_iMSS = m_iMSS;
 | 
						|
    else
 | 
						|
        m_iMSS = hs->m_iMSS;
 | 
						|
 | 
						|
    // exchange info for maximum flow window size
 | 
						|
    m_iFlowWindowSize     = hs->m_iFlightFlagSize;
 | 
						|
    hs->m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize;
 | 
						|
 | 
						|
    m_iPeerISN = hs->m_iISN;
 | 
						|
 | 
						|
    m_iRcvLastAck = hs->m_iISN;
 | 
						|
#ifdef ENABLE_LOGGING
 | 
						|
    m_iDebugPrevLastAck = m_iRcvLastAck;
 | 
						|
#endif
 | 
						|
    m_iRcvLastSkipAck  = m_iRcvLastAck;
 | 
						|
    m_iRcvLastAckAck   = hs->m_iISN;
 | 
						|
    m_iRcvCurrSeqNo    = hs->m_iISN - 1;
 | 
						|
    m_iRcvCurrPhySeqNo = hs->m_iISN - 1;
 | 
						|
 | 
						|
    m_PeerID  = hs->m_iID;
 | 
						|
    hs->m_iID = m_SocketID;
 | 
						|
 | 
						|
    // use peer's ISN and send it back for security check
 | 
						|
    m_iISN = hs->m_iISN;
 | 
						|
 | 
						|
    m_iLastDecSeq        = m_iISN - 1;
 | 
						|
    m_iSndLastAck        = m_iISN;
 | 
						|
    m_iSndLastDataAck    = m_iISN;
 | 
						|
    m_iSndLastFullAck    = m_iISN;
 | 
						|
    m_iSndCurrSeqNo      = m_iISN - 1;
 | 
						|
    m_iSndLastAck2       = m_iISN;
 | 
						|
    m_ullSndLastAck2Time = CTimer::getTime();
 | 
						|
 | 
						|
    // this is a reponse handshake
 | 
						|
    hs->m_iReqType = URQ_CONCLUSION;
 | 
						|
 | 
						|
    if (hs->m_iVersion > HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        // The version is agreed; this code is executed only in case
 | 
						|
        // when AGENT is listener. In this case, conclusion response
 | 
						|
        // must always contain HSv5 handshake extensions.
 | 
						|
        hs->m_extension = true;
 | 
						|
    }
 | 
						|
 | 
						|
    // get local IP address and send the peer its IP address (because UDP cannot get local IP address)
 | 
						|
    memcpy(m_piSelfIP, hs->m_piPeerIP, 16);
 | 
						|
    CIPAddress::ntop(peer, hs->m_piPeerIP, m_iIPversion);
 | 
						|
 | 
						|
    int udpsize          = m_iMSS - CPacket::UDP_HDR_SIZE;
 | 
						|
    m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE;
 | 
						|
    HLOGC(mglog.Debug, log << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
    // Prepare all structures
 | 
						|
    if (!prepareConnectionObjects(*hs, HSD_DRAW, 0))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT.");
 | 
						|
        // If the SRT Handshake extension was provided and wasn't interpreted
 | 
						|
        // correctly, the connection should be rejected.
 | 
						|
        //
 | 
						|
        // Respond with the rejection message and exit with exception
 | 
						|
        // so that the caller will know that this new socket should be deleted.
 | 
						|
        hs->m_iReqType = URQFailure(m_RejectReason);
 | 
						|
        throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
    }
 | 
						|
    // Since now you can use m_pCryptoControl
 | 
						|
 | 
						|
    CInfoBlock ib;
 | 
						|
    ib.m_iIPversion = m_iIPversion;
 | 
						|
    CInfoBlock::convert(peer, m_iIPversion, ib.m_piIP);
 | 
						|
    if (m_pCache->lookup(&ib) >= 0)
 | 
						|
    {
 | 
						|
        m_iRTT       = ib.m_iRTT;
 | 
						|
        m_iBandwidth = ib.m_iBandwidth;
 | 
						|
    }
 | 
						|
 | 
						|
    // This should extract the HSREQ and KMREQ portion in the handshake packet.
 | 
						|
    // This could still be a HSv4 packet and contain no such parts, which will leave
 | 
						|
    // this entity as "non-SRT-handshaken", and await further HSREQ and KMREQ sent
 | 
						|
    // as UMSG_EXT.
 | 
						|
    uint32_t kmdata[SRTDATA_MAXSIZE];
 | 
						|
    size_t   kmdatasize = SRTDATA_MAXSIZE;
 | 
						|
    if (!interpretSrtHandshake(*hs, hspkt, kmdata, &kmdatasize))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "acceptAndRespond: interpretSrtHandshake failed - responding with REJECT.");
 | 
						|
        // If the SRT Handshake extension was provided and wasn't interpreted
 | 
						|
        // correctly, the connection should be rejected.
 | 
						|
        //
 | 
						|
        // Respond with the rejection message and return false from
 | 
						|
        // this function so that the caller will know that this new
 | 
						|
        // socket should be deleted.
 | 
						|
        hs->m_iReqType = URQFailure(m_RejectReason);
 | 
						|
        throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    SRT_REJECT_REASON rr = setupCC();
 | 
						|
    // UNKNOWN used as a "no error" value
 | 
						|
    if (rr != SRT_REJ_UNKNOWN)
 | 
						|
    {
 | 
						|
        hs->m_iReqType = URQFailure(rr);
 | 
						|
        m_RejectReason = rr;
 | 
						|
        throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr *)new sockaddr_in : (sockaddr *)new sockaddr_in6;
 | 
						|
    memcpy(m_pPeerAddr, peer, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6));
 | 
						|
 | 
						|
    // And of course, it is connected.
 | 
						|
    m_bConnected = true;
 | 
						|
 | 
						|
    // register this socket for receiving data packets
 | 
						|
    m_pRNode->m_bOnList = true;
 | 
						|
    m_pRcvQueue->setNewEntry(this);
 | 
						|
 | 
						|
    // send the response to the peer, see listen() for more discussions about this
 | 
						|
    // XXX Here create CONCLUSION RESPONSE with:
 | 
						|
    // - just the UDT handshake, if HS_VERSION_UDT4,
 | 
						|
    // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP
 | 
						|
    size_t size = m_iMaxSRTPayloadSize;
 | 
						|
    // Allocate the maximum possible memory for an SRT payload.
 | 
						|
    // This is a maximum you can send once.
 | 
						|
    CPacket response;
 | 
						|
    response.setControl(UMSG_HANDSHAKE);
 | 
						|
    response.allocate(size);
 | 
						|
 | 
						|
    // This will serialize the handshake according to its current form.
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size);
 | 
						|
    if (!createSrtHandshake(Ref(response), Ref(*hs), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize))
 | 
						|
    {
 | 
						|
        LOGC(mglog.Error, log << "acceptAndRespond: error creating handshake response");
 | 
						|
        throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    // Set target socket ID to the value from received handshake's source ID.
 | 
						|
    response.m_iID = m_PeerID;
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
    {
 | 
						|
        // To make sure what REALLY is being sent, parse back the handshake
 | 
						|
        // data that have been just written into the buffer.
 | 
						|
        CHandShake debughs;
 | 
						|
        debughs.load_from(response.m_pcData, response.getLength());
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << CONID() << "acceptAndRespond: sending HS to peer, reqtype=" << RequestTypeStr(debughs.m_iReqType)
 | 
						|
                  << " version=" << debughs.m_iVersion << " (connreq:" << RequestTypeStr(m_ConnReq.m_iReqType)
 | 
						|
                  << "), target_socket=" << response.m_iID << ", my_socket=" << debughs.m_iID);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // NOTE: BLOCK THIS instruction in order to cause the final
 | 
						|
    // handshake to be missed and cause the problem solved in PR #417.
 | 
						|
    // When missed this message, the caller should not accept packets
 | 
						|
    // coming as connected, but continue repeated handshake until finally
 | 
						|
    // received the listener's handshake.
 | 
						|
    m_pSndQueue->sendto(peer, response);
 | 
						|
}
 | 
						|
 | 
						|
// This function is required to be called when a caller receives an INDUCTION
 | 
						|
// response from the listener and would like to create a CONCLUSION that includes
 | 
						|
// the SRT handshake extension. This extension requires that the crypter object
 | 
						|
// be created, but it's still too early for it to be completely configured.
 | 
						|
// This function then precreates the object so that the handshake extension can
 | 
						|
// be created, as this happens before the completion of the connection (and
 | 
						|
// therefore configuration of the crypter object), which can only take place upon
 | 
						|
// reception of CONCLUSION response from the listener.
 | 
						|
bool CUDT::createCrypter(HandshakeSide side, bool bidirectional)
 | 
						|
{
 | 
						|
    // Lazy initialization
 | 
						|
    if (m_pCryptoControl)
 | 
						|
        return true;
 | 
						|
 | 
						|
    // Write back this value, when it was just determined.
 | 
						|
    m_SrtHsSide = side;
 | 
						|
 | 
						|
    m_pCryptoControl.reset(new CCryptoControl(this, m_SocketID));
 | 
						|
 | 
						|
    // XXX These below are a little bit controversial.
 | 
						|
    // These data should probably be filled only upon
 | 
						|
    // reception of the conclusion handshake - otherwise
 | 
						|
    // they have outdated values.
 | 
						|
    m_pCryptoControl->setCryptoSecret(m_CryptoSecret);
 | 
						|
 | 
						|
    if (bidirectional || m_bDataSender)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_iSndCryptoKeyLen);
 | 
						|
        m_pCryptoControl->setCryptoKeylen(m_iSndCryptoKeyLen);
 | 
						|
    }
 | 
						|
 | 
						|
    return m_pCryptoControl->init(side, bidirectional);
 | 
						|
}
 | 
						|
 | 
						|
SRT_REJECT_REASON CUDT::setupCC()
 | 
						|
{
 | 
						|
    // Prepare configuration object,
 | 
						|
    // Create the CCC object and configure it.
 | 
						|
 | 
						|
    // UDT also sets back the congestion window: ???
 | 
						|
    // m_dCongestionWindow = m_pCC->m_dCWndSize;
 | 
						|
 | 
						|
    // XXX Not sure about that. May happen that AGENT wants
 | 
						|
    // tsbpd mode, but PEER doesn't, even in bidirectional mode.
 | 
						|
    // This way, the reception side should get precedense.
 | 
						|
    // if (bidirectional || m_bDataSender || m_bTwoWayData)
 | 
						|
    //    m_bPeerTsbPd = m_bOPT_TsbPd;
 | 
						|
 | 
						|
    // SrtCongestion will retrieve whatever parameters it needs
 | 
						|
    // from *this.
 | 
						|
    if (!m_CongCtl.configure(this))
 | 
						|
    {
 | 
						|
        return SRT_REJ_CONGESTION;
 | 
						|
    }
 | 
						|
 | 
						|
    // Configure filter module
 | 
						|
    if (m_OPT_PktFilterConfigString != "")
 | 
						|
    {
 | 
						|
        // This string, when nonempty, defines that the corrector shall be
 | 
						|
        // configured. Otherwise it's left uninitialized.
 | 
						|
 | 
						|
        // At this point we state everything is checked and the appropriate
 | 
						|
        // corrector type is already selected, so now create it.
 | 
						|
        HLOGC(mglog.Debug, log << "filter: Configuring Corrector: " << m_OPT_PktFilterConfigString);
 | 
						|
        if (!m_PacketFilter.configure(this, m_pRcvBuffer->getUnitQueue(), m_OPT_PktFilterConfigString))
 | 
						|
        {
 | 
						|
            return SRT_REJ_FILTER;
 | 
						|
        }
 | 
						|
 | 
						|
        m_PktFilterRexmitLevel = m_PacketFilter.arqLevel();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // When we have no filter, ARQ should work in ALWAYS mode.
 | 
						|
        m_PktFilterRexmitLevel = SRT_ARQ_ALWAYS;
 | 
						|
    }
 | 
						|
 | 
						|
    // Override the value of minimum NAK interval, per SrtCongestion's wish.
 | 
						|
    // When default 0 value is returned, the current value set by CUDT
 | 
						|
    // is preserved.
 | 
						|
    uint64_t min_nak_tk = m_CongCtl->minNAKInterval();
 | 
						|
    if (min_nak_tk)
 | 
						|
        m_ullMinNakInt_tk = min_nak_tk;
 | 
						|
 | 
						|
    // Update timers
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
    m_ullLastRspTime_tk    = currtime_tk;
 | 
						|
    m_ullNextACKTime_tk    = currtime_tk + m_ullACKInt_tk;
 | 
						|
    m_ullNextNAKTime_tk    = currtime_tk + m_ullNAKInt_tk;
 | 
						|
    m_ullLastRspAckTime_tk = currtime_tk;
 | 
						|
    m_ullLastSndTime_tk    = currtime_tk;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "setupCC: setting parameters: mss=" << m_iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize
 | 
						|
              << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)"
 | 
						|
              << " rtt=" << m_iRTT << " bw=" << m_iBandwidth);
 | 
						|
 | 
						|
    updateCC(TEV_INIT, TEV_INIT_RESET);
 | 
						|
    return SRT_REJ_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::considerLegacySrtHandshake(uint64_t timebase)
 | 
						|
{
 | 
						|
    // Do a fast pre-check first - this simply declares that agent uses HSv5
 | 
						|
    // and the legacy SRT Handshake is not to be done. Second check is whether
 | 
						|
    // agent is sender (=initiator in HSv4).
 | 
						|
    if (!isTsbPd() || !m_bDataSender)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (m_iSndHsRetryCnt <= 0)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "Legacy HSREQ: not needed, expire counter=" << m_iSndHsRetryCnt);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    uint64_t now = CTimer::getTime();
 | 
						|
    if (timebase != 0)
 | 
						|
    {
 | 
						|
        // Then this should be done only if it's the right time,
 | 
						|
        // the TSBPD mode is on, and when the counter is "still rolling".
 | 
						|
        /*
 | 
						|
         * SRT Handshake with peer:
 | 
						|
         * If...
 | 
						|
         * - we want TsbPd mode; and
 | 
						|
         * - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and
 | 
						|
         * - and did not get answer back from peer
 | 
						|
         * - last sent handshake req should have been replied (RTT*1.5 elapsed); and
 | 
						|
         * then (re-)send handshake request.
 | 
						|
         */
 | 
						|
        if (timebase > now) // too early
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "Legacy HSREQ: TOO EARLY, will still retry " << m_iSndHsRetryCnt << " times");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // If 0 timebase, it means that this is the initial sending with the very first
 | 
						|
    // payload packet sent. Send only if this is still set to maximum+1 value.
 | 
						|
    else if (m_iSndHsRetryCnt < SRT_MAX_HSRETRY + 1)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "Legacy HSREQ: INITIAL, REPEATED, so not to be done. Will repeat on sending " << m_iSndHsRetryCnt
 | 
						|
                  << " times");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "Legacy HSREQ: SENDING, will repeat " << m_iSndHsRetryCnt << " times if no response");
 | 
						|
    m_iSndHsRetryCnt--;
 | 
						|
    m_ullSndHsLastTime_us = now;
 | 
						|
    sendSrtMsg(SRT_CMD_HSREQ);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkSndTimers(Whether2RegenKm regen)
 | 
						|
{
 | 
						|
    if (m_SrtHsSide == HSD_INITIATOR)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase");
 | 
						|
        // Legacy method for HSREQ, only if initiator.
 | 
						|
        considerLegacySrtHandshake(m_ullSndHsLastTime_us + m_iRTT * 3 / 2);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "checkSndTimers: HS SIDE: " << (m_SrtHsSide == HSD_RESPONDER ? "RESPONDER" : "DRAW (IPE?)")
 | 
						|
                  << " - not considering legacy handshake");
 | 
						|
    }
 | 
						|
 | 
						|
    // This must be done always on sender, regardless of HS side.
 | 
						|
    // When regen == DONT_REGEN_KM, it's a handshake call, so do
 | 
						|
    // it only for initiator.
 | 
						|
    if (regen || m_SrtHsSide == HSD_INITIATOR)
 | 
						|
    {
 | 
						|
        // Don't call this function in "non-regen mode" (sending only),
 | 
						|
        // if this side is RESPONDER. This shall be called only with
 | 
						|
        // regeneration request, which is required by the sender.
 | 
						|
        if (m_pCryptoControl)
 | 
						|
            m_pCryptoControl->sendKeysToPeer(regen);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::addressAndSend(CPacket &pkt)
 | 
						|
{
 | 
						|
    pkt.m_iID        = m_PeerID;
 | 
						|
    pkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
 | 
						|
    m_pSndQueue->sendto(m_pPeerAddr, pkt);
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::close()
 | 
						|
{
 | 
						|
    // NOTE: this function is called from within the garbage collector thread.
 | 
						|
 | 
						|
    if (!m_bOpened)
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << " - closing socket:");
 | 
						|
 | 
						|
    if (m_Linger.l_onoff != 0)
 | 
						|
    {
 | 
						|
        uint64_t entertime = CTimer::getTime();
 | 
						|
 | 
						|
        HLOGC(mglog.Debug, log << CONID() << " ... (linger)");
 | 
						|
        while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) &&
 | 
						|
               (CTimer::getTime() - entertime < m_Linger.l_linger * uint64_t(1000000)))
 | 
						|
        {
 | 
						|
            // linger has been checked by previous close() call and has expired
 | 
						|
            if (m_ullLingerExpiration >= entertime)
 | 
						|
                break;
 | 
						|
 | 
						|
            if (!m_bSynSending)
 | 
						|
            {
 | 
						|
                // if this socket enables asynchronous sending, return immediately and let GC to close it later
 | 
						|
                if (m_ullLingerExpiration == 0)
 | 
						|
                    m_ullLingerExpiration = entertime + m_Linger.l_linger * uint64_t(1000000);
 | 
						|
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << "CUDT::close: linger-nonblocking, setting expire time T="
 | 
						|
                          << FormatTime(m_ullLingerExpiration));
 | 
						|
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
            timespec ts;
 | 
						|
            ts.tv_sec  = 0;
 | 
						|
            ts.tv_nsec = 1000000;
 | 
						|
            nanosleep(&ts, NULL);
 | 
						|
#else
 | 
						|
            Sleep(1);
 | 
						|
#endif
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // remove this socket from the snd queue
 | 
						|
    if (m_bConnected)
 | 
						|
        m_pSndQueue->m_pSndUList->remove(this);
 | 
						|
 | 
						|
    /*
 | 
						|
     * update_events below useless
 | 
						|
     * removing usock for EPolls right after (remove_usocks) clears it (in other HAI patch).
 | 
						|
     *
 | 
						|
     * What is in EPoll shall be the responsibility of the application, if it want local close event,
 | 
						|
     * it would remove the socket from the EPoll after close.
 | 
						|
     */
 | 
						|
    // trigger any pending IO events.
 | 
						|
    s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true);
 | 
						|
    // then remove itself from all epoll monitoring
 | 
						|
    try
 | 
						|
    {
 | 
						|
        for (set<int>::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++i)
 | 
						|
            s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID);
 | 
						|
    }
 | 
						|
    catch (...)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    // XXX What's this, could any of the above actions make it !m_bOpened?
 | 
						|
    if (!m_bOpened)
 | 
						|
    {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Inform the threads handler to stop.
 | 
						|
    m_bClosing = true;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "CLOSING STATE. Acquiring connection lock");
 | 
						|
 | 
						|
    CGuard cg(m_ConnectionLock);
 | 
						|
 | 
						|
    // Signal the sender and recver if they are waiting for data.
 | 
						|
    releaseSynch();
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "CLOSING, removing from listener/connector");
 | 
						|
 | 
						|
    if (m_bListening)
 | 
						|
    {
 | 
						|
        m_bListening = false;
 | 
						|
        m_pRcvQueue->removeListener(this);
 | 
						|
    }
 | 
						|
    else if (m_bConnecting)
 | 
						|
    {
 | 
						|
        m_pRcvQueue->removeConnector(m_SocketID);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bConnected)
 | 
						|
    {
 | 
						|
        if (!m_bShutdown)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer");
 | 
						|
            sendCtrl(UMSG_SHUTDOWN);
 | 
						|
        }
 | 
						|
 | 
						|
        m_pCryptoControl->close();
 | 
						|
 | 
						|
        // Store current connection information.
 | 
						|
        CInfoBlock ib;
 | 
						|
        ib.m_iIPversion = m_iIPversion;
 | 
						|
        CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP);
 | 
						|
        ib.m_iRTT       = m_iRTT;
 | 
						|
        ib.m_iBandwidth = m_iBandwidth;
 | 
						|
        m_pCache->update(&ib);
 | 
						|
 | 
						|
        m_bConnected = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bTsbPd && !pthread_equal(m_RcvTsbPdThread, pthread_t()))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "CLOSING, joining TSBPD thread...");
 | 
						|
        void *retval;
 | 
						|
        int ret SRT_ATR_UNUSED = pthread_join(m_RcvTsbPdThread, &retval);
 | 
						|
        HLOGC(mglog.Debug, log << "... " << (ret == 0 ? "SUCCEEDED" : "FAILED"));
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "CLOSING, joining send/receive threads");
 | 
						|
 | 
						|
    // waiting all send and recv calls to stop
 | 
						|
    CGuard sendguard(m_SendLock);
 | 
						|
    CGuard recvguard(m_RecvLock);
 | 
						|
 | 
						|
    // Locking m_RcvBufferLock to protect calling to m_pCryptoControl->decrypt(Ref(packet))
 | 
						|
    // from the processData(...) function while resetting Crypto Control.
 | 
						|
    CGuard::enterCS(m_RcvBufferLock);
 | 
						|
    m_pCryptoControl.reset();
 | 
						|
    CGuard::leaveCS(m_RcvBufferLock);
 | 
						|
 | 
						|
    m_lSrtVersion            = SRT_DEF_VERSION;
 | 
						|
    m_lPeerSrtVersion        = SRT_VERSION_UNK;
 | 
						|
    m_lMinimumPeerSrtVersion = SRT_VERSION_MAJ1;
 | 
						|
    m_ullRcvPeerStartTime    = 0;
 | 
						|
 | 
						|
    m_bOpened = false;
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 Old, mostly original UDT based version of CUDT::send.
 | 
						|
 Left for historical reasons.
 | 
						|
 | 
						|
int CUDT::send(const char* data, int len)
 | 
						|
{
 | 
						|
   // throw an exception if not connected
 | 
						|
   if (m_bBroken || m_bClosing)
 | 
						|
      throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
   else if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
      throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
   if (len <= 0)
 | 
						|
      return 0;
 | 
						|
 | 
						|
   // Check if the current congctl accepts the call with given parameters.
 | 
						|
   if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_SEND, data, len, -1, false))
 | 
						|
      throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
 | 
						|
   CGuard sendguard(m_SendLock);
 | 
						|
 | 
						|
   if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
   {
 | 
						|
      // delay the EXP timer to avoid mis-fired timeout
 | 
						|
      uint64_t currtime_tk;
 | 
						|
      CTimer::rdtsc(currtime_tk);
 | 
						|
      // (fix keepalive) m_ullLastRspTime_tk = currtime_tk;
 | 
						|
      m_ullLastRspAckTime_tk = currtime_tk;
 | 
						|
      m_iReXmitCount = 1;
 | 
						|
   }
 | 
						|
   if (sndBuffersLeft() <= 0)
 | 
						|
   {
 | 
						|
      if (!m_bSynSending)
 | 
						|
         throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0);
 | 
						|
      else
 | 
						|
      {
 | 
						|
          {
 | 
						|
              // wait here during a blocking sending
 | 
						|
              CGuard sendblock_lock(m_SendBlockLock);
 | 
						|
              if (m_iSndTimeOut < 0)
 | 
						|
              {
 | 
						|
                  while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth)
 | 
						|
                      pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock);
 | 
						|
              }
 | 
						|
              else
 | 
						|
              {
 | 
						|
                  uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * uint64_t(1000);
 | 
						|
                  timespec locktime;
 | 
						|
 | 
						|
                  locktime.tv_sec = exptime / 1000000;
 | 
						|
                  locktime.tv_nsec = (exptime % 1000000) * 1000;
 | 
						|
 | 
						|
                  while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth && (CTimer::getTime() < exptime))
 | 
						|
                      pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime);
 | 
						|
              }
 | 
						|
          }
 | 
						|
 | 
						|
         // check the connection status
 | 
						|
         if (m_bBroken || m_bClosing)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
         else if (!m_bConnected)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
         else if (!m_bPeerHealth)
 | 
						|
         {
 | 
						|
            m_bPeerHealth = true;
 | 
						|
            throw CUDTException(MJ_PEERERROR);
 | 
						|
         }
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   if (sndBuffersLeft() <= 0)
 | 
						|
   {
 | 
						|
      if (m_iSndTimeOut >= 0)
 | 
						|
         throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
 | 
						|
 | 
						|
      return 0;
 | 
						|
   }
 | 
						|
 | 
						|
   int size = min(len, sndBuffersLeft() * m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
   // record total time used for sending
 | 
						|
   if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
      m_llSndDurationCounter = CTimer::getTime();
 | 
						|
 | 
						|
   // insert the user buffer into the sending list
 | 
						|
   m_pSndBuffer->addBuffer(data, size); // inorder=false, ttl=-1
 | 
						|
 | 
						|
   // insert this socket to snd list if it is not on the list yet
 | 
						|
   m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
 | 
						|
 | 
						|
   if (sndBuffersLeft() <= 0)
 | 
						|
   {
 | 
						|
      // write is not available any more
 | 
						|
      s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false);
 | 
						|
   }
 | 
						|
 | 
						|
   return size;
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
int CUDT::receiveBuffer(char *data, int len)
 | 
						|
{
 | 
						|
    if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_RECV, data, len, -1, false))
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
 | 
						|
    CGuard recvguard(m_RecvLock);
 | 
						|
 | 
						|
    if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        if (m_bShutdown)
 | 
						|
        {
 | 
						|
            // For stream API, return 0 as a sign of EOF for transmission.
 | 
						|
            // That's a bit controversial because theoretically the
 | 
						|
            // UMSG_SHUTDOWN message may be lost as every UDP packet, although
 | 
						|
            // another theory states that this will never happen because this
 | 
						|
            // packet has a total size of 42 bytes and such packets are
 | 
						|
            // declared as never dropped - but still, this is UDP so there's no
 | 
						|
            // guarantee.
 | 
						|
 | 
						|
            // The most reliable way to inform the party that the transmission
 | 
						|
            // has ended would be to send a single empty packet (that is,
 | 
						|
            // a data packet that contains only an SRT header in the UDP
 | 
						|
            // payload), which is a normal data packet that can undergo
 | 
						|
            // normal sequence check and retransmission rules, so it's ensured
 | 
						|
            // that this packet will be received. Receiving such a packet should
 | 
						|
            // make this function return 0, potentially also without breaking
 | 
						|
            // the connection and potentially also with losing no ability to
 | 
						|
            // send some larger portion of data next time.
 | 
						|
            HLOGC(mglog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF");
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no")
 | 
						|
                  << " SHUTDOWN. Reporting as BROKEN.");
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        if (!m_bSynRecving)
 | 
						|
        {
 | 
						|
            throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            /* Kick TsbPd thread to schedule next wakeup (if running) */
 | 
						|
            if (m_iRcvTimeOut < 0)
 | 
						|
            {
 | 
						|
                while (stillConnected() && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
                {
 | 
						|
                    // Do not block forever, check connection status each 1 sec.
 | 
						|
                    CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, 1000000);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000;
 | 
						|
                while (stillConnected() && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
                {
 | 
						|
                    CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, m_iRcvTimeOut * 1000);
 | 
						|
                    if (CTimer::getTime() >= exptime)
 | 
						|
                        break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // throw an exception if not connected
 | 
						|
    if (!m_bConnected)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        // See at the beginning
 | 
						|
        if (!m_bMessageAPI && m_bShutdown)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF");
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << (m_bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no")
 | 
						|
                  << " SHUTDOWN. Reporting as BROKEN.");
 | 
						|
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    const int res = m_pRcvBuffer->readBuffer(data, len);
 | 
						|
 | 
						|
    /* Kick TsbPd thread to schedule next wakeup (if running) */
 | 
						|
    if (m_bTsbPd)
 | 
						|
    {
 | 
						|
        HLOGP(tslog.Debug, "Ping TSBPD thread to schedule wakeup");
 | 
						|
        pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        // read is not available any more
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
    }
 | 
						|
 | 
						|
    if ((res <= 0) && (m_iRcvTimeOut >= 0))
 | 
						|
        throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
 | 
						|
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkNeedDrop(ref_t<bool> bCongestion)
 | 
						|
{
 | 
						|
    if (!m_bPeerTLPktDrop)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (!m_bMessageAPI)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "The SRTO_TLPKTDROP flag can only be used with message API.");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    int bytes, timespan_ms;
 | 
						|
    // (returns buffer size in buffer units, ignored)
 | 
						|
    m_pSndBuffer->getCurrBufSize(Ref(bytes), Ref(timespan_ms));
 | 
						|
 | 
						|
    // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms)
 | 
						|
    // Minimum value must accomodate an I-Frame (~8 x average frame size)
 | 
						|
    // >>need picture rate or app to set min treshold
 | 
						|
    // >>using 1 sec for worse case 1 frame using all bit budget.
 | 
						|
    // picture rate would be useful in auto SRT setting for min latency
 | 
						|
    // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable
 | 
						|
    int threshold_ms = 0;
 | 
						|
    if (m_iOPT_SndDropDelay >= 0)
 | 
						|
    {
 | 
						|
        threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_iOPT_SndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) +
 | 
						|
                       (2 * COMM_SYN_INTERVAL_US / 1000);
 | 
						|
    }
 | 
						|
 | 
						|
    if (threshold_ms && timespan_ms > threshold_ms)
 | 
						|
    {
 | 
						|
        // protect packet retransmission
 | 
						|
        CGuard::enterCS(m_RecvAckLock);
 | 
						|
        int dbytes;
 | 
						|
        int dpkts = m_pSndBuffer->dropLateData(dbytes, CTimer::getTime() - (threshold_ms * 1000));
 | 
						|
        if (dpkts > 0)
 | 
						|
        {
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            m_stats.traceSndDrop += dpkts;
 | 
						|
            m_stats.sndDropTotal += dpkts;
 | 
						|
            m_stats.traceSndBytesDrop += dbytes;
 | 
						|
            m_stats.sndBytesDropTotal += dbytes;
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
            int32_t realack = m_iSndLastDataAck;
 | 
						|
#endif
 | 
						|
            int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts);
 | 
						|
 | 
						|
            m_iSndLastAck     = fakeack;
 | 
						|
            m_iSndLastDataAck = fakeack;
 | 
						|
 | 
						|
            int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck);
 | 
						|
            m_pSndLossList->remove(minlastack);
 | 
						|
            /* If we dropped packets not yet sent, advance current position */
 | 
						|
            // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1)
 | 
						|
            if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0)
 | 
						|
            {
 | 
						|
                m_iSndCurrSeqNo = minlastack;
 | 
						|
            }
 | 
						|
            LOGC(dlog.Error, log << "SND-DROPPED " << dpkts << " packets - lost delaying for " << timespan_ms << "ms");
 | 
						|
 | 
						|
            HLOGC(dlog.Debug,
 | 
						|
                  log << "drop,now " << CTimer::getTime() << "us," << realack << "-" << m_iSndCurrSeqNo << " seqs,"
 | 
						|
                      << dpkts << " pkts," << dbytes << " bytes," << timespan_ms << " ms");
 | 
						|
        }
 | 
						|
        *bCongestion = true;
 | 
						|
        CGuard::leaveCS(m_RecvAckLock);
 | 
						|
    }
 | 
						|
    else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "cong, NOW: " << CTimer::getTime() << "us, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms");
 | 
						|
 | 
						|
        *bCongestion = true;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, uint64_t srctime)
 | 
						|
{
 | 
						|
    SRT_MSGCTRL mctrl = srt_msgctrl_default;
 | 
						|
    mctrl.msgttl      = msttl;
 | 
						|
    mctrl.inorder     = inorder;
 | 
						|
    mctrl.srctime     = srctime;
 | 
						|
    return this->sendmsg2(data, len, Ref(mctrl));
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::sendmsg2(const char *data, int len, ref_t<SRT_MSGCTRL> r_mctrl)
 | 
						|
{
 | 
						|
    SRT_MSGCTRL &mctrl       = *r_mctrl;
 | 
						|
    bool         bCongestion = false;
 | 
						|
 | 
						|
    // throw an exception if not connected
 | 
						|
    if (m_bBroken || m_bClosing)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
    else if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if (len <= 0)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "INVALID: Data size for sending declared with length: " << len);
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    int  msttl   = mctrl.msgttl;
 | 
						|
    bool inorder = mctrl.inorder;
 | 
						|
 | 
						|
    // Sendmsg isn't restricted to the congctl type, however the congctl
 | 
						|
    // may want to have something to say here.
 | 
						|
    // NOTE: SrtCongestion is also allowed to throw CUDTException() by itself!
 | 
						|
    {
 | 
						|
        SrtCongestion::TransAPI api = SrtCongestion::STA_MESSAGE;
 | 
						|
        CodeMinor               mn  = MN_INVALMSGAPI;
 | 
						|
        if (!m_bMessageAPI)
 | 
						|
        {
 | 
						|
            api = SrtCongestion::STA_BUFFER;
 | 
						|
            mn  = MN_INVALBUFFERAPI;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!m_CongCtl->checkTransArgs(api, SrtCongestion::STAD_SEND, data, len, msttl, inorder))
 | 
						|
            throw CUDTException(MJ_NOTSUP, mn, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    // NOTE: the length restrictions differ in STREAM API and in MESSAGE API:
 | 
						|
 | 
						|
    // - STREAM API:
 | 
						|
    //   At least 1 byte free sending buffer space is needed
 | 
						|
    //   (in practice, one unit buffer of 1456 bytes).
 | 
						|
    //   This function will send as much as possible, and return
 | 
						|
    //   how much was actually sent.
 | 
						|
 | 
						|
    // - MESSAGE API:
 | 
						|
    //   At least so many bytes free in the sending buffer is needed,
 | 
						|
    //   as the length of the data, otherwise this function will block
 | 
						|
    //   or return MJ_AGAIN until this condition is satisfied. The EXACTLY
 | 
						|
    //   such number of data will be then written out, and this function
 | 
						|
    //   will effectively return either -1 (error) or the value of 'len'.
 | 
						|
    //   This call will be also rejected from upside when trying to send
 | 
						|
    //   out a message of a length that exceeds the total size of the sending
 | 
						|
    //   buffer (configurable by SRTO_SNDBUF).
 | 
						|
 | 
						|
    if (m_bMessageAPI && len > int(m_iSndBufSize * m_iMaxSRTPayloadSize))
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error,
 | 
						|
             log << "Message length (" << len << ") exceeds the size of sending buffer: "
 | 
						|
                 << (m_iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed.");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    /* XXX
 | 
						|
       This might be worth preserving for several occasions, but it
 | 
						|
       must be at least conditional because it breaks backward compat.
 | 
						|
    if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK())
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "Encryption is required, but the peer did not supply correct credentials. Sending
 | 
						|
    rejected."); throw CUDTException(MJ_SETUP, MN_SECURITY, 0);
 | 
						|
    }
 | 
						|
    */
 | 
						|
 | 
						|
    CGuard sendguard(m_SendLock);
 | 
						|
 | 
						|
    if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
    {
 | 
						|
        // delay the EXP timer to avoid mis-fired timeout
 | 
						|
        uint64_t currtime_tk;
 | 
						|
        CTimer::rdtsc(currtime_tk);
 | 
						|
 | 
						|
        CGuard ack_lock(m_RecvAckLock);
 | 
						|
        m_ullLastRspAckTime_tk = currtime_tk; // (fix keepalive)
 | 
						|
        m_iReXmitCount         = 1; // can be modified in checkRexmitTimer and processCtrlAck (receiver's thread)
 | 
						|
    }
 | 
						|
 | 
						|
    // checkNeedDrop(...) may lock m_RecvAckLock
 | 
						|
    // to modify m_pSndBuffer and m_pSndLossList
 | 
						|
    checkNeedDrop(Ref(bCongestion));
 | 
						|
 | 
						|
    int minlen = 1; // Minimum sender buffer space required for STREAM API
 | 
						|
    if (m_bMessageAPI)
 | 
						|
    {
 | 
						|
        // For MESSAGE API the minimum outgoing buffer space required is
 | 
						|
        // the size that can carry over the whole message as passed here.
 | 
						|
        minlen = (len + m_iMaxSRTPayloadSize - 1) / m_iMaxSRTPayloadSize;
 | 
						|
    }
 | 
						|
 | 
						|
    if (sndBuffersLeft() < minlen)
 | 
						|
    {
 | 
						|
        //>>We should not get here if SRT_ENABLE_TLPKTDROP
 | 
						|
        // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop.
 | 
						|
        if (!m_bSynSending)
 | 
						|
            throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0);
 | 
						|
 | 
						|
        {
 | 
						|
            // wait here during a blocking sending
 | 
						|
            CGuard sendblock_lock(m_SendBlockLock);
 | 
						|
 | 
						|
            if (m_iSndTimeOut < 0)
 | 
						|
            {
 | 
						|
                while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth)
 | 
						|
                    pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                const uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * uint64_t(1000);
 | 
						|
 | 
						|
                while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth && exptime > CTimer::getTime())
 | 
						|
                    CTimer::condTimedWaitUS(&m_SendBlockCond, &m_SendBlockLock, m_iSndTimeOut * uint64_t(1000));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // check the connection status
 | 
						|
        if (m_bBroken || m_bClosing)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
        else if (!m_bConnected)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
        else if (!m_bPeerHealth)
 | 
						|
        {
 | 
						|
            m_bPeerHealth = true;
 | 
						|
            throw CUDTException(MJ_PEERERROR);
 | 
						|
        }
 | 
						|
 | 
						|
        /*
 | 
						|
         * The code below is to return ETIMEOUT when blocking mode could not get free buffer in time.
 | 
						|
         * If no free buffer available in non-blocking mode, we alredy returned. If buffer availaible,
 | 
						|
         * we test twice if this code is outside the else section.
 | 
						|
         * This fix move it in the else (blocking-mode) section
 | 
						|
         */
 | 
						|
        if (sndBuffersLeft() < minlen)
 | 
						|
        {
 | 
						|
            if (m_iSndTimeOut >= 0)
 | 
						|
                throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
 | 
						|
 | 
						|
            // XXX This looks very weird here, however most likely
 | 
						|
            // this will happen only in the following case, when
 | 
						|
            // the above loop has been interrupted, which happens when:
 | 
						|
            // 1. The buffers left gets enough for minlen - but this is excluded
 | 
						|
            //    in the first condition here.
 | 
						|
            // 2. In the case of sending timeout, the above loop was interrupted
 | 
						|
            //    due to reaching timeout, but this is excluded by the second
 | 
						|
            //    condition here
 | 
						|
            // 3. The 'stillConnected()' or m_bPeerHealth condition is false, of which:
 | 
						|
            //    - broken/closing status is checked and responded with CONNECTION/CONNLOST
 | 
						|
            //    - not connected status is checked and responded with CONNECTION/NOCONN
 | 
						|
            //    - m_bPeerHealth condition is checked and responded with PEERERROR
 | 
						|
            //
 | 
						|
            // ERGO: never happens?
 | 
						|
            LOGC(mglog.Fatal,
 | 
						|
                 log << "IPE: sendmsg: the loop exited, while not enough size, still connected, peer healthy. "
 | 
						|
                        "Impossible.");
 | 
						|
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // If the sender's buffer is empty,
 | 
						|
    // record total time used for sending
 | 
						|
    if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
    {
 | 
						|
        CGuard::enterCS(m_StatsLock);
 | 
						|
        m_stats.sndDurationCounter = CTimer::getTime();
 | 
						|
        CGuard::leaveCS(m_StatsLock);
 | 
						|
    }
 | 
						|
 | 
						|
    int size = len;
 | 
						|
    if (!m_bMessageAPI)
 | 
						|
    {
 | 
						|
        // For STREAM API it's allowed to send less bytes than the given buffer.
 | 
						|
        // Just return how many bytes were actually scheduled for writing.
 | 
						|
        // XXX May be reasonable to add a flag that requires that the function
 | 
						|
        // not return until the buffer is sent completely.
 | 
						|
        size = min(len, sndBuffersLeft() * m_iMaxSRTPayloadSize);
 | 
						|
    }
 | 
						|
 | 
						|
    {
 | 
						|
        CGuard recvAckLock(m_RecvAckLock);
 | 
						|
        // insert the user buffer into the sending list
 | 
						|
        // This should be protected by a mutex. m_SendLock does this.
 | 
						|
        m_pSndBuffer->addBuffer(data, size, mctrl.msgttl, mctrl.inorder, mctrl.srctime, Ref(mctrl.msgno));
 | 
						|
        HLOGC(dlog.Debug, log << CONID() << "sock:SENDING srctime: " << mctrl.srctime << "us DATA SIZE: " << size);
 | 
						|
 | 
						|
        if (sndBuffersLeft() < 1) // XXX Not sure if it should test if any space in the buffer, or as requried.
 | 
						|
        {
 | 
						|
            // write is not available any more
 | 
						|
            s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // insert this socket to the snd list if it is not on the list yet
 | 
						|
    // m_pSndUList->pop may lock CSndUList::m_ListLock and then m_RecvAckLock
 | 
						|
    m_pSndQueue->m_pSndUList->update(this, CSndUList::rescheduleIf(bCongestion));
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_ECN
 | 
						|
    if (bCongestion)
 | 
						|
        throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0);
 | 
						|
#endif /* SRT_ENABLE_ECN */
 | 
						|
    return size;
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::recv(char *data, int len)
 | 
						|
{
 | 
						|
    if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if (len <= 0)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recv.");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bMessageAPI)
 | 
						|
    {
 | 
						|
        SRT_MSGCTRL mctrl = srt_msgctrl_default;
 | 
						|
        return receiveMessage(data, len, Ref(mctrl));
 | 
						|
    }
 | 
						|
 | 
						|
    return receiveBuffer(data, len);
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::recvmsg(char *data, int len, uint64_t &srctime)
 | 
						|
{
 | 
						|
    if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if (len <= 0)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recvmsg.");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bMessageAPI)
 | 
						|
    {
 | 
						|
        SRT_MSGCTRL mctrl = srt_msgctrl_default;
 | 
						|
        int         ret   = receiveMessage(data, len, Ref(mctrl));
 | 
						|
        srctime           = mctrl.srctime;
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    return receiveBuffer(data, len);
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::recvmsg2(char *data, int len, ref_t<SRT_MSGCTRL> mctrl)
 | 
						|
{
 | 
						|
    if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if (len <= 0)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "Length of '" << len << "' supplied to srt_recvmsg.");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_bMessageAPI)
 | 
						|
        return receiveMessage(data, len, mctrl);
 | 
						|
 | 
						|
    return receiveBuffer(data, len);
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::receiveMessage(char *data, int len, ref_t<SRT_MSGCTRL> r_mctrl)
 | 
						|
{
 | 
						|
    SRT_MSGCTRL &mctrl = *r_mctrl;
 | 
						|
    // Recvmsg isn't restricted to the congctl type, it's the most
 | 
						|
    // basic method of passing the data. You can retrieve data as
 | 
						|
    // they come in, however you need to match the size of the buffer.
 | 
						|
    if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_MESSAGE, SrtCongestion::STAD_RECV, data, len, -1, false))
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI, 0);
 | 
						|
 | 
						|
    CGuard recvguard(m_RecvLock);
 | 
						|
 | 
						|
    /* XXX DEBUG STUFF - enable when required
 | 
						|
       char charbool[2] = {'0', '1'};
 | 
						|
       char ptrn [] = "RECVMSG/BEGIN BROKEN 1 CONN 1 CLOSING 1 SYNCR 1 NMSG                                ";
 | 
						|
       int pos [] = {21, 28, 38, 46, 53};
 | 
						|
       ptrn[pos[0]] = charbool[m_bBroken];
 | 
						|
       ptrn[pos[1]] = charbool[m_bConnected];
 | 
						|
       ptrn[pos[2]] = charbool[m_bClosing];
 | 
						|
       ptrn[pos[3]] = charbool[m_bSynRecving];
 | 
						|
       int wrtlen = sprintf(ptrn + pos[4], "%d", m_pRcvBuffer->getRcvMsgNum());
 | 
						|
       strcpy(ptrn + pos[4] + wrtlen, "\n");
 | 
						|
       fputs(ptrn, stderr);
 | 
						|
    // */
 | 
						|
 | 
						|
    if (m_bBroken || m_bClosing)
 | 
						|
    {
 | 
						|
        int res       = m_pRcvBuffer->readMsg(data, len);
 | 
						|
        mctrl.srctime = 0;
 | 
						|
 | 
						|
        /* Kick TsbPd thread to schedule next wakeup (if running) */
 | 
						|
        if (m_bTsbPd)
 | 
						|
            pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
 | 
						|
        if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
        {
 | 
						|
            // read is not available any more
 | 
						|
            s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
        }
 | 
						|
 | 
						|
        if (res == 0)
 | 
						|
        {
 | 
						|
            if (!m_bMessageAPI && m_bShutdown)
 | 
						|
                return 0;
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
            return res;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_bSynRecving)
 | 
						|
    {
 | 
						|
 | 
						|
        int res = m_pRcvBuffer->readMsg(data, len, r_mctrl);
 | 
						|
        if (res == 0)
 | 
						|
        {
 | 
						|
            // read is not available any more
 | 
						|
 | 
						|
            // Kick TsbPd thread to schedule next wakeup (if running)
 | 
						|
            if (m_bTsbPd)
 | 
						|
                pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
 | 
						|
            // Shut up EPoll if no more messages in non-blocking mode
 | 
						|
            s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
            throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
            {
 | 
						|
                // Kick TsbPd thread to schedule next wakeup (if running)
 | 
						|
                if (m_bTsbPd)
 | 
						|
                    pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
 | 
						|
                // Shut up EPoll if no more messages in non-blocking mode
 | 
						|
                s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
 | 
						|
                // After signaling the tsbpd for ready data, report the bandwidth.
 | 
						|
                double bw SRT_ATR_UNUSED = Bps2Mbps(m_iBandwidth * m_iMaxSRTPayloadSize);
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth
 | 
						|
                          << " buffers per second)");
 | 
						|
            }
 | 
						|
            return res;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    int  res     = 0;
 | 
						|
    bool timeout = false;
 | 
						|
    // Do not block forever, check connection status each 1 sec.
 | 
						|
    uint64_t recvtmo = m_iRcvTimeOut < 0 ? 1000 : m_iRcvTimeOut;
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
        if (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady()))
 | 
						|
        {
 | 
						|
            /* Kick TsbPd thread to schedule next wakeup (if running) */
 | 
						|
            if (m_bTsbPd)
 | 
						|
            {
 | 
						|
                HLOGP(tslog.Debug, "recvmsg: KICK tsbpd()");
 | 
						|
                pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
            }
 | 
						|
 | 
						|
            do
 | 
						|
            {
 | 
						|
                if (CTimer::condTimedWaitUS(&m_RecvDataCond, &m_RecvLock, recvtmo * 1000) == ETIMEDOUT)
 | 
						|
                {
 | 
						|
                    if (!(m_iRcvTimeOut < 0))
 | 
						|
                        timeout = true;
 | 
						|
                    HLOGP(tslog.Debug, "recvmsg: DATA COND: EXPIRED -- trying to get data anyway");
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    HLOGP(tslog.Debug, "recvmsg: DATA COND: KICKED.");
 | 
						|
                }
 | 
						|
            } while (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady()));
 | 
						|
        }
 | 
						|
 | 
						|
        /* XXX DEBUG STUFF - enable when required
 | 
						|
        LOGC(dlog.Debug, "RECVMSG/GO-ON BROKEN " << m_bBroken << " CONN " << m_bConnected
 | 
						|
                << " CLOSING " << m_bClosing << " TMOUT " << timeout
 | 
						|
                << " NMSG " << m_pRcvBuffer->getRcvMsgNum());
 | 
						|
                */
 | 
						|
 | 
						|
        res = m_pRcvBuffer->readMsg(data, len, r_mctrl);
 | 
						|
 | 
						|
        if (m_bBroken || m_bClosing)
 | 
						|
        {
 | 
						|
            if (!m_bMessageAPI && m_bShutdown)
 | 
						|
                return 0;
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
        }
 | 
						|
        else if (!m_bConnected)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
    } while ((res == 0) && !timeout);
 | 
						|
 | 
						|
    if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        // Falling here means usually that res == 0 && timeout == true.
 | 
						|
        // res == 0 would repeat the above loop, unless there was also a timeout.
 | 
						|
        // timeout has interrupted the above loop, but with res > 0 this condition
 | 
						|
        // wouldn't be satisfied.
 | 
						|
 | 
						|
        // read is not available any more
 | 
						|
 | 
						|
        // Kick TsbPd thread to schedule next wakeup (if running)
 | 
						|
        if (m_bTsbPd)
 | 
						|
        {
 | 
						|
            HLOGP(tslog.Debug, "recvmsg: KICK tsbpd() (buffer empty)");
 | 
						|
            pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
        }
 | 
						|
 | 
						|
        // Shut up EPoll if no more messages in non-blocking mode
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
    }
 | 
						|
 | 
						|
    // Unblock when required
 | 
						|
    // LOGC(tslog.Debug, "RECVMSG/EXIT RES " << res << " RCVTIMEOUT");
 | 
						|
 | 
						|
    if ((res <= 0) && (m_iRcvTimeOut >= 0))
 | 
						|
        throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
 | 
						|
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block)
 | 
						|
{
 | 
						|
    if (m_bBroken || m_bClosing)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
    else if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
 | 
						|
    if (size <= 0 && size != -1)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_SEND, 0, size, -1, false))
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
 | 
						|
    if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK())
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error,
 | 
						|
             log << "Encryption is required, but the peer did not supply correct credentials. Sending rejected.");
 | 
						|
        throw CUDTException(MJ_SETUP, MN_SECURITY, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    CGuard sendguard(m_SendLock);
 | 
						|
 | 
						|
    if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
    {
 | 
						|
        // delay the EXP timer to avoid mis-fired timeout
 | 
						|
        uint64_t currtime_tk;
 | 
						|
        CTimer::rdtsc(currtime_tk);
 | 
						|
        // (fix keepalive) m_ullLastRspTime_tk = currtime_tk;
 | 
						|
        m_ullLastRspAckTime_tk = currtime_tk;
 | 
						|
        m_iReXmitCount         = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    // positioning...
 | 
						|
    try
 | 
						|
    {
 | 
						|
        if (size == -1)
 | 
						|
        {
 | 
						|
            ifs.seekg(0, std::ios::end);
 | 
						|
            size = ifs.tellg();
 | 
						|
            if (offset > size)
 | 
						|
                throw 0; // let it be caught below
 | 
						|
        }
 | 
						|
 | 
						|
        // This will also set the position back to the beginning
 | 
						|
        // in case when it was moved to the end for measuring the size.
 | 
						|
        // This will also fail if the offset exceeds size, so measuring
 | 
						|
        // the size can be skipped if not needed.
 | 
						|
        ifs.seekg((streamoff)offset);
 | 
						|
        if (!ifs.good())
 | 
						|
            throw 0;
 | 
						|
    }
 | 
						|
    catch (...)
 | 
						|
    {
 | 
						|
        // XXX It would be nice to note that this is reported
 | 
						|
        // by exception only if explicitly requested by setting
 | 
						|
        // the exception flags in the stream. Here it's fixed so
 | 
						|
        // that when this isn't set, the exception is "thrown manually".
 | 
						|
        throw CUDTException(MJ_FILESYSTEM, MN_SEEKGFAIL);
 | 
						|
    }
 | 
						|
 | 
						|
    int64_t tosend = size;
 | 
						|
    int     unitsize;
 | 
						|
 | 
						|
    // sending block by block
 | 
						|
    while (tosend > 0)
 | 
						|
    {
 | 
						|
        if (ifs.fail())
 | 
						|
            throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL);
 | 
						|
 | 
						|
        if (ifs.eof())
 | 
						|
            break;
 | 
						|
 | 
						|
        unitsize = int((tosend >= block) ? block : tosend);
 | 
						|
 | 
						|
        {
 | 
						|
            CGuard lk(m_SendBlockLock);
 | 
						|
 | 
						|
            while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth)
 | 
						|
                pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock);
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_bBroken || m_bClosing)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
        else if (!m_bConnected)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
        else if (!m_bPeerHealth)
 | 
						|
        {
 | 
						|
            // reset peer health status, once this error returns, the app should handle the situation at the peer side
 | 
						|
            m_bPeerHealth = true;
 | 
						|
            throw CUDTException(MJ_PEERERROR);
 | 
						|
        }
 | 
						|
 | 
						|
        // record total time used for sending
 | 
						|
        if (m_pSndBuffer->getCurrBufSize() == 0)
 | 
						|
        {
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            m_stats.sndDurationCounter = CTimer::getTime();
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
        }
 | 
						|
 | 
						|
        {
 | 
						|
            CGuard        recvAckLock(m_RecvAckLock);
 | 
						|
            const int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize);
 | 
						|
 | 
						|
            if (sentsize > 0)
 | 
						|
            {
 | 
						|
                tosend -= sentsize;
 | 
						|
                offset += sentsize;
 | 
						|
            }
 | 
						|
 | 
						|
            if (sndBuffersLeft() <= 0)
 | 
						|
            {
 | 
						|
                // write is not available any more
 | 
						|
                s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // insert this socket to snd list if it is not on the list yet
 | 
						|
        m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
 | 
						|
    }
 | 
						|
 | 
						|
    return size - tosend;
 | 
						|
}
 | 
						|
 | 
						|
int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block)
 | 
						|
{
 | 
						|
    if (!m_bConnected || !m_CongCtl.ready())
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
    else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        if (!m_bMessageAPI && m_bShutdown)
 | 
						|
            return 0;
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (size <= 0)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_RECV, 0, size, -1, false))
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
 | 
						|
    if (m_bTsbPd)
 | 
						|
    {
 | 
						|
        LOGC(dlog.Error, log << "Reading from file is incompatible with TSBPD mode and would cause a deadlock\n");
 | 
						|
        throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    CGuard recvguard(m_RecvLock);
 | 
						|
 | 
						|
    // Well, actually as this works over a FILE (fstream), not just a stream,
 | 
						|
    // the size can be measured anyway and predicted if setting the offset might
 | 
						|
    // have a chance to work or not.
 | 
						|
 | 
						|
    // positioning...
 | 
						|
    try
 | 
						|
    {
 | 
						|
        if (offset > 0)
 | 
						|
        {
 | 
						|
            // Don't do anything around here if the offset == 0, as this
 | 
						|
            // is the default offset after opening. Whether this operation
 | 
						|
            // is performed correctly, it highly depends on how the file
 | 
						|
            // has been open. For example, if you want to overwrite parts
 | 
						|
            // of an existing file, the file must exist, and the ios::trunc
 | 
						|
            // flag must not be set. If the file is open for only ios::out,
 | 
						|
            // then the file will be truncated since the offset position on
 | 
						|
            // at the time when first written; if ios::in|ios::out, then
 | 
						|
            // it won't be truncated, just overwritten.
 | 
						|
 | 
						|
            // What is required here is that if offset is 0, don't try to
 | 
						|
            // change the offset because this might be impossible with
 | 
						|
            // the current flag set anyway.
 | 
						|
 | 
						|
            // Also check the status and CAUSE exception manually because
 | 
						|
            // you don't know, as well, whether the user has set exception
 | 
						|
            // flags.
 | 
						|
 | 
						|
            ofs.seekp((streamoff)offset);
 | 
						|
            if (!ofs.good())
 | 
						|
                throw 0; // just to get caught :)
 | 
						|
        }
 | 
						|
    }
 | 
						|
    catch (...)
 | 
						|
    {
 | 
						|
        // XXX It would be nice to note that this is reported
 | 
						|
        // by exception only if explicitly requested by setting
 | 
						|
        // the exception flags in the stream. For a case, when it's not,
 | 
						|
        // an additional explicit throwing happens when failbit is set.
 | 
						|
        throw CUDTException(MJ_FILESYSTEM, MN_SEEKPFAIL);
 | 
						|
    }
 | 
						|
 | 
						|
    int64_t torecv   = size;
 | 
						|
    int     unitsize = block;
 | 
						|
    int     recvsize;
 | 
						|
 | 
						|
    // receiving... "recvfile" is always blocking
 | 
						|
    while (torecv > 0)
 | 
						|
    {
 | 
						|
        if (ofs.fail())
 | 
						|
        {
 | 
						|
            // send the sender a signal so it will not be blocked forever
 | 
						|
            int32_t err_code = CUDTException::EFILE;
 | 
						|
            sendCtrl(UMSG_PEERERROR, &err_code);
 | 
						|
 | 
						|
            throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL);
 | 
						|
        }
 | 
						|
 | 
						|
        pthread_mutex_lock(&m_RecvDataLock);
 | 
						|
        while (stillConnected() && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
            pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock);
 | 
						|
        pthread_mutex_unlock(&m_RecvDataLock);
 | 
						|
 | 
						|
        if (!m_bConnected)
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
        else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady())
 | 
						|
        {
 | 
						|
 | 
						|
            if (!m_bMessageAPI && m_bShutdown)
 | 
						|
                return 0;
 | 
						|
            throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
        }
 | 
						|
 | 
						|
        unitsize = int((torecv == -1 || torecv >= block) ? block : torecv);
 | 
						|
        recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize);
 | 
						|
 | 
						|
        if (recvsize > 0)
 | 
						|
        {
 | 
						|
            torecv -= recvsize;
 | 
						|
            offset += recvsize;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        // read is not available any more
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false);
 | 
						|
    }
 | 
						|
 | 
						|
    return size - torecv;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous)
 | 
						|
{
 | 
						|
    if (!m_bConnected)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
 | 
						|
    if (m_bBroken || m_bClosing)
 | 
						|
        throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
 | 
						|
 | 
						|
    CGuard statsguard(m_StatsLock);
 | 
						|
 | 
						|
    uint64_t currtime = CTimer::getTime();
 | 
						|
    perf->msTimeStamp = (currtime - m_stats.startTime) / 1000;
 | 
						|
 | 
						|
    perf->pktSent              = m_stats.traceSent;
 | 
						|
    perf->pktRecv              = m_stats.traceRecv;
 | 
						|
    perf->pktSndLoss           = m_stats.traceSndLoss;
 | 
						|
    perf->pktRcvLoss           = m_stats.traceRcvLoss;
 | 
						|
    perf->pktRetrans           = m_stats.traceRetrans;
 | 
						|
    perf->pktRcvRetrans        = m_stats.traceRcvRetrans;
 | 
						|
    perf->pktSentACK           = m_stats.sentACK;
 | 
						|
    perf->pktRecvACK           = m_stats.recvACK;
 | 
						|
    perf->pktSentNAK           = m_stats.sentNAK;
 | 
						|
    perf->pktRecvNAK           = m_stats.recvNAK;
 | 
						|
    perf->usSndDuration        = m_stats.sndDuration;
 | 
						|
    perf->pktReorderDistance   = m_stats.traceReorderDistance;
 | 
						|
    perf->pktReorderTolerance  = m_iReorderTolerance;
 | 
						|
    perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime;
 | 
						|
    perf->pktRcvBelated        = m_stats.traceRcvBelated;
 | 
						|
 | 
						|
    perf->pktSndFilterExtra  = m_stats.sndFilterExtra;
 | 
						|
    perf->pktRcvFilterExtra  = m_stats.rcvFilterExtra;
 | 
						|
    perf->pktRcvFilterSupply = m_stats.rcvFilterSupply;
 | 
						|
    perf->pktRcvFilterLoss   = m_stats.rcvFilterLoss;
 | 
						|
 | 
						|
    /* perf byte counters include all headers (SRT+UDP+IP) */
 | 
						|
    const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE;
 | 
						|
    perf->byteSent       = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize);
 | 
						|
    perf->byteRecv       = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize);
 | 
						|
    perf->byteRetrans    = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize);
 | 
						|
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
 | 
						|
    perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize);
 | 
						|
#endif
 | 
						|
 | 
						|
    perf->pktSndDrop  = m_stats.traceSndDrop;
 | 
						|
    perf->pktRcvDrop  = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt;
 | 
						|
    perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize);
 | 
						|
    perf->byteRcvDrop =
 | 
						|
        m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt;
 | 
						|
    perf->pktRcvUndecrypt  = m_stats.traceRcvUndecrypt;
 | 
						|
    perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt;
 | 
						|
 | 
						|
    perf->pktSentTotal       = m_stats.sentTotal;
 | 
						|
    perf->pktRecvTotal       = m_stats.recvTotal;
 | 
						|
    perf->pktSndLossTotal    = m_stats.sndLossTotal;
 | 
						|
    perf->pktRcvLossTotal    = m_stats.rcvLossTotal;
 | 
						|
    perf->pktRetransTotal    = m_stats.retransTotal;
 | 
						|
    perf->pktSentACKTotal    = m_stats.sentACKTotal;
 | 
						|
    perf->pktRecvACKTotal    = m_stats.recvACKTotal;
 | 
						|
    perf->pktSentNAKTotal    = m_stats.sentNAKTotal;
 | 
						|
    perf->pktRecvNAKTotal    = m_stats.recvNAKTotal;
 | 
						|
    perf->usSndDurationTotal = m_stats.m_sndDurationTotal;
 | 
						|
 | 
						|
    perf->byteSentTotal           = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize);
 | 
						|
    perf->byteRecvTotal           = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize);
 | 
						|
    perf->byteRetransTotal        = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize);
 | 
						|
    perf->pktSndFilterExtraTotal  = m_stats.sndFilterExtraTotal;
 | 
						|
    perf->pktRcvFilterExtraTotal  = m_stats.rcvFilterExtraTotal;
 | 
						|
    perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal;
 | 
						|
    perf->pktRcvFilterLossTotal   = m_stats.rcvFilterLossTotal;
 | 
						|
 | 
						|
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
 | 
						|
    perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize);
 | 
						|
#endif
 | 
						|
    perf->pktSndDropTotal  = m_stats.sndDropTotal;
 | 
						|
    perf->pktRcvDropTotal  = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal;
 | 
						|
    perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize);
 | 
						|
    perf->byteRcvDropTotal =
 | 
						|
        m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal;
 | 
						|
    perf->pktRcvUndecryptTotal  = m_stats.m_rcvUndecryptTotal;
 | 
						|
    perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal;
 | 
						|
    //<
 | 
						|
 | 
						|
    double interval = double(currtime - m_stats.lastSampleTime);
 | 
						|
 | 
						|
    //>mod
 | 
						|
    perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval;
 | 
						|
    perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval;
 | 
						|
    //<
 | 
						|
 | 
						|
    perf->usPktSndPeriod      = m_ullInterval_tk / double(m_ullCPUFrequency);
 | 
						|
    perf->pktFlowWindow       = m_iFlowWindowSize;
 | 
						|
    perf->pktCongestionWindow = (int)m_dCongestionWindow;
 | 
						|
    perf->pktFlightSize       = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) - 1;
 | 
						|
    perf->msRTT               = (double)m_iRTT / 1000.0;
 | 
						|
    //>new
 | 
						|
    perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0;
 | 
						|
    perf->msRcvTsbPdDelay = m_bTsbPd ? m_iTsbPdDelay_ms : 0;
 | 
						|
    perf->byteMSS         = m_iMSS;
 | 
						|
 | 
						|
    perf->mbpsMaxBW = m_llMaxBW > 0 ? Bps2Mbps(m_llMaxBW) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0;
 | 
						|
 | 
						|
    //<
 | 
						|
    uint32_t availbw = (uint64_t)(m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth);
 | 
						|
 | 
						|
    perf->mbpsBandwidth = Bps2Mbps(availbw * (m_iMaxSRTPayloadSize + pktHdrSize));
 | 
						|
 | 
						|
    if (pthread_mutex_trylock(&m_ConnectionLock) == 0)
 | 
						|
    {
 | 
						|
        if (m_pSndBuffer)
 | 
						|
        {
 | 
						|
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
 | 
						|
            if (instantaneous)
 | 
						|
            {
 | 
						|
                /* Get instant SndBuf instead of moving average for application-based Algorithm
 | 
						|
                   (such as NAE) in need of fast reaction to network condition changes. */
 | 
						|
                perf->pktSndBuf = m_pSndBuffer->getCurrBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf));
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                perf->pktSndBuf = m_pSndBuffer->getAvgBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf));
 | 
						|
            }
 | 
						|
#else
 | 
						|
            perf->pktSndBuf = m_pSndBuffer->getCurrBufSize(Ref(perf->byteSndBuf), Ref(perf->msSndBuf));
 | 
						|
#endif
 | 
						|
            perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize);
 | 
						|
            //<
 | 
						|
            perf->byteAvailSndBuf = (m_iSndBufSize - perf->pktSndBuf) * m_iMSS;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            perf->byteAvailSndBuf = 0;
 | 
						|
            // new>
 | 
						|
            perf->pktSndBuf  = 0;
 | 
						|
            perf->byteSndBuf = 0;
 | 
						|
            perf->msSndBuf   = 0;
 | 
						|
            //<
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_pRcvBuffer)
 | 
						|
        {
 | 
						|
            perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_iMSS;
 | 
						|
            // new>
 | 
						|
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
 | 
						|
            if (instantaneous) // no need for historical API for Rcv side
 | 
						|
            {
 | 
						|
                perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                perf->pktRcvBuf = m_pRcvBuffer->getRcvAvgDataSize(perf->byteRcvBuf, perf->msRcvBuf);
 | 
						|
            }
 | 
						|
#else
 | 
						|
            perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf);
 | 
						|
#endif
 | 
						|
            //<
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            perf->byteAvailRcvBuf = 0;
 | 
						|
            // new>
 | 
						|
            perf->pktRcvBuf  = 0;
 | 
						|
            perf->byteRcvBuf = 0;
 | 
						|
            perf->msRcvBuf   = 0;
 | 
						|
            //<
 | 
						|
        }
 | 
						|
 | 
						|
        pthread_mutex_unlock(&m_ConnectionLock);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        perf->byteAvailSndBuf = 0;
 | 
						|
        perf->byteAvailRcvBuf = 0;
 | 
						|
        // new>
 | 
						|
        perf->pktSndBuf  = 0;
 | 
						|
        perf->byteSndBuf = 0;
 | 
						|
        perf->msSndBuf   = 0;
 | 
						|
 | 
						|
        perf->byteRcvBuf = 0;
 | 
						|
        perf->msRcvBuf   = 0;
 | 
						|
        //<
 | 
						|
    }
 | 
						|
 | 
						|
    if (clear)
 | 
						|
    {
 | 
						|
        m_stats.traceSndDrop           = 0;
 | 
						|
        m_stats.traceRcvDrop           = 0;
 | 
						|
        m_stats.traceSndBytesDrop      = 0;
 | 
						|
        m_stats.traceRcvBytesDrop      = 0;
 | 
						|
        m_stats.traceRcvUndecrypt      = 0;
 | 
						|
        m_stats.traceRcvBytesUndecrypt = 0;
 | 
						|
        // new>
 | 
						|
        m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0;
 | 
						|
        //<
 | 
						|
        m_stats.traceSent = m_stats.traceRecv = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans =
 | 
						|
            m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0;
 | 
						|
        m_stats.sndDuration                                                       = 0;
 | 
						|
        m_stats.traceRcvRetrans                                                   = 0;
 | 
						|
        m_stats.traceRcvBelated                                                   = 0;
 | 
						|
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
 | 
						|
        m_stats.traceRcvBytesLoss = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
        m_stats.sndFilterExtra = 0;
 | 
						|
        m_stats.rcvFilterExtra = 0;
 | 
						|
 | 
						|
        m_stats.rcvFilterSupply = 0;
 | 
						|
        m_stats.rcvFilterLoss   = 0;
 | 
						|
 | 
						|
        m_stats.lastSampleTime = currtime;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::updateCC(ETransmissionEvent evt, EventVariant arg)
 | 
						|
{
 | 
						|
    // Special things that must be done HERE, not in SrtCongestion,
 | 
						|
    // because it involves the input buffer in CUDT. It would be
 | 
						|
    // slightly dangerous to give SrtCongestion access to it.
 | 
						|
 | 
						|
    // According to the rules, the congctl should be ready at the same
 | 
						|
    // time when the sending buffer. For sanity check, check both first.
 | 
						|
    if (!m_CongCtl.ready() || !m_pSndBuffer)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << "updateCC: CAN'T DO UPDATE - congctl " << (m_CongCtl.ready() ? "ready" : "NOT READY")
 | 
						|
                 << "; sending buffer " << (m_pSndBuffer ? "NOT CREATED" : "created"));
 | 
						|
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "updateCC: EVENT:" << TransmissionEventStr(evt));
 | 
						|
 | 
						|
    if (evt == TEV_INIT)
 | 
						|
    {
 | 
						|
        // only_input uses:
 | 
						|
        // 0: in the beginning and when SRTO_MAXBW was changed
 | 
						|
        // 1: SRTO_INPUTBW was changed
 | 
						|
        // 2: SRTO_OHEADBW was changed
 | 
						|
        EInitEvent only_input = arg.get<EventVariant::INIT>();
 | 
						|
        // false = TEV_INIT_RESET: in the beginning, or when MAXBW was changed.
 | 
						|
 | 
						|
        if (only_input && m_llMaxBW)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "updateCC/TEV_INIT: non-RESET stage and m_llMaxBW already set to " << m_llMaxBW);
 | 
						|
            // Don't change
 | 
						|
        }
 | 
						|
        else // either m_llMaxBW == 0 or only_input == TEV_INIT_RESET
 | 
						|
        {
 | 
						|
            // Use the values:
 | 
						|
            // - if SRTO_MAXBW is >0, use it.
 | 
						|
            // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW
 | 
						|
            // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling
 | 
						|
            // Bytes/s
 | 
						|
            int bw = m_llMaxBW != 0 ? m_llMaxBW :                       // When used SRTO_MAXBW
 | 
						|
                         m_llInputBW != 0 ? withOverhead(m_llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW
 | 
						|
                             0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling
 | 
						|
 | 
						|
            // Note: setting bw == 0 uses BW_INFINITE value in LiveCC
 | 
						|
            m_CongCtl->updateBandwidth(m_llMaxBW, bw);
 | 
						|
 | 
						|
            if (only_input == TEV_INIT_OHEADBW)
 | 
						|
            {
 | 
						|
                // On updated SRTO_OHEADBW don't change input rate.
 | 
						|
                // This only influences the call to withOverhead().
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // No need to calculate input reate if the bandwidth is set
 | 
						|
                const bool disable_in_rate_calc = (bw != 0);
 | 
						|
                m_pSndBuffer->resetInputRateSmpPeriod(disable_in_rate_calc);
 | 
						|
            }
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "updateCC/TEV_INIT: updating BW=" << m_llMaxBW
 | 
						|
                      << (only_input == TEV_INIT_RESET
 | 
						|
                              ? " (UNCHANGED)"
 | 
						|
                              : only_input == TEV_INIT_OHEADBW ? " (only Overhead)" : " (updated sampling rate)"));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // This part is also required only by LiveCC, however not
 | 
						|
    // moved there due to that it needs access to CSndBuffer.
 | 
						|
    if (evt == TEV_ACK || evt == TEV_LOSSREPORT || evt == TEV_CHECKTIMER)
 | 
						|
    {
 | 
						|
        // Specific part done when MaxBW is set to 0 (auto) and InputBW is 0.
 | 
						|
        // This requests internal input rate sampling.
 | 
						|
        if (m_llMaxBW == 0 && m_llInputBW == 0)
 | 
						|
        {
 | 
						|
            // Get auto-calculated input rate, Bytes per second
 | 
						|
            const int64_t inputbw = m_pSndBuffer->getInputRate();
 | 
						|
 | 
						|
            /*
 | 
						|
             * On blocked transmitter (tx full) and until connection closes,
 | 
						|
             * auto input rate falls to 0 but there may be still lot of packet to retransmit
 | 
						|
             * Calling updateBandwidth with 0 sets maxBW to default BW_INFINITE (1 Gbps)
 | 
						|
             * and sendrate skyrockets for retransmission.
 | 
						|
             * Keep previously set maximum in that case (inputbw == 0).
 | 
						|
             */
 | 
						|
            if (inputbw != 0)
 | 
						|
                m_CongCtl->updateBandwidth(0, withOverhead(inputbw)); // Bytes/sec
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "udpateCC: emitting signal for EVENT:" << TransmissionEventStr(evt));
 | 
						|
 | 
						|
    // Now execute a congctl-defined action for that event.
 | 
						|
    EmitSignal(evt, arg);
 | 
						|
 | 
						|
    // This should be done with every event except ACKACK and SEND/RECEIVE
 | 
						|
    // After any action was done by the congctl, update the congestion window and sending interval.
 | 
						|
    if (evt != TEV_ACKACK && evt != TEV_SEND && evt != TEV_RECEIVE)
 | 
						|
    {
 | 
						|
        // This part comes from original UDT.
 | 
						|
        // NOTE: THESE things come from CCC class:
 | 
						|
        // - m_dPktSndPeriod
 | 
						|
        // - m_dCWndSize
 | 
						|
        m_ullInterval_tk    = (uint64_t)(m_CongCtl->pktSndPeriod_us() * m_ullCPUFrequency);
 | 
						|
        m_dCongestionWindow = m_CongCtl->cgWindowSize();
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "updateCC: updated values from congctl: interval=" << m_ullInterval_tk << "tk ("
 | 
						|
                  << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << m_dCongestionWindow);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "udpateCC: finished handling for EVENT:" << TransmissionEventStr(evt));
 | 
						|
 | 
						|
#if 0 // debug
 | 
						|
    static int callcnt = 0;
 | 
						|
    if (!(callcnt++ % 250)) cerr << "SndPeriod=" << (m_ullInterval_tk/m_ullCPUFrequency) << "\n");
 | 
						|
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::initSynch()
 | 
						|
{
 | 
						|
    pthread_mutex_init(&m_SendBlockLock, NULL);
 | 
						|
    pthread_cond_init(&m_SendBlockCond, NULL);
 | 
						|
    pthread_mutex_init(&m_RecvDataLock, NULL);
 | 
						|
    pthread_cond_init(&m_RecvDataCond, NULL);
 | 
						|
    pthread_mutex_init(&m_SendLock, NULL);
 | 
						|
    pthread_mutex_init(&m_RecvLock, NULL);
 | 
						|
    pthread_mutex_init(&m_RcvLossLock, NULL);
 | 
						|
    pthread_mutex_init(&m_RecvAckLock, NULL);
 | 
						|
    pthread_mutex_init(&m_RcvBufferLock, NULL);
 | 
						|
    pthread_mutex_init(&m_ConnectionLock, NULL);
 | 
						|
    pthread_mutex_init(&m_StatsLock, NULL);
 | 
						|
 | 
						|
    memset(&m_RcvTsbPdThread, 0, sizeof m_RcvTsbPdThread);
 | 
						|
    pthread_cond_init(&m_RcvTsbPdCond, NULL);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::destroySynch()
 | 
						|
{
 | 
						|
    pthread_mutex_destroy(&m_SendBlockLock);
 | 
						|
    pthread_cond_destroy(&m_SendBlockCond);
 | 
						|
    pthread_mutex_destroy(&m_RecvDataLock);
 | 
						|
    pthread_cond_destroy(&m_RecvDataCond);
 | 
						|
    pthread_mutex_destroy(&m_SendLock);
 | 
						|
    pthread_mutex_destroy(&m_RecvLock);
 | 
						|
    pthread_mutex_destroy(&m_RcvLossLock);
 | 
						|
    pthread_mutex_destroy(&m_RecvAckLock);
 | 
						|
    pthread_mutex_destroy(&m_RcvBufferLock);
 | 
						|
    pthread_mutex_destroy(&m_ConnectionLock);
 | 
						|
    pthread_mutex_destroy(&m_StatsLock);
 | 
						|
    pthread_cond_destroy(&m_RcvTsbPdCond);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::releaseSynch()
 | 
						|
{
 | 
						|
    // wake up user calls
 | 
						|
    pthread_mutex_lock(&m_SendBlockLock);
 | 
						|
    pthread_cond_signal(&m_SendBlockCond);
 | 
						|
    pthread_mutex_unlock(&m_SendBlockLock);
 | 
						|
 | 
						|
    pthread_mutex_lock(&m_SendLock);
 | 
						|
    pthread_mutex_unlock(&m_SendLock);
 | 
						|
 | 
						|
    pthread_mutex_lock(&m_RecvDataLock);
 | 
						|
    pthread_cond_signal(&m_RecvDataCond);
 | 
						|
    pthread_mutex_unlock(&m_RecvDataLock);
 | 
						|
 | 
						|
    pthread_mutex_lock(&m_RecvLock);
 | 
						|
    pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
    pthread_mutex_unlock(&m_RecvLock);
 | 
						|
 | 
						|
    pthread_mutex_lock(&m_RecvDataLock);
 | 
						|
    if (!pthread_equal(m_RcvTsbPdThread, pthread_t()))
 | 
						|
    {
 | 
						|
        pthread_join(m_RcvTsbPdThread, NULL);
 | 
						|
        m_RcvTsbPdThread = pthread_t();
 | 
						|
    }
 | 
						|
    pthread_mutex_unlock(&m_RecvDataLock);
 | 
						|
 | 
						|
    pthread_mutex_lock(&m_RecvLock);
 | 
						|
    pthread_mutex_unlock(&m_RecvLock);
 | 
						|
}
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
static void DebugAck(string hdr, int prev, int ack)
 | 
						|
{
 | 
						|
    if (!prev)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << hdr << "ACK " << ack);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    prev     = CSeqNo::incseq(prev);
 | 
						|
    int diff = CSeqNo::seqoff(prev, ack);
 | 
						|
    if (diff < 0)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << hdr << "ACK ERROR: " << prev << "-" << ack << "(diff " << diff << ")");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    bool shorted = diff > 100; // sanity
 | 
						|
    if (shorted)
 | 
						|
        ack = CSeqNo::incseq(prev, 100);
 | 
						|
 | 
						|
    ostringstream ackv;
 | 
						|
    for (; prev != ack; prev = CSeqNo::incseq(prev))
 | 
						|
        ackv << prev << " ";
 | 
						|
    if (shorted)
 | 
						|
        ackv << "...";
 | 
						|
    HLOGC(mglog.Debug, log << hdr << "ACK (" << (diff + 1) << "): " << ackv.str() << ack);
 | 
						|
}
 | 
						|
#else
 | 
						|
static inline void DebugAck(string, int, int) {}
 | 
						|
#endif
 | 
						|
 | 
						|
void CUDT::sendCtrl(UDTMessageType pkttype, const void *lparam, void *rparam, int size)
 | 
						|
{
 | 
						|
    CPacket  ctrlpkt;
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
 | 
						|
    ctrlpkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
 | 
						|
    int nbsent        = 0;
 | 
						|
    int local_prevack = 0;
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
    struct SaveBack
 | 
						|
    {
 | 
						|
        int &      target;
 | 
						|
        const int &source;
 | 
						|
 | 
						|
        ~SaveBack() { target = source; }
 | 
						|
    } l_saveback = {m_iDebugPrevLastAck, m_iRcvLastAck};
 | 
						|
    (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable]
 | 
						|
 | 
						|
    local_prevack = m_iDebugPrevLastAck;
 | 
						|
#endif
 | 
						|
 | 
						|
    switch (pkttype)
 | 
						|
    {
 | 
						|
    case UMSG_ACK: // 010 - Acknowledgement
 | 
						|
    {
 | 
						|
        int32_t ack;
 | 
						|
 | 
						|
        // If there is no loss, the ACK is the current largest sequence number plus 1;
 | 
						|
        // Otherwise it is the smallest sequence number in the receiver loss list.
 | 
						|
        if (m_pRcvLossList->getLossLength() == 0)
 | 
						|
            ack = CSeqNo::incseq(m_iRcvCurrSeqNo);
 | 
						|
        else
 | 
						|
            ack = m_pRcvLossList->getFirstLostSeq();
 | 
						|
 | 
						|
        if (m_iRcvLastAckAck == ack)
 | 
						|
            break;
 | 
						|
 | 
						|
        // send out a lite ACK
 | 
						|
        // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number
 | 
						|
        if (size == SEND_LITE_ACK)
 | 
						|
        {
 | 
						|
            ctrlpkt.pack(pkttype, NULL, &ack, size);
 | 
						|
            ctrlpkt.m_iID = m_PeerID;
 | 
						|
            nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
            DebugAck("sendCtrl(lite):" + CONID(), local_prevack, ack);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        // There are new received packets to acknowledge, update related information.
 | 
						|
        /* tsbpd thread may also call ackData when skipping packet so protect code */
 | 
						|
        CGuard::enterCS(m_RcvBufferLock);
 | 
						|
 | 
						|
        // IF ack > m_iRcvLastAck
 | 
						|
        if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0)
 | 
						|
        {
 | 
						|
            int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack);
 | 
						|
 | 
						|
            IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck);
 | 
						|
            m_iRcvLastAck     = ack;
 | 
						|
            m_iRcvLastSkipAck = ack;
 | 
						|
 | 
						|
            // XXX Unknown as to whether it matters.
 | 
						|
            // This if (acksize) causes that ackData() won't be called.
 | 
						|
            // With size == 0 it wouldn't do anything except calling CTimer::triggerEvent().
 | 
						|
            // This, again, signals the condition, CTimer::m_EventCond.
 | 
						|
            // This releases CTimer::waitForEvent() call used in CUDTUnited::selectEx().
 | 
						|
            // Preventing to call this on zero size makes sense, if it prevents false alerts.
 | 
						|
            if (acksize > 0)
 | 
						|
                m_pRcvBuffer->ackData(acksize);
 | 
						|
            CGuard::leaveCS(m_RcvBufferLock);
 | 
						|
 | 
						|
            // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond,
 | 
						|
            // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which
 | 
						|
            // will signal m_RecvDataCond when there's time to play for particular
 | 
						|
            // data packet.
 | 
						|
            HLOGC(dlog.Debug,
 | 
						|
                  log << "ACK: clip %" << oldack << "-%" << ack << ", REVOKED " << acksize << " from RCV buffer");
 | 
						|
 | 
						|
            if (m_bTsbPd)
 | 
						|
            {
 | 
						|
                /* Newly acknowledged data, signal TsbPD thread */
 | 
						|
                pthread_mutex_lock(&m_RecvLock);
 | 
						|
                if (m_bTsbPdAckWakeup)
 | 
						|
                    pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
                pthread_mutex_unlock(&m_RecvLock);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (m_bSynRecving)
 | 
						|
                {
 | 
						|
                    // signal a waiting "recv" call if there is any data available
 | 
						|
                    pthread_mutex_lock(&m_RecvDataLock);
 | 
						|
                    pthread_cond_signal(&m_RecvDataCond);
 | 
						|
                    pthread_mutex_unlock(&m_RecvDataLock);
 | 
						|
                }
 | 
						|
                // acknowledge any waiting epolls to read
 | 
						|
                s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true);
 | 
						|
                CTimer::triggerEvent();
 | 
						|
            }
 | 
						|
            CGuard::enterCS(m_RcvBufferLock);
 | 
						|
        }
 | 
						|
        else if (ack == m_iRcvLastAck)
 | 
						|
        {
 | 
						|
            // If the ACK was just sent already AND elapsed time did not exceed RTT,
 | 
						|
            if ((currtime_tk - m_ullLastAckTime_tk) < ((m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency))
 | 
						|
            {
 | 
						|
                CGuard::leaveCS(m_RcvBufferLock);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Not possible (m_iRcvCurrSeqNo+1 < m_iRcvLastAck ?)
 | 
						|
            CGuard::leaveCS(m_RcvBufferLock);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        // [[using assert( ack >= m_iRcvLastAck && is_periodic_ack ) ]]
 | 
						|
 | 
						|
        // Send out the ACK only if has not been received by the sender before
 | 
						|
        if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0)
 | 
						|
        {
 | 
						|
            // NOTE: The BSTATS feature turns on extra fields above size 6
 | 
						|
            // also known as ACKD_TOTAL_SIZE_VER100.
 | 
						|
            int32_t data[ACKD_TOTAL_SIZE];
 | 
						|
 | 
						|
            // Case you care, CAckNo::incack does exactly the same thing as
 | 
						|
            // CSeqNo::incseq. Logically the ACK number is a different thing
 | 
						|
            // than sequence number (it's a "journal" for ACK request-response,
 | 
						|
            // and starts from 0, unlike sequence, which starts from a random
 | 
						|
            // number), but still the numbers are from exactly the same domain.
 | 
						|
            m_iAckSeqNo           = CAckNo::incack(m_iAckSeqNo);
 | 
						|
            data[ACKD_RCVLASTACK] = m_iRcvLastAck;
 | 
						|
            data[ACKD_RTT]        = m_iRTT;
 | 
						|
            data[ACKD_RTTVAR]     = m_iRTTVar;
 | 
						|
            data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize();
 | 
						|
            // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock
 | 
						|
            if (data[ACKD_BUFFERLEFT] < 2)
 | 
						|
                data[ACKD_BUFFERLEFT] = 2;
 | 
						|
 | 
						|
            // NOTE: m_CongCtl->ACKTimeout_us() should be taken into account.
 | 
						|
            if (currtime_tk - m_ullLastAckTime_tk > m_ullACKInt_tk)
 | 
						|
            {
 | 
						|
                int rcvRate;
 | 
						|
                int ctrlsz = ACKD_TOTAL_SIZE_UDTBASE * ACKD_FIELD_SIZE; // Minimum required size
 | 
						|
 | 
						|
                data[ACKD_RCVSPEED]  = m_RcvTimeWindow.getPktRcvSpeed(Ref(rcvRate));
 | 
						|
                data[ACKD_BANDWIDTH] = m_RcvTimeWindow.getBandwidth();
 | 
						|
 | 
						|
                //>>Patch while incompatible (1.0.2) receiver floating around
 | 
						|
                if (m_lPeerSrtVersion == SrtVersion(1, 0, 2))
 | 
						|
                {
 | 
						|
                    data[ACKD_RCVRATE] = rcvRate;                                     // bytes/sec
 | 
						|
                    data[ACKD_XMRATE]  = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec
 | 
						|
                    ctrlsz             = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102;
 | 
						|
                }
 | 
						|
                else if (m_lPeerSrtVersion >= SrtVersion(1, 0, 3))
 | 
						|
                {
 | 
						|
                    // Normal, currently expected version.
 | 
						|
                    data[ACKD_RCVRATE] = rcvRate; // bytes/sec
 | 
						|
                    ctrlsz             = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER101;
 | 
						|
                }
 | 
						|
                // ELSE: leave the buffer with ...UDTBASE size.
 | 
						|
 | 
						|
                ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, ctrlsz);
 | 
						|
                CTimer::rdtsc(m_ullLastAckTime_tk);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL);
 | 
						|
            }
 | 
						|
 | 
						|
            ctrlpkt.m_iID        = m_PeerID;
 | 
						|
            ctrlpkt.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
            nbsent               = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
            DebugAck("sendCtrl: " + CONID(), local_prevack, ack);
 | 
						|
 | 
						|
            m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck);
 | 
						|
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            ++m_stats.sentACK;
 | 
						|
            ++m_stats.sentACKTotal;
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
        }
 | 
						|
        CGuard::leaveCS(m_RcvBufferLock);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement
 | 
						|
        ctrlpkt.pack(pkttype, lparam);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_LOSSREPORT: // 011 - Loss Report
 | 
						|
    {
 | 
						|
        // Explicitly defined lost sequences
 | 
						|
        if (rparam)
 | 
						|
        {
 | 
						|
            int32_t *lossdata = (int32_t *)rparam;
 | 
						|
 | 
						|
            size_t bytes = sizeof(*lossdata) * size;
 | 
						|
            ctrlpkt.pack(pkttype, NULL, lossdata, bytes);
 | 
						|
 | 
						|
            ctrlpkt.m_iID = m_PeerID;
 | 
						|
            nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            ++m_stats.sentNAK;
 | 
						|
            ++m_stats.sentNAKTotal;
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
        }
 | 
						|
        // Call with no arguments - get loss list from internal data.
 | 
						|
        else if (m_pRcvLossList->getLossLength() > 0)
 | 
						|
        {
 | 
						|
            // this is periodically NAK report; make sure NAK cannot be sent back too often
 | 
						|
 | 
						|
            // read loss list from the local receiver loss list
 | 
						|
            int32_t *data = new int32_t[m_iMaxSRTPayloadSize / 4];
 | 
						|
            int      losslen;
 | 
						|
            m_pRcvLossList->getLossArray(data, losslen, m_iMaxSRTPayloadSize / 4);
 | 
						|
 | 
						|
            if (0 < losslen)
 | 
						|
            {
 | 
						|
                ctrlpkt.pack(pkttype, NULL, data, losslen * 4);
 | 
						|
                ctrlpkt.m_iID = m_PeerID;
 | 
						|
                nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
                CGuard::enterCS(m_StatsLock);
 | 
						|
                ++m_stats.sentNAK;
 | 
						|
                ++m_stats.sentNAKTotal;
 | 
						|
                CGuard::leaveCS(m_StatsLock);
 | 
						|
            }
 | 
						|
 | 
						|
            delete[] data;
 | 
						|
        }
 | 
						|
 | 
						|
        // update next NAK time, which should wait enough time for the retansmission, but not too long
 | 
						|
        m_ullNAKInt_tk = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency;
 | 
						|
 | 
						|
        // Fix the NAKreport period according to the congctl
 | 
						|
        m_ullNAKInt_tk = m_CongCtl->updateNAKInterval(
 | 
						|
            m_ullNAKInt_tk, m_RcvTimeWindow.getPktRcvSpeed(), m_pRcvLossList->getLossLength());
 | 
						|
 | 
						|
        // This is necessary because a congctl need not wish to define
 | 
						|
        // its own minimum interval, in which case the default one is used.
 | 
						|
        if (m_ullNAKInt_tk < m_ullMinNakInt_tk)
 | 
						|
            m_ullNAKInt_tk = m_ullMinNakInt_tk;
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case UMSG_CGWARNING: // 100 - Congestion Warning
 | 
						|
        ctrlpkt.pack(pkttype);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        CTimer::rdtsc(m_ullLastWarningTime);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_KEEPALIVE: // 001 - Keep-alive
 | 
						|
        ctrlpkt.pack(pkttype);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_HANDSHAKE: // 000 - Handshake
 | 
						|
        ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake));
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_SHUTDOWN: // 101 - Shutdown
 | 
						|
        ctrlpkt.pack(pkttype);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_DROPREQ: // 111 - Msg drop request
 | 
						|
        ctrlpkt.pack(pkttype, lparam, rparam, 8);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error
 | 
						|
        ctrlpkt.pack(pkttype, lparam);
 | 
						|
        ctrlpkt.m_iID = m_PeerID;
 | 
						|
        nbsent        = m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_EXT: // 0x7FFF - Resevered for future use
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    // Fix keepalive
 | 
						|
    if (nbsent)
 | 
						|
        m_ullLastSndTime_tk = currtime_tk;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno)
 | 
						|
{
 | 
						|
    // Update sender's loss list and acknowledge packets in the sender's buffer
 | 
						|
    {
 | 
						|
        // m_RecvAckLock protects sender's loss list and epoll
 | 
						|
        CGuard ack_lock(m_RecvAckLock);
 | 
						|
 | 
						|
        const int offset = CSeqNo::seqoff(m_iSndLastDataAck, ackdata_seqno);
 | 
						|
        // IF distance between m_iSndLastDataAck and ack is nonempty...
 | 
						|
        if (offset <= 0)
 | 
						|
            return;
 | 
						|
 | 
						|
        // update sending variables
 | 
						|
        m_iSndLastDataAck = ackdata_seqno;
 | 
						|
 | 
						|
        // remove any loss that predates 'ack' (not to be considered loss anymore)
 | 
						|
        m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck));
 | 
						|
 | 
						|
        // acknowledge the sending buffer (remove data that predate 'ack')
 | 
						|
        m_pSndBuffer->ackData(offset);
 | 
						|
 | 
						|
        // acknowledde any waiting epolls to write
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
 | 
						|
    }
 | 
						|
 | 
						|
    // insert this socket to snd list if it is not on the list yet
 | 
						|
    m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
 | 
						|
 | 
						|
    if (m_bSynSending)
 | 
						|
    {
 | 
						|
        CGuard lk(m_SendBlockLock);
 | 
						|
        pthread_cond_signal(&m_SendBlockCond);
 | 
						|
    }
 | 
						|
 | 
						|
    const int64_t currtime = CTimer::getTime();
 | 
						|
    // record total time used for sending
 | 
						|
    CGuard::enterCS(m_StatsLock);
 | 
						|
    m_stats.sndDuration += currtime - m_stats.sndDurationCounter;
 | 
						|
    m_stats.m_sndDurationTotal += currtime - m_stats.sndDurationCounter;
 | 
						|
    m_stats.sndDurationCounter = currtime;
 | 
						|
    CGuard::leaveCS(m_StatsLock);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::processCtrlAck(const CPacket &ctrlpkt, const uint64_t currtime_tk)
 | 
						|
{
 | 
						|
    const int32_t *ackdata       = (const int32_t *)ctrlpkt.m_pcData;
 | 
						|
    const int32_t  ackdata_seqno = ackdata[ACKD_RCVLASTACK];
 | 
						|
 | 
						|
    const bool isLiteAck = ctrlpkt.getLength() == (size_t)SEND_LITE_ACK;
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ackdata_seqno << " [ACK=" << m_iSndLastAck
 | 
						|
              << "]" << (isLiteAck ? "[LITE]" : "[FULL]"));
 | 
						|
 | 
						|
    updateSndLossListOnACK(ackdata_seqno);
 | 
						|
 | 
						|
    // Process a lite ACK
 | 
						|
    if (isLiteAck)
 | 
						|
    {
 | 
						|
        if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0)
 | 
						|
        {
 | 
						|
            CGuard ack_lock(m_RecvAckLock);
 | 
						|
            m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno);
 | 
						|
            m_iSndLastAck = ackdata_seqno;
 | 
						|
 | 
						|
            // TODO: m_ullLastRspAckTime_tk should be protected with m_RecvAckLock
 | 
						|
            // because the sendmsg2 may want to change it at the same time.
 | 
						|
            m_ullLastRspAckTime_tk = currtime_tk;
 | 
						|
            m_iReXmitCount         = 1; // Reset re-transmit count since last ACK
 | 
						|
        }
 | 
						|
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Decide to send ACKACK or not
 | 
						|
    {
 | 
						|
        // Sequence number of the ACK packet
 | 
						|
        const int32_t ack_seqno = ctrlpkt.getAckSeqNo();
 | 
						|
 | 
						|
        // Send ACK acknowledgement (UMSG_ACKACK).
 | 
						|
        // There can be less ACKACK packets in the stream, than the number of ACK packets.
 | 
						|
        // Only send ACKACK every syn interval or if ACK packet with the sequence number
 | 
						|
        // already acknowledged (with ACKACK) has come again, which probably means ACKACK was lost.
 | 
						|
        const uint64_t now = CTimer::getTime();
 | 
						|
        if ((now - m_ullSndLastAck2Time > (uint64_t)COMM_SYN_INTERVAL_US) || (ack_seqno == m_iSndLastAck2))
 | 
						|
        {
 | 
						|
            sendCtrl(UMSG_ACKACK, &ack_seqno);
 | 
						|
            m_iSndLastAck2       = ack_seqno;
 | 
						|
            m_ullSndLastAck2Time = now;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Begin of the new code with TLPKTDROP.
 | 
						|
    //
 | 
						|
 | 
						|
    // Protect packet retransmission
 | 
						|
    CGuard::enterCS(m_RecvAckLock);
 | 
						|
 | 
						|
    // Check the validation of the ack
 | 
						|
    if (CSeqNo::seqcmp(ackdata_seqno, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)
 | 
						|
    {
 | 
						|
        CGuard::leaveCS(m_RecvAckLock);
 | 
						|
        // this should not happen: attack or bug
 | 
						|
        LOGC(glog.Error,
 | 
						|
             log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current "
 | 
						|
                 << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!");
 | 
						|
        m_bBroken        = true;
 | 
						|
        m_iBrokenCounter = 0;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0)
 | 
						|
    {
 | 
						|
        // Update Flow Window Size, must update before and together with m_iSndLastAck
 | 
						|
        m_iFlowWindowSize      = ackdata[ACKD_BUFFERLEFT];
 | 
						|
        m_iSndLastAck          = ackdata_seqno;
 | 
						|
        m_ullLastRspAckTime_tk = currtime_tk; // Should be protected with m_RecvAckLock
 | 
						|
        m_iReXmitCount         = 1;           // Reset re-transmit count since last ACK
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * We must not ignore full ack received by peer
 | 
						|
     * if data has been artificially acked by late packet drop.
 | 
						|
     * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck)
 | 
						|
     * and ack position in send buffer (m_iSndLastDataAck).
 | 
						|
     * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update)
 | 
						|
     * occures, we drop received acks (as duplicates) and do not update stats like RTT,
 | 
						|
     * which may go crazy and stay there, preventing proper stream recovery.
 | 
						|
     */
 | 
						|
 | 
						|
    if (CSeqNo::seqoff(m_iSndLastFullAck, ackdata_seqno) <= 0)
 | 
						|
    {
 | 
						|
        // discard it if it is a repeated ACK
 | 
						|
        CGuard::leaveCS(m_RecvAckLock);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    m_iSndLastFullAck = ackdata_seqno;
 | 
						|
 | 
						|
    //
 | 
						|
    // END of the new code with TLPKTDROP
 | 
						|
    //
 | 
						|
    CGuard::leaveCS(m_RecvAckLock);
 | 
						|
 | 
						|
    size_t acksize   = ctrlpkt.getLength(); // TEMPORARY VALUE FOR CHECKING
 | 
						|
    bool   wrongsize = 0 != (acksize % ACKD_FIELD_SIZE);
 | 
						|
    acksize          = acksize / ACKD_FIELD_SIZE; // ACTUAL VALUE
 | 
						|
 | 
						|
    if (wrongsize)
 | 
						|
    {
 | 
						|
        // Issue a log, but don't do anything but skipping the "odd" bytes from the payload.
 | 
						|
        LOGC(mglog.Error,
 | 
						|
             log << CONID() << "Received UMSG_ACK payload is not evened up to 4-byte based field size - cutting to "
 | 
						|
                 << acksize << " fields");
 | 
						|
    }
 | 
						|
 | 
						|
    // Start with checking the base size.
 | 
						|
    if (acksize < ACKD_TOTAL_SIZE_SMALL)
 | 
						|
    {
 | 
						|
        LOGC(mglog.Error, log << CONID() << "Invalid ACK size " << acksize << " fields - less than minimum required!");
 | 
						|
        // Ack is already interpreted, just skip further parts.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    // This check covers fields up to ACKD_BUFFERLEFT.
 | 
						|
 | 
						|
    // Update RTT
 | 
						|
    // m_iRTT = ackdata[ACKD_RTT];
 | 
						|
    // m_iRTTVar = ackdata[ACKD_RTTVAR];
 | 
						|
    // XXX These ^^^ commented-out were blocked in UDT;
 | 
						|
    // the current RTT calculations are exactly the same as in UDT4.
 | 
						|
    const int rtt = ackdata[ACKD_RTT];
 | 
						|
 | 
						|
    m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT));
 | 
						|
    m_iRTT    = avg_iir<8>(m_iRTT, rtt);
 | 
						|
 | 
						|
    /* Version-dependent fields:
 | 
						|
     * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL):
 | 
						|
     *   ACKD_RCVLASTACK
 | 
						|
     *   ACKD_RTT
 | 
						|
     *   ACKD_RTTVAR
 | 
						|
     *   ACKD_BUFFERLEFT
 | 
						|
     * Additional UDT fields, not always attached:
 | 
						|
     *   ACKD_RCVSPEED
 | 
						|
     *   ACKD_BANDWIDTH
 | 
						|
     * SRT extension version 1.0.2 (bstats):
 | 
						|
     *   ACKD_RCVRATE
 | 
						|
     * SRT extension version 1.0.4:
 | 
						|
     *   ACKD_XMRATE
 | 
						|
     */
 | 
						|
 | 
						|
    if (acksize > ACKD_TOTAL_SIZE_SMALL)
 | 
						|
    {
 | 
						|
        // This means that ACKD_RCVSPEED and ACKD_BANDWIDTH fields are available.
 | 
						|
        int pktps     = ackdata[ACKD_RCVSPEED];
 | 
						|
        int bandwidth = ackdata[ACKD_BANDWIDTH];
 | 
						|
        int bytesps;
 | 
						|
 | 
						|
        /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in
 | 
						|
         * bytes/sec instead of pkts/sec */
 | 
						|
        /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
 | 
						|
        if (acksize > ACKD_TOTAL_SIZE_UDTBASE)
 | 
						|
            bytesps = ackdata[ACKD_RCVRATE];
 | 
						|
        else
 | 
						|
            bytesps = pktps * m_iMaxSRTPayloadSize;
 | 
						|
 | 
						|
        m_iBandwidth        = avg_iir<8>(m_iBandwidth, bandwidth);
 | 
						|
        m_iDeliveryRate     = avg_iir<8>(m_iDeliveryRate, pktps);
 | 
						|
        m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps);
 | 
						|
        // XXX not sure if ACKD_XMRATE is of any use. This is simply
 | 
						|
        // calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize.
 | 
						|
 | 
						|
        // Update Estimated Bandwidth and packet delivery rate
 | 
						|
        // m_iRcvRate = m_iDeliveryRate;
 | 
						|
        // ^^ This has been removed because with the SrtCongestion class
 | 
						|
        // instead of reading the m_iRcvRate local field this will read
 | 
						|
        // cudt->deliveryRate() instead.
 | 
						|
    }
 | 
						|
 | 
						|
    checkSndTimers(REGEN_KM);
 | 
						|
    updateCC(TEV_ACK, ackdata_seqno);
 | 
						|
 | 
						|
    CGuard::enterCS(m_StatsLock);
 | 
						|
    ++m_stats.recvACK;
 | 
						|
    ++m_stats.recvACKTotal;
 | 
						|
    CGuard::leaveCS(m_StatsLock);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::processCtrl(CPacket &ctrlpkt)
 | 
						|
{
 | 
						|
    // Just heard from the peer, reset the expiration count.
 | 
						|
    m_iEXPCount = 1;
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
    m_ullLastRspTime_tk    = currtime_tk;
 | 
						|
    bool using_rexmit_flag = m_bPeerRexmitFlag;
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " ("
 | 
						|
              << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID);
 | 
						|
 | 
						|
    switch (ctrlpkt.getType())
 | 
						|
    {
 | 
						|
    case UMSG_ACK: // 010 - Acknowledgement
 | 
						|
        processCtrlAck(ctrlpkt, currtime_tk);
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement
 | 
						|
    {
 | 
						|
        int32_t ack = 0;
 | 
						|
        int     rtt = -1;
 | 
						|
 | 
						|
        // update RTT
 | 
						|
        rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack);
 | 
						|
        if (rtt <= 0)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Error,
 | 
						|
                 log << "IPE: ACK node overwritten when acknowledging " << ctrlpkt.getAckSeqNo()
 | 
						|
                     << " (ack extracted: " << ack << ")");
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        // if increasing delay detected...
 | 
						|
        //   sendCtrl(UMSG_CGWARNING);
 | 
						|
 | 
						|
        // RTT EWMA
 | 
						|
        m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2;
 | 
						|
        m_iRTT    = (m_iRTT * 7 + rtt) >> 3;
 | 
						|
 | 
						|
        updateCC(TEV_ACKACK, ack);
 | 
						|
 | 
						|
        // This function will put a lock on m_RecvLock by itself, as needed.
 | 
						|
        // It must be done inside because this function reads the current time
 | 
						|
        // and if waiting for the lock has caused a delay, the time will be
 | 
						|
        // inaccurate. Additionally it won't lock if TSBPD mode is off, and
 | 
						|
        // won't update anything. Note that if you set TSBPD mode and use
 | 
						|
        // srt_recvfile (which doesn't make any sense), you'll have a deadlock.
 | 
						|
        m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock);
 | 
						|
 | 
						|
        // update last ACK that has been received by the sender
 | 
						|
        if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0)
 | 
						|
            m_iRcvLastAckAck = ack;
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case UMSG_LOSSREPORT: // 011 - Loss Report
 | 
						|
    {
 | 
						|
        int32_t *losslist     = (int32_t *)(ctrlpkt.m_pcData);
 | 
						|
        size_t   losslist_len = ctrlpkt.getLength() / 4;
 | 
						|
 | 
						|
        bool secure = true;
 | 
						|
 | 
						|
        // protect packet retransmission
 | 
						|
        CGuard::enterCS(m_RecvAckLock);
 | 
						|
 | 
						|
        // This variable is used in "normal" logs, so it may cause a warning
 | 
						|
        // when logging is forcefully off.
 | 
						|
        int32_t wrong_loss SRT_ATR_UNUSED = CSeqNo::m_iMaxSeqNo;
 | 
						|
 | 
						|
        // decode loss list message and insert loss into the sender loss list
 | 
						|
        for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++i)
 | 
						|
        {
 | 
						|
            if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST))
 | 
						|
            {
 | 
						|
                // Then it's this is a <lo, hi> specification with HI in a consecutive cell.
 | 
						|
                int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]);
 | 
						|
                int32_t losslist_hi = losslist[i + 1];
 | 
						|
                // <lo, hi> specification means that the consecutive cell has been already interpreted.
 | 
						|
                ++i;
 | 
						|
 | 
						|
                HLOGF(mglog.Debug,
 | 
						|
                      "received UMSG_LOSSREPORT: %d-%d (%d packets)...",
 | 
						|
                      losslist_lo,
 | 
						|
                      losslist_hi,
 | 
						|
                      CSeqNo::seqoff(losslist_lo, losslist_hi) + 1);
 | 
						|
 | 
						|
                if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) ||
 | 
						|
                    (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0))
 | 
						|
                {
 | 
						|
                    // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq
 | 
						|
                    secure     = false;
 | 
						|
                    wrong_loss = losslist_hi;
 | 
						|
                    // XXX leaveCS: really necessary? 'break' will break the 'for' loop, not the 'switch' statement.
 | 
						|
                    // and the leaveCS is done again next to the 'for' loop end.
 | 
						|
                    CGuard::leaveCS(m_RecvAckLock);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                int num = 0;
 | 
						|
                if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0)
 | 
						|
                    num = m_pSndLossList->insert(losslist_lo, losslist_hi);
 | 
						|
                else if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0)
 | 
						|
                {
 | 
						|
                    // This should be theoretically impossible because this would mean
 | 
						|
                    // that the received packet loss report informs about the loss that predates
 | 
						|
                    // the ACK sequence.
 | 
						|
                    // However, this can happen if the packet reordering has caused the earlier sent
 | 
						|
                    // LOSSREPORT will be delivered after later sent ACK. Whatever, ACK should be
 | 
						|
                    // more important, so simply drop the part that predates ACK.
 | 
						|
                    num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi);
 | 
						|
                }
 | 
						|
 | 
						|
                CGuard::enterCS(m_StatsLock);
 | 
						|
                m_stats.traceSndLoss += num;
 | 
						|
                m_stats.sndLossTotal += num;
 | 
						|
                CGuard::leaveCS(m_StatsLock);
 | 
						|
            }
 | 
						|
            else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0)
 | 
						|
            {
 | 
						|
                HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d (1 packet)...", losslist[i]);
 | 
						|
 | 
						|
                if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0)
 | 
						|
                {
 | 
						|
                    // seq_a must not be greater than the most recent sent seq
 | 
						|
                    secure     = false;
 | 
						|
                    wrong_loss = losslist[i];
 | 
						|
                    CGuard::leaveCS(m_RecvAckLock);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                int num = m_pSndLossList->insert(losslist[i], losslist[i]);
 | 
						|
 | 
						|
                CGuard::enterCS(m_StatsLock);
 | 
						|
                m_stats.traceSndLoss += num;
 | 
						|
                m_stats.sndLossTotal += num;
 | 
						|
                CGuard::leaveCS(m_StatsLock);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        CGuard::leaveCS(m_RecvAckLock);
 | 
						|
 | 
						|
        updateCC(TEV_LOSSREPORT, EventVariant(losslist, losslist_len));
 | 
						|
 | 
						|
        if (!secure)
 | 
						|
        {
 | 
						|
            LOGC(mglog.Warn,
 | 
						|
                 log << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo
 | 
						|
                     << " vs loss %" << wrong_loss);
 | 
						|
            // this should not happen: attack or bug
 | 
						|
            m_bBroken        = true;
 | 
						|
            m_iBrokenCounter = 0;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        // the lost packet (retransmission) should be sent out immediately
 | 
						|
        m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);
 | 
						|
 | 
						|
        CGuard::enterCS(m_StatsLock);
 | 
						|
        ++m_stats.recvNAK;
 | 
						|
        ++m_stats.recvNAKTotal;
 | 
						|
        CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case UMSG_CGWARNING: // 100 - Delay Warning
 | 
						|
        // One way packet delay is increasing, so decrease the sending rate
 | 
						|
        m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125);
 | 
						|
        m_iLastDecSeq    = m_iSndCurrSeqNo;
 | 
						|
        // XXX Note as interesting fact: this is only prepared for handling,
 | 
						|
        // but nothing in the code is sending this message. Probably predicted
 | 
						|
        // for a custom congctl. There's a predicted place to call it under
 | 
						|
        // UMSG_ACKACK handling, but it's commented out.
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_KEEPALIVE: // 001 - Keep-alive
 | 
						|
        // The only purpose of keep-alive packet is to tell that the peer is still alive
 | 
						|
        // nothing needs to be done.
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_HANDSHAKE: // 000 - Handshake
 | 
						|
    {
 | 
						|
        CHandShake req;
 | 
						|
        req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength());
 | 
						|
 | 
						|
        HLOGC(mglog.Debug, log << "processCtrl: got HS: " << req.show());
 | 
						|
 | 
						|
        if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...???
 | 
						|
            || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION
 | 
						|
        {
 | 
						|
            // The peer side has not received the handshake message, so it keeps querying
 | 
						|
            // resend the handshake packet
 | 
						|
 | 
						|
            // This condition embraces cases when:
 | 
						|
            // - this is normal accept() and URQ_INDUCTION was received
 | 
						|
            // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS
 | 
						|
            // or CONCLUSION)
 | 
						|
            // - this is any of URQ_ERROR_* - well...
 | 
						|
            CHandShake initdata;
 | 
						|
            initdata.m_iISN            = m_iISN;
 | 
						|
            initdata.m_iMSS            = m_iMSS;
 | 
						|
            initdata.m_iFlightFlagSize = m_iFlightFlagSize;
 | 
						|
 | 
						|
            // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT.
 | 
						|
            // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION.
 | 
						|
            initdata.m_iReqType = (!m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT;
 | 
						|
            initdata.m_iID      = m_SocketID;
 | 
						|
 | 
						|
            uint32_t kmdata[SRTDATA_MAXSIZE];
 | 
						|
            size_t   kmdatasize = SRTDATA_MAXSIZE;
 | 
						|
            bool     have_hsreq = false;
 | 
						|
            if (req.m_iVersion > HS_VERSION_UDT4)
 | 
						|
            {
 | 
						|
                initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener...
 | 
						|
                int hs_flags        = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
 | 
						|
                if (hs_flags != 0) // has SRT extensions
 | 
						|
                {
 | 
						|
                    HLOGC(mglog.Debug,
 | 
						|
                          log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)
 | 
						|
                              << " WITH SRT ext");
 | 
						|
                    have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize);
 | 
						|
                    if (!have_hsreq)
 | 
						|
                    {
 | 
						|
                        initdata.m_iVersion = 0;
 | 
						|
                        m_RejectReason      = SRT_REJ_ROGUE;
 | 
						|
                        initdata.m_iReqType = URQFailure(m_RejectReason);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // Extensions are added only in case of CONCLUSION (not AGREEMENT).
 | 
						|
                        // Actually what is expected here is that this may either process the
 | 
						|
                        // belated-repeated handshake from a caller (and then it's CONCLUSION,
 | 
						|
                        // and should be added with HSRSP/KMRSP), or it's a belated handshake
 | 
						|
                        // of Rendezvous when it has already considered itself connected.
 | 
						|
                        // Sanity check - according to the rules, there should be no such situation
 | 
						|
                        if (m_bRendezvous && m_SrtHsSide == HSD_RESPONDER)
 | 
						|
                        {
 | 
						|
                            LOGC(mglog.Error,
 | 
						|
                                 log << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in "
 | 
						|
                                        "handshake phase.");
 | 
						|
                        }
 | 
						|
 | 
						|
                        // The 'extension' flag will be set from this variable; set it to false
 | 
						|
                        // in case when the AGREEMENT response is to be sent.
 | 
						|
                        have_hsreq = initdata.m_iReqType == URQ_CONCLUSION;
 | 
						|
                        HLOGC(mglog.Debug,
 | 
						|
                              log << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType)
 | 
						|
                                  << " kmdatasize=" << kmdatasize);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType));
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                initdata.m_iVersion = HS_VERSION_UDT4;
 | 
						|
            }
 | 
						|
 | 
						|
            initdata.m_extension = have_hsreq;
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType)
 | 
						|
                      << (have_hsreq ? " WITH SRT HS response extensions" : ""));
 | 
						|
 | 
						|
            // XXX here interpret SRT handshake extension
 | 
						|
            CPacket response;
 | 
						|
            response.setControl(UMSG_HANDSHAKE);
 | 
						|
            response.allocate(m_iMaxSRTPayloadSize);
 | 
						|
 | 
						|
            // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE.
 | 
						|
            // There is also no possible IPE condition in case of HSv4 - for this version it will always return true.
 | 
						|
            if (createSrtHandshake(Ref(response), Ref(initdata), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize))
 | 
						|
            {
 | 
						|
                response.m_iID        = m_PeerID;
 | 
						|
                response.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
                int nbsent            = m_pSndQueue->sendto(m_pPeerAddr, response);
 | 
						|
                if (nbsent)
 | 
						|
                {
 | 
						|
                    uint64_t currtime_tk;
 | 
						|
                    CTimer::rdtsc(currtime_tk);
 | 
						|
                    m_ullLastSndTime_tk = currtime_tk;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED.");
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case UMSG_SHUTDOWN: // 101 - Shutdown
 | 
						|
        m_bShutdown      = true;
 | 
						|
        m_bClosing       = true;
 | 
						|
        m_bBroken        = true;
 | 
						|
        m_iBrokenCounter = 60;
 | 
						|
 | 
						|
        // Signal the sender and recver if they are waiting for data.
 | 
						|
        releaseSynch();
 | 
						|
        // Unblock any call so they learn the connection_broken error
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true);
 | 
						|
 | 
						|
        CTimer::triggerEvent();
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_DROPREQ: // 111 - Msg drop request
 | 
						|
        CGuard::enterCS(m_RecvLock);
 | 
						|
        m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag);
 | 
						|
        CGuard::leaveCS(m_RecvLock);
 | 
						|
 | 
						|
        dropFromLossLists(*(int32_t *)ctrlpkt.m_pcData, *(int32_t *)(ctrlpkt.m_pcData + 4));
 | 
						|
 | 
						|
        // move forward with current recv seq no.
 | 
						|
        if ((CSeqNo::seqcmp(*(int32_t *)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) &&
 | 
						|
            (CSeqNo::seqcmp(*(int32_t *)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0))
 | 
						|
        {
 | 
						|
            m_iRcvCurrSeqNo = *(int32_t *)(ctrlpkt.m_pcData + 4);
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_PEERERROR: // 1000 - An error has happened to the peer side
 | 
						|
        // int err_type = packet.getAddInfo();
 | 
						|
 | 
						|
        // currently only this error is signalled from the peer side
 | 
						|
        // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately
 | 
						|
        // giving the app a chance to fix the issue
 | 
						|
 | 
						|
        m_bPeerHealth = false;
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
    case UMSG_EXT: // 0x7FFF - reserved and user defined messages
 | 
						|
        HLOGF(mglog.Debug, "CONTROL EXT MSG RECEIVED: %08X\n", ctrlpkt.getExtendedType());
 | 
						|
        {
 | 
						|
            // This has currently two roles in SRT:
 | 
						|
            // - HSv4 (legacy) handshake
 | 
						|
            // - refreshed KMX (initial KMX is done still in the HS process in HSv5)
 | 
						|
            bool understood = processSrtMsg(&ctrlpkt);
 | 
						|
            // CAREFUL HERE! This only means that this update comes from the UMSG_EXT
 | 
						|
            // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean
 | 
						|
            // the handshake version, but the reason of calling this function.
 | 
						|
            //
 | 
						|
            // Fortunately, the only messages taken into account in this function
 | 
						|
            // are HSREQ and HSRSP, which should *never* be interchanged when both
 | 
						|
            // parties are HSv5.
 | 
						|
            if (understood)
 | 
						|
            {
 | 
						|
                updateAfterSrtHandshake(ctrlpkt.getExtendedType(), HS_VERSION_UDT4);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                updateCC(TEV_CUSTOM, &ctrlpkt);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::updateSrtRcvSettings()
 | 
						|
{
 | 
						|
    if (m_bTsbPd)
 | 
						|
    {
 | 
						|
        /* We are TsbPd receiver */
 | 
						|
        CGuard::enterCS(m_RecvLock);
 | 
						|
        m_pRcvBuffer->setRcvTsbPdMode(m_ullRcvPeerStartTime, m_iTsbPdDelay_ms * 1000);
 | 
						|
        CGuard::leaveCS(m_RecvLock);
 | 
						|
 | 
						|
        HLOGF(mglog.Debug,
 | 
						|
              "AFTER HS: Set Rcv TsbPd mode: delay=%u.%03u secs",
 | 
						|
              m_iTsbPdDelay_ms / 1000,
 | 
						|
              m_iTsbPdDelay_ms % 1000);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "AFTER HS: Rcv TsbPd mode not set");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::updateSrtSndSettings()
 | 
						|
{
 | 
						|
    if (m_bPeerTsbPd)
 | 
						|
    {
 | 
						|
        /* We are TsbPd sender */
 | 
						|
        // XXX Check what happened here.
 | 
						|
        // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iRTT + (4 * m_iRTTVar)) / 1000);
 | 
						|
        /*
 | 
						|
         * For sender to apply Too-Late Packet Drop
 | 
						|
         * option (m_bTLPktDrop) must be enabled and receiving peer shall support it
 | 
						|
         */
 | 
						|
        HLOGF(mglog.Debug,
 | 
						|
              "AFTER HS: Set Snd TsbPd mode %s: delay=%d.%03d secs",
 | 
						|
              m_bPeerTLPktDrop ? "with TLPktDrop" : "without TLPktDrop",
 | 
						|
              m_iPeerTsbPdDelay_ms / 1000,
 | 
						|
              m_iPeerTsbPdDelay_ms % 1000);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "AFTER HS: Snd TsbPd mode not set");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::updateAfterSrtHandshake(int srt_cmd, int hsv)
 | 
						|
{
 | 
						|
 | 
						|
    switch (srt_cmd)
 | 
						|
    {
 | 
						|
    case SRT_CMD_HSREQ:
 | 
						|
    case SRT_CMD_HSRSP:
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // The only possibility here is one of these two:
 | 
						|
    // - Agent is RESPONDER and it receives HSREQ.
 | 
						|
    // - Agent is INITIATOR and it receives HSRSP.
 | 
						|
    //
 | 
						|
    // In HSv4, INITIATOR is sender and RESPONDER is receiver.
 | 
						|
    // In HSv5, both are sender AND receiver.
 | 
						|
    //
 | 
						|
    // This function will be called only ONCE in this
 | 
						|
    // instance, through either HSREQ or HSRSP.
 | 
						|
 | 
						|
    if (hsv > HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        updateSrtRcvSettings();
 | 
						|
        updateSrtSndSettings();
 | 
						|
    }
 | 
						|
    else if (srt_cmd == SRT_CMD_HSRSP)
 | 
						|
    {
 | 
						|
        // HSv4 INITIATOR is sender
 | 
						|
        updateSrtSndSettings();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // HSv4 RESPONDER is receiver
 | 
						|
        updateSrtRcvSettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::packLostData(CPacket &packet, uint64_t &origintime)
 | 
						|
{
 | 
						|
    // protect m_iSndLastDataAck from updating by ACK processing
 | 
						|
    CGuard ackguard(m_RecvAckLock);
 | 
						|
 | 
						|
    while ((packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0)
 | 
						|
    {
 | 
						|
        const int offset = CSeqNo::seqoff(m_iSndLastDataAck, packet.m_iSeqNo);
 | 
						|
        if (offset < 0)
 | 
						|
        {
 | 
						|
            LOGC(dlog.Error,
 | 
						|
                 log << "IPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " << packet.m_iSeqNo
 | 
						|
                     << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset << ". Continue");
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        int msglen;
 | 
						|
 | 
						|
        const int payload = m_pSndBuffer->readData(&(packet.m_pcData), offset, packet.m_iMsgNo, origintime, msglen);
 | 
						|
        SRT_ASSERT(payload != 0);
 | 
						|
        if (payload == -1)
 | 
						|
        {
 | 
						|
            int32_t seqpair[2];
 | 
						|
            seqpair[0] = packet.m_iSeqNo;
 | 
						|
            seqpair[1] = CSeqNo::incseq(seqpair[0], msglen);
 | 
						|
            sendCtrl(UMSG_DROPREQ, &packet.m_iMsgNo, seqpair, 8);
 | 
						|
 | 
						|
            // only one msg drop request is necessary
 | 
						|
            m_pSndLossList->remove(seqpair[1]);
 | 
						|
 | 
						|
            // skip all dropped packets
 | 
						|
            if (CSeqNo::seqcmp(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])) < 0)
 | 
						|
                m_iSndCurrSeqNo = CSeqNo::incseq(seqpair[1]);
 | 
						|
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        // NOTE: This is just a sanity check. Returning 0 is impossible to happen
 | 
						|
        // in case of retransmission. If the offset was a positive value, then the
 | 
						|
        // block must exist in the old blocks because it wasn't yet cut off by ACK
 | 
						|
        // and has been already recorded as sent (otherwise the peer wouldn't send
 | 
						|
        // back the loss report). May something happen here in case when the send
 | 
						|
        // loss record has been updated by the FASTREXMIT.
 | 
						|
        else if (payload == 0)
 | 
						|
            continue;
 | 
						|
 | 
						|
        // At this point we no longer need the ACK lock,
 | 
						|
        // because we are going to return from the function.
 | 
						|
        // Therefore unlocking in order not to block other threads.
 | 
						|
        ackguard.forceUnlock();
 | 
						|
 | 
						|
        CGuard::enterCS(m_StatsLock);
 | 
						|
        ++m_stats.traceRetrans;
 | 
						|
        ++m_stats.retransTotal;
 | 
						|
        m_stats.traceBytesRetrans += payload;
 | 
						|
        m_stats.bytesRetransTotal += payload;
 | 
						|
        CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
        // Despite the contextual interpretation of packet.m_iMsgNo around
 | 
						|
        // CSndBuffer::readData version 2 (version 1 doesn't return -1), in this particular
 | 
						|
        // case we can be sure that this is exactly the value of PH_MSGNO as a bitset.
 | 
						|
        // So, set here the rexmit flag if the peer understands it.
 | 
						|
        if (m_bPeerRexmitFlag)
 | 
						|
        {
 | 
						|
            packet.m_iMsgNo |= PACKET_SND_REXMIT;
 | 
						|
        }
 | 
						|
 | 
						|
        return payload;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::packData(CPacket &packet, uint64_t &ts_tk)
 | 
						|
{
 | 
						|
    int      payload           = 0;
 | 
						|
    bool     probe             = false;
 | 
						|
    uint64_t origintime        = 0;
 | 
						|
    bool     new_packet_packed = false;
 | 
						|
    bool     filter_ctl_pkt    = false;
 | 
						|
 | 
						|
    int kflg = EK_NOENC;
 | 
						|
 | 
						|
    uint64_t entertime_tk;
 | 
						|
    CTimer::rdtsc(entertime_tk);
 | 
						|
 | 
						|
#if 0 // debug: TimeDiff histogram
 | 
						|
   static int lldiffhisto[23] = {0};
 | 
						|
   static int llnodiff = 0;
 | 
						|
   if (m_ullTargetTime_tk != 0)
 | 
						|
   {
 | 
						|
      int ofs = 11 + ((entertime_tk - m_ullTargetTime_tk)/(int64_t)m_ullCPUFrequency)/1000;
 | 
						|
      if (ofs < 0) ofs = 0;
 | 
						|
      else if (ofs > 22) ofs = 22;
 | 
						|
      lldiffhisto[ofs]++;
 | 
						|
   }
 | 
						|
   else if(m_ullTargetTime_tk == 0)
 | 
						|
   {
 | 
						|
      llnodiff++;
 | 
						|
   }
 | 
						|
   static int callcnt = 0;
 | 
						|
   if (!(callcnt++ % 5000)) {
 | 
						|
      fprintf(stderr, "%6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n",
 | 
						|
        lldiffhisto[0],lldiffhisto[1],lldiffhisto[2],lldiffhisto[3],lldiffhisto[4],lldiffhisto[5],
 | 
						|
        lldiffhisto[6],lldiffhisto[7],lldiffhisto[8],lldiffhisto[9],lldiffhisto[10],lldiffhisto[11]);
 | 
						|
      fprintf(stderr, "%6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n",
 | 
						|
        lldiffhisto[12],lldiffhisto[13],lldiffhisto[14],lldiffhisto[15],lldiffhisto[16],lldiffhisto[17],
 | 
						|
        lldiffhisto[18],lldiffhisto[19],lldiffhisto[20],lldiffhisto[21],lldiffhisto[21],llnodiff);
 | 
						|
   }
 | 
						|
#endif
 | 
						|
    if ((0 != m_ullTargetTime_tk) && (entertime_tk > m_ullTargetTime_tk))
 | 
						|
        m_ullTimeDiff_tk += entertime_tk - m_ullTargetTime_tk;
 | 
						|
 | 
						|
    string reason;
 | 
						|
 | 
						|
    payload = packLostData(packet, origintime);
 | 
						|
    if (payload > 0)
 | 
						|
    {
 | 
						|
        reason = "reXmit";
 | 
						|
    }
 | 
						|
    else if (m_PacketFilter &&
 | 
						|
             m_PacketFilter.packControlPacket(Ref(packet), m_iSndCurrSeqNo, m_pCryptoControl->getSndCryptoFlags()))
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "filter: filter/CTL packet ready - packing instead of data.");
 | 
						|
        payload        = packet.getLength();
 | 
						|
        reason         = "filter";
 | 
						|
        filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set
 | 
						|
 | 
						|
        // Stats
 | 
						|
 | 
						|
        {
 | 
						|
            CGuard lg(m_StatsLock);
 | 
						|
            ++m_stats.sndFilterExtra;
 | 
						|
            ++m_stats.sndFilterExtraTotal;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // If no loss, and no packetfilter control packet, pack a new packet.
 | 
						|
 | 
						|
        // check congestion/flow window limit
 | 
						|
        int cwnd    = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow));
 | 
						|
        int seqdiff = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo));
 | 
						|
        if (cwnd >= seqdiff)
 | 
						|
        {
 | 
						|
            // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the
 | 
						|
            // send buffer. This should be somehow avoided, the crypto flags should be set
 | 
						|
            // together with encrypting, and the packet should be sent as is, when rexmitting.
 | 
						|
            // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field
 | 
						|
            // isn't a useless redundant state copy. If it is, then taking the flags here can be removed.
 | 
						|
            kflg    = m_pCryptoControl->getSndCryptoFlags();
 | 
						|
            payload = m_pSndBuffer->readData(&(packet.m_pcData), packet.m_iMsgNo, origintime, kflg);
 | 
						|
            if (payload)
 | 
						|
            {
 | 
						|
                m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo);
 | 
						|
                // m_pCryptoControl->m_iSndCurrSeqNo = m_iSndCurrSeqNo;
 | 
						|
 | 
						|
                packet.m_iSeqNo = m_iSndCurrSeqNo;
 | 
						|
 | 
						|
                // every 16 (0xF) packets, a packet pair is sent
 | 
						|
                if ((packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0)
 | 
						|
                    probe = true;
 | 
						|
 | 
						|
                new_packet_packed = true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                m_ullTargetTime_tk = 0;
 | 
						|
                m_ullTimeDiff_tk   = 0;
 | 
						|
                ts_tk              = 0;
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(dlog.Debug,
 | 
						|
                  log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow
 | 
						|
                      << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << seqdiff);
 | 
						|
            m_ullTargetTime_tk = 0;
 | 
						|
            m_ullTimeDiff_tk   = 0;
 | 
						|
            ts_tk              = 0;
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        reason = "normal";
 | 
						|
    }
 | 
						|
 | 
						|
    // Normally packet.m_iTimeStamp field is set exactly here,
 | 
						|
    // usually as taken from m_StartTime and current time, unless live
 | 
						|
    // mode in which case it is based on 'origintime' as set during scheduling.
 | 
						|
    // In case when this is a filter control packet, the m_iTimeStamp field already
 | 
						|
    // contains the exactly needed value, and it's a timestamp clip, not a real
 | 
						|
    // timestamp.
 | 
						|
    if (!filter_ctl_pkt)
 | 
						|
    {
 | 
						|
        if (m_bPeerTsbPd)
 | 
						|
        {
 | 
						|
            /*
 | 
						|
             * When timestamp is carried over in this sending stream from a received stream,
 | 
						|
             * it may be older than the session start time causing a negative packet time
 | 
						|
             * that may block the receiver's Timestamp-based Packet Delivery.
 | 
						|
             * XXX Isn't it then better to not decrease it by m_StartTime? As long as it
 | 
						|
             * doesn't screw up the start time on the other side.
 | 
						|
             */
 | 
						|
            if (origintime >= m_stats.startTime)
 | 
						|
                packet.m_iTimeStamp = int(origintime - m_stats.startTime);
 | 
						|
            else
 | 
						|
                packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    packet.m_iID = m_PeerID;
 | 
						|
    packet.setLength(payload);
 | 
						|
 | 
						|
    /* Encrypt if 1st time this packet is sent and crypto is enabled */
 | 
						|
    if (kflg)
 | 
						|
    {
 | 
						|
        // XXX Encryption flags are already set on the packet before calling this.
 | 
						|
        // See readData() above.
 | 
						|
        if (m_pCryptoControl->encrypt(Ref(packet)))
 | 
						|
        {
 | 
						|
            // Encryption failed
 | 
						|
            //>>Add stats for crypto failure
 | 
						|
            ts_tk = 0;
 | 
						|
            LOGC(dlog.Error, log << "ENCRYPT FAILED - packet won't be sent, size=" << payload);
 | 
						|
            return -1; // Encryption failed
 | 
						|
        }
 | 
						|
        payload = packet.getLength(); /* Cipher may change length */
 | 
						|
        reason += " (encrypted)";
 | 
						|
    }
 | 
						|
 | 
						|
    if (new_packet_packed && m_PacketFilter)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "filter: Feeding packet for source clip");
 | 
						|
        m_PacketFilter.feedSource(Ref(packet));
 | 
						|
    }
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr()
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << CONID() << "packData: " << reason << " packet seq=" << packet.m_iSeqNo << " (ACK=" << m_iSndLastAck
 | 
						|
              << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << packet.MessageFlagStr() << ")");
 | 
						|
#endif
 | 
						|
 | 
						|
    // Fix keepalive
 | 
						|
    m_ullLastSndTime_tk = entertime_tk;
 | 
						|
 | 
						|
    considerLegacySrtHandshake(0);
 | 
						|
 | 
						|
    // WARNING: TEV_SEND is the only event that is reported from
 | 
						|
    // the CSndQueue::worker thread. All others are reported from
 | 
						|
    // CRcvQueue::worker. If you connect to this signal, make sure
 | 
						|
    // that you are aware of prospective simultaneous access.
 | 
						|
    updateCC(TEV_SEND, &packet);
 | 
						|
 | 
						|
    // XXX This was a blocked code also originally in UDT. Probably not required.
 | 
						|
    // Left untouched for historical reasons.
 | 
						|
    // Might be possible that it was because of that this is send from
 | 
						|
    // different thread than the rest of the signals.
 | 
						|
    // m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp);
 | 
						|
 | 
						|
    CGuard::enterCS(m_StatsLock);
 | 
						|
    m_stats.traceBytesSent += payload;
 | 
						|
    m_stats.bytesSentTotal += payload;
 | 
						|
    ++m_stats.traceSent;
 | 
						|
    ++m_stats.sentTotal;
 | 
						|
    CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
    if (probe)
 | 
						|
    {
 | 
						|
        // sends out probing packet pair
 | 
						|
        ts_tk = entertime_tk;
 | 
						|
        probe = false;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
#if USE_BUSY_WAITING
 | 
						|
        ts_tk = entertime_tk + m_ullInterval_tk;
 | 
						|
#else
 | 
						|
        if (m_ullTimeDiff_tk >= m_ullInterval_tk)
 | 
						|
        {
 | 
						|
            ts_tk = entertime_tk;
 | 
						|
            m_ullTimeDiff_tk -= m_ullInterval_tk;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ts_tk = entertime_tk + m_ullInterval_tk - m_ullTimeDiff_tk;
 | 
						|
            m_ullTimeDiff_tk = 0;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    m_ullTargetTime_tk = ts_tk;
 | 
						|
 | 
						|
    return payload;
 | 
						|
}
 | 
						|
 | 
						|
// This is a close request, but called from the
 | 
						|
void CUDT::processClose()
 | 
						|
{
 | 
						|
    sendCtrl(UMSG_SHUTDOWN);
 | 
						|
 | 
						|
    m_bShutdown      = true;
 | 
						|
    m_bClosing       = true;
 | 
						|
    m_bBroken        = true;
 | 
						|
    m_iBrokenCounter = 60;
 | 
						|
 | 
						|
    HLOGP(mglog.Debug, "processClose: sent message and set flags");
 | 
						|
 | 
						|
    if (m_bTsbPd)
 | 
						|
    {
 | 
						|
        HLOGP(mglog.Debug, "processClose: lock-and-signal TSBPD");
 | 
						|
        CGuard rl(m_RecvLock);
 | 
						|
        pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
    }
 | 
						|
 | 
						|
    // Signal the sender and recver if they are waiting for data.
 | 
						|
    releaseSynch();
 | 
						|
    // Unblock any call so they learn the connection_broken error
 | 
						|
    s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true);
 | 
						|
 | 
						|
    HLOGP(mglog.Debug, "processClose: triggering timer event to spread the bad news");
 | 
						|
    CTimer::triggerEvent();
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::sendLossReport(const std::vector<std::pair<int32_t, int32_t> > &loss_seqs)
 | 
						|
{
 | 
						|
    typedef vector<pair<int32_t, int32_t> > loss_seqs_t;
 | 
						|
 | 
						|
    vector<int32_t> seqbuffer;
 | 
						|
    seqbuffer.reserve(2 * loss_seqs.size()); // pessimistic
 | 
						|
    for (loss_seqs_t::const_iterator i = loss_seqs.begin(); i != loss_seqs.end(); ++i)
 | 
						|
    {
 | 
						|
        if (i->first == i->second)
 | 
						|
        {
 | 
						|
            seqbuffer.push_back(i->first);
 | 
						|
            HLOGF(mglog.Debug, "lost packet %d: sending LOSSREPORT", i->first);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            seqbuffer.push_back(i->first | LOSSDATA_SEQNO_RANGE_FIRST);
 | 
						|
            seqbuffer.push_back(i->second);
 | 
						|
            HLOGF(mglog.Debug,
 | 
						|
                  "lost packets %d-%d (%d packets): sending LOSSREPORT",
 | 
						|
                  i->first,
 | 
						|
                  i->second,
 | 
						|
                  1 + CSeqNo::seqcmp(i->second, i->first));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!seqbuffer.empty())
 | 
						|
    {
 | 
						|
        sendCtrl(UMSG_LOSSREPORT, NULL, &seqbuffer[0], seqbuffer.size());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::processData(CUnit *in_unit)
 | 
						|
{
 | 
						|
    CPacket &packet = in_unit->m_Packet;
 | 
						|
 | 
						|
    // XXX This should be called (exclusively) here:
 | 
						|
    // m_pRcvBuffer->addLocalTsbPdDriftSample(packet.getMsgTimeStamp());
 | 
						|
    // Just heard from the peer, reset the expiration count.
 | 
						|
    m_iEXPCount = 1;
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
    m_ullLastRspTime_tk = currtime_tk;
 | 
						|
 | 
						|
    // We are receiving data, start tsbpd thread if TsbPd is enabled
 | 
						|
    if (m_bTsbPd && pthread_equal(m_RcvTsbPdThread, pthread_t()))
 | 
						|
    {
 | 
						|
        HLOGP(mglog.Debug, "Spawning TSBPD thread");
 | 
						|
        int st = 0;
 | 
						|
        {
 | 
						|
            ThreadName tn("SRT:TsbPd");
 | 
						|
            st = pthread_create(&m_RcvTsbPdThread, NULL, CUDT::tsbpd, this);
 | 
						|
        }
 | 
						|
        if (st != 0)
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2;
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
    static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"};
 | 
						|
    string                   rexmit_reason;
 | 
						|
#endif
 | 
						|
 | 
						|
    if (pktrexmitflag == 1)
 | 
						|
    {
 | 
						|
        // This packet was retransmitted
 | 
						|
        CGuard::enterCS(m_StatsLock);
 | 
						|
        m_stats.traceRcvRetrans++;
 | 
						|
        CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
#if ENABLE_HEAVY_LOGGING
 | 
						|
        // Check if packet was retransmitted on request or on ack timeout
 | 
						|
        // Search the sequence in the loss record.
 | 
						|
        rexmit_reason = " by ";
 | 
						|
        if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo))
 | 
						|
            rexmit_reason += "REQUEST";
 | 
						|
        else
 | 
						|
            rexmit_reason += "ACK-TMOUT";
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(dlog.Debug,
 | 
						|
          log << CONID() << "processData: RECEIVED DATA: size=" << packet.getLength() << " seq=" << packet.getSeqNo());
 | 
						|
 | 
						|
    updateCC(TEV_RECEIVE, &packet);
 | 
						|
    ++m_iPktCount;
 | 
						|
 | 
						|
    const int pktsz = packet.getLength();
 | 
						|
    // Update time information
 | 
						|
   // XXX Note that this adds the byte size of a packet
 | 
						|
   // of which we don't yet know as to whether this has
 | 
						|
   // carried out some useful data or some excessive data
 | 
						|
   // that will be later discarded.
 | 
						|
   // FIXME: before adding this on the rcv time window,
 | 
						|
   // make sure that this packet isn't going to be
 | 
						|
   // effectively discarded, as repeated retransmission,
 | 
						|
   // for example, burdens the link, but doesn't better the speed.
 | 
						|
    m_RcvTimeWindow.onPktArrival(pktsz);
 | 
						|
 | 
						|
   // Probe the packet pair if needed.
 | 
						|
   // Conditions and any extra data required for the packet
 | 
						|
   // this function will extract and test as needed.
 | 
						|
 | 
						|
    const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0;
 | 
						|
    const bool retransmitted = m_bPeerRexmitFlag && packet.getRexmitFlag();
 | 
						|
 | 
						|
    // Retransmitted and unordered packets do not provide expected measurement.
 | 
						|
    // We expect the 16th and 17th packet to be sent regularly,
 | 
						|
    // otherwise measurement must be rejected.
 | 
						|
    m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted);
 | 
						|
 | 
						|
    CGuard::enterCS(m_StatsLock);
 | 
						|
    m_stats.traceBytesRecv += pktsz;
 | 
						|
    m_stats.bytesRecvTotal += pktsz;
 | 
						|
    ++m_stats.traceRecv;
 | 
						|
    ++m_stats.recvTotal;
 | 
						|
    CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
    typedef vector<pair<int32_t, int32_t> > loss_seqs_t;
 | 
						|
    loss_seqs_t                             filter_loss_seqs;
 | 
						|
    loss_seqs_t                             srt_loss_seqs;
 | 
						|
    vector<CUnit *>                         incoming;
 | 
						|
    bool                                    was_sent_in_order          = true;
 | 
						|
    bool                                    reorder_prevent_lossreport = false;
 | 
						|
 | 
						|
    // If the peer doesn't understand REXMIT flag, send rexmit request
 | 
						|
    // always immediately.
 | 
						|
    int initial_loss_ttl = 0;
 | 
						|
    if (m_bPeerRexmitFlag)
 | 
						|
        initial_loss_ttl = m_iReorderTolerance;
 | 
						|
 | 
						|
    // After introduction of packet filtering, the "recordable loss detection"
 | 
						|
    // does not exactly match the true loss detection. When a FEC filter is
 | 
						|
    // working, for example, then getting one group filled with all packet but
 | 
						|
    // the last one and the FEC control packet, in this special case this packet
 | 
						|
    // won't be notified at all as lost because it will be recovered by the
 | 
						|
    // filter immediately before anyone notices what happened (and the loss
 | 
						|
    // detection for the further functionality is checked only afterwards,
 | 
						|
    // and in this case the immediate recovery makes the loss to not be noticed
 | 
						|
    // at all).
 | 
						|
    //
 | 
						|
    // Because of that the check for losses must happen BEFORE passing the packet
 | 
						|
    // to the filter and before the filter could recover the packet before anyone
 | 
						|
    // notices :)
 | 
						|
 | 
						|
    if (packet.getMsgSeq() != 0) // disregard filter-control packets, their seq may mean nothing
 | 
						|
    {
 | 
						|
        int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo);
 | 
						|
        if (diff > 1)
 | 
						|
        {
 | 
						|
            CGuard lg(m_StatsLock);
 | 
						|
            int    loss = diff - 1; // loss is all that is above diff == 1
 | 
						|
            m_stats.traceRcvLoss += loss;
 | 
						|
            m_stats.rcvLossTotal += loss;
 | 
						|
            uint64_t lossbytes = loss * m_pRcvBuffer->getRcvAvgPayloadSize();
 | 
						|
            m_stats.traceRcvBytesLoss += lossbytes;
 | 
						|
            m_stats.rcvBytesLossTotal += lossbytes;
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " "
 | 
						|
                      << CSeqNo::decseq(packet.m_iSeqNo) << "]");
 | 
						|
        }
 | 
						|
 | 
						|
        if (diff > 0)
 | 
						|
        {
 | 
						|
            // Record if it was further than latest
 | 
						|
            m_iRcvCurrPhySeqNo = packet.m_iSeqNo;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    {
 | 
						|
        // Start of offset protected section
 | 
						|
        // Prevent TsbPd thread from modifying Ack position while adding data
 | 
						|
        // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData()
 | 
						|
        CGuard recvbuf_acklock(m_RcvBufferLock);
 | 
						|
 | 
						|
        // vector<CUnit*> undec_units;
 | 
						|
        if (m_PacketFilter)
 | 
						|
        {
 | 
						|
            // Stuff this data into the filter
 | 
						|
            m_PacketFilter.receive(in_unit, Ref(incoming), Ref(filter_loss_seqs));
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs)
 | 
						|
                      << " loss to report, "
 | 
						|
                      << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF"
 | 
						|
                                                                   : "REPORT ONLY THOSE"));
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Stuff in just one packet that has come in.
 | 
						|
            incoming.push_back(in_unit);
 | 
						|
        }
 | 
						|
 | 
						|
        bool excessive = true; // stays true unless it was successfully added
 | 
						|
 | 
						|
        // Needed for possibly check for needsQuickACK.
 | 
						|
        bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0);
 | 
						|
 | 
						|
        // Loop over all incoming packets that were filtered out.
 | 
						|
        // In case when there is no filter, there's just one packet in 'incoming',
 | 
						|
        // the one that came in the input of this function.
 | 
						|
        for (vector<CUnit *>::iterator i = incoming.begin(); i != incoming.end(); ++i)
 | 
						|
        {
 | 
						|
            CUnit *  u    = *i;
 | 
						|
            CPacket &rpkt = u->m_Packet;
 | 
						|
 | 
						|
            // m_iRcvLastSkipAck is the base sequence number for the receiver buffer.
 | 
						|
            // This is the offset in the buffer; if this is negative, it means that
 | 
						|
            // this sequence is already in the past and the buffer is not interested.
 | 
						|
            // Meaning, this packet will be rejected, even if it could potentially be
 | 
						|
            // one of missing packets in the transmission.
 | 
						|
            int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo);
 | 
						|
 | 
						|
            IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED");
 | 
						|
 | 
						|
            if (offset < 0)
 | 
						|
            {
 | 
						|
                IF_HEAVY_LOGGING(exc_type = "BELATED");
 | 
						|
                uint64_t tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp());
 | 
						|
                uint64_t bltime =
 | 
						|
                    CountIIR(uint64_t(m_stats.traceBelatedTime) * 1000, CTimer::getTime() - tsbpdtime, 0.2);
 | 
						|
 | 
						|
                CGuard::enterCS(m_StatsLock);
 | 
						|
                m_stats.traceBelatedTime = double(bltime) / 1000.0;
 | 
						|
                m_stats.traceRcvBelated++;
 | 
						|
                CGuard::leaveCS(m_StatsLock);
 | 
						|
                HLOGC(mglog.Debug,
 | 
						|
                      log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/"
 | 
						|
                          << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr());
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            const int avail_bufsize = m_pRcvBuffer->getAvailBufSize();
 | 
						|
            if (offset >= avail_bufsize)
 | 
						|
            {
 | 
						|
                // This is already a sequence discrepancy. Probably there could be found
 | 
						|
                // some way to make it continue reception by overriding the sequence and
 | 
						|
                // make a kinda TLKPTDROP, but there has been found no reliable way to do this.
 | 
						|
                if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty())
 | 
						|
                {
 | 
						|
                    // Only in live mode. In File mode this shall not be possible
 | 
						|
                    // because the sender should stop sending in this situation.
 | 
						|
                    // In Live mode this means that there is a gap between the
 | 
						|
                    // lowest sequence in the empty buffer and the incoming sequence
 | 
						|
                    // that exceeds the buffer size. Receiving data in this situation
 | 
						|
                    // is no longer possible and this is a point of no return.
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. offset=" << offset
 | 
						|
                             << " avail=" << avail_bufsize << " ack.seq=" << m_iRcvLastSkipAck
 | 
						|
                             << " pkt.seq=" << rpkt.m_iSeqNo << " rcv-remain=" << m_pRcvBuffer->debugGetSize());
 | 
						|
 | 
						|
                    // This is a scoped lock with AckLock, but for the moment
 | 
						|
                    // when processClose() is called this lock must be taken out,
 | 
						|
                    // otherwise this will cause a deadlock. We don't need this
 | 
						|
                    // lock anymore, and at 'return' it will be unlocked anyway.
 | 
						|
                    recvbuf_acklock.forceUnlock();
 | 
						|
                    processClose();
 | 
						|
                    return -1;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << CONID() << "No room to store incoming packet: offset=" << offset
 | 
						|
                             << " avail=" << avail_bufsize << " ack.seq=" << m_iRcvLastSkipAck
 | 
						|
                             << " pkt.seq=" << rpkt.m_iSeqNo << " rcv-remain=" << m_pRcvBuffer->debugGetSize());
 | 
						|
                    return -1;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            bool adding_successful = true;
 | 
						|
            if (m_pRcvBuffer->addData(*i, offset) < 0)
 | 
						|
            {
 | 
						|
                // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet.
 | 
						|
                // So this packet is "redundant".
 | 
						|
                IF_HEAVY_LOGGING(exc_type = "UNACKED");
 | 
						|
                adding_successful = false;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                IF_HEAVY_LOGGING(exc_type = "ACCEPTED");
 | 
						|
                excessive = false;
 | 
						|
                if (u->m_Packet.getMsgCryptoFlags())
 | 
						|
                {
 | 
						|
                    EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt(Ref(u->m_Packet)) : ENCS_NOTSUP;
 | 
						|
                    if (rc != ENCS_CLEAR)
 | 
						|
                    {
 | 
						|
                        // Could not decrypt
 | 
						|
                        // Keep packet in received buffer
 | 
						|
                        // Crypto flags are still set
 | 
						|
                        // It will be acknowledged
 | 
						|
                        {
 | 
						|
                            CGuard lg(m_StatsLock);
 | 
						|
                            m_stats.traceRcvUndecrypt += 1;
 | 
						|
                            m_stats.traceRcvBytesUndecrypt += pktsz;
 | 
						|
                            m_stats.m_rcvUndecryptTotal += 1;
 | 
						|
                            m_stats.m_rcvBytesUndecryptTotal += pktsz;
 | 
						|
                        }
 | 
						|
 | 
						|
                        // Log message degraded to debug because it may happen very often
 | 
						|
                        HLOGC(dlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data.");
 | 
						|
                        adding_successful = false;
 | 
						|
                        IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED");
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset
 | 
						|
                  << " BUFr=" << avail_bufsize
 | 
						|
                  << " (" << exc_type << "/" << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: "
 | 
						|
                  << packet.MessageFlagStr());
 | 
						|
 | 
						|
            // Decryption should have made the crypto flags EK_NOENC.
 | 
						|
            // Otherwise it's an error.
 | 
						|
            if (adding_successful)
 | 
						|
            {
 | 
						|
                HLOGC(dlog.Debug,
 | 
						|
                      log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo));
 | 
						|
                if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection.
 | 
						|
                {
 | 
						|
                    int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo);
 | 
						|
                    int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo);
 | 
						|
 | 
						|
                    srt_loss_seqs.push_back(make_pair(seqlo, seqhi));
 | 
						|
 | 
						|
                    if (initial_loss_ttl)
 | 
						|
                    {
 | 
						|
                        // pack loss list for (possibly belated) NAK
 | 
						|
                        // The LOSSREPORT will be sent in a while.
 | 
						|
 | 
						|
                        for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i)
 | 
						|
                        {
 | 
						|
                            m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl));
 | 
						|
                        }
 | 
						|
                        HLOGC(mglog.Debug,
 | 
						|
                              log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs)
 | 
						|
                                  << " tolerance: " << initial_loss_ttl);
 | 
						|
                        reorder_prevent_lossreport = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Update the current largest sequence number that has been received.
 | 
						|
            // Or it is a retransmitted packet, remove it from receiver loss list.
 | 
						|
            if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0)
 | 
						|
            {
 | 
						|
                m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                unlose(rpkt); // was BELATED or RETRANSMITTED
 | 
						|
                was_sent_in_order &= 0 != pktrexmitflag;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // This is moved earlier after introducing filter because it shouldn't
 | 
						|
        // be executed in case when the packet was rejected by the receiver buffer.
 | 
						|
        // However now the 'excessive' condition may be true also in case when
 | 
						|
        // a truly non-excessive packet has been received, just it has been temporarily
 | 
						|
        // stored for better times by the filter module. This way 'excessive' is also true,
 | 
						|
        // although the old condition that a packet with a newer sequence number has arrived
 | 
						|
        // or arrived out of order may still be satisfied.
 | 
						|
        if (!incoming_belated && was_sent_in_order)
 | 
						|
        {
 | 
						|
            // Basing on some special case in the packet, it might be required
 | 
						|
            // to enforce sending ACK immediately (earlier than normally after
 | 
						|
            // a given period).
 | 
						|
            if (m_CongCtl->needsQuickACK(packet))
 | 
						|
            {
 | 
						|
                CTimer::rdtsc(m_ullNextACKTime_tk);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (excessive)
 | 
						|
        {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
    } // End of recvbuf_acklock
 | 
						|
 | 
						|
    if (m_bClosing)
 | 
						|
    {
 | 
						|
        // RcvQueue worker thread can call processData while closing (or close while processData)
 | 
						|
        // This race condition exists in the UDT design but the protection against TsbPd thread
 | 
						|
        // (with AckLock) and decryption enlarged the probability window.
 | 
						|
        // Application can crash deep in decrypt stack since crypto context is deleted in close.
 | 
						|
        // RcvQueue worker thread will not necessarily be deleted with this connection as it can be
 | 
						|
        // used by others (socket multiplexer).
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (incoming.empty())
 | 
						|
    {
 | 
						|
        // Treat as excessive. This is when a filter cumulates packets
 | 
						|
        // until the loss is rebuilt, or eats up a filter control packet
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!srt_loss_seqs.empty())
 | 
						|
    {
 | 
						|
        // A loss is detected
 | 
						|
        {
 | 
						|
            // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)?
 | 
						|
            // And probably protect m_FreshLoss as well.
 | 
						|
 | 
						|
            HLOGC(mglog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING.");
 | 
						|
            // if record_loss == false, nothing will be contained here
 | 
						|
            // Insert lost sequence numbers to the receiver loss list
 | 
						|
            CGuard lg(m_RcvLossLock);
 | 
						|
            for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i)
 | 
						|
            {
 | 
						|
                // If loss found, insert them to the receiver loss list
 | 
						|
                m_pRcvLossList->insert(i->first, i->second);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS;
 | 
						|
        if (!reorder_prevent_lossreport && report_recorded_loss)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs));
 | 
						|
            sendLossReport(srt_loss_seqs);
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_bTsbPd)
 | 
						|
        {
 | 
						|
            pthread_mutex_lock(&m_RecvLock);
 | 
						|
            pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
            pthread_mutex_unlock(&m_RecvLock);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Separately report loss records of those reported by a filter.
 | 
						|
    // ALWAYS report whatever has been reported back by a filter. Note that
 | 
						|
    // the filter never reports anything when rexmit fallback level is ALWAYS or NEVER.
 | 
						|
    // With ALWAYS only those are reported that were recorded here by SRT.
 | 
						|
    // With NEVER, nothing is to be reported.
 | 
						|
    if (!filter_loss_seqs.empty())
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs));
 | 
						|
        sendLossReport(filter_loss_seqs);
 | 
						|
 | 
						|
        if (m_bTsbPd)
 | 
						|
        {
 | 
						|
            pthread_mutex_lock(&m_RecvLock);
 | 
						|
            pthread_cond_signal(&m_RcvTsbPdCond);
 | 
						|
            pthread_mutex_unlock(&m_RecvLock);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it.
 | 
						|
 | 
						|
    // PERFORMANCE CONSIDERATIONS:
 | 
						|
    // This list is quite inefficient as a data type and finding the candidate to send UMSG_LOSSREPORT
 | 
						|
    // is linear time. On the other hand, there are some special cases that are important for performance:
 | 
						|
    // - only the first (plus some following) could have had TTL drown to 0
 | 
						|
    // - the only (little likely) possibility that the next-to-first record has TTL=0 is when there was
 | 
						|
    //   a loss range split (due to unlose() of one sequence)
 | 
						|
    // - first found record with TTL>0 means end of "ready to LOSSREPORT" records
 | 
						|
    // So:
 | 
						|
    // All you have to do is:
 | 
						|
    //  - start with first element and continue with next elements, as long as they have TTL=0
 | 
						|
    //    If so, send the loss report and remove this element.
 | 
						|
    //  - Since the first element that has TTL>0, iterate until the end of container and decrease TTL.
 | 
						|
    //
 | 
						|
    // This will be efficient becase the loop to increment one field (without any condition check)
 | 
						|
    // can be quite well optimized.
 | 
						|
 | 
						|
    vector<int32_t> lossdata;
 | 
						|
    {
 | 
						|
        CGuard lg(m_RcvLossLock);
 | 
						|
 | 
						|
        // XXX There was a mysterious crash around m_FreshLoss. When the initial_loss_ttl is 0
 | 
						|
        // (that is, "belated loss report" feature is off), don't even touch m_FreshLoss.
 | 
						|
        if (initial_loss_ttl && !m_FreshLoss.empty())
 | 
						|
        {
 | 
						|
            deque<CRcvFreshLoss>::iterator i = m_FreshLoss.begin();
 | 
						|
 | 
						|
            // Phase 1: take while TTL <= 0.
 | 
						|
            // There can be more than one record with the same TTL, if it has happened before
 | 
						|
            // that there was an 'unlost' (@c unlose) sequence that has split one detected loss
 | 
						|
            // into two records.
 | 
						|
            for (; i != m_FreshLoss.end() && i->ttl <= 0; ++i)
 | 
						|
            {
 | 
						|
                HLOGF(mglog.Debug,
 | 
						|
                      "Packet seq %d-%d (%d packets) considered lost - sending LOSSREPORT",
 | 
						|
                      i->seq[0],
 | 
						|
                      i->seq[1],
 | 
						|
                      CSeqNo::seqoff(i->seq[0], i->seq[1]) + 1);
 | 
						|
                addLossRecord(lossdata, i->seq[0], i->seq[1]);
 | 
						|
            }
 | 
						|
 | 
						|
            // Remove elements that have been processed and prepared for lossreport.
 | 
						|
            if (i != m_FreshLoss.begin())
 | 
						|
            {
 | 
						|
                m_FreshLoss.erase(m_FreshLoss.begin(), i);
 | 
						|
                i = m_FreshLoss.begin();
 | 
						|
            }
 | 
						|
 | 
						|
            if (m_FreshLoss.empty())
 | 
						|
            {
 | 
						|
                HLOGP(mglog.Debug, "NO MORE FRESH LOSS RECORDS.");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                HLOGF(mglog.Debug,
 | 
						|
                      "STILL %" PRIzu " FRESH LOSS RECORDS, FIRST: %d-%d (%d) TTL: %d",
 | 
						|
                      m_FreshLoss.size(),
 | 
						|
                      i->seq[0],
 | 
						|
                      i->seq[1],
 | 
						|
                      1 + CSeqNo::seqoff(i->seq[0], i->seq[1]),
 | 
						|
                      i->ttl);
 | 
						|
            }
 | 
						|
 | 
						|
            // Phase 2: rest of the records should have TTL decreased.
 | 
						|
            for (; i != m_FreshLoss.end(); ++i)
 | 
						|
                --i->ttl;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!lossdata.empty())
 | 
						|
    {
 | 
						|
        sendCtrl(UMSG_LOSSREPORT, NULL, &lossdata[0], lossdata.size());
 | 
						|
    }
 | 
						|
 | 
						|
    // was_sent_in_order means either of:
 | 
						|
    // - packet was sent in order (first if branch above)
 | 
						|
    // - packet was sent as old, but was a retransmitted packet
 | 
						|
 | 
						|
    if (m_bPeerRexmitFlag && was_sent_in_order)
 | 
						|
    {
 | 
						|
        ++m_iConsecOrderedDelivery;
 | 
						|
        if (m_iConsecOrderedDelivery >= 50)
 | 
						|
        {
 | 
						|
            m_iConsecOrderedDelivery = 0;
 | 
						|
            if (m_iReorderTolerance > 0)
 | 
						|
            {
 | 
						|
                m_iReorderTolerance--;
 | 
						|
                CGuard::enterCS(m_StatsLock);
 | 
						|
                m_stats.traceReorderDistance--;
 | 
						|
                CGuard::leaveCS(m_StatsLock);
 | 
						|
                HLOGF(mglog.Debug,
 | 
						|
                      "ORDERED DELIVERY of 50 packets in a row - decreasing tolerance to %d",
 | 
						|
                      m_iReorderTolerance);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/// This function is called when a packet has arrived, which was behind the current
 | 
						|
/// received sequence - that is, belated or retransmitted. Try to remove the packet
 | 
						|
/// from both loss records: the general loss record and the fresh loss record.
 | 
						|
///
 | 
						|
/// Additionally, check - if supported by the peer - whether the "latecoming" packet
 | 
						|
/// has been sent due to retransmission or due to reordering, by checking the rexmit
 | 
						|
/// support flag and rexmit flag itself. If this packet was surely ORIGINALLY SENT
 | 
						|
/// it means that the current network connection suffers of packet reordering. This
 | 
						|
/// way try to introduce a dynamic tolerance by calculating the difference between
 | 
						|
/// the current packet reception sequence and this packet's sequence. This value
 | 
						|
/// will be set to the tolerance value, which means that later packet retransmission
 | 
						|
/// will not be required immediately, but only after receiving N next packets that
 | 
						|
/// do not include the lacking packet.
 | 
						|
/// The tolerance is not increased infinitely - it's bordered by m_iMaxReorderTolerance.
 | 
						|
/// This value can be set in options - SRT_LOSSMAXTTL.
 | 
						|
void CUDT::unlose(const CPacket &packet)
 | 
						|
{
 | 
						|
    CGuard  lg(m_RcvLossLock);
 | 
						|
    int32_t sequence = packet.m_iSeqNo;
 | 
						|
    m_pRcvLossList->remove(sequence);
 | 
						|
 | 
						|
    // Rest of this code concerns only the "belated lossreport" feature.
 | 
						|
 | 
						|
    bool has_increased_tolerance = false;
 | 
						|
    bool was_reordered           = false;
 | 
						|
 | 
						|
    if (m_bPeerRexmitFlag)
 | 
						|
    {
 | 
						|
        // If the peer understands the REXMIT flag, it means that the REXMIT flag is contained
 | 
						|
        // in the PH_MSGNO field.
 | 
						|
 | 
						|
        // The packet is considered coming originally (just possibly out of order), if REXMIT
 | 
						|
        // flag is NOT set.
 | 
						|
        was_reordered = !packet.getRexmitFlag();
 | 
						|
        if (was_reordered)
 | 
						|
        {
 | 
						|
            HLOGF(mglog.Debug, "received out-of-band packet seq %d", sequence);
 | 
						|
 | 
						|
            const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo));
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance);
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
            if (seqdiff > m_iReorderTolerance)
 | 
						|
            {
 | 
						|
                const int new_tolerance = min(seqdiff, m_iMaxReorderTolerance);
 | 
						|
                HLOGF(mglog.Debug,
 | 
						|
                      "Belated by %d seqs - Reorder tolerance %s %d",
 | 
						|
                      seqdiff,
 | 
						|
                      (new_tolerance == m_iReorderTolerance) ? "REMAINS with" : "increased to",
 | 
						|
                      new_tolerance);
 | 
						|
                m_iReorderTolerance = new_tolerance;
 | 
						|
                has_increased_tolerance =
 | 
						|
                    true; // Yes, even if reorder tolerance is already at maximum - this prevents decreasing tolerance.
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug, log << CONID() << "received reXmitted packet seq=" << sequence);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGF(mglog.Debug, "received reXmitted or belated packet seq %d (distinction not supported by peer)", sequence);
 | 
						|
    }
 | 
						|
 | 
						|
    // Don't do anything if "belated loss report" feature is not used.
 | 
						|
    // In that case the FreshLoss list isn't being filled in at all, the
 | 
						|
    // loss report is sent directly.
 | 
						|
    // Note that this condition blocks two things being done in this function:
 | 
						|
    // - remove given sequence from the fresh loss record
 | 
						|
    //   (in this case it's empty anyway)
 | 
						|
    // - decrease current reorder tolerance based on whether packets come in order
 | 
						|
    //   (current reorder tolerance is 0 anyway)
 | 
						|
    if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    size_t i       = 0;
 | 
						|
    int    had_ttl = 0;
 | 
						|
    for (i = 0; i < m_FreshLoss.size(); ++i)
 | 
						|
    {
 | 
						|
        had_ttl = m_FreshLoss[i].ttl;
 | 
						|
        switch (m_FreshLoss[i].revoke(sequence))
 | 
						|
        {
 | 
						|
        case CRcvFreshLoss::NONE:
 | 
						|
            continue; // Not found. Search again.
 | 
						|
 | 
						|
        case CRcvFreshLoss::STRIPPED:
 | 
						|
            goto breakbreak; // Found and the modification is applied. We're done here.
 | 
						|
 | 
						|
        case CRcvFreshLoss::DELETE:
 | 
						|
            // No more elements. Kill it.
 | 
						|
            m_FreshLoss.erase(m_FreshLoss.begin() + i);
 | 
						|
            // Every loss is unique. We're done here.
 | 
						|
            goto breakbreak;
 | 
						|
 | 
						|
        case CRcvFreshLoss::SPLIT:
 | 
						|
            // Oh, this will be more complicated. This means that it was in between.
 | 
						|
            {
 | 
						|
                // So create a new element that will hold the upper part of the range,
 | 
						|
                // and this one modify to be the lower part of the range.
 | 
						|
 | 
						|
                // Keep the current end-of-sequence value for the second element
 | 
						|
                int32_t next_end = m_FreshLoss[i].seq[1];
 | 
						|
 | 
						|
                // seq-1 set to the end of this element
 | 
						|
                m_FreshLoss[i].seq[1] = CSeqNo::decseq(sequence);
 | 
						|
                // seq+1 set to the begin of the next element
 | 
						|
                int32_t next_begin = CSeqNo::incseq(sequence);
 | 
						|
 | 
						|
                // Use position of the NEXT element because insertion happens BEFORE pointed element.
 | 
						|
                // Use the same TTL (will stay the same in the other one).
 | 
						|
                m_FreshLoss.insert(m_FreshLoss.begin() + i + 1,
 | 
						|
                                   CRcvFreshLoss(next_begin, next_end, m_FreshLoss[i].ttl));
 | 
						|
            }
 | 
						|
            goto breakbreak;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Could have made the "return" instruction instead of goto, but maybe there will be something
 | 
						|
    // to add in future, so keeping that.
 | 
						|
breakbreak:;
 | 
						|
 | 
						|
    if (i != m_FreshLoss.size())
 | 
						|
    {
 | 
						|
        HLOGF(mglog.Debug, "sequence %d removed from belated lossreport record", sequence);
 | 
						|
    }
 | 
						|
 | 
						|
    if (was_reordered)
 | 
						|
    {
 | 
						|
        m_iConsecOrderedDelivery = 0;
 | 
						|
        if (has_increased_tolerance)
 | 
						|
        {
 | 
						|
            m_iConsecEarlyDelivery = 0; // reset counter
 | 
						|
        }
 | 
						|
        else if (had_ttl > 2)
 | 
						|
        {
 | 
						|
            ++m_iConsecEarlyDelivery; // otherwise, and if it arrived quite earlier, increase counter
 | 
						|
            HLOGF(mglog.Debug, "... arrived at TTL %d case %d", had_ttl, m_iConsecEarlyDelivery);
 | 
						|
 | 
						|
            // After 10 consecutive
 | 
						|
            if (m_iConsecEarlyDelivery >= 10)
 | 
						|
            {
 | 
						|
                m_iConsecEarlyDelivery = 0;
 | 
						|
                if (m_iReorderTolerance > 0)
 | 
						|
                {
 | 
						|
                    m_iReorderTolerance--;
 | 
						|
                    CGuard::enterCS(m_StatsLock);
 | 
						|
                    m_stats.traceReorderDistance--;
 | 
						|
                    CGuard::leaveCS(m_StatsLock);
 | 
						|
                    HLOGF(mglog.Debug,
 | 
						|
                          "... reached %d times - decreasing tolerance to %d",
 | 
						|
                          m_iConsecEarlyDelivery,
 | 
						|
                          m_iReorderTolerance);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If hasn't increased tolerance, but the packet appeared at TTL less than 2, do nothing.
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::dropFromLossLists(int32_t from, int32_t to)
 | 
						|
{
 | 
						|
    CGuard lg(m_RcvLossLock);
 | 
						|
    m_pRcvLossList->remove(from, to);
 | 
						|
 | 
						|
    HLOGF(mglog.Debug, "TLPKTDROP seq %d-%d (%d packets)", from, to, CSeqNo::seqoff(from, to));
 | 
						|
 | 
						|
    if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    // All code below concerns only "belated lossreport" feature.
 | 
						|
 | 
						|
    // It's highly unlikely that this is waiting to send a belated UMSG_LOSSREPORT,
 | 
						|
    // so treat it rather as a sanity check.
 | 
						|
 | 
						|
    // It's enough to check if the first element of the list starts with a sequence older than 'to'.
 | 
						|
    // If not, just do nothing.
 | 
						|
 | 
						|
    size_t delete_index = 0;
 | 
						|
    for (size_t i = 0; i < m_FreshLoss.size(); ++i)
 | 
						|
    {
 | 
						|
        CRcvFreshLoss::Emod result = m_FreshLoss[i].revoke(from, to);
 | 
						|
        switch (result)
 | 
						|
        {
 | 
						|
        case CRcvFreshLoss::DELETE:
 | 
						|
            delete_index = i + 1; // PAST THE END
 | 
						|
            continue;             // There may be further ranges that are included in this one, so check on.
 | 
						|
 | 
						|
        case CRcvFreshLoss::NONE:
 | 
						|
        case CRcvFreshLoss::STRIPPED:
 | 
						|
            break; // THIS BREAKS ONLY 'switch', not 'for'!
 | 
						|
 | 
						|
        case CRcvFreshLoss::SPLIT:; // This function never returns it. It's only a compiler shut-up.
 | 
						|
        }
 | 
						|
 | 
						|
        break; // Now this breaks also FOR.
 | 
						|
    }
 | 
						|
 | 
						|
    m_FreshLoss.erase(m_FreshLoss.begin(),
 | 
						|
                      m_FreshLoss.begin() + delete_index); // with delete_index == 0 will do nothing
 | 
						|
}
 | 
						|
 | 
						|
// This function, as the name states, should bake a new cookie.
 | 
						|
int32_t CUDT::bake(const sockaddr *addr, int32_t current_cookie, int correction)
 | 
						|
{
 | 
						|
    static unsigned int distractor = 0;
 | 
						|
    unsigned int        rollover   = distractor + 10;
 | 
						|
 | 
						|
    for (;;)
 | 
						|
    {
 | 
						|
        // SYN cookie
 | 
						|
        char clienthost[NI_MAXHOST];
 | 
						|
        char clientport[NI_MAXSERV];
 | 
						|
        getnameinfo(addr,
 | 
						|
                    (m_iIPversion == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6),
 | 
						|
                    clienthost,
 | 
						|
                    sizeof(clienthost),
 | 
						|
                    clientport,
 | 
						|
                    sizeof(clientport),
 | 
						|
                    NI_NUMERICHOST | NI_NUMERICSERV);
 | 
						|
        int64_t timestamp = ((CTimer::getTime() - m_stats.startTime) / 60000000) + distractor -
 | 
						|
                            correction; // secret changes every one minute
 | 
						|
        stringstream cookiestr;
 | 
						|
        cookiestr << clienthost << ":" << clientport << ":" << timestamp;
 | 
						|
        union {
 | 
						|
            unsigned char cookie[16];
 | 
						|
            int32_t       cookie_val;
 | 
						|
        };
 | 
						|
        CMD5::compute(cookiestr.str().c_str(), cookie);
 | 
						|
 | 
						|
        if (cookie_val != current_cookie)
 | 
						|
            return cookie_val;
 | 
						|
 | 
						|
        ++distractor;
 | 
						|
 | 
						|
        // This is just to make the loop formally breakable,
 | 
						|
        // but this is virtually impossible to happen.
 | 
						|
        if (distractor == rollover)
 | 
						|
            return cookie_val;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// XXX This is quite a mystery, why this function has a return value
 | 
						|
// and what the purpose for it was. There's just one call of this
 | 
						|
// function in the whole code and in that call the return value is
 | 
						|
// ignored. Actually this call happens in the CRcvQueue::worker thread,
 | 
						|
// where it makes a response for incoming UDP packet that might be
 | 
						|
// a connection request. Should any error occur in this process, there
 | 
						|
// is no way to "report error" that happened here. Basing on that
 | 
						|
// these values in original UDT code were quite like the values
 | 
						|
// for m_iReqType, they have been changed to URQ_* symbols, which
 | 
						|
// may mean that the intent for the return value was to send this
 | 
						|
// value back as a control packet back to the connector.
 | 
						|
//
 | 
						|
// This function is run when the CRcvQueue object is reading packets
 | 
						|
// from the multiplexer (@c CRcvQueue::worker_RetrieveUnit) and the
 | 
						|
// target socket ID is 0.
 | 
						|
//
 | 
						|
// XXX Make this function return EConnectStatus enum type (extend if needed),
 | 
						|
// and this will be directly passed to the caller.
 | 
						|
SRT_REJECT_REASON CUDT::processConnectRequest(const sockaddr *addr, CPacket &packet)
 | 
						|
{
 | 
						|
    // XXX ASSUMPTIONS:
 | 
						|
    // [[using assert(packet.m_iID == 0)]]
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "processConnectRequest: received a connection request");
 | 
						|
 | 
						|
    if (m_bClosing)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_CLOSE;
 | 
						|
        HLOGC(mglog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing.");
 | 
						|
        return m_RejectReason;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Closing a listening socket only set bBroken
 | 
						|
     * If a connect packet is received while closing it gets through
 | 
						|
     * processing and crashes later.
 | 
						|
     */
 | 
						|
    if (m_bBroken)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_CLOSE;
 | 
						|
        HLOGC(mglog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken.");
 | 
						|
        return m_RejectReason;
 | 
						|
    }
 | 
						|
    size_t exp_len =
 | 
						|
        CHandShake::m_iContentSize; // When CHandShake::m_iContentSize is used in log, the file fails to link!
 | 
						|
 | 
						|
    // NOTE!!! Old version of SRT code checks if the size of the HS packet
 | 
						|
    // is EQUAL to the above CHandShake::m_iContentSize.
 | 
						|
 | 
						|
    // Changed to < exp_len because we actually need that the packet
 | 
						|
    // be at least of a size for handshake, although it may contain
 | 
						|
    // more data, depending on what's inside.
 | 
						|
    if (packet.getLength() < exp_len)
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len
 | 
						|
                  << ")");
 | 
						|
        return m_RejectReason;
 | 
						|
    }
 | 
						|
 | 
						|
    // Dunno why the original UDT4 code only MUCH LATER was checking if the packet was UMSG_HANDSHAKE.
 | 
						|
    // It doesn't seem to make sense to deserialize it into the handshake structure if we are not
 | 
						|
    // sure that the packet contains the handshake at all!
 | 
						|
    if (!packet.isControl(UMSG_HANDSHAKE))
 | 
						|
    {
 | 
						|
        m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
        LOGC(mglog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message");
 | 
						|
        return m_RejectReason;
 | 
						|
    }
 | 
						|
 | 
						|
    CHandShake hs;
 | 
						|
    hs.load_from(packet.m_pcData, packet.getLength());
 | 
						|
 | 
						|
    // XXX MOST LIKELY this hs should be now copied into m_ConnRes field, which holds
 | 
						|
    // the handshake structure sent from the peer (no matter the role or mode).
 | 
						|
    // This should simplify the createSrtHandshake() function which can this time
 | 
						|
    // simply write the crafted handshake structure into m_ConnReq, which needs no
 | 
						|
    // participation of the local handshake and passing it as a parameter through
 | 
						|
    // newConnection() -> acceptAndRespond() -> createSrtHandshake(). This is also
 | 
						|
    // required as a source of the peer's information used in processing in other
 | 
						|
    // structures.
 | 
						|
 | 
						|
    int32_t cookie_val = bake(addr);
 | 
						|
 | 
						|
    HLOGC(mglog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val);
 | 
						|
 | 
						|
    // REQUEST:INDUCTION.
 | 
						|
    // Set a cookie, a target ID, and send back the same as
 | 
						|
    // RESPONSE:INDUCTION.
 | 
						|
    if (hs.m_iReqType == URQ_INDUCTION)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket");
 | 
						|
 | 
						|
        // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp
 | 
						|
        // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies
 | 
						|
        // only the first 4 bytes. Moreover, it's dangerous on some platforms because the char
 | 
						|
        // array need not be aligned to int32_t - changed to union in a hope that using int32_t
 | 
						|
        // inside a union will enforce whole union to be aligned to int32_t.
 | 
						|
        hs.m_iCookie = cookie_val;
 | 
						|
        packet.m_iID = hs.m_iID;
 | 
						|
 | 
						|
        // Ok, now's the time. The listener sets here the version 5 handshake,
 | 
						|
        // even though the request was 4. This is because the old client would
 | 
						|
        // simply return THE SAME version, not even looking into it, giving the
 | 
						|
        // listener false impression as if it supported version 5.
 | 
						|
        //
 | 
						|
        // If the caller was really HSv4, it will simply ignore the version 5 in INDUCTION;
 | 
						|
        // it will respond with CONCLUSION, but with its own set version, which is version 4.
 | 
						|
        //
 | 
						|
        // If the caller was really HSv5, it will RECOGNIZE this version 5 in INDUCTION, so
 | 
						|
        // it will respond with version 5 when sending CONCLUSION.
 | 
						|
 | 
						|
        hs.m_iVersion = HS_VERSION_SRT1;
 | 
						|
 | 
						|
        // Additionally, set this field to a MAGIC value. This field isn't used during INDUCTION
 | 
						|
        // by HSv4 client, HSv5 client can use it to additionally verify that this is a HSv5 listener.
 | 
						|
        // In this field we also advertise the PBKEYLEN value. When 0, it's considered not advertised.
 | 
						|
        hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_iSndCryptoKeyLen);
 | 
						|
        bool whether SRT_ATR_UNUSED = m_iSndCryptoKeyLen != 0;
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processConnectRequest: " << (whether ? "" : "NOT ")
 | 
						|
                  << " Advertising PBKEYLEN - value = " << m_iSndCryptoKeyLen);
 | 
						|
 | 
						|
        size_t size = packet.getLength();
 | 
						|
        hs.store_to(packet.m_pcData, Ref(size));
 | 
						|
        packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
        m_pSndQueue->sendto(addr, packet);
 | 
						|
        return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code.
 | 
						|
    }
 | 
						|
 | 
						|
    // Otherwise this should be REQUEST:CONCLUSION.
 | 
						|
    // Should then come with the correct cookie that was
 | 
						|
    // set in the above INDUCTION, in the HS_VERSION_SRT1
 | 
						|
    // should also contain extra data.
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie...");
 | 
						|
    if (hs.m_iCookie != cookie_val)
 | 
						|
    {
 | 
						|
        cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie
 | 
						|
 | 
						|
        if (hs.m_iCookie != cookie_val)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_RDVCOOKIE;
 | 
						|
            HLOGC(mglog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring.");
 | 
						|
            return m_RejectReason;
 | 
						|
        }
 | 
						|
 | 
						|
        HLOGC(mglog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding.");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding.");
 | 
						|
    }
 | 
						|
 | 
						|
    int32_t id = hs.m_iID;
 | 
						|
 | 
						|
    // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5).
 | 
						|
    // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same
 | 
						|
    // as in the handshake request. When this message is received, the connector side should
 | 
						|
    // switch itself to the version number HS_VERSION_UDT4 and continue the old way (that is,
 | 
						|
    // continue sending URQ_INDUCTION, but this time with HS_VERSION_UDT4).
 | 
						|
 | 
						|
    bool accepted_hs = true;
 | 
						|
 | 
						|
    if (hs.m_iVersion == HS_VERSION_SRT1)
 | 
						|
    {
 | 
						|
        // No further check required.
 | 
						|
        // The m_iType contains handshake extension flags.
 | 
						|
    }
 | 
						|
    else if (hs.m_iVersion == HS_VERSION_UDT4)
 | 
						|
    {
 | 
						|
        // In UDT, and so in older SRT version, the hs.m_iType field should contain
 | 
						|
        // the socket type, although SRT only allowed this field to be UDT_DGRAM.
 | 
						|
        // Older SRT version contained that value in a field, but now that this can
 | 
						|
        // only contain UDT_DGRAM the field itself has been abandoned.
 | 
						|
        // For the sake of any old client that reports version 4 handshake, interpret
 | 
						|
        // this hs.m_iType field as a socket type and check if it's UDT_DGRAM.
 | 
						|
 | 
						|
        // Note that in HSv5 hs.m_iType contains extension flags.
 | 
						|
        if (hs.m_iType != UDT_DGRAM)
 | 
						|
        {
 | 
						|
            m_RejectReason = SRT_REJ_ROGUE;
 | 
						|
            accepted_hs    = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Unsupported version
 | 
						|
        // (NOTE: This includes "version=0" which is a rejection flag).
 | 
						|
        m_RejectReason = SRT_REJ_VERSION;
 | 
						|
        accepted_hs    = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!accepted_hs)
 | 
						|
    {
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason
 | 
						|
                  << " MSG: " << srt_rejectreason_str(m_RejectReason));
 | 
						|
        // mismatch, reject the request
 | 
						|
        hs.m_iReqType = URQFailure(m_RejectReason);
 | 
						|
        size_t size   = CHandShake::m_iContentSize;
 | 
						|
        hs.store_to(packet.m_pcData, Ref(size));
 | 
						|
        packet.m_iID        = id;
 | 
						|
        packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
        m_pSndQueue->sendto(addr, packet);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        SRT_REJECT_REASON error  = SRT_REJ_UNKNOWN;
 | 
						|
        int               result = s_UDTUnited.newConnection(m_SocketID, addr, &hs, packet, Ref(error));
 | 
						|
 | 
						|
        // This is listener - m_RejectReason need not be set
 | 
						|
        // because listener has no functionality of giving the app
 | 
						|
        // insight into rejected callers.
 | 
						|
 | 
						|
        // --->
 | 
						|
        //        (global.) CUDTUnited::updateListenerMux
 | 
						|
        //        (new Socket.) CUDT::acceptAndRespond
 | 
						|
        if (result == -1)
 | 
						|
        {
 | 
						|
            hs.m_iReqType = URQFailure(error);
 | 
						|
            LOGF(mglog.Error, "UU:newConnection: rsp(REJECT): %d - %s", hs.m_iReqType, srt_rejectreason_str(error));
 | 
						|
        }
 | 
						|
 | 
						|
        // CONFUSION WARNING!
 | 
						|
        //
 | 
						|
        // The newConnection() will call acceptAndRespond() if the processing
 | 
						|
        // was successful - IN WHICH CASE THIS PROCEDURE SHOULD DO NOTHING.
 | 
						|
        // Ok, almost nothing - see update_events below.
 | 
						|
        //
 | 
						|
        // If newConnection() failed, acceptAndRespond() will not be called.
 | 
						|
        // Ok, more precisely, the thing that acceptAndRespond() is expected to do
 | 
						|
        // will not be done (this includes sending any response to the peer).
 | 
						|
        //
 | 
						|
        // Now read CAREFULLY. The newConnection() will return:
 | 
						|
        //
 | 
						|
        // - -1: The connection processing failed due to errors like:
 | 
						|
        //       - memory alloation error
 | 
						|
        //       - listen backlog exceeded
 | 
						|
        //       - any error propagated from CUDT::open and CUDT::acceptAndRespond
 | 
						|
        // - 0: The connection already exists
 | 
						|
        // - 1: Connection accepted.
 | 
						|
        //
 | 
						|
        // So, update_events is called only if the connection is established.
 | 
						|
        // Both 0 (repeated) and -1 (error) require that a response be sent.
 | 
						|
        // The CPacket object that has arrived as a connection request is here
 | 
						|
        // reused for the connection rejection response (see URQ_ERROR_REJECT set
 | 
						|
        // as m_iReqType).
 | 
						|
 | 
						|
        // send back a response if connection failed or connection already existed
 | 
						|
        // new connection response should be sent in acceptAndRespond()
 | 
						|
        if (result != 1)
 | 
						|
        {
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req="
 | 
						|
                      << RequestTypeStr(hs.m_iReqType));
 | 
						|
            size_t size = CHandShake::m_iContentSize;
 | 
						|
            hs.store_to(packet.m_pcData, Ref(size));
 | 
						|
            packet.m_iID        = id;
 | 
						|
            packet.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
 | 
						|
            m_pSndQueue->sendto(addr, packet);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // a new connection has been created, enable epoll for write
 | 
						|
            s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    LOGC(mglog.Note, log << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType));
 | 
						|
 | 
						|
    return RejectReasonForURQ(hs.m_iReqType);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::addLossRecord(std::vector<int32_t> &lr, int32_t lo, int32_t hi)
 | 
						|
{
 | 
						|
    if (lo == hi)
 | 
						|
        lr.push_back(lo);
 | 
						|
    else
 | 
						|
    {
 | 
						|
        lr.push_back(lo | LOSSDATA_SEQNO_RANGE_FIRST);
 | 
						|
        lr.push_back(hi);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkACKTimer(uint64_t currtime_tk)
 | 
						|
{
 | 
						|
    if (currtime_tk > m_ullNextACKTime_tk // ACK time has come
 | 
						|
                                          // OR the number of sent packets since last ACK has reached
 | 
						|
                                          // the congctl-defined value of ACK Interval
 | 
						|
                                          // (note that none of the builtin congctls defines ACK Interval)
 | 
						|
        || (m_CongCtl->ACKMaxPackets() > 0 && m_iPktCount >= m_CongCtl->ACKMaxPackets()))
 | 
						|
    {
 | 
						|
        // ACK timer expired or ACK interval is reached
 | 
						|
        sendCtrl(UMSG_ACK);
 | 
						|
        CTimer::rdtsc(currtime_tk);
 | 
						|
 | 
						|
        const int ack_interval_tk =
 | 
						|
            m_CongCtl->ACKTimeout_us() > 0 ? m_CongCtl->ACKTimeout_us() * m_ullCPUFrequency : m_ullACKInt_tk;
 | 
						|
        m_ullNextACKTime_tk = currtime_tk + ack_interval_tk;
 | 
						|
 | 
						|
        m_iPktCount      = 0;
 | 
						|
        m_iLightACKCount = 1;
 | 
						|
    }
 | 
						|
    // Or the transfer rate is so high that the number of packets
 | 
						|
    // have reached the value of SelfClockInterval * LightACKCount before
 | 
						|
    // the time has come according to m_ullNextACKTime_tk. In this case a "lite ACK"
 | 
						|
    // is sent, which doesn't contain statistical data and nothing more
 | 
						|
    // than just the ACK number. The "fat ACK" packets will be still sent
 | 
						|
    // normally according to the timely rules.
 | 
						|
    else if (m_iPktCount >= SELF_CLOCK_INTERVAL * m_iLightACKCount)
 | 
						|
    {
 | 
						|
        // send a "light" ACK
 | 
						|
        sendCtrl(UMSG_ACK, NULL, NULL, SEND_LITE_ACK);
 | 
						|
        ++m_iLightACKCount;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkNAKTimer(uint64_t currtime_tk)
 | 
						|
{
 | 
						|
    // XXX The problem with working NAKREPORT with SRT_ARQ_ONREQ
 | 
						|
    // is not that it would be inappropriate, but because it's not
 | 
						|
    // implemented. The reason for it is that the structure of the
 | 
						|
    // loss list container (m_pRcvLossList) is such that it is expected
 | 
						|
    // that the loss records are ordered by sequence numbers (so
 | 
						|
    // that two ranges sticking together are merged in place).
 | 
						|
    // Unfortunately in case of SRT_ARQ_ONREQ losses must be recorded
 | 
						|
    // as before, but they should not be reported, until confirmed
 | 
						|
    // by the filter. By this reason they appear often out of order
 | 
						|
    // and for adding them properly the loss list container wasn't
 | 
						|
    // prepared. This then requires some more effort to implement.
 | 
						|
    if (!m_bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS)
 | 
						|
        return;
 | 
						|
 | 
						|
    /*
 | 
						|
     * m_bRcvNakReport enables NAK reports for SRT.
 | 
						|
     * Retransmission based on timeout is bandwidth consuming,
 | 
						|
     * not knowing what to retransmit when the only NAK sent by receiver is lost,
 | 
						|
     * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT).
 | 
						|
     */
 | 
						|
    const int loss_len = m_pRcvLossList->getLossLength();
 | 
						|
    SRT_ASSERT(loss_len >= 0);
 | 
						|
 | 
						|
    if (loss_len > 0)
 | 
						|
    {
 | 
						|
        if (currtime_tk <= m_ullNextNAKTime_tk)
 | 
						|
            return; // wait for next NAK time
 | 
						|
 | 
						|
        sendCtrl(UMSG_LOSSREPORT);
 | 
						|
    }
 | 
						|
 | 
						|
    m_ullNextNAKTime_tk = currtime_tk + m_ullNAKInt_tk;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::checkExpTimer(uint64_t currtime_tk)
 | 
						|
{
 | 
						|
    // In UDT the m_bUserDefinedRTO and m_iRTO were in CCC class.
 | 
						|
    // There's nothing in the original code that alters these values.
 | 
						|
 | 
						|
    uint64_t next_exp_time_tk;
 | 
						|
    if (m_CongCtl->RTO())
 | 
						|
    {
 | 
						|
        next_exp_time_tk = m_ullLastRspTime_tk + m_CongCtl->RTO() * m_ullCPUFrequency;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        uint64_t exp_int_tk = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency;
 | 
						|
        if (exp_int_tk < m_iEXPCount * m_ullMinExpInt_tk)
 | 
						|
            exp_int_tk = m_iEXPCount * m_ullMinExpInt_tk;
 | 
						|
        next_exp_time_tk = m_ullLastRspTime_tk + exp_int_tk;
 | 
						|
    }
 | 
						|
 | 
						|
    if (currtime_tk <= next_exp_time_tk)
 | 
						|
        return false;
 | 
						|
 | 
						|
    // ms -> us
 | 
						|
    const int PEER_IDLE_TMO_US = m_iOPT_PeerIdleTimeout * 1000;
 | 
						|
    // Haven't received any information from the peer, is it dead?!
 | 
						|
    // timeout: at least 16 expirations and must be greater than 5 seconds
 | 
						|
    if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) &&
 | 
						|
        (currtime_tk - m_ullLastRspTime_tk > PEER_IDLE_TMO_US * m_ullCPUFrequency))
 | 
						|
    {
 | 
						|
        //
 | 
						|
        // Connection is broken.
 | 
						|
        // UDT does not signal any information about this instead of to stop quietly.
 | 
						|
        // Application will detect this when it calls any UDT methods next time.
 | 
						|
        //
 | 
						|
        HLOGC(mglog.Debug,
 | 
						|
              log << "CONNECTION EXPIRED after " << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "ms");
 | 
						|
        m_bClosing       = true;
 | 
						|
        m_bBroken        = true;
 | 
						|
        m_iBrokenCounter = 30;
 | 
						|
 | 
						|
        // update snd U list to remove this socket
 | 
						|
        m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);
 | 
						|
 | 
						|
        releaseSynch();
 | 
						|
 | 
						|
        // app can call any UDT API to learn the connection_broken error
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true);
 | 
						|
 | 
						|
        CTimer::triggerEvent();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    HLOGC(mglog.Debug,
 | 
						|
          log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) << " elapsed="
 | 
						|
              << ((currtime_tk - m_ullLastRspTime_tk) / m_ullCPUFrequency) << "/" << (+PEER_IDLE_TMO_US) << "us");
 | 
						|
 | 
						|
    ++m_iEXPCount;
 | 
						|
 | 
						|
    /*
 | 
						|
     * (keepalive fix)
 | 
						|
     * duB:
 | 
						|
     * It seems there is confusion of the direction of the Response here.
 | 
						|
     * LastRspTime is supposed to be when receiving (data/ctrl) from peer
 | 
						|
     * as shown in processCtrl and processData,
 | 
						|
     * Here we set because we sent something?
 | 
						|
     *
 | 
						|
     * Disabling this code that prevent quick reconnection when peer disappear
 | 
						|
     */
 | 
						|
    // Reset last response time since we've just sent a heart-beat.
 | 
						|
    // (fixed) m_ullLastRspTime_tk = currtime_tk;
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkRexmitTimer(uint64_t currtime_tk)
 | 
						|
{
 | 
						|
    /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT.
 | 
						|
     *
 | 
						|
     * LATEREXMIT is only used with FileCC.
 | 
						|
     * The mode is triggered when some time has passed since the last ACK from
 | 
						|
     * the receiver, while there is still some unacknowledged data in the sender's buffer,
 | 
						|
     * and the loss list is empty.
 | 
						|
     *
 | 
						|
     * FASTREXMIT is only used with LiveCC.
 | 
						|
     * The mode is triggered if the receiver does not send periodic NAK reports,
 | 
						|
     * when some time has passed since the last ACK from the receiver,
 | 
						|
     * while there is still some unacknowledged data in the sender's buffer.
 | 
						|
     *
 | 
						|
     * In case the above conditions are met, the unacknowledged packets
 | 
						|
     * in the sender's buffer will be added to loss list and retransmitted.
 | 
						|
     */
 | 
						|
 | 
						|
    const uint64_t rtt_syn = (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US);
 | 
						|
    const uint64_t exp_int = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency;
 | 
						|
 | 
						|
    if (currtime_tk <= (m_ullLastRspAckTime_tk + exp_int))
 | 
						|
        return;
 | 
						|
 | 
						|
    // If there is no unacknowledged data in the sending buffer,
 | 
						|
    // then there is nothing to retransmit.
 | 
						|
    if (m_pSndBuffer->getCurrBufSize() <= 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT;
 | 
						|
    const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT;
 | 
						|
 | 
						|
    // If the receiver will send periodic NAK reports, then FASTREXMIT is inactive.
 | 
						|
    // MIND that probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off.
 | 
						|
    if (is_fastrexmit && m_bPeerNakReport)
 | 
						|
        return;
 | 
						|
 | 
						|
    // We need to retransmit only when the data in the sender's buffer was already sent.
 | 
						|
    // Otherwise it might still be sent regulary.
 | 
						|
    bool retransmit = false;
 | 
						|
    // - the sender loss list is empty (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track)
 | 
						|
    if (is_laterexmit && (CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && m_pSndLossList->getLossLength() == 0)
 | 
						|
        retransmit = true;
 | 
						|
 | 
						|
    if (is_fastrexmit && (CSeqNo::seqoff(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0))
 | 
						|
        retransmit = true;
 | 
						|
 | 
						|
    if (retransmit)
 | 
						|
    {
 | 
						|
        // Sender: Insert all the packets sent after last received acknowledgement into the sender loss list.
 | 
						|
        CGuard acklock(m_RecvAckLock); // Protect packet retransmission
 | 
						|
        // Resend all unacknowledged packets on timeout, but only if there is no packet in the loss list
 | 
						|
        const int32_t csn = m_iSndCurrSeqNo;
 | 
						|
        const int     num = m_pSndLossList->insert(m_iSndLastAck, csn);
 | 
						|
        if (num > 0)
 | 
						|
        {
 | 
						|
            CGuard::enterCS(m_StatsLock);
 | 
						|
            m_stats.traceSndLoss += num;
 | 
						|
            m_stats.sndLossTotal += num;
 | 
						|
            CGuard::leaveCS(m_StatsLock);
 | 
						|
 | 
						|
            HLOGC(mglog.Debug,
 | 
						|
                  log << CONID() << "ENFORCED " << (is_laterexmit ? "LATEREXMIT" : "FASTREXMIT")
 | 
						|
                      << " by ACK-TMOUT (scheduling): " << CSeqNo::incseq(m_iSndLastAck) << "-" << csn << " ("
 | 
						|
                      << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    ++m_iReXmitCount;
 | 
						|
 | 
						|
    checkSndTimers(DONT_REGEN_KM);
 | 
						|
    const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT;
 | 
						|
    updateCC(TEV_CHECKTIMER, stage);
 | 
						|
 | 
						|
    // immediately restart transmission
 | 
						|
    m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::checkTimers()
 | 
						|
{
 | 
						|
    // update CC parameters
 | 
						|
    updateCC(TEV_CHECKTIMER, TEV_CHT_INIT);
 | 
						|
    // uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9);
 | 
						|
    // if (m_ullInterval_tk < minint)
 | 
						|
    //   m_ullInterval_tk = minint;
 | 
						|
    // NOTE: This commented-out ^^^ code was commented out in original UDT. Leaving for historical reasons
 | 
						|
 | 
						|
    uint64_t currtime_tk;
 | 
						|
    CTimer::rdtsc(currtime_tk);
 | 
						|
 | 
						|
    // This is a very heavy log, unblock only for temporary debugging!
 | 
						|
#if 0
 | 
						|
    HLOGC(mglog.Debug, log << CONID() << "checkTimers: nextacktime=" << FormatTime(m_ullNextACKTime_tk)
 | 
						|
        << " AckInterval=" << m_iACKInterval
 | 
						|
        << " pkt-count=" << m_iPktCount << " liteack-count=" << m_iLightACKCount);
 | 
						|
#endif
 | 
						|
 | 
						|
    // Check if it is time to send ACK
 | 
						|
    checkACKTimer(currtime_tk);
 | 
						|
 | 
						|
    // Check if it is time to send a loss report
 | 
						|
    checkNAKTimer(currtime_tk);
 | 
						|
 | 
						|
    // Check if the connection is expired
 | 
						|
    if (checkExpTimer(currtime_tk))
 | 
						|
        return;
 | 
						|
 | 
						|
    // Check if FAST or LATE packet retransmission is required
 | 
						|
    checkRexmitTimer(currtime_tk);
 | 
						|
 | 
						|
    //   uint64_t exp_int = (m_iRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) * m_ullCPUFrequency;
 | 
						|
    if (currtime_tk > m_ullLastSndTime_tk + (COMM_KEEPALIVE_PERIOD_US * m_ullCPUFrequency))
 | 
						|
    {
 | 
						|
        sendCtrl(UMSG_KEEPALIVE);
 | 
						|
        HLOGP(mglog.Debug, "KEEPALIVE");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::addEPoll(const int eid)
 | 
						|
{
 | 
						|
    CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock);
 | 
						|
    m_sPollID.insert(eid);
 | 
						|
    CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock);
 | 
						|
 | 
						|
    if (!stillConnected())
 | 
						|
        return;
 | 
						|
 | 
						|
    CGuard::enterCS(m_RecvLock);
 | 
						|
    if (m_pRcvBuffer->isRcvDataReady())
 | 
						|
    {
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true);
 | 
						|
    }
 | 
						|
    CGuard::leaveCS(m_RecvLock);
 | 
						|
 | 
						|
    if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())
 | 
						|
    {
 | 
						|
        s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::removeEPoll(const int eid)
 | 
						|
{
 | 
						|
    // clear IO events notifications;
 | 
						|
    // since this happens after the epoll ID has been removed, they cannot be set again
 | 
						|
    set<int> remove;
 | 
						|
    remove.insert(eid);
 | 
						|
    s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false);
 | 
						|
 | 
						|
    CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock);
 | 
						|
    m_sPollID.erase(eid);
 | 
						|
    CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl)
 | 
						|
{
 | 
						|
    if (evt >= TEV__SIZE)
 | 
						|
        return; // sanity check
 | 
						|
 | 
						|
    m_Slots[evt].push_back(sl);
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::DisconnectSignal(ETransmissionEvent evt)
 | 
						|
{
 | 
						|
    if (evt >= TEV__SIZE)
 | 
						|
        return; // sanity check
 | 
						|
 | 
						|
    m_Slots[evt].clear();
 | 
						|
}
 | 
						|
 | 
						|
void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var)
 | 
						|
{
 | 
						|
    for (std::vector<EventSlot>::iterator i = m_Slots[tev].begin(); i != m_Slots[tev].end(); ++i)
 | 
						|
    {
 | 
						|
        i->emit(tev, var);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes)
 | 
						|
{
 | 
						|
    CUDTSocket *s = s_UDTUnited.locate(u);
 | 
						|
    if (!s || !s->m_pUDT)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    CSndBuffer *b = s->m_pUDT->m_pSndBuffer;
 | 
						|
 | 
						|
    if (!b)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    int bytecount, timespan;
 | 
						|
    int count = b->getCurrBufSize(Ref(bytecount), Ref(timespan));
 | 
						|
 | 
						|
    if (blocks)
 | 
						|
        *blocks = count;
 | 
						|
 | 
						|
    if (bytes)
 | 
						|
        *bytes = bytecount;
 | 
						|
 | 
						|
    return std::abs(timespan);
 | 
						|
}
 | 
						|
 | 
						|
SRT_REJECT_REASON CUDT::rejectReason(SRTSOCKET u)
 | 
						|
{
 | 
						|
    CUDTSocket *s = s_UDTUnited.locate(u);
 | 
						|
    if (!s || !s->m_pUDT)
 | 
						|
        return SRT_REJ_UNKNOWN;
 | 
						|
 | 
						|
    return s->m_pUDT->m_RejectReason;
 | 
						|
}
 | 
						|
 | 
						|
bool CUDT::runAcceptHook(CUDT *acore, const sockaddr *peer, const CHandShake *hs, const CPacket &hspkt)
 | 
						|
{
 | 
						|
    // Prepare the information for the hook.
 | 
						|
 | 
						|
    // We need streamid.
 | 
						|
    char target[MAX_SID_LENGTH + 1];
 | 
						|
    memset(target, 0, MAX_SID_LENGTH + 1);
 | 
						|
 | 
						|
    // Just for a case, check the length.
 | 
						|
    // This wasn't done before, and we could risk memory crash.
 | 
						|
    // In case of error, this will remain unset and the empty
 | 
						|
    // string will be passed as streamid.
 | 
						|
 | 
						|
    int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs->m_iType);
 | 
						|
 | 
						|
    // This tests if there are any extensions.
 | 
						|
    if (hspkt.getLength() > CHandShake::m_iContentSize + 4 && IsSet(ext_flags, CHandShake::HS_EXT_CONFIG))
 | 
						|
    {
 | 
						|
        uint32_t *begin = reinterpret_cast<uint32_t *>(hspkt.m_pcData + CHandShake::m_iContentSize);
 | 
						|
        size_t    size  = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0
 | 
						|
        uint32_t *next  = 0;
 | 
						|
        size_t    length   = size / sizeof(uint32_t);
 | 
						|
        size_t    blocklen = 0;
 | 
						|
 | 
						|
        for (;;) // ONE SHOT, but continuable loop
 | 
						|
        {
 | 
						|
            int cmd = FindExtensionBlock(begin, length, Ref(blocklen), Ref(next));
 | 
						|
 | 
						|
            const size_t bytelen = blocklen * sizeof(uint32_t);
 | 
						|
 | 
						|
            if (cmd == SRT_CMD_SID)
 | 
						|
            {
 | 
						|
                if (!bytelen || bytelen > MAX_SID_LENGTH)
 | 
						|
                {
 | 
						|
                    LOGC(mglog.Error,
 | 
						|
                         log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +MAX_SID_LENGTH
 | 
						|
                             << " - PROTOCOL ERROR, REJECTING");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                // See comment at CUDT::interpretSrtHandshake().
 | 
						|
                memcpy(target, begin + 1, bytelen);
 | 
						|
 | 
						|
                // Un-swap on big endian machines
 | 
						|
                ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
 | 
						|
 | 
						|
                // Nothing more expected from connection block.
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            else if (cmd == SRT_CMD_NONE)
 | 
						|
            {
 | 
						|
                // End of blocks
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Any other kind of message extracted. Search on.
 | 
						|
                length -= (next - begin);
 | 
						|
                begin = next;
 | 
						|
                if (begin)
 | 
						|
                    continue;
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    try
 | 
						|
    {
 | 
						|
        int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs->m_iVersion, peer, target);
 | 
						|
        if (result == -1)
 | 
						|
            return false;
 | 
						|
    }
 | 
						|
    catch (...)
 | 
						|
    {
 | 
						|
        LOGP(mglog.Error, "runAcceptHook: hook interrupted by exception");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 |