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

隨筆 - 298  文章 - 377  trackbacks - 0
<2016年6月>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊(cè)

收藏夾

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

SPI
隨著WOSA模型的出現(xiàn),在Ws2_32.dll和協(xié)議堆棧之間多了一層開(kāi)放的接口,就是SPI。Winsock2 SPI和Winsock2 API在性質(zhì)上是一樣的,只是他們的服務(wù)對(duì)象不同,API提供的接口工作在應(yīng)用層的上層,為應(yīng)用程序提供接口,在Winsock之上,而SPI提供的接口工作在應(yīng)用層的最底層,為核心的網(wǎng)絡(luò)服務(wù)提供接口,在Winsock之下。如果按照OSI分層標(biāo)準(zhǔn)來(lái)劃分,SPI應(yīng)該是工作在會(huì)話層,API工作在應(yīng)用層。如果有人非要象我一開(kāi)始這樣,非要分出個(gè)一二三四的話,這樣的理解或許能讓你得到一點(diǎn)滿足,當(dāng)然這種理解不是完全正確的,但是至少在層次的先后上,這樣的理解應(yīng)該是正確的。我們的工作逐漸進(jìn)入了OSI的下層,要學(xué)習(xí),就要多費(fèi)一些口舌了。

SPI的上沿是Ws2_32.dll,而它的下沿是協(xié)議堆棧和名字空間,當(dāng)然分層服務(wù)可以多層疊放,擴(kuò)展上下沿。這里講述向協(xié)議堆棧的服務(wù)。SPI包含兩種服務(wù),一種是基礎(chǔ)服務(wù),一種是分層服務(wù)。

SPI實(shí)例實(shí)際上就是一個(gè)動(dòng)態(tài)庫(kù),開(kāi)放了一個(gè)初始化函數(shù):WSPStartup 或者 NSPStartup。其他函數(shù)的指針通過(guò)一個(gè)函數(shù)分配表在此初始化函數(shù)中指定給Ws2_32.dll。Ws2_32.dll在需要的時(shí)候?qū)⑾鄳?yīng)的服務(wù)/協(xié)議實(shí)現(xiàn)模塊裝載入內(nèi)存,在不需要的時(shí)候卸載。

一般來(lái)說(shuō),當(dāng)一個(gè)應(yīng)用程序調(diào)用 Winsock2 API 函數(shù)的時(shí)候,Winsock2 都會(huì)調(diào)用相應(yīng)的 Winsock2 SPI 函數(shù)。如 WSPAccept, WSPConnect, etc. 下述特例不經(jīng)過(guò)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的函數(shù)原型定義在ws2spi.h中,前綴都有WSP,NSP,WPU,WSC,有興趣可以瀏覽一下。相應(yīng)的對(duì)應(yīng)函數(shù)有30個(gè)。都會(huì)在WSPStartup的實(shí)現(xiàn)中得到這些函數(shù)指針。

要注意的是:WSPStartup并不是在WSAStartup調(diào)用時(shí)被調(diào)用的,一旦要WSPStartup的時(shí)候也就是說(shuō)明需要用到服務(wù)提供者了,那么Ws2_32什么時(shí)候加載服務(wù)提供者呢?應(yīng)用程序創(chuàng)建了套接字的時(shí)候,Ws2_32就根據(jù)套接字的地址家族,類型,協(xié)議信息等加載相應(yīng)的提供者,這時(shí)候,WSPStartup就會(huì)被調(diào)用,服務(wù)提供者就開(kāi)始調(diào)度它的傳輸函數(shù)等等內(nèi)容了。

現(xiàn)在有個(gè)問(wèn)題,既然我們知道Windows Sockets動(dòng)態(tài)庫(kù)和協(xié)議堆棧之間沒(méi)有直接聯(lián)系了,那么,如果我的應(yīng)用程序想要調(diào)用我的SPI實(shí)例的擴(kuò)展函數(shù)的時(shí)候那該怎么辦呢?Ws2_32.dll不可能再擴(kuò)展專門的函數(shù)調(diào)用,讓他按照規(guī)范再去調(diào)用協(xié)議堆棧提供者的擴(kuò)展函數(shù)。其實(shí),Ws2_32.dll提供了一個(gè)函數(shù)來(lái)解決這個(gè)額外的擴(kuò)展問(wèn)題,那就是WSAIoctl函數(shù),通過(guò)命令碼 SIO_GET_EXTENSION_FUNCTION_POINTER,輸入緩沖區(qū)是擴(kuò)展函數(shù)的標(biāo)識(shí)符,輸出參數(shù)就是該函數(shù)的指針了。那么,應(yīng)用程序就可以直接跳過(guò)Ws2_32.dll而直接調(diào)用擴(kuò)展函數(shù)。

