unit RioSocks5Server;
{
//chatGPT写的 编译没通过还要调整
调用例子
uses
Winapi.Winsock2, System.SysUtils, RioSocks5Server, RioProxyServer;
var
Socks5Server: TRioSocks5Server;
ProxyServer: TRioProxyServer;
begin
Socks5Server := TRioSocks5Server.Create;
ProxyServer := TRioProxyServer.Create;
try
Socks5Server.Start('0.0.0.0', 1080, 1024);
ProxyServer.Start('0.0.0.0', 0, 1024);
ProxyServer.OnProxyComplete := Socks5Server.OnProxyComplete;
ProxyServer.AuthRequired := True;
ProxyServer.Authenticator := TMyAuthenticator.Create;
TMyAuthenticator(ProxyServer.Authenticator).AddUser('username', 'password');
while True do
begin
Sleep(1000);
end;
finally
Socks5Server.Free;
ProxyServer.Free;
end;
end;
}
interface
uses
System.SysUtils,
System.Classes,
System.SyncObjs,
System.Win.ScktComp,
Winapi.Windows,
Winapi.WinSock2,
IdWinsock2,
Soap.Rio;
type
TRioSocks5Server = class
private
FRioHandle: THandle;
FRioCompletionPort: THandle;
FServerSocket: TServerSocket;
FNumBuffers: Integer;
FBufferSize: Integer;
FOverlappedArray: array of TOverlapped;
FBufferArray: array of Pointer;
FAcceptThread: TThread;
FClientThreads: TThreadList;
FUserName: string;
FPassword: string;
procedure CreateRIO;
procedure DestroyRIO;
procedure OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure OnClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure AcceptThreadFunc;
procedure ClientThreadFunc(Socket: TCustomWinSocket);
public
constructor Create(ANumBuffers, ABufferSize: Integer; const AUserName, APassword: string);
destructor Destroy; override;
procedure Start;
procedure Stop;
end;
function RIOCreateExtensionFunctionTable(const FunctionTableId: TGUID; const FunctionTable: PRIO_EXTENSION_FUNCTION_TABLE): HRESULT; stdcall; external 'mswsock.dll' name 'RIOCreateExtensionFunctionTable';
implementation
type
TRIOContext = record
Socket: TCustomWinSocket;
Buffer: Pointer;
Overlapped: TOverlapped;
ProxyConnected: Boolean;
end;
PRIOContext = ^TRIOContext;
const
MAX_RIO_REQUESTS = 1024;
MAX_RIO_RESULTS = 1024;
MAX_RIO_THREADS = 16;
SOCKS5_VERSION = $05;
SOCKS5_AUTH_NONE = $00;
SOCKS5_AUTH_USERNAME_PASSWORD = $02;
SOCKS5_AUTH_NO_ACCEPTABLE_METHODS = $FF;
SOCKS5_CMD_CONNECT = $01;
SOCKS5_ATYP_IPV4 = $01;
SOCKS5_ATYP_DOMAINNAME = $03;
SOCKS5_REP_SUCCESS = $00;
SOCKS5_REP_GENERAL_FAILURE = $01;
SOCKS5_REP_CONNECTION_NOT_ALLOWED = $02;
SOCKS5_REP_NETWORK_UNREACHABLE = $03;
SOCKS5_REP_HOST_UNREACHABLE = $04;
SOCKS5_REP_CONNECTION_REFUSED = $05;
SOCKS5_REP_TTL_EXPIRED = $06;
SOCKS5_REP_COMMAND_NOT_SUPPORTED = $07;
SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED = $08;
constructor TRioSocks5Server.Create(ANumBuffers, ABufferSize: Integer; const AUserName, APassword: string);
begin
FNumBuffers := ANumBuffers;
FBufferSize := ABufferSize;
FServerSocket := TServerSocket.Create(nil);
FServerSocket.OnClientConnect := OnClientConnect;
FServerSocket.OnClientRead := OnClientRead;
FServerSocket.OnClientDisconnect := OnClientDisconnect;
FClientThreads := TThreadList.Create;
FUserName := AUserName;
FPassword := APassword;
end;
destructor TRioSocks5Server.Destroy;
begin
Stop;
FClientThreads.Free;
FServerSocket.Free;
inherited;
end;
procedure TRioSocks5Server.Start;
begin
CreateRIO;
FServerSocket.Active := True;
FAcceptThread := TThread.CreateAnonymousThread(AcceptThreadFunc);
FAcceptThread.FreeOnTerminate := False;
FAcceptThread.Start;
end;
procedure TRioSocks5Server.Stop;
begin
FServerSocket.Active := False;
FAcceptThread.Terminate;
FAcceptThread.WaitFor;
DestroyRIO;
end;
procedure TRioSocks5Server.CreateRIO;
var
ExtensionFunctionTable: RIO_EXTENSION_FUNCTION_TABLE;
I: Integer;
Result: HRESULT;
begin
FillChar(ExtensionFunctionTable, SizeOf(ExtensionFunctionTable), 0);
ExtensionFunctionTable.cbSize := SizeOf(ExtensionFunctionTable);
Result := RIOCreateExtensionFunctionTable(@ExtensionFunctionTable, RIO_CORRUPT_CQ or RIO_MSG_DONT_NOTIFY);
if Result <> S_OK then
raise Exception.Create('Failed to create RIO extension function table');
ExtensionFunctionTable.RIOReceive := RIOReceive;
ExtensionFunctionTable.RIOSend := RIOSend;
ExtensionFunctionTable.RIOCloseCompletionQueue := RIOCloseCompletionQueue;
ExtensionFunctionTable.RIOCreateCompletionQueue := RIOCreateCompletionQueue;
ExtensionFunctionTable.RIOCreateRequestQueue := RIOCreateRequestQueue;
ExtensionFunctionTable.RIODequeueCompletion := RIODequeueCompletion;
ExtensionFunctionTable.RIODeregisterBuffer := RIODeregisterBuffer;
ExtensionFunctionTable.RIORegisterBuffer := RIORegisterBuffer;
ExtensionFunctionTable.RIONotify := RIONotify;
ExtensionFunctionTable.RIOReceiveEx := RIOReceiveEx;
ExtensionFunctionTable.RIOSendEx := RIOSendEx;
ExtensionFunctionTable.RIOSendMessages := RIOSendMessages;
ExtensionFunctionTable.RIOReceiveMessages := RIOReceiveMessages;
ExtensionFunctionTable.RIOResizeCompletionQueue := RIOResizeCompletionQueue;
ExtensionFunctionTable.RIOResizeRequestQueue := RIOResizeRequestQueue;
ExtensionFunctionTable.RIOGetCompletionEx := RIOGetCompletionEx;
ExtensionFunctionTable.RIOGetRequestEx := RIOGetRequestEx;
ExtensionFunctionTable.RIOGetResultEx := RIOGetResultEx;
ExtensionFunctionTable.RIODestroyRequestQueue := RIODestroyRequestQueue;
ExtensionFunctionTable.RIODestroyCompletionQueue := RIODestroyCompletionQueue;
ExtensionFunctionTable.RIOReceiveMsg := RIOReceiveMsg;
ExtensionFunctionTable.RIOSendMsg := RIOSendMsg;
FillChar(FOverlappedArray[0], Length(FOverlappedArray) * SizeOf(TOverlapped), 0);
FillChar(FBufferArray[0], Length(FBufferArray) * SizeOf(Pointer), 0);
FRioHandle := CreateEvent(nil, False, False, nil);
if FRioHandle = 0 then
raise Exception.Create('Failed to create RIO event');
Result := RIORegisterBuffer(FBufferArray[0], Length(FBufferArray) * FBufferSize);
if Result <> S_OK then
raise Exception.Create('Failed to register RIO buffers');
ExtensionFunctionTable.MaxOutstandingReceive := FNumBuffers;
ExtensionFunctionTable.MaxOutstandingSend := FNumBuffers;
ExtensionFunctionTable.MaxReceiveDataBuffers := 1;
ExtensionFunctionTable.MaxSendDataBuffers := 1;
ExtensionFunctionTable.RegistrationGuid := TGUID.Empty;
ExtensionFunctionTable.ReceiveCompletionRoutine := nil;
ExtensionFunctionTable.SendCompletionRoutine := nil;
ExtensionFunctionTable.ReceiveExCancellationRoutine := nil;
ExtensionFunctionTable.SendExCancellationRoutine := nil;
ExtensionFunctionTable.GetOverlappedResultEx := nil;
FRioCompletionPort := ExtensionFunctionTable.RIOCreateCompletionQueue(MAX_RIO_REQUESTS, nil);
if FRioCompletionPort = RIO_INVALID_CQ then
raise Exception.Create('Failed to create RIO completion port');
Result := ExtensionFunctionTable.RIORegisterBuffer(FBufferArray[0], Length(FBufferArray) * FBufferSize);
if Result <> S_OK then
raise Exception.Create('Failed to register RIO buffers');
for I := 0 to MAX_RIO_REQUESTS - 1 do
begin
PRIOContext(@FOverlappedArray[I])^.Buffer := Pointer(UIntPtr(FBufferArray[0]) + (I * FBufferSize));
PRIOContext(@FOverlappedArray[I])^.ProxyConnected := False;
end;
Result := ExtensionFunctionTable.RIOCreateRequestQueue(FServerSocket.SocketHandle, MAX_RIO_REQUESTS, 1, MAX_RIO_REQUESTS, FBufferSize, FBufferSize, 0, nil, FRioCompletionPort, nil);
if Result <> S_OK then
raise Exception.Create('Failed to create RIO request queue');
end;
procedure TRioSocks5Server.DestroyRIO;
var
ExtensionFunctionTable: RIO_EXTENSION_FUNCTION_TABLE;
Result: HRESULT;
begin
FillChar(ExtensionFunctionTable, SizeOf(ExtensionFunctionTable), 0);
ExtensionFunctionTable.cbSize := SizeOf(ExtensionFunctionTable);
Result := RIOCreateExtensionFunctionTable(@ExtensionFunctionTable, RIO_CORRUPT_CQ or RIO_MSG_DONT_NOTIFY);
if Result <> S_OK then
raise Exception.Create('Failed to create RIO extension function table');
Result := ExtensionFunctionTable.RIOCloseCompletionQueue(FRioCompletionPort);
if Result <> S_OK then
raise Exception.Create('Failed to close RIO completion port');
Result := ExtensionFunctionTable.RIODeregisterBuffer(FBufferArray[0]);
if Result <> S_OK then
raise Exception.Create('Failed to deregister RIO buffers');
CloseHandle(FRioHandle);
end;
procedure TRioSocks5Server.OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
RioRequestContext: PRIOContext;
BytesReceived: DWORD;
begin
if Socket.SocketType = stStream then
begin
BytesReceived := Socket.ReceiveBuf(FBufferArray[0]^, FBufferSize, 0);
if BytesReceived >= 3 then
begin
if (PByte(FBufferArray[0])^ = SOCKS5_VERSION) and (PByte(FBufferArray[0])^ = BytesReceived - 1) and ((PByte(FBufferArray[0]) + 1)^ > 0) then
begin
RioRequestContext := PRIOContext(FOverlappedArray[0].hEvent);
RioRequestContext.Socket := Socket;
Move(FBufferArray[0]^, PByte(@RioRequestContext.Buffer)^, BytesReceived);
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RioRequestContext.ProxyConnected := False;
if (PByte(FBufferArray[0]) + 1)^ >= 1 then
RioRequestContext.ProxyConnected := True;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end;
end;
end;
Socket.Close;
end;
procedure TRioSocks5Server.OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
RioRequestContext: PRIOContext;
BytesReceived: DWORD;
UserName: string;
Password: string;
begin
RioRequestContext := PRIOContext(Socket.Data);
BytesReceived := 0;
try
BytesReceived := Socket.ReceiveBuf(Pointer(UIntPtr(RioRequestContext.Buffer) + RioRequestContext.Overlapped.InternalHigh)^, FBufferSize - RioRequestContext.Overlapped.InternalHigh, 0);
except
end;
if BytesReceived > 0 then
begin
if not RioRequestContext.ProxyConnected then
begin
if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_AUTH_USERNAME_PASSWORD) and (BytesReceived = 3) then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RioRequestContext.ProxyConnected := True;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end
else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_AUTH_NONE) and (BytesReceived = 2) then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RioRequestContext.ProxyConnected := True;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end
else
begin
Socket.Close;
Exit;
end;
end
else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = 0) then
begin
case PByte(RioRequestContext.Buffer + 3)^ of
SOCKS5_ATYP_IPV4:
begin
if BytesReceived >= 10 then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end;
end;
SOCKS5_ATYP_DOMAINNAME:
begin
if (BytesReceived >= 5) and (BytesReceived = 5 + PByte(RioRequestContext.Buffer + 4)^) then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end;
end;
SOCKS5_ATYP_IPV6:
begin
if BytesReceived >= 22 then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end;
end;
end;
Socket.Close;
Exit;
end
else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = $FF) then
begin
Socket.Close;
Exit;
end
else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = $02) and (BytesReceived >= 3) then
begin
if (PByte(RioRequestContext.Buffer + 3)^ = 0) and (BytesReceived = 4) then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end
else if (PByte(RioRequestContext.Buffer + 3)^ = 1) and (BytesReceived >= 5) and (PByte(RioRequestContext.Buffer + 4)^ > 0) then
begin
SetLength(UserName, PByte(RioRequestContext.Buffer + 4)^);
Move((PByte(RioRequestContext.Buffer + 5))^, PByte(UserName)^, Length(UserName));
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end
else if (PByte(RioRequestContext.Buffer + 3)^ = 2) and (BytesReceived >= 6) and (PByte(RioRequestContext.Buffer + 4)^ > 0) then
begin
SetLength(UserName, PByte(RioRequestContext.Buffer + 4)^);
Move((PByte(RioRequestContext.Buffer + 5))^, PByte(UserName)^, Length(UserName));
if (BytesReceived >= 6 + Length(UserName)) and (PByte(RioRequestContext.Buffer + 5 + Length(UserName))^ > 0) then
begin
SetLength(Password, PByte(RioRequestContext.Buffer + 5 + Length(UserName))^);
Move((PByte(RioRequestContext.Buffer + 6 + Length(UserName)))^, PByte(Password)^, Length(Password));
if (not FAuthRequired) or ((FAuthRequired) and (FAuthenticator.Authenticate(UserName, Password))) then
begin
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end
else
begin
FServerSocket.SocketConnection[Socket.Handle].SendBuf(SOCKS5_AUTH_FAILED_RESPONSE, 2);
Socket.Close;
Exit;
end;
end;
end;
Socket.Close;
Exit;
end
else
begin
RioRequestContext.Socket.SocketType := stCustom;
FProxyServer.BeginProxyRequest(RioRequestContext.Socket, FBufferArray[0]^, BytesReceived);
RioRequestContext.Socket := nil;
RioRequestContext.ProxyConnected := False;
RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
Exit;
end;
end;
Socket.Close;
end;
procedure TRioSocks5Server.OnProxyComplete(Sender: TObject; ClientSocket, ProxySocket: TCustomWinSocket; Request: TProxyRequest; Buffer: TBytes; BytesTransferred: Integer);
var
RioRequestContext: PRIOContext;
SentBytes: Integer;
begin
if not Assigned(ClientSocket) or not Assigned(ProxySocket) then
Exit;
if not (ClientSocket.SocketType = stCustom) or not (ProxySocket.SocketType = stCustom) then
Exit;
RioRequestContext := PRIOContext(ClientSocket.Data);
if not Assigned(RioRequestContext) then
begin
ClientSocket.Close;
ProxySocket.Close;
Exit;
end;
if not RioRequestContext.ProxyConnected then
begin
ClientSocket.Close;
ProxySocket.Close;
Exit;
end;
try
ProxySocket.SendBuf(Buffer[0], BytesTransferred);
RioRequestContext.Socket := ProxySocket;
RioRequestContext.Overlapped.Offset := 0;
RioRequestContext.Overlapped.OffsetHigh := 0;
RioRequestContext.Overlapped.hEvent := FRioHandle;
RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
except
ClientSocket.Close;
ProxySocket.Close;
end;
end;
end.
Windows新异步网络编程模型5 RIO-socks5服务端
于 2023-02-26 17:07:49 首次发布