Winsock + Protocolos mas usados versión 1 (HTTP)

Iniciado por HaDeS, -, 3 Noviembre 2007, 04:32 AM

0 Miembros y 1 Visitante están viendo este tema.

HaDeS, -

(Cabe aclarar que los contenidos son básicos, así que ¡no se aceptan críticas destructivas!, de seguro también me equivoque por ahí, así que los errores me los envían al privado)

Protocolo HTTP
Es un protocolo donde se debe seguir el esquema de cliente-servidor (Donde el cliente se conecta al servidor, hace una petición, y el servidor responde de acuerdo a la estructura de esa petición), luego de que el servidor procesa la petición, se cierra la conexión, aunque el cliente también la puede cerrar. Como este protocolo no es un protocolo de estado, esto es que no guarda información que haya mandado el cliente, entonces se recurre a las cookies como archivo temporal para hacer esto.

Cliente
Existe un formato estándar para la petición que realiza el cliente, así:
El crlf significa retorno de carro y avance de línea [enter]

(Método) (Archivo o recurso) (Versión protocolo)(crlf)
(Cabecera1)(:) (Valor)(crlf)
(Cabecera2)(:) (Valor)(crlf)
(CabeceraN)(:) (Valor)(crlf)
(crlf)
(Cuerpo del mensaje)


Metodos::
Los métodos dependen del servidor y pueden ser definidos por el programador, aunque los que se usan comúnmente o los "necesarios" por así decir son el método GET y POST. También existen varios como OPTIONS, HEAD, TRACE, PUT, pero no los vamos a tratar.
GET: Se utiliza para hacer petición de un archivo X indicado en la primera línea del formato de petición del cliente.
POST: Se utiliza para enviar datos a un archivo X indicado en la primera línea del formato de petición el cliente.
OPTIONS: Se utiliza para hacer petición al servidor de los métodos que puede usar el cliente.

Cabeceras::
Son varias las cabeceras que necesitamos para realizar una petición correctamente. Entre ellas encontramos Host, User-Agent, Accept, Accept-Charset, Keep-Alive, Connection. Cabe decir que no son necesarias las cabeceras, ya que con solo la conexión y el método podemos hacer un request a una página.
Host: Indica el nombre de la "página web" a la que queremos acceder. Si el servidor aloja varias páginas web, se debe indicar esta cabecera, ya que si no lo hacemos se puede estar accediendo a otra parte del servidor.
User-Agent: Indica el nombre de la aplicación que usa el cliente en el momento de la conexión.
Accept: Indica los tipos de archivo que acepta el cliente.
Acept-Charset: Indica que tipo de caracteres acepta el servidor.
Connection: Indica si la conexión se cierra al momento de procesar la petición, o si queda a la escucha de mas peticiones. Si es así se indica la cabecera keep-alive.
Keep-Alive: Indica el tiempo en milisegundos de espera del servidor en escucha de más peticiones. Se usa en conexiones persistentes, y sirve para hace peticiones sobre múltiples objetos.

Servidor:
La respuesta del servidor al cliente va así:

(Respuesta)(crlf)
(Cabeceras del servidor)(crlf)
(crlf)
(Cuerpo del mensaje)


Respuesta:
Indica la versión y un código de estado. Los códigos más comunes son:
200 = Indica que todo va bien
300 = Que existen múltiples archivos con algún nombre
302 = Recurso movido temporalmente
400 = Mala petición
403 = Denegado
404 = No se encontró el recurso
500 = Error interno del servidor
501 = No implementado (Para los métodos)
Entre otros.

Cabeceras del servidor:
En estas cabeceras se puede encontrar información acerca del servidor, tal como el nombre mismo del servidor que se usa, el sistema operativo en el cual se encuentra (Aunque muchas veces no lo da), la fecha y hora en la que se encuentra, las cookies que el manda, el tipo de contenido que se envió, entre otros.

Cuerpo del mensaje:
En esta se encuentra la respuesta en si del servidor a un recurso que existe.


Para entenderlo todo mejor un ejemplo de las dos conexiones mas usadas, GET y POST.

Caso1
Tenemos un servidor donde esta alojado un archivo html llamado "ejemplo.html" que esta ubicado en la carpeta "ejemplos" de la carpeta raíz de tal servidor. Además de esto sabemos que el nombre del servidor es "ejemplo.co"

Petición del cliente:

GET /ejemplos/ejemplo.html http/1.1 (crlf)
Host: ejemplo.co (crlf)
Connection: Close (crlf)
(crlf)


A lo que el servidor respondería:

HTTP/1.1 200 OK (crlf)
Server: Ejemplos/1.1 (crlf)
Date: Fri 02 Nov 2007 00:00:03 GMT (crlf)
Content-Length: 81 (crlf)
(crlf)
<html><head><title>Ejemplos</title></head><body><h1>Ejemplo!!</h1></body></html>

