;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.
			
			
			
				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).
			
			
			
				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?
			
				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:
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)}
			
			
			
				A... no habia entendido "hilo trabajador"....
QueueUserWorkItem, solo se usa RET. Confirmado :)
Gracias Nuevamente.
			
			
			
				De nadas  :)