unit Socks5Proxy;{Write By Wenjinshan.}interfaceuses Windows, SysUtils, Classes, ExtCtrls, ScktComp, Forms, StdCtrls,winsock ,MYNMUDP,math;const //MAXurl=255; VER=#5; IsServer=$40000000; DefaultPort=1080; StartPort=4000; type PCharArray = ^TCharArray; TCharArray = array[0..32767] of Char; session_record=record Valid:boolean; //是否有效 Close:boolean; step:integer;//连接步骤 UdpClient:TMYNMUDP; //客户的udp UdpSite:TMYNMUDP; //网站的udp TcpSite:TClientSocket; //网站的tcp TcpClient:TCustomWinSocket; //客户的tcp ListenServer:TServerSocket; //Listen的服务器 ListenOneThread:TCustomWinSocket; LastError:integer; end; TSocks5Proxy = class private ServerSocket1: TServerSocket; TimerRefresh: TTimer; NMUDP1: TMYNMUDP; FPort: Integer; Fuser: String; Fpass: String; function GetSock5Host(buf:pchar;var p:integer):string; procedure DataReceived(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer); procedure FindMyPort(udp:TMYNMUDP;MyIP:string;var Start_Port:integer); procedure ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); procedure TimerRefreshTimer(Sender: TObject); procedure NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer); procedure ListenServerClientConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ListenServerClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ListenServerClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ListenServerClientRead(Sender: TObject; Socket: TCustomWinSocket); procedure SetPort(const Value: Integer); procedure SetUser(const Value: String); procedure SetPass(const Value: String); { Private declarations } public constructor Create; destructor Destroy; override; procedure StopServer; procedure StartServer; property User: String read FUser write SetUser; property Pass: String read FPass write SetPass; property Port: Integer read FPort write SetPort; end;var session:array of session_record; LastPort:integer; //最后一个使用的端口implementationfunction WordToS(w:word):string;begin setlength(result,sizeof(word)); pword(@result[1])^:=w;end;function LongWordToS(w:Longword):string;begin setlength(result,sizeof(Longword)); pLongword(@result[1])^:=w;end;function CharToAIISC(buf:pchar;len:integer):string;//把字符全用十六进制的"xx "的形式表示出来var i:integer;begin result:=''; for i:=0 to len-1 do begin result:=result+format('%2.2X ',[ord(Buf[i])]); end;end;function CharToString(Buf:pchar;length:integer):string;//char转变成stringvar s:string;begin setlength(s,length); move(Buf[0],s[1],length); //不能用strcopy result:=s;end;procedure TSocks5Proxy.DataReceived(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer);var buf:array[0..3]of char;begin if NumberBytes<>sizeof(buf) then exit; (Sender as TMYNMUDP).ReadBuffer(buf, NumberBytes); if pinteger(@buf)^=Port then (Sender as TMYNMUDP).tag:=1; //有效的end;procedure TSocks5Proxy.FindMyPort(udp:TMYNMUDP;MyIP:string;var Start_Port:integer);var buf:array[0..3]of char; t:dword;begin {下面搜索本机有效的udp端口} udp.Tag:=0; //无效的 udp.RemoteHost:= MyIP; //自己发给自己 udp.OnDataReceived:=DataReceived; udp.LocalPort:=4000; udp.RemotePort:=udp.LocalPort; pinteger(@buf)^:=udp.LocalPort; udp.SendBuffer(buf,sizeof(buf)); t:=gettickcount; while (udp.tag=0)and(gettickcount-t<50)do application.ProcessMessages; while udp.tag=0 do //端口不合法,改变端口再发 begin udp.LocalPort:=Start_Port; inc(Start_Port); if Start_Port=MAXWORD then Start_Port:=StartPort; udp.RemotePort:=udp.LocalPort; pinteger(@buf)^:=udp.LocalPort; udp.SendBuffer(buf,sizeof(buf)); t:=gettickcount; while (udp.tag=0)and(gettickcount-t<50)do application.ProcessMessages; end;end;function TSocks5Proxy.GetSock5Host(buf:pchar;var p:integer):string;var s:string; ip:longword;begin result:=''; case buf[p] of #1:begin ip:=Plongword(@buf[p+1])^; if ip<>0 then result:=string(inet_ntoa(Tinaddr(ip))); inc(p,5); end; #3:begin setlength(s,ord(buf[p+1])); move(buf[p+2],s[1],ord(buf[p+1])); result:=s; //GetIP(s); inc(p,ord(buf[p+1])+2); end; end;end;constructor TSocks5Proxy.Create; procedure InitProxyServer; begin with ServerSocket1 do begin OnClientConnect := ServerSocket1ClientConnect; OnClientDisconnect := ServerSocket1ClientDisconnect; OnClientRead := ServerSocket1ClientRead; OnClientError :=ServerSocket1ClientError end; end; procedure InitLookupTimer; begin with TimerRefresh do begin Interval := 200; Enabled := False; OnTimer := TimerRefreshTimer; end; end;begin LastPort:=StartPort;//最后一个使用的udp端口,可设为任意值 ServerSocket1 := TServerSocket.Create(nil); InitProxyServer; TimerRefresh := TTimer.Create(nil); InitLookupTimer; NMUDP1:= TMYNMUDP.Create(nil);end;destructor TSocks5Proxy.Destroy;begin TimerRefresh.Free; ServerSocket1.Free; NMUDP1.Free; inherited;end;procedure TSocks5Proxy.StopServer;begin TimerRefresh.Enabled := False; ServerSocket1.Active := False;end;procedure TSocks5Proxy.StartServer;begintry ServerSocket1.Port := FPort; ServerSocket1.Active := True;exceptend;end;procedure TSocks5Proxy.SetPort(const Value: Integer);begin if not ServerSocket1.Active then begin FPort := Value; end;end;procedure TSocks5Proxy.SetUser(const Value: String);begin FUser := Value;end;procedure TSocks5Proxy.SetPass(const Value: String);begin FPass := Value;end;procedure TSocks5Proxy.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);var i,j:integer;begin j:=-1; //从客户端,代理服务器,网站端关系记录数组中找一个休眠记录 for i:=0 to length(session)-1 do begin if (not session[i].Valid)then begin j:=i; session[j].Valid:=true; session[j].close:=false; break;//找到后退出 end; end; if j=-1 then //如果没有找到休眠记录,新建一个记录 begin j:=length(session); setlength(session,j+1); //重新设置记录数量 session[j].Valid:=true; session[j].close:=false; end;// session[j].ClientS:=socket; //客户端口 socket.Data:=pointer(j); //会话指针 session[j].step:=0;//第0步 session[j].UdpSite:=nil; session[j].UdpClient:=nil; session[j].TcpSite:=nil; session[j].TcpClient:=nil; session[j].ListenServer:=nil;end;procedure TSocks5Proxy.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);var i:integer;begin i:=integer(Socket.data); session[i].TcpClient:=nil; session[i].close:=true; if session[i].ListenServer<>nil then if session[i].ListenServer.Active then session[i].ListenServer.Active:=false; TimerRefresh.Enabled:=true;end;procedure TSocks5Proxy.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);begin errorcode:=0; if Socket.Connected then Socket.close; ServerSocket1ClientDisconnect(Sender,Socket);end;procedure wait(ticks:dword);var t:dword;begin t:=gettickcount; while gettickcount-t<ticks do application.ProcessMessages;end;{客户发信息过来时}procedure TSocks5Proxy.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);const MaxBuf=10240;var i,p,ReceiveBufLength:integer; sendtext:string; ReceiveBuf:array[0..MaxBuf-1]of char; siteHost:string; IP:string; user,pass:string;begin i:=integer(Socket.data);//会话指针 ReceiveBufLength:=min(MaxBuf,socket.ReceiveLength); socket.ReceiveBuf(ReceiveBuf, ReceiveBufLength); if (session[i].step=0) then begin if (plongword(@ReceiveBuf)^ and $00FFFFFF = $00000105)or (plongword(@ReceiveBuf)^ and $00FFFFFF = $00000205) then //客户端询问代理端是否可以无密码连接 begin //告诉客户端可以连接 if Fuser='' then begin sendtext:=VER+#0; inc(session[i].step,2); Socket.SendText(sendtext) ; end; { >> '00' 无验证需求 >> '01' 通用安全服务应用程序接口(GSSAPI) >> '02' 用户名/密码(USERNAME/PASSWORD) >> '03' 至 X'7F' IANA 分配(IANA ASSIGNED) >> '80' 至 X'FE' 私人方法保留(RESERVED FOR PRIVATE METHODS) >> 'FF' 无可接受方法(NO ACCEPTABLE METHODS) } end else if (plongword(@ReceiveBuf)^ and $00FFFFFF = $00020105)or (plongword(@ReceiveBuf)^ and $00FFFFFF = $00020205) then //客户端询问代理端是否可以有密码连接 begin if Fuser<>'' then begin //有密码 sendtext:=VER+#2; inc(session[i].step); Socket.SendText(sendtext) ; end; end; end else if (session[i].step=1) then begin if(ReceiveBufLength<3)then exit; if(ReceiveBuf[0]=#1)then begin p:=1; setlength(user,ord(ReceiveBuf[p])); move(ReceiveBuf[p+1],user[1],ord(ReceiveBuf[p])); inc(p, ord(ReceiveBuf[p])+1); setlength(pass,ord(ReceiveBuf[p])); move(ReceiveBuf[p+1],pass[1],ord(ReceiveBuf[p])); if(user=Fuser)and(pass=Fpass)then begin inc(session[i].step); Socket.SendText(#1#0); end else begin session[i].step:=0; Socket.SendText(#1#$2); end; end; end else if (session[i].step=2) then begin if(ReceiveBufLength<4)then exit; case plongword(@ReceiveBuf)^ and $00FFFFFF of $00000305: begin //客户端把自己的udp端口告诉代理端 p:=3; siteHost:=GetSock5Host(@ReceiveBuf,p); if siteHost='' then begin session[i].udpClient:=TMYNMUDP.Create(nil); //生成一个新的udp,用于以后连接客户 FindMyPort(session[i].udpClient,ServerSocket1.Socket.LocalHost,Lastport); session[i].udpClient.Tag:=i; //会话指针,表示客户 session[i].udpClient.OnDataReceived:=NMUDP1DataReceived; session[i].udpClient.RemotePort:= ntohs( pword(@ReceiveBuf[p])^ ); session[i].udpClient.RemoteHost:= string(inet_ntoa(Socket.RemoteAddr.sin_addr)); inc(p,2); session[i].udpSite:=TMYNMUDP.Create(nil); //生成一个新的udp,用于以后连接网站 FindMyPort(session[i].udpSite,ServerSocket1.Socket.LocalHost,Lastport); session[i].udpSite.Tag:=i or IsServer; //会话指 针,IsServer表示网站 session[i].udpSite.OnDataReceived:=NMUDP1DataReceived; setlength(IP,4); plongword(@IP[1])^:=inet_addr(pchar(socket.LocalAddress)); sendtext:=VER+#0#0#1+ IP + WordToS(htons(session[i].udpClient.LocalPort));//sock5服务器的端口,htons高低位互换 inc(session[i].step); Socket.SendText(sendtext); end; end; $00000105: begin //客户端把自己的connect端口告诉代理端 p:=3; siteHost:=GetSock5Host(@ReceiveBuf,p); if siteHost<>'' then begin// session[i].ConnectOrListen:=true; //客户是Connect session[i].TcpClient:=socket; session[i].TcpSite:=TClientSocket.Create(nil); session[i].TcpSite.Host:=siteHost; session[i].TcpSite.Port:=ntohs( pword(@ReceiveBuf[p])^ ); //要connect的端口, ntohs高低位互换 inc(p,2); session[i].TcpSite.Tag:=i; session[i].TcpSite.OnError:=ClientSocket1Error; session[i].TcpSite.OnDisconnect:=ClientSocket1Disconnect; session[i].TcpSite.OnRead:=ClientSocket1Read; session[i].LastError:=0; try session[i].TcpSite.Active:=true; except end; while(session[i].LastError=0)and(session[i].TcpSite<>nil)and(not session[i].TcpSite.Active) do application.ProcessMessages; if session[i].TcpSite=nil then exit; inc(session[i].step); if not session[i].TcpSite.Active then begin socket.SendText(VER+chr(session[i].LastError)); end else socket.SendText(VER+#0#0#1+ LongwordToS(inet_addr(pchar(socket.LocalAddress))) + WordToS(htons(socket.LocalPort)));// caption:=inttostr(socket.LocalPort); end; end; $00000205: begin //客户端把自己的Listen端口告诉代理端 p:=3; siteHost:=GetSock5Host(@ReceiveBuf,p);// if siteHost<>'' then begin// session[i].ConnectOrListen:=true; //客户是Connect session[i].TcpClient:=socket; session[i].ListenServer:=TServerSocket.Create(nil); session[i].ListenServer.OnClientConnect:=ListenServerClientConnect; session[i].ListenServer.OnClientDisconnect:=ListenServerClientDisconnect; session[i].ListenServer.OnClientError:=ListenServerClientError; session[i].ListenServer.OnClientRead:=ListenServerClientRead; session[i].ListenServer.Port:=ntohs( pword(@ReceiveBuf[p])^ ); try session[i].ListenServer.Active:=true; except end; if not session[i].ListenServer.Active then for p:=0 to 10000 do begin session[i].ListenServer.Port:=LastPort; inc(LastPort); if LastPort=MAXWORD then LastPort:=StartPort; try session[i].ListenServer.Active:=true; break; except end; end; session[i].ListenServer.Socket.Data:=pointer(i); inc(session[i].step); socket.SendText(VER+#0#0#1+ LongwordToS(inet_addr(pchar(socket.LocalAddress))) + WordToS(htons(session[i].ListenServer.Port))); end; end; end; end else if (session[i].step=3) then begin if (session[i].TcpSite<>nil)then begin if (session[i].TcpSite.Active) then begin while (not session[i].close)and(session[i].TcpSite.Socket.SendBuf(ReceiveBuf,ReceiveBufLength)=-1) do sleep(100); end else socket.Close; end else if (session[i].ListenOneThread<>nil)then begin if session[i].ListenOneThread.Connected then begin while (not session[i].close)and(session[i].ListenOneThread.SendBuf(ReceiveBuf,ReceiveBufLength)=-1) do sleep(100); end; end; end;end;procedure TSocks5Proxy.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);begin//end;procedure TSocks5Proxy.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket);var i:integer;begin i:=(sender as TClientsocket).tag; session[i].close:=true; if session[i].LastError=0 then session[i].LastError:=-1; TimerRefresh.Enabled:=true;end;procedure TSocks5Proxy.ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);begin session[(Sender as TClientSocket).tag].LastError:=ErrorCode; errorcode:=0; if Socket.Connected then Socket.close; ClientSocket1Disconnect(Sender,Socket);end;procedure TSocks5Proxy.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);var i:integer; Rectext:string;begin i:=(sender as TClientsocket).tag; if session[i].close then exit; Rectext:=socket.ReceiveText; if session[i].TcpClient.Connected then while (not session[i].close)and(session[i].TcpClient.SendText(Rectext)=-1)do //发送数据 sleep(100);end;procedure TSocks5Proxy.TimerRefreshTimer(Sender: TObject);var i:integer;begin TimerRefresh.Enabled:=false; for i:=length(Session)-1 downto 0 do begin if session[i].close then begin if (session[i].TcpClient<>nil) then begin if (session[i].TcpClient.Connected) then session[i].TcpClient.Close; //不用free session[i].TcpClient:=nil; end; if (session[i].TcpSite<>nil) then begin if (session[i].TcpSite.Active) then session[i].TcpSite.Close; session[i].TcpSite.free; session[i].TcpSite:=nil; end; if session[i].UdpSite<>nil then begin session[i].UdpSite.Free;//释放udp端口 session[i].UdpSite:=nil; end; if session[i].UdpClient<>nil then begin session[i].UdpClient.Free;//释放udp端口 session[i].UdpClient:=nil; end; if (session[i].ListenServer<>nil) then begin if session[i].ListenServer.Active then session[i].ListenServer.Active:=false; session[i].ListenServer:=nil; end; session[i].Valid:=false; end; end;end;procedure TSocks5Proxy.NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer);type TCharArray1024=array[0..2048] of char; PCharArray1024=^TCharArray1024;var p,i:integer; siteHost:string; buffer:array[0..2048] of char;// s:string;begin i:=(Sender as TMYNMUDP).Tag and (not IsServer); //连接序号 NumberBytes:=min(sizeof(buffer),NumberBytes); if ((Sender as TMYNMUDP).Tag and IsServer) <>0 then begin //表示这是网站发过来的 (Sender as TMYNMUDP).ReadBuffer(PCharArray1024(@buffer[10])^, NumberBytes); plongword(@buffer)^:=$01000000; pdword(@buffer[4])^ := inet_addr(pchar(FromIP)); pword(@buffer[8])^:= htons(Port); if session[i].UdpClient<>nil then session[i].UdpClient.SendBuffer(buffer,NumberBytes+10); end else begin //表示客户发过来的 if (NumberBytes>=4)then begin session[i].udpClient.RemotePort:=Port; (Sender as TMYNMUDP).ReadBuffer(buffer, NumberBytes); if(session[i].UdpSite<>nil) and (pdword(@buffer)^ and $00ffffff=$00000000)then //以IP v4格式的IP地址来发送 begin p:=3; siteHost:=GetSock5Host(@Buffer,p); if siteHost<>'' then begin session[i].UdpSite.RemoteHost:= siteHost; session[i].UdpSite.RemotePort:= ntohs( pword(@Buffer[p])^ ); inc(p,2); session[i].UdpSite.SendBuffer(PCharArray1024(@buffer[p])^,NumberBytes- p); end end; end; end;end;procedure TSocks5Proxy.ListenServerClientConnect(Sender: TObject; Socket: TCustomWinSocket);var i:integer;begin if (Sender as TServerWinSocket).ActiveConnections>1 then begin Socket.Data:=pointer(-1); Socket.Close; exit; end; i:=integer((Sender as TServerWinSocket).Data); Socket.Data:=pointer(i); session[i].ListenOneThread:=Socket; session[i].TcpClient.SendText(VER+#0#0#1+ LongwordToS(Longword(socket.RemoteAddr.sin_addr)) + WordToS(ntohs(Socket.RemotePort)));end;procedure TSocks5Proxy.ListenServerClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);var i:integer;begin i:=integer(Socket.data); if i=-1 then exit; session[i].close:=true;// if session[i].ListenServer.Active then// session[i].ListenServer.Active:=false; TimerRefresh.Enabled:=true;end;procedure TSocks5Proxy.ListenServerClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);begin ErrorCode:=0; ListenServerClientDisconnect(Sender,Socket);end;procedure TSocks5Proxy.ListenServerClientRead(Sender: TObject; Socket: TCustomWinSocket);var i:integer;begin i:=integer(Socket.data); if i=-1 then exit; if (not session[i].close)and(session[i].TcpClient<>nil)and(session[i].TcpClient.Connected) then begin while (not session[i].close)and(session[i].TcpClient.SendText(Socket.ReceiveText)=-1)do sleep(100); end;end;end.