El content-length indica cuantos bytes fueron transferidos en el cuerpo del mensaje.
Luego de esto se cerraría la conexión.

Acá entra el concepto de variables get usadas en php. Veamos:

Hola.php ubicado en la carpeta raiz de un servidor que se llama variables.com:
Código (php) [Seleccionar]

$mensaje = $_GET['msg'];
Echo $mensaje;


Petición:

GET /hola.php?msg=holaaaa HTTP/1.0 (crlf)
Host: variables.com (crlf)
Connection: Close (crlf)
(crlf)


Respuesta:

HTTP/1.0 200 OK
Server: Ejemplos/1.1 (crlf)
Date: Fri 02 Nov 2007 00:00:06 GMT (crlf)
Content-Length: 7 (crlf)
(crlf)
holaaaa


Vemos que en la petición, la variable get se envía en la primera línea, donde se hace petición del recurso. El recurso de separa de la variable con un signo de interrogación "?" y la variable tiene el formato: "Variable" "=" "Contenido": hola.php?msg=holaaa.
El script obtiene el contenido de la variable "msg" y hace un echo con ella.
Se cierra la conexión.

HaDeS, -

#1
Caso2
Acá implementaremos el método POST. Tenemos un archivo php llamado datos el cual recibe el nombre, apellido, y teléfono de una persona X. No esta ubicado en ningún host, así que vamos a acceder por la dirección IP.

Datos.php:
Código (php) [Seleccionar]

$nombre = $_POST['name'];
$apellido = $_POST['lname'];
$telefono = $_POST['tel'];

$a = fopen("buscar.php", "a");
fwrite($a, $nombre . " .. " . $apellido . " .. " . $telefono . "<br>");
fclose($a);
echo "Bien :)";


La petición iría así:

POST /datos.php HTTP/1.1 (crlf)
Content-Length: 37 (crlf)
(crlf)
name=maría&lname=cristina&tel=2880000


No contiene la cabecera host, por lo que dije antes, de que solo se conectaría al ip.
Y la respuesta del servidor:

HTTP/1.1 200 OK
Server: Ejemplos/1.1 (crlf)
Date: Fri 02 Nov 2007 00:00:09 GMT (crlf)
Content-Length: 7 (crlf)
(crlf)
Bien :)


El script guardaría en el archivo "Buscar.php" el nombre, apellido y teléfono de la persona la cual ingreso los datos.
Ahora para hacer todo mas dinámico les propongo un ejercicio :P
Resulta que el archivo "buscar.php" no estaba vació del todo, el programador se ingenuo una forma para hacer que él solo ingresara a ese archivo, pero no se percato de que el archivo podría salirse de sus manos y parar en las mías :D
Ahora este es el código de buscar.php:
Código (php) [Seleccionar]

$acceso = 0;
$var = $_SERVER['HTTP_MD5'];
$var2 = $_POST['codigo'];

if($var2 == "HaDeS"){
if( md5($var2) == $var){
//Acceso, vista de datos y manejo de sesiones.
            }else{ exit("Fuera!") };
}else{ exit("Fuera!") };
?>
maría .. cristina .. 2880000

El ejercicio trata de fabricar la petición que nos dará acceso a la información que se guarda en este file... xD
Editado: También deberan encontrar la vulnerabilidad de algún archivo que publique mas arriba, y luego como explotarla!!

Y el segundo caso del segundo caso (envío de archivos):
Tenemos un archivo "subir.asp" con el siguiente code:
Código (asp) [Seleccionar]

