[Codigo] Pequeño Servidor HTTP

Iniciado por _Enko, 3 Diciembre 2011, 22:29 PM

0 Miembros y 2 Visitantes están viendo este tema.

_Enko

Código (asm) [Seleccionar]

;modificar szRoot para indicar la ubicacion del servidor, default C:\Server
;msvcrt no viene en el paquete de fasm, la crean con la utilidad dll2inc
;o hacen los include a mano^^
format PE Console
entry start

include '%fasminc%/win32a.inc'
include '%fasminc%/macro/if.inc'

MAX_QUEUE   equ 100     ;max namber of pending connections
BUFFER_SIZE equ 2048    ;max size of the header sent by the client
MAX_URL_SIZE equ 1024   ;max size of url in the header send
SHOW_DETAILED_MSG equ TRUE ;set TRUE if want to see the full client message

section '.data' data readable writeable
    wsa     WSADATA
    port    dd 80
     
    szPause db "PAUSE",0
    h404    db "HTTP/1.1 404 Not Found",13,10,"Server: EnkoHttpServer 1.0.0.0 ",13,10,13,10,"<HTML><BODY>404 Not Found</BODY></HTML>",13,10
    .size = $ - h404
    h200    db "HTTP/1.1 200 OK",13,10,"Server: EnkoHttpServer 1.0.0.0 ",13,10,"Allow: GET",13,10,13,10
    .size = $ - h200
    szRoot      db "C:\Server",0
    szSlashes   db '/\',0
    szSlash     db '\',0
    szTockens   db " ",13,10,0
    szError     db "ERROR: %i",13,10,0
    szDot       db ".",0
    szFileNotExists db "File Not Exists",13,10,0
    szInt       db "%i",0
    szFile      db "FILE: %s",13,10,0
    szClient    db "Client IP: %s",13,10,0
    szRequest   db "REQUEST: %s",13,10,0
    szStatus    db "STATUS: %s",13,10,0
    szString    db "%s",0
    szEndLine   db 13,10,0
    .size = $ - szEndLine
    szBr        db "<br>",0
    szIndex     db "index.html",0
    szFileSent    db "File Sent ok",13,10,0
    .size = $ - szBr
    wSocketVersion dd 0x0101
    transmiteBuffer dd h200,h200.size,NULL,NULL
    thread  dd ?     
    peer    dd ?
    peerAddr sockaddr_in
    sizePeerAddr dd sizeof.sockaddr_in
    sock     dd ?
    sock_addr   sockaddr_in
    szBuffer db 32 dup ?
    szIp     db 16 dup ?
     
     
section '.code' code readable executable
start:
    ;inicializacion socket
    invoke  WSAStartup, [wSocketVersion], wsa     
    invoke  socket,AF_INET,SOCK_STREAM,NULL
    mov     [sock],eax     
    mov     [sock_addr.sin_family], AF_INET
    invoke  htons,[port]
    mov     [sock_addr.sin_port],ax     
    mov     [sock_addr.sin_addr],NULL
    invoke  bind, [sock], sock_addr,sizeof.sockaddr_in
    .if eax <> 0
        invoke  WSAGetLastError
        cinvoke printf, szError, eax         
    .endif     
    invoke listen, [sock],MAX_QUEUE
    ;activamos la escucha del socket
accepted:
    invoke accept, [sock],peerAddr,sizePeerAddr
    mov     [peer],eax 
    stdcall ipToString,[peerAddr.sin_addr],szIp
    cinvoke printf, szClient,szIp
    ;nuevo hilo para cada conexion
    invoke CreateThread, NULL,NULL, resolveConnection,[peer],NULL,NULL
    ;o mejor la funcion de abajo, ayudaria a la estabilidad y contra ataques DOS
    ;invoke QueueUserWorkItem, resolveConnection, [peer], NULL ;WT_EXECUTEDEFAUL
    jmp     accepted
    invoke  ExitProcess,0

;hilo que resuelve las peticiones
proc resolveConnection, lpParam
local lpeer: DWORD, lbuffer: DWORD, lurl: DWORD, lfile: DWORD 
    mov     eax, [lpParam] Dermatoesqueleto    mov     [lpeer],eax
    cinvoke malloc, BUFFER_SIZE
    mov     [lbuffer],eax
    invoke  recv, [lpeer],[lbuffer],BUFFER_SIZE,0 
    ;vemos si hay un mensaje en el buffer y continuamos
    .if eax <> 0   
        mov ebx,[lbuffer]
        mov byte [ebx+eax],0
        .if SHOW_DETAILED_MSG
            cinvoke printf,szRequest, [lbuffer]   
        .endif
        cinvoke strtok, [lbuffer], szTockens
        .if eax <> 0 
            ;solamente respondemos al mensaje GET
            .if dword[eax] = "GET"           
                ;url sring allocation
                ;buscamos la url en la peticion GET
                ;solamente las peticiones que terminan con alguna extensión
                cinvoke malloc, MAX_URL_SIZE
                mov    [lurl],eax
                cinvoke strcpy, [lurl],szRoot
                cinvoke strtok, NULL, szTockens
                cinvoke strcat,[lurl],eax   
                mov     ebx,eax     
                cinvoke strlen, eax
                mov edx,eax
                dec edx
                .repeat 
                    dec eax
                    cmp byte[ebx+eax],"/"
                    .if ZERO?
                        mov byte[ebx+eax],"\"
                    .endif                     
                .until eax = 0
                .if byte[ebx+edx] = "\"
                    cinvoke strcat,[lurl],szIndex
                .endif
                cinvoke printf, szFile, [lurl]
                ;ya tenemos el nombre del archivo, ahora lo abrimos desde el disco
                invoke  CreateFile,[lurl],GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,0
                ;si el archivo existe, lo enviamos
                .if eax <> INVALID_HANDLE_VALUE 
                    mov [lfile],eax             
                    invoke  GetFileSize, [lfile],NULL
                    invoke  TransmitFile, [lpeer],[lfile],eax,NULL,NULL,transmiteBuffer,NULL 
                    .if eax
                        ;escribimos en la consola el detalle
                        cinvoke printf, szStatus, szFileSent
                    .endif
                    invoke  CloseHandle, [lfile]
                .else
                    ;no existe el archivo, error 404
                    cinvoke printf, szStatus, szFileNotExists                     
                    invoke  send, [lpeer], h404, h404.size, 0               
                .endif
                cinvoke free, [lurl]
            .endif
        .endif
    .endif
    .exit:
    cinvoke free, [lbuffer]
    invoke  CloseHandle, [lpeer]
    invoke  ExitThread
    ret
