青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2016年7月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

SPI
隨著WOSA模型的出現,在Ws2_32.dll和協議堆棧之間多了一層開放的接口,就是SPI。Winsock2 SPI和Winsock2 API在性質上是一樣的,只是他們的服務對象不同,API提供的接口工作在應用層的上層,為應用程序提供接口,在Winsock之上,而SPI提供的接口工作在應用層的最底層,為核心的網絡服務提供接口,在Winsock之下。如果按照OSI分層標準來劃分,SPI應該是工作在會話層,API工作在應用層。如果有人非要象我一開始這樣,非要分出個一二三四的話,這樣的理解或許能讓你得到一點滿足,當然這種理解不是完全正確的,但是至少在層次的先后上,這樣的理解應該是正確的。我們的工作逐漸進入了OSI的下層,要學習,就要多費一些口舌了。

SPI的上沿是Ws2_32.dll,而它的下沿是協議堆棧和名字空間,當然分層服務可以多層疊放,擴展上下沿。這里講述向協議堆棧的服務。SPI包含兩種服務,一種是基礎服務,一種是分層服務。

SPI實例實際上就是一個動態庫,開放了一個初始化函數:WSPStartup 或者 NSPStartup。其他函數的指針通過一個函數分配表在此初始化函數中指定給Ws2_32.dll。Ws2_32.dll在需要的時候將相應的服務/協議實現模塊裝載入內存,在不需要的時候卸載。

一般來說,當一個應用程序調用 Winsock2 API 函數的時候,Winsock2 都會調用相應的 Winsock2 SPI 函數。如 WSPAccept, WSPConnect, etc. 下述特例不經過SPI:
htonl,htons,ntohs,ntohl,inet_addr,inet_ntoa,gethostname,getXbyY,WSAAsnyGetXByY,wsacANCELaSYNCrEQUEST,WSAEnumProtocols,WSAIsBlocking,WSASetBlocking,WSAUnhookBlocking,WSAGetLastError,WSASetLastError,WSACreateEvent,WSACloseEvent,WSASetEvent,WSAResetEvent,WSAWaitForMultipleEvents.SPI的函數原型定義在ws2spi.h中,前綴都有WSP,NSP,WPU,WSC,有興趣可以瀏覽一下。相應的對應函數有30個。都會在WSPStartup的實現中得到這些函數指針。

要注意的是:WSPStartup并不是在WSAStartup調用時被調用的,一旦要WSPStartup的時候也就是說明需要用到服務提供者了,那么Ws2_32什么時候加載服務提供者呢?應用程序創建了套接字的時候,Ws2_32就根據套接字的地址家族,類型,協議信息等加載相應的提供者,這時候,WSPStartup就會被調用,服務提供者就開始調度它的傳輸函數等等內容了。

現在有個問題,既然我們知道Windows Sockets動態庫和協議堆棧之間沒有直接聯系了,那么,如果我的應用程序想要調用我的SPI實例的擴展函數的時候那該怎么辦呢?Ws2_32.dll不可能再擴展專門的函數調用,讓他按照規范再去調用協議堆棧提供者的擴展函數。其實,Ws2_32.dll提供了一個函數來解決這個額外的擴展問題,那就是WSAIoctl函數,通過命令碼 SIO_GET_EXTENSION_FUNCTION_POINTER,輸入緩沖區是擴展函數的標識符,輸出參數就是該函數的指針了。那么,應用程序就可以直接跳過Ws2_32.dll而直接調用擴展函數。

在動手編寫SPI實例之前,我們需要了解一下基礎服務和分層服務的區別以及系統如何標志這兩種服務,然后講述如何在系統中安裝一個自己的SPI實例,來擴展你的網絡傳輸功能。以及如何卸載自己的SPI實例,來恢復系統原來默認的設置。最后再來講述如何編寫SPI的實例。

基礎服務執行核心的網絡傳輸協議功能,而分層服務在基礎服務的基礎上執行自定義的通訊控制,所有的真正的數據交換是通過基礎服務提供者來實現的,分層服務提供者無需再去實現網絡協議的功能。簡單的網絡封包的截取和管理可以在此進行。

需要提及套接字句柄。當調用下層SPI的時候,如果調用WSPSocket,WSPAccept,WSPJoinLeaf時,服務提供者必須返回一個套接字句柄,Winsock2允許在此句柄上直接調用 ReadFile/WriteFile來讀寫數據。這個句柄有兩種類型:IFS(可安裝文件系統句柄)和 Non IFS(不可安裝的文件系統句柄)。微軟的基礎服務提供者都是IFS句柄,但是分層服務提供者可以是IFS,也可以是Non IFS。但是如果分層提供者是IFS提供者,就必須將下一級IFS提供者的句柄上傳到上一級提供者或者WS2_32.dll,此時對套接字的ReadFile/WriteFile調用會跳過分層服務提供者的WSPSend/WSPRecv函數而直接通過基礎服務提供者的功能進行數據讀寫,而且分層服務提供者將不能處理一個完成端口的重疊I/0操作,這些操作也將繞過分層提供者,而直接通過基礎提供者完成數據的讀寫。所以,最好將分層服務提供者定義為Non IFS句柄的服務提供者,這樣就可以對網絡數據進行完整的監控。具體做法是在協議信息結構中將dwServiceFlags1標志的XP1_IFS_HANDLES標志去掉。這樣,你的分層提供這就是一個Non IFS分層服務提供者,Non IFS服務提供者使用WSPCreateSocketHandle上調函數建立套接字句柄,Winsock2會把ReadFile/WriteFile的調用重定向到WSPSend和WSPReceive上去,但這無疑會對系統性能產生負面影響。

Windows系統為服務提供者維護了一個目錄,這些信息保存在注冊表中,要更改/安裝服務提供者,就必須對此目錄信息進行維護。系統提供了一些函數簡化對此信息的訪問,他們都是以WSC開頭。

對于基礎服務提供者的安裝相當簡單,只要準備一個WSAPROTOCOL_INFOW結構,用來代表基礎服務提供者信息,正確填充合適的值,然后調用WSCInstallProvider函數就可以完成基礎服務提供者的安裝。但是這種方法只對新增加的基礎服務提供者來講是很方便的,但是我們往往要利用系統的基礎服務提供者來實現基本的協議,比如說TCP協議的實現,所以在這種情況下,方便的方法是不通過WSCInstallProvider函數,而是我們自己修改目錄條目信息,對于基礎提供者,只要把基礎提供者的動態庫路徑信息改成我們自己虛擬的基礎服務提供者路徑就可以了。這樣安裝的后續工作必須是把所有調用傳給原來的基礎服務提供者,所以在我們虛擬的服務提供者程序中,必須能夠檢索到原來的服務提供者路徑信息。記住,安裝之前千萬要備份原來的目錄信息。否則,一旦發生錯誤會引起網絡無法訪問。

基礎服務提供者安裝代碼實例如下所示:

void CBSPinstallDlg::OnBtnInstall() 
{
// Update m_strFileName which is our mimic Base Provider filename
UpdateData(TRUE);
if(0==BackupBSP()) InstallBSP();
}

