Source code of Windows XP (NT5)
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

/*++
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 );
}