在動(dòng)手編寫SPI實(shí)例之前,我們需要了解一下基礎(chǔ)服務(wù)和分層服務(wù)的區(qū)別以及系統(tǒng)如何標(biāo)志這兩種服務(wù),然后講述如何在系統(tǒng)中安裝一個(gè)自己的SPI實(shí)例,來(lái)擴(kuò)展你的網(wǎng)絡(luò)傳輸功能。以及如何卸載自己的SPI實(shí)例,來(lái)恢復(fù)系統(tǒng)原來(lái)默認(rèn)的設(shè)置。最后再來(lái)講述如何編寫SPI的實(shí)例。

基礎(chǔ)服務(wù)執(zhí)行核心的網(wǎng)絡(luò)傳輸協(xié)議功能,而分層服務(wù)在基礎(chǔ)服務(wù)的基礎(chǔ)上執(zhí)行自定義的通訊控制,所有的真正的數(shù)據(jù)交換是通過(guò)基礎(chǔ)服務(wù)提供者來(lái)實(shí)現(xiàn)的,分層服務(wù)提供者無(wú)需再去實(shí)現(xiàn)網(wǎng)絡(luò)協(xié)議的功能。簡(jiǎn)單的網(wǎng)絡(luò)封包的截取和管理可以在此進(jìn)行。

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

Windows系統(tǒng)為服務(wù)提供者維護(hù)了一個(gè)目錄,這些信息保存在注冊(cè)表中,要更改/安裝服務(wù)提供者,就必須對(duì)此目錄信息進(jìn)行維護(hù)。系統(tǒng)提供了一些函數(shù)簡(jiǎn)化對(duì)此信息的訪問(wèn),他們都是以WSC開(kāi)頭。

對(duì)于基礎(chǔ)服務(wù)提供者的安裝相當(dāng)簡(jiǎn)單,只要準(zhǔn)備一個(gè)WSAPROTOCOL_INFOW結(jié)構(gòu),用來(lái)代表基礎(chǔ)服務(wù)提供者信息,正確填充合適的值,然后調(diào)用WSCInstallProvider函數(shù)就可以完成基礎(chǔ)服務(wù)提供者的安裝。但是這種方法只對(duì)新增加的基礎(chǔ)服務(wù)提供者來(lái)講是很方便的,但是我們往往要利用系統(tǒng)的基礎(chǔ)服務(wù)提供者來(lái)實(shí)現(xiàn)基本的協(xié)議,比如說(shuō)TCP協(xié)議的實(shí)現(xiàn),所以在這種情況下,方便的方法是不通過(guò)WSCInstallProvider函數(shù),而是我們自己修改目錄條目信息,對(duì)于基礎(chǔ)提供者,只要把基礎(chǔ)提供者的動(dòng)態(tài)庫(kù)路徑信息改成我們自己虛擬的基礎(chǔ)服務(wù)提供者路徑就可以了。這樣安裝的后續(xù)工作必須是把所有調(diào)用傳給原來(lái)的基礎(chǔ)服務(wù)提供者,所以在我們虛擬的服務(wù)提供者程序中,必須能夠檢索到原來(lái)的服務(wù)提供者路徑信息。記住,安裝之前千萬(wàn)要備份原來(lái)的目錄信息。否則,一旦發(fā)生錯(cuò)誤會(huì)引起網(wǎng)絡(luò)無(wú)法訪問(wèn)。

基礎(chǔ)服務(wù)提供者安裝代碼實(shí)例如下所示:

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,"你已經(jīng)安裝了基礎(chǔ)服務(wù)提供者,是不是還要繼續(xù)安裝?","確認(rèn)",MB_YESNO))
{
PostQuitMessage(0);
return -1;
}
}

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

// 開(kāi)始安裝
// 首先備份原來(lái)的服務(wù)提供者目錄條目信息
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()
{
// 備份完成
// 接下來(lái),更改系統(tǒng)原來(lái)的基礎(chǔ)服務(wù)提供者目錄條目信息為我的基礎(chǔ)提供者信息
// 枚舉協(xié)議
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
{
// 取得協(xié)議信息
int nTotalProtocols=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
if(nTotalProtocols==SOCKET_ERROR) return;
// 查找TCP/IP協(xié)議信息
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)
{
// 更改基礎(chǔ)服務(wù)提供者信息
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 協(xié)議信息在目錄條目中的第 %d 項(xiàng)。\n",i+1);
else TRACE("無(wú)法根據(jù)協(xié)議描述判斷TCP/IP協(xié)議信息。\n";
}
__finally
{
if(lpProtocolInfo) delete[] lpProtocolInfo;
lpProtocolInfo=NULL;
}
}

而對(duì)于分層服務(wù)提供者而言,安裝要復(fù)雜一點(diǎn)。他需要建立兩個(gè)WSAPROTOCOL_INFOW結(jié)構(gòu),一個(gè)代表分層提供者(ChainLen==0),另一個(gè)代表協(xié)議鏈(ChainLen>1),利用該協(xié)議鏈把分層服務(wù)提供者和基礎(chǔ)服務(wù)提供者連接起來(lái)。
分層服務(wù)提供者安裝的具體的步驟,概括起來(lái)大致分為五步:
第一,初始化兩個(gè)協(xié)議信息結(jié)構(gòu),初始化可以借助于WSCEnumProtocols函數(shù)取回的信息來(lái)完成;
第二,用WSCInstallProvider調(diào)用來(lái)安裝你的分層服務(wù)提供者;
第三,在第二步成功的基礎(chǔ)上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號(hào)。并利用這個(gè)目錄信息來(lái)設(shè)置一個(gè)協(xié)議鏈的目錄信息,通過(guò)這個(gè)協(xié)議鏈,將你的分層提供者和基礎(chǔ)服務(wù)提供者(或者是它下一層的分層協(xié)議提供者)連接起來(lái)。
第四,調(diào)用WSCInstallProvider安裝這個(gè)協(xié)議鏈。
第五,分層服務(wù)提供者安裝成功后,Windows會(huì)在服務(wù)提供者目錄的最后加入這個(gè)新的條目。但是我們?yōu)榱俗屛覀兊姆謱臃?wù)提供者成為系統(tǒng)默認(rèn)的TCP服務(wù)提供者的話,就必須對(duì)目錄進(jìn)行重新排列,讓新安裝的協(xié)議鏈目錄條目放在最上面。這些排序過(guò)程通過(guò)調(diào)用WSCWriteProviderOrder完成,該函數(shù)將所有的服務(wù)提供者重新排序。需要包含 <sporder.h>頭文件和sporder.lib庫(kù)。

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

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
{
// 第一步,初始化兩個(gè)協(xié)議信息結(jié)構(gòu),初始化可以借助于WSCEnumProtocols函數(shù)取回的信息來(lái)完成;
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調(diào)用來(lái)安裝你的分層服務(wù)提供者;
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;
}

// 第三步,在第二步成功的基礎(chǔ)上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號(hào)。
// 并利用這個(gè)目錄信息來(lái)設(shè)置一個(gè)協(xié)議鏈的目錄信息,通過(guò)這個(gè)協(xié)議鏈,將分層提供者和基礎(chǔ)服務(wù)提供者
// (或者是它下一層的分層協(xié)議提供者)連接起來(lái)。
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;

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

// 第五步,安裝成功后將所有的服務(wù)提供者重新排序。
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;
}
}

好了,到目前,我們安裝了基礎(chǔ)服務(wù)提供者和分層服務(wù)提供者,到了該卸載他們的時(shí)候了,對(duì)于基礎(chǔ)服務(wù)提供者,簡(jiǎn)單地恢復(fù)原來(lái)的目錄信息就可以了,對(duì)于分層服務(wù)提供者,調(diào)用WSCDeinstallProvider卸載函數(shù)可以簡(jiǎn)單的卸載,但是對(duì)于分層服務(wù)提供者,一定要維護(hù)好協(xié)議鏈,把所有和你的分層服務(wù)提供者的協(xié)議鏈相關(guān)的信息都清除掉,恢復(fù)安裝之前的協(xié)議鏈狀態(tài),免得發(fā)生協(xié)議鏈斷開(kāi)的現(xiàn)象發(fā)生,如果斷開(kāi)了,就會(huì)影響網(wǎng)絡(luò)的功能。Windows SPI在這方面存在缺陷,如果很多人裝了很多個(gè)分層服務(wù)提供者,那怎么辦呢?會(huì)很麻煩的。

接下來(lái),我們最后來(lái)看看怎么樣實(shí)現(xiàn)一個(gè)服務(wù)提供者的最基本的實(shí)例,我們可以試圖在這里對(duì)網(wǎng)絡(luò)封包進(jìn)行截取。

以下是基礎(chǔ)服務(wù)提供者實(shí)例,對(duì)于分層服務(wù)提供者,麻煩!我懶得寫了:

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;
}

對(duì)于不同的I/0操作模式,需要進(jìn)行不同的處理
對(duì)于WSARecv,WSARecvFrom,WSASend,WSASendTo的重疊調(diào)用,保存重疊信息,重點(diǎn)是完成例程
// 客戶程序如果用完成例程,那么將不能直接監(jiān)視到收發(fā)的數(shù)據(jù),為了解決這個(gè)問(wèn)題
// 我們可以在服務(wù)提供者內(nèi)提供我們自己的完成例程截獲數(shù)據(jù),在我們截獲了數(shù)據(jù)之后,
// 再把控制權(quán)交還給客戶機(jī)的完成例程。
//
// 首先,保存重疊操作參數(shù)信息的結(jié)構(gòu)
typedef struct tagOVERLAPPED_RECORDER
{
DWORD                  dwType; // WSASend,WSASendTo,WSARecv,WSARecvFrom
                                   // 用來(lái)標(biāo)志監(jiān)視的數(shù)據(jù)操作方式
    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;

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

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

FeedBack:
# re: SPI 2015-11-25 10:35 李超
最近在弄這方面的東西,大神能否知道一下怎么處理不同的I/O操作?  回復(fù)  更多評(píng)論
  
# re: SPI 2015-11-25 10:36 李超
可能比較久遠(yuǎn)了。。。  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
              久久久女女女女999久久| 欧美日韩亚洲不卡| 欧美日韩一级视频| 免费成人小视频| 理论片一区二区在线| av成人手机在线| 亚洲黄色成人| 先锋影音国产精品| 久久久水蜜桃av免费网站| 最新日韩在线视频| 欧美性生交xxxxx久久久| 性亚洲最疯狂xxxx高清| 亚洲电影自拍| 久久精品夜色噜噜亚洲aⅴ| 亚洲人成网站色ww在线| 日韩一区二区精品在线观看| 国产亚洲成av人片在线观看桃| 麻豆国产精品777777在线| 欧美精品福利在线| 久久综合色天天久久综合图片| 一本色道久久综合亚洲精品高清| 久久久水蜜桃| 亚洲精品国偷自产在线99热| 久热精品视频在线观看一区| 欧美高清在线一区二区| 久久久亚洲成人| 亚洲激情电影在线| 麻豆九一精品爱看视频在线观看免费| 亚洲二区视频| 午夜国产一区| 亚洲欧美日韩直播| 欧美刺激性大交免费视频| 欧美成人综合网站| 国产精品欧美精品| 欧美精品少妇一区二区三区| 国产精品乱码一区二区三区| 在线不卡免费欧美| 久久亚洲私人国产精品va媚药| 亚洲欧洲精品一区二区| 午夜精品久久| 欧美性大战久久久久久久| 在线观看国产成人av片| 久久久久久97三级| 久久视频一区| 国产精品中文字幕欧美| 国产日韩欧美综合在线| 一本色道综合亚洲| 亚洲二区免费| 久久精品欧美日韩| 欧美电影免费| 亚洲一区二区三区777| 欧美一区日韩一区| 欧美自拍偷拍午夜视频| 国产一区二区视频在线观看| 韩国av一区二区三区四区| 影音先锋日韩精品| 久久精品国内一区二区三区| 狠狠色伊人亚洲综合网站色| 国产精品亚洲第一区在线暖暖韩国| 欧美视频在线看| 国产精品色在线| 亚洲综合日韩中文字幕v在线| 亚洲精品在线三区| 久久久欧美精品| 国产性做久久久久久| 性欧美video另类hd性玩具| 亚洲欧美在线aaa| 久久精品免费看| 亚洲欧美日韩国产中文在线| 国产精品亚洲综合天堂夜夜| 欧美一区二区女人| 欧美影院视频| 麻豆国产va免费精品高清在线| 激情六月婷婷久久| 亚洲一区二区视频在线观看| 一区二区三区精品视频在线观看| 久久精品99| 久久在线观看视频| 亚洲国产精品久久精品怡红院| 欧美高清一区| 欧美午夜三级| 久久精品国产99| 久久色中文字幕| 日韩午夜在线| 亚洲免费在线观看| 欧美一区二区国产| 麻豆国产精品va在线观看不卡| 在线日本高清免费不卡| 亚洲欧洲综合| 国产精品久久久久一区二区| 欧美一区二区久久久| 久久九九精品99国产精品| 亚洲在线免费观看| 亚洲成人在线网站| 99精品欧美一区二区三区| 国产日本欧美一区二区| 在线视频精品一区| 性亚洲最疯狂xxxx高清| 亚洲毛片在线看| 性色av一区二区三区红粉影视| 亚洲精品国产日韩| 每日更新成人在线视频| 亚洲免费电影在线| 亚洲一区二区三区中文字幕在线| 国产午夜精品久久| 亚洲伦理中文字幕| 欧美成人一区二区三区在线观看| 亚洲少妇一区| 日韩视频免费在线| 精品1区2区3区4区| 亚洲少妇最新在线视频| 国产精品国产三级国产aⅴ浪潮 | 亚洲精品国产精品久久清纯直播| 久久综合伊人77777| 国产亚洲欧美色| 亚洲毛片网站| 亚洲国产精品嫩草影院| 午夜精品久久久久久久男人的天堂| 亚洲精品影院在线观看| 久久久精品动漫| 久久不射中文字幕| 国产精品毛片在线| 一区二区三区日韩欧美精品| 亚洲日本精品国产第一区| 久久久av毛片精品| 久久久精品999| 国产视频一区在线观看| 亚洲在线中文字幕| 西西裸体人体做爰大胆久久久| 欧美色欧美亚洲另类二区| 欧美88av| 欧美激情女人20p| 快she精品国产999| 国产无一区二区| 亚洲综合欧美| 欧美一区二区三区的| 国产精品丝袜久久久久久app| 在线视频亚洲| 午夜免费日韩视频| 国产精品一区一区三区| 欧美亚洲午夜视频在线观看| 久久久xxx| 亚洲第一天堂无码专区| 久久久久久久一区二区| 久久综合色影院| 亚洲黄色成人| 一区二区欧美在线观看| 在线亚洲精品| 国产精品久久久久久妇女6080| 一本一本久久| 欧美中在线观看| 一区二区视频欧美| 欧美成人三级在线| 一本一本久久| 久久99伊人| 亚洲国产欧美不卡在线观看| 欧美久久影院| 久久综合九色综合久99| 欧美日韩一级大片网址| 亚洲视频精品在线| 久久久天天操| 亚洲精品社区| 国产精品日韩精品欧美精品| 久久国内精品视频| 91久久亚洲| 久久精品噜噜噜成人av农村| 在线观看欧美激情| 欧美日韩三区四区| 欧美专区在线| 日韩视频一区| 裸体一区二区三区| 亚洲视频成人| 在线观看视频亚洲| 欧美日韩一区二| 久久精品最新地址| 亚洲一区不卡| 欧美国产三级| 狠狠爱综合网| 欧美日韩在线一二三| 久久国产精品黑丝| 在线中文字幕一区| 亚洲国产精品成人| 久久精品视频一| 美日韩精品免费| 国产亚洲制服色| 亚洲一区制服诱惑| 欧美国产精品中文字幕| 亚洲午夜91| 亚洲大胆女人| 国产精品欧美日韩久久| 免费影视亚洲| 久久这里只精品最新地址| 亚洲精品在线视频观看| 国产日本欧洲亚洲| 欧美日韩国产成人| 亚洲国产精品ⅴa在线观看 | 麻豆九一精品爱看视频在线观看免费| 夜夜嗨av一区二区三区免费区| 欧美gay视频| 久久精品伊人|