Dim Archivo
Set Archivo = Server.CreateObject("Perits.Upload")
Archivo.Save(Server.MapPath("\"))
Set Archivo = Nothing


Y tenemos el formulario de envío:
Código (html) [Seleccionar]

<form action=subir.asp method=post enctype=multipart/form-data>
<input name=archivo type=file>
<input type=submit name=Submit value=Mandar>
</form>


La petición, vamos a mandar un archivo de 25 bytes para no ampliarnos tanto:

POST /subir.asp HTTP/1.0 (crlf)
Content-Type: multipart/form-data, boundary=tyoasEdRQLsovS3LUFPBS5Eyy7tOwzil (crlf)
Content-Length: 197 (crlf)
(crlf)
--tyoasEdRQLsovS3LUFPBS5Eyy7tOwzil (crlf)
Content-Disposition: form-data; name="archivo"; filename="imagen.jpg" (crlf)
Content-Type: image/jpeg (crlf)
(crlf)
Soy un jpg modificado!!! (crlf)
--tyoasEdRQLsovS3LUFPBS5Eyy7tOwzil—


Como ven, ya se pone un poco complicada la cosa, pero miremos su estructura:

POST (Recurso) (Versión protocolo) (crlf)
(Content-Type)(:) (Tipo de contenido)(,) (boundary)(=)(codigo) (crlf)
(Content-Length)(:) (Longitud de datos del cuerpo del mensaje) (crlf)
(crlf)
(codigo) (crlf)
(Content-Disposition)(:) (datos) (crlf)
(Content-Type)(:) (Tipo archivo) (crlf)
(crlf)
(contenido archivo) (crlf)
(codigo)

Explico varias cosas:
El content-type varia por el tipo de archivo que mandamos, si estamos mandando varios post o varios archivos este tipo sera: multipart/form-data, si es otro tipo de archivos remitirse a: http://www.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html

El codigo sirve para separar los diferentes post que estamos mandando, diferentes archivos por ejemplo, seria:

(codigo)
Archivo 1
(codigo)

(codigo)
Archivo 2
(codigo)


El content-Length es la longitud de los datos, pero no solo de los archivos si no de todo el cuerpo del mensaje, incluyendo los códigos.

Ja! No se que mas decir jaja, a ver, en el content-disposition de la petición que armamos "Content-Disposition: form-data; name="archivo"; filename="imagen.jpg"", se distinguen varios puntos, el "name" que es la variable que estamos enviando, y el "filename" que supuestamente es el nombre del archivo que mandamos.

Y bueno, con esto se acaba la breve explicación del protocolo http, si quieren investigar y saber más acerca de este, existe el rfc: http://tools.ietf.org/html/rfc2616.

HaDeS, -

#2
Código Visual Basic:
El código es lo mas sencillo de todo el proceso, voy a publicar solo la base, y luego el proyecto entero.

Necesitamos:
CommandButton: Nombre = Request
TextBox: Nombre = Url
TextBox: Nombre = Petición
                Multicine = True
TextBox: Nombre = Resp
                Multiline = True
Componente: Microsoft Winsock Control 6.0: Nombre = Ws

Código (vb) [Seleccionar]

Private Sub Request_Click()
Host = SepararHostdeUrl(Url.text)
Ws.Connect Host, 80 'El puerto por defecto del servicio http es el 80

Volver:
If Ws.State <> sckConnected Then 'Si el cliente no esta conectado entonces va a el punto de referencia "Volver"
       DoEvents
       Goto Volver
End If
'Esto provocaría un loop infinito si no se hacen bien las cosas, en el código completo esta corregido el fallo.

Ws.Senddata Petición.text
End Sub

Private Function SepararHostdeUrl(Url$) As String
'Esta función sirve para separar el host de la URL
Dim Host$, X%
If Url <> "http://" Then
    Host = Replace(Url, "http://", "")
    X = InStr(1, Host, "/")
Volver:
    If X <> 1 Then
        If X <> 0 And Len(Host) <> X Then
            Host = Mid$(Host, 1, X - 1)
        Else
            Host = Replace(Host, "/", "")
        End If
    Else
        Host = Mid$(Host, 2, Len(Host) - 1)
        GoTo Volver
    End If
    X = InStr(1, Host, ".")
    If X <> 0 And Len(Host) = X Then
        Host = Replace(Host, ".", "")
        Host = Host & ".com"
    ElseIf X = 0 And Host <> "localhost" Then
        Host = Host & ".com"
    End If
Volver2:
    If InStr(1, UrlChars, Mid$(Host, 1, 1), vbTextCompare) = 0 Then
        Host = Mid$(Host, 2, Len(Host) - 1)
        GoTo Volver2
    End If
Volver3:
    If InStr(1, Mid$(UrlChars, 1, 26), Mid$(Host, Len(Host), 1), vbTextCompare) = 0 Then
        Host = Mid$(Host, 1, Len(Host) - 1)
        GoTo Volver3
    End If
End If
SepararHostdeUrl = Host
End Function

Private Sub Ws_DataArrival(ByVal bytesTotal As Long)
Dim Respuesta as string
Ws.Getdata Respuesta, vbstring, bytestotal
Resp.Text = Respuesta
End Sub


Explico varias cosas:
La función "SepararHostdeUrl" todavía no esta completamente terminada, me falta agregarle para que funcione con números ip.

Ws.Connect, se utiliza para conectar al servidor, el primer parámetro indica el host o Ip del servidor, y el segundo indica el puerto en el cual se esta brindando el servicio.

Ws.Senddata, se utiliza para enviar datos al servidor.

Ws.Getdata, se utiliza para recibir lo que nos manda el servidor, el primer parámetro es la variable en la cual se almacenara lo que este nos envié, el segundo parámetro es el tipo de dato que se esta recibiendo y el tercer dato es la longitud de los datos que recibimos.

El textbox "Petición" debe estar especialmente armada con la petición que queremos enviar. Ej:

GET / HTTP/1.1
...

En la versión completa del minibrowser, esta implementadas algunas funciones como guardar la respuesta del servidor, editar variables, construirlas, manejo de cookies.

Guardar respuesta del servidor:
Como vemos en las respuesta del servidor, podemos separar dos elementos claros, las cabeceras del servidor y el cuerpo del mensaje, los cuales están separados por un doble (crlf), para guardar la respuesta del servidor, por ejemplo un archivo .exe, lo unico que hacemos en el codigo es separar toda la respuesta en dos, la primera parte es la que desechamos que son las cabeceras del servidor, y la segunda la guardamos la cual es el archivo en si:
Código (vb) [Seleccionar]

Ws.getdata Datos
...
If InStr(1, Datos, vbCrLf & vbCrLf) Then 'Si en los datos se encuentra el doble crlf
Guardar = Split(Respuesta, vbCrLf & vbCrLf, 2)(1) 'variable guardar es la segunda parte de la division de la respuesta teniendo encuenta el crlf
Else
    Guardar = Datos 'si no se encuentra el doble crlf, la variable guardar son los datos
End If
    Canal = FreeFile 'escogemos canal libre
    Open App.path & "\archivo" For Binary Access Write As Canal 'abrimos el archivo "archivo" en modo binari0, para los archivo como imagenes o ejecutables.
    Put #Canal, LOF(Canal) + 1, Guardar 'escribimos sobre el archivo
    Close #Canal 'cerramos canal


Sin programación sería algo así:
HTTP/1.1 200 OK
Bla Bla
Bla Bla
Content-Length: 1024

BMP....

El código separaría las cabeceras que manda el servidor con el cuerpo del mensaje, y luego guardaría el cuerpo del mensaje en el archivo indicado. En este caso se guardaría un bmp de 1 mega.

Manejo de Cookies:
Para el manejo de cookies, nos debemos fijar en la cabecera que nos manda el servidor.
Un ejemplo:

HTTP/1.1 302 Found
Date: Sat, 03 Nov 2007 02:03:25 GMT
Server: Apache
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Set-Cookie: pwl=4d186321c1a7f0f354b297e8914ab240; expires=Sat, 03 Nov 2007 03:03:26 GMT
Content-Length: 0


El valor de la cookie, nos la manda el servidor con la cabecera Set-Cookie, por lo cual si queremos manejar las cookies, debemos agregar un TextBox con el nombre Cookie.text, y el siguiente código meterlo en el Ws_DataArrival:
Código (vb) [Seleccionar]

Ws.getdata Datos
Parte1 = Split(Datos, vbCrLf) 'dividimos la cabecera del servidor según el crlf
    For i = 0 To UBound(Parte1) 'recorremos todas las cabeceras
        Parte2 = Split(Parte1(i), " ", 2) 'cada cabecera la dividimos según la cabecera y contenido
        If LCase$(Parte2(0)) = "set-cookie:" Then 'si la cabecera es set-cookie
            Parte3 = Split(Parte2(1), ";") 'dividimos el contenido por ";"
            For n = 0 To UBound(Parte3) 'recorremos su contenido
                If LCase$(Mid$(Trim$(Parte3(n)), 1, 8)) <> "expires=" And LCase$(Mid$(Trim$(Parte3(n)), 1, 6)) <> "path=/" Then 'ignoramos los "expires" y los "path"
                    If InStr(1, Cookie.Text, Parte3(n)) = 0 Then 'si el contenido de la cookie ya estaba en el textbox se ignora el nuevo contenido
                        If Cookie.Text = "" Then 'si cookie.text esta vacío
                            Cookie.Text = Parte3(n) 'metemos la cookie
                        Else
                            Cookie.Text = Cookie.Text & "; " & Parte3(n) 'si no esta vacío, ponemos el contenido de cookie mas un "; " y luego metemos la cookie nueva
                        End If
                    End If
                End If
            Next n
        End If
Next i


Bueno, es todo!! ya hare la versión dos para irc, o smtp no se xD
Adjunto el proyecto entero:
Esta todo comentado para que los que están comenzando con el visual basic lo entiendan.

Saludos :)

Hendrix

"Todos los días perdemos una docena de genios en el anonimato. Y se van. Y nadie sabe de ellos, de su historia, de su peripecia, de lo que han hecho, de sus angustias, de sus alegrías. Pero al menos una docena de genios se van todos los días sin que sepamos de ellos". - Juan Antonio Cebrián

Ferсhu

Muy buena info. Esta muy bueno el programa para manejar peticiones http  :D

ya no voy a usar mas el netcat jaja, ahora es mas facil.

Espero q le hagas le hagas mas modificaciones jaja ya sabes cuales.



LeandroA

Exelente Trabajo muy bueno el aporte

Saludos y segui asi


Karcrack