Posted on 2009-01-04 22:47
S.l.e!ep.¢% 閱讀(3983)
評論(0) 編輯 收藏 引用
有很多公司不能直接和Internet相連,必須通過代理和www連接,瀏覽、下載資料。
代理服務器支持的協議也有所不同,有支持Sock、HTTP代理的這樣我們做的客戶端軟件就需要支持這些代理,使用戶能夠通過這些代理透過防火墻和外網相連,一般Sock分為Sock4和Sock5,這里我們只實現Sock5協議。
下面說明一下實現的方法。
代理的實現方法實際就是Socket編程,只要通訊時遵循它們的協議就可以。首先理解它們的協議是關鍵。
示意圖:
1.1??? Sock5協議實現
1.1.1??????? Sock5協議內容
1.? 客戶端首先要與代理服務器連接,連接后要向代理發送版本號、認證方法、方法選擇格式如下:
版本號(1字節) |? 供選擇的認證方法(1字節) ?|? 方法序列(1-255個字節長度)
如果你支持的版本為SOCK5那么版本號就為0x05;可供選擇的方法是指你的協議支持幾種認證方式,因為我們實現只支持一種所以就填0x01,如果你是兩種就寫0x02;認證方法序列包括(0x00為不需認證、0x02為需要用戶名和密碼認證,通常是這兩種,如果想了解更多請參看Rfc1928)。
這樣組合你的報文應該為:0x05 0x01 0x00
如果需要認證那么為:0x05 0x01 0x02
2.? 代理接收到客戶端的請求,會向客戶端返回信息,格式為:
版本號 | 服務器選定的方法
如果服務器支持驗證方式,返回的報文為:
0x05 0x02
3.? 接下來根據服務器的驗證方式,發送驗證信息了,報文格式為:
0x01 | 用戶名長度(1字節)| 用戶名(長度根據用戶名長度域指定) | 口令長度(1字節) | 口令(長度由口令長度域指定)
4.? 服務器接收信息后進行驗證,返回如下格式:
0x01 | 驗證結果標志
驗證結果標志:0x00表示驗證成功,其他值均為錯誤碼
1.1.2??????? Sock5協議實現
根據上面所述的步驟和協議內容,就可以實現SOCK5代理了。下面我給出我寫的實現SOCKT5代理的函數代碼和注釋,供參考(delphi6+win2K+sp4調試通過,代理服務器用的是CCProxy6.0)
function?TBZSock5ProxyApi.SetProxy(ASocket:?Pointer):?Integer;
var
??proxyUser,?proxyPwd:?String;
??bIsValid:?Boolean;
??sock:?
^
TSocket;
??sockServer:?TSockAddrIn;
??command:?array?[
0
..
9
]?of?Byte;
??re,?len,?ulen,?plen:?Integer;
//
??buffer:?PByte;
??buffer:?array?[
0
..
1023
]?of?Byte;
begin
??sock?:
=
?ASocket;
??
if
?FProxyParam.GetServer?
=
?
''
?then
??begin
????Result?:
=
?
0
;
????Exit;
??end
??
else
??begin
????Result?:
=
?
0
;
????sock
^
?:
=
?socket(AF_INET,?SOCK_STREAM,?
0
);
????
if
?sock
^
?
=
?INVALID_SOCKET?then
????begin
??????Result?:
=
?
1
;
??????Exit;
????end;
?
????sockServer.sin_family?:
=
?AF_INET;
????sockServer.sin_port?:
=
?htons(FProxyParam.GetPort);????
//
將整形數變為網絡字節流
????sockServer.sin_addr.S_addr?:
=
?inet_addr(PChar(FProxyParam.GetServer));
?
????
//
連接遠程主機
????
if
?WinSock.connect(sock
^
,?sockServer,?SizeOf(sockServer))?
<>
?
0
?then
????begin
??????Result?:
=
?
1
;
??????Exit;
????end;
?
????bIsValid?:
=
?FProxyParam.GetProxyValid;
?
????
//
發送SOCK5協議指令
????FillChar(command,?
10
,?
0
);
????command[
0
]?:
=
?
5
;
????
if
?bIsValid?then
??????command[
1
]?:
=
?
2
????
else
??????command[
1
]?:
=
?
1
;
????
if
?bIsValid?then
??????command[
2
]?:
=
?
2
????
else
??????command[
2
]?:
=
?
0
;
?
????
//
發送登陸指令
????
if
?bIsValid?then
??????re?:
=
?WinSock.send(sock
^
,?command,?
4
,?
0
)
????
else
??????re?:
=
?WinSock.send(sock
^
,?command,?
3
,?
0
);
????
if
?re?
=
?SOCKET_ERROR?then
????begin
??????Result?:
=
?
1
;
??????Exit;
????end;
?
????
//
接收返回的消息
????fillchar(command,?
10
,?
0
);?????????????????????????
//
接收前用0再次填充
????re?:
=
?WinSock.recv(sock
^
,?command,?
2
,?
0
);
????
if
?re?
<>
?
2
?then
????begin
??????Result?:
=
?
1
;
??????Exit;
????end;
????
if
?command[
1
]
=
$FF?then
????begin
??????Result?:
=
?
1
;
??????Exit;
????end;
?
????
if
?(not?bIsValid)?and?(command[
1
]
=
0
)?then
????begin
??????Exit;
????end;
?
????proxyUser?:
=
?FProxyParam.GetUsername;
????proxyPwd?:
=
?FProxyParam.GetPassword;
?
????
if
?command[
1
]?
<>
?
0
?then
????begin
??????
if
?command[
1
]?
<>
?
2
?then
??????begin
????????Result?:
=
?
1
;
????????Exit;
??????end;
??????
if
?bIsValid?then
??????begin
????????ulen?:
=
?Length(proxyUser);
????????plen?:
=
?Length(proxyPwd);
????????len?:
=
?
3
?
+
?ulen?
+
?plen;
????????fillchar(buffer,?
1024
,?
0
);
?
????????buffer[
0
]?:
=
?
5
;
????????buffer[
1
]?:
=
?ulen;
????????StrPCopy(@buffer[
2
],?proxyuser);
????????buffer[
2
?
+
?ulen]?:
=
?plen;
????????StrPCopy(@buffer[
2
?
+
?ulen?
+
?
1
],?proxyPwd);
?
????????
//
發送驗證信息
????????re?:
=
?send(sock
^
,?buffer,?len,?
0
);
????????
if
?re?
=
?SOCKET_ERROR?then
????????begin
??????????Result?:
=
?
1
;
??????????Exit;
????????end;
?
????????
//
接收驗證返回信息
????????fillchar(command,?
10
,?
0
);
????????re?:
=
?recv(sock
^
,?command,?
2
,?
0
);
????????
if
?((re
<>
2
)?or?((command[
0
]
<>
1
)?and?(command[
1
]
<>
0
?)))?then
????????begin
??????????Result?:
=
?
1
;
??????????Exit;
????????end;
??????end???
//
if?bisValid
??????
else
??????begin
????????Result?:
=
?
1
;
????????Exit;
??????end;?????????????????????
//
????end;?
//
?if?command[1]<>0
??end;??
//
end?first?if
?
end;
上面的函數中有一個FproxyParam變量,它是代理參數的值對象,聲明如下:
??TProxyParam?
=
?
class
??
private
????FUsername,????????
//
代理驗證用戶名
????FPassword,????????
//
代理驗證密碼
????FServer:?String;????
//
代理服務器地址
????FPort:?Integer;?????
//
代理服務器端口號
????FIsValid:?Boolean;??
//
是否驗證?????如果代理服務器是驗證的,那么此值應該為true
?
????procedure?Clear;
??
public
????constructor?Create;
????procedure?SetUsername(AUsername:?String);
????procedure?SetPassword(APassword:?String);
????procedure?SetServer(AServer:?String);
????procedure?SetPort(APort:?Integer);
????procedure?SetProxyValid(AValid:?Boolean);
?
????function?GetUsername:?String;
????function?GetPassword:?String;
????function?GetServer:?String;
????function?GetPort:?Integer;
????function?GetProxyValid:?Boolean;
??end;
使用此函數:
假設已經創建了一個ClientSocket1(TclientSocket對象),它的IP和Port設置為代理服務器的IP和Port,那么就有下面的代碼:
......
Var
Re:Integer;
Begin
Re:
=
SetProxy(@(ClientSocket.Socket.SocketHandle))?;??
//
如果驗證成功,那么就可以用返回的//socket進行相應的通訊
If?Re
=
0
?then?
Begn
??ShowMessage(‘Sock5?is?ok’);?????????
//
代理服務器就會有連接的數據流顯示
End
Else?begin
??ShowMessage(‘Sock5?is?error’);
End;
End;
……
上面的例子如果設置代理成功,那么代理服務器就會有連接的數據流顯示。表示與代理服務器有數據交換,如果沒有成功則務數據流顯示。
?
?
?????????????????????????????????????????????????????????????? 李孟巖
????????????????????????????????????????????????????????????????????? mengyan_yl@sohu.com
??????????????????????????????????????????????????????????????????????????????????? 2005-07-05實現Sock5代理