| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- {
- 文件名:RealICQProxy.pas
- 功 能:定义代理类,代理类用于存储代理连接的相关信息。
- 建 立:尹进
- 历 史:
- 2005.12.23:补文件说明信息(尹进)
- }
- unit RealICQProxy;
- interface
- uses
- RealICQSocket, WinSock2, OverbyteIcsNtlmMsgs,
- SysUtils, Classes, Windows, EncdDecd;
- const
- ProxyErrorMsgs: array[0..9] of String = (
- '成功',
- '普通的SOCKS服务器请求失败',
- '现有的规则不允许的连接',
- '网络不可达',
- '主机不可达',
- '连接被拒',
- 'TTL超时',
- '不支持的命令',
- '不支持的地址类型',
- '未知错误');
-
- type
- //代理类型
- TProxyType = (ptNone,ptSocks5,ptHttp);
- //代理相关信息
- TProxy = class(TPersistent)
- private
- FProxyType: TProxyType; //代理类型
- FAddress: string; //代理服务器地址
- FPort: Integer; //代理服务器端口号
- FUsername: string; //代理服务器验证用户名
- FPassword: string; //密码
- FDomain: string; //域
-
- FOnChange: TNotifyEvent;
- procedure SetProxyType(Value: TProxyType);
- procedure SetAddress(Value: string);
- procedure SetPort(Value: Integer);
- procedure SetUsername(Value: string);
- procedure SetPassword(Value: string);
- procedure SetDomain(Value: string);
- protected
- procedure DoChange;
- public
- procedure Assign(Source: TPersistent); override;
- published
- property ProxyType: TProxyType read FProxyType write SetProxyType default ptNone;
- property Address: string read FAddress write SetAddress;
- property Port: Integer read FPort write SetPort;
- property Username: string read FUsername write SetUsername;
- property Password: string read FPassword write SetPassword;
- property Domain: string read FDomain write SetDomain;
- property OnChange: TNotifyEvent read FOnChange write FOnChange;
- end;
- //代理协议类型
- TProxyProtocolType = (ppTCP, ppUDP, ppBind);
-
- function ConnectToSocks5Proxy(ADestOrLocalAddress: String; ADestOrLocalPort: Word;
- AProxyAddress: String; AProxyPort: Word;
- AUserName: String; APassword: String;
- AProxyProtocolType: TProxyProtocolType;
- var BindProxyAddr :TSockAddrIn):TSocket;
-
- function ConnectToHTTPProxy(ADestAddress: String; ADestPort: Word;
- AProxyAddress: String; AProxyPort: Word;
- AUserName: String; APassword: String; ADomain: String):TSocket;
- implementation
- //------------------------------------------------------------------------------
- function ConnectToSocks5Proxy(ADestOrLocalAddress: String; ADestOrLocalPort: Word;
- AProxyAddress: String; AProxyPort: Word;
- AUserName: String; APassword: String;
- AProxyProtocolType: TProxyProtocolType;
- var BindProxyAddr :TSockAddrIn):TSocket;
- var
- ProxySocket: TSocket;
- ProxyAddr: TSockAddrIn;
- ServerAddr: TSockAddrIn;
- LastError: Integer;
- ret,
- nIndex,
- iLoop,
- ReturnValue: Integer;
- Buf: array[0..255] of Byte;
- begin
- ProxySocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if ProxySocket = INVALID_SOCKET then raise TSocketException.CreateFmt('创建套接字失败,错误代码:%d',[WSAGetLastError]);
- ProxyAddr.sin_family:= AF_INET;
- ProxyAddr.sin_port:= htons(AProxyPort);
- ProxyAddr.sin_addr.S_addr:= inet_addr(PChar(AProxyAddress));
-
- ret := connect(ProxySocket,@ProxyAddr,SizeOf(ProxyAddr));
- if ret = SOCKET_ERROR then
begin
LastError := WSAGetLastError();
if LastError <> 0 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.CreateFmt('无法建立与代理服务器的连接,错误代码:%d', [LastError]);
- end;
end;
- nIndex := 0;
- Buf[nIndex]:= $05; // Socks5协议
- Inc(nIndex, 1);
- Buf[nIndex]:= $02; // 包中有两种验证方式
- Inc(nIndex, 1);
- Buf[nIndex]:= $00; // 无需校验
- Inc(nIndex, 1);
- Buf[nIndex]:= $02; // 需用户名密码校验
- //Inc(nIndex, 1);
- Send(ProxySocket, Buf, 4, 0);
- FillChar(Buf, 256, #0);
- ReturnValue := Recv(ProxySocket, Buf, 2, 0);
- if ReturnValue <> 2 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create('代理服务器上返回了错误的数据');
- end;
-
- if Buf[1] = $FF then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create('客户端所列出的方法列表中没有一个方法被代理服务器选中,连接必须被关闭');
- end;
-
- if Buf[1] = $02 then // 需用户名密码校验
- begin
- FillChar(Buf, 256, #0);
- Buf[0]:= $01;
- //填充用户名
- Buf[1]:= Length(AUsername);
- for iLoop := 0 to Buf[1] - 1 do
- Buf[2 + iLoop] := Ord(AUsername[iLoop + 1]);
- //填充密码
- Buf[2 + Length(AUsername)] := Length(APassword);
- for iLoop := 0 to Buf[2 + Length(AUsername)] - 1 do
- Buf[3 + Length(AUsername) + iLoop] := Ord(APassword[iLoop + 1]);
- Send(ProxySocket, Buf, Length(AUsername) + Length(APassword) + 3, 0);
-
- ReturnValue := recv(ProxySocket, Buf, 2, 0);
- if ReturnValue <> 2 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create('代理服务器上返回了错误的数据');
- end;
-
- if Buf[1] <> $00 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create('代理服务器登录名或密码错误');
- end;
- end;
- //发送具体的命令
- nIndex := 0;
- Buf[nIndex]:= $05; //协议版本Socks5
- Inc(nIndex, 1);
- if AProxyProtocolType = ppTCP then
- Buf[nIndex]:= $01 // 命令类型 = 连接(connect)
- else if AProxyProtocolType = ppBind then
- Buf[nIndex]:= $02 // 命令类型 = BIND
- else if AProxyProtocolType = ppUDP then
- Buf[nIndex]:= $03; // 命令类型 = UDP(UDP ASSOCIATE)
- Inc(nIndex, 1);
- Buf[nIndex]:= $00; //保留
- Inc(nIndex, 1);
- //填充地址和端口,如果是TCP代理,则为要连接到的远程服务器的地址和端口
- //如果是UDP代理,则为UDP本地绑定的地址和端口
- ServerAddr.sin_family:= AF_INET;
- ServerAddr.sin_port:= htons(ADestOrLocalPort);
- ServerAddr.sin_addr.S_addr:= inet_addr(PChar(ADestOrLocalAddress));
-
- if ServerAddr.sin_addr.S_addr = INADDR_NONE then
- begin
- Buf[nIndex]:= $03; //地址类型(域名)
- Inc(nIndex, 1);
-
- Buf[nIndex]:= Length(ADestOrLocalAddress); //域名长度
- Inc(nIndex, 1);
- CopyMemory(@Buf[nIndex], PChar(ADestOrLocalAddress), Length(ADestOrLocalAddress));
- Inc(nIndex, Length(ADestOrLocalAddress));
- end
- else
- begin
- Buf[nIndex]:= $01; //地址类型IPv4
- Inc(nIndex, 1);
-
- CopyMemory(@Buf[nIndex], @ServerAddr.sin_addr, 4);
- Inc(nIndex, 4);
- end;
- CopyMemory(@Buf[nIndex], @ServerAddr.sin_port, 2);
- Inc(nIndex, 2);
- Send(ProxySocket, Buf, nIndex, 0);
- FillChar(Buf, 256, #0);
- Recv(ProxySocket, Buf, 256, 0);
- if (Buf[0] <> $05) and (Buf[1] <> $00) then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create(ProxyErrorMsgs[Buf[1]]);
- end;
- BindProxyAddr.sin_family:= AF_INET;
- CopyMemory(@BindProxyAddr.sin_addr, @Buf[4], 4); //获取Proxy的映射地址
- CopyMemory(@BindProxyAddr.sin_port, @Buf[8], 2); //获取Proxy的映射端口号
- Result := ProxySocket;
- end;
- //------------------------------------------------------------------------------
- function ConnectToHTTPProxy(ADestAddress: String; ADestPort: Word;
- AProxyAddress: String; AProxyPort: Word;
- AUserName: String; APassword: String; ADomain: String):TSocket;
- var
- ProxySocket: TSocket;
- ProxyAddr: TSockAddrIn;
- LastError: Integer;
- StrHttpProxySend: String;
- ChallengString: String; //NTLM验证时用到
- ReturnValue: Integer;
- Buf: array[0..8191] of Byte;
- LMMsg2:TNTLM_Msg2_Info;
-
- procedure AddHttpHeaders;
- begin
- StrHttpProxySend := StrHttpProxySend +
- 'Accept: */*' + #$D#$A +
- 'Content-Type: text/html' + #$D#$A +
- 'Connection: Keep-Alive' + #$D#$A +
- 'Proxy-Connection: Keep-Alive' + #$D#$A +
- 'Content-length: 0' + #$D#$A#$D#$A;
- end;
- procedure SendRequest(ANeedResetConnect: Boolean = True);
- begin
- AddHttpHeaders;
- if ANeedResetConnect then
- begin
- if ProxySocket <> INVALID_SOCKET then closesocket(ProxySocket);
- ProxySocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if ProxySocket = INVALID_SOCKET then
- begin
- closesocket(ProxySocket);
- raise TSocketException.CreateFmt('创建套接字失败,错误代码:%d',[WSAGetLastError]);
- end;
- ProxyAddr.sin_family:= AF_INET;
- ProxyAddr.sin_port:= htons(AProxyPort);
- ProxyAddr.sin_addr.S_addr:= inet_addr(PChar(AProxyAddress));
-
- connect(ProxySocket,@ProxyAddr,SizeOf(ProxyAddr));
- LastError := WSAGetLastError();
- if LastError <> 0 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.CreateFmt('无法建立与代理服务器的连接,错误代码:%d', [LastError]);
- end;
- end;
- //MessageBox(0, Pchar(StrHttpProxySend), '发送消息', MB_OK);
- CopyMemory(@Buf[0], PChar(StrHttpProxySend), Length(StrHttpProxySend));
- Send(ProxySocket, Buf, Length(StrHttpProxySend), 0);
- FillChar(Buf, 8192, #0);
- ReturnValue := Recv(ProxySocket, Buf, 8192, 0);
- if ReturnValue <= 0 then
- begin
- closesocket(ProxySocket);
- raise TSocketException.Create('未能通过HTTP代理连接到指定的主机');
- end;
- SetLength(StrHttpProxySend, ReturnValue);
- CopyMemory(PChar(StrHttpProxySend), @Buf[0], ReturnValue);
- end;
- begin
- ProxySocket := INVALID_SOCKET;
-
- if Length(Trim(AUserName)) > 0 then
- StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A + 'Proxy-Authorization:Basic %s' + #$D#$A, [ADestAddress, ADestPort, EncodeString(AUserName + ':' + APassword)])
- else
- StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A, [ADestAddress, ADestPort]);
- SendRequest;
- //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
- if (Copy(StrHttpProxySend, 1, 12) <> 'HTTP/1.0 200') and (Copy(StrHttpProxySend, 1, 12) <> 'HTTP/1.1 200') then
- begin
- if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 407') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 407') then
- begin
- //尝试以 NTLM 方式验证
- if (Length(Trim(ADomain)) > 0) and (Pos('NTLM', StrHttpProxySend) >= 1) then
- begin
- StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A + 'Proxy-Authorization:NTLM %s' + #$D#$A, [ADestAddress, ADestPort, NtlmGetMessage1(ADestAddress, ADomain)]);
- SendRequest;
- //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
- if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 407') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 407') then
- begin
- if Pos('NTLM', StrHttpProxySend) >= 1 then
- begin
- ChallengString := Copy(StrHttpProxySend,
- Pos('NTLM ', StrHttpProxySend) + 5,
- Length(StrHttpProxySend));
- ChallengString := Copy(ChallengString,
- 1,
- Pos(#$D#$A, ChallengString) - 1);
- LMMsg2 := NtlmGetMessage2(ChallengString);
- StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A + 'Proxy-Authorization:NTLM %s' + #$D#$A, [ADestAddress, ADestPort, NtlmGetMessage3(ADomain, ADestAddress, AUserName, APassword, LMMsg2.Challenge)]);
- SendRequest(False);
- //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
- if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 200') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 200') then
- begin
- Result := ProxySocket;
- Exit;
- end;
- end;
- end;
- end;
-
- closesocket(ProxySocket);
- raise TSocketException.Create('未能建立HTTP代理连接,用户名和密码错误')
- end
- else
- begin
- closesocket(ProxySocket);
- raise TSocketException.CreateFmt('未能建立HTTP代理连接,错误信息:%s', [StrHttpProxySend]);
- end;
- end;
- Result := ProxySocket;
- end;
- {TProxy}
- //------------------------------------------------------------------------------
- procedure TProxy.DoChange;
- begin
- if Assigned(FOnChange) then FOnChange(Self);
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.Assign(Source: TPersistent);
- begin
- if Source is TProxy then
- begin
- FProxyType := TProxy(Source).ProxyType;
- FAddress := TProxy(Source).Address;
- FPort := TProxy(Source).Port;
- FUsername := TProxy(Source).Username;
- FPassword := TProxy(Source).Password;
- FDomain := TProxy(Source).Domain;
- //inherited Assign(Source);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetProxyType(Value: TProxyType);
- begin
- if FProxyType = Value then Exit;
-
- FProxyType := Value;
- DoChange;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetAddress(Value: string);
- begin
- if FAddress = Value then Exit;
- FAddress := Value;
- DoChange;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetPort(Value: Integer);
- begin
- if FPort = Value then Exit;
- if (Value<0) or (Value>65535) then raise TSocketException.Create('端口号必须为0-65535之间的数值');
- FPort := Value;
- DoChange;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetUsername(Value: string);
- begin
- if FUsername = Value then Exit;
- FUsername := Value;
- DoChange;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetPassword(Value: string);
- begin
- if FPassword = Value then Exit;
- FPassword := Value;
- DoChange;
- end;
- //------------------------------------------------------------------------------
- procedure TProxy.SetDomain(Value: string);
- begin
- if FDomain = Value then Exit;
- FDomain := Value;
- DoChange;
- end;
- end.
|