445f854cae
git-svn-id: svn://kolibrios.org@1542 a494cfbc-eb01-0410-851d-a64ba20cac60
1307 lines
41 KiB
NASM
1307 lines
41 KiB
NASM
format MS COFF
|
|
|
|
public @EXPORT as 'EXPORTS'
|
|
|
|
include '../struct.inc'
|
|
include '../proc32.inc'
|
|
include '../macros.inc'
|
|
purge section,mov,add,sub
|
|
|
|
include '../network.inc'
|
|
|
|
section '.flat' code readable align 16
|
|
|
|
;;===========================================================================;;
|
|
lib_init: ;//////////////////////////////////////////////////////////////////;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Library entry point (called after library load) ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> eax = pointer to memory allocation routine ;;
|
|
;> ebx = pointer to memory freeing routine ;;
|
|
;> ecx = pointer to memory reallocation routine ;;
|
|
;> edx = pointer to library loading routine ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = 1 (fail) / 0 (ok) (library initialization result) ;;
|
|
;;===========================================================================;;
|
|
mov [mem.alloc], eax
|
|
mov [mem.free], ebx
|
|
mov [mem.realloc], ecx
|
|
mov [dll.load], edx
|
|
mov [DNSrequestID], 1
|
|
stdcall edx, @IMPORT
|
|
xor eax, eax
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; in_addr_t __stdcall inet_addr(__in const char* hostname); ;;
|
|
inet_addr: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Convert the string from standard IPv4 dotted notation to integer IP addr. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter = host name ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = IP address on success / -1 on error ;;
|
|
;;===========================================================================;;
|
|
; 0. Save used registers for __stdcall.
|
|
push ebx esi edi
|
|
mov esi, [esp+16] ; esi = hostname
|
|
; 1. Check that only allowed symbols are present.
|
|
; (hex digits, possibly letters 'x'/'X' and up to 3 dots)
|
|
push esi
|
|
xor ecx, ecx
|
|
.calcdots_loop:
|
|
; loop for all characters in string
|
|
lodsb
|
|
; check for end of string
|
|
cmp al, 0
|
|
jz .calcdots_loop_done
|
|
; check for dot
|
|
cmp al, '.'
|
|
jz .dot
|
|
; check for digit
|
|
sub al, '0'
|
|
cmp al, 9
|
|
jbe .calcdots_loop
|
|
; check for hex letter
|
|
sub al, 'A' - '0' ; 'A'-'F' -> 0-5, 'a'-'f' -> 20h-25h
|
|
and al, not 20h
|
|
cmp al, 'F' - 'A'
|
|
jbe .calcdots_loop
|
|
; check for 'x'/'X'
|
|
cmp al, 'X' - 'A'
|
|
jz .calcdots_loop
|
|
jmp .fail.pop
|
|
.dot:
|
|
inc ecx
|
|
jmp .calcdots_loop
|
|
.calcdots_loop_done:
|
|
cmp ecx, 4
|
|
jae .fail.pop
|
|
; 2. The name can be valid dotted name; try to convert, checking limit
|
|
pop esi
|
|
xor edi, edi ; edi = address
|
|
push 0xFFFFFFFF
|
|
pop edx ; edx = mask for rest of address
|
|
; 2a. Convert name except for last group.
|
|
jecxz .ip_convert_2b
|
|
.ip_convert_2a:
|
|
push ecx
|
|
mov ecx, 0xFF ; limit for all groups except for last
|
|
call .get_number
|
|
pop ecx
|
|
jc .fail
|
|
cmp byte [esi-1], '.'
|
|
jnz .fail
|
|
shl edi, 8
|
|
shr edx, 8
|
|
add edi, eax
|
|
loop .ip_convert_2a
|
|
; 2b. Convert last group.
|
|
.ip_convert_2b:
|
|
mov ecx, edx
|
|
call .get_number
|
|
jc .fail
|
|
cmp byte [esi-1], 0
|
|
jnz .fail
|
|
@@:
|
|
shl edi, 8
|
|
shr edx, 8
|
|
jnz @b
|
|
add edi, eax
|
|
; 2c. Convert to network byte order.
|
|
bswap edi
|
|
; 3. Set return value, restore used registers and return.
|
|
xchg eax, edi
|
|
.ret:
|
|
pop edi esi ebx
|
|
ret 4
|
|
; 4. On error, return -1.
|
|
.fail.pop:
|
|
pop esi
|
|
.fail:
|
|
push -1
|
|
pop eax
|
|
jmp .ret
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for IP parsing. ;;
|
|
.get_number: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Converts string to number. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> esi -> string ;;
|
|
;> ecx = limit for number ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = number ;;
|
|
;< CF set on error (too big number) / cleared on success ;;
|
|
;< esi -> end of number representation ;;
|
|
;;===========================================================================;;
|
|
; 0. Save edx, which is used in caller.
|
|
push edx
|
|
; 1. Initialize number, zero eax so that lodsb gets full dword.
|
|
xor eax, eax
|
|
xor edx, edx
|
|
; 2. Get used numeral system: 0x = hex, otherwise 0 = octal, otherwise decimal
|
|
push 10
|
|
pop ebx
|
|
lodsb
|
|
cmp al, '0'
|
|
jnz .convert
|
|
push 8
|
|
pop ebx
|
|
lodsb
|
|
cmp al, 'x'
|
|
jnz .convert
|
|
add ebx, ebx
|
|
; 3. Loop while digits are encountered.
|
|
.convert:
|
|
; 4. Convert digit from text representation to binary value.
|
|
or al, 20h ; '0'-'9' -> '0'-'9', 'A'-'F' -> 'a'-'f'
|
|
sub al, '0'
|
|
cmp al, 9
|
|
jbe .digit
|
|
sub al, 'a' - '0'
|
|
cmp al, 'f' - 'a'
|
|
ja .convert_done
|
|
add al, 10
|
|
.digit:
|
|
; 5. Digit must be less than base of numeral system.
|
|
cmp eax, ebx
|
|
jae .convert_done
|
|
; 6. Advance the number.
|
|
imul edx, ebx
|
|
add edx, eax
|
|
cmp edx, ecx
|
|
ja .gn_error
|
|
; 3b. Continue loop.
|
|
lodsb
|
|
jmp .convert
|
|
.convert_done:
|
|
; 7. Invalid character, number converted, return success.
|
|
xchg eax, edx
|
|
pop edx
|
|
clc
|
|
ret
|
|
.gn_error:
|
|
; 8. Too big number, return error.
|
|
pop edx
|
|
stc
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; char* __stdcall inet_ntoa(struct in_addr in); ;;
|
|
inet_ntoa: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Convert the Internet host address to standard IPv4 dotted notation. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter = host address ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = pointer to resulting string (in static buffer) ;;
|
|
;;===========================================================================;;
|
|
; 0. Save used registers for __stdcall.
|
|
push ebx esi edi
|
|
mov bl, 0xCD ; constant for div 10
|
|
; 1. Write octet 4 times.
|
|
mov edi, .buffer
|
|
mov edx, [esp+16] ; eax = in
|
|
mov al, dl
|
|
call .write
|
|
mov al, dh
|
|
shr edx, 16
|
|
call .write
|
|
mov al, dl
|
|
call .write
|
|
mov al, dh
|
|
call .write
|
|
; 2. Replace final dot with terminating zero.
|
|
mov byte [edi-1], 0
|
|
; 3. Restore used registers, set result value and return.
|
|
pop edi esi ebx
|
|
mov eax, .buffer
|
|
ret 4
|
|
|
|
.write:
|
|
movzx esi, al
|
|
mul bl
|
|
add esi, ('.' shl 8) + '0'
|
|
shr ah, 3 ; ah = al / 10
|
|
movzx ecx, ah
|
|
add ecx, ecx
|
|
lea ecx, [ecx*5]
|
|
sub esi, ecx ; lobyte(esi) = al % 10, hibyte(esi) = '.'
|
|
test ah, ah
|
|
jz .1digit
|
|
cmp ah, 10
|
|
jb .2digit
|
|
cmp ah, 20
|
|
sbb cl, cl
|
|
add cl, '2'
|
|
mov byte [edi], cl
|
|
movzx ecx, cl
|
|
lea ecx, [ecx*5]
|
|
sub ah, cl
|
|
sub ah, cl
|
|
add ah, ('0'*11) and 255
|
|
mov byte [edi+1], ah
|
|
mov word [edi+2], si
|
|
add edi, 4
|
|
ret
|
|
.2digit:
|
|
add ah, '0'
|
|
mov byte [edi], ah
|
|
mov word [edi+1], si
|
|
add edi, 3
|
|
ret
|
|
.1digit:
|
|
mov word [edi], si
|
|
add edi, 2
|
|
ret
|
|
|
|
struct __gai_reqdata
|
|
socketnum dd ?
|
|
; external code should not look on rest of this structure,
|
|
; it is internal for getaddrinfo_start/process/abort
|
|
reqid dw ? ; DNS request ID
|
|
socktype db ? ; SOCK_* or 0 for any
|
|
db ?
|
|
service dd ?
|
|
flags dd ?
|
|
reserved rb 16
|
|
ends
|
|
|
|
;;===========================================================================;;
|
|
;; int __stdcall getaddrinfo(__in const char* hostname, ;;
|
|
;; __in const char* servname, ;;
|
|
;; __in const struct addrinfo* hints, ;;
|
|
;; __out struct addrinfo **res); ;;
|
|
getaddrinfo: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Get a list of IP addresses and port numbers for given host and service ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter (optional) = host name ;;
|
|
;> second parameter (optional) = service name (decimal number for now) ;;
|
|
;> third parameter (optional) = hints for socketnum type ;;
|
|
;> fourth parameter = pointer to result (head of L1-list) ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = 0 on success / one of EAI_ codes on error ;;
|
|
;;===========================================================================;;
|
|
; 0. Save used registers for __stdcall.
|
|
push ebx esi edi
|
|
mov edi, [esp+28] ; edi = res
|
|
; 1. Create and send DNS packet.
|
|
sub esp, sizeof.__gai_reqdata ; reserve stack place (1)
|
|
push esp ; fifth parameter = pointer to (1)
|
|
push edi ; fourth parameter = res
|
|
push dword [esp+32+sizeof.__gai_reqdata] ; third parameter = hints
|
|
push dword [esp+32+sizeof.__gai_reqdata] ; second parameter = servname
|
|
push dword [esp+32+sizeof.__gai_reqdata] ; first parameter = hostname
|
|
call getaddrinfo_start
|
|
test eax, eax
|
|
jns .ret ; if name resolved without network activity, return
|
|
; 2. Wait for DNS reply.
|
|
; 2a. Ignore all events except network stack.
|
|
mcall 40, EVM_STACK
|
|
push eax ; save previous event mask (2)
|
|
; 2b. Get upper limit for wait time. Use timeout = 5 seconds.
|
|
mcall 26, 9 ; get time stamp
|
|
xchg esi, eax ; save time stamp to esi
|
|
mov ebx, 500 ; start value for timeout
|
|
add esi, ebx
|
|
.wait:
|
|
; 2c. Wait for event with timeout.
|
|
mcall 23 ; wait for event - must be stack event
|
|
; 2d. Check for timeout.
|
|
test eax, eax
|
|
lea eax, [esp+4] ; pointer to (1)
|
|
jz .timeout
|
|
; 3. Got packet. Call processing function.
|
|
push edi ; second parameter: pointer to result
|
|
push eax ; first parameter: pointer to reqdata
|
|
call getaddrinfo_process
|
|
; 4. Test whether wait loop must be continued.
|
|
test eax, eax
|
|
jns .ret.restore
|
|
; 2e. Recalculate timeout value.
|
|
mcall 26, 9
|
|
mov ebx, esi
|
|
sub ebx, eax
|
|
; 2f. Check that time is not over; if not, continue wait loop
|
|
cmp ebx, 500
|
|
jbe .wait
|
|
.timeout:
|
|
; 5. Timeout: abort and return error
|
|
push eax
|
|
call getaddrinfo_abort
|
|
and dword [edi], 0
|
|
push EAI_AGAIN
|
|
pop eax
|
|
.ret.restore:
|
|
; 6. Restore event mask.
|
|
pop ebx ; get event mask (2)
|
|
push eax ; save return code (3)
|
|
mcall 40
|
|
pop eax ; restore return code (3)
|
|
.ret:
|
|
; 7. Restore stack pointer, used registers and return.
|
|
add esp, sizeof.__gai_reqdata ; undo (1)
|
|
pop edi esi ebx
|
|
ret 16
|
|
|
|
;;===========================================================================;;
|
|
;; int __stdcall getaddrinfo_start(__in const char* hostname, ;;
|
|
;; __in const char* servname, ;;
|
|
;; __in const struct addrinfo* hints, ;;
|
|
;; __out struct addrinfo **res, ;;
|
|
;; __out struct __gai_reqdata* reqdata); ;;
|
|
getaddrinfo_start: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Initiator for getaddrinfo, sends DNS request ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first 4 parameters same as for getaddrinfo ;;
|
|
;> last parameter = pointer to buffer for __gai_reqdata, must be passed to ;;
|
|
;> getaddrinfo_process as is ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = <0 if wait loop must be entered / 0 on success / EAI_* on error ;;
|
|
;;===========================================================================;;
|
|
;; Known limitations: ;;
|
|
;; 1. No support for TCP connections => ;;
|
|
;; 1a. Long replies will be truncated, and not all IP addresses will be got. ;;
|
|
;; 2. No support for iterative resolving => ;;
|
|
;; 2a. In theory may fail with some servers. ;;
|
|
;; 3. Assumes that domain for relative names is always root, ".". ;;
|
|
;; 4. Does not support lookup of services by name, ;;
|
|
;; only decimal representation is supported. ;;
|
|
;; 5. Assumes that IPv4 is always configured, so AI_ADDRCONFIG has no effect.;;
|
|
;;===========================================================================;;
|
|
; 0. Create stack frame and save used registers for __stdcall.
|
|
push ebx esi edi
|
|
push ebp
|
|
mov ebp, esp
|
|
virtual at ebp-8
|
|
.recent_restsize dd ? ; this is for memory alloc in ._.generate_data
|
|
.recent_page dd ? ; this is for memory alloc in ._.generate_data
|
|
rd 5 ; saved regs and return address
|
|
.hostname dd ?
|
|
.servname dd ?
|
|
.hints dd ?
|
|
.res dd ?
|
|
.reqdata dd ?
|
|
end virtual
|
|
xor edi, edi
|
|
push edi ; init .recent_page
|
|
push edi ; init .recent_restsize
|
|
; 1. Check that parameters are correct and can be handled by this implementation.
|
|
; 1a. If 'res' pointer is given, set result to zero.
|
|
mov eax, [.res]
|
|
test eax, eax
|
|
jz @f
|
|
mov [eax], edi
|
|
@@:
|
|
; 1b. Only AI_SUPPORTED flags are supported for hints->ai_flags.
|
|
mov ecx, [.hints]
|
|
xor edx, edx
|
|
jecxz .nohints
|
|
mov edx, [ecx+addrinfo.ai_flags]
|
|
.nohints:
|
|
mov ebx, [.reqdata]
|
|
mov [ebx+__gai_reqdata.flags], edx
|
|
push EAI_BADFLAGS
|
|
pop eax
|
|
test edx, not AI_SUPPORTED
|
|
jnz .ret
|
|
; 1c. Either hostname or servname must be given. If AI_CANONNAME is set,
|
|
; hostname must also be set.
|
|
cmp [.hostname], edi
|
|
jnz @f
|
|
test dl, AI_CANONNAME
|
|
jnz .ret
|
|
push EAI_NONAME
|
|
pop eax
|
|
cmp [.servname], edi
|
|
jz .ret
|
|
@@:
|
|
; 1d. Only IPv4 is supported, so hints->ai_family must be either PF_UNSPEC or PF_INET.
|
|
push EAI_FAMILY
|
|
pop eax
|
|
jecxz @f
|
|
cmp [ecx+addrinfo.ai_family], edi
|
|
jz @f
|
|
cmp [ecx+addrinfo.ai_family], AF_INET4
|
|
jnz .ret
|
|
@@:
|
|
; 1e. Valid combinations for ai_socktype/ai_protocol: 0/0 for any or
|
|
; SOCK_STREAM/IPPROTO_TCP, SOCK_DGRAM/IPPROTO_UDP
|
|
; (raw socketnums are not yet supported by the kernel)
|
|
xor edx, edx ; assume 0=any if no hints
|
|
jecxz .socketnum_type_ok
|
|
mov edx, [ecx+addrinfo.ai_socktype]
|
|
mov esi, [ecx+addrinfo.ai_protocol]
|
|
; 1f. Test for ai_socktype=0 and ai_protocol=0.
|
|
test edx, edx
|
|
jnz .check_socktype
|
|
test esi, esi
|
|
jz .socketnum_type_ok
|
|
; 1g. ai_socktype=0, ai_protocol is nonzero.
|
|
push EAI_SERVICE
|
|
pop eax
|
|
inc edx ; edx = SOCK_STREAM
|
|
cmp esi, IPPROTO_TCP
|
|
jz .socketnum_type_ok
|
|
inc edx ; edx = SOCK_DGRAM
|
|
cmp esi, IPPROTO_UDP
|
|
jz .socketnum_type_ok
|
|
.ret:
|
|
; Restore saved registers, destroy stack frame and return.
|
|
mov esp, ebp
|
|
pop ebp
|
|
pop edi esi ebx
|
|
ret 20
|
|
; 1h. ai_socktype is nonzero.
|
|
.check_socktype:
|
|
push EAI_SOCKTYPE
|
|
pop eax
|
|
cmp edx, SOCK_STREAM
|
|
jz .check_tcp
|
|
cmp edx, SOCK_DGRAM
|
|
jnz .ret
|
|
test esi, esi
|
|
jz .socketnum_type_ok
|
|
cmp esi, IPPROTO_UDP
|
|
jz .socketnum_type_ok
|
|
jmp .ret
|
|
.check_tcp:
|
|
test esi, esi
|
|
jz .socketnum_type_ok
|
|
cmp esi, IPPROTO_TCP
|
|
jnz .ret
|
|
.socketnum_type_ok:
|
|
mov [ebx+__gai_reqdata.socktype], dl
|
|
; 2. Resolve service.
|
|
; 2a. If no name is given, remember value -1.
|
|
push -1
|
|
pop edx
|
|
mov esi, [.servname]
|
|
test esi, esi
|
|
jz .service_resolved
|
|
; 2b. Loop for characters of string while digits are encountered.
|
|
xor edx, edx
|
|
xor eax, eax
|
|
.serv_to_number:
|
|
lodsb
|
|
sub al, '0'
|
|
cmp al, 9
|
|
ja .serv_to_number_done
|
|
; for each digit, set edx = edx*10 + <digit>
|
|
lea edx, [edx*5]
|
|
lea edx, [edx*2+eax]
|
|
; check for correctness: service port must fit in word
|
|
cmp edx, 0x10000
|
|
jae .service_not_number
|
|
jmp .serv_to_number
|
|
.serv_to_number_done:
|
|
and edx, 0xFFFF ; make sure that port fits
|
|
; 2c. If zero character reached, name is resolved;
|
|
; otherwise, return error (no support for symbolic names yet)
|
|
cmp al, -'0'
|
|
jz .service_resolved
|
|
.service_not_number:
|
|
push EAI_NONAME
|
|
pop eax
|
|
jmp .ret
|
|
.service_resolved:
|
|
; 2d. Save result to reqdata.
|
|
mov [ebx+__gai_reqdata.service], edx
|
|
; 3. Process host name.
|
|
mov esi, [.hostname]
|
|
; 3a. If hostname is not given,
|
|
; use localhost for active socketnums and INADDR_ANY for passive socketnums.
|
|
mov eax, 0x0100007F ; 127.0.0.1 in network byte order
|
|
test byte [ebx+__gai_reqdata.flags], AI_PASSIVE
|
|
jz @f
|
|
xor eax, eax
|
|
@@:
|
|
test esi, esi
|
|
jz .hostname_is_ip
|
|
; 3b. Check for dotted IPv4 name.
|
|
push esi
|
|
call inet_addr
|
|
cmp eax, -1
|
|
jz .resolve_hostname
|
|
.hostname_is_ip:
|
|
; 3c. hostname is valid representation of IP address, and we have resolved it.
|
|
; Generate result, if .res pointer is not NULL.
|
|
mov ebx, [.reqdata]
|
|
mov esi, [.res]
|
|
test esi, esi
|
|
jz .no_result
|
|
call getaddrinfo._.generate_data
|
|
; 3d. Check for memory allocation error.
|
|
.3d:
|
|
push EAI_MEMORY
|
|
pop eax
|
|
test esi, esi
|
|
jz .ret
|
|
; 3e. If AI_CANONNAME is set, copy input name.
|
|
test byte [ebx+__gai_reqdata.flags], AI_CANONNAME
|
|
jz .no_result
|
|
; 3f. Calculate length of name.
|
|
push -1
|
|
pop ecx
|
|
mov edi, [.hostname]
|
|
xor eax, eax
|
|
repnz scasb
|
|
not ecx
|
|
; 3g. Check whether it fits on one page with main data.
|
|
cmp ecx, [.recent_restsize]
|
|
jbe .name_fits
|
|
; 3h. If not, allocate new page.
|
|
push ecx
|
|
add ecx, 4 ; first dword contains number of objects on the page
|
|
mcall 68, 12
|
|
pop ecx
|
|
; 3i. If allocation has failed, free addrinfo and return error.
|
|
test eax, eax
|
|
jnz .name_allocated
|
|
push [.res]
|
|
call freeaddrinfo
|
|
push EAI_MEMORY
|
|
pop eax
|
|
jmp .ret
|
|
.name_allocated:
|
|
; 3j. Otherwise, set edi to allocated memory and continue to 3l.
|
|
xchg edi, eax ; put result to edi
|
|
push 1
|
|
pop eax
|
|
stosd ; number of objects on the page = 1
|
|
jmp .copy_name
|
|
.name_fits:
|
|
; 3k. Get pointer to free memory in allocated page.
|
|
mov edi, [.recent_page]
|
|
mov eax, edi
|
|
and eax, not 0xFFF
|
|
inc dword [eax] ; increase number of objects
|
|
.copy_name:
|
|
; 3l. Put pointer to struct addrinfo.
|
|
mov eax, [.res]
|
|
mov eax, [eax]
|
|
mov [eax+addrinfo.ai_canonname], edi
|
|
; 3m. Copy name.
|
|
rep movsb
|
|
.no_result:
|
|
; 3n. Return success.
|
|
xor eax, eax
|
|
jmp .ret
|
|
; 4. Host address is not dotted IP. Test whether we are allowed to contact DNS.
|
|
; Return error if no.
|
|
.resolve_hostname:
|
|
push EAI_NONAME
|
|
pop eax
|
|
mov ebx, [.reqdata]
|
|
test byte [ebx+__gai_reqdata.flags], AI_NUMERICHOST
|
|
jnz .ret
|
|
; Host address is domain name. Contact DNS server.
|
|
mov esi, [.hostname]
|
|
; 5. Reserve stack place for UDP packet.
|
|
; According to RFC1035, maximum UDP packet size in DNS is 512 bytes.
|
|
sub esp, 512
|
|
; 6. Create DNS request packet.
|
|
; 6a. Set pointer to start of buffer.
|
|
mov edi, esp
|
|
; 6b. Get request ID, write it to buffer.
|
|
push 1
|
|
pop eax
|
|
lock xadd [DNSrequestID], eax ; atomically increment ID, get old value
|
|
stosw
|
|
mov [ebx+__gai_reqdata.reqid], ax
|
|
; 6c. Packed field: QR=0 (query), Opcode=0000 (standard query),
|
|
; AA=0 (ignored in requests), TC=0 (no truncation),
|
|
; RD=1 (recursion desired)
|
|
mov al, 00000001b
|
|
stosb
|
|
; 6d. Packed field: ignored in requests
|
|
mov al, 0
|
|
stosb
|
|
; 6e. Write questions count = 1 and answers count = 0
|
|
; Note that network byte order is big-endian.
|
|
mov eax, 0x00000100
|
|
stosd
|
|
; 6f. Write nameservers count = 0 and additional records count = 0
|
|
xor eax, eax
|
|
stosd
|
|
; 6g. Write request data: name
|
|
; According to RFC1035, maximum length of name is 255 bytes.
|
|
; For correct names, buffer cannot overflow.
|
|
lea ebx, [esi+256] ; ebx = limit for name (including terminating zero)
|
|
; translate string "www.yandex.ru" {00} to byte data {03} "www" {06} "yandex" {02} "ru" {00}
|
|
.nameloop: ; here we go in the start of each label: before "www", before "yandex", before "ru"
|
|
xor ecx, ecx ; ecx = length of current label
|
|
inc edi ; skip length, it will be filled later
|
|
.labelloop: ; here we go for each symbol of name
|
|
lodsb ; get next character
|
|
test al, al ; terminating zero?
|
|
jz .endname
|
|
cmp esi, ebx ; limit exceeded?
|
|
jae .wrongname
|
|
cmp al, '.' ; end of label?
|
|
jz .labelend
|
|
stosb ; put next character
|
|
inc ecx ; increment label length
|
|
jmp .labelloop
|
|
.wrongname:
|
|
push EAI_NONAME
|
|
pop eax
|
|
jmp .ret
|
|
.labelend:
|
|
test ecx, ecx ; null label can be only in the end of name
|
|
jz .wrongname
|
|
.endname:
|
|
cmp ecx, 63
|
|
ja .wrongname
|
|
; write length to byte [edi-ecx-1]
|
|
mov eax, ecx
|
|
neg eax
|
|
mov byte [edi+eax-1], cl
|
|
cmp byte [esi-1], 0 ; that was last label in the name?
|
|
jnz .nameloop
|
|
; write terminating zero if not yet
|
|
mov al, 0
|
|
cmp byte [edi-1], al
|
|
jz @f
|
|
stosb
|
|
@@:
|
|
; 6h. Write request data:
|
|
; query type = A (host address) = 1,
|
|
; query class = IN (internet IPv4 address) = 1
|
|
; Note that network byte order is big-endian.
|
|
mov eax, 0x01000100
|
|
stosd
|
|
; 7. Get DNS server address.
|
|
mcall 75, 0x00000004 ; protocol IP=0, device number=0, function=get DNS address
|
|
cmp eax, -1
|
|
je .ret.dnserr
|
|
mov esi, eax ; put server address to esi
|
|
; 8. Open UDP socketnum to DNS server, port 53.
|
|
; 8a. Create new socketnum.
|
|
mcall 74, 0, AF_INET4, SOCK_DGRAM
|
|
cmp eax, -1 ; error?
|
|
jz .ret.dnserr
|
|
mov ecx, eax ; put socketnum handle to ecx
|
|
; 8b. Create sockaddr structure on the stack.
|
|
push 0
|
|
push 0 ; sin_zero
|
|
push esi ; sin_addr
|
|
push AF_INET4 + (53 shl 16)
|
|
; sin_family and sin_port in network byte order
|
|
; 8c. Connect.
|
|
mcall 74, 4, , esp, sizeof.sockaddr_in
|
|
; 8d. Restore the stack, undo 8b.
|
|
add esp, esi
|
|
; 8e. Check result.
|
|
cmp eax, -1
|
|
jz .ret.close
|
|
; 9. Send DNS request packet.
|
|
sub edi, esp ; get packet length
|
|
mov esi, edi
|
|
xor edi, edi
|
|
mcall 74, 6, , esp
|
|
cmp eax, -1
|
|
jz .ret.close
|
|
mov eax, [.reqdata]
|
|
mov [eax+__gai_reqdata.socketnum], ecx
|
|
push -1
|
|
pop eax ; return status: more processing required
|
|
jmp .ret.dns
|
|
.ret.close:
|
|
mcall 74, 1
|
|
.ret.dnserr:
|
|
push EAI_AGAIN
|
|
pop eax
|
|
.ret.dns:
|
|
; 6. Restore stack pointer and return.
|
|
jmp .ret
|
|
|
|
;;===========================================================================;;
|
|
;; int __stdcall getaddrinfo_process(__in struct __gai_reqdata* reqdata, ;;
|
|
;; __out struct addrinfo** res); ;;
|
|
getaddrinfo_process: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Processes network events from DNS reply ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter = pointer to struct __gai_reqdata filled by ..._start ;;
|
|
;> second parameter = same as for getaddrinfo ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = -1 if more processing required / 0 on success / >0 = error code ;;
|
|
;;===========================================================================;;
|
|
; 0. Create stack frame.
|
|
push ebp
|
|
mov ebp, esp
|
|
virtual at ebp-.locals_size
|
|
.locals_start:
|
|
.datagram rb 512
|
|
.addrname dd ?
|
|
.name dd ?
|
|
.res_list_tail dd ?
|
|
.cname dd ?
|
|
.recent_restsize dd ? ; this is for memory alloc in ._.generate_data
|
|
.recent_page dd ? ; this is for memory alloc in ._.generate_data
|
|
.locals_size = $ - .locals_start
|
|
rd 2
|
|
.reqdata dd ?
|
|
.res dd ?
|
|
end virtual
|
|
xor eax, eax
|
|
push eax ; initialize .recent_page
|
|
push eax ; initialize .recent_restsize
|
|
push eax ; initialize .cname
|
|
push [.res] ; initialize .res_list_tail
|
|
sub esp, .locals_size-16 ; reserve place for other vars
|
|
mov edx, esp ; edx -> buffer for datagram
|
|
; 1. Save used registers for __stdcall.
|
|
push ebx esi edi
|
|
mov edi, [.reqdata]
|
|
; 2. Read UDP datagram.
|
|
mov ecx, [edi+__gai_reqdata.socketnum]
|
|
push edi
|
|
mcall 74, 7, , , 512, 0
|
|
pop edi
|
|
; 3. Ignore events for other socketnums (return if no data read)
|
|
test eax, eax
|
|
jz .ret.more_processing_required
|
|
; 4. Sanity check: discard too short packets.
|
|
xchg ecx, eax ; save packet length in ecx
|
|
cmp ecx, 12
|
|
jb .ret.more_processing_required
|
|
; 5. Discard packets with ID != request ID.
|
|
mov eax, dword [edi+__gai_reqdata.reqid]
|
|
cmp ax, [edx]
|
|
jnz .ret.more_processing_required
|
|
; 6. Sanity check: discard query packets.
|
|
test byte [edx+2], 80h
|
|
jz .ret.more_processing_required
|
|
; 7. Sanity check: must be exactly one query (our).
|
|
cmp word [edx+4], 0x0100 ; note network byte order
|
|
jnz .ret.more_processing_required
|
|
; 8. Check for errors. Return EAI_NONAME for error code 3 and EAI_FAIL for other.
|
|
mov al, [edx+3]
|
|
and al, 0xF
|
|
jz @f
|
|
cmp al, 3
|
|
jnz .ret.no_recovery
|
|
jmp .ret.no_name
|
|
@@:
|
|
; 9. Locate answers section. Exactly 1 query is present in this packet.
|
|
add ecx, edx ; ecx = limit
|
|
lea esi, [edx+12]
|
|
call .skip_name
|
|
lodsd ; skip QTYPE and QCLASS field
|
|
cmp esi, ecx
|
|
ja .ret.no_recovery
|
|
; 10. Loop through all answers.
|
|
movzx ebx, word [edx+6] ; get answers count
|
|
xchg bl, bh ; network -> Intel byte order
|
|
.answers_loop:
|
|
dec ebx
|
|
js .answers_done
|
|
; 10a. Process each record.
|
|
mov [.name], esi
|
|
; 10b. Skip name field.
|
|
call .skip_name
|
|
; 10c. Get record information, handle two types for class IN (internet).
|
|
lodsd ; get type and class
|
|
cmp esi, ecx
|
|
ja .ret.no_recovery
|
|
cmp eax, 0x01000500 ; type=5, class=1?
|
|
jz .got_cname
|
|
cmp eax, 0x01000100 ; type=1, class=1?
|
|
jnz .answers_loop.next
|
|
.got_addr:
|
|
; 10d. Process record A, host address.
|
|
add esi, 10
|
|
cmp esi, ecx
|
|
ja .ret.no_recovery
|
|
cmp word [esi-6], 0x0400 ; RDATA for A records must be 4 bytes long
|
|
jnz .ret.no_recovery
|
|
mov eax, [.name]
|
|
mov [.addrname], eax
|
|
; 10e. Create corresponding record in the answer.
|
|
push ebx ecx esi
|
|
mov eax, [esi-4] ; IP address
|
|
mov esi, [.res_list_tail] ; pointer to result
|
|
test esi, esi
|
|
jz .no_result ; do not save if .res is NULL
|
|
mov ebx, [.reqdata] ; request data
|
|
call getaddrinfo._.generate_data
|
|
mov [.res_list_tail], esi
|
|
pop esi ecx ebx
|
|
cmp [.res_list_tail], 0
|
|
jnz .answers_loop
|
|
; 10f. If generate_data failed (this means memory allocation failure), abort
|
|
jmp .ret.no_memory
|
|
.no_result:
|
|
pop esi ecx ebx
|
|
jmp .answers_loop
|
|
.got_cname:
|
|
; 10g. Process record CNAME, main host name.
|
|
lea eax, [esi+6]
|
|
mov [.cname], eax
|
|
.answers_loop.next:
|
|
; 10h. Skip other record fields, advance to next record.
|
|
lodsd ; skip TTL
|
|
xor eax, eax
|
|
lodsw ; get length of RDATA field
|
|
xchg al, ah ; network -> Intel byte order
|
|
add esi, eax
|
|
cmp esi, ecx
|
|
ja .ret.no_recovery
|
|
jmp .answers_loop
|
|
.answers_done:
|
|
; 11. Check that there is at least 1 answer.
|
|
mov eax, [.res_list_tail]
|
|
cmp [.res], eax
|
|
jz .ret.no_data
|
|
; 12. If canonical name was required, add it now.
|
|
mov eax, [.reqdata]
|
|
test byte [eax+__gai_reqdata.flags], AI_CANONNAME
|
|
jz .no_canon_name
|
|
; 12a. If at least one CNAME record is present, use name from last such record.
|
|
; Otherwise, use name from one of A records.
|
|
mov esi, [.cname]
|
|
test esi, esi
|
|
jnz .has_cname
|
|
mov esi, [.addrname]
|
|
.has_cname:
|
|
; 12b. Calculate name length.
|
|
call .get_name_length
|
|
jc .ret.no_recovery
|
|
; 12c. Check that the caller really want to get data.
|
|
cmp [.res], 0
|
|
jz .no_canon_name
|
|
; 12d. Allocate memory for name.
|
|
call getaddrinfo._.memalloc
|
|
test edi, edi
|
|
jz .ret.no_memory
|
|
; 12e. Make first entry in .res list point to canonical name.
|
|
mov eax, [.res]
|
|
mov eax, [eax]
|
|
mov [eax+addrinfo.ai_canonname], edi
|
|
; 12f. Decode name.
|
|
call .decode_name
|
|
.no_canon_name:
|
|
; 13. Set status to success.
|
|
xor eax, eax
|
|
jmp .ret.close
|
|
; Handle errors.
|
|
.ret.more_processing_required:
|
|
push -1
|
|
pop eax
|
|
jmp .ret
|
|
.ret.no_recovery:
|
|
push EAI_FAIL
|
|
pop eax
|
|
jmp .ret.destroy
|
|
.ret.no_memory:
|
|
push EAI_MEMORY
|
|
pop eax
|
|
jmp .ret.destroy
|
|
.ret.no_name:
|
|
.ret.no_data:
|
|
push EAI_NONAME
|
|
pop eax
|
|
.ret.destroy:
|
|
; 14. If an error occured, free memory acquired so far.
|
|
push eax
|
|
mov esi, [.res]
|
|
test esi, esi
|
|
jz @f
|
|
pushd [esi]
|
|
call freeaddrinfo
|
|
and dword [esi], 0
|
|
@@:
|
|
pop eax
|
|
.ret.close:
|
|
; 15. Close socketnum.
|
|
push eax
|
|
mov ecx, [.reqdata]
|
|
mov ecx, [ecx+__gai_reqdata.socketnum]
|
|
mcall 74, 1
|
|
pop eax
|
|
; 16. Restore used registers, destroy stack frame and return.
|
|
.ret:
|
|
pop edi esi ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
ret 8
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for skipping names in DNS packet. ;;
|
|
.skip_name: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Skips name in DNS packet. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> esi -> name ;;
|
|
;> ecx = end of packet ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< esi -> end of name ;;
|
|
;;===========================================================================;;
|
|
xor eax, eax
|
|
cmp esi, ecx
|
|
jae .skip_name.done
|
|
lodsb
|
|
test al, al
|
|
jz .skip_name.done
|
|
test al, 0xC0
|
|
jnz .skip_name.pointer
|
|
add esi, eax
|
|
jmp .skip_name
|
|
.skip_name.pointer:
|
|
inc esi
|
|
.skip_name.done:
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for calculating length of name in DNS packet. ;;
|
|
.get_name_length: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Calculate length of name (including terminating zero) in DNS packet. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> edx = start of packet ;;
|
|
;> esi -> name ;;
|
|
;> ecx = end of packet ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< eax = length of name ;;
|
|
;< CF set on error / cleared on success ;;
|
|
;;===========================================================================;;
|
|
xor ebx, ebx ; ebx will hold data length
|
|
.get_name_length.zero:
|
|
xor eax, eax
|
|
.get_name_length.loop:
|
|
cmp esi, ecx
|
|
jae .get_name_length.fail
|
|
lodsb
|
|
test al, al
|
|
jz .get_name_length.done
|
|
test al, 0xC0
|
|
jnz .get_name_length.pointer
|
|
add esi, eax
|
|
inc ebx
|
|
add ebx, eax
|
|
cmp ebx, 256
|
|
jbe .get_name_length.loop
|
|
.get_name_length.fail:
|
|
stc
|
|
ret
|
|
.get_name_length.pointer:
|
|
and al, 0x3F
|
|
mov ah, al
|
|
lodsb
|
|
lea esi, [edx+eax]
|
|
jmp .get_name_length.zero
|
|
.get_name_length.done:
|
|
test ebx, ebx
|
|
jz .get_name_length.fail
|
|
xchg eax, ebx
|
|
clc
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for decoding DNS name. ;;
|
|
.decode_name: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Decode name in DNS packet. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> edx = start of packet ;;
|
|
;> esi -> name in packet ;;
|
|
;> edi -> buffer for decoded name ;;
|
|
;;===========================================================================;;
|
|
xor eax, eax
|
|
lodsb
|
|
test al, al
|
|
jz .decode_name.done
|
|
test al, 0xC0
|
|
jnz .decode_name.pointer
|
|
mov ecx, eax
|
|
rep movsb
|
|
mov al, '.'
|
|
stosb
|
|
jmp .decode_name
|
|
.decode_name.pointer:
|
|
and al, 0x3F
|
|
mov ah, al
|
|
lodsb
|
|
lea esi, [edx+eax]
|
|
jmp .decode_name
|
|
.decode_name.done:
|
|
mov byte [edi-1], 0
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for allocating memory for getaddrinfo. ;;
|
|
getaddrinfo._.memalloc: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Memory allocation. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> eax = size in bytes, must be less than page size. ;;
|
|
;> [ebp-4] = .recent_page = last allocated page ;;
|
|
;> [ebp-8] = .recent_restsize = bytes rest in last allocated page ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< edi -> allocated memory / NULL on error ;;
|
|
;;===========================================================================;;
|
|
; 1. Set edi to result of function.
|
|
mov edi, [ebp-4]
|
|
; 2. Check whether we need to allocate a new page.
|
|
cmp eax, [ebp-8]
|
|
jbe .no_new_page
|
|
; 2. Allocate new page if need. Reset edi to new result.
|
|
push eax ebx
|
|
mcall 68, 12, 0x1000
|
|
xchg edi, eax ; put result to edi
|
|
pop ebx eax
|
|
; 3. Check returned value of allocator. Fail if it failed.
|
|
test edi, edi
|
|
jz .ret
|
|
; 4. Update .recent_page and .recent_restsize.
|
|
add edi, 4
|
|
sub ecx, 4
|
|
mov [ebp-4], edi
|
|
mov [ebp-8], ecx
|
|
.no_new_page:
|
|
; 5. Increase number of objects on this page.
|
|
push eax
|
|
mov eax, edi
|
|
and eax, not 0xFFF
|
|
inc dword [eax]
|
|
pop eax
|
|
; 6. Advance last allocated pointer, decrease memory size.
|
|
add [ebp-4], eax
|
|
sub [ebp-8], eax
|
|
; 7. Return.
|
|
.ret:
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; Internal auxiliary function for freeing memory for freeaddrinfo. ;;
|
|
getaddrinfo._.memfree: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Free memory. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> eax = pointer ;;
|
|
;;===========================================================================;;
|
|
; 1. Get start of page.
|
|
mov ecx, eax
|
|
and ecx, not 0xFFF
|
|
; 2. Decrease number of objects.
|
|
dec dword [ecx]
|
|
; 3. If it goes to zero, free the page.
|
|
jnz @f
|
|
push ebx
|
|
mcall 68, 13
|
|
pop ebx
|
|
@@:
|
|
; 4. Done.
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
getaddrinfo._.generate_data: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Generate item(s) of getaddrinfo result list by one IP address. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> eax = IP address ;;
|
|
;> ebx = request data ;;
|
|
;> esi = pointer to result ;;
|
|
;> [ebp-4] = .recent_page = last allocated page ;;
|
|
;> [ebp-8] = .recent_restsize = bytes rest in last allocated page ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;< esi = pointer to next list item for result / NULL on error ;;
|
|
;;===========================================================================;;
|
|
; 1. If no service is given, append one item with zero port.
|
|
; append one item with zero socktype/protocol/port.
|
|
cmp [ebx+__gai_reqdata.service], -1
|
|
jnz .has_service
|
|
call .append_item
|
|
; 1a. If neither protocol nor socktype were specified,
|
|
; leave zeroes in socktype and protocol.
|
|
mov cl, [ebx+__gai_reqdata.socktype]
|
|
test cl, cl
|
|
jz .no_socktype
|
|
; 1b. Otherwise, set socktype and protocol to desired.
|
|
call .set_socktype
|
|
.no_socktype:
|
|
ret
|
|
.has_service:
|
|
; 2. If TCP is allowed, append item for TCP.
|
|
cmp [ebx+__gai_reqdata.socktype], 0
|
|
jz .tcp_ok
|
|
cmp [ebx+__gai_reqdata.socktype], SOCK_STREAM
|
|
jnz .tcp_disallowed
|
|
.tcp_ok:
|
|
call .append_item
|
|
mov cl, SOCK_STREAM
|
|
call .set_socktype
|
|
call .set_port
|
|
.tcp_disallowed:
|
|
; 3. If UDP is allowed, append item for UDP.
|
|
cmp [ebx+__gai_reqdata.socktype], 0
|
|
jz .udp_ok
|
|
cmp [ebx+__gai_reqdata.socktype], SOCK_DGRAM
|
|
jnz .udp_disallowed
|
|
.udp_ok:
|
|
call .append_item
|
|
mov cl, SOCK_DGRAM
|
|
call .set_socktype
|
|
call .set_port
|
|
.udp_disallowed:
|
|
ret
|
|
|
|
.append_item:
|
|
; 1. Allocate memory for struct sockaddr_in and struct addrinfo.
|
|
push eax
|
|
push sizeof.addrinfo + sizeof.sockaddr_in
|
|
pop eax
|
|
call getaddrinfo._.memalloc
|
|
; 2. Check for memory allocation fail.
|
|
test edi, edi
|
|
jz .no_memory
|
|
; 3. Zero allocated memory.
|
|
push (sizeof.addrinfo + sizeof.sockaddr_in) / 4
|
|
pop ecx
|
|
xor eax, eax
|
|
push edi
|
|
rep stosd
|
|
pop edi
|
|
; 4. Fill struct addrinfo.
|
|
mov eax, [ebx+__gai_reqdata.flags]
|
|
mov [edi+addrinfo.ai_flags], eax
|
|
mov byte [edi+addrinfo.ai_family], AF_INET4
|
|
mov byte [edi+addrinfo.ai_addrlen], sizeof.sockaddr_in
|
|
lea ecx, [edi+sizeof.addrinfo]
|
|
mov [edi+addrinfo.ai_addr], ecx
|
|
; 5. Fill struct sockaddr_in.
|
|
mov byte [ecx+sockaddr_in.sin_family], AF_INET4
|
|
pop eax
|
|
mov [ecx+sockaddr_in.sin_addr], eax
|
|
; 6. Append new item to the list.
|
|
mov [esi], edi
|
|
lea esi, [edi+addrinfo.ai_next]
|
|
; 7. Return.
|
|
ret
|
|
.no_memory:
|
|
pop eax
|
|
xor esi, esi
|
|
ret
|
|
|
|
.set_socktype:
|
|
; Set ai_socktype and ai_protocol fields by given socketnum type.
|
|
mov byte [edi+addrinfo.ai_socktype], cl
|
|
dec cl
|
|
jnz .set_udp
|
|
.set_tcp:
|
|
mov byte [edi+addrinfo.ai_protocol], IPPROTO_TCP
|
|
ret
|
|
.set_udp:
|
|
mov byte [edi+addrinfo.ai_protocol], IPPROTO_UDP
|
|
ret
|
|
|
|
.set_port:
|
|
; Just copy port from input __gai_reqdata to output addrinfo.
|
|
push edx
|
|
mov edx, [ebx+__gai_reqdata.service]
|
|
xchg dl, dh ; convert to network byte order
|
|
mov [edi+sizeof.addrinfo+sockaddr_in.sin_port], dx
|
|
pop edx
|
|
ret
|
|
|
|
;;===========================================================================;;
|
|
;; void __stdcall getaddrinfo_abort(__in struct __gai_reqdata* reqdata); ;;
|
|
getaddrinfo_abort: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Abort process started by getaddrinfo_start, free all resources. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter = pointer to struct __gai_reqdata filled by ..._start ;;
|
|
;;===========================================================================;;
|
|
; 0. Save used registers for __stdcall.
|
|
push ebx
|
|
; 1. Allocated resources: only socketnum, so close it and return.
|
|
mov eax, [esp+8]
|
|
mov ecx, [eax+__gai_reqdata.socketnum]
|
|
mcall 74, 1
|
|
; 2. Restore used registers and return.
|
|
pop ebx
|
|
ret 4
|
|
|
|
;;===========================================================================;;
|
|
;; void __stdcall freeaddrinfo(__in struct addrinfo* ai); ;;
|
|
freeaddrinfo: ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;? Free one or more addrinfo structures returned by getaddrinfo. ;;
|
|
;;---------------------------------------------------------------------------;;
|
|
;> first parameter = head of list of structures ;;
|
|
; (may be arbitrary sublist of original) ;;
|
|
;;===========================================================================;;
|
|
; 1. Loop for all items in the list.
|
|
mov edx, [esp+4] ; eax = ai
|
|
.loop:
|
|
test edx, edx
|
|
jz .done
|
|
; 2. Free each item.
|
|
; 2a. Free ai_canonname, if allocated.
|
|
mov eax, [edx+addrinfo.ai_canonname]
|
|
test eax, eax
|
|
jz .no_canon_name
|
|
call getaddrinfo._.memfree
|
|
.no_canon_name:
|
|
; 2b. Remember next item
|
|
; (after freeing the field ai_next can became unavailable).
|
|
pushd [edx+addrinfo.ai_next]
|
|
; 2c. Free item itself.
|
|
xchg eax, edx
|
|
call getaddrinfo._.memfree
|
|
; 2d. Restore pointer to next item and continue loop.
|
|
pop edx
|
|
jmp .loop
|
|
.done:
|
|
; 3. Done.
|
|
ret 4
|
|
|
|
;;===========================================================================;;
|
|
;;///////////////////////////////////////////////////////////////////////////;;
|
|
;;===========================================================================;;
|
|
;! Exported functions section ;;
|
|
;;===========================================================================;;
|
|
;;///////////////////////////////////////////////////////////////////////////;;
|
|
;;===========================================================================;;
|
|
|
|
|
|
align 4
|
|
@EXPORT:
|
|
export \
|
|
lib_init , 'lib_init' , \
|
|
0x00010001 , 'version' , \
|
|
inet_addr , 'inet_addr' , \
|
|
inet_ntoa , 'inet_ntoa' , \
|
|
getaddrinfo , 'getaddrinfo' , \
|
|
getaddrinfo_start , 'getaddrinfo_start' , \
|
|
getaddrinfo_process , 'getaddrinfo_process' , \
|
|
getaddrinfo_abort , 'getaddrinfo_abort' , \
|
|
freeaddrinfo , 'freeaddrinfo'
|
|
|
|
; import from libini
|
|
align 4
|
|
@IMPORT:
|
|
|
|
library libini, 'libini.obj'
|
|
import libini, \
|
|
ini.get_str, 'ini_get_str', \
|
|
ini.get_int, 'ini_get_int'
|
|
|
|
|
|
section '.data' data readable writable align 16
|
|
; uninitialized data
|
|
mem.alloc dd ?
|
|
mem.free dd ?
|
|
mem.realloc dd ?
|
|
dll.load dd ?
|
|
|
|
DNSrequestID dd ?
|
|
|
|
inet_ntoa.buffer rb 16 ; static buffer for inet_ntoa
|