diff --git a/file_server.inc b/file_server.inc new file mode 100644 index 0000000..032a2c4 --- /dev/null +++ b/file_server.inc @@ -0,0 +1,278 @@ +; Это модуль для обработки стандартных запросов на получение файла +; по пути, который не относится к другому модулю + + +file_server: + + ; check http version (skip RTSP) + mov ecx, [esi + CONNECT_DATA.http_verion] + cmp dword[ecx], 'HTTP' + jne .err_http_501 + cmp word[ecx + 4], '/1' + jne .err_http_501 + +; cmp byte[ecx + 7], '1' +; je .http_1_1 +; ; http 1.0; +; +;.http_1_1: + + ; check name on ../../ and other bad items + mov ecx, [esi + CONNECT_DATA.uri_path] +@@: + cmp byte[ecx], 0 + je .good_name + inc ecx + cmp dword[ecx - 1], '/../' ; if path "/.." then system read dir and + jne @b ; and skip this path with 404 error + jmp .err_http_501 + +.good_name: + + ; check file of name (generate full name: work_dir + path) + ; + ; alloc 4 kib for path to file name + ; memory alloced on stack and for working program alloced with buffer + ; for code stack + lea edx, [esp - 6*1024 - 4096] ; edx <- buffer for full path + + mov edi, edx + sub edi, sizeof.FILED; edi <- struct FILED + mov [edi + FILED.path], edx + + mov eax, esi + push esi edi + ;; copy work_dir + mov esi, GLOBAL_DATA.work_dir + mov edi, edx + mov ecx, [GLOBAL_DATA.work_dir.size] + rep movsb + ;; copy path + mov esi, [eax + CONNECT_DATA.uri_path] +@@: + movsb + cmp byte[esi - 1], 0 + jne @b + + mov eax, edi + dec eax + pop edi esi + mov [edi + FILED.end_path], eax + + ;; check file info (skip of directory and if syscall return error) + lea eax, [edi - 40] ; buffer for file info + mov [edi + FILED.buffer], eax + + push edi + call FileInfo + test eax, eax + jnz .err_not_found + + test dword[edi - 40], 11000b ; check dir OR disk partition + jnz .err_not_found + + + ; check method OPTIONS + mov ecx, [esi + CONNECT_DATA.http_method] + cmp dword[ecx], 'OPTI' + jne @f + cmp dword[ecx + 4], 'ONS' + je .send_options +@@: + cmp dword[ecx], 'POST' + jne @f + cmp byte[ecx + 4], 0 + je .send_file +@@: + cmp dword[ecx], 'GET' + jne .err_http_501 + +.send_file: + ; create http response (set 200 code, MINE type and length of body) + ;; alloc 33 kib + push dword 33*1024 + call Alloc + test eax, eax + jz .err_http_501 ; error memory + + push esi + push edi + push eax ; save pointer for Free + + mov esi, base_response + mov ecx, response.body + mov edi, [esp] + rep movsb + + mov edi, [esp] + mov dword[edi + response.code], '200 ' + + push dword[esp + 4] ; <-FILED + call Get_MIME_Type + + add edi, response.content_type + mov esi, eax +@@: + movsb + cmp byte[esi], 0 + jne @b + + + pop edi ; buffer on 32 kib + pop ebx ; FD + pop esi ; CONNECT_DATA + + ; copy length + mov edx, [ebx - 40 + 36] + mov eax, [ebx - 40 + 32] + lea ecx, [edi + response.content_len + 21] + test eax, eax + jne @f + test edx, edx + jz .err_not_found_1 +@@: + cmp edx, 1000 ; 999*4 - max length of file + jae .err_not_found_1 + ; edx:eax ecx - end char in string '000000' +.div1000: + div dword[_DIV_1000_] + push eax + + mov eax, edx + xor edx, edx +;.div100: + div dword[_DIV_100_] + push eax + + mov eax, edx + xor edx, edx + div dword[_DIV_10_] + add byte[ecx], dl + add byte[ecx - 1], al + sub ecx, 3 + + pop eax + add byte[ecx + 1], al + ;xor edx, edx + ;test eax, eax + ;jne .div100 + pop eax + xor edx, edx + test eax, eax + jne .div1000 + + ;send_response_header + push dword 0 ; flags + push response.body + push edi + push dword[esi + CONNECT_DATA.socket] + call netfunc_send + cmp eax, -1 + je .exit_free + + mov dword[ebx + FILED.offset], 0 + mov dword[ebx + FILED.offset + 4], 0 + mov [ebx + FILED.buffer], edi + mov [ebx + FILED.size], 32*1024 +.send_response_body: + ; read 32 kib to file + push ebx + call FileRead + test eax, eax + jz @f + cmp eax, 6 + jne .exit_free +@@: + ; send this block data + push dword 0 ; flags + push 32*1024 + push edi + push dword[esi + CONNECT_DATA.socket] + call netfunc_send + cmp eax, -1 + je .exit_free + + ; check end file + mov ecx, [ebx + FILED.offset + 4] + cmp [ebx - 4], ecx + ja .add_offset + + mov edx, [ebx + FILED.offset] + cmp [ebx - 8], edx + jbe .exit_free + +.add_offset: + add [ebx + FILED.offset], 32*1024 + adc [ebx + FILED.offset + 4], 0 + + jmp @b + +.exit_free: + push edi + call Free + ret + +.send_options: + ; send standart options of file server + push dword 0 ; flags + push http_response_options.size + push http_response_options + push dword[esi + CONNECT_DATA.socket] + call netfunc_send + + ret + +.err_not_found_1: + push edi + call Free +.err_not_found: + ; send error 404 + push dword 0 ; flags + push http_response_err_404.size + push http_response_err_404 + push dword[esi + CONNECT_DATA.socket] + call netfunc_send + + ret + +.err_http_501: + ; send error 501 + push dword 0 ; flags + push http_response_err_501.size + push http_response_err_501 + push dword[esi + CONNECT_DATA.socket] + call netfunc_send + + ret + + +; char* stdcall Get_MIME_Type(FILED* fd); //path is ASCIIZ string +Get_MIME_Type: + push esi edi + mov edx, [GLOBAL_DATA.MIME_types_arr] + mov esi, [esp + 4*2 + 4] + sub edx, 8 +.next: + add edx, 8 + mov ecx, [esi + FILED.end_path] ;pointer to \0 full path + + cmp dword[edx], 0 + jz .found + + mov edi, [edx] + movzx eax, byte[edi] + sub ecx, eax +@@: + inc edi + mov al, [edi] + cmp byte[ecx], al + jne .next + + inc ecx + test al, al + jne @b + +.found: + mov eax, [edx + 4] + pop edi esi + ret 4 diff --git a/httpd.asm b/httpd.asm index 82f4686..33cd00c 100644 --- a/httpd.asm +++ b/httpd.asm @@ -7,15 +7,20 @@ ; Version 0.0.1, 12 November 2023 ; ; ; ;*****************************************************************************; -use32 -org 0 - +;include "macros.inc" +include 'D:\kos\programs\macros.inc' +;include 'D:\kos\programs\network.inc' + use32 + org 0 + db 'MENUET01' + dd 1, START, I_END, MEM, STACKTOP, PATH, 0 +;KOS_APP_START include 'sys_func.inc' include 'settings.inc' - -start: +;CODE +START: mcall 68, 11 ; init heap mcall 40, EVM_STACK ;set event bitmap @@ -30,7 +35,7 @@ start: jnz .err_settings ;init server socket - push dword SO_NONBLOCK + push dword SO_NONBLOCK ; IPPROTO_TCP push dword SOCK_STREAM push dword AF_INET4 call netfunc_socket; AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we dont want to block on accept @@ -64,9 +69,9 @@ start: .listen_err: .bind_err: push dword[srv_socket] - call close; [srv_socket] + call netfunc_close; [srv_socket] -..err_settings: +.err_settings: .sock_err: mcall -1 @@ -77,9 +82,12 @@ thread_connect: mcall 40, EVM_STACK ; set event bitmap - network event ; ожидание подключения Accept, sockaddr находится на вершине стека нового потока - lea edx, [esp + CONNECT_DATA.sockaddr] ; new sockaddr - push dword 16 ; 16 byte - sockaddr length - push edx + ;lea edx, [esp + CONNECT_DATA.sockaddr] ; new sockaddr + ;push dword 16 ; 16 byte - sockaddr length + ;push edx + push srv_sockaddr.length + push dword srv_sockaddr + push dword[srv_socket] call netfunc_accept @@ -119,8 +127,8 @@ thread_connect: test eax, eax jz @f add [esi + CONNECT_DATA.request_size], eax - cmp [esi + CONNECT_DATA.request_size], 0x8000 ; check end buffer - jb @b + ; cmp [esi + CONNECT_DATA.request_size], 0x8000 ; check end buffer + ; jb @b @@: ; после получения всего запроса(более или менее всего) выделяем озу для ; ассоциативного массива заголовков и аргументов запроса @@ -129,21 +137,26 @@ thread_connect: ; esp + 1024 .. esp + 2048 -> for URI args sub esp, 2048 - ; parse http message - call parse_http_query ; ecx - buffer edx - length data in buffer + ; parse http message + mov ecx, [esi + CONNECT_DATA.buffer_request] + call parse_http_query ; ecx - buffer test eax, eax jz .err_parse ; вызов нужной функции из списка моделей ; TODO + + ; if not found units, call file_server + call file_server ; esi - struct + ;TEST SERVER, DELETE ON RELISE + ; end work thread jmp .end_work .err_parse: - ; send error 501 - + call file_server.err_http_501 .end_work: add esp, 2048 ; free OUT buffer @@ -167,14 +180,21 @@ thread_connect: include 'parser.inc' +include 'file_server.inc' ; DATA AND FUNCTION include 'httpd_lib.inc' +default_ini_path: db 'httpd.ini',0 +I_END: +;DATA + ; DATA -srv_backlog: dd 0 ; максимум одновременных подключений подключений +;UDATA -srv_socket: dd 0 +srv_backlog: rd 1 ; максимум одновременных подключений подключений + +srv_socket: rd 1 srv_sockaddr: dw AF_INET4 @@ -184,9 +204,21 @@ srv_sockaddr: .length = $ - srv_sockaddr GLOBAL_DATA: - .units dd 0 ; указатель на ассоциативный массив пути и указателя на функцию либы(см ниж) - .unit_count dd 0 ; количество записей в массиве - .libs dd 0 ; указатель на массив указателей на ассоциативные массивы библиотек + .units rd 1 ; указатель на ассоциативный массив пути и указателя на функцию либы(см ниж) + .unit_count rd 1 ; количество записей в массиве + .libs rd 1 ; указатель на массив указателей на ассоциативные массивы библиотек + .work_dir rb 1024 ; max size path to work directory + .work_dir.size rd 1 ; length string + .unit_dir rb 1024 + .unit_dir.size rd 1 + + .MIME_types_arr rd 1 ;; .flags dd 0 ; 1 - all hosts(элемент hosts не указатель на массив, а на функцию) - +PATH: + rb 256 +; stack memory + rb 4096 +STACKTOP: +MEM: +;KOS_APP_END diff --git a/httpd.kex b/httpd.kex new file mode 100644 index 0000000..2b23b94 Binary files /dev/null and b/httpd.kex differ diff --git a/httpd_lib.inc b/httpd_lib.inc index 212e2b4..fcd2741 100644 --- a/httpd_lib.inc +++ b/httpd_lib.inc @@ -83,27 +83,108 @@ http_err_response: db 13, 10 .size = $ - http_err_response +http_response_err_501: + db 'HTTP/1.1 ' + db '501 ',13, 10 + db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Content-length: 91', 13, 10 + db 'Content-type: text/plain', 13, 10; + db 'Connection: close', 13, 10 + db 13, 10 + db 'Error parsing your request message. The version is not supported or another error.' +.size = $ - http_response_err_501 + +http_response_err_404: + db 'HTTP/1.1 ' + db '404 ',13, 10 + db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Content-length: 45', 13, 10 + db 'Content-type: text/plain', 13, 10; + db 'Connection: close', 13, 10 + db 13, 10 + db 'The server could not find the requested page.' +.size = $ - http_response_err_404 + +http_response_options: + db 'HTTP/1.1 ' + db '204 ',13, 10 + db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Allow: OPTIONS, GET, POST', 13, 10 + db 'Connection: close', 13, 10 + db 13, 10 +.size = $ - http_response_options + + + base_response: -label response at 0 +response: db 'HTTP/1.0 ' -.code: db '000 ',13, 10 - db 'Server: httpd(kolibri os)/0.0.1', 13, 10 +.code = $ - response + db '000 ',13, 10 + db 'Server: simple-httpd/0.0.1', 13, 10 ; httpd(kolibri os)/0.0.1, 13, 10 db 'Cache-Control: no-cache', 13, 10 db 'Content-Encoding: ' -.content_encod: db 'identity', 13, 10 - db 'Date: ' -.date: db 'Sun, 30 Oct 2022 09:29:13 GMT',13, 10 +.content_encod = $ - response + db 'identity', 13, 10 +; db 'Date: ' +;.date: db 'Sun, 30 Oct 2022 09:29:13 GMT',13, 10 db 'Content-length: ' -.content_len: db ' ', 13, 10 +;.content_len: db ' ', 13, 10 +.content_len = $ - response + db '0000000000000000000000', 13, 10 db 'Content-type: ' -.content_type: db ' ', 13, 10; +.content_type = $ - response + db ' ', 13, 10; ;'text/html; charset=utf-8' -.end_headers: ;нужно, когда базового заголовка не хватает -.connection db 'Connection: close', 13, 10 +;.end_headers: ;нужно, когда базового заголовка не хватает +.connection = $ - response + db 'Connection: close', 13, 10 db 13, 10 -.body: ; с этого оффсета уже писать данные +.body = $ - response ; с этого оффсета уже писать данные ; min HTTP request size ; "GET / HTTP/1.1" - 18 byte min_http_size = 18 + + +MIME_TYPES: +.html: db 'text/html',0 +.css: db 'text/css',0 +.js: db 'text/javascript',0 +.txt: db 'text/plain',0 +.json: db 'application/json',0 +.pdf: db 'application/pdf',0 + +.png: db 'image/png',0 +.mp3: db 'audio/mpeg',0 +.mp4: db 'video/mp4',0 +.other: db 'application/octet-stream',0 ; for unknow file - all file :) + +MIME_FILE_FORMAT: +.html: db 5,'.html',0 +.css: db 4,'.css',0 +.js: db 3,'.js',0 +.txt: db 4,'.txt',0 +.pdf: db 4,'.pdf',0 +.json: db 5,'.json',0 + +.png: db 4,'.png',0 +.mp3: db 4,'.mp3',0 +.mp4: db 4,'.mp4',0 + +STD_MIME_TYPE_ARR: + dd MIME_FILE_FORMAT.html, MIME_TYPES.html,\ + MIME_FILE_FORMAT.css, MIME_TYPES.css,\ + MIME_FILE_FORMAT.js, MIME_TYPES.js,\ + MIME_FILE_FORMAT.txt, MIME_TYPES.txt,\ + MIME_FILE_FORMAT.pdf, MIME_TYPES.pdf,\ + MIME_FILE_FORMAT.json, MIME_TYPES.json,\ + MIME_FILE_FORMAT.png, MIME_TYPES.png,\ + MIME_FILE_FORMAT.mp3, MIME_TYPES.mp3,\ + MIME_FILE_FORMAT.mp4, MIME_TYPES.mp4,\ + 0, MIME_TYPES.other + +_DIV_10_: dd 10 +_DIV_100_: dd 100 +_DIV_1000_: dd 1000 diff --git a/parser.inc b/parser.inc index 38a6b61..d513902 100644 --- a/parser.inc +++ b/parser.inc @@ -58,7 +58,7 @@ parse_url: je .get_query cmp byte[ecx], '#' - je get_fragment + je .get_fragment cmp byte[ecx], ' ' ; check end path jne @b @@ -106,7 +106,7 @@ parse_url: cmp byte[ecx], ' ' je .exit_2 - cmp byte[ecx] '&' + cmp byte[ecx], '&' jne @b jmp .get_query_new_arg @@ -122,7 +122,7 @@ parse_url: inc ecx cmp byte[ecx - 1], ' ' jne @b - mov [ecx - 1], 0 + mov byte[ecx - 1], 0 .exit: ret @@ -138,17 +138,31 @@ parse_url: parse_headers: ; init array mov [esi + CONNECT_DATA.num_headers], 0 - mov [esi + CONNECT_DATA.http_headers], BASE_ARRAY_HEADERS + lea eax, [BASE_ARRAY_HEADERS] + mov [esi + CONNECT_DATA.http_headers], eax xor edx, edx + + ; for check size + mov eax, [esi + CONNECT_DATA.request_size] + add eax, [esi + CONNECT_DATA.buffer_request] + .new_str: + cmp ecx, eax + jae .exit + cmp word[ecx], 0x0A0D ; \n jnz .find_header + .exit: ; end find heeaders + mov byte[ecx], 0 add ecx, 2 ; ecx = base for body message ret .find_header: ; add new item in array headers + cmp edx, 512 ; max count headers + jae .exit + inc edx mov dword[esi + CONNECT_DATA.num_headers], edx ; save pointer to name header @@ -157,6 +171,9 @@ parse_headers: @@: inc ecx + cmp ecx, eax + jae .exit + cmp byte[ecx], ':' jnz @b @@ -165,8 +182,11 @@ parse_headers: ; save pointer to value mov dword[BASE_ARRAY_HEADERS + (edx-1)*8 + 4], ecx @@: + cmp ecx, eax + jae .exit + inc ecx - cmp byte[ecx - 1], 0x0A0D + cmp word[ecx - 1], 0x0A0D jnz @b mov byte[ecx - 1], 0 inc ecx ; set offset on new string diff --git a/settings.inc b/settings.inc index 4b36290..ebf9cbd 100644 --- a/settings.inc +++ b/settings.inc @@ -30,18 +30,40 @@ ends ; OUT: eax - 0 or err_code load_settings: ; check file path - sub esp, 40 ; size file info struct - push esp - push ecx - call FileInfo - lea esp, [esp + 40] - test eax, eax - jnz .err + ;sub esp, 40 ; size file info struct + ;push esp + ;push ecx + ;call FileInfo + ;lea esp, [esp + 40] + ;test eax, eax + ;jnz .err + ; TEST SERVER + mov word[srv_sockaddr], AF_INET4 + mov word[srv_sockaddr.port], 0x5000 ; 80 port + mov dword[srv_sockaddr.ip], 0x1589A8C0 ; 192.168.137.21 or 0xc0a88915 ? + mov dword[srv_backlog], 10 + + push esi edi + mov edi, GLOBAL_DATA.work_dir + mov esi, test_workdir + mov ecx, test_workdir.size + rep movsb + pop edi esi + mov dword[GLOBAL_DATA.work_dir.size], test_workdir.size + + + mov dword[GLOBAL_DATA.MIME_types_arr], STD_MIME_TYPE_ARR + + xor eax, eax ret .err: ret +test_workdir: db '/sys' +.size = $ - test_workdir + + ; Config format: ; Standart INI file: ; - ";" or "#" comments diff --git a/sys_func.inc b/sys_func.inc index 7b04e19..b01e78d 100644 --- a/sys_func.inc +++ b/sys_func.inc @@ -1,5 +1,112 @@ ; ABSTRACT SYSTEM FUNCTIONS +;======== inclede network.inc ======== +; Socket types +SOCK_STREAM = 1 +SOCK_DGRAM = 2 +SOCK_RAW = 3 + +; IP protocols +IPPROTO_IP = 0 +IPPROTO_ICMP = 1 +IPPROTO_TCP = 6 +IPPROTO_UDP = 17 +IPPROTO_RAW = 255 + +; IP options +IP_TTL = 2 + +; Address families +AF_UNSPEC = 0 +AF_LOCAL = 1 +AF_INET4 = 2 ; IPv4 +AF_INET6 = 10 ; IPv6 + +PF_UNSPEC = AF_UNSPEC +PF_LOCAL = AF_LOCAL +PF_INET4 = AF_INET4 +PF_INET6 = AF_INET6 + +; Flags for addrinfo +AI_PASSIVE = 1 +AI_CANONNAME = 2 +AI_NUMERICHOST = 4 +AI_NUMERICSERV = 8 +AI_ADDRCONFIG = 0x400 + +; internal definition +AI_SUPPORTED = 0x40F + +; for system function 76 +API_ETH = 0 shl 16 +API_IPv4 = 1 shl 16 +API_ICMP = 2 shl 16 +API_UDP = 3 shl 16 +API_TCP = 4 shl 16 +API_ARP = 5 shl 16 +API_PPPOE = 6 shl 16 + +; Socket flags for user calls +MSG_PEEK = 0x02 +MSG_DONTWAIT = 0x40 + +; Socket levels +SOL_SOCKET = 0xffff + +; Socket options +SO_BINDTODEVICE = 1 shl 9 +SO_NONBLOCK = 1 shl 31 + +struct sockaddr_in + sin_family dw ? ; sa_family_t + sin_port dw ? ; in_port_t + sin_addr dd ? ; struct in_addr + sin_zero rb 8 ; zero +ends + +struct addrinfo + ai_flags dd ? ; bitmask of AI_* + ai_family dd ? ; PF_* + ai_socktype dd ? ; SOCK_* + ai_protocol dd ? ; 0 or IPPROTO_* + ai_addrlen dd ? ; length of ai_addr + ai_canonname dd ? ; char* + ai_addr dd ? ; struct sockaddr* + ai_next dd ? ; struct addrinfo* +ends + +EAI_ADDRFAMILY = 1 +EAI_AGAIN = 2 +EAI_BADFLAGS = 3 +EAI_FAIL = 4 +EAI_FAMILY = 5 +EAI_MEMORY = 6 +EAI_NONAME = 8 +EAI_SERVICE = 9 +EAI_SOCKTYPE = 10 +EAI_BADHINTS = 12 +EAI_PROTOCOL = 13 +EAI_OVERFLOW = 14 + +; Socket error codes +; Error Codes +ENOBUFS = 1 +EINPROGRESS = 2 +EOPNOTSUPP = 4 +EWOULDBLOCK = 6 +ENOTCONN = 9 +EALREADY = 10 +EINVAL = 11 +EMSGSIZE = 12 +ENOMEM = 18 +EADDRINUSE = 20 +ECONNREFUSED = 61 +ECONNRESET = 52 +EISCONN = 56 +ETIMEDOUT = 60 +ECONNABORTED = 53 +;======== End include======== + ; stdcall socket(uint32_t domain, type, proto) netfunc_socket: push ebx esi @@ -11,7 +118,7 @@ netfunc_socket: ;stdcall close(uint32_t sock_number); netfunc_close: push ebx - call 75, 1, [esp + 4 + 4] + mcall 75, 1, [esp + 4 + 4] pop ebx ret 4 @@ -80,11 +187,31 @@ Free: pop ebx ret 4 -;NTSTATUS stdcall FileInfo(const char* path, void* buff) + +struct FILED + opcode rd 1 + offset rd 2 + size rd 1 + buffer rd 1 + rb 1 + path rd 1 + end_path rd 1 +ends + +;NTSTATUS stdcall FileInfo(FILED* file) FileInfo: - - ret 8 -;NTSATTUS stdcall FileRead(const char* path, void* buff, uint32_t size) + push ebx + mov ebx, [esp + 4*1 + 4] + mov dword[ebx], 5 ; file info + mov dword[ebx + FILED.offset + 4], 0 ; zero flag + mcall 70 + pop ebx + ret 4 +;NTSATTUS stdcall FileRead(FILED* file) FileRead: - - ret 12 \ No newline at end of file + push ebx + mov ebx, [esp + 4*1 + 4] + mov dword[ebx], 0 ; read file + mcall 70 + pop ebx + ret 4 \ No newline at end of file