endp

;convertimos DWORD ip  a una cadena
proc ipToString, ip, string
local buffer: DWORD
    mov ebx, [ip]
    mov esi,4
    mov eax,[string]
    mov byte [eax],0
    .repeat
        xor     eax,eax
        mov     al,bl
        cinvoke sprintf,[buffer],szInt,eax
        cinvoke strcat,[string],[buffer]
        cinvoke strcat,[string],szDot
        shr ebx,8
        dec esi
    .until esi=0
    cinvoke strlen, [string]
    mov ebx, [string]
    mov byte[ebx+eax-1],0
    mov byte[buffer],0
    ret
endp
section '.idata' import data readable writeable

library kernel32,'KERNEL32.DLL',\
        user32,'USER32.DLL',\
        msvcrt,'msvcrt.dll',\
        wsock32, 'WSOCK32.DLL'

include '%fasminc%\api\kernel32.inc'
include '%fasminc%\api\user32.inc'
include '%fasminc%\api\msvcrt.inc'
include '%fasminc%\api\wsock32.inc'


El codigo funciona bastante bien. Si la url es complicada es probable que no la pueda resolver. En canto a saturacion o DDOS, sumamente susceptible.

Screenshot
http://i41.tinypic.com/5lxyfk.png

hay una duda que tengo:
Si se usa  CreateThreade es necesario usar ExitThread... hasta ahi bien.

Ahora, si se  usa QueueUserWorkItem, ¿Hace falta?
en la msdn no especifica, hice la prueba y aparentemente no. Ya que si se usa, en una prueba rapida se nota que deja de funcionar la segunda vez que se llama.

Saludos.

Eternal Idol

Si llama a ExitThread terminas el hilo trabajador ese, mala idea. Lo ideal es que nunca llames a esa funcion, con retornar es suficiente (fijate que codigo se ejecuta justo despues cuando retornas de un hilo creado con CreateThread).
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

_Enko

Hola, la idea es que no es un hilo solo.

Es decir, para cada conexion nueva, se genera un hilo nuevo. Y cuando se desconecta el usuario, creo que tendria que terminar el hilo.

la tecnica es super saturable. 100 peticiones equivaldrian a 100 hilos.

Por eso creo que cada vez que el hilo hace su trabajo, deberia terminarlo.
(Hilo creado con CreateThread)

Citar
Si llama a ExitThread terminas el hilo trabajador ese, mala idea.
con usar ret, el hilo es eliminado?

Eternal Idol

Con hilo trabajador me refiero al que procesa el work item que encolas con QueueUserWorkItem (Queues a work item to a worker thread in the thread pool), el punto es que el hilo trabajador no lo creaste vos y tampoco lo tenes que terminar (un hilo trabajador de estos es, simplificando, un bucle esperando a que haya un work item en la cola para llamar al  LPTHREAD_START_ROUTINE del mismo y continuar el bucle en cuanto este retorne). Con usar ret le devolves el control al hilo trabajador y su bucle, en el caso de un hilo creado con CreateThread la funcion a la que retornas llama a ExitThread inmediatamente. Es asi:

Código (asm) [Seleccionar]
kernel32!BaseThreadInitThunk:
755c3388 8bff            mov     edi,edi
755c338a 55              push    ebp
755c338b 8bec            mov     ebp,esp
755c338d 85c9            test    ecx,ecx
755c338f 0f85752e0000    jne     kernel32!BaseThreadInitThunk+0x15 (755c620a)
755c3395 ff7508          push    dword ptr [ebp+8]
755c3398 ffd2            call    edx ;el LPTHREAD_START_ROUTINE
755c339a 50              push    eax
755c339b ff1504075c75    call    dword ptr [kernel32!_imp__RtlExitUserThread (755c0704)] ds:002b:755c0704={ntdll!RtlExitUserThread (77e2d598)}
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

_Enko

A... no habia entendido "hilo trabajador"....

QueueUserWorkItem, solo se usa RET. Confirmado :)


Gracias Nuevamente.

Eternal Idol

La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón