Os presento un mini servidor proxy que desvía nuestro tráfico de internet hacia otro servidor Proxy, que llamaré Proxy real.
En un principio tuve necesidad de no conectar directamente con el servidor Proxy real, sino hacerlo a través de otro PC, para ello escribí esta pequeña aplicación que hace de intermediaria entre un explorador y el Proxy real.
Su funcionamiento es muy simple, básicamente se trata de un servidor de escucha a nivel socket y al tiempo conecta con el Proxy real en su puerto. Un hilo se encargara de establecer la conexión entre ambos lados y se trasmitir todo el diálogo http.
El código no usa componentes, es a nivel de sockets. Puede funcionar como un pequeño espía o debuger al ver el tráfico. El debuger es muy rudimentario, mostraría todos los mensajes de todos los hilos creados. Cada 64K borra el Memo donde muestra los comandos para no sobrecargarlo.
La interfece consta de tres edit:
1.- Puerto de comunicación con nuestro Proxy ( la IP será la máquina donde se ejecute)
2.- Dirección del Proxy real
3.- Puerto del Proxy real
El proyecto es un prototipo y puede mejorarse mucho, e incluso convertirlo en un Proxy real interpretando los comandos http.
El núcleo del servidor-cliente es el siguiente código, compuesto de dos TThreard, el servidor y los hilos de comunicación por cada cliente conectado.
unit ThProxy; interface uses Classes, Windows, Messages, winsock, syncobjs; type TServer = class(TThread) private Sock_e: TSOCKET; // Sock de escucha FPort: DWORD; protected procedure Execute; override; public Constructor Create(Port: DWORD; _ProxyServer: String; _ProxyPort: DWORD); procedure Close(); end; TClient = class(TThread) private FDSet: TFDSet; FSocket: TSOCKET; DSocket: TSOCKET; Buffer: PCHAR; function Connect(IP: PCHAR; Port: DWORD): TSOCKET; function Select(): integer; procedure SendLog(); function SendHttp(Socket1, Socket2: TSOCKET): boolean; function TrasmitHttp: boolean; protected procedure Execute; override; public Constructor Create(Socket: TSOCKET); end; implementation { TServer } uses Unit1; const BUFSIZE = 124*16; var CS: TCriticalSection; ProxyServer: String; ProxyPort: DWORD; function GetReadableBytes(Sock: TSOCKET): integer; begin Result:= -1; if ioctlsocket(sock, FIONREAD, Result) < 0 then Result:= 0; end; Constructor TServer.Create(Port: DWORD; _ProxyServer: String; _ProxyPort: DWORD); begin inherited Create(false); FreeOnTerminate:= true; FPort:= Port; ProxyServer:= _ProxyServer; ProxyPort:= _ProxyPort; end; procedure TServer.Close(); begin closesocket(Sock_e); Terminate; end; procedure TServer.Execute; var Wsa: WSADATA; Sock_c: TSOCKET; // Sock de comunicación Addr_s, Addr_c: sockaddr_in; Len: integer; begin Len:= 0; // Inicializamos if WSAStartup(MAKEWORD(2, 0), Wsa) <> 0 then exit; CS:= TCriticalSection.Create; //Creamos el socket Sock_e:= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if Sock_e <> INVALID_SOCKET then begin // Dirección IP y puerto Addr_s.sin_family:= AF_INET; Addr_s.sin_addr.s_addr:= INADDR_ANY; Addr_s.sin_port:= htons(FPort); // Asociamos el socket al puerto if bind(Sock_e, Addr_s, sizeof(sockaddr_in)) <> -1 then begin // Escuchando puerto if listen(Sock_e, 1) <> -1 then begin // Bucle principal del servidor while not Terminated do begin //hay una conexión entrante y la aceptamos Len:= sizeof(sockaddr_in); Sock_c:= accept(Sock_e, @Addr_c, @Len); if Sock_c <> INVALID_SOCKET then TClient.Create(Sock_c); end; end; closesocket(Sock_e); end; // if(bind) end; // if(Sock_e) CS.Free; WSACleanup; end; function TClient.Connect(IP: PCHAR; Port: DWORD): TSOCKET; var host: Phostent; direc: sockaddr_in; Error: boolean; begin Error:= true; Result:= -1; Result:= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(Result <> -1) then begin //Dirección IP del servidor y Puerto host:= gethostbyname(IP); if host <> nil then begin direc.sin_family:= AF_INET; direc.sin_port:= htons(Port); direc.sin_addr.S_addr:= PInAddr(host.h_addr_list^)^.S_addr; ZeroMemory(@direc.sin_zero[0], 8); if Winsock.Connect(Result, direc, sizeof(direc)) <> -1 then Error:= false; end; if Error then begin closesocket(Result); Result:= -1; end; end; end; Constructor TClient.Create(Socket: TSOCKET); begin inherited Create(false); FreeOnTerminate:= true; FSocket:= Socket; end; function TClient.Select(): integer; var Time: timeval; begin Time.tv_sec:= 0; Time.tv_usec:= 10; FD_ZERO(FDSet); FD_SET(FSocket, FDSet); FD_SET(DSocket, FDSet); // Comprobamos si ha recibido algun mensaje Result:= Winsock.select(0, @FDSet, 0, 0, @Time); end; procedure TClient.SendLog(); begin try CS.Enter; with Form1 do begin if CheckBox1.Checked then begin if Length(Memo1.Text) + lstrlen(Buffer) >= 64000 then Memo1.Clear; Memo1.Text:= Form1.Memo1.Text + Buffer; Memo1.Perform( EM_LINESCROLL, 0, Memo1.Lines.Count); end; end; finally CS.Leave(); end; end; function TClient.SendHttp(Socket1, Socket2: TSOCKET): boolean; var Count, Len, nBytes: DWORD; begin Count:= 0; Len:= 0; Buffer^:= #0; nBytes:= GetReadableBytes(Socket1); if nBytes>0 then begin if FSocket = Socket1 then lstrcpy(Buffer, #13+#10 + 'Local:' + #13+#10) else lstrcpy(Buffer, #13+#10 + 'Remoto:' + #13+#10); SendLog; end; while (nBytes > 0) and (Len <> -1) do begin if nBytes > BUFSIZE-1 then nBytes:= BUFSIZE-1; Len:= recv(Socket1, Buffer^, nBytes, 0); //recibimos los datos que envie if Len > 0 then begin Count:= Count + Len; Buffer[Len]:= #0; SendLog; send(Socket2, Buffer^, Len, 0); end; nBytes:= GetReadableBytes(Socket1); end; Result:= Count > 0; end; function TClient.TrasmitHttp: boolean; begin Result:= false; Buffer^:= #0; repeat Select(); // recibo de la fuente y envío al destino if FD_ISSET(FSocket, FDSet) then SendHttp(FSocket, DSocket); // Recibo respuesta de destino y la envío a la fuente if FD_ISSET(DSocket, FDSet) then Result:= SendHttp(DSocket, FSocket); until Buffer^ = #0 end; procedure TClient.Execute; var Len: integer; T: boolean; begin Len:= 0; Buffer:= nil; GetMem(Buffer, BUFSIZE); T:= false; // Bucle de comunicación repeat DSocket:= Connect(PCHAR(ProxyServer), ProxyPort); if DSocket <> -1 then T:= TrasmitHttp; closesocket(DSocket); until not T; closesocket(FSocket); FreeMem(Buffer); end; end.
Para probarlo en nuestro PC podemos configurar el navegador para que el proxy sea 127.0.0.1 en el puerto 8080. En nuestro miniproxy estableceremos los valores del puerto de escucha (8080) y del proxy real.
Adjunto el código completo del proyecto.
Saludos.