mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2760 lines
84 KiB
2760 lines
84 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
|
|
Module Name:
|
|
|
|
socket.cpp
|
|
|
|
Abstract:
|
|
|
|
Implementation of CSocketManager class.
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
10-Jan-1997 DonRyan
|
|
Created.
|
|
|
|
--*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Include files //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "globals.h"
|
|
|
|
#define SOCKET_ISRX 0x01
|
|
#define SOCKET_ISTX 0x02
|
|
#define SOCKET_ISRXTX (SOCKET_ISRX | SOCKET_ISTX)
|
|
|
|
#define DBG_DWKIND 1
|
|
|
|
const char g_sPolicyLocator[] = "APP=MICROSOFT TAPI,VER=3.0";
|
|
const char g_sAppName[] = "MICROSOFT TAPI";
|
|
|
|
#define MAX_PROVIDERSPECIFIC_BUFFER \
|
|
(sizeof(RSVP_RESERVE_INFO) + \
|
|
sizeof(FLOWDESCRIPTOR) + \
|
|
(sizeof(RSVP_FILTERSPEC)*MAX_FILTERS) + \
|
|
sizeof(RSVP_POLICY_INFO) + \
|
|
RSVP_POLICY_HDR_LEN + \
|
|
(IDPE_ATTR_HDR_LEN * 2) + \
|
|
MAX_QOS_NAME + \
|
|
sizeof(g_sPolicyLocator) + 4 + \
|
|
sizeof(g_sAppName) + 4 + \
|
|
sizeof(",SAPP=UNKNOWN,SAPP="))
|
|
|
|
|
|
DWORD AddQosAppID(
|
|
IN OUT char *pAppIdBuf,
|
|
IN WORD wBufLen,
|
|
IN const char *szPolicyLocator,
|
|
IN const char *szAppName,
|
|
IN const char *szAppClass,
|
|
IN char *szQosName
|
|
);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// CSocketManager implementation //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
CSocketManager::CSocketManager(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor for CSocketManager class.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns an HRESULT value.
|
|
|
|
--*/
|
|
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CSocketManager::CSocketManager")
|
|
));
|
|
|
|
// initialize linked list of sockets
|
|
InitializeListHead(&m_SharedSockets);
|
|
}
|
|
|
|
|
|
CSocketManager::~CSocketManager(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for CSocketManager class.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns an HRESULT value.
|
|
|
|
--*/
|
|
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CSocketManager::~CSocketManager")
|
|
));
|
|
|
|
// check for unaccounted for sockets
|
|
if (!IsListEmpty(&m_SharedSockets)) {
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::~CSocketManager: Shared sockets still open")
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
CSocketManager::GetSocket(
|
|
SOCKET *pSocket,
|
|
struct sockaddr_in *pAddr,
|
|
struct sockaddr_in *pLocalAddr,
|
|
DWORD dwScope,
|
|
DWORD dwKind,
|
|
WSAPROTOCOL_INFO *pProtocolInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a socket from an address structure.
|
|
|
|
Arguments:
|
|
|
|
pSocket - pointer to socket which will be filled in.
|
|
|
|
pAddr - pointer to destination address structure.
|
|
|
|
pLocalAddr - pointer to local address structure
|
|
|
|
dwScope - multicast scope used for sending.
|
|
|
|
dwKind - if true, socket is just for sending.
|
|
|
|
pProtocolInfo - specify the protocol to be used
|
|
|
|
Return Values:
|
|
|
|
Returns error code from WSAGetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CSocketManager::GetSocket")
|
|
));
|
|
|
|
// object lock to this object
|
|
CAutoLock LockThis(pStateLock());
|
|
|
|
// initialize
|
|
DWORD dwErr = NOERROR;
|
|
|
|
BOOL fReuse;
|
|
|
|
/////////////////////////////////////////////////////
|
|
// If a protocol has been specified, use WSASocket(),
|
|
// otherwise use socket()
|
|
/////////////////////////////////////////////////////
|
|
|
|
SOCKET NewSocket;
|
|
|
|
int Flags = WSA_FLAG_OVERLAPPED;
|
|
|
|
if (pProtocolInfo) {
|
|
|
|
if (IS_MULTICAST(pAddr->sin_addr.s_addr))
|
|
Flags |= WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF;
|
|
}
|
|
|
|
NewSocket = WSASocket(AF_INET, SOCK_DGRAM, 0, pProtocolInfo, 0, Flags);
|
|
|
|
// validate socket handle returned
|
|
if (NewSocket == INVALID_SOCKET) {
|
|
|
|
// obtain last error
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSocket: failed %d"),
|
|
dwErr
|
|
));
|
|
|
|
goto cleanup; // bail...
|
|
}
|
|
|
|
struct sockaddr_in LocalAddr;
|
|
|
|
// initialize
|
|
LocalAddr.sin_family = AF_INET;
|
|
LocalAddr.sin_addr = pLocalAddr->sin_addr;
|
|
//INADDR_ANY;
|
|
|
|
// determine whether we need to specify particular port
|
|
LocalAddr.sin_port =
|
|
(dwKind & SOCKET_MASK_RECV)? pAddr->sin_port : htons(0);
|
|
|
|
// set socket options required before binding
|
|
//if (IS_MULTICAST(pAddr->sin_addr.s_addr)) {
|
|
|
|
fReuse = TRUE;
|
|
|
|
if (setsockopt(
|
|
NewSocket,
|
|
SOL_SOCKET,
|
|
SO_REUSEADDR,
|
|
(PCHAR)&fReuse,
|
|
sizeof(fReuse)
|
|
) == SOCKET_ERROR) {
|
|
|
|
// obtain last error
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("Could not modify REUSEADDR: %d"),
|
|
dwErr
|
|
));
|
|
|
|
goto cleanup; // bail...
|
|
}
|
|
|
|
// bind rtp socket to the local address specified
|
|
if (bind(NewSocket, (sockaddr *)&LocalAddr, sizeof(LocalAddr))) {
|
|
|
|
// obtain last error
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("bind returned %d"),
|
|
dwErr
|
|
));
|
|
|
|
goto cleanup; // bail...
|
|
}
|
|
|
|
// set socket options required after binding
|
|
|
|
// receiving?
|
|
if ( (dwKind & SOCKET_MASK_RECV) ) {
|
|
|
|
if (IS_MULTICAST(pAddr->sin_addr.s_addr)) {
|
|
|
|
struct ip_mreq mreq;
|
|
|
|
// initialize multicast group address
|
|
mreq.imr_multiaddr.s_addr = pAddr->sin_addr.s_addr;
|
|
mreq.imr_interface.s_addr = INADDR_ANY;
|
|
|
|
// join multicast group
|
|
if(setsockopt(NewSocket,
|
|
IPPROTO_IP,
|
|
IP_ADD_MEMBERSHIP,
|
|
(char*)&mreq,
|
|
sizeof(mreq)
|
|
) == SOCKET_ERROR) {
|
|
|
|
// obtain last error
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("Could not join multicast group %d"),
|
|
dwErr
|
|
));
|
|
|
|
goto cleanup; // bail...
|
|
}
|
|
}
|
|
}
|
|
|
|
// transmitting?
|
|
if ( (dwKind & SOCKET_MASK_SEND) ) {
|
|
|
|
// set ttl
|
|
if (setsockopt(
|
|
NewSocket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_TTL,
|
|
(PCHAR)&dwScope,
|
|
sizeof(dwScope)
|
|
) == SOCKET_ERROR) {
|
|
|
|
// obtain last error
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("Could not modify time-to-live %d"),
|
|
dwErr
|
|
));
|
|
|
|
goto cleanup; // bail...
|
|
}
|
|
}
|
|
|
|
// copy new socket
|
|
*pSocket = NewSocket;
|
|
|
|
return NOERROR;
|
|
|
|
cleanup:
|
|
|
|
// see if we created socket
|
|
if (NewSocket != INVALID_SOCKET) {
|
|
|
|
// make sure socket is closed
|
|
if (closesocket(NewSocket)) {
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("closesocket returned %d"),
|
|
WSAGetLastError()
|
|
));
|
|
}
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CSocketManager::ReleaseSocket(
|
|
SOCKET Socket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases a socket.
|
|
|
|
Arguments:
|
|
|
|
Socket - socket to release.
|
|
|
|
Return Values:
|
|
|
|
Returns error code from WSAGetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CSocketManager::ReleaseSocket")
|
|
));
|
|
|
|
// object lock to this object
|
|
CAutoLock LockThis(pStateLock());
|
|
|
|
DWORD dwErr = NOERROR;
|
|
|
|
// see if we created socket
|
|
if (Socket != INVALID_SOCKET) {
|
|
|
|
// make sure socket closed
|
|
if (closesocket(Socket)) {
|
|
|
|
// retrieve error code
|
|
dwErr = WSAGetLastError();
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP2,
|
|
TEXT("closesocket returned %d"),
|
|
dwErr
|
|
));
|
|
}
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
/* Sockets are shared IF the source and destination address/port
|
|
* match, AND the cookie matches AND the QOS state matches.
|
|
*
|
|
* They are matched provided the max number of receiver(s) and/or
|
|
* sender(s) hasn't been reached.
|
|
*
|
|
* local remote cookie local Addr remote Addr QOS enabled
|
|
* ======= ======= ======= =========== =========== =========
|
|
* Receiver: RlocRTP 0 RCookie RlocAddr RremAddr RQOSstate
|
|
* Sender: 0 SremRTP SCookie SlocAddr SremAddr SQOSstate
|
|
*
|
|
* The RTP sockets for sender and receiver will be matched provided
|
|
* ALL the colums match, the wildcard value (0) matches anything.
|
|
*
|
|
* The RTCP sockets will be matched following the same rules, except
|
|
* that in local and remote port, I will have the RTCP ports:
|
|
*
|
|
* local remote cookie local Addr remote Addr QOS enabled
|
|
* ======= ======= ======= =========== =========== =========
|
|
* Receiver: RlocRTCP RremRTCP RCookie RlocAddr RremAddr RQOSstate
|
|
* Sender: SlocRTCP SremRTCP SCookie SlocAddr SremAddr SQOSstate
|
|
*
|
|
* The cookie is constructed using the local and remote RTCP ports
|
|
*
|
|
* For a sender and a receiver that should belong to the same RTP/RTCP
|
|
* session, the session may end having 2 or 3 sockets depending on the
|
|
* order on which the receiver and sender are started, and depending
|
|
* on the use of wildcard ports (port 0 is the wildcard value).
|
|
*
|
|
* The different scenarios are as follow:
|
|
*
|
|
* 1. If wildcard is not used, and since the beggining both, receiver
|
|
* and sender, receive the right local and remote ports (which will be
|
|
* the same for the receiver and sender), then the sockets will be
|
|
* shared no matter the order on which the receiver and sender
|
|
* starts. The RTP/RTCP session will have 2 sockets.
|
|
*
|
|
* 2. Wildcard ports are used, i.e. the sender specify a local port 0,
|
|
* and the receiver specifies a remote port 0, and the sender starts
|
|
* first. In this case, as the socket needs to be bound (QOS), the
|
|
* local port is system assigned, when the receiver is started,
|
|
* everything will match except the local ports, so a new socket will
|
|
* be created. The RTP/RTCP session will have 3 sockets.
|
|
*
|
|
* 3. Wildcard ports are used, i.e. the sender specify a local port 0,
|
|
* and the receiver specifies a remote port 0, and the receiver starts
|
|
* first. In this case, when the sender starts with a wildcard local
|
|
* port, everything will match and the socket will be shared for the
|
|
* receiver and sender. The RTP/RTCP session will have 2 sockets.
|
|
*
|
|
* The RTP/RTCP session is shared based on the 3 sockets (RTPRecv,
|
|
* RTPSend, RTCP), in some cases of course RTPRecv == RTPSend. */
|
|
|
|
DWORD
|
|
CSocketManager::GetSharedSocket(
|
|
CShSocket **ppCShSocket,
|
|
long *pMaxShare,
|
|
DWORD cookie,
|
|
DWORD *pAddr,
|
|
WORD *pPort,
|
|
DWORD dwScope,
|
|
DWORD dwKind,
|
|
WSAPROTOCOL_INFO *pProtocolInfo,
|
|
DWORD dwMaxFilters,
|
|
CRtpSession *pCRtpSession
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a shared socket from an address structure.
|
|
|
|
Arguments:
|
|
|
|
pAddr - pointer to local/remote address.
|
|
|
|
pPort - pointer to local/remote port.
|
|
|
|
pSocket - pointer to socket which will be filled in.
|
|
|
|
dwScope - multicast scope used for sending.
|
|
|
|
Return Values:
|
|
|
|
Returns error code from WSAGetLastError.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!ppCShSocket || !pAddr[LOCAL] || !pAddr[REMOTE]) {
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"failed: NULL pointer")
|
|
));
|
|
return(E_POINTER);
|
|
}
|
|
|
|
char remaddr[RTPNTOASIZE];
|
|
char locaddr[RTPNTOASIZE];
|
|
|
|
RtpNtoA(pAddr[LOCAL], locaddr);
|
|
RtpNtoA(pAddr[REMOTE], remaddr);
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: TTL:%d "
|
|
"Local:%s/%d Remote:%s/%d Get(%d,%d), "
|
|
"Max(%d,%d), Cook(%d,%d), Ini(%s,%s), QOS(%d,%d)"),
|
|
dwScope,
|
|
locaddr, ntohs(pPort[LOCAL]),
|
|
remaddr, ntohs(pPort[REMOTE]),
|
|
(dwKind & SOCKET_MASK_RECV)? 1:0,
|
|
(dwKind & SOCKET_MASK_SEND)? 1:0,
|
|
pMaxShare[0], pMaxShare[1],
|
|
ntohs((WORD)(cookie & 0xffff)), ntohs((WORD)(cookie >> 16)),
|
|
(dwKind & SOCKET_MASK_INIT_RECV)? "R":"-",
|
|
(dwKind & SOCKET_MASK_INIT_SEND)? "S":"-",
|
|
((dwKind & SOCKET_MASK_QOS_SES) != 0),
|
|
((dwKind & SOCKET_MASK_QOS_RQ) != 0)
|
|
));
|
|
|
|
if ( ((struct in_addr *)&pAddr[REMOTE])->s_addr == INADDR_ANY ) {
|
|
// I may add a check against the local IP address(es)
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"failed: remote address == INADDR_ANY")
|
|
));
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
*ppCShSocket = NULL;
|
|
|
|
if (!(dwKind & SOCKET_MASK_RS)) {
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"failed: no kind specified")
|
|
));
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
// object lock to this object
|
|
CAutoLock LockThis(pStateLock());
|
|
|
|
PLIST_ENTRY pLE;
|
|
CShSocket *pCShSocket;
|
|
|
|
BOOL found = FALSE;
|
|
|
|
int count;
|
|
// loop through shared sockets
|
|
for(pLE = m_SharedSockets.Flink, count = 0;
|
|
!found && (pLE != &m_SharedSockets);
|
|
pLE = pLE->Flink, count++) {
|
|
|
|
// obtain shared socket entry from list entry
|
|
pCShSocket = CONTAINING_RECORD(pLE, CShSocket, Link);
|
|
|
|
// check cookie
|
|
if (cookie != pCShSocket->GetCookie())
|
|
continue; // do not match
|
|
|
|
// find out if local and remote address/port match with
|
|
// current, take into account wildcard values
|
|
// (in this case that is 0)
|
|
DWORD match = 0;
|
|
DWORD match_bit = 0x1;
|
|
|
|
for(DWORD p = LOCAL; p <= REMOTE; p++) {
|
|
|
|
// port
|
|
if ( (p == REMOTE) && (dwKind & SOCKET_MASK_RTCPMATCH) ) {
|
|
match |= match_bit; /* force remote port to match
|
|
* (used for RTCP) */
|
|
} else {
|
|
if (!pCShSocket->GetPort(p) || !pPort[p])
|
|
match |= match_bit; // wildcard port match
|
|
else if (pCShSocket->GetPort(p) == pPort[p])
|
|
match |= match_bit; // same port match
|
|
else
|
|
break; // doesn't match, no need to continue
|
|
}
|
|
match_bit <<= 1;
|
|
|
|
// address
|
|
if (!pCShSocket->GetAddress(p) || !pAddr[p])
|
|
match |= match_bit; // wildcard address match
|
|
else if (pCShSocket->GetAddress(p) == pAddr[p])
|
|
match |= match_bit; // same address match
|
|
else
|
|
break; // doesn't match, no need to continue
|
|
match_bit <<= 1;
|
|
}
|
|
|
|
if (match == 0xf) {
|
|
// local and remote address/port DO match
|
|
found = TRUE;
|
|
|
|
for(DWORD k = SOCKET_FIRST; k < SOCKET_LAST; k++) {
|
|
if ( ( (dwKind & SOCKET_MASK(k)) &&
|
|
|
|
( (pCShSocket->GetRefCount(k) >= pMaxShare[k]) ||
|
|
(pCShSocket->GetRefCount(k) >=
|
|
pCShSocket->GetMaxCount(k)) ) )
|
|
|
|
||
|
|
|
|
((dwKind & SOCKET_MASK_QOS_SES) !=
|
|
(pCShSocket->GetKind() & SOCKET_MASK_QOS_SES)) ) {
|
|
|
|
// This kind (recv/send) already exists
|
|
#if defined(DEBUG)
|
|
DWORD addr;
|
|
|
|
addr = pCShSocket->GetAddress(LOCAL);
|
|
RtpNtoA(addr, locaddr);
|
|
|
|
addr = pCShSocket->GetAddress(REMOTE);
|
|
RtpNtoA(addr, remaddr);
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"Full socket %d:%s/%d-%s/%d "
|
|
"Cook(%d, %d), "
|
|
"Cur(%d,%d), Lim(%d,%d), "
|
|
"Ini(%s,%s), "
|
|
"QOS(%d,%d), Ctx(0x%X,0x%X)"
|
|
),
|
|
pCShSocket->GetShSocket(),
|
|
locaddr,
|
|
ntohs(pCShSocket->GetPort(LOCAL)),
|
|
remaddr,
|
|
ntohs(pCShSocket->GetPort(REMOTE)),
|
|
ntohs((WORD)(cookie & 0xffff)),
|
|
ntohs((WORD)(cookie >> 16)),
|
|
pCShSocket->GetRefCount(SOCKET_RECV),
|
|
pCShSocket->GetRefCount(SOCKET_SEND),
|
|
pCShSocket->GetMaxCount(SOCKET_RECV),
|
|
pCShSocket->GetMaxCount(SOCKET_SEND),
|
|
(pCShSocket->GetKind() & SOCKET_MASK_INIT_RECV) ?
|
|
"R":"-",
|
|
(pCShSocket->GetKind() & SOCKET_MASK_INIT_SEND) ?
|
|
"S":"-",
|
|
pCShSocket->IsQOSSession(),
|
|
pCShSocket->IsQOSEnabled(),
|
|
pCShSocket->m_pCRtpSession[SOCKET_RECV],
|
|
pCShSocket->m_pCRtpSession[SOCKET_SEND]
|
|
));
|
|
#endif
|
|
// If multicast, create a new socket,
|
|
// if unicast, fail!
|
|
// Actually do not fail in unicast either
|
|
|
|
// In both cases create a new socket
|
|
found = FALSE;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
|
|
// initialize wildcards to right value
|
|
|
|
for(DWORD p = LOCAL; p <= REMOTE; p++) {
|
|
// wildcard port
|
|
if (!pCShSocket->GetPort(p)) {
|
|
pCShSocket->SetPort(p, pPort[p]);
|
|
}
|
|
|
|
// wildcard address
|
|
if (!pCShSocket->GetAddress(p)) {
|
|
pCShSocket->SetAddress(p, pAddr[p]);
|
|
}
|
|
}
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"requested socket %d already created."),
|
|
pCShSocket->GetShSocket()
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
// Allocate new shared socket structure
|
|
pCShSocket = new CShSocket(pAddr[REMOTE],
|
|
pMaxShare,
|
|
pProtocolInfo,
|
|
dwMaxFilters,
|
|
&hr);
|
|
if (!pCShSocket) {
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"out of memory: %d"),
|
|
GetLastError()
|
|
));
|
|
return(E_FAIL);
|
|
} else if (FAILED(hr)) {
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"failed: 0x%X"),
|
|
hr
|
|
));
|
|
pCShSocket->CloseSocket();
|
|
delete pCShSocket;
|
|
return(E_FAIL);
|
|
} else {
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"New socket created: %d"),
|
|
pCShSocket->GetShSocket()
|
|
));
|
|
}
|
|
|
|
// Save local and remote address/port (including wildcards)
|
|
for(DWORD p = LOCAL; p <= REMOTE; p++) {
|
|
// port
|
|
pCShSocket->SetPort(p, pPort[p]);
|
|
|
|
// address
|
|
pCShSocket->SetAddress(p, pAddr[p]);
|
|
}
|
|
|
|
pCShSocket->SetCookie(cookie);
|
|
|
|
// Insert to list of shared sockets
|
|
InsertHeadList(&m_SharedSockets, &pCShSocket->Link);
|
|
|
|
pCShSocket->SetKind(dwKind & SOCKET_MASK_QOS_SES);
|
|
pCShSocket->SetKind(dwKind & SOCKET_MASK_QOS_RQ);
|
|
}
|
|
|
|
// Now some final socket options may be requiered
|
|
|
|
int errorR = 0;
|
|
|
|
// Set socket kind and add ref count for that kind
|
|
if (dwKind & SOCKET_MASK_RECV) {
|
|
pCShSocket->AddRefCount(SOCKET_RECV);
|
|
pCShSocket->SetKind(SOCKET_MASK_RECV);
|
|
}
|
|
|
|
// Do this just once per socket per receiver
|
|
if ( (dwKind & SOCKET_MASK_INIT_RECV) &&
|
|
!pCShSocket->GetInit(SOCKET_MASK_INIT_RECV) ) {
|
|
|
|
// Mark socket as initialized for RECV
|
|
pCShSocket->SetInit(SOCKET_MASK_INIT_RECV);
|
|
|
|
if (!errorR && !pCShSocket->GetInit(SOCKET_MASK_INIT_BIND)) {
|
|
pCShSocket->SetInit(SOCKET_MASK_INIT_BIND);
|
|
|
|
SOCKADDR_IN localaddr;
|
|
ZeroMemory(&localaddr, sizeof(localaddr));
|
|
SOCKET sock = pCShSocket->GetShSocket();
|
|
DWORD addr;
|
|
|
|
addr = pCShSocket->GetAddress(LOCAL);
|
|
localaddr.sin_family = AF_INET;
|
|
localaddr.sin_addr = *(struct in_addr *) &addr;
|
|
localaddr.sin_port = pCShSocket->GetPort(LOCAL);
|
|
|
|
// bind rtp socket to the local address specified
|
|
if (bind(sock, (SOCKADDR *)&localaddr, sizeof(localaddr))) {
|
|
|
|
// obtain last error
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"RECV bind(%d) port: %d failed: %d"),
|
|
sock, ntohs(localaddr.sin_port), WSAGetLastError()
|
|
));
|
|
|
|
errorR++;
|
|
} else if (!pCShSocket->GetPort(LOCAL)) {
|
|
/* if local port was assigned by the system, we need
|
|
* to update its value, we don't want the wildcard (0)
|
|
* to remain, because if it does, the socket may be
|
|
* erroneously shared with another socket requesting
|
|
* an specific local port */
|
|
int localaddrlen = sizeof(localaddr);
|
|
|
|
if (!getsockname(sock,
|
|
(struct sockaddr *)&localaddr,
|
|
&localaddrlen)) {
|
|
pCShSocket->SetPort(LOCAL, localaddr.sin_port);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!errorR) {
|
|
if (IS_MULTICAST(pAddr[REMOTE])) {
|
|
// Join the group for receivers
|
|
|
|
SOCKADDR_IN joinaddr;
|
|
ZeroMemory(&joinaddr, sizeof(joinaddr));
|
|
DWORD addr;
|
|
|
|
addr = pCShSocket->GetAddress(REMOTE);
|
|
joinaddr.sin_family = AF_INET;
|
|
joinaddr.sin_addr = *(struct in_addr *) &addr;
|
|
joinaddr.sin_port = pCShSocket->GetPort(REMOTE);
|
|
|
|
if (WSAJoinLeaf(pCShSocket->GetShSocket(),
|
|
(const struct sockaddr *)&joinaddr,
|
|
sizeof(joinaddr),
|
|
NULL, NULL, NULL, NULL,
|
|
JL_RECEIVER_ONLY) == INVALID_SOCKET) {
|
|
|
|
errorR++;
|
|
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"WSAJoinLeaf(RECEIVER) failed: %d"),
|
|
WSAGetLastError()
|
|
));
|
|
|
|
} else {
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"WSAJoinLeaf(RECEIVER) succeded")
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int errorS = 0;
|
|
|
|
// Set socket kind and add ref count for that kind
|
|
if (dwKind & SOCKET_MASK_SEND) {
|
|
pCShSocket->AddRefCount(SOCKET_SEND);
|
|
pCShSocket->SetKind(SOCKET_MASK_SEND);
|
|
}
|
|
|
|
// Do this just once per socket per sender
|
|
if ( (dwKind & SOCKET_MASK_INIT_SEND) &&
|
|
!pCShSocket->GetInit(SOCKET_MASK_INIT_SEND) ) {
|
|
|
|
// Mark socket as initialized for SEND
|
|
pCShSocket->SetInit(SOCKET_MASK_INIT_SEND);
|
|
|
|
if (!errorS && !pCShSocket->GetInit(SOCKET_MASK_INIT_BIND)) {
|
|
|
|
pCShSocket->SetInit(SOCKET_MASK_INIT_BIND);
|
|
|
|
SOCKADDR_IN localaddr;
|
|
ZeroMemory(&localaddr, sizeof(localaddr));
|
|
SOCKET sock = pCShSocket->GetShSocket();
|
|
DWORD addr;
|
|
|
|
addr = pCShSocket->GetAddress(LOCAL);
|
|
localaddr.sin_family = AF_INET;
|
|
localaddr.sin_addr = *(struct in_addr *) &addr;
|
|
localaddr.sin_port = pCShSocket->GetPort(LOCAL);
|
|
|
|
// bind rtp socket to the local address specified
|
|
if (bind(sock, (SOCKADDR *)&localaddr, sizeof(localaddr))) {
|
|
|
|
// obtain last error
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"SEND bind(%d) port:%d failed:%d"),
|
|
sock, ntohs(localaddr.sin_port), WSAGetLastError()
|
|
));
|
|
|
|
errorS++;
|
|
} else if (!pCShSocket->GetPort(LOCAL)) {
|
|
/* if local port was assigned by the system, we need
|
|
* to update its value, we don't want the wildcard (0)
|
|
* to remain, because if it does, the socket may be
|
|
* erroneously shared with another socket requesting
|
|
* an specific local port */
|
|
int localaddrlen = sizeof(localaddr);
|
|
|
|
if (!getsockname(sock,
|
|
(struct sockaddr *)&localaddr,
|
|
&localaddrlen)) {
|
|
pCShSocket->SetPort(LOCAL, localaddr.sin_port);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TTL for senders
|
|
if (!errorS) {
|
|
if (setsockopt(
|
|
pCShSocket->GetShSocket(),
|
|
IPPROTO_IP,
|
|
IS_MULTICAST(pAddr[REMOTE])?
|
|
IP_MULTICAST_TTL : IP_TTL,
|
|
(PCHAR)&dwScope,
|
|
sizeof(dwScope)
|
|
) == SOCKET_ERROR) {
|
|
|
|
DWORD dwError = WSAGetLastError();
|
|
|
|
// Only Administrators can change TTL,
|
|
// that is not a reason to fail altogether
|
|
if (dwError != WSAEACCES)
|
|
errorS++;
|
|
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"setsockopt(%s)=%d failed: %d"),
|
|
IS_MULTICAST(pAddr[REMOTE])?
|
|
"IP_MULTICAST_TTL" : "IP_TTL",
|
|
dwScope,
|
|
dwError
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!errorS) {
|
|
if (IS_MULTICAST(pAddr[REMOTE])) {
|
|
// Set multicast address
|
|
|
|
SOCKADDR_IN joinaddr;
|
|
ZeroMemory(&joinaddr, sizeof(joinaddr));
|
|
DWORD addr;
|
|
|
|
addr = pCShSocket->GetAddress(REMOTE);
|
|
joinaddr.sin_family = AF_INET;
|
|
joinaddr.sin_addr = *(struct in_addr *) &addr;
|
|
joinaddr.sin_port = pCShSocket->GetPort(REMOTE);
|
|
|
|
if (WSAJoinLeaf(pCShSocket->GetShSocket(),
|
|
(const struct sockaddr *)&joinaddr,
|
|
sizeof(joinaddr),
|
|
NULL, NULL, NULL, NULL,
|
|
JL_SENDER_ONLY) == INVALID_SOCKET) {
|
|
|
|
errorS++;
|
|
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"WSAJoinLeaf(SENDER) failed: %d"),
|
|
WSAGetLastError()
|
|
));
|
|
} else {
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"WSAJoinLeaf(SENDER) succeded")
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pCRtpSession) {
|
|
if (pCRtpSession->IsSender()) {
|
|
pCShSocket->m_pCRtpSession[SOCKET_SEND] = pCRtpSession;
|
|
pCShSocket->m_pCRtpSession2[SOCKET_SEND] = pCRtpSession;
|
|
} else {
|
|
pCShSocket->m_pCRtpSession[SOCKET_RECV] = pCRtpSession;
|
|
pCShSocket->m_pCRtpSession2[SOCKET_RECV] = pCRtpSession;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
#if defined(DEBUG)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CSocketManager::GetSharedSocket: "
|
|
"Searched on %d elements =================="),
|
|
count
|
|
));
|
|
|
|
char Str[512];
|
|
CShSocket *pCShSocket;
|
|
|
|
for(pLE = m_SharedSockets.Flink;
|
|
pLE != &m_SharedSockets;
|
|
pLE = pLE->Flink) {
|
|
|
|
DWORD addr;
|
|
|
|
// obtain shared socket entry from list entry
|
|
pCShSocket = CONTAINING_RECORD(pLE, CShSocket, Link);
|
|
|
|
addr = pCShSocket->GetAddress(LOCAL);
|
|
RtpNtoA(addr, locaddr);
|
|
|
|
addr = pCShSocket->GetAddress(REMOTE);
|
|
RtpNtoA(addr, remaddr);
|
|
|
|
wsprintf(Str,
|
|
" {%d:%s/%05d-%s/%05d Cur(%d,%d), Max(%d,%d), "
|
|
"Cook(%05d,%05d), "
|
|
"Ini(%s,%s), QOS(%d,%d), Ctx(0x%X,0x%X)}",
|
|
pCShSocket->GetShSocket(),
|
|
locaddr, ntohs(pCShSocket->GetPort(LOCAL)),
|
|
remaddr, ntohs(pCShSocket->GetPort(REMOTE)),
|
|
pCShSocket->GetRefCount(0),
|
|
pCShSocket->GetRefCount(1),
|
|
pCShSocket->GetMaxCount(0),
|
|
pCShSocket->GetMaxCount(1),
|
|
ntohs((WORD)(pCShSocket->GetCookie() & 0xffff)),
|
|
ntohs((WORD)(pCShSocket->GetCookie() >> 16)),
|
|
(pCShSocket->GetKind() & SOCKET_MASK_INIT_RECV)? "R":"-",
|
|
(pCShSocket->GetKind() & SOCKET_MASK_INIT_SEND)? "S":"-",
|
|
pCShSocket->IsQOSSession(),
|
|
pCShSocket->IsQOSEnabled(),
|
|
pCShSocket->m_pCRtpSession[SOCKET_RECV],
|
|
pCShSocket->m_pCRtpSession[SOCKET_SEND]
|
|
);
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("%s"), Str
|
|
));
|
|
}
|
|
}
|
|
#endif
|
|
///////////////////////////////////////////////
|
|
|
|
if (errorR)
|
|
ReleaseSharedSocket(pCShSocket, SOCKET_MASK_RECV, pCRtpSession);
|
|
|
|
if (errorS)
|
|
ReleaseSharedSocket(pCShSocket, SOCKET_MASK_SEND, pCRtpSession);
|
|
|
|
if (errorR + errorS)
|
|
return(E_FAIL);
|
|
|
|
*ppCShSocket = pCShSocket;
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
DWORD
|
|
CSocketManager::ReleaseSharedSocket(CShSocket *pCShSocket, DWORD dwKind,
|
|
CRtpSession *pCRtpSession)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases a shared socket (RTCP).
|
|
|
|
Arguments:
|
|
|
|
Socket - shared socket to release.
|
|
|
|
Return Values:
|
|
|
|
Returns error code from WSAGetLastError.
|
|
|
|
--*/
|
|
{
|
|
CShSocket *pCShSocket2;
|
|
PLIST_ENTRY pLE;
|
|
|
|
CheckPointer(pCShSocket, E_POINTER);
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::ReleaseSharedSocket: %d"),
|
|
pCShSocket->GetShSocket()
|
|
));
|
|
|
|
// object lock to this object
|
|
CAutoLock LockThis(pStateLock());
|
|
|
|
PRTP_SESSION pRTPSession = (PRTP_SESSION)NULL;
|
|
|
|
if (pCRtpSession->GetpRTPSession()) {
|
|
// This code to debug the hang in RTCP
|
|
pRTPSession = pCRtpSession->GetpRTPSession();
|
|
}
|
|
|
|
// obtain pointer to first
|
|
pLE = m_SharedSockets.Flink;
|
|
|
|
// loop through shared sockets to make shure this exist
|
|
while (pLE != &m_SharedSockets) {
|
|
|
|
// obtain shared socket entry from list entry
|
|
pCShSocket2 = CONTAINING_RECORD(pLE, CShSocket, Link);
|
|
|
|
// check for matching socket
|
|
if (pCShSocket == pCShSocket2) {
|
|
|
|
SOCKET sock = pCShSocket->GetShSocket();
|
|
DWORD mask = 0x1;
|
|
|
|
if (pRTPSession) {
|
|
for(DWORD s = SOCK_RECV; s <= SOCK_RTCP; s++) {
|
|
if (sock == pRTPSession->pSocket[s])
|
|
break;
|
|
else
|
|
mask <<= 1;
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
if (mask > (1<<SOCK_RTCP)) {
|
|
char str[256];
|
|
wsprintf(str,
|
|
"CShSocket[0x%X] socket=%d "
|
|
"m_pCRtpSession[0x%X - 0x%X, 0x%X - 0x%X]\n"
|
|
"CRtpSession[0x%X] RTPSession[0x%X]\n",
|
|
pCShSocket, sock,
|
|
pCShSocket->m_pCRtpSession[0],
|
|
pCShSocket->m_pCRtpSession2[0],
|
|
pCShSocket->m_pCRtpSession[1],
|
|
pCShSocket->m_pCRtpSession2[1],
|
|
pCRtpSession, pRTPSession);
|
|
OutputDebugString(str);
|
|
//DebugBreak();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
for(DWORD k = SOCKET_FIRST; k < SOCKET_LAST; k++) {
|
|
if (dwKind & SOCKET_MASK(k) & pCShSocket->GetKind()) {
|
|
// This kind (recv/send) exists
|
|
|
|
if (pCShSocket->GetRefCount(k) > 1)
|
|
if (pRTPSession)
|
|
pRTPSession->dwStatus |= (mask << 24);
|
|
|
|
if (!pCShSocket->DelRefCount(k)) {
|
|
// if no more refs, reset kind and
|
|
// null the RtpSession pointer
|
|
pCShSocket->RstKind(SOCKET_MASK(k));
|
|
pCShSocket->m_pCRtpSession[k] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pCShSocket->GetRefCountAll()) {
|
|
|
|
RemoveEntryList(&pCShSocket->Link);
|
|
|
|
HRESULT hr = pCShSocket->CloseSocket();
|
|
|
|
delete pCShSocket;
|
|
|
|
if (pRTPSession) {
|
|
|
|
if (!pRTPSession->dwCloseTime)
|
|
pRTPSession->dwCloseTime = GetTickCount();
|
|
|
|
if (FAILED(hr)) {
|
|
// Close socket failed
|
|
pRTPSession->dwLastError = WSAGetLastError();
|
|
|
|
pRTPSession->dwStatus |= (mask << 4);
|
|
} else {
|
|
pRTPSession->dwStatus |= mask;
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
|
|
} else {
|
|
|
|
if (pRTPSession)
|
|
pRTPSession->dwStatus |= (mask << 8);
|
|
}
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// goto next pointer
|
|
pLE = pLE->Flink;
|
|
}
|
|
|
|
if (pRTPSession) {
|
|
if (pRTPSession->dwStatus & (1<<12))
|
|
pRTPSession->dwStatus |= (1<<13);
|
|
else
|
|
pRTPSession->dwStatus |= (1<<12);
|
|
}
|
|
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CSocketManager::ReleaseSharedSocket: "
|
|
"failed: structure not found: %d"),
|
|
pCShSocket->GetShSocket()
|
|
));
|
|
|
|
// Structure not found
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CShSocket
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
CShSocket::CShSocket(DWORD dwRemAddr,
|
|
long *plMaxShare,
|
|
WSAPROTOCOL_INFO *pProtocolInfo,
|
|
DWORD dwMaxFilters,
|
|
HRESULT *phr)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CShSocket(%d,%d)"),
|
|
plMaxShare[0], plMaxShare[1]
|
|
));
|
|
|
|
// Create a new socket
|
|
int Flags = WSA_FLAG_OVERLAPPED;
|
|
BOOL fReuse;
|
|
DWORD dwLoopBack = 0;
|
|
|
|
ZeroMemory(this, sizeof(CShSocket));
|
|
|
|
// Record the limits
|
|
m_MaxCount[0] = plMaxShare[0];
|
|
m_MaxCount[1] = plMaxShare[1];
|
|
|
|
if (pProtocolInfo) {
|
|
// Asking for QOS, reserve the requiered structure
|
|
// for the QOS reservation
|
|
m_pCRtpQOSReserve = new CRtpQOSReserve(this, dwMaxFilters);
|
|
|
|
if (!m_pCRtpQOSReserve) {
|
|
// Log info about success/failure,
|
|
// may also notify or fail.
|
|
// TODO
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (IS_MULTICAST(dwRemAddr))
|
|
Flags |= WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF;
|
|
|
|
m_Socket = WSASocket(AF_INET, SOCK_DGRAM, 0,
|
|
pProtocolInfo, 0, Flags);
|
|
|
|
// validate socket handle returned
|
|
if (m_Socket == INVALID_SOCKET) {
|
|
|
|
// obtain last error
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CShSocket: failed %d"),
|
|
WSAGetLastError()
|
|
));
|
|
|
|
goto cleanup;
|
|
}
|
|
#if 0
|
|
// XXX: Don't close the handle so a second close will break
|
|
SetHandleInformation((HANDLE)m_Socket,
|
|
HANDLE_FLAG_PROTECT_FROM_CLOSE,
|
|
HANDLE_FLAG_PROTECT_FROM_CLOSE);
|
|
#endif
|
|
|
|
fReuse = TRUE;
|
|
|
|
// BUG!!! (build 1735): WSARecv fails for QoS enabled sockets
|
|
// in unicast with error WSAEINVAL=10022 when
|
|
// I use REUSEADDR.
|
|
//
|
|
// Anyway, allowing REUSEADDR unicast is not a good thing,
|
|
// in fact we want to prevent that -- suppose another application
|
|
// is using the same address/port pair, we would get a weird
|
|
// behavior withouth knowing the reason, so we better fail in such
|
|
// a case.
|
|
// In multicast that's a different matter, we want to be able to
|
|
// REUSEADDR, WS2 delivers a copy of each packet ro every listener
|
|
|
|
if (IS_MULTICAST(dwRemAddr)) {
|
|
if (setsockopt(
|
|
m_Socket,
|
|
SOL_SOCKET,
|
|
SO_REUSEADDR,
|
|
(PCHAR)&fReuse,
|
|
sizeof(fReuse)
|
|
) == SOCKET_ERROR) {
|
|
|
|
// obtain last error
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CShSocket: "
|
|
"setsockopt(SO_REUSEADDR) failed: %d"),
|
|
WSAGetLastError()
|
|
));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// BUGBUG, this should be done only for receivers,
|
|
// but if the socket is created first for a sender
|
|
// this option would have already been set.
|
|
// This is not a problem right now because this flag
|
|
// is set first when the socket is created, and then
|
|
// can be updated only by a receiver.
|
|
if (setsockopt(
|
|
m_Socket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_LOOP,
|
|
(PCHAR)&dwLoopBack,
|
|
sizeof(dwLoopBack)
|
|
) == SOCKET_ERROR) {
|
|
|
|
// obtain last error
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CShSocket: "
|
|
"setsockopt(IP_MULTICAST_LOOP) failed: %d"),
|
|
WSAGetLastError()
|
|
));
|
|
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// bind lives now in GetSharedSocket in the socket initialization,
|
|
// done differently for a sender, a receiver, and a sender/reciever
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP2,
|
|
TEXT("CShSocket::CShSocket: succeded: %d"),
|
|
m_Socket
|
|
));
|
|
|
|
*phr = NOERROR;
|
|
return;
|
|
|
|
cleanup:
|
|
|
|
if (m_pCRtpQOSReserve) {
|
|
delete m_pCRtpQOSReserve;
|
|
m_pCRtpQOSReserve = NULL;
|
|
}
|
|
|
|
if (m_Socket != INVALID_SOCKET) {
|
|
closesocket(m_Socket);
|
|
m_Socket = INVALID_SOCKET;
|
|
}
|
|
|
|
*phr = E_FAIL;
|
|
return;
|
|
}
|
|
|
|
CShSocket::~CShSocket()
|
|
{
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::~CShSocket"),
|
|
m_Socket
|
|
));
|
|
}
|
|
|
|
HRESULT
|
|
CShSocket::CloseSocket()
|
|
{
|
|
HRESULT dwError = E_FAIL;
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CloseSocket: Socket:%d"),
|
|
m_Socket
|
|
));
|
|
|
|
#if defined(DEBUG)
|
|
if (m_RefCount[0] + m_RefCount[1])
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CloseSocket: Inconsistency detected "
|
|
"in socket:%d: m_RefCount[0,1]=%d,%d"),
|
|
m_Socket, m_RefCount[0], m_RefCount[1]
|
|
));
|
|
#endif
|
|
|
|
ASSERT( !(m_RefCount[0] + m_RefCount[1]) );
|
|
|
|
if (m_Socket != INVALID_SOCKET) {
|
|
#if 0
|
|
// XXX: Now allow to close the handle
|
|
SetHandleInformation((HANDLE)m_Socket,
|
|
HANDLE_FLAG_PROTECT_FROM_CLOSE,
|
|
0);
|
|
#endif
|
|
if (closesocket(m_Socket)) {
|
|
// On error, do not wait for any overlapped IO to complete
|
|
TraceRetail((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CloseSocket: closesocket(%d) failed: %d"),
|
|
m_Socket, WSAGetLastError()
|
|
));
|
|
} else {
|
|
dwError = NOERROR;
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::CloseSocket: closesocket(%d)"),
|
|
m_Socket
|
|
));
|
|
}
|
|
}
|
|
|
|
if (m_pCRtpQOSReserve) {
|
|
delete m_pCRtpQOSReserve;
|
|
m_pCRtpQOSReserve = NULL;
|
|
}
|
|
|
|
return(dwError);
|
|
}
|
|
|
|
HRESULT
|
|
CShSocket::ShSocketStopQOS(DWORD dwIsSender)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CShSocket::ShSocketStopQOS")
|
|
));
|
|
|
|
if (m_pCRtpQOSReserve)
|
|
return(m_pCRtpQOSReserve->Unreserve(dwIsSender));
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InitializeFlowSpec
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
InitializeFlowSpec(
|
|
IN OUT PFLOWSPEC FlowSpec,
|
|
IN SERVICETYPE ServiceType
|
|
)
|
|
{
|
|
FlowSpec->TokenRate = QOS_NOT_SPECIFIED;
|
|
FlowSpec->TokenBucketSize = QOS_NOT_SPECIFIED;
|
|
FlowSpec->PeakBandwidth = QOS_NOT_SPECIFIED;
|
|
FlowSpec->Latency = QOS_NOT_SPECIFIED;
|
|
FlowSpec->DelayVariation = QOS_NOT_SPECIFIED;
|
|
FlowSpec->ServiceType = ServiceType;
|
|
FlowSpec->MaxSduSize = QOS_NOT_SPECIFIED;
|
|
FlowSpec->MinimumPolicedSize = QOS_NOT_SPECIFIED;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CRtpQOSReserve
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CRtpQOSReserve::CRtpQOSReserve(CShSocket *pCShSocket, DWORD dwMaxFilters)
|
|
: m_pCShSocket(pCShSocket),
|
|
|
|
m_pRsvpSSRC(NULL),
|
|
m_pRsvpFilterSpec(NULL),
|
|
|
|
m_Style(RSVP_DEFAULT_STYLE),
|
|
m_dwFlags(flags_par(FG_RES_CONFIRMATION_REQUEST)),
|
|
|
|
m_dwLastReserveTime(0),
|
|
m_dwReserveIntervalTime(INITIAL_RESERVE_INTERVAL_TIME) // ms
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::CRtpQOSReserve")
|
|
));
|
|
|
|
InitializeFlowSpec( &m_qos.ReceivingFlowspec, SERVICETYPE_NOCHANGE );
|
|
InitializeFlowSpec( &m_qos.SendingFlowspec, SERVICETYPE_NOCHANGE );
|
|
|
|
m_qos.ProviderSpecific.len = 0;
|
|
m_qos.ProviderSpecific.buf = NULL;
|
|
|
|
// Initial values for destination address
|
|
ZeroMemory(&m_destaddr, sizeof(m_destaddr));
|
|
m_destaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
|
|
m_destaddr.ObjectHdr.ObjectLength =
|
|
sizeof(m_destaddr) +
|
|
sizeof(m_sockin_destaddr);
|
|
m_destaddr.SocketAddress = (SOCKADDR *)&m_sockin_destaddr;
|
|
m_destaddr.SocketAddressLength = sizeof(m_sockin_destaddr);
|
|
|
|
SetMaxFilters(dwMaxFilters);
|
|
}
|
|
|
|
CRtpQOSReserve::~CRtpQOSReserve()
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::~CRtpQOSReserve")
|
|
));
|
|
|
|
if (m_pRsvpSSRC)
|
|
delete m_pRsvpSSRC;
|
|
|
|
if (m_pRsvpFilterSpec)
|
|
delete m_pRsvpFilterSpec;
|
|
|
|
m_MaxFilters = m_NumFilters = 0;
|
|
m_pRsvpSSRC = NULL;
|
|
m_pRsvpFilterSpec = NULL;
|
|
}
|
|
|
|
// change the max number of filters,
|
|
// and flush the current list
|
|
HRESULT
|
|
CRtpQOSReserve::SetMaxFilters(DWORD dwMaxFilters)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::SetMaxFilters(%d)"), dwMaxFilters
|
|
));
|
|
|
|
if (dwMaxFilters > MAX_FILTERS)
|
|
return(E_INVALIDARG);
|
|
|
|
if (m_MaxFilters != dwMaxFilters) {
|
|
|
|
// release old memory
|
|
if (m_pRsvpSSRC)
|
|
delete m_pRsvpSSRC;
|
|
if (m_pRsvpFilterSpec)
|
|
delete m_pRsvpFilterSpec;
|
|
|
|
m_MaxFilters = dwMaxFilters;
|
|
|
|
if (dwMaxFilters > 0) {
|
|
// get new memory
|
|
m_pRsvpSSRC = new DWORD[dwMaxFilters];
|
|
|
|
m_pRsvpFilterSpec = new RSVP_FILTERSPEC[dwMaxFilters];
|
|
|
|
if (!(m_pRsvpSSRC && m_pRsvpFilterSpec)) {
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::SetMaxFilters(%d) failed"),
|
|
dwMaxFilters
|
|
));
|
|
|
|
if (m_pRsvpSSRC) {
|
|
delete m_pRsvpSSRC;
|
|
m_pRsvpSSRC = (DWORD *)NULL;
|
|
}
|
|
|
|
if (m_pRsvpFilterSpec) {
|
|
delete m_pRsvpFilterSpec;
|
|
m_pRsvpFilterSpec = (RSVP_FILTERSPEC *)NULL;
|
|
}
|
|
|
|
m_MaxFilters = 0;
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_NumFilters = 0;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// Ask for the template's names (e.g. G711, G723, H261CIF, etc.)
|
|
HRESULT
|
|
CRtpQOSReserve::QueryTemplates(char *templates, int size)
|
|
{
|
|
WSABUF wsabuf;
|
|
QOS qos; // For query this parameter should be passed as NULL
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::QueryTemplates")
|
|
));
|
|
|
|
if ( !templates || size < 1 || !m_pCShSocket )
|
|
return(hr);
|
|
|
|
templates[0] = '\0';
|
|
|
|
wsabuf.buf = templates;
|
|
wsabuf.len = size;
|
|
|
|
qos.ProviderSpecific.len = 0;
|
|
qos.ProviderSpecific.buf = NULL;
|
|
|
|
if (WSAGetQOSByName(m_pCShSocket->GetShSocket(), &wsabuf, &qos)) {
|
|
char *str, *str0;
|
|
|
|
// change NULLs by SPACE
|
|
for(str0 = str = templates;
|
|
(*str || *(str+1)) && ((str-str0) < size);
|
|
str++) {
|
|
if (!*str)
|
|
*str = ' ';
|
|
}
|
|
|
|
hr = NOERROR;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// Get just one template
|
|
HRESULT
|
|
CRtpQOSReserve::GetTemplate(char *template_name, char *qosClass, QOS *pqos)
|
|
{
|
|
WSABUF wsabuf;
|
|
DWORD dwNewTokenRate;
|
|
DWORD dwTokenBucketSize;
|
|
DWORD dwMaxSduSize;
|
|
DWORD dwMinimumPolicedSize;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::GetTemplate")
|
|
));
|
|
|
|
if (!template_name || !pqos)
|
|
return(hr);
|
|
|
|
wsabuf.buf = template_name;
|
|
wsabuf.len = strlen(template_name);
|
|
if (wsabuf.len > 0)
|
|
wsabuf.len++; // Include NULL character
|
|
|
|
pqos->ProviderSpecific.len = 0;
|
|
pqos->ProviderSpecific.buf = NULL;
|
|
|
|
/*
|
|
* RTP = 12 bytes.
|
|
*
|
|
* Add at least 3% to the nominal token rate
|
|
*
|
|
* For G711. It is (240 + 40) * (1000 / 30) = 9333.33 bytes => 9613
|
|
* (currently 8500), Min size 252 (current 340)
|
|
*
|
|
* For G723. It is (24 + 40) * (1000 / 30) = 2133.33 bytes => 2200
|
|
* (currently 1138), Min size 32 (current 68)
|
|
*
|
|
* For GSM. It is (65 + 40) * (1000 / 40) = 2625 bytes => 2704
|
|
* (currently 2150), Min size 77 (current 86)
|
|
* */
|
|
|
|
if (WSAGetQOSByName(m_pCShSocket->GetShSocket(), &wsabuf, pqos)) {
|
|
|
|
dwNewTokenRate = (DWORD)-1;
|
|
dwTokenBucketSize = (DWORD)-1;
|
|
dwMaxSduSize = (DWORD)-1;
|
|
dwMinimumPolicedSize = (DWORD)-1;
|
|
|
|
if (!strcmp("G711", template_name)) {
|
|
|
|
dwNewTokenRate = 9613;
|
|
dwMaxSduSize = (240 * 3) + 12; /* 732 */
|
|
dwTokenBucketSize = dwMaxSduSize * 2;
|
|
dwMinimumPolicedSize = 92; /* Actually we support 10ms,
|
|
* so change from 30ms to 10ms */
|
|
|
|
} else if (!strcmp("G723", template_name)) {
|
|
/*
|
|
* WARNING: MSP passes G723, but template name is G723.1
|
|
*/
|
|
dwNewTokenRate = 2198;
|
|
dwMaxSduSize = (24 * 3) + 12; /* 84 */
|
|
dwTokenBucketSize = dwMaxSduSize * 4;
|
|
dwMinimumPolicedSize = 32;
|
|
|
|
} else if (!strcmp("GSM6.10", template_name)) {
|
|
|
|
dwNewTokenRate = 2704;
|
|
dwMinimumPolicedSize = 77;
|
|
}
|
|
|
|
/* TokenRate & PeakBandwidth */
|
|
if (dwNewTokenRate != (DWORD)-1) {
|
|
|
|
if (dwNewTokenRate > pqos->SendingFlowspec.TokenRate) {
|
|
/* take the maximum */
|
|
|
|
pqos->SendingFlowspec.TokenRate = dwNewTokenRate;
|
|
|
|
pqos->ReceivingFlowspec.TokenRate = dwNewTokenRate;
|
|
}
|
|
|
|
pqos->SendingFlowspec.PeakBandwidth =
|
|
(dwNewTokenRate * 17) / 10; /* + 70 % */
|
|
|
|
pqos->ReceivingFlowspec.PeakBandwidth =
|
|
(dwNewTokenRate * 17) / 10; /* + 70 % */
|
|
}
|
|
|
|
/* TokenBucketSize */
|
|
if (dwTokenBucketSize != (DWORD)-1) {
|
|
|
|
if (dwTokenBucketSize > pqos->SendingFlowspec.TokenBucketSize) {
|
|
/* take the maximum */
|
|
|
|
pqos->SendingFlowspec.TokenBucketSize = dwTokenBucketSize;
|
|
|
|
pqos->ReceivingFlowspec.TokenBucketSize = dwTokenBucketSize;
|
|
}
|
|
}
|
|
|
|
/* MaxSduSize */
|
|
if (dwMaxSduSize != (DWORD)-1) {
|
|
|
|
if (dwMaxSduSize > pqos->SendingFlowspec.MaxSduSize) {
|
|
/* take the maximum */
|
|
|
|
pqos->SendingFlowspec.MaxSduSize = dwMaxSduSize;
|
|
|
|
pqos->ReceivingFlowspec.MaxSduSize = dwMaxSduSize;
|
|
}
|
|
}
|
|
|
|
/* MinimumPolicedSize */
|
|
if (dwMinimumPolicedSize != (DWORD)-1) {
|
|
|
|
if (dwMinimumPolicedSize <
|
|
pqos->SendingFlowspec.MinimumPolicedSize) {
|
|
/* take the minimum */
|
|
|
|
pqos->SendingFlowspec.MinimumPolicedSize =
|
|
dwMinimumPolicedSize;
|
|
|
|
pqos->ReceivingFlowspec.MinimumPolicedSize =
|
|
dwMinimumPolicedSize;
|
|
}
|
|
}
|
|
|
|
hr = NOERROR;
|
|
|
|
// Save class and name
|
|
strncpy(m_QOSclass, qosClass, sizeof(m_QOSclass));
|
|
strncpy(m_QOSname, template_name, sizeof(m_QOSname));
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// Set the Sender/Receiver FlowSpec
|
|
HRESULT
|
|
CRtpQOSReserve::SetFlowSpec(FLOWSPEC *pFlowSpec, DWORD dwIsSender)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::SetFlowSpec")
|
|
));
|
|
|
|
if (!pFlowSpec)
|
|
return(E_FAIL);
|
|
|
|
CopyMemory(dwIsSender? &m_qos.SendingFlowspec : &m_qos.ReceivingFlowspec,
|
|
pFlowSpec,
|
|
sizeof(m_qos.SendingFlowspec));
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// Scale a flow spec
|
|
// Only scale the following parameters:
|
|
//
|
|
// TokenRate; /* In Bytes/sec */
|
|
// TokenBucketSize; /* In Bytes */
|
|
// PeakBandwidth; /* In Bytes/sec */
|
|
//
|
|
// TokenBucketSize and PeakBandwidth are scaled up, but not down
|
|
|
|
HRESULT
|
|
CRtpQOSReserve::ScaleFlowSpec(FLOWSPEC *pFlowSpec,
|
|
DWORD dwNumParticipants,
|
|
DWORD dwMaxParticipants,
|
|
DWORD dwBandwidth)
|
|
{
|
|
DWORD dwOverallBW = pFlowSpec->TokenRate * dwMaxParticipants;
|
|
|
|
dwBandwidth /= 8; // flowspec is in bytes/sec
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::ScaleFlowSpec(%u, %u, %u b/s) "
|
|
"flowspec(Tr:%u, Tbs:%u, PBw:%u, ST:%u, "
|
|
"MaxSDU:%u MinSize:%u)"),
|
|
dwNumParticipants, dwMaxParticipants, dwBandwidth*8,
|
|
pFlowSpec->TokenRate,
|
|
pFlowSpec->TokenBucketSize,
|
|
(pFlowSpec->PeakBandwidth != QOS_NOT_SPECIFIED) ?
|
|
pFlowSpec->PeakBandwidth : QOS_NOT_SPECIFIED,
|
|
pFlowSpec->ServiceType,
|
|
pFlowSpec->MaxSduSize, pFlowSpec->MinimumPolicedSize
|
|
));
|
|
|
|
if (dwOverallBW <= dwBandwidth) {
|
|
// use as it is, scale up to dwNumParticipants
|
|
pFlowSpec->TokenRate *= dwNumParticipants;
|
|
pFlowSpec->TokenBucketSize *= dwNumParticipants;
|
|
if (pFlowSpec->PeakBandwidth != QOS_NOT_SPECIFIED)
|
|
pFlowSpec->PeakBandwidth *= dwNumParticipants;
|
|
} else {
|
|
// don't have all we need, scale according
|
|
// to number of participants
|
|
|
|
DWORD fac1;
|
|
DWORD fac2;
|
|
|
|
if (dwNumParticipants == dwMaxParticipants) {
|
|
// use all the bandwidth available
|
|
|
|
// Scale = 1 + [ (Bw - TokenRate) / TokenRate ]
|
|
// Scale = Bw / TokenRate = fac1 / fac2
|
|
|
|
fac1 = dwBandwidth;
|
|
fac2 = pFlowSpec->TokenRate;
|
|
} else {
|
|
// use the bandwidth according to num of participants
|
|
|
|
// Scale = [ ((Bw / Max) * Num ] / TokenRate
|
|
// Scale = (Bw * Num) / (TokenRate * Max) = fac1 / fac2
|
|
|
|
fac1 = dwBandwidth * dwNumParticipants;
|
|
fac2 = pFlowSpec->TokenRate * dwMaxParticipants;
|
|
}
|
|
|
|
// scale TokenRate up or down
|
|
pFlowSpec->TokenRate =
|
|
(pFlowSpec->TokenRate * fac1) / fac2;
|
|
|
|
if (fac1 > fac2) {
|
|
// can still scale up the other parameters
|
|
|
|
pFlowSpec->TokenBucketSize =
|
|
((pFlowSpec->TokenBucketSize * fac1) / fac2);
|
|
|
|
if (pFlowSpec->PeakBandwidth != QOS_NOT_SPECIFIED)
|
|
pFlowSpec->PeakBandwidth =
|
|
((pFlowSpec->PeakBandwidth * fac1) / fac2);
|
|
}
|
|
}
|
|
|
|
// The bandwidth we request include RTP/UDP/IP headers overhead,
|
|
// but RSVP also scales up to consider headers overhead, to ovoid
|
|
// requesting more bandwidth than we intend, pass to RSVP a
|
|
// smaller value such that the final one RSVP comes up with would
|
|
// be the original value we request.
|
|
|
|
DWORD RSVPTokenRate;
|
|
|
|
if (pFlowSpec->MinimumPolicedSize > 0) {
|
|
|
|
RSVPTokenRate =
|
|
(pFlowSpec->TokenRate * 1000) /
|
|
(1000 + 28000/pFlowSpec->MinimumPolicedSize);
|
|
}
|
|
|
|
DWORD RSVPPeakBandwidth;
|
|
|
|
RSVPPeakBandwidth = pFlowSpec->PeakBandwidth;
|
|
|
|
if (RSVPPeakBandwidth != QOS_NOT_SPECIFIED) {
|
|
|
|
RSVPPeakBandwidth =
|
|
(pFlowSpec->PeakBandwidth * 1000) /
|
|
(1000 + 28000/pFlowSpec->MinimumPolicedSize);
|
|
}
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::ScaleFlowSpec: "
|
|
"flowspec(Tr:%u/%u, Tbs:%u, PBw:%u/%u ST:%u, "
|
|
"MaxSDU:%u MinSize:%u) scaled"),
|
|
pFlowSpec->TokenRate, RSVPTokenRate,
|
|
pFlowSpec->TokenBucketSize,
|
|
(pFlowSpec->PeakBandwidth != QOS_NOT_SPECIFIED) ?
|
|
pFlowSpec->PeakBandwidth : QOS_NOT_SPECIFIED,
|
|
(RSVPPeakBandwidth != QOS_NOT_SPECIFIED) ?
|
|
RSVPPeakBandwidth : QOS_NOT_SPECIFIED,
|
|
pFlowSpec->ServiceType,
|
|
pFlowSpec->MaxSduSize, pFlowSpec->MinimumPolicedSize
|
|
));
|
|
|
|
pFlowSpec->TokenRate = RSVPTokenRate;
|
|
pFlowSpec->PeakBandwidth = RSVPPeakBandwidth;
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// Set the destination address (required for unicast)
|
|
HRESULT
|
|
CRtpQOSReserve::SetDestAddr(LPBYTE pbDestAddr, DWORD dwAddrLen)
|
|
{
|
|
CheckPointer(pbDestAddr, E_POINTER);
|
|
|
|
if (dwAddrLen != sizeof(m_sockin_destaddr))
|
|
return(E_INVALIDARG);
|
|
|
|
char addrstr[RTPNTOASIZE];
|
|
SOCKADDR_IN *pSinAddr = (SOCKADDR_IN *)pbDestAddr;
|
|
|
|
TraceRetail((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::SetDestAddr(%s/%d)"),
|
|
RtpNtoA(pSinAddr->sin_addr.s_addr, addrstr),
|
|
ntohs(((SOCKADDR_IN *)pbDestAddr)->sin_port)
|
|
));
|
|
|
|
// Update only the address as the other elements do not change
|
|
// since they are first initialized
|
|
CopyMemory(&m_sockin_destaddr, pbDestAddr, dwAddrLen);
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// Find out if an SSRC is in the reservation list or not
|
|
DWORD
|
|
CRtpQOSReserve::FindSSRC(DWORD ssrc)
|
|
{
|
|
if (m_pRsvpSSRC && m_NumFilters) {
|
|
DWORD i;
|
|
|
|
for(i = 0; i < m_NumFilters; i++)
|
|
if (ssrc == m_pRsvpSSRC[i])
|
|
break;
|
|
|
|
if (i < m_NumFilters)
|
|
return(i);
|
|
else
|
|
return(-1);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
// Add/Delete one SSRC (participant) to the
|
|
// Shared Explicit Filter (SEF) list
|
|
// 0==delete; other==add
|
|
HRESULT
|
|
CRtpQOSReserve::AddDeleteSSRC(DWORD ssrc, DWORD dwAddDel)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AddDeleteSSRC(0x%X, %s)"),
|
|
ssrc, dwAddDel? "ADD":"DEL"
|
|
));
|
|
|
|
CAutoLock LockThis(pStateLock());
|
|
|
|
if (m_pRsvpFilterSpec) {
|
|
|
|
unsigned int i;
|
|
|
|
// Lookup the SSRC and find out if it is already in
|
|
// the priority list
|
|
|
|
for(i = 0; i < m_NumFilters; i++)
|
|
if (ssrc == m_pRsvpSSRC[i])
|
|
break;
|
|
|
|
if (i < m_NumFilters) { // if (in list)
|
|
|
|
/////////////////////////
|
|
// Found at the ith place
|
|
/////////////////////////
|
|
|
|
if (dwAddDel) {
|
|
//////////////////////
|
|
// ******* ADD *******
|
|
//////////////////////
|
|
hr = NOERROR;// Add -- do nothing, already in list
|
|
|
|
} else {
|
|
|
|
/////////////////////////
|
|
// ******* DELETE *******
|
|
/////////////////////////
|
|
|
|
// remove from list
|
|
RSVP_FILTERSPEC *rsvp1 = &m_pRsvpFilterSpec[i];
|
|
RSVP_FILTERSPEC *rsvp2 = rsvp1 + 1;
|
|
|
|
DWORD *ssrc1 = &m_pRsvpSSRC[i];
|
|
DWORD *ssrc2 = ssrc1 + 1;
|
|
|
|
for(m_NumFilters--;
|
|
i < m_NumFilters;
|
|
rsvp1++, rsvp2++, ssrc1++, ssrc2++, i++) {
|
|
|
|
MoveMemory(rsvp1, rsvp2, sizeof(*rsvp1));
|
|
*ssrc1 = *ssrc2;
|
|
}
|
|
|
|
}
|
|
hr = NOERROR;
|
|
} else { // else if (not in list)
|
|
|
|
/////////////////////
|
|
// Not found in list!
|
|
/////////////////////
|
|
|
|
if (dwAddDel) {
|
|
|
|
//////////////////////
|
|
// ******* ADD *******
|
|
//////////////////////
|
|
|
|
// add to the list
|
|
|
|
// Validate the number of SRRCs in the list
|
|
if (m_NumFilters < m_MaxFilters) {
|
|
|
|
CRtpSession *pCRtpSession =
|
|
m_pCShSocket->GetpCRtpSession(-1);
|
|
|
|
if (pCRtpSession) {
|
|
|
|
DWORD addrlen;
|
|
SOCKADDR_IN saddr;
|
|
|
|
// Get SSRC's IP address/port (from RTP packets)
|
|
addrlen = sizeof(saddr);
|
|
hr = pCRtpSession->
|
|
GetParticipantAddress(ssrc,
|
|
(LPBYTE)&saddr,
|
|
(int *)&addrlen);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
RSVP_FILTERSPEC *rsvp =
|
|
&m_pRsvpFilterSpec[m_NumFilters];
|
|
|
|
rsvp->Type = FILTERSPECV4;
|
|
rsvp->FilterSpecV4.Address.Addr =
|
|
saddr.sin_addr.s_addr;
|
|
|
|
// Take the port from the RTP Session's address
|
|
// as the learned address may be from an
|
|
// RTCP packet and hence be the wrong port
|
|
// number.
|
|
//
|
|
// !!!!! Note
|
|
// The address from RTP packets may not be
|
|
// available yet, it will be once we receive
|
|
// a valid RTP packet.
|
|
// If that address is not available, then
|
|
// all 0's address will be returned
|
|
// but the function will not fail.
|
|
|
|
// The sending port can be anything, as
|
|
// in NetMeeting, In this case,
|
|
// the MSP needs to lookup participants
|
|
// using the SSRC/CNAME, being CNAME
|
|
// the unique ID.
|
|
rsvp->FilterSpecV4.Port = saddr.sin_port;
|
|
|
|
if (saddr.sin_port) {
|
|
// Record the filter only if a valid
|
|
// address/port is available
|
|
m_pRsvpSSRC[m_NumFilters] = ssrc;
|
|
|
|
m_NumFilters++;
|
|
|
|
// Succeeds only if really added
|
|
// to the list
|
|
char addrstr[RTPNTOASIZE];
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AddDeleteSSRC"
|
|
"(%s: 0x%X/%s/%d)"),
|
|
dwAddDel? "ADD":"DEL",
|
|
ssrc,
|
|
RtpNtoA(saddr.sin_addr.s_addr,addrstr),
|
|
ntohs(saddr.sin_port)
|
|
));
|
|
|
|
hr = NOERROR;
|
|
}
|
|
} else {
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AddDeleteSSRC"
|
|
": could not get IP addr for 0x%X"),
|
|
ssrc
|
|
));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
/////////////////////////
|
|
// ******* DELETE *******
|
|
/////////////////////////
|
|
|
|
hr = NOERROR;// do nothing, not in list
|
|
}
|
|
} // if (not in list)
|
|
} // if (filters list exist)
|
|
|
|
return(hr);
|
|
}
|
|
|
|
void dumpQOS(char *msg, QOS *pQOS);
|
|
void dumpObjectType(char *msg, char *ptr, unsigned int len);
|
|
|
|
HRESULT
|
|
CRtpQOSReserve::Reserve(DWORD dwIsSender)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(%s) @ %d"),
|
|
dwIsSender? "SEND":"RECV",
|
|
GetTickCount() - m_dwLastReserveTime
|
|
));
|
|
|
|
DWORD len;
|
|
char *ptr;
|
|
QOS qos;
|
|
char buf[MAX_PROVIDERSPECIFIC_BUFFER];
|
|
|
|
QOS_DESTADDR *dest_addr;
|
|
RSVP_RESERVE_INFO *reserve_info;
|
|
FLOWDESCRIPTOR *flow_desc;
|
|
RSVP_FILTERSPEC *filterspec;
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
CopyMemory(&qos, &m_qos, sizeof(qos));
|
|
|
|
// Always enable notifications,
|
|
// except if the WSAIctl(SIO_SET_QOS) fails or
|
|
// we are receiver and are requesting BEST_EFFORT
|
|
m_pCShSocket->ModifyFlags(
|
|
dwIsSender? FG_SOCK_ENABLE_NOTIFY_SEND:FG_SOCK_ENABLE_NOTIFY_RECV,
|
|
1);
|
|
|
|
if (m_pCShSocket->TestFlags(
|
|
dwIsSender? FG_SOCK_ENABLE_NOTIFY_SEND:FG_SOCK_ENABLE_NOTIFY_RECV)) {
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(%s) Enable Notifications"),
|
|
dwIsSender? "SEND":"RECV"
|
|
));
|
|
} else {
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(%s) Disable Notifications"),
|
|
dwIsSender? "SEND":"RECV"
|
|
));
|
|
}
|
|
|
|
qos.ProviderSpecific.len = 0;
|
|
qos.ProviderSpecific.buf = NULL;
|
|
ptr = buf;
|
|
|
|
if (dwIsSender) {
|
|
// Do not change the receiver
|
|
qos.ReceivingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
|
|
|
// Init the destination object if unicast
|
|
if (!IS_MULTICAST(m_sockin_destaddr.sin_addr.s_addr) &&
|
|
(m_sockin_destaddr.sin_addr.s_addr != INADDR_ANY)) {
|
|
|
|
if (!fg_tst(m_dwFlags, FG_RES_DEST_ADDR_OBJECT_USED)) {
|
|
|
|
// Specify the dest addr object only once,
|
|
// to do so remember it was used
|
|
fg_set(m_dwFlags, FG_RES_DEST_ADDR_OBJECT_USED);;
|
|
|
|
dest_addr = (QOS_DESTADDR *)ptr;
|
|
|
|
ZeroMemory((char *)dest_addr,
|
|
sizeof(m_destaddr) +
|
|
sizeof(m_sockin_destaddr));
|
|
dest_addr->ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
|
|
dest_addr->ObjectHdr.ObjectLength =
|
|
sizeof(m_destaddr);
|
|
//sizeof(m_sockin_destaddr);
|
|
|
|
// Copy QOS_DESTADDR and SocketAddress, update pointer
|
|
// to SocketAddress
|
|
|
|
CopyMemory((char *)dest_addr,
|
|
(char *)&m_destaddr,
|
|
sizeof(m_destaddr));
|
|
|
|
CopyMemory((char *)(dest_addr + 1),
|
|
(char *)&m_sockin_destaddr,
|
|
sizeof(m_sockin_destaddr));
|
|
|
|
dest_addr->SocketAddress = (const struct sockaddr *)
|
|
(dest_addr + 1);
|
|
dest_addr->SocketAddressLength = sizeof(m_sockin_destaddr);
|
|
|
|
ptr += sizeof(m_destaddr) + sizeof(m_sockin_destaddr);
|
|
}
|
|
}
|
|
|
|
reserve_info = (RSVP_RESERVE_INFO *)ptr;
|
|
|
|
// Partially Init RSVP_RESERVE_INFO
|
|
ZeroMemory(reserve_info, sizeof(RSVP_RESERVE_INFO));
|
|
reserve_info->ObjectHdr.ObjectType = RSVP_OBJECT_RESERVE_INFO;
|
|
reserve_info->ConfirmRequest =
|
|
flags_tst(FG_RES_CONFIRMATION_REQUEST);
|
|
reserve_info->Style = m_Style;
|
|
|
|
// Add QOS app ID later at ptr
|
|
ptr += sizeof(RSVP_RESERVE_INFO);
|
|
|
|
// Scale the flow spec for the sender (if needed)
|
|
ScaleFlowSpec(&qos.SendingFlowspec, 1, 1, m_MaxBandwidth);
|
|
|
|
// Remember when was the last time a reserve was done for the sender
|
|
// (not really reserve but specify the flowspec)
|
|
SetLastReserveTime(GetTickCount());
|
|
|
|
} else {
|
|
|
|
// Do not change the sender
|
|
qos.SendingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
|
|
|
reserve_info = (RSVP_RESERVE_INFO *)ptr;
|
|
|
|
// Partially Init RSVP_RESERVE_INFO
|
|
ZeroMemory(reserve_info, sizeof(RSVP_RESERVE_INFO));
|
|
reserve_info->ObjectHdr.ObjectType = RSVP_OBJECT_RESERVE_INFO;
|
|
reserve_info->ConfirmRequest =
|
|
flags_tst(FG_RES_CONFIRMATION_REQUEST);
|
|
reserve_info->Style = m_Style;
|
|
|
|
if (m_Style == RSVP_SHARED_EXPLICIT_STYLE) {
|
|
// Shared Explicit filter -- SEF
|
|
|
|
if (m_pRsvpFilterSpec && m_NumFilters > 0) {
|
|
// We have some filters
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(RECV) "
|
|
"Multicast(SE, %d)"),
|
|
m_NumFilters
|
|
));
|
|
|
|
// Scale the flow descriptor to m_NumFilters
|
|
ScaleFlowSpec(&qos.ReceivingFlowspec,
|
|
m_NumFilters,
|
|
m_MaxFilters,
|
|
m_MaxBandwidth);
|
|
|
|
// Build the ProviderSpecific buffer
|
|
|
|
flow_desc = (FLOWDESCRIPTOR *)(reserve_info + 1);
|
|
|
|
filterspec = (RSVP_FILTERSPEC *)(flow_desc + 1);
|
|
|
|
// Init RSVP_RESERVE_INFO
|
|
reserve_info->ObjectHdr.ObjectLength =
|
|
sizeof(RSVP_RESERVE_INFO) +
|
|
sizeof(FLOWDESCRIPTOR) +
|
|
(sizeof(RSVP_FILTERSPEC) * m_NumFilters);
|
|
reserve_info->NumFlowDesc = 1;
|
|
reserve_info->FlowDescList = flow_desc;
|
|
|
|
// Init FLOWDESCRIPTOR
|
|
CopyMemory(&flow_desc->FlowSpec,
|
|
&qos.ReceivingFlowspec,
|
|
sizeof(qos.ReceivingFlowspec));
|
|
flow_desc->NumFilters = m_NumFilters;
|
|
flow_desc->FilterList = filterspec;;
|
|
|
|
// Init RSVP_FILTERSPEC
|
|
CopyMemory(filterspec,
|
|
m_pRsvpFilterSpec,
|
|
m_NumFilters * sizeof(RSVP_FILTERSPEC));
|
|
|
|
// Add QOS app ID later at ptr
|
|
ptr = (char *)filterspec +
|
|
m_NumFilters * sizeof(RSVP_FILTERSPEC);
|
|
|
|
} else {
|
|
|
|
// Nothing selected yet, select BEST_EFFORT
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(RECV) "
|
|
"Multicast(SE, %d) pass to BEST EFFORT"),
|
|
m_NumFilters
|
|
));
|
|
|
|
qos.ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
|
|
|
|
// no reserve_info needed
|
|
// Don't add QOS app ID
|
|
reserve_info = (RSVP_RESERVE_INFO *)NULL;
|
|
|
|
// Not allowed to start notifications yet as
|
|
// we are going to request BEST EFFORT
|
|
m_pCShSocket->ModifyFlags(
|
|
dwIsSender? FG_SOCK_ENABLE_NOTIFY_SEND:
|
|
FG_SOCK_ENABLE_NOTIFY_RECV,
|
|
0);
|
|
}
|
|
|
|
} else if (m_Style == RSVP_WILDCARD_STYLE) {
|
|
// Share N*FlowSpec -- WF
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(RECV) Multicast(WF)")
|
|
));
|
|
|
|
// Scale the flow spec to m_MaxFilters
|
|
ScaleFlowSpec(&qos.ReceivingFlowspec,
|
|
m_MaxFilters,
|
|
m_MaxFilters,
|
|
m_MaxBandwidth);
|
|
|
|
// Add QOS app ID later at ptr
|
|
ptr = (char *)(reserve_info + 1);
|
|
|
|
} else {
|
|
// RSVP_DEFAULT_STYLE || RSVP_FIXED_FILTER_STYLE
|
|
// Unicast -- FF
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(RECV) Unicast(DEF STYLE)")
|
|
));
|
|
|
|
// Scale the flow spec to m_MaxFilters
|
|
ScaleFlowSpec(&qos.ReceivingFlowspec,
|
|
m_MaxFilters,
|
|
m_MaxFilters,
|
|
m_MaxBandwidth);
|
|
|
|
// Add QOS app ID later at ptr
|
|
ptr = (char *)(reserve_info + 1);
|
|
}
|
|
|
|
}
|
|
|
|
if (reserve_info) {
|
|
// Add QOS APP ID if reserve info is defined
|
|
len = AddQosAppID(ptr,
|
|
sizeof(buf) - (ptr - buf),
|
|
g_sPolicyLocator,
|
|
g_sAppName,
|
|
m_QOSclass,
|
|
m_QOSname);
|
|
|
|
if (len > 0) {
|
|
reserve_info->PolicyElementList = (RSVP_POLICY_INFO *)ptr;
|
|
ptr += len;
|
|
}
|
|
|
|
reserve_info->ObjectHdr.ObjectLength = (DWORD)
|
|
(ptr - (char *)reserve_info);
|
|
|
|
// Init ProviderSpecific
|
|
qos.ProviderSpecific.len = ptr - buf;
|
|
qos.ProviderSpecific.buf = buf;
|
|
}
|
|
|
|
DWORD outBufSize = 0;
|
|
|
|
// Set QOS using WSAIoctl
|
|
#if defined(DEBUG)
|
|
DWORD t0 = GetTickCount();
|
|
|
|
dumpQOS("CRtpQOSReserve::Reserve(before)", &qos);
|
|
|
|
if (qos.ProviderSpecific.buf &&
|
|
qos.ProviderSpecific.len >= sizeof(QOS_OBJECT_HDR)) {
|
|
|
|
dumpObjectType("CRtpQOSReserve::Reserve",
|
|
qos.ProviderSpecific.buf,
|
|
qos.ProviderSpecific.len);
|
|
}
|
|
#endif
|
|
if ( WSAIoctl(m_pCShSocket->GetShSocket(),
|
|
SIO_SET_QOS,
|
|
(LPVOID)&qos,
|
|
sizeof(qos),
|
|
NULL,
|
|
0,
|
|
&outBufSize,
|
|
NULL,
|
|
NULL) ) {
|
|
|
|
// WSAIoctl failed, disable notifications
|
|
m_pCShSocket->ModifyFlags(
|
|
dwIsSender? FG_SOCK_ENABLE_NOTIFY_SEND:
|
|
FG_SOCK_ENABLE_NOTIFY_RECV,
|
|
0);
|
|
|
|
hr = E_FAIL;
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(%s) WSAIoctl failed: %d"),
|
|
dwIsSender? "SEND":"RECV", WSAGetLastError()
|
|
));
|
|
} else {
|
|
#if defined(DEBUG)
|
|
DWORD t1 = GetTickCount();
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Reserve(%s) WSAIoctl succeeded, "
|
|
"DELAY: %d ms"),
|
|
dwIsSender? "SEND":"RECV", t1-t0
|
|
));
|
|
|
|
dumpQOS("CRtpQOSReserve::Reserve(after )", &qos);
|
|
|
|
if (qos.ProviderSpecific.buf &&
|
|
qos.ProviderSpecific.len >= sizeof(QOS_OBJECT_HDR)) {
|
|
|
|
dumpObjectType("CRtpQOSReserve::Reserve",
|
|
qos.ProviderSpecific.buf,
|
|
qos.ProviderSpecific.len);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CRtpQOSReserve::Unreserve(DWORD dwIsSender)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::Unreserve(%s)"),
|
|
dwIsSender? "SEND":"RECV"
|
|
));
|
|
|
|
QOS qos;
|
|
CopyMemory(&qos, &m_qos, sizeof(qos));
|
|
|
|
qos.ProviderSpecific.len = 0;
|
|
qos.ProviderSpecific.buf = NULL;
|
|
|
|
if (dwIsSender) {
|
|
qos.SendingFlowspec.ServiceType = SERVICETYPE_NOTRAFFIC;
|
|
qos.ReceivingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
|
} else {
|
|
qos.SendingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
|
qos.ReceivingFlowspec.ServiceType = SERVICETYPE_NOTRAFFIC;
|
|
}
|
|
|
|
// Disable notifications
|
|
m_pCShSocket->ModifyFlags(
|
|
dwIsSender? FG_SOCK_ENABLE_NOTIFY_SEND:FG_SOCK_ENABLE_NOTIFY_RECV,
|
|
0);
|
|
|
|
DWORD outBufSize = 0;
|
|
|
|
// Set QOS using WSAIoctl
|
|
if ( WSAIoctl(m_pCShSocket->GetShSocket(),
|
|
SIO_SET_QOS,
|
|
(LPVOID)&qos,
|
|
sizeof(qos),
|
|
NULL,
|
|
0,
|
|
&outBufSize,
|
|
NULL,
|
|
NULL) )
|
|
return(E_FAIL);
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// Ask for permission to send
|
|
HRESULT
|
|
CRtpQOSReserve::AllowedToSend()
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AllowedToSend")
|
|
));
|
|
|
|
#if defined(SIO_CHK_QOS)
|
|
|
|
DWORD request = ALLOWED_TO_SEND_DATA;
|
|
DWORD result;
|
|
DWORD bytes_returned = 0;
|
|
|
|
if ( WSAIoctl(m_pCShSocket->GetShSocket(),
|
|
SIO_CHK_QOS,
|
|
(LPVOID)&request,
|
|
sizeof(request),
|
|
(LPVOID)&result,
|
|
sizeof(result),
|
|
&bytes_returned,
|
|
NULL,
|
|
NULL) ) {
|
|
|
|
TraceDebug((
|
|
TRACE_ERROR,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AllowedToSend: "
|
|
"WSAIoctl(SIO_CHK_QOS) failed: %d"),
|
|
WSAGetLastError()
|
|
));
|
|
|
|
return(NO_ERROR); // For safety, return NOEROR
|
|
}
|
|
|
|
result = result? NOERROR : E_FAIL;
|
|
#else
|
|
result = NOERROR;
|
|
#endif
|
|
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::AllowedToSend: %s"),
|
|
(SUCCEEDED(result))? "YES":"NO"
|
|
));
|
|
|
|
return(result);
|
|
}
|
|
|
|
// Inquire about the link's speed
|
|
HRESULT
|
|
CRtpQOSReserve::LinkSpeed(DWORD *pdwLinkSpeed)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::LinkSpeed")
|
|
));
|
|
|
|
CheckPointer(pdwLinkSpeed, E_POINTER);
|
|
|
|
*pdwLinkSpeed = 0;
|
|
|
|
#if defined(SIO_CHK_QOS)
|
|
|
|
DWORD request = LINE_RATE;
|
|
|
|
if ( WSAIoctl(m_pCShSocket->GetShSocket(),
|
|
SIO_CHK_QOS,
|
|
(LPVOID)&request,
|
|
sizeof(request),
|
|
NULL,
|
|
0,
|
|
pdwLinkSpeed,
|
|
NULL,
|
|
NULL) ) {
|
|
return(E_FAIL);
|
|
}
|
|
#endif
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
// Inquire about the estimated available bandwidth
|
|
HRESULT
|
|
CRtpQOSReserve::EstimatedAvailableBandwidth(DWORD *pdwBandwidth)
|
|
{
|
|
TraceDebug((
|
|
TRACE_TRACE,
|
|
TRACE_DEVELOP,
|
|
TEXT("CRtpQOSReserve::EstimatedAvailableBandwidth")
|
|
));
|
|
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
/*+++
|
|
|
|
Description:
|
|
|
|
This routine generates the application identity PE given the
|
|
name and policy locator strings for the application.
|
|
|
|
szAppName is used to construct the CREDENTIAL attribute of the
|
|
Identity PE. Its subtype is set to ASCII_ID.
|
|
|
|
szPolicyLocator is used to construct the POLICY_LOCATOR
|
|
attribute of the Identity PE. Its subtype is set to ASCII_DN.
|
|
|
|
Refer to draft-ietf-rap-rsvp-identity-03.txt and
|
|
draft-bernet-appid-00.txt for details on the Identity Policy
|
|
Elements. Also draft-bernet-appid-00.txt conatins some
|
|
examples for arguments szPolicyLocator and szAppName.
|
|
|
|
The PE is generated in the supplied buffer. If the length of
|
|
the buffer is not enough, zero is returned.
|
|
|
|
Parameters: szAppName app name, string, caller supply
|
|
szPolicyLocator Policy Locator string, caller supply
|
|
wBufLen length of caller allocated buffer
|
|
pAppIdBuf pointer to caller allocated buffer
|
|
|
|
Return Values:
|
|
Number of bytes used from buffer
|
|
---*/
|
|
DWORD AddQosAppID(
|
|
IN OUT char *pAppIdBuf,
|
|
IN WORD wBufLen,
|
|
IN const char *szPolicyLocator,
|
|
IN const char *szAppName,
|
|
IN const char *szAppClass,
|
|
IN char *szQosName
|
|
)
|
|
{
|
|
RSVP_POLICY_INFO *pPolicyInfo = ( RSVP_POLICY_INFO* )pAppIdBuf;
|
|
RSVP_POLICY* pAppIdPE;
|
|
IDPE_ATTR *pAttr;
|
|
USHORT nAppIdAttrLen;
|
|
USHORT nPolicyLocatorAttrLen;
|
|
USHORT nTotalPaddedLen;
|
|
char str[128];
|
|
|
|
// Calculate the length of the buffer required
|
|
strcpy(str,",SAPP=");
|
|
strcat(str, szAppClass);
|
|
strcat(str, ",SAPP=");
|
|
strcat(str, szQosName);
|
|
|
|
nPolicyLocatorAttrLen =
|
|
IDPE_ATTR_HDR_LEN + strlen( szPolicyLocator ) + strlen( str ) + 1;
|
|
|
|
nAppIdAttrLen = IDPE_ATTR_HDR_LEN + strlen( szAppName ) + 1;
|
|
|
|
nTotalPaddedLen = sizeof( RSVP_POLICY_INFO ) - ( sizeof( UCHAR ) * 4 ) +
|
|
RSVP_BYTE_MULTIPLE( nAppIdAttrLen ) +
|
|
RSVP_BYTE_MULTIPLE( nPolicyLocatorAttrLen );
|
|
|
|
// If the supplied buffer is not long enough, return 0
|
|
if( wBufLen < nTotalPaddedLen )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ZeroMemory( pAppIdBuf, nTotalPaddedLen );
|
|
|
|
// Set the RSVP_POLICY_INFO header
|
|
pPolicyInfo->ObjectHdr.ObjectType = RSVP_OBJECT_POLICY_INFO;
|
|
pPolicyInfo->ObjectHdr.ObjectLength = nTotalPaddedLen;
|
|
pPolicyInfo->NumPolicyElement = 1;
|
|
|
|
// Now set up RSVP_POLICY object header
|
|
pAppIdPE = pPolicyInfo->PolicyElement;
|
|
pAppIdPE->Len = RSVP_POLICY_HDR_LEN +
|
|
RSVP_BYTE_MULTIPLE( nAppIdAttrLen ) +
|
|
RSVP_BYTE_MULTIPLE( nPolicyLocatorAttrLen );
|
|
pAppIdPE->Type = PE_TYPE_APPID;
|
|
|
|
// The first application id attribute is the policy locator string
|
|
pAttr = ( IDPE_ATTR * )( (char *)pAppIdPE + RSVP_POLICY_HDR_LEN );
|
|
|
|
// Set the attribute length in network order.
|
|
pAttr->PeAttribLength = htons( nPolicyLocatorAttrLen );
|
|
pAttr->PeAttribType = PE_ATTRIB_TYPE_POLICY_LOCATOR;
|
|
pAttr->PeAttribSubType = POLICY_LOCATOR_SUB_TYPE_ASCII_DN;
|
|
strcpy( (char *)pAttr->PeAttribValue, szPolicyLocator );
|
|
strcat( (char *)pAttr->PeAttribValue, str );
|
|
|
|
// The application name attribute comes next
|
|
pAttr = ( IDPE_ATTR * )( (char*)pAttr +
|
|
RSVP_BYTE_MULTIPLE( nPolicyLocatorAttrLen ) );
|
|
|
|
pAttr->PeAttribLength = htons( nAppIdAttrLen );
|
|
pAttr->PeAttribType = PE_ATTRIB_TYPE_CREDENTIAL;
|
|
pAttr->PeAttribSubType = CREDENTIAL_SUB_TYPE_ASCII_ID;
|
|
strcpy( ( char * )pAttr->PeAttribValue, szAppName );
|
|
|
|
return( nTotalPaddedLen );
|
|
}
|