📄 btsdpcon.cpp
字号:
tmpStream += cs.GetTotalSize();
ASSERT((tmpStream - stream) == totalSize);
#endif // DBG
SdpPrint(SDP_DBG_CLIENT_INFO, ("sending initial request for service attribute search\n"));
return SendInitialRequestToServer(pIrp, stream, (USHORT) totalSize, pdu.pduId);
Error:
if (stream) {
ExFreePool(stream);
}
if (pTreeService) {
SdpFreeTree(pTreeService);
}
if (pTreeAttrib) {
SdpFreeTree(pTreeAttrib);
}
ReleaseAndProcessNextRequest(pIrp);
return status;
}
#if ! (defined (UNDER_CE) || defined (WINCE_EMULATION))
BOOLEAN
SdpConnection::_RegisterConnectionOnTimer(
SdpConnection *pConnection,
BOOLEAN Register
)
{
BOOLEAN empty;
BOOLEAN res = TRUE;
static BOOLEAN firstCall = TRUE;
if (firstCall) {
firstCall = FALSE;
InitializeListHead(&_TimeoutListHead);
KeInitializeSpinLock(&_TimeoutListLock);
KeInitializeTimerEx(&_TimeoutTimer, NotificationTimer);
KeInitializeDpc(&_TimeoutTimerDpc,
_TimeoutTimerDpcProc,
NULL);
}
KIRQL irql;
KeAcquireSpinLock(&_TimeoutListLock, &irql);
if (Register == FALSE) {
SdpConnection *pConFind = NULL;
PLIST_ENTRY pEntry;
res = FALSE;
SdpPrint(SDP_DBG_TO_INFO,
("trying to unregister connection %p\n", pConnection));
pEntry = _TimeoutListHead.Flink;
while (pEntry != &_TimeoutListHead) {
pConFind = CONTAINING_RECORD(pEntry, SdpConnection, timerEntry);
if (pConFind == pConnection) {
res = TRUE;
RemoveEntryList(&pConnection->timerEntry);
pConnection->Release(&_TimeoutTimerDpc);
SdpPrint(SDP_DBG_TO_INFO, ("found it in list, removing\n"));
break;
}
pEntry = pEntry->Flink;
}
if (IsListEmpty(&_TimeoutListHead)) {
SdpPrint(SDP_DBG_TO_TRACE, ("stopping timeout timer\n"));
KeCancelTimer(&_TimeoutTimer);
}
}
else {
empty = IsListEmpty(&_TimeoutListHead);
InsertTailList(&_TimeoutListHead, &pConnection->timerEntry);
pConnection->timeoutTime = 0x0;
pConnection->Acquire(&_TimeoutTimerDpc);
SdpPrint(SDP_DBG_TO_INFO, ("adding connection %p to timeout list\n", pConnection));
if (empty) {
LARGE_INTEGER scanTime;
SdpPrint(SDP_DBG_TO_TRACE, ("starting timeout timer\n"));
scanTime = RtlConvertLongToLargeInteger(-10*1000*1000 * SDP_SCAN_INTERVAL);
KeSetTimerEx(&_TimeoutTimer,
scanTime,
SDP_SCAN_INTERVAL * 1000, // in ms
&_TimeoutTimerDpc);
}
}
KeReleaseSpinLock(&_TimeoutListLock, irql);
return res;
}
void
SdpConnection::_TimeoutTimerDpcProc(
IN PKDPC Dpc,
IN PVOID DeviceObject,
IN PVOID Context1,
IN PVOID Context2
)
{
CList<SdpConnection, FIELD_OFFSET(SdpConnection, timerEntry)> list;
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Context1);
UNREFERENCED_PARAMETER(Context2);
KIRQL irql;
KeAcquireSpinLock(&_TimeoutListLock, &irql);
SdpPrint(SDP_DBG_TO_TRACE, ("entering timeout timer DPC\n"))
SdpConnection *pCon = NULL;
PLIST_ENTRY pEntry;
pEntry = _TimeoutListHead.Flink;
while (pEntry != &_TimeoutListHead) {
LONG count;
pCon = CONTAINING_RECORD(pEntry, SdpConnection, timerEntry);
count = InterlockedIncrement(&pCon->timeoutTime);
SdpPrint(SDP_DBG_TO_INFO,
("connection %p has elapsed %d s, will timeout at %d s\n",
pCon, count, pCon->maxTimeout));
//
// Advance to the next element first and foremost
//
pEntry = pEntry->Flink;
//
// If we have timed out or the requestor has cancelled their request
// we will yank it out of the timer list and cancel the underlying
// read request. In the read completion routine we will process accordingly
//
// NOTE: letting the user cancel the request before we get a response
// is tricky b/c we are only allowed on pended request to th server
// and cancelling the underlying read means we need to shut down
// entire connection and that is not fair for all the other applications
// that are piggybacking this connection
//
if (count == pCon->maxTimeout /* || pCon->pRequestorIrp->Cancel*/) {
//
// Then remove this element from the list and then add it to our
// local list
//
RemoveEntryList(&pCon->timerEntry);
list.AddTail(pCon);
}
}
KeReleaseSpinLock(&_TimeoutListLock, irql);
while (!list.IsEmpty()) {
pCon = list.RemoveHead();
SdpPrint(SDP_DBG_TO_INFO,
("cancelling irp %p on connection %p\n", pCon->pReadIrp, pCon));
IoCancelIrp(pCon->pReadIrp);
pCon->Release(&_TimeoutTimerDpc);
}
}
NTSTATUS
SdpConnection::_TimeoutDisconnectComplete(
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp,
PVOID pContext
)
{
SdpConnection *pCon = (SdpConnection *) pContext;
SdpPrint(SDP_DBG_TO_TRACE, ("timeout disconnection complete\n"));
//
// By setting the address to zero, we are allowing new connections to be
// created to this device (b/c we will not find an active connection in the
// SdpInterface list)
//
pCon->btAddress = 0x0;
pCon->Release(pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SdpConnection::_ReadAsyncCompletion(
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp,
PVOID pContext
)
{
SdpPrint(SDP_DBG_BRB_TRACE, ("read async complete, status = 0x%x\n",
pIrp->IoStatus.Status));
UNREFERENCED_PARAMETER(pDeviceObject);
UNREFERENCED_PARAMETER(pIrp);
SdpConnection *pCon = (SdpConnection*) pContext;
BOOLEAN removed;
//
// Will only be in the timer queue if the read is from a client connection
//
if (pCon->IsClient()) {
removed = _RegisterConnectionOnTimer(pCon, FALSE);
}
else {
removed = TRUE;
}
//
// If we have successfully removed the conneciton from the timer, then the
// timer did not have a change to timeout our I/O and we can proceed. If
// the remove failed, then the timer has fired and cancelled our I/O. There
// is an edge condition where the I/O completes and have removed the connection
// from the timer list in the timer (to cancel it) but have not yet actually
// cancelled the irp. Because of this, we must allocate a new irp to do the
// disconnect (but we can still reuse our read brb).
//
if (removed == FALSE) {
SdpPrint(SDP_DBG_TO_INFO,
("read async complete, connection NOT in timer queue\n"));
//
// The user must close the connection up receiving this error code. We
// shall disconnect from the device but keep the SdpConnection in the
// SdpInterface list until all clients close down their handles.
//
KIRQL irql;
//
// By setting the channel state to closed, SdpInterface::AcquireConnection
// will gracefully fail, not allowing any new queries to go out on the
// wire.
//
pCon->LockState(&irql);
pCon->channelState = SdpChannelStateClosed;
pCon->UnlockState(irql);
//
// Can't complete the client requests here b/c we free pool when we do so
// and we could be at DPC freeing paged pool. Just drop into the worker
// item and do it there.
//
}
pCon->ReadAsyncComplete();
return STATUS_MORE_PROCESSING_REQUIRED;
}
void SdpConnection::ReadAsyncComplete()
{
ExInitializeWorkItem(&readWorkItem, _ReadWorkItemCallback, (PVOID) this);
ExQueueWorkItem(&readWorkItem, DelayedWorkQueue);
}
void SdpConnection::_ReadWorkItemCallback(IN PVOID Parameter)
{
((SdpConnection*) Parameter)->ReadWorkItemWorker();
}
void SdpConnection::ReadWorkItemWorker()
{
if (GetChannelState() == SdpChannelStateClosed) {
//
// Channel is closed, clean up
//
// Passing STATIS_IO_TIMEOUT will complete all of the pended irps as
// well
//
CompleteClientRequest(0, STATUS_IO_TIMEOUT);
//
// The timer could have this connection in its local list and cancel cancel
// this read irp....so we can use pCon->pReadIrp...BUT we can use
// pCon->pWriteIrp b/c it is in no timer queue and thus cannot be
// cancelled
//
BRB *pBrb = pReadBrb;
RtlZeroMemory(pBrb, sizeof(BRB));
pBrb->BrbHeader.Length = sizeof (_BRB_L2CA_DISCONNECT_REQ);
pBrb->BrbHeader.Type = BRB_L2CA_DISCONNECT_REQ;
pBrb->BrbHeader.Status = STATUS_SUCCESS;
pBrb->BrbL2caDisconnectReq.hConnection = hConnection;
IoReuseIrp(pWriteIrp, STATUS_SUCCESS);
Acquire(pWriteIrp);
SendBrbAsync(pBrb, _TimeoutDisconnectComplete, pWriteIrp, (PVOID) this);
}
else if (!NT_SUCCESS(pReadIrp->IoStatus.Status)) {
if (IsClient()) {
CompleteClientRequest(0, pReadIrp->IoStatus.Status);
}
else {
Release(pReadIrp);
}
}
else if (pReadBrb->BrbL2caAclTransfer.BufferSize < sizeof(SdpPDU)) {
if (IsClient()) {
CompleteClientRequest(0, STATUS_INVALID_NETWORK_RESPONSE);
}
else {
USHORT transactionId;
if (pReadBrb->BrbL2caAclTransfer.BufferSize >=
(sizeof(UCHAR) + sizeof(USHORT))) {
RtlRetrieveUshort(&transactionId,
(((PUCHAR) pReadBrb->BrbL2caAclTransfer.Buffer)+1));
transactionId = RtlUshortByteSwap(transactionId);
}
else {
//
// Is there a good value for this?
//
transactionId = 0x00;
}
WriteSdpError(transactionId , SDP_ERROR_INVALID_PDU_SIZE);
}
}
else {
PUCHAR pResponse;
SdpPDU pdu;
pResponse = (PUCHAR) pdu.Read(pReadBrb->BrbL2caAclTransfer.Buffer);
ULONG_PTR info = 0;
NTSTATUS status = STATUS_SUCCESS;
SDP_ERROR sdpError;
if (IsClient()) {
if (pRequestorIrp->Cancel) {
CompleteClientRequest(0, STATUS_CANCELLED);
return;
}
switch (pdu.pduId) {
case SdpPDU_ServiceSearchResponse:
status = OnServiceSearchResponse(&pdu, pResponse, &info);
break;
case SdpPDU_ServiceAttributeResponse:
status = OnServiceAttributeResponse(&pdu, pResponse, &info);
break;
case SdpPDU_ServiceSearchAttributeResponse:
status = OnServiceSearchAttributeResponse(&pdu, pResponse, &info);
break;
case SdpPDU_Error:
default:
status = STATUS_INVALID_NETWORK_RESPONSE;
break;
}
if (status != STATUS_PENDING) {
CompleteClientRequest(info, status);
}
}
else {
switch (pdu.pduId) {
case SdpPDU_ServiceSearchRequest:
ASSERT(!IsClient());
status = OnServiceSearchRequest(&pdu, pResponse, &sdpError);
break;
case SdpPDU_ServiceAttributeRequest:
ASSERT(!IsClient());
status = OnServiceAttributeRequest(&pdu, pResponse, &sdpError);
break;
case SdpPDU_ServiceSearchAttributeRequest:
ASSERT(!IsClient());
status = OnServiceSearchAttributeRequest(&pdu, pResponse, &sdpError);
break;
default:
sdpError = SDP_ERROR_INVALID_REQUEST_SYNTAX;
status = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(status)) {
ReadAsync();
WriteSdpError(pdu.transactionId, sdpError);
}
}
}
}
#else // UNDER_CE
NTSTATUS SdpConnection::ReadWorkItemWorker(PUCHAR pResponse, USHORT usSize) {
SdpPDU pdu;
ULONG_PTR info = 0; // ignored by WinCE.
NTSTATUS status = STATUS_SUCCESS;
SDP_ERROR sdpError;
if (usSize < sizeof(SdpPDU)) {
if (IsClient()) {
return ERROR_INVALID_PARAMETER;
}
else {
USHORT transactionId;
if (usSize >= sizeof(UCHAR) + sizeof(USHORT)) {
RtlRetrieveUshort(&transactionId,pResponse+1);
transactionId = RtlUshortByteSwap(transactionId);
}
else {
transactionId = 0x00;
}
WriteSdpError(transactionId , SDP_ERROR_INVALID_PDU_SIZE);
}
}
pResponse = (PUCHAR) pdu.Read(pResponse);
if (IsClient()) {
switch (pdu.pduId) {
case SdpPDU_ServiceSearchResponse:
status = OnServiceSearchResponse(&pdu, pResponse, &info);
break;
case SdpPDU_ServiceAttributeResponse:
status = OnServiceAttributeResponse(&pdu, pResponse, &info);
break;
case SdpPDU_ServiceSearchAttributeResponse:
status = OnServiceSearchAttributeResponse(&pdu, pResponse, &info);
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -