隨著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
聶文龍 閱讀(1857)
評論(2) 編輯 收藏 引用 所屬分類:
net work