HTTP代理實現請求報文的攔截與篡改8--自動設置及取消代理


返回目錄 

  接上篇,這次繼續將程序完善,為其添加自動設置和取消代理的功能 ,主要用到一個API:InternetSetOption。從名字就知道他是干什么的了:設置互聯網選項用的,HTTP代理屬於互聯網的范圍,自然設置代理選項就要用到他了。

我們來看一下他的定義

BOOL InternetSetOption(
  _In_  HINTERNET hInternet,
  _In_  DWORD dwOption,
  _In_  LPVOID lpBuffer,
  _In_  DWORD dwBufferLength
);

Parameters 參數
hInternet [in]  
Handle on which to set information.   
想設置信息的那個句柄
dwOption [in]   
Internet option to be set. This can be one of the Option Flags values. 
要設置的網絡選項,可以是一個或者多個Option Flags 的值 
lpBuffer [in]
Pointer to a buffer that contains the option setting.
包含選項設置的緩存的指針。 
dwBufferLength [in]
Size of the lpBuffer buffer. If lpBuffer contains a string, the size is in TCHARs. If lpBuffer contains anything other than a string, the size is in bytes. lpBuffer緩存的大小, 如果lpBuffer包含一個字符串,這個大小就是字符串的大小,假如lpBuffer除了字符串還包括一些其它東西,這個大小就是byte 的大小 
Return value 返回值 
Returns TRUE if successful, or FALSE otherwise. To get a specific error message, call GetLastError. 
如果成功了返回TRUE,否則FALSE。如果想得到是什么錯誤,調用 GetLastError 。

  官方套話:本人翻譯水平有限,其中錯誤在所難免,敬請原諒,此話后面照樣適用,不再重復說明, OVER 

  從這些參數的定義可知,第一個參數是要設置信息的程序句柄,第二個是要設置的網絡選項的類型(Option flags),可以是一個也可以多個。

  下面我們再來看看Option Flags有哪些:   

http://msdn.microsoft.com/zh-cn/library/aa385328(v=vs.85).aspx  

  這個網址列出了所有的Option flags的網址,N多,對我們有用的,就兩個。其它的自己去看  

 

  一.INTERNET_OPTION_PER_CONNECTION_OPTION    75  

Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies a list of options for a particular connection. This is used by InternetQueryOption and InternetSetOption. This option is only valid in Internet Explorer 5 and later.
為一個特定的連接設置或者恢復一個INTERNET_PER_CONN_OPTION_LIST 的列表結構。一般被使用於InternetQueryOption 和 InternetSetOption 兩個API。IE5.0及以上才支持這個選項。   
Note  INTERNET_OPTION_PER_CONNECTION_OPTION causes the settings to be changed on a system-wide basis when a NULLhandle is used in the call to InternetSetOption. To refresh the global proxy settings, you must call InternetSetOption with theINTERNET_OPTION_REFRESH option flag.
注 : 當 調用InternetSetOption.時,如果第二個參數設置成了此值,而第一個參數為NULL時,所引起的變更將是全局性的也就是系統級別的。刷新全局代理設置,你必須調用InternetSetOption,並使用INTERNET_OPTION_REFRESH
Note  To change proxy information for the entire process without affecting the global settings in Internet Explorer 5 and later, use this option on the handle that is returned from InternetOpen. The following code example changes the proxy for the whole process even though the HINTERNET handle is closed and is not used by any requests.
注 : 如果想在IE5.0及以上瀏覽器上改變代理設置但又不想影響全局,那么設置句柄為InternetOpen返回的值......  后面的不翻了.......    
For more information and code examples, see KB article 226473

  從上面我們知道了。

InternetSetOption(
  NULL,
  INTERNET_OPTION_PER_CONNECTION_OPTION,
  連接選項(一個INTERNET_PER_CONN_OPTION_LIST類型的列表結構),
  第三個參數的大小
);

  執行上面的API后,我們就可以根據第三個參數里的選項(一個或者多個)來變更互聯網的設置了,而且這些變更是全局性的, 因為第一個參數是NULL,如果第一個參數換成 InternetOpen API返回的句柄的話, 那么第三個參數里的選項, 就是僅針對IE5.0及以上的瀏覽器了 。。 

  看到這里,其實大家應該已經很清楚了,  INTERNET_PER_CONN_OPTION_LIST結構 是一個關鍵,因為這個結構就是用來存放要變更的選項的內容的。下面我們再來看看這個結構的定義     。 