int CBSPinstallDlg::EnumRegisterKey(HKEY hKey,TCHAR* pszKeyName,TCHAR** ppBuf,DWORD* pdwBufSize)
{
HKEY hSubKey=NULL;
if(RegOpenKeyEx(hKey,pszKeyName,0,KEY_ALL_ACCESS,&hSubKey)!=ERROR_SUCCESS) 
return -1;
TCHAR szKey[20]={'\0'};
if(hKey==HKEY_CURRENT_CONFIG) strcpy(szKey,"HKEY_LOCAL_MACHINE"; else
if(hKey==HKEY_CURRENT_USER) strcpy(szKey,"HKEY_CURRENT_USER"; else
if(hKey==HKEY_LOCAL_MACHINE) strcpy(szKey,"HKEY_LOCAL_MACHINE"; else
if(hKey==HKEY_USERS) strcpy(szKey,"HKEY_USERS"; else
if(hKey==HKEY_PERFORMANCE_DATA) strcpy(szKey,"HKEY_PERFORMANCE_DATA"; else
if(hKey==HKEY_DYN_DATA) strcpy(szKey,"HKEY_DYN_DATA"; else 
return -1;

int len=strlen(szKey)+strlen(pszKeyName)+5;
TCHAR* pszItem=new TCHAR[len+1];
memset(pszItem,0,len+1);
sprintf(pszItem,"[%s\\%s]\r\n",szKey,pszKeyName);

TCHAR* pBuf=*ppBuf;
DWORD dwBufSize=*pdwBufSize;
TCHAR* pTmp=new TCHAR[dwBufSize+len];
memset(pTmp,0,dwBufSize+len);
memmove(pTmp,pBuf,dwBufSize);
memmove(pTmp+dwBufSize,pszItem,len);
delete[] pszItem; pszItem=NULL;
delete[] pBuf; pBuf=NULL;
dwBufSize+=len;
*ppBuf=pTmp;
*pdwBufSize=dwBufSize;

// "Num_Catalog_Entries"=dword:00000013
// "Next_Catalog_Entry_ID"=dword:0000043e
// "Serial_Access_Num"=dword:00000014
DWORD cbClass=0;
DWORD cSubKeys=0;
DWORD cbMaxSubKeyLen=0;
DWORD cbMaxClassLen=0;
DWORD cValues=0;
DWORD cbMaxValueNameLen=0;
DWORD cbMaxValueLen=0;
int nRet=RegQueryInfoKey(hSubKey,
NULL,
&cbClass,
NULL,
&cSubKeys,
&cbMaxSubKeyLen,
&cbMaxClassLen,
&cValues,
&cbMaxValueNameLen,
&cbMaxValueLen,
NULL,
NULL);
if(nRet!=ERROR_SUCCESS) 
return -1;
for(DWORD dwIndex=0;dwIndex<cValues;dwIndex++)
{
DWORD dwItemNameSize=cbMaxValueNameLen+1;
TCHAR* pszItemName=new TCHAR[dwItemNameSize];
memset(pszItemName,0,dwItemNameSize);
DWORD dwDataSize=cbMaxValueLen+1;
BYTE* pbyData=new BYTE[dwDataSize];
memset(pbyData,0,dwDataSize);
DWORD dwType=0;
nRet=RegEnumValue(hSubKey,dwIndex,pszItemName,&dwItemNameSize,NULL,&dwType,pbyData,&dwDataSize);
if(nRet!=ERROR_SUCCESS)
{
delete[] pszItemName; pszItemName=NULL;
delete[] pbyData; pbyData=NULL;
return -1;
}
TCHAR* pBuf=*ppBuf;
DWORD dwBufSize=*pdwBufSize;
if(dwDataSize>0)
{
TCHAR* szTmp=new TCHAR[dwDataSize*4+266];
memset(szTmp,0,dwDataSize*4+266);
szTmp[0]='"';
int nPos=1;
memmove(szTmp+nPos,pszItemName,strlen(pszItemName));
nPos+=strlen(pszItemName);
szTmp[nPos]='"';nPos++;
szTmp[nPos]='=';nPos++;
switch(dwType)
{
case REG_SZ:
szTmp[nPos]='"';nPos++;
memmove(szTmp,pbyData,dwDataSize);
nPos+=dwDataSize;
szTmp[nPos]='"';nPos++;
szTmp[nPos]='\r';nPos++;
szTmp[nPos]='\n';nPos++;
break;
case REG_DWORD:
{
char sz[20]="dword:\0";
sprintf(sz,"%s%08x",sz,*(DWORD*)pbyData);
memmove(szTmp+nPos,sz,strlen(sz));
nPos+=strlen(sz);
}
break;
case REG_BINARY:
{
char sz[20]="hex:\0";
memmove(szTmp+nPos,sz,strlen(sz));
nPos+=strlen(sz);
int j=0;
for(UINT i=0;i<dwDataSize;i++)
{
if(( (j==0 && nPos%78==0) || \
 ( j>0 && (nPos-81-(j-1)*80)%77==0) \
    && i<dwDataSize-1)
{
j++;
szTmp[nPos]='\\';nPos++;
szTmp[nPos]='\r';nPos++;
szTmp[nPos]='\n';nPos++;
if(j>0)
{
szTmp[nPos]=' ';nPos++;
szTmp[nPos]=' ';nPos++;
}
}
if(i<dwDataSize-1)
{
TCHAR szAppend[20]={'\0'};
sprintf(szAppend,"%02x,",pbyData[i]);
strcat(szTmp,szAppend);
nPos+=3;
}
else
{
TCHAR szAppend[20]={'\0'};
sprintf(szAppend,"%02x",pbyData[i]);
strcat(szTmp,szAppend);
nPos+=2;
}
}
}
default:
break;
}
delete[] pszItemName; pszItemName=NULL;
delete[] pbyData; pbyData=NULL;
szTmp[nPos]='\r';nPos++;
szTmp[nPos]='\n';nPos++;
TCHAR* pTmp=new TCHAR[dwBufSize+nPos];
memset(pTmp,0,dwBufSize+nPos);
memmove(pTmp,pBuf,dwBufSize);
memmove(pTmp+dwBufSize,szTmp,nPos);
delete[] szTmp;szTmp=NULL;
delete[] pBuf;pBuf=NULL;
dwBufSize+=nPos;
*ppBuf=pTmp;
*pdwBufSize=dwBufSize;
}
if(pszItemName) delete[] pszItemName; pszItemName=NULL;
if(pbyData) delete[] pbyData; pbyData=NULL;
}

{
TCHAR* pBuf=*ppBuf;
DWORD dwBufSize=*pdwBufSize;
TCHAR* pTmp=new TCHAR[dwBufSize+2];
memset(pTmp,0,dwBufSize+2);
memmove(pTmp,pBuf,dwBufSize);
pTmp[dwBufSize]='\r';
pTmp[dwBufSize+1]='\n';
*ppBuf=pTmp;
*pdwBufSize=dwBufSize+2;
delete[] pBuf; pBuf=NULL;
}

DWORD dwSubKeyLen=cbMaxSubKeyLen+1;
// [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries]
for(dwIndex=0;dwIndex<cSubKeys;dwIndex++)
{
TCHAR* szKeyName=new TCHAR[dwSubKeyLen];
memset(szKeyName,0,dwSubKeyLen);
int nRet=RegEnumKey(hSubKey,dwIndex,szKeyName,dwSubKeyLen);
if(nRet!=ERROR_SUCCESS)
{
delete[] szKeyName; szKeyName=NULL;
return -1;
}
TCHAR szKeyNameAnother[266]={'\0'};
wsprintf(szKeyNameAnother,"%s\\%s",pszKeyName,szKeyName);
delete[] szKeyName; szKeyName=NULL;
nRet=EnumRegisterKey(HKEY_LOCAL_MACHINE,szKeyNameAnother,ppBuf,pdwBufSize);
if(nRet==-1) return -1;
}

RegCloseKey(hSubKey);
return 0;
}

int CBSPinstallDlg::BackupBSP()
{
TCHAR szSysDir[266]={'\0'};
GetWindowsDirectory(szSysDir,266);
TCHAR szBackupFile[266]={'\0'};
sprintf(szBackupFile,"%s\\MinBSP.reg",szSysDir);
CFileStatus status;
if(CFile::GetStatus(szBackupFile,status))
{
if(IDNO==::MessageBox(NULL,"你已經安裝了基礎服務提供者,是不是還要繼續安裝?","確認",MB_YESNO))
{
PostQuitMessage(0);
return -1;
}
}

if(m_strFileName.GetLength()==0)
{
AfxMessageBox("請指定基礎服務提供者。";
m_edtFileName.SetFocus();
m_edtFileName.SetSel(0,-1);
return -1;
}

// 開始安裝
// 首先備份原來的服務提供者目錄條目信息
CFile f;
if(f.Open(szBackupFile,CFile::modeCreate|CFile::modeReadWrite))
{
TCHAR szTmp[266]="Windows Registry Editor Version 5.00\r\n\r\n\0";
DWORD dwBufSize=strlen(szTmp);
TCHAR* lpBuf=new TCHAR[dwBufSize];
memset(lpBuf,0,dwBufSize);
memmove(lpBuf,szTmp,dwBufSize);

TCHAR szKeyName[1024]="SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters\\Protocol_Catalog9\0";
int nRet=EnumRegisterKey(HKEY_LOCAL_MACHINE,szKeyName,&lpBuf,&dwBufSize);
if(nRet==-1) {f.Close();delete[] lpBuf;lpBuf=NULL;return -1;}

nRet=dwBufSize*2+2;
WCHAR* pwszResult=new WCHAR[nRet];
memset(pwszResult,0,nRet);
nRet=MultiByteToWideChar(CP_ACP,0,lpBuf,dwBufSize,pwszResult,nRet);
BYTE by=0xFF;
f.Write(&by,1);
by=0xFE;
f.Write(&by,1);
f.Write(pwszResult,nRet*2);
f.Close();
delete[] pwszResult; pwszResult=NULL;
delete[] lpBuf;lpBuf=NULL;
}
return 0;
}

void CBSPinstallDlg::InstallBSP()
{
// 備份完成
// 接下來,更改系統原來的基礎服務提供者目錄條目信息為我的基礎提供者信息
// 枚舉協議
LPWSAPROTOCOL_INFOW lpProtocolInfo=NULL;
DWORD dwProtocolInfoSize=0;
int nErrorCode=0;
int nRet=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
if(nRet==SOCKET_ERROR && nErrorCode!=WSAENOBUFS)
{
TRACE1("WSCEnumProtocols() returns error: %d\n",nErrorCode);
return;
}
lpProtocolInfo=(LPWSAPROTOCOL_INFOW) new BYTE[dwProtocolInfoSize];
memset(lpProtocolInfo,0,dwProtocolInfoSize);
__try
{
// 取得協議信息
int nTotalProtocols=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
if(nTotalProtocols==SOCKET_ERROR) return;
// 查找TCP/IP協議信息
TCHAR szTCPIP[20]="[TCP/IP]\0";
TCHAR szTCP95[20]=".TCP\0";
TCHAR szProtocol[MAX_PATH]={'\0'};
TCHAR szKeyName[]="SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters\\Protocol_Catalog9\\Catalog_Entries\0";
HKEY hSubKey=NULL;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKeyName,0,KEY_ALL_ACCESS,&hSubKey)!=ERROR_SUCCESS) return;
for(int i=0;i<nTotalProtocols;i++)
{
memset(szProtocol,0,MAX_PATH);
WCHAR* pwsz=(lpProtocolInfo+i)->szProtocol;
BSTR bstr=SysAllocString(pwsz);
WideCharToMultiByte(CP_ACP,0,bstr,-1,szProtocol,255,NULL,NULL);
SysFreeString(bstr);
// if(strstr(szProtocol,szTCPIP)!=NULL || strstr(szProtocol,szTCP95)!=NULL) break;
if((lpProtocolInfo+i)->ProtocolChain.ChainLen==1)
{
// 更改基礎服務提供者信息
TCHAR szSubKeyName[MAX_PATH]={'\0'};
int nRet=RegEnumKey(hSubKey,i,szSubKeyName,MAX_PATH);
if(nRet!=ERROR_SUCCESS) return;
HKEY hSubKeySub=NULL;
DWORD dwSubKeyLen=strlen(szKeyName);
TCHAR* pszKeyName=new TCHAR[dwSubKeyLen+strlen(szSubKeyName)+2];
memset(pszKeyName,0,dwSubKeyLen+strlen(szSubKeyName)+2);
wsprintf(pszKeyName,"%s\\%s",szKeyName,szSubKeyName);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,pszKeyName,0,KEY_ALL_ACCESS,&hSubKeySub)!=ERROR_SUCCESS)
{
if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
return;
}
DWORD cbClass=0;
DWORD cSubKeys=0;
DWORD cbMaxSubKeyLen=0;
DWORD cbMaxClassLen=0;
DWORD cValues=0;
DWORD cbMaxValueNameLen=0;
DWORD cbMaxValueLen=0;
nRet=RegQueryInfoKey(hSubKeySub,
NULL,
&cbClass,
NULL,
&cSubKeys,
&cbMaxSubKeyLen,
&cbMaxClassLen,
&cValues,
&cbMaxValueNameLen,
&cbMaxValueLen,
NULL,
NULL);
if(nRet!=ERROR_SUCCESS) 
{
cbMaxValueNameLen=MAX_PATH;
cbMaxValueLen=MAX_PATH+sizeof(WSAPROTOCOL_INFOW)+2;
}
DWORD dwItemNameSize=cbMaxValueNameLen+1;
TCHAR* pszItemName=new TCHAR[dwItemNameSize];
memset(pszItemName,0,dwItemNameSize);
DWORD dwDataSize=cbMaxValueLen+1;
BYTE* pbyData=new BYTE[dwDataSize];
memset(pbyData,0,dwDataSize);
DWORD dwType=0;
nRet=RegEnumValue(hSubKeySub,0,pszItemName,&dwItemNameSize,NULL,&dwType,pbyData,&dwDataSize);
if(nRet!=ERROR_SUCCESS)
{
if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
if(pszItemName) delete[] pszItemName; pszItemName=NULL;
if(pbyData) delete[] pbyData; pbyData=NULL;
return;
}
memset(pbyData,0,MAX_PATH);
TCHAR szDllPath[MAX_PATH+2];
wsprintf(szDllPath,"%%SystemRoot%%\\System32\\%s",m_strFileName);
int nLen=strlen(szDllPath);
memmove(pbyData,szDllPath,nLen);
nRet=RegSetValueEx(hSubKeySub,pszItemName,NULL,dwType,pbyData,dwDataSize);
if(nRet!=ERROR_SUCCESS)
{
if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
if(pszItemName) delete[] pszItemName; pszItemName=NULL;
if(pbyData) delete[] pbyData; pbyData=NULL;
return;
}
if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
if(pszItemName) delete[] pszItemName; pszItemName=NULL;
if(pbyData) delete[] pbyData; pbyData=NULL;
}
}

if(i<nTotalProtocols) TRACE1("TCP/IP 協議信息在目錄條目中的第 %d 項。\n",i+1);
else TRACE("無法根據協議描述判斷TCP/IP協議信息。\n";
}
__finally
{
if(lpProtocolInfo) delete[] lpProtocolInfo;
lpProtocolInfo=NULL;
}
}

而對于分層服務提供者而言,安裝要復雜一點。他需要建立兩個WSAPROTOCOL_INFOW結構,一個代表分層提供者(ChainLen==0),另一個代表協議鏈(ChainLen>1),利用該協議鏈把分層服務提供者和基礎服務提供者連接起來。
分層服務提供者安裝的具體的步驟,概括起來大致分為五步:
第一,初始化兩個協議信息結構,初始化可以借助于WSCEnumProtocols函數取回的信息來完成;
第二,用WSCInstallProvider調用來安裝你的分層服務提供者;
第三,在第二步成功的基礎上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號。并利用這個目錄信息來設置一個協議鏈的目錄信息,通過這個協議鏈,將你的分層提供者和基礎服務提供者(或者是它下一層的分層協議提供者)連接起來。
第四,調用WSCInstallProvider安裝這個協議鏈。
第五,分層服務提供者安裝成功后,Windows會在服務提供者目錄的最后加入這個新的條目。但是我們為了讓我們的分層服務提供者成為系統默認的TCP服務提供者的話,就必須對目錄進行重新排列,讓新安裝的協議鏈目錄條目放在最上面。這些排序過程通過調用WSCWriteProviderOrder完成,該函數將所有的服務提供者重新排序。需要包含 <sporder.h>頭文件和sporder.lib庫。

以下是單純針對TCP協議(其他協議處理方法雷同)的分層服務提供者示例:

int CLSPinstallDlg::EnumProtocols(LPWSAPROTOCOL_INFOW* lppInfo,DWORD& dwProtocolInfoSize,int& nTotalProtocols)
{
LPWSAPROTOCOL_INFOW lpProtocolInfo=*lppInfo;
if(lpProtocolInfo!=NULL) delete[] lpProtocolInfo; lpProtocolInfo=NULL;
int nErrorCode=0;
int nRet=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
if(nRet==SOCKET_ERROR&&nErrorCode!=WSAENOBUFS) return nErrorCode;
lpProtocolInfo = (LPWSAPROTOCOL_INFOW)new BYTE[dwProtocolInfoSize];
ASSERT(lpProtocolInfo!=NULL);
nTotalProtocols = WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
if(nTotalProtocols== SOCKET_ERROR) return -1;
*lppInfo=lpProtocolInfo;

return 0;
}

void CLSPinstallDlg::InstallLSP()
{
LPWSAPROTOCOL_INFOW lpProtocolInfo=NULL;
DWORD dwProtocolInfoSize=0;
int nTotalProtocols=0;
_try
{
// 第一步,初始化兩個協議信息結構,初始化可以借助于WSCEnumProtocols函數取回的信息來完成;
int nRet=EnumProtocols(&lpProtocolInfo,dwProtocolInfoSize,nTotalProtocols);
if(nRet!=0)
{
TRACE1("WSCEnumProtocols() returns error: %d\n",nRet);
return;
}
DWORD dwLayeredCatalogId,dwTcpCatalogId;
WSAPROTOCOL_INFOW stuLayeredInfo,stuTcpChainInfo;
for(int i=0;i<nTotalProtocols;i++)
{
if(lpProtocolInfo[i].iAddressFamily == AF_INET && lpProtocolInfo[i].iProtocol == IPPROTO_TCP)
{
dwTcpCatalogId = lpProtocolInfo[i].dwCatalogEntryId;
memmove(&stuTcpChainInfo, &lpProtocolInfo[i], sizeof(WSAPROTOCOL_INFOW));
memmove(&stuLayeredInfo, &lpProtocolInfo[i], sizeof(WSAPROTOCOL_INFOW));
stuTcpChainInfo.dwServiceFlags1 = lpProtocolInfo[i].dwServiceFlags1 & ~XP1_IFS_HANDLES; 
break;
}
}

// 第二步,用WSCInstallProvider調用來安裝你的分層服務提供者;
wcscpy(stuLayeredInfo.szProtocol, L"Mini Layered Provider";
stuLayeredInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL;
WCHAR wszDllPath[MAX_PATH*2+2]={'\0'};
swprintf(wszDllPath,L"%%SystemRoot%%\\System32\\%s",m_strFileName);
int nErrorCode=0;
nRet=WSCInstallProvider(&guidMiniProvider,wszDllPath,&stuLayeredInfo,1,&nErrorCode);
if(nRet==SOCKET_ERROR)
{
printf("WSCInstallProvider failed %d\n", nErrorCode);
return;
}

// 第三步,在第二步成功的基礎上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號。
// 并利用這個目錄信息來設置一個協議鏈的目錄信息,通過這個協議鏈,將分層提供者和基礎服務提供者
// (或者是它下一層的分層協議提供者)連接起來。
nRet=EnumProtocols(&lpProtocolInfo,dwProtocolInfoSize,nTotalProtocols);
if(nRet!=0)
{
TRACE1("WSCEnumProtocols() returns error: %d\n",nRet);
return;
}
for (i=0;i<nTotalProtocols;i++)
{
if(memcmp(&lpProtocolInfo[i].ProviderId, &guidMiniProvider,sizeof (GUID))==0)
{
dwLayeredCatalogId = lpProtocolInfo[i].dwCatalogEntryId;
break;
}
}
WCHAR wszChainName[MAX_PATH*2+2]={'\0'};
swprintf(wszChainName, L"Mini Layered TCP [%s]", stuTcpChainInfo.szProtocol);
wcscpy(stuTcpChainInfo.szProtocol,wszChainName);
if (stuTcpChainInfo.ProtocolChain.ChainLen==BASE_PROTOCOL)
{
stuTcpChainInfo.ProtocolChain.ChainEntries[1] = dwTcpCatalogId;
}
else
{
for (i=stuTcpChainInfo.ProtocolChain.ChainLen;i>0;i--)
{
stuTcpChainInfo.ProtocolChain.ChainEntries[i+1] = stuTcpChainInfo.ProtocolChain.ChainEntries[i];
}
}
stuTcpChainInfo.ProtocolChain.ChainLen++;
stuTcpChainInfo.ProtocolChain.ChainEntries[0] = dwLayeredCatalogId;

// 第四步,調用WSCInstallProvider安裝這個協議鏈
nRet=WSCInstallProvider(&guidMiniProviderChain,wszDllPath,&stuTcpChainInfo,1,&nErrorCode);
if(nRet==SOCKET_ERROR)
{
printf("WSCInstallProvider for protocol chain failed %d\n", nErrorCode);
return;
}

// 第五步,安裝成功后將所有的服務提供者重新排序。
LPDWORD lpCatalogEntries=(LPDWORD)new BYTE[nTotalProtocols*sizeof(DWORD)];
ASSERT(lpCatalogEntries!=NULL);
memset(lpCatalogEntries,0,nTotalProtocols*sizeof(DWORD));
DWORD dwIndex=0;
for (i=0;i<nTotalProtocols;i++)
{
if(memcmp(&lpProtocolInfo[i].ProviderId, &guidMiniProvider, sizeof (GUID))==0 || \
   memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProviderChain, sizeof (GUID))==0)
lpCatalogEntries[dwIndex++] = lpProtocolInfo[i].dwCatalogEntryId;
}
for (i=0;i<nTotalProtocols;i++)
{
if(memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProvider, sizeof (GUID))!=0 && \
   memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProviderChain, sizeof (GUID))!=0)
lpCatalogEntries[dwIndex++]=lpProtocolInfo[i].dwCatalogEntryId;
}
nRet=WSCWriteProviderOrder(lpCatalogEntries,nTotalProtocols);
delete[] lpCatalogEntries; lpCatalogEntries=NULL; 
if(nRet!=ERROR_SUCCESS)
{
printf("WSCWriteProviderOrder failed %d\n", nErrorCode);
return;
}
}
__finally
{
if(lpProtocolInfo) delete[] lpProtocolInfo; lpProtocolInfo=NULL;
}
}

好了,到目前,我們安裝了基礎服務提供者和分層服務提供者,到了該卸載他們的時候了,對于基礎服務提供者,簡單地恢復原來的目錄信息就可以了,對于分層服務提供者,調用WSCDeinstallProvider卸載函數可以簡單的卸載,但是對于分層服務提供者,一定要維護好協議鏈,把所有和你的分層服務提供者的協議鏈相關的信息都清除掉,恢復安裝之前的協議鏈狀態,免得發生協議鏈斷開的現象發生,如果斷開了,就會影響網絡的功能。Windows SPI在這方面存在缺陷,如果很多人裝了很多個分層服務提供者,那怎么辦呢?會很麻煩的。

接下來,我們最后來看看怎么樣實現一個服務提供者的最基本的實例,我們可以試圖在這里對網絡封包進行截取。

以下是基礎服務提供者實例,對于分層服務提供者,麻煩!我懶得寫了:

int WSPAPI WSPStartup(
    WORD wVersion,
    LPWSPDATA lpWSPData,
    LPWSAPROTOCOL_INFOW lpProtocolInfo,
    WSPUPCALLTABLE UpCallTable,
    LPWSPPROC_TABLE lpProcTable)
{

int nTotalProtocols=0;
    int nProviderPathLen=MAX_PATH;
    TCHAR szLibraryPath[MAX_PATH]={'\0'};
    TCHAR szProviderPath[MAX_PATH]={'\0'};;
    LPWSAPROTOCOL_INFOW lpOrigProtocolInfo=NULL;
theApp.m_hLibOrigBase=NULL;
    LPWSPSTARTUP WSPStartupFunc = NULL;

    EnterCriticalSection(&theApp.m_CriticalSection);
    if (!theApp.m_nEntityCount)
    {
CFile f;
TCHAR szFileName[MAX_PATH]={'\0'};
GetWindowsDirectory(szFileName,MAX_PATH);
sprintf(szFileName,"%s\\%s",szFileName,OLD_BSP_CATALOG);
if(!f.Open(szFileName,CFile::modeRead)) return WSAEPROVIDERFAILEDINIT;
DWORD dwLen=f.GetLength();
DWORD dwSize=MAX_PATH+sizeof(WSAPROTOCOL_INFOW);
nTotalProtocols=dwLen/dwSize;
lpOrigProtocolInfo=new WSAPROTOCOL_INFOW[nTotalProtocols];
for(int i=0;i<nTotalProtocols;i++)
{
f.Seek(dwSize*i+MAX_PATH,0);
f.Read(&lpOrigProtocolInfo[i],sizeof(WSAPROTOCOL_INFOW));
if(lpProtocolInfo->ProviderId==lpOrigProtocolInfo->ProviderId)
{
f.Seek(dwSize*i,0);
f.Read(szProviderPath,MAX_PATH);
break;
}
}
f.Close();
delete[] lpOrigProtocolInfo; lpOrigProtocolInfo=NULL;
if(!ExpandEnvironmentStrings(szProviderPath,szLibraryPath,MAX_PATH)) return WSAEPROVIDERFAILEDINIT;

theApp.m_hLibOrigBase=LoadLibrary(szLibraryPath);
if(theApp.m_hLibOrigBase==NULL) return WSAEPROVIDERFAILEDINIT;

WSPStartupFunc=(LPWSPSTARTUP)GetProcAddress(theApp.m_hLibOrigBase,"WSPStartup";
if(WSPStartupFunc==NULL) return WSAEPROVIDERFAILEDINIT;

int nRet=(*WSPStartupFunc)(wVersion,lpWSPData,lpProtocolInfo,UpCallTable,lpProcTable);
if(nRet!=ERROR_SUCCESS) return nRet;

memmove(&theApp.m_NextProcTable, lpProcTable, sizeof(WSPPROC_TABLE));

lpProcTable->lpWSPAccept = WSPAccept;
lpProcTable->lpWSPAddressToString = WSPAddressToString;
lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
lpProcTable->lpWSPBind = WSPBind;
lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
lpProcTable->lpWSPCleanup = WSPCleanup;
lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
lpProcTable->lpWSPConnect = WSPConnect;
lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
lpProcTable->lpWSPEventSelect = WSPEventSelect;
lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
lpProcTable->lpWSPGetSockName = WSPGetSockName;
lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
lpProcTable->lpWSPIoctl = WSPIoctl;
lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
lpProcTable->lpWSPListen = WSPListen;
lpProcTable->lpWSPRecv = WSPRecv;
lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
lpProcTable->lpWSPSelect = WSPSelect;
lpProcTable->lpWSPSend = WSPSend;
lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
lpProcTable->lpWSPSendTo = WSPSendTo;
lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
lpProcTable->lpWSPShutdown = WSPShutdown;
lpProcTable->lpWSPSocket = WSPSocket;
lpProcTable->lpWSPStringToAddress = WSPStringToAddress;

theApp.m_lpWSPData = lpWSPData;
theApp.m_lpProcTable = lpProcTable;
    }
else
    {
        lpWSPData = theApp.m_lpWSPData;
        lpProcTable = theApp.m_lpProcTable;
    }
    theApp.m_nEntityCount++;
    LeaveCriticalSection(&theApp.m_CriticalSection);

   return 0;
}

int WSPAPI WSPCleanup (LPINT lpErrno)
{
int nRet=theApp.m_NextProcTable.lpWSPCleanup(lpErrno);

EnterCriticalSection(&theApp.m_CriticalSection);
theApp.m_nEntityCount--;
    if (theApp.m_nEntityCount == 0)
    {
        FreeLibrary(theApp.m_hLibOrigBase);
        theApp.m_hLibOrigBase = NULL;
    }
    LeaveCriticalSection(&theApp.m_CriticalSection);

return nRet;
}

對于不同的I/0操作模式,需要進行不同的處理
對于WSARecv,WSARecvFrom,WSASend,WSASendTo的重疊調用,保存重疊信息,重點是完成例程
// 客戶程序如果用完成例程,那么將不能直接監視到收發的數據,為了解決這個問題
// 我們可以在服務提供者內提供我們自己的完成例程截獲數據,在我們截獲了數據之后,
// 再把控制權交還給客戶機的完成例程。
//
// 首先,保存重疊操作參數信息的結構
typedef struct tagOVERLAPPED_RECORDER
{
DWORD                  dwType; // WSASend,WSASendTo,WSARecv,WSARecvFrom
                                   // 用來標志監視的數據操作方式
    SOCKET                 s,                                                 
    LPWSABUF               lpBuffers,                                       
    DWORD                  dwBufferCount,                                      
    LPDWORD                lpNumberOfBytesSent,                              
    DWORD                  dwFlags,                                            
  const SOCKADDR_IN FAR  *lpFromOrTo,                         
    int                    iFromOrTolen,                        
    LPWSAOVERLAPPED        lpOverlapped,                             
LPWSAOVERLAPPED_COMPLETION_ROUTINE   lpCompletionRoutine,
    LPWSATHREADID          lpThreadId,                            
  LPINT                  lpErrno  
}
OVERLAPPED_RECORDER, *POVERLAPPED_RECORDER, *LPOVERLAPPED_RECORDER;

// 其次替換重疊操作的完成例程為自定義的例程
// 例程完成后,控制權交給客戶機的完成例程,我們可以刪除上面保存的重疊參數信息結構了
// 完成例程原型如下:
void CALLBACK CompletionRoutine (
 IN    DWORD             dwError, 
 IN    DWORD             cbTransferred, 
 IN    LPWSAOVERLAPPED   lpOverlapped, 
 IN    DWORD             dwFlags 
);

posted on 2007-08-17 13:19 聶文龍 閱讀(1884) 評論(2)  編輯 收藏 引用 所屬分類: net work

FeedBack:
# re: SPI 2015-11-25 10:35 李超
最近在弄這方面的東西,大神能否知道一下怎么處理不同的I/O操作?  回復  更多評論
  
# re: SPI 2015-11-25 10:36 李超
可能比較久遠了。。。  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              在线日本欧美| 久久久精品国产免大香伊| 亚洲欧美一区二区在线观看| 亚洲精品在线二区| 亚洲人成人99网站| 亚洲激情一区二区| 亚洲精品日韩久久| 中文一区二区在线观看| 亚洲一区二区欧美| 西西裸体人体做爰大胆久久久| 亚洲欧美日韩视频二区| 午夜视频一区二区| 久久人人看视频| 欧美激情亚洲视频| 日韩亚洲精品电影| 午夜亚洲精品| 久久亚洲欧美| 欧美日韩中文精品| 国产麻豆综合| 亚洲电影欧美电影有声小说| 日韩一级黄色大片| 午夜欧美大尺度福利影院在线看| 久久久www成人免费无遮挡大片| 美女视频黄a大片欧美| 亚洲日本久久| 在线天堂一区av电影| 性欧美办公室18xxxxhd| 欧美jjzz| 国产亚洲观看| 一区二区三区高清不卡| 久久久噜噜噜久久中文字幕色伊伊| 亚洲丰满少妇videoshd| 亚洲自拍三区| 欧美国产一区二区在线观看 | 亚洲国产精品久久久| 日韩视频一区二区三区| 久久久久久久久久久久久女国产乱| 欧美激情一区二区三区在线视频观看 | 亚洲人在线视频| 小处雏高清一区二区三区| 欧美va亚洲va国产综合| 国产毛片一区| 亚洲视频在线观看网站| 欧美风情在线观看| 欧美一区=区| 欧美午夜片欧美片在线观看| 宅男精品视频| 免播放器亚洲| 国产一区二区三区日韩| 亚洲免费一在线| 亚洲电影有码| 久久综合伊人77777尤物| 国产欧美在线看| 亚洲免费视频观看| 亚洲最新色图| 欧美人成免费网站| 99re热这里只有精品免费视频| 久久亚洲二区| 欧美与黑人午夜性猛交久久久| 国产精品日韩精品欧美精品| 亚洲一区在线免费| 日韩一级片网址| 欧美久久成人| 一本久久综合亚洲鲁鲁| 亚洲国产mv| 欧美国产一区二区在线观看| 91久久在线观看| 欧美第一黄色网| 久久综合色综合88| 亚洲国产精品欧美一二99| 欧美成人性生活| 欧美成人午夜激情视频| 日韩视频一区二区在线观看 | 亚洲综合另类| 国产麻豆成人精品| 久久久青草婷婷精品综合日韩| 欧美一级午夜免费电影| 国语自产精品视频在线看一大j8| 久久精品五月婷婷| 久久亚洲精品一区二区| 亚洲精品女人| 日韩亚洲欧美一区| 国产精品永久免费观看| 欧美一区二区三区喷汁尤物| 午夜视频精品| 亚洲大胆人体视频| 亚洲精品影院在线观看| 国产精品成人一区| 久久久久se| 欧美高清在线观看| 午夜视频在线观看一区二区| 久久久777| 一区二区三区四区五区精品| 亚洲欧美日韩一区在线观看| 激情欧美丁香| 亚洲乱码一区二区| 国内一区二区三区在线视频| 亚洲国产另类久久精品| 国产精品久久久一区二区| 久久久久在线| 欧美日本乱大交xxxxx| 久久国产精品久久国产精品| 欧美成人三级在线| 欧美一区二区视频观看视频| 久久伊人一区二区| 亚洲综合视频1区| 久久综合国产精品| 国产精品久久久久久模特| 欧美一区二区三区免费视| 欧美成人视屏| 久久久久久久欧美精品| 欧美日韩亚洲天堂| 欧美高清视频一二三区| 国产伦精品一区二区三区免费| 欧美好吊妞视频| 国产精品永久免费观看| 9久草视频在线视频精品| 亚洲国产精品久久人人爱蜜臀| 香蕉精品999视频一区二区 | 免费成人av| 国产精品羞羞答答xxdd| 亚洲高清av| 亚洲成人资源| 久久精品九九| 欧美一区国产在线| 欧美无乱码久久久免费午夜一区 | 国产农村妇女精品一区二区| 最近看过的日韩成人| 一区二区在线观看av| 亚洲嫩草精品久久| 亚洲小说欧美另类社区| 欧美精品一区二区三区一线天视频| 免费精品99久久国产综合精品| 国产热re99久久6国产精品| 中文久久精品| 一区二区三区日韩欧美| 欧美精品在线一区| 亚洲精品国精品久久99热| 亚洲人精品午夜| 久久在线观看视频| 欧美国产日韩二区| 亚洲国产欧美一区二区三区久久 | 欧美大尺度在线| 亚洲电影中文字幕| 欧美在线综合| 久久亚洲午夜电影| 一区二区三区在线观看欧美| 久久国产精品久久久| 久久米奇亚洲| 国内精品久久久| 久久全国免费视频| 欧美国产乱视频| 亚洲精品自在久久| 欧美视频在线播放| 亚洲欧美国产日韩天堂区| 久久成人精品一区二区三区| 国内精品视频在线观看| 久久成人精品| 亚洲电影天堂av| 亚洲午夜高清视频| 国产欧美日韩在线视频| 久久精品国产欧美激情| 嫩草成人www欧美| 亚洲精品日本| 国产精品卡一卡二卡三| 午夜亚洲性色福利视频| 欧美xart系列高清| 国产精品99免视看9| 亚洲图色在线| 另类酷文…触手系列精品集v1小说| 亚洲国产影院| 国产精品va| 久久男人资源视频| 91久久精品国产| 午夜天堂精品久久久久| 亚洲第一天堂av| 欧美日韩亚洲一区二| 性久久久久久| 亚洲精品国产品国语在线app| 午夜精品久久久久久99热软件| 国产一区二区三区在线播放免费观看 | 国产情人节一区| 欧美激情1区| 欧美在线高清| 9色porny自拍视频一区二区| 久久久国际精品| 这里只有精品在线播放| 狠色狠色综合久久| 国产精品久久久久秋霞鲁丝| 久久亚洲电影| 午夜视频在线观看一区二区三区| 亚洲国产精品一区二区第四页av | 欧美精品一区三区| 久久丁香综合五月国产三级网站| 亚洲区在线播放| 看片网站欧美日韩| 午夜视频一区在线观看| 一区二区精品| 亚洲日本在线观看| 一区在线观看视频|