Subversion Repositories Kolibri OS

Rev

Rev 1514 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. format MS COFF
  2.  
  3. public @EXPORT as 'EXPORTS'
  4.  
  5. include '../struct.inc'
  6. include '../proc32.inc'
  7. include '../macros.inc'
  8. purge section,mov,add,sub
  9.  
  10. include '../network.inc'
  11.  
  12. section '.flat' code readable align 16
  13.  
  14. ;;===========================================================================;;
  15. lib_init: ;//////////////////////////////////////////////////////////////////;;
  16. ;;---------------------------------------------------------------------------;;
  17. ;? Library entry point (called after library load)                           ;;
  18. ;;---------------------------------------------------------------------------;;
  19. ;> eax = pointer to memory allocation routine                                ;;
  20. ;> ebx = pointer to memory freeing routine                                   ;;
  21. ;> ecx = pointer to memory reallocation routine                              ;;
  22. ;> edx = pointer to library loading routine                                  ;;
  23. ;;---------------------------------------------------------------------------;;
  24. ;< eax = 1 (fail) / 0 (ok) (library initialization result)                   ;;
  25. ;;===========================================================================;;
  26.         mov     [mem.alloc], eax
  27.         mov     [mem.free], ebx
  28.         mov     [mem.realloc], ecx
  29.         mov     [dll.load], edx
  30.         mov     [DNSrequestID], 1
  31.         stdcall edx, @IMPORT
  32.         xor     eax, eax
  33.         ret
  34.  
  35. ;;===========================================================================;;
  36. ;; in_addr_t __stdcall inet_addr(__in const char* hostname);                 ;;
  37. inet_addr:                                                                   ;;
  38. ;;---------------------------------------------------------------------------;;
  39. ;? Convert the string from standard IPv4 dotted notation to integer IP addr. ;;
  40. ;;---------------------------------------------------------------------------;;
  41. ;> first parameter = host name                                               ;;
  42. ;;---------------------------------------------------------------------------;;
  43. ;< eax = IP address on success / -1 on error                                 ;;
  44. ;;===========================================================================;;
  45. ; 0. Save used registers for __stdcall.
  46.         push    ebx esi edi
  47.         mov     esi, [esp+16]   ; esi = hostname
  48. ; 1. Check that only allowed symbols are present.
  49. ; (hex digits, possibly letters 'x'/'X' and up to 3 dots)
  50.         push    esi
  51.         xor     ecx, ecx
  52. .calcdots_loop:
  53. ; loop for all characters in string
  54.         lodsb
  55. ; check for end of string
  56.         cmp     al, 0
  57.         jz      .calcdots_loop_done
  58. ; check for dot
  59.         cmp     al, '.'
  60.         jz      .dot
  61. ; check for digit
  62.         sub     al, '0'
  63.         cmp     al, 9
  64.         jbe     .calcdots_loop
  65. ; check for hex letter
  66.         sub     al, 'A' - '0'   ; 'A'-'F' -> 0-5, 'a'-'f' -> 20h-25h
  67.         and     al, not 20h
  68.         cmp     al, 'F' - 'A'
  69.         jbe     .calcdots_loop
  70. ; check for 'x'/'X'
  71.         cmp     al, 'X' - 'A'
  72.         jz      .calcdots_loop
  73.         jmp     .fail.pop
  74. .dot:
  75.         inc     ecx
  76.         jmp     .calcdots_loop
  77. .calcdots_loop_done:
  78.         cmp     ecx, 4
  79.         jae     .fail.pop
  80. ; 2. The name can be valid dotted name; try to convert, checking limit
  81.         pop     esi
  82.         xor     edi, edi        ; edi = address
  83.         push    0xFFFFFFFF
  84.         pop     edx             ; edx = mask for rest of address
  85. ; 2a. Convert name except for last group.
  86.         jecxz   .ip_convert_2b
  87. .ip_convert_2a:
  88.         push    ecx
  89.         mov     ecx, 0xFF       ; limit for all groups except for last
  90.         call    .get_number
  91.         pop     ecx
  92.         jc      .fail
  93.         cmp     byte [esi-1], '.'
  94.         jnz     .fail
  95.         shl     edi, 8
  96.         shr     edx, 8
  97.         add     edi, eax
  98.         loop    .ip_convert_2a
  99. ; 2b. Convert last group.
  100. .ip_convert_2b:
  101.         mov     ecx, edx
  102.         call    .get_number
  103.         jc      .fail
  104.         cmp     byte [esi-1], 0
  105.         jnz     .fail
  106. @@:
  107.         shl     edi, 8
  108.         shr     edx, 8
  109.         jnz     @b
  110.         add     edi, eax
  111. ; 2c. Convert to network byte order.
  112.         bswap   edi
  113. ; 3. Set return value, restore used registers and return.
  114.         xchg    eax, edi
  115. .ret:
  116.         pop     edi esi ebx
  117.         ret     4
  118. ; 4. On error, return -1.
  119. .fail.pop:
  120.         pop     esi
  121. .fail:
  122.         push    -1
  123.         pop     eax
  124.         jmp     .ret
  125.  
  126. ;;===========================================================================;;
  127. ;; Internal auxiliary function for IP parsing.                                        ;;
  128. .get_number:                                                                 ;;
  129. ;;---------------------------------------------------------------------------;;
  130. ;? Converts string to number.                                                ;;
  131. ;;---------------------------------------------------------------------------;;
  132. ;> esi -> string                                                             ;;
  133. ;> ecx = limit for number                                                    ;;
  134. ;;---------------------------------------------------------------------------;;
  135. ;< eax = number                                                              ;;
  136. ;< CF set on error (too big number) / cleared on success                     ;;
  137. ;< esi -> end of number representation                                       ;;
  138. ;;===========================================================================;;
  139. ; 0. Save edx, which is used in caller.
  140.         push    edx
  141. ; 1. Initialize number, zero eax so that lodsb gets full dword.
  142.         xor     eax, eax
  143.         xor     edx, edx
  144. ; 2. Get used numeral system: 0x = hex, otherwise 0 = octal, otherwise decimal
  145.         push    10
  146.         pop     ebx
  147.         lodsb
  148.         cmp     al, '0'
  149.         jnz     .convert
  150.         push    8
  151.         pop     ebx
  152.         lodsb
  153.         cmp     al, 'x'
  154.         jnz     .convert
  155.         add     ebx, ebx
  156. ; 3. Loop while digits are encountered.
  157. .convert:
  158. ; 4. Convert digit from text representation to binary value.
  159.         or      al, 20h ; '0'-'9' -> '0'-'9', 'A'-'F' -> 'a'-'f'
  160.         sub     al, '0'
  161.         cmp     al, 9
  162.         jbe     .digit
  163.         sub     al, 'a' - '0'
  164.         cmp     al, 'f' - 'a'
  165.         ja      .convert_done
  166.         add     al, 10
  167. .digit:
  168. ; 5. Digit must be less than base of numeral system.
  169.         cmp     eax, ebx
  170.         jae     .convert_done
  171. ; 6. Advance the number.
  172.         imul    edx, ebx
  173.         add     edx, eax
  174.         cmp     edx, ecx
  175.         ja      .gn_error
  176. ; 3b. Continue loop.
  177.         lodsb
  178.         jmp     .convert
  179. .convert_done:
  180. ; 7. Invalid character, number converted, return success.
  181.         xchg    eax, edx
  182.         pop     edx
  183.         clc
  184.         ret
  185. .gn_error:
  186. ; 8. Too big number, return error.
  187.         pop     edx
  188.         stc
  189.         ret
  190.  
  191. ;;===========================================================================;;
  192. ;; char* __stdcall inet_ntoa(struct in_addr in);                             ;;
  193. inet_ntoa:                                                                   ;;
  194. ;;---------------------------------------------------------------------------;;
  195. ;? Convert the Internet host address to standard IPv4 dotted notation.       ;;
  196. ;;---------------------------------------------------------------------------;;
  197. ;> first parameter = host address                                            ;;
  198. ;;---------------------------------------------------------------------------;;
  199. ;< eax = pointer to resulting string (in static buffer)                      ;;
  200. ;;===========================================================================;;
  201. ; 0. Save used registers for __stdcall.
  202.         push    ebx esi edi
  203.         mov     bl, 0xCD        ; constant for div 10
  204. ; 1. Write octet 4 times.
  205.         mov     edi, .buffer
  206.         mov     edx, [esp+16]   ; eax = in
  207.         mov     al, dl
  208.         call    .write
  209.         mov     al, dh
  210.         shr     edx, 16
  211.         call    .write
  212.         mov     al, dl
  213.         call    .write
  214.         mov     al, dh
  215.         call    .write
  216. ; 2. Replace final dot with terminating zero.
  217.         mov     byte [edi-1], 0
  218. ; 3. Restore used registers, set result value and return.
  219.         pop     edi esi ebx
  220.         mov     eax, .buffer
  221.         ret     4
  222.  
  223. .write:
  224.         movzx   esi, al
  225.         mul     bl
  226.         add     esi, ('.' shl 8) + '0'
  227.         shr     ah, 3   ; ah = al / 10
  228.         movzx   ecx, ah
  229.         add     ecx, ecx
  230.         lea     ecx, [ecx*5]
  231.         sub     esi, ecx        ; lobyte(esi) = al % 10, hibyte(esi) = '.'
  232.         test    ah, ah
  233.         jz      .1digit
  234.         cmp     ah, 10
  235.         jb      .2digit
  236.         cmp     ah, 20
  237.         sbb     cl, cl
  238.         add     cl, '2'
  239.         mov     byte [edi], cl
  240.         movzx   ecx, cl
  241.         lea     ecx, [ecx*5]
  242.         sub     ah, cl
  243.         sub     ah, cl
  244.         add     ah, ('0'*11) and 255
  245.         mov     byte [edi+1], ah
  246.         mov     word [edi+2], si
  247.         add     edi, 4
  248.         ret
  249. .2digit:
  250.         add     ah, '0'
  251.         mov     byte [edi], ah
  252.         mov     word [edi+1], si
  253.         add     edi, 3
  254.         ret
  255. .1digit:
  256.         mov     word [edi], si
  257.         add     edi, 2
  258.         ret
  259.  
  260. struct __gai_reqdata
  261.         socketnum  dd      ?
  262. ; external code should not look on rest of this structure,
  263. ; it is internal for getaddrinfo_start/process/abort
  264.         reqid           dw      ?       ; DNS request ID
  265.         socktype        db      ?       ; SOCK_* or 0 for any
  266.                         db      ?
  267.         service         dd      ?
  268.         flags           dd      ?
  269.         reserved        rb      16
  270. ends
  271.  
  272. ;;===========================================================================;;
  273. ;; int __stdcall getaddrinfo(__in const char* hostname,                      ;;
  274. ;;                           __in const char* servname,                      ;;
  275. ;;                           __in const struct addrinfo* hints,              ;;
  276. ;;                           __out struct addrinfo **res);                   ;;
  277. getaddrinfo:                                                                 ;;
  278. ;;---------------------------------------------------------------------------;;
  279. ;? Get a list of IP addresses and port numbers for given host and service    ;;
  280. ;;---------------------------------------------------------------------------;;
  281. ;> first parameter (optional) = host name                                    ;;
  282. ;> second parameter (optional) = service name (decimal number for now)       ;;
  283. ;> third parameter (optional) = hints for socketnum type                        ;;
  284. ;> fourth parameter = pointer to result (head of L1-list)                    ;;
  285. ;;---------------------------------------------------------------------------;;
  286. ;< eax = 0 on success / one of EAI_ codes on error                           ;;
  287. ;;===========================================================================;;
  288. ; 0. Save used registers for __stdcall.
  289.         push    ebx esi edi
  290.         mov     edi, [esp+28]   ; edi = res
  291. ; 1. Create and send DNS packet.
  292.         sub     esp, sizeof.__gai_reqdata       ; reserve stack place (1)
  293.         push    esp             ; fifth parameter = pointer to (1)
  294.         push    edi             ; fourth parameter = res
  295.         push    dword [esp+32+sizeof.__gai_reqdata]     ; third parameter = hints
  296.         push    dword [esp+32+sizeof.__gai_reqdata]     ; second parameter = servname
  297.         push    dword [esp+32+sizeof.__gai_reqdata]     ; first parameter = hostname
  298.         call    getaddrinfo_start
  299.         test    eax, eax
  300.         jns     .ret    ; if name resolved without network activity, return
  301. ; 2. Wait for DNS reply.
  302. ; 2a. Ignore all events except network stack.
  303.         mcall   40, EVM_STACK
  304.         push    eax     ; save previous event mask (2)
  305. ; 2b. Get upper limit for wait time. Use timeout = 5 seconds.
  306.         mcall   26, 9   ; get time stamp
  307.         xchg    esi, eax        ; save time stamp to esi
  308.         mov     ebx, 500        ; start value for timeout
  309.         add     esi, ebx
  310. .wait:
  311. ; 2c. Wait for event with timeout.
  312.         mcall   23      ; wait for event - must be stack event
  313. ; 2d. Check for timeout.
  314.         test    eax, eax
  315.         lea     eax, [esp+4]    ; pointer to (1)
  316.         jz      .timeout
  317. ; 3. Got packet. Call processing function.
  318.         push    edi     ; second parameter: pointer to result
  319.         push    eax     ; first parameter: pointer to reqdata
  320.         call    getaddrinfo_process
  321. ; 4. Test whether wait loop must be continued.
  322.         test    eax, eax
  323.         jns     .ret.restore
  324. ; 2e. Recalculate timeout value.
  325.         mcall   26, 9
  326.         mov     ebx, esi
  327.         sub     ebx, eax
  328. ; 2f. Check that time is not over; if not, continue wait loop
  329.         cmp     ebx, 500
  330.         jbe     .wait
  331. .timeout:
  332. ; 5. Timeout: abort and return error
  333.         push    eax
  334.         call    getaddrinfo_abort
  335.         and     dword [edi], 0
  336.         push    EAI_AGAIN
  337.         pop     eax
  338. .ret.restore:
  339. ; 6. Restore event mask.
  340.         pop     ebx     ; get event mask (2)
  341.         push    eax     ; save return code (3)
  342.         mcall   40
  343.         pop     eax     ; restore return code (3)
  344. .ret:
  345. ; 7. Restore stack pointer, used registers and return.
  346.         add     esp, sizeof.__gai_reqdata       ; undo (1)
  347.         pop     edi esi ebx
  348.         ret     16
  349.  
  350. ;;===========================================================================;;
  351. ;; int __stdcall getaddrinfo_start(__in const char* hostname,                ;;
  352. ;;                                 __in const char* servname,                ;;
  353. ;;                                 __in const struct addrinfo* hints,        ;;
  354. ;;                                 __out struct addrinfo **res,              ;;
  355. ;;                                 __out struct __gai_reqdata* reqdata);     ;;
  356. getaddrinfo_start:                                                           ;;
  357. ;;---------------------------------------------------------------------------;;
  358. ;? Initiator for getaddrinfo, sends DNS request                              ;;
  359. ;;---------------------------------------------------------------------------;;
  360. ;> first 4 parameters same as for getaddrinfo                                ;;
  361. ;> last parameter = pointer to buffer for __gai_reqdata, must be passed to   ;;
  362. ;>                  getaddrinfo_process as is                                ;;
  363. ;;---------------------------------------------------------------------------;;
  364. ;< eax = <0 if wait loop must be entered / 0 on success / EAI_* on error     ;;
  365. ;;===========================================================================;;
  366. ;; Known limitations:                                                        ;;
  367. ;; 1. No support for TCP connections =>                                      ;;
  368. ;; 1a. Long replies will be truncated, and not all IP addresses will be got. ;;
  369. ;; 2. No support for iterative resolving =>                                  ;;
  370. ;; 2a. In theory may fail with some servers.                                 ;;
  371. ;; 3. Assumes that domain for relative names is always root, ".".            ;;
  372. ;; 4. Does not support lookup of services by name,                           ;;
  373. ;;    only decimal representation is supported.                              ;;
  374. ;; 5. Assumes that IPv4 is always configured, so AI_ADDRCONFIG has no effect.;;
  375. ;;===========================================================================;;
  376. ; 0. Create stack frame and save used registers for __stdcall.
  377.         push    ebx esi edi
  378.         push    ebp
  379.         mov     ebp, esp
  380. virtual at ebp-8
  381. .recent_restsize dd     ?       ; this is for memory alloc in ._.generate_data
  382. .recent_page    dd      ?       ; this is for memory alloc in ._.generate_data
  383.         rd      5       ; saved regs and return address
  384. .hostname       dd      ?
  385. .servname       dd      ?
  386. .hints          dd      ?
  387. .res            dd      ?
  388. .reqdata        dd      ?
  389. end virtual
  390.         xor     edi, edi
  391.         push    edi     ; init .recent_page
  392.         push    edi     ; init .recent_restsize
  393. ; 1. Check that parameters are correct and can be handled by this implementation.
  394. ; 1a. If 'res' pointer is given, set result to zero.
  395.         mov     eax, [.res]
  396.         test    eax, eax
  397.         jz      @f
  398.         mov     [eax], edi
  399. @@:
  400. ; 1b. Only AI_SUPPORTED flags are supported for hints->ai_flags.
  401.         mov     ecx, [.hints]
  402.         xor     edx, edx
  403.         jecxz   .nohints
  404.         mov     edx, [ecx+addrinfo.ai_flags]
  405. .nohints:
  406.         mov     ebx, [.reqdata]
  407.         mov     [ebx+__gai_reqdata.flags], edx
  408.         push    EAI_BADFLAGS
  409.         pop     eax
  410.         test    edx, not AI_SUPPORTED
  411.         jnz     .ret
  412. ; 1c. Either hostname or servname must be given. If AI_CANONNAME is set,
  413. ; hostname must also be set.
  414.         cmp     [.hostname], edi
  415.         jnz     @f
  416.         test    dl, AI_CANONNAME
  417.         jnz     .ret
  418.         push    EAI_NONAME
  419.         pop     eax
  420.         cmp     [.servname], edi
  421.         jz      .ret
  422. @@:
  423. ; 1d. Only IPv4 is supported, so hints->ai_family must be either PF_UNSPEC or PF_INET.
  424.         push    EAI_FAMILY
  425.         pop     eax
  426.         jecxz   @f
  427.         cmp     [ecx+addrinfo.ai_family], edi
  428.         jz      @f
  429.         cmp     [ecx+addrinfo.ai_family], AF_INET4
  430.         jnz     .ret
  431. @@:
  432. ; 1e. Valid combinations for ai_socktype/ai_protocol: 0/0 for any or
  433. ;       SOCK_STREAM/IPPROTO_TCP, SOCK_DGRAM/IPPROTO_UDP
  434. ;       (raw socketnums are not yet supported by the kernel)
  435.         xor     edx, edx        ; assume 0=any if no hints
  436.         jecxz   .socketnum_type_ok
  437.         mov     edx, [ecx+addrinfo.ai_socktype]
  438.         mov     esi, [ecx+addrinfo.ai_protocol]
  439. ; 1f. Test for ai_socktype=0 and ai_protocol=0.
  440.         test    edx, edx
  441.         jnz     .check_socktype
  442.         test    esi, esi
  443.         jz      .socketnum_type_ok
  444. ; 1g. ai_socktype=0, ai_protocol is nonzero.
  445.         push    EAI_SERVICE
  446.         pop     eax
  447.         inc     edx     ; edx = SOCK_STREAM
  448.         cmp     esi, IPPROTO_TCP
  449.         jz      .socketnum_type_ok
  450.         inc     edx     ; edx = SOCK_DGRAM
  451.         cmp     esi, IPPROTO_UDP
  452.         jz      .socketnum_type_ok
  453. .ret:
  454. ; Restore saved registers, destroy stack frame and return.
  455.         mov     esp, ebp
  456.         pop     ebp
  457.         pop     edi esi ebx
  458.         ret     20
  459. ; 1h. ai_socktype is nonzero.
  460. .check_socktype:
  461.         push    EAI_SOCKTYPE
  462.         pop     eax
  463.         cmp     edx, SOCK_STREAM
  464.         jz      .check_tcp
  465.         cmp     edx, SOCK_DGRAM
  466.         jnz     .ret
  467.         test    esi, esi
  468.         jz      .socketnum_type_ok
  469.         cmp     esi, IPPROTO_UDP
  470.         jz      .socketnum_type_ok
  471.         jmp     .ret
  472. .check_tcp:
  473.         test    esi, esi
  474.         jz      .socketnum_type_ok
  475.         cmp     esi, IPPROTO_TCP
  476.         jnz     .ret
  477. .socketnum_type_ok:
  478.         mov     [ebx+__gai_reqdata.socktype], dl
  479. ; 2. Resolve service.
  480. ; 2a. If no name is given, remember value -1.
  481.         push    -1
  482.         pop     edx
  483.         mov     esi, [.servname]
  484.         test    esi, esi
  485.         jz      .service_resolved
  486. ; 2b. Loop for characters of string while digits are encountered.
  487.         xor     edx, edx
  488.         xor     eax, eax
  489. .serv_to_number:
  490.         lodsb
  491.         sub     al, '0'
  492.         cmp     al, 9
  493.         ja      .serv_to_number_done
  494. ; for each digit, set edx = edx*10 + <digit>
  495.         lea     edx, [edx*5]
  496.         lea     edx, [edx*2+eax]
  497. ; check for correctness: service port must fit in word
  498.         cmp     edx, 0x10000
  499.         jae     .service_not_number
  500.         jmp     .serv_to_number
  501. .serv_to_number_done:
  502.         and     edx, 0xFFFF     ; make sure that port fits
  503. ; 2c. If zero character reached, name is resolved;
  504. ; otherwise, return error (no support for symbolic names yet)
  505.         cmp     al, -'0'
  506.         jz      .service_resolved
  507. .service_not_number:
  508.         push    EAI_NONAME
  509.         pop     eax
  510.         jmp     .ret
  511. .service_resolved:
  512. ; 2d. Save result to reqdata.
  513.         mov     [ebx+__gai_reqdata.service], edx
  514. ; 3. Process host name.
  515.         mov     esi, [.hostname]
  516. ; 3a. If hostname is not given,
  517. ;       use localhost for active socketnums and INADDR_ANY for passive socketnums.
  518.         mov     eax, 0x0100007F ; 127.0.0.1 in network byte order
  519.         test    byte [ebx+__gai_reqdata.flags], AI_PASSIVE
  520.         jz      @f
  521.         xor     eax, eax
  522. @@:
  523.         test    esi, esi
  524.         jz      .hostname_is_ip
  525. ; 3b. Check for dotted IPv4 name.
  526.         push    esi
  527.         call    inet_addr
  528.         cmp     eax, -1
  529.         jz      .resolve_hostname
  530. .hostname_is_ip:
  531. ; 3c. hostname is valid representation of IP address, and we have resolved it.
  532. ; Generate result, if .res pointer is not NULL.
  533.         mov     ebx, [.reqdata]
  534.         mov     esi, [.res]
  535.         test    esi, esi
  536.         jz      .no_result
  537.         call    getaddrinfo._.generate_data
  538. ; 3d. Check for memory allocation error.
  539. .3d:
  540.         push    EAI_MEMORY
  541.         pop     eax
  542.         test    esi, esi
  543.         jz      .ret
  544. ; 3e. If AI_CANONNAME is set, copy input name.
  545.         test    byte [ebx+__gai_reqdata.flags], AI_CANONNAME
  546.         jz      .no_result
  547. ; 3f. Calculate length of name.
  548.         push    -1
  549.         pop     ecx
  550.         mov     edi, [.hostname]
  551.         xor     eax, eax
  552.         repnz   scasb
  553.         not     ecx
  554. ; 3g. Check whether it fits on one page with main data.
  555.         cmp     ecx, [.recent_restsize]
  556.         jbe     .name_fits
  557. ; 3h. If not, allocate new page.
  558.         push    ecx
  559.         add     ecx, 4  ; first dword contains number of objects on the page
  560.         mcall   68, 12
  561.         pop     ecx
  562. ; 3i. If allocation has failed, free addrinfo and return error.
  563.         test    eax, eax
  564.         jnz     .name_allocated
  565.         push    [.res]
  566.         call    freeaddrinfo
  567.         push    EAI_MEMORY
  568.         pop     eax
  569.         jmp     .ret
  570. .name_allocated:
  571. ; 3j. Otherwise, set edi to allocated memory and continue to 3l.
  572.         xchg    edi, eax        ; put result to edi
  573.         push    1
  574.         pop     eax
  575.         stosd   ; number of objects on the page = 1
  576.         jmp     .copy_name
  577. .name_fits:
  578. ; 3k. Get pointer to free memory in allocated page.
  579.         mov     edi, [.recent_page]
  580.         mov     eax, edi
  581.         and     eax, not 0xFFF
  582.         inc     dword [eax]     ; increase number of objects
  583. .copy_name:
  584. ; 3l. Put pointer to struct addrinfo.
  585.         mov     eax, [.res]
  586.         mov     eax, [eax]
  587.         mov     [eax+addrinfo.ai_canonname], edi
  588. ; 3m. Copy name.
  589.         rep     movsb
  590. .no_result:
  591. ; 3n. Return success.
  592.         xor     eax, eax
  593.         jmp     .ret
  594. ; 4. Host address is not dotted IP. Test whether we are allowed to contact DNS.
  595. ; Return error if no.
  596. .resolve_hostname:
  597.         push    EAI_NONAME
  598.         pop     eax
  599.         mov     ebx, [.reqdata]
  600.         test    byte [ebx+__gai_reqdata.flags], AI_NUMERICHOST
  601.         jnz     .ret
  602. ; Host address is domain name. Contact DNS server.
  603.         mov     esi, [.hostname]
  604. ; 5. Reserve stack place for UDP packet.
  605. ; According to RFC1035, maximum UDP packet size in DNS is 512 bytes.
  606.         sub     esp, 512
  607. ; 6. Create DNS request packet.
  608. ; 6a. Set pointer to start of buffer.
  609.         mov     edi, esp
  610. ; 6b. Get request ID, write it to buffer.
  611.         push    1
  612.         pop     eax
  613. lock    xadd    [DNSrequestID], eax     ; atomically increment ID, get old value
  614.         stosw
  615.         mov     [ebx+__gai_reqdata.reqid], ax
  616. ; 6c. Packed field: QR=0 (query), Opcode=0000 (standard query),
  617. ;       AA=0 (ignored in requests), TC=0 (no truncation),
  618. ;       RD=1 (recursion desired)
  619.         mov     al, 00000001b
  620.         stosb
  621. ; 6d. Packed field: ignored in requests
  622.         mov     al, 0
  623.         stosb
  624. ; 6e. Write questions count = 1 and answers count = 0
  625. ; Note that network byte order is big-endian.
  626.         mov     eax, 0x00000100
  627.         stosd
  628. ; 6f. Write nameservers count = 0 and additional records count = 0
  629.         xor     eax, eax
  630.         stosd
  631. ; 6g. Write request data: name
  632. ; According to RFC1035, maximum length of name is 255 bytes.
  633. ; For correct names, buffer cannot overflow.
  634.         lea     ebx, [esi+256]  ; ebx = limit for name (including terminating zero)
  635. ; translate string "www.yandex.ru" {00} to byte data {03} "www" {06} "yandex" {02} "ru" {00}
  636. .nameloop:      ; here we go in the start of each label: before "www", before "yandex", before "ru"
  637.         xor     ecx, ecx        ; ecx = length of current label
  638.         inc     edi     ; skip length, it will be filled later
  639. .labelloop:     ; here we go for each symbol of name
  640.         lodsb   ; get next character
  641.         test    al, al  ; terminating zero?
  642.         jz      .endname
  643.         cmp     esi, ebx        ; limit exceeded?
  644.         jae     .wrongname
  645.         cmp     al, '.' ; end of label?
  646.         jz      .labelend
  647.         stosb   ; put next character
  648.         inc     ecx     ; increment label length
  649.         jmp     .labelloop
  650. .wrongname:
  651.         push    EAI_NONAME
  652.         pop     eax
  653.         jmp     .ret
  654. .labelend:
  655.         test    ecx, ecx        ; null label can be only in the end of name
  656.         jz      .wrongname
  657. .endname:
  658.         cmp     ecx, 63
  659.         ja      .wrongname
  660. ; write length to byte [edi-ecx-1]
  661.         mov     eax, ecx
  662.         neg     eax
  663.         mov     byte [edi+eax-1], cl
  664.         cmp     byte [esi-1], 0 ; that was last label in the name?
  665.         jnz     .nameloop
  666. ; write terminating zero if not yet
  667.         mov     al, 0
  668.         cmp     byte [edi-1], al
  669.         jz      @f
  670.         stosb
  671. @@:
  672. ; 6h. Write request data:
  673. ;       query type = A (host address) = 1,
  674. ;       query class = IN (internet IPv4 address) = 1
  675. ; Note that network byte order is big-endian.
  676.         mov     eax, 0x01000100
  677.         stosd
  678. ; 7. Get DNS server address.
  679.         mcall   75, 0x00000004 ; protocol IP=0, device number=0, function=get DNS address
  680.         cmp     eax, -1
  681.         je      .ret.dnserr
  682.         mov     esi, eax        ; put server address to esi
  683. ; 8. Open UDP socketnum to DNS server, port 53.
  684. ; 8a. Create new socketnum.
  685.         mcall   74, 0, AF_INET4, SOCK_DGRAM
  686.         cmp     eax, -1 ; error?
  687.         jz      .ret.dnserr
  688.         mov     ecx, eax        ; put socketnum handle to ecx
  689. ; 8b. Create sockaddr structure on the stack.
  690.         push    0
  691.         push    0       ; sin_zero
  692.         push    esi     ; sin_addr
  693.         push    AF_INET4 + (53 shl 16)
  694.                         ; sin_family and sin_port in network byte order
  695. ; 8c. Connect.
  696.         mcall   74, 4, , esp, sizeof.sockaddr_in
  697. ; 8d. Restore the stack, undo 8b.
  698.         add     esp, esi
  699. ; 8e. Check result.
  700.         cmp     eax, -1
  701.         jz      .ret.close
  702. ; 9. Send DNS request packet.
  703.         sub     edi, esp        ; get packet length
  704.         mov     esi, edi
  705.         xor     edi, edi
  706.         mcall   74, 6, , esp
  707.         cmp     eax, -1
  708.         jz      .ret.close
  709.         mov     eax, [.reqdata]
  710.         mov     [eax+__gai_reqdata.socketnum], ecx
  711.         push    -1
  712.         pop     eax     ; return status: more processing required
  713.         jmp     .ret.dns
  714. .ret.close:
  715.         mcall   74, 1
  716. .ret.dnserr:
  717.         push    EAI_AGAIN
  718.         pop     eax
  719. .ret.dns:
  720. ; 6. Restore stack pointer and return.
  721.         jmp     .ret
  722.  
  723. ;;===========================================================================;;
  724. ;; int __stdcall getaddrinfo_process(__in struct __gai_reqdata* reqdata,     ;;
  725. ;;                                   __out struct addrinfo** res);           ;;
  726. getaddrinfo_process:                                                         ;;
  727. ;;---------------------------------------------------------------------------;;
  728. ;? Processes network events from DNS reply                                   ;;
  729. ;;---------------------------------------------------------------------------;;
  730. ;> first parameter = pointer to struct __gai_reqdata filled by ..._start     ;;
  731. ;> second parameter = same as for getaddrinfo                                ;;
  732. ;;---------------------------------------------------------------------------;;
  733. ;< eax = -1 if more processing required / 0 on success / >0 = error code     ;;
  734. ;;===========================================================================;;
  735. ; 0. Create stack frame.
  736.         push    ebp
  737.         mov     ebp, esp
  738. virtual at ebp-.locals_size
  739. .locals_start:
  740. .datagram       rb      512
  741. .addrname       dd      ?
  742. .name           dd      ?
  743. .res_list_tail  dd      ?
  744. .cname          dd      ?
  745. .recent_restsize dd     ?       ; this is for memory alloc in ._.generate_data
  746. .recent_page    dd      ?       ; this is for memory alloc in ._.generate_data
  747. .locals_size = $ - .locals_start
  748.                 rd      2
  749. .reqdata        dd      ?
  750. .res            dd      ?
  751. end virtual
  752.         xor     eax, eax
  753.         push    eax     ; initialize .recent_page
  754.         push    eax     ; initialize .recent_restsize
  755.         push    eax     ; initialize .cname
  756.         push    [.res]  ; initialize .res_list_tail
  757.         sub     esp, .locals_size-16    ; reserve place for other vars
  758.         mov     edx, esp        ; edx -> buffer for datagram
  759. ; 1. Save used registers for __stdcall.
  760.         push    ebx esi edi
  761.         mov     edi, [.reqdata]
  762. ; 2. Read UDP datagram.
  763.         mov     ecx, [edi+__gai_reqdata.socketnum]
  764.         push    edi
  765.         mcall   74, 7, , , 512, 0
  766.         pop     edi
  767. ; 3. Ignore events for other socketnums (return if no data read)
  768.         test    eax, eax
  769.         jz      .ret.more_processing_required
  770. ; 4. Sanity check: discard too short packets.
  771.         xchg    ecx, eax        ; save packet length in ecx
  772.         cmp     ecx, 12
  773.         jb      .ret.more_processing_required
  774. ; 5. Discard packets with ID != request ID.
  775.         mov     eax, dword [edi+__gai_reqdata.reqid]
  776.         cmp     ax, [edx]
  777.         jnz     .ret.more_processing_required
  778. ; 6. Sanity check: discard query packets.
  779.         test    byte [edx+2], 80h
  780.         jz      .ret.more_processing_required
  781. ; 7. Sanity check: must be exactly one query (our).
  782.         cmp     word [edx+4], 0x0100    ; note network byte order
  783.         jnz     .ret.more_processing_required
  784. ; 8. Check for errors. Return EAI_NONAME for error code 3 and EAI_FAIL for other.
  785.         mov     al, [edx+3]
  786.         and     al, 0xF
  787.         jz      @f
  788.         cmp     al, 3
  789.         jnz     .ret.no_recovery
  790.         jmp     .ret.no_name
  791. @@:
  792. ; 9. Locate answers section. Exactly 1 query is present in this packet.
  793.         add     ecx, edx        ; ecx = limit
  794.         lea     esi, [edx+12]
  795.         call    .skip_name
  796.         lodsd           ; skip QTYPE and QCLASS field
  797.         cmp     esi, ecx
  798.         ja      .ret.no_recovery
  799. ; 10. Loop through all answers.
  800.         movzx   ebx, word [edx+6]       ; get answers count
  801.         xchg    bl, bh          ; network -> Intel byte order
  802. .answers_loop:
  803.         dec     ebx
  804.         js      .answers_done
  805. ; 10a. Process each record.
  806.         mov     [.name], esi
  807. ; 10b. Skip name field.
  808.         call    .skip_name
  809. ; 10c. Get record information, handle two types for class IN (internet).
  810.         lodsd           ; get type and class
  811.         cmp     esi, ecx
  812.         ja      .ret.no_recovery
  813.         cmp     eax, 0x01000500 ; type=5, class=1?
  814.         jz      .got_cname
  815.         cmp     eax, 0x01000100 ; type=1, class=1?
  816.         jnz     .answers_loop.next
  817. .got_addr:
  818. ; 10d. Process record A, host address.
  819.         add     esi, 10
  820.         cmp     esi, ecx
  821.         ja      .ret.no_recovery
  822.         cmp     word [esi-6], 0x0400    ; RDATA for A records must be 4 bytes long
  823.         jnz     .ret.no_recovery
  824.         mov     eax, [.name]
  825.         mov     [.addrname], eax
  826. ; 10e. Create corresponding record in the answer.
  827.         push    ebx ecx esi
  828.         mov     eax, [esi-4]    ; IP address
  829.         mov     esi, [.res_list_tail]   ; pointer to result
  830.         test    esi, esi
  831.         jz      .no_result      ; do not save if .res is NULL
  832.         mov     ebx, [.reqdata] ; request data
  833.         call    getaddrinfo._.generate_data
  834.         mov     [.res_list_tail], esi
  835.         pop     esi ecx ebx
  836.         cmp     [.res_list_tail], 0
  837.         jnz     .answers_loop
  838. ; 10f. If generate_data failed (this means memory allocation failure), abort
  839.         jmp     .ret.no_memory
  840. .no_result:
  841.         pop     esi ecx ebx
  842.         jmp     .answers_loop
  843. .got_cname:
  844. ; 10g. Process record CNAME, main host name.
  845.         lea     eax, [esi+6]
  846.         mov     [.cname], eax
  847. .answers_loop.next:
  848. ; 10h. Skip other record fields, advance to next record.
  849.         lodsd   ; skip TTL
  850.         xor     eax, eax
  851.         lodsw   ; get length of RDATA field
  852.         xchg    al, ah  ; network -> Intel byte order
  853.         add     esi, eax
  854.         cmp     esi, ecx
  855.         ja      .ret.no_recovery
  856.         jmp     .answers_loop
  857. .answers_done:
  858. ; 11. Check that there is at least 1 answer.
  859.         mov     eax, [.res_list_tail]
  860.         cmp     [.res], eax
  861.         jz      .ret.no_data
  862. ; 12. If canonical name was required, add it now.
  863.         mov     eax, [.reqdata]
  864.         test    byte [eax+__gai_reqdata.flags], AI_CANONNAME
  865.         jz      .no_canon_name
  866. ; 12a. If at least one CNAME record is present, use name from last such record.
  867. ; Otherwise, use name from one of A records.
  868.         mov     esi, [.cname]
  869.         test    esi, esi
  870.         jnz     .has_cname
  871.         mov     esi, [.addrname]
  872. .has_cname:
  873. ; 12b. Calculate name length.
  874.         call    .get_name_length
  875.         jc      .ret.no_recovery
  876. ; 12c. Check that the caller really want to get data.
  877.         cmp     [.res], 0
  878.         jz      .no_canon_name
  879. ; 12d. Allocate memory for name.
  880.         call    getaddrinfo._.memalloc
  881.         test    edi, edi
  882.         jz      .ret.no_memory
  883. ; 12e. Make first entry in .res list point to canonical name.
  884.         mov     eax, [.res]
  885.         mov     eax, [eax]
  886.         mov     [eax+addrinfo.ai_canonname], edi
  887. ; 12f. Decode name.
  888.         call    .decode_name
  889. .no_canon_name:
  890. ; 13. Set status to success.
  891.         xor     eax, eax
  892.         jmp     .ret.close
  893. ; Handle errors.
  894. .ret.more_processing_required:
  895.         push    -1
  896.         pop     eax
  897.         jmp     .ret
  898. .ret.no_recovery:
  899.         push    EAI_FAIL
  900.         pop     eax
  901.         jmp     .ret.destroy
  902. .ret.no_memory:
  903.         push    EAI_MEMORY
  904.         pop     eax
  905.         jmp     .ret.destroy
  906. .ret.no_name:
  907. .ret.no_data:
  908.         push    EAI_NONAME
  909.         pop     eax
  910. .ret.destroy:
  911. ; 14. If an error occured, free memory acquired so far.
  912.         push    eax
  913.         mov     esi, [.res]
  914.         test    esi, esi
  915.         jz      @f
  916.         pushd   [esi]
  917.         call    freeaddrinfo
  918.         and     dword [esi], 0
  919. @@:
  920.         pop     eax
  921. .ret.close:
  922. ; 15. Close socketnum.
  923.         push    eax
  924.         mov     ecx, [.reqdata]
  925.         mov     ecx, [ecx+__gai_reqdata.socketnum]
  926.         mcall   74, 1
  927.         pop     eax
  928. ; 16. Restore used registers, destroy stack frame and return.
  929. .ret:
  930.         pop     edi esi ebx
  931.         mov     esp, ebp
  932.         pop     ebp
  933.         ret     8
  934.  
  935. ;;===========================================================================;;
  936. ;; Internal auxiliary function for skipping names in DNS packet.             ;;
  937. .skip_name:                                                                  ;;
  938. ;;---------------------------------------------------------------------------;;
  939. ;? Skips name in DNS packet.                                                 ;;
  940. ;;---------------------------------------------------------------------------;;
  941. ;> esi -> name                                                               ;;
  942. ;> ecx = end of packet                                                       ;;
  943. ;;---------------------------------------------------------------------------;;
  944. ;< esi -> end of name                                                        ;;
  945. ;;===========================================================================;;
  946.         xor     eax, eax
  947.         cmp     esi, ecx
  948.         jae     .skip_name.done
  949.         lodsb
  950.         test    al, al
  951.         jz      .skip_name.done
  952.         test    al, 0xC0
  953.         jnz     .skip_name.pointer
  954.         add     esi, eax
  955.         jmp     .skip_name
  956. .skip_name.pointer:
  957.         inc     esi
  958. .skip_name.done:
  959.         ret
  960.  
  961. ;;===========================================================================;;
  962. ;; Internal auxiliary function for calculating length of name in DNS packet. ;;
  963. .get_name_length:                                                            ;;
  964. ;;---------------------------------------------------------------------------;;
  965. ;? Calculate length of name (including terminating zero) in DNS packet.      ;;
  966. ;;---------------------------------------------------------------------------;;
  967. ;> edx = start of packet                                                     ;;
  968. ;> esi -> name                                                               ;;
  969. ;> ecx = end of packet                                                       ;;
  970. ;;---------------------------------------------------------------------------;;
  971. ;< eax = length of name                                                      ;;
  972. ;< CF set on error / cleared on success                                      ;;
  973. ;;===========================================================================;;
  974.         xor     ebx, ebx        ; ebx will hold data length
  975. .get_name_length.zero:
  976.         xor     eax, eax
  977. .get_name_length.loop:
  978.         cmp     esi, ecx
  979.         jae     .get_name_length.fail
  980.         lodsb
  981.         test    al, al
  982.         jz      .get_name_length.done
  983.         test    al, 0xC0
  984.         jnz     .get_name_length.pointer
  985.         add     esi, eax
  986.         inc     ebx
  987.         add     ebx, eax
  988.         cmp     ebx, 256
  989.         jbe     .get_name_length.loop
  990. .get_name_length.fail:
  991.         stc
  992.         ret
  993. .get_name_length.pointer:
  994.         and     al, 0x3F
  995.         mov     ah, al
  996.         lodsb
  997.         lea     esi, [edx+eax]
  998.         jmp     .get_name_length.zero
  999. .get_name_length.done:
  1000.         test    ebx, ebx
  1001.         jz      .get_name_length.fail
  1002.         xchg    eax, ebx
  1003.         clc
  1004.         ret
  1005.  
  1006. ;;===========================================================================;;
  1007. ;; Internal auxiliary function for decoding DNS name.                        ;;
  1008. .decode_name:                                                                ;;
  1009. ;;---------------------------------------------------------------------------;;
  1010. ;? Decode name in DNS packet.                                                ;;
  1011. ;;---------------------------------------------------------------------------;;
  1012. ;> edx = start of packet                                                     ;;
  1013. ;> esi -> name in packet                                                     ;;
  1014. ;> edi -> buffer for decoded name                                            ;;
  1015. ;;===========================================================================;;
  1016.         xor     eax, eax
  1017.         lodsb
  1018.         test    al, al
  1019.         jz      .decode_name.done
  1020.         test    al, 0xC0
  1021.         jnz     .decode_name.pointer
  1022.         mov     ecx, eax
  1023.         rep     movsb
  1024.         mov     al, '.'
  1025.         stosb
  1026.         jmp     .decode_name
  1027. .decode_name.pointer:
  1028.         and     al, 0x3F
  1029.         mov     ah, al
  1030.         lodsb
  1031.         lea     esi, [edx+eax]
  1032.         jmp     .decode_name
  1033. .decode_name.done:
  1034.         mov     byte [edi-1], 0
  1035.         ret
  1036.  
  1037. ;;===========================================================================;;
  1038. ;; Internal auxiliary function for allocating memory for getaddrinfo.        ;;
  1039. getaddrinfo._.memalloc:                                                      ;;
  1040. ;;---------------------------------------------------------------------------;;
  1041. ;? Memory allocation.                                                        ;;
  1042. ;;---------------------------------------------------------------------------;;
  1043. ;> eax = size in bytes, must be less than page size.                         ;;
  1044. ;> [ebp-4] = .recent_page = last allocated page                              ;;
  1045. ;> [ebp-8] = .recent_restsize = bytes rest in last allocated page            ;;
  1046. ;;---------------------------------------------------------------------------;;
  1047. ;< edi -> allocated memory / NULL on error                                   ;;
  1048. ;;===========================================================================;;
  1049. ; 1. Set edi to result of function.
  1050.         mov     edi, [ebp-4]
  1051. ; 2. Check whether we need to allocate a new page.
  1052.         cmp     eax, [ebp-8]
  1053.         jbe     .no_new_page
  1054. ; 2. Allocate new page if need. Reset edi to new result.
  1055.         push    eax ebx
  1056.         mcall   68, 12, 0x1000
  1057.         xchg    edi, eax        ; put result to edi
  1058.         pop     ebx eax
  1059. ; 3. Check returned value of allocator. Fail if it failed.
  1060.         test    edi, edi
  1061.         jz      .ret
  1062. ; 4. Update .recent_page and .recent_restsize.
  1063.         add     edi, 4
  1064.         sub     ecx, 4
  1065.         mov     [ebp-4], edi
  1066.         mov     [ebp-8], ecx
  1067. .no_new_page:
  1068. ; 5. Increase number of objects on this page.
  1069.         push    eax
  1070.         mov     eax, edi
  1071.         and     eax, not 0xFFF
  1072.         inc     dword [eax]
  1073.         pop     eax
  1074. ; 6. Advance last allocated pointer, decrease memory size.
  1075.         add     [ebp-4], eax
  1076.         sub     [ebp-8], eax
  1077. ; 7. Return.
  1078. .ret:
  1079.         ret
  1080.  
  1081. ;;===========================================================================;;
  1082. ;; Internal auxiliary function for freeing memory for freeaddrinfo.          ;;
  1083. getaddrinfo._.memfree:                                                       ;;
  1084. ;;---------------------------------------------------------------------------;;
  1085. ;? Free memory.                                                              ;;
  1086. ;;---------------------------------------------------------------------------;;
  1087. ;> eax = pointer                                                             ;;
  1088. ;;===========================================================================;;
  1089. ; 1. Get start of page.
  1090.         mov     ecx, eax
  1091.         and     ecx, not 0xFFF
  1092. ; 2. Decrease number of objects.
  1093.         dec     dword [ecx]
  1094. ; 3. If it goes to zero, free the page.
  1095.         jnz     @f
  1096.         push    ebx
  1097.         mcall   68, 13
  1098.         pop     ebx
  1099. @@:
  1100. ; 4. Done.
  1101.         ret
  1102.  
  1103. ;;===========================================================================;;
  1104. getaddrinfo._.generate_data:                                                 ;;
  1105. ;;---------------------------------------------------------------------------;;
  1106. ;? Generate item(s) of getaddrinfo result list by one IP address.            ;;
  1107. ;;---------------------------------------------------------------------------;;
  1108. ;> eax = IP address                                                          ;;
  1109. ;> ebx = request data                                                        ;;
  1110. ;> esi = pointer to result                                                   ;;
  1111. ;> [ebp-4] = .recent_page = last allocated page                              ;;
  1112. ;> [ebp-8] = .recent_restsize = bytes rest in last allocated page            ;;
  1113. ;;---------------------------------------------------------------------------;;
  1114. ;< esi = pointer to next list item for result / NULL on error                ;;
  1115. ;;===========================================================================;;
  1116. ; 1. If no service is given, append one item with zero port.
  1117. ; append one item with zero socktype/protocol/port.
  1118.         cmp     [ebx+__gai_reqdata.service], -1
  1119.         jnz     .has_service
  1120.         call    .append_item
  1121. ; 1a. If neither protocol nor socktype were specified,
  1122. ;       leave zeroes in socktype and protocol.
  1123.         mov     cl, [ebx+__gai_reqdata.socktype]
  1124.         test    cl, cl
  1125.         jz      .no_socktype
  1126. ; 1b. Otherwise, set socktype and protocol to desired.
  1127.         call    .set_socktype
  1128. .no_socktype:
  1129.         ret
  1130. .has_service:
  1131. ; 2. If TCP is allowed, append item for TCP.
  1132.         cmp     [ebx+__gai_reqdata.socktype], 0
  1133.         jz      .tcp_ok
  1134.         cmp     [ebx+__gai_reqdata.socktype], SOCK_STREAM
  1135.         jnz     .tcp_disallowed
  1136. .tcp_ok:
  1137.         call    .append_item
  1138.         mov     cl, SOCK_STREAM
  1139.         call    .set_socktype
  1140.         call    .set_port
  1141. .tcp_disallowed:
  1142. ; 3. If UDP is allowed, append item for UDP.
  1143.         cmp     [ebx+__gai_reqdata.socktype], 0
  1144.         jz      .udp_ok
  1145.         cmp     [ebx+__gai_reqdata.socktype], SOCK_DGRAM
  1146.         jnz     .udp_disallowed
  1147. .udp_ok:
  1148.         call    .append_item
  1149.         mov     cl, SOCK_DGRAM
  1150.         call    .set_socktype
  1151.         call    .set_port
  1152. .udp_disallowed:
  1153.         ret
  1154.  
  1155. .append_item:
  1156. ; 1. Allocate memory for struct sockaddr_in and struct addrinfo.
  1157.         push    eax
  1158.         push    sizeof.addrinfo + sizeof.sockaddr_in
  1159.         pop     eax
  1160.         call    getaddrinfo._.memalloc
  1161. ; 2. Check for memory allocation fail.
  1162.         test    edi, edi
  1163.         jz      .no_memory
  1164. ; 3. Zero allocated memory.
  1165.         push    (sizeof.addrinfo + sizeof.sockaddr_in) / 4
  1166.         pop     ecx
  1167.         xor     eax, eax
  1168.         push    edi
  1169.         rep     stosd
  1170.         pop     edi
  1171. ; 4. Fill struct addrinfo.
  1172.         mov     eax, [ebx+__gai_reqdata.flags]
  1173.         mov     [edi+addrinfo.ai_flags], eax
  1174.         mov     byte [edi+addrinfo.ai_family], AF_INET4
  1175.         mov     byte [edi+addrinfo.ai_addrlen], sizeof.sockaddr_in
  1176.         lea     ecx, [edi+sizeof.addrinfo]
  1177.         mov     [edi+addrinfo.ai_addr], ecx
  1178. ; 5. Fill struct sockaddr_in.
  1179.         mov     byte [ecx+sockaddr_in.sin_family], AF_INET4
  1180.         pop     eax
  1181.         mov     [ecx+sockaddr_in.sin_addr], eax
  1182. ; 6. Append new item to the list.
  1183.         mov     [esi], edi
  1184.         lea     esi, [edi+addrinfo.ai_next]
  1185. ; 7. Return.
  1186.         ret
  1187. .no_memory:
  1188.         pop     eax
  1189.         xor     esi, esi
  1190.         ret
  1191.  
  1192. .set_socktype:
  1193. ; Set ai_socktype and ai_protocol fields by given socketnum type.
  1194.         mov     byte [edi+addrinfo.ai_socktype], cl
  1195.         dec     cl
  1196.         jnz     .set_udp
  1197. .set_tcp:
  1198.         mov     byte [edi+addrinfo.ai_protocol], IPPROTO_TCP
  1199.         ret
  1200. .set_udp:
  1201.         mov     byte [edi+addrinfo.ai_protocol], IPPROTO_UDP
  1202.         ret
  1203.  
  1204. .set_port:
  1205. ; Just copy port from input __gai_reqdata to output addrinfo.
  1206.         push    edx
  1207.         mov     edx, [ebx+__gai_reqdata.service]
  1208.         xchg    dl, dh  ; convert to network byte order
  1209.         mov     [edi+sizeof.addrinfo+sockaddr_in.sin_port], dx
  1210.         pop     edx
  1211.         ret
  1212.  
  1213. ;;===========================================================================;;
  1214. ;; void __stdcall getaddrinfo_abort(__in struct __gai_reqdata* reqdata);      ;;
  1215. getaddrinfo_abort:                                                           ;;
  1216. ;;---------------------------------------------------------------------------;;
  1217. ;? Abort process started by getaddrinfo_start, free all resources.           ;;
  1218. ;;---------------------------------------------------------------------------;;
  1219. ;> first parameter = pointer to struct __gai_reqdata filled by ..._start     ;;
  1220. ;;===========================================================================;;
  1221. ; 0. Save used registers for __stdcall.
  1222.         push    ebx
  1223. ; 1. Allocated resources: only socketnum, so close it and return.
  1224.         mov     eax, [esp+8]
  1225.         mov     ecx, [eax+__gai_reqdata.socketnum]
  1226.         mcall   74, 1
  1227. ; 2. Restore used registers and return.
  1228.         pop     ebx
  1229.         ret     4
  1230.  
  1231. ;;===========================================================================;;
  1232. ;; void __stdcall freeaddrinfo(__in struct addrinfo* ai);                    ;;
  1233. freeaddrinfo:                                                                ;;
  1234. ;;---------------------------------------------------------------------------;;
  1235. ;? Free one or more addrinfo structures returned by getaddrinfo.             ;;
  1236. ;;---------------------------------------------------------------------------;;
  1237. ;> first parameter = head of list of structures                              ;;
  1238. ;                    (may be arbitrary sublist of original)                  ;;
  1239. ;;===========================================================================;;
  1240. ; 1. Loop for all items in the list.
  1241.         mov     edx, [esp+4]    ; eax = ai
  1242. .loop:
  1243.         test    edx, edx
  1244.         jz      .done
  1245. ; 2. Free each item.
  1246. ; 2a. Free ai_canonname, if allocated.
  1247.         mov     eax, [edx+addrinfo.ai_canonname]
  1248.         test    eax, eax
  1249.         jz      .no_canon_name
  1250.         call    getaddrinfo._.memfree
  1251. .no_canon_name:
  1252. ; 2b. Remember next item
  1253. ;       (after freeing the field ai_next can became unavailable).
  1254.         pushd   [edx+addrinfo.ai_next]
  1255. ; 2c. Free item itself.
  1256.         xchg    eax, edx
  1257.         call    getaddrinfo._.memfree
  1258. ; 2d. Restore pointer to next item and continue loop.
  1259.         pop     edx
  1260.         jmp     .loop
  1261. .done:
  1262. ; 3. Done.
  1263.         ret     4
  1264.  
  1265. ;;===========================================================================;;
  1266. ;;///////////////////////////////////////////////////////////////////////////;;
  1267. ;;===========================================================================;;
  1268. ;! Exported functions section                                                ;;
  1269. ;;===========================================================================;;
  1270. ;;///////////////////////////////////////////////////////////////////////////;;
  1271. ;;===========================================================================;;
  1272.  
  1273.  
  1274. align 4
  1275. @EXPORT:
  1276. export  \
  1277.         lib_init                , 'lib_init'            , \
  1278.         0x00010001              , 'version'             , \
  1279.         inet_addr               , 'inet_addr'           , \
  1280.         inet_ntoa               , 'inet_ntoa'           , \
  1281.         getaddrinfo             , 'getaddrinfo'         , \
  1282.         getaddrinfo_start       , 'getaddrinfo_start'   , \
  1283.         getaddrinfo_process     , 'getaddrinfo_process' , \
  1284.         getaddrinfo_abort       , 'getaddrinfo_abort'   , \
  1285.         freeaddrinfo            , 'freeaddrinfo'
  1286.  
  1287. ; import from libini
  1288. align 4
  1289. @IMPORT:
  1290.  
  1291. library libini, 'libini.obj'
  1292. import  libini, \
  1293.         ini.get_str, 'ini_get_str',     \
  1294.         ini.get_int, 'ini_get_int'
  1295.  
  1296.  
  1297. section '.data' data readable writable align 16
  1298. ; uninitialized data
  1299. mem.alloc   dd ?
  1300. mem.free    dd ?
  1301. mem.realloc dd ?
  1302. dll.load    dd ?
  1303.  
  1304. DNSrequestID    dd      ?
  1305.  
  1306. inet_ntoa.buffer        rb      16      ; static buffer for inet_ntoa
  1307.