Subversion Repositories Kolibri OS

Rev

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