typedef struct {
  DWORD                      dwSize;
  LPTSTR                     pszConnection;
  DWORD                      dwOptionCount;
  DWORD                      dwOptionError;
  LPINTERNET_PER_CONN_OPTION pOptions;
} INTERNET_PER_CONN_OPTION_LIST, *LPINTERNET_PER_CONN_OPTION_LIST;

Members 成員 
dwSize
Size of the structure, in bytes. 結構的大小  
pszConnection
Pointer to a string that contains the name of the RAS connection or NULL, which indicates the default or LAN connection, to set or query options on. 一個NULL值或者一個字符串指針,這個字符串就是你要設置或者查詢選項的那個默認的或者局域網的RAS連接的名字。(我日,這翻譯的還真拗口和難懂,看來漢語中也要引入從句這種語法規則 ) 算了,還是直譯吧,指向一個NULL或者字符串指針 , 哪個字符串呢?就是RAS連接的名字,那又是個哪個RAS連接呢?默認的或者局域網的連接,再補充一下,就是你要設置或者查詢選項的那個 。       
dwOptionCount
Number of options to query or set. 要查詢或者設置的選項的個數  
dwOptionError
Options that failed, if an error occurs. 選項那個失敗的,假如一個錯誤發生(逐詞直譯)
pOptions
Pointer to an array of INTERNET_PER_CONN_OPTION structures containing the options to query or set. 一個包含查詢或設置選項的INTERNET_PER_CONN_OPTION結構的數組的指針     

  還是來看一個例子吧 。例子的力量是偉大的。

INTERNET_PER_CONN_OPTION_LIST list ;
INTERNET_PER_CONN_OPTION options[5];  // 有幾個選項需要設置這里就分配幾個     
list.pszConnection = NULL;   // 為NULL,表示默認的連接
list.dwOptionCount = 5 ;  //  options的個數
list.dwOptionError = 0 ; 
list.pOptions = options;  // 指向options,就是選項的數組   

  這里又出來一個新結構INTERNET_PER_CONN_OPTION , 再來看一看其定義 

typedef struct {
  DWORD dwOption;
  union {
    DWORD    dwValue;
    LPTSTR   pszValue;
    FILETIME ftValue;
  } Value;
} INTERNET_PER_CONN_OPTION, *LPINTERNET_PER_CONN_OPTION;

只有2個成員,一個DWORD類型的dwOption,另外一個union類型的value ; 看看其說明

Members 成員 
dwOption
Option to be queried or set. This member can be one of the following values. 被查詢或者設置的選項,這個成員可以下面的值當中的一個。 下面很多值,這里就不列出來了。后面說明的時候會對用到的進行說明    
Value
Union that contains the value for the option. It can be any one of the following types depending on the value of dwOption: 選項的值,可以下面所列類型中的一個,至於是那個,這個要依賴你的dwOption的值
dwValue
Unsigned long integer value. 無符號長整型
pszValue
Pointer to a string value. 指向一個字符串的指針 
ftValue
A FILETIME structure. 一個FILETIME結構 

  根據上面的描述,第一個成員是要設置的選項的類型,第二個是要設置的選項的值,至於這個值是什么類型,要依據第一個選項的類型的來決定。繼續例子 

