RealICQProxy.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. {
  2. 文件名:RealICQProxy.pas
  3. 功 能:定义代理类,代理类用于存储代理连接的相关信息。
  4. 建 立:尹进
  5. 历 史:
  6. 2005.12.23:补文件说明信息(尹进)
  7. }
  8. unit RealICQProxy;
  9. interface
  10. uses
  11. RealICQSocket, WinSock2, OverbyteIcsNtlmMsgs,
  12. SysUtils, Classes, Windows, EncdDecd;
  13. const
  14. ProxyErrorMsgs: array[0..9] of String = (
  15. '成功',
  16. '普通的SOCKS服务器请求失败',
  17. '现有的规则不允许的连接',
  18. '网络不可达',
  19. '主机不可达',
  20. '连接被拒',
  21. 'TTL超时',
  22. '不支持的命令',
  23. '不支持的地址类型',
  24. '未知错误');
  25. type
  26. //代理类型
  27. TProxyType = (ptNone,ptSocks5,ptHttp);
  28. //代理相关信息
  29. TProxy = class(TPersistent)
  30. private
  31. FProxyType: TProxyType; //代理类型
  32. FAddress: string; //代理服务器地址
  33. FPort: Integer; //代理服务器端口号
  34. FUsername: string; //代理服务器验证用户名
  35. FPassword: string; //密码
  36. FDomain: string; //域
  37. FOnChange: TNotifyEvent;
  38. procedure SetProxyType(Value: TProxyType);
  39. procedure SetAddress(Value: string);
  40. procedure SetPort(Value: Integer);
  41. procedure SetUsername(Value: string);
  42. procedure SetPassword(Value: string);
  43. procedure SetDomain(Value: string);
  44. protected
  45. procedure DoChange;
  46. public
  47. procedure Assign(Source: TPersistent); override;
  48. published
  49. property ProxyType: TProxyType read FProxyType write SetProxyType default ptNone;
  50. property Address: string read FAddress write SetAddress;
  51. property Port: Integer read FPort write SetPort;
  52. property Username: string read FUsername write SetUsername;
  53. property Password: string read FPassword write SetPassword;
  54. property Domain: string read FDomain write SetDomain;
  55. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  56. end;
  57. //代理协议类型
  58. TProxyProtocolType = (ppTCP, ppUDP, ppBind);
  59. function ConnectToSocks5Proxy(ADestOrLocalAddress: String; ADestOrLocalPort: Word;
  60. AProxyAddress: String; AProxyPort: Word;
  61. AUserName: String; APassword: String;
  62. AProxyProtocolType: TProxyProtocolType;
  63. var BindProxyAddr :TSockAddrIn):TSocket;
  64. function ConnectToHTTPProxy(ADestAddress: String; ADestPort: Word;
  65. AProxyAddress: String; AProxyPort: Word;
  66. AUserName: String; APassword: String; ADomain: String):TSocket;
  67. implementation
  68. //------------------------------------------------------------------------------
  69. function ConnectToSocks5Proxy(ADestOrLocalAddress: String; ADestOrLocalPort: Word;
  70. AProxyAddress: String; AProxyPort: Word;
  71. AUserName: String; APassword: String;
  72. AProxyProtocolType: TProxyProtocolType;
  73. var BindProxyAddr :TSockAddrIn):TSocket;
  74. var
  75. ProxySocket: TSocket;
  76. ProxyAddr: TSockAddrIn;
  77. ServerAddr: TSockAddrIn;
  78. LastError: Integer;
  79. ret,
  80. nIndex,
  81. iLoop,
  82. ReturnValue: Integer;
  83. Buf: array[0..255] of Byte;
  84. begin
  85. ProxySocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  86. if ProxySocket = INVALID_SOCKET then raise TSocketException.CreateFmt('创建套接字失败,错误代码:%d',[WSAGetLastError]);
  87. ProxyAddr.sin_family:= AF_INET;
  88. ProxyAddr.sin_port:= htons(AProxyPort);
  89. ProxyAddr.sin_addr.S_addr:= inet_addr(PChar(AProxyAddress));
  90. ret := connect(ProxySocket,@ProxyAddr,SizeOf(ProxyAddr));
  91. if ret = SOCKET_ERROR then begin LastError := WSAGetLastError(); if LastError <> 0 then
  92. begin
  93. closesocket(ProxySocket);
  94. raise TSocketException.CreateFmt('无法建立与代理服务器的连接,错误代码:%d', [LastError]);
  95. end; end;
  96. nIndex := 0;
  97. Buf[nIndex]:= $05; // Socks5协议
  98. Inc(nIndex, 1);
  99. Buf[nIndex]:= $02; // 包中有两种验证方式
  100. Inc(nIndex, 1);
  101. Buf[nIndex]:= $00; // 无需校验
  102. Inc(nIndex, 1);
  103. Buf[nIndex]:= $02; // 需用户名密码校验
  104. //Inc(nIndex, 1);
  105. Send(ProxySocket, Buf, 4, 0);
  106. FillChar(Buf, 256, #0);
  107. ReturnValue := Recv(ProxySocket, Buf, 2, 0);
  108. if ReturnValue <> 2 then
  109. begin
  110. closesocket(ProxySocket);
  111. raise TSocketException.Create('代理服务器上返回了错误的数据');
  112. end;
  113. if Buf[1] = $FF then
  114. begin
  115. closesocket(ProxySocket);
  116. raise TSocketException.Create('客户端所列出的方法列表中没有一个方法被代理服务器选中,连接必须被关闭');
  117. end;
  118. if Buf[1] = $02 then // 需用户名密码校验
  119. begin
  120. FillChar(Buf, 256, #0);
  121. Buf[0]:= $01;
  122. //填充用户名
  123. Buf[1]:= Length(AUsername);
  124. for iLoop := 0 to Buf[1] - 1 do
  125. Buf[2 + iLoop] := Ord(AUsername[iLoop + 1]);
  126. //填充密码
  127. Buf[2 + Length(AUsername)] := Length(APassword);
  128. for iLoop := 0 to Buf[2 + Length(AUsername)] - 1 do
  129. Buf[3 + Length(AUsername) + iLoop] := Ord(APassword[iLoop + 1]);
  130. Send(ProxySocket, Buf, Length(AUsername) + Length(APassword) + 3, 0);
  131. ReturnValue := recv(ProxySocket, Buf, 2, 0);
  132. if ReturnValue <> 2 then
  133. begin
  134. closesocket(ProxySocket);
  135. raise TSocketException.Create('代理服务器上返回了错误的数据');
  136. end;
  137. if Buf[1] <> $00 then
  138. begin
  139. closesocket(ProxySocket);
  140. raise TSocketException.Create('代理服务器登录名或密码错误');
  141. end;
  142. end;
  143. //发送具体的命令
  144. nIndex := 0;
  145. Buf[nIndex]:= $05; //协议版本Socks5
  146. Inc(nIndex, 1);
  147. if AProxyProtocolType = ppTCP then
  148. Buf[nIndex]:= $01 // 命令类型 = 连接(connect)
  149. else if AProxyProtocolType = ppBind then
  150. Buf[nIndex]:= $02 // 命令类型 = BIND
  151. else if AProxyProtocolType = ppUDP then
  152. Buf[nIndex]:= $03; // 命令类型 = UDP(UDP ASSOCIATE)
  153. Inc(nIndex, 1);
  154. Buf[nIndex]:= $00; //保留
  155. Inc(nIndex, 1);
  156. //填充地址和端口,如果是TCP代理,则为要连接到的远程服务器的地址和端口
  157. //如果是UDP代理,则为UDP本地绑定的地址和端口
  158. ServerAddr.sin_family:= AF_INET;
  159. ServerAddr.sin_port:= htons(ADestOrLocalPort);
  160. ServerAddr.sin_addr.S_addr:= inet_addr(PChar(ADestOrLocalAddress));
  161. if ServerAddr.sin_addr.S_addr = INADDR_NONE then
  162. begin
  163. Buf[nIndex]:= $03; //地址类型(域名)
  164. Inc(nIndex, 1);
  165. Buf[nIndex]:= Length(ADestOrLocalAddress); //域名长度
  166. Inc(nIndex, 1);
  167. CopyMemory(@Buf[nIndex], PChar(ADestOrLocalAddress), Length(ADestOrLocalAddress));
  168. Inc(nIndex, Length(ADestOrLocalAddress));
  169. end
  170. else
  171. begin
  172. Buf[nIndex]:= $01; //地址类型IPv4
  173. Inc(nIndex, 1);
  174. CopyMemory(@Buf[nIndex], @ServerAddr.sin_addr, 4);
  175. Inc(nIndex, 4);
  176. end;
  177. CopyMemory(@Buf[nIndex], @ServerAddr.sin_port, 2);
  178. Inc(nIndex, 2);
  179. Send(ProxySocket, Buf, nIndex, 0);
  180. FillChar(Buf, 256, #0);
  181. Recv(ProxySocket, Buf, 256, 0);
  182. if (Buf[0] <> $05) and (Buf[1] <> $00) then
  183. begin
  184. closesocket(ProxySocket);
  185. raise TSocketException.Create(ProxyErrorMsgs[Buf[1]]);
  186. end;
  187. BindProxyAddr.sin_family:= AF_INET;
  188. CopyMemory(@BindProxyAddr.sin_addr, @Buf[4], 4); //获取Proxy的映射地址
  189. CopyMemory(@BindProxyAddr.sin_port, @Buf[8], 2); //获取Proxy的映射端口号
  190. Result := ProxySocket;
  191. end;
  192. //------------------------------------------------------------------------------
  193. function ConnectToHTTPProxy(ADestAddress: String; ADestPort: Word;
  194. AProxyAddress: String; AProxyPort: Word;
  195. AUserName: String; APassword: String; ADomain: String):TSocket;
  196. var
  197. ProxySocket: TSocket;
  198. ProxyAddr: TSockAddrIn;
  199. LastError: Integer;
  200. StrHttpProxySend: String;
  201. ChallengString: String; //NTLM验证时用到
  202. ReturnValue: Integer;
  203. Buf: array[0..8191] of Byte;
  204. LMMsg2:TNTLM_Msg2_Info;
  205. procedure AddHttpHeaders;
  206. begin
  207. StrHttpProxySend := StrHttpProxySend +
  208. 'Accept: */*' + #$D#$A +
  209. 'Content-Type: text/html' + #$D#$A +
  210. 'Connection: Keep-Alive' + #$D#$A +
  211. 'Proxy-Connection: Keep-Alive' + #$D#$A +
  212. 'Content-length: 0' + #$D#$A#$D#$A;
  213. end;
  214. procedure SendRequest(ANeedResetConnect: Boolean = True);
  215. begin
  216. AddHttpHeaders;
  217. if ANeedResetConnect then
  218. begin
  219. if ProxySocket <> INVALID_SOCKET then closesocket(ProxySocket);
  220. ProxySocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  221. if ProxySocket = INVALID_SOCKET then
  222. begin
  223. closesocket(ProxySocket);
  224. raise TSocketException.CreateFmt('创建套接字失败,错误代码:%d',[WSAGetLastError]);
  225. end;
  226. ProxyAddr.sin_family:= AF_INET;
  227. ProxyAddr.sin_port:= htons(AProxyPort);
  228. ProxyAddr.sin_addr.S_addr:= inet_addr(PChar(AProxyAddress));
  229. connect(ProxySocket,@ProxyAddr,SizeOf(ProxyAddr));
  230. LastError := WSAGetLastError();
  231. if LastError <> 0 then
  232. begin
  233. closesocket(ProxySocket);
  234. raise TSocketException.CreateFmt('无法建立与代理服务器的连接,错误代码:%d', [LastError]);
  235. end;
  236. end;
  237. //MessageBox(0, Pchar(StrHttpProxySend), '发送消息', MB_OK);
  238. CopyMemory(@Buf[0], PChar(StrHttpProxySend), Length(StrHttpProxySend));
  239. Send(ProxySocket, Buf, Length(StrHttpProxySend), 0);
  240. FillChar(Buf, 8192, #0);
  241. ReturnValue := Recv(ProxySocket, Buf, 8192, 0);
  242. if ReturnValue <= 0 then
  243. begin
  244. closesocket(ProxySocket);
  245. raise TSocketException.Create('未能通过HTTP代理连接到指定的主机');
  246. end;
  247. SetLength(StrHttpProxySend, ReturnValue);
  248. CopyMemory(PChar(StrHttpProxySend), @Buf[0], ReturnValue);
  249. end;
  250. begin
  251. ProxySocket := INVALID_SOCKET;
  252. if Length(Trim(AUserName)) > 0 then
  253. StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A + 'Proxy-Authorization:Basic %s' + #$D#$A, [ADestAddress, ADestPort, EncodeString(AUserName + ':' + APassword)])
  254. else
  255. StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A, [ADestAddress, ADestPort]);
  256. SendRequest;
  257. //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
  258. if (Copy(StrHttpProxySend, 1, 12) <> 'HTTP/1.0 200') and (Copy(StrHttpProxySend, 1, 12) <> 'HTTP/1.1 200') then
  259. begin
  260. if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 407') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 407') then
  261. begin
  262. //尝试以 NTLM 方式验证
  263. if (Length(Trim(ADomain)) > 0) and (Pos('NTLM', StrHttpProxySend) >= 1) then
  264. begin
  265. StrHttpProxySend := Format('CONNECT %s:%d HTTP/1.1' + #$D#$A + 'Proxy-Authorization:NTLM %s' + #$D#$A, [ADestAddress, ADestPort, NtlmGetMessage1(ADestAddress, ADomain)]);
  266. SendRequest;
  267. //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
  268. if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 407') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 407') then
  269. begin
  270. if Pos('NTLM', StrHttpProxySend) >= 1 then
  271. begin
  272. ChallengString := Copy(StrHttpProxySend,
  273. Pos('NTLM ', StrHttpProxySend) + 5,
  274. Length(StrHttpProxySend));
  275. ChallengString := Copy(ChallengString,
  276. 1,
  277. Pos(#$D#$A, ChallengString) - 1);
  278. LMMsg2 := NtlmGetMessage2(ChallengString);
  279. 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)]);
  280. SendRequest(False);
  281. //MessageBox(0, Pchar(StrHttpProxySend), '收到反馈消息', MB_OK);
  282. if (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.0 200') or (Copy(StrHttpProxySend, 1, 12) = 'HTTP/1.1 200') then
  283. begin
  284. Result := ProxySocket;
  285. Exit;
  286. end;
  287. end;
  288. end;
  289. end;
  290. closesocket(ProxySocket);
  291. raise TSocketException.Create('未能建立HTTP代理连接,用户名和密码错误')
  292. end
  293. else
  294. begin
  295. closesocket(ProxySocket);
  296. raise TSocketException.CreateFmt('未能建立HTTP代理连接,错误信息:%s', [StrHttpProxySend]);
  297. end;
  298. end;
  299. Result := ProxySocket;
  300. end;
  301. {TProxy}
  302. //------------------------------------------------------------------------------
  303. procedure TProxy.DoChange;
  304. begin
  305. if Assigned(FOnChange) then FOnChange(Self);
  306. end;
  307. //------------------------------------------------------------------------------
  308. procedure TProxy.Assign(Source: TPersistent);
  309. begin
  310. if Source is TProxy then
  311. begin
  312. FProxyType := TProxy(Source).ProxyType;
  313. FAddress := TProxy(Source).Address;
  314. FPort := TProxy(Source).Port;
  315. FUsername := TProxy(Source).Username;
  316. FPassword := TProxy(Source).Password;
  317. FDomain := TProxy(Source).Domain;
  318. //inherited Assign(Source);
  319. end;
  320. end;
  321. //------------------------------------------------------------------------------
  322. procedure TProxy.SetProxyType(Value: TProxyType);
  323. begin
  324. if FProxyType = Value then Exit;
  325. FProxyType := Value;
  326. DoChange;
  327. end;
  328. //------------------------------------------------------------------------------
  329. procedure TProxy.SetAddress(Value: string);
  330. begin
  331. if FAddress = Value then Exit;
  332. FAddress := Value;
  333. DoChange;
  334. end;
  335. //------------------------------------------------------------------------------
  336. procedure TProxy.SetPort(Value: Integer);
  337. begin
  338. if FPort = Value then Exit;
  339. if (Value<0) or (Value>65535) then raise TSocketException.Create('端口号必须为0-65535之间的数值');
  340. FPort := Value;
  341. DoChange;
  342. end;
  343. //------------------------------------------------------------------------------
  344. procedure TProxy.SetUsername(Value: string);
  345. begin
  346. if FUsername = Value then Exit;
  347. FUsername := Value;
  348. DoChange;
  349. end;
  350. //------------------------------------------------------------------------------
  351. procedure TProxy.SetPassword(Value: string);
  352. begin
  353. if FPassword = Value then Exit;
  354. FPassword := Value;
  355. DoChange;
  356. end;
  357. //------------------------------------------------------------------------------
  358. procedure TProxy.SetDomain(Value: string);
  359. begin
  360. if FDomain = Value then Exit;
  361. FDomain := Value;
  362. DoChange;
  363. end;
  364. end.