INTERNET_PER_CONN_OPTION options[5] ; 
options[0].dwOption = INTERNET_PER_CONN_FLAGS ;
options[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER ;
options[1].Value.pszValue = _T("http=127.0.0.1:8888;");   
options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS ;
options[2].Value.pszValue = _T("<-loopback>;");  
options[3].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL ;
options[3].Value.pszValue = NULL;  
options[4].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;          
options[4].Value.dwValue = 0  ; 

  舉這個例子的原因是因為這五個選項正好是我們設置代理服務器要用到的選項,現在一個一個的來進行說明。

  1. INTERNET_PER_CONN_FLAGS

Sets or retrieves the connection type. The Valuemember will contain one or more of the following values: 設置或者恢復連接類型,值成員將包含一個或者多個如下的值: 
PROXY_TYPE_DIRECT
The connection does not use a proxy server. 連接不使用代理服務器。
PROXY_TYPE_PROXY
The connection uses an explicitly set proxy server. 連接使用一個顯式設置的代理服務器
PROXY_TYPE_AUTO_PROXY_URL
The connection downloads and processes an automatic configuration script at a specified URL. 在某個指定的URL上此連接下載和處理一個自動配置腳本,沒明白啥意思,不過也用不到 
PROXY_TYPE_AUTO_DETECT
The connection automatically detects settings. 連接自動檢查設置,這個也用不到 

  再看我們例子里的設置

options[0].dwOption = INTERNET_PER_CONN_FLAGS ;
options[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;

設置成 PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY 這個代表啟用代理.

也就是下圖的那個勾就勾上了 

如果只設置成PROXY_TYPE_DIRECT

options[0].Value.dwValue = PROXY_TYPE_DIRECT ;

則表示取消代理。

也就是上圖的勾沒了 。  

 

  2. INTERNET_PER_CONN_PROXY_SERVER

Sets or retrieves a string containing the proxy servers.
設置或恢復包含代理服務器信息的字符串

  也看例子程序里的設置 

options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER ;
options[1].Value.pszValue = _T("http=127.0.0.1:8888;");  

  因為是個字符串,所以是pszValue ,內容的格式如下:

  “http=127.0.0.1:8888 ; “ 也可以是 “http=127.0.0.1:8888 ;https=192.168.1.88:8080 ;” 

  能看出來規律嗎。就是用分號分開不同協議的代理設置,用等號分開協議名和代理服務器的地址和端口。

  那么類推一下,如果還需要再設置個FTP協議的代理服務器,代理服務器的地址是本機,端口是8888,那么應該怎么設置呢.

  “http=127.0.0.1:8888 ;https=192.168.1.88:8080 ;ftp=127.0.0.1:8888;”  

  http=127.0.0.1:8888 ;

 

  http=127.0.0.1:8888 ;https=192.168.1.88:8080 ;

 

  http=127.0.0.1:8888 ;https=192.168.1.88:8080 ;ftp=127.0.0.1:8888;    

 

  看明白了吧,如果U自認還是正常人類的話。那么我們繼續往下了。   

 

  3. INTERNET_PER_CONN_PROXY_BYPASS 

  一樣的先看定義

Sets or retrieves a string containing the URLs that do not use the proxy server.
設置或者恢復不使用代理的網址。

  看他的命名就基本能猜出來他是干什么的了,就是設置一些網址直接訪問網絡,而不使用代理。這個選項是很有用處的,例如,你想使用代理訪問國外網站,但是使用了代理后,很多的國內網站又訪問不了,怎么辦呢,很簡單,把網內的網站加到這個例外情況(忽略列表)里就可以了。     

  同樣的,再看看我們例子里的設置 

options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS ;
options[2].Value.pszValue = _T("<-loopback>;");  

  至於loopback是什么,可自行GOOGLE或BAIDU。      

   

  后面還有兩個選項就不詳細講了,一個設NULL,一個設0就可以了。

 

  前面的代碼綜合起來就是

INTERNET_PER_CONN_OPTION_LIST list ;
INTERNET_PER_CONN_OPTION options[5];  // 有幾個選項需要設置這里就分配幾個     
list.pszConnection = NULL;   // 為NULL,表示默認的連接
list.dwOptionCount = 5 ;  //  options的個數
list.dwOptionError = 0 ; 
list.pOptions = options;  // 指向options,就是選項的數組   
options[0].dwOption = INTERNET_PER_CONN_FLAGS ;
options[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER ;
options[1].Value.pszValue = _T("http=127.0.0.1:8888;");   
options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS ;
options[2].Value.pszValue = _T("<-loopback>;");  
options[3].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL ;
options[3].Value.pszValue = NULL;  
options[4].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;          
options[4].Value.dwValue = 0  ; 

InternetSetOption(
  NULL,
  INTERNET_OPTION_PER_CONNECTION_OPTION,
  &options,
  sizeof(options)
);      

  二. INTERNET_OPTION_PROXY_SETTINGS_CHANGED   95

Alerts the current WinInet instance that proxy settings have changed and that they must update with the new settings. To alert all available WinInet instances, set the Buffer parameter of InternetSetOption to NULL and BufferLength to 0 when passing this option. This option can be set on the handle returned by InternetConnect or HttpOpenRequest.
通知當前的WinInet實例代理設置已經被改變了,必須去更新他的代理設置,如果要通知所有有效的WinInet實例的話,第三個參數設成NULL,最后一個參數設成0 。

  我們當然是想通知所有的WinInet實例了,所以這里后面兩個參數設成NULL和0就行了。

InternetSetOption(
  NULL,
  INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
  NULL,
  0 
);

  這一步重要,使用INTERNET_OPTION_PER_CONNECTION_OPTION后,設置其實已經改變了,但是還不能立即生效,需要重啟后才能生效,但如果想立即生效,就要用到INTERNET_OPTION_PROXY_SETTINGS_CHANGED了。

  好了,再和前面的代碼綜合一下,另外了加了點注釋 。  

INTERNET_PER_CONN_OPTION options[5] ; 
options[0].dwOption = INTERNET_PER_CONN_FLAGS ;
options[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER ;
options[1].Value.pszValue = _T("http=127.0.0.1:8888;");   
options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS ;
options[2].Value.pszValue = _T("<-loopback>;");  
options[3].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL ;
options[3].Value.pszValue = NULL;  
options[4].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;          
options[4].Value.dwValue = 0  ; 

// 改變設置 
InternetSetOption(
  NULL,
  INTERNET_OPTION_PER_CONNECTION_OPTION,
  &options,
  sizeof(options)
);

// 通知所有的WINET實例,設置已經改變了,請立即更新代理設置     
InternetSetOption(
  NULL,
  INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
  NULL,
  0 
);  

  在C#中調用API,尤其涉及到過多結構體的時候,非常的麻煩,所以為了方便,前面的例子我使用了C++來寫。至於C#中如何實現這些功能,各位可自行看源代碼,原理都是一樣的,代碼見 附錄 -WinINetProxy.cs  文件 - SetToWinINET 方法. 

 

  為了實現啟動的時候自動設置代理,退出的時候自動退出代理的功能,我們需要在FrmMain.cs 做兩處變動  。

  第一處FrmMain構造函數的最后。

  原來是

proxy.Start(Config.ListenPort);

  現在變更為

if (proxy.Attach())    // 自動設置代理成功后
{
    proxy.Start(Config.ListenPort);    // 啟動代理服務器
}

  第二處FrmMain_FormClosed事件的代碼里

  原來是

if (proxy != null)
{
  proxy.Stop();
}

  變更為

if (proxy != null)
{
  proxy.Stop();
  proxy.Detach();
}

  從上面可以看到在程序啟動的時候,我們增加了一個proxy.Attach(),而在程序退出時又增加了一個proxy.Detach()。Attach是自動設置代理,Detach是取消代理。這兩個方法全部是增加在Proxy類里的 

 1 internal bool Attach()
 2 {
 3   if (!this.IsAttached)
 4   {
 5     return this.winINetProxy.SetToWinINET("DefaultLAN");
 6   }
 7   return true; 
 8 }
 9 
10 internal bool Detach()
11 {
12   if (this.IsAttached)
13   {
14     return this.winINetProxy.SetToWinINET("DefaultLAN");
15   }
16   return true; 
17 }  

  這兩個方法都是調用了winINetProxy.SetToWinNet方法。winINetProxy 是WinInetPorxy類的一個實例,WinInetProxy類就是前面提到的設置代理和取消代理的實現類 。  

附錄(源代碼+程序)                                                     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM