Subversion Repositories Kolibri OS

Rev

Rev 6720 | 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 socket 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.         jz      .timeout
  315. ; 3. Got packet. Call processing function.
  316.         lea     eax, [esp+4]
  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.         lea     eax, [esp+4]    ; pointer to (1)
  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   76, API_IPv4 + (1 shl 8) + 4 ; protocol IP=0, device number=0, function=get DNS address
  680.         cmp     eax, -1
  681.         je      .ret.dnserr
  682.         push    eax             ; save server address to the stack
  683. ; 8. Open UDP socketnum to DNS server, port 53.
  684. ; 8a. Create new socketnum.
  685.         mcall   75, 0, AF_INET4, SOCK_DGRAM, 0
  686.         pop     esi             ; restore server address saved at step 7
  687.         cmp     eax, -1 ; error?
  688.         jz      .ret.dnserr
  689.         mov     ecx, eax        ; put socketnum handle to ecx
  690. ; 8b. Create sockaddr structure on the stack.
  691.         push    0
  692.         push    0       ; sin_zero
  693.         push    esi     ; sin_addr
  694.         push    AF_INET4 + (53 shl 24)
  695.                         ; sin_family and sin_port in network byte order
  696. ; 8c. Connect.
  697.         mcall   75, 4, , esp, sizeof.sockaddr_in
  698. ; 8d. Restore the stack, undo 8b.
  699.         add     esp, esi
  700. ; 8e. Check result.
  701.         cmp     eax, -1
  702.         jz      .ret.close
  703. ; 9. Send DNS request packet.
  704.         sub     edi, esp        ; get packet length
  705.         mov     esi, edi
  706.         xor     edi, edi
  707.         mcall   75, 6, , esp
  708.         cmp     eax, -1
  709.         jz      .ret.close
  710.         mov     eax, [.reqdata]
  711.         mov     [eax+__gai_reqdata.socketnum], ecx
  712.         push    -1
  713.         pop     eax     ; return status: more processing required
  714.         jmp     .ret.dns
  715. .ret.close:
  716.         mcall   75, 1
  717. .ret.dnserr:
  718.         push    EAI_AGAIN
  719.         pop     eax
  720. .ret.dns:
  721. ; 6. Restore stack pointer and return.
  722.         jmp     .ret
  723.  
  724. ;;===========================================================================;;
  725. ;; int __stdcall getaddrinfo_process(__in struct __gai_reqdata* reqdata,     ;;
  726. ;;                                   __out struct addrinfo** res);           ;;
  727. getaddrinfo_process:                                                         ;;
  728. ;;---------------------------------------------------------------------------;;
  729. ;? Processes network events from DNS reply                                   ;;
  730. ;;---------------------------------------------------------------------------;;
  731. ;> first parameter = pointer to struct __gai_reqdata filled by ..._start     ;;
  732. ;> second parameter = same as for getaddrinfo                                ;;
  733. ;;---------------------------------------------------------------------------;;
  734. ;< eax = -1 if more processing required / 0 on success / >0 = error code     ;;
  735. ;;===========================================================================;;
  736. ; 0. Create stack frame.
  737.         push    ebp
  738.         mov     ebp, esp
  739. virtual at ebp-.locals_size
  740. .locals_start:
  741. .datagram       rb      512
  742. .addrname       dd      ?
  743. .name           dd      ?
  744. .res_list_tail  dd      ?
  745. .cname          dd      ?
  746. .recent_restsize dd     ?       ; this is for memory alloc in ._.generate_data
  747. .recent_page    dd      ?       ; this is for memory alloc in ._.generate_data
  748. .locals_size = $ - .locals_start
  749.                 rd      2
  750. .reqdata        dd      ?
  751. .res            dd      ?
  752. end virtual
  753.         xor     eax, eax
  754.         push    eax     ; initialize .recent_page
  755.         push    eax     ; initialize .recent_restsize
  756.         push    eax     ; initialize .cname
  757.         push    [.res]  ; initialize .res_list_tail
  758.         sub     esp, .locals_size-16    ; reserve place for other vars
  759.         mov     edx, esp        ; edx -> buffer for datagram
  760. ; 1. Save used registers for __stdcall.
  761.         push    ebx esi edi
  762.         mov     edi, [.reqdata]
  763. ; 2. Read UDP datagram.
  764.         mov     ecx, [edi+__gai_reqdata.socketnum]
  765.         push    edi
  766.         mcall   75, 7, , , 512, MSG_DONTWAIT
  767.         pop     edi
  768. ; 3. Check for socket errors
  769.         cmp     eax, -1
  770.         jne     @f
  771.         cmp     ebx, EWOULDBLOCK
  772.         je      .ret.more_processing_required
  773.         jmp     .ret.no_recovery
  774.   @@:
  775. ; 4. Sanity check: discard too short packets.
  776.         xchg    ecx, eax        ; save packet length in ecx
  777.         cmp     ecx, 12
  778.         jb      .ret.more_processing_required
  779. ; 5. Discard packets with ID != request ID.
  780.         mov     eax, dword [edi+__gai_reqdata.reqid]
  781.         cmp     ax, [edx]
  782.         jnz     .ret.more_processing_required
  783. ; 6. Sanity check: discard query packets.
  784.         test    byte [edx+2], 80h
  785.         jz      .ret.more_processing_required
  786. ; 7. Sanity check: must be exactly one query (our).
  787.         cmp     word [edx+4], 0x0100    ; note network byte order
  788.         jnz     .ret.more_processing_required
  789. ; 8. Check for errors. Return EAI_NONAME for error code 3 and EAI_FAIL for other.
  790.         mov     al, [edx+3]
  791.         and     al, 0xF
  792.         jz      @f
  793.         cmp     al, 3
  794.         jnz     .ret.no_recovery
  795.         jmp     .ret.no_name
  796. @@:
  797. ; 9. Locate answers section. Exactly 1 query is present in this packet.
  798.         add     ecx, edx        ; ecx = limit
  799.         lea     esi, [edx+12]
  800.         call    .skip_name
  801.         lodsd           ; skip QTYPE and QCLASS field
  802.         cmp     esi, ecx
  803.         ja      .ret.no_recovery
  804. ; 10. Loop through all answers.
  805.         movzx   ebx, word [edx+6]       ; get answers count
  806.         xchg    bl, bh          ; network -> Intel byte order
  807. .answers_loop:
  808.         dec     ebx
  809.         js      .answers_done
  810. ; 10a. Process each record.
  811.         mov     [.name], esi
  812. ; 10b. Skip name field.
  813.         call    .skip_name
  814. ; 10c. Get record information, handle two types for class IN (internet).
  815.         lodsd           ; get type and class
  816.         cmp     esi, ecx
  817.         ja      .ret.no_recovery
  818.         cmp     eax, 0x01000500 ; type=5, class=1?
  819.         jz      .got_cname
  820.         cmp     eax, 0x01000100 ; type=1, class=1?
  821.         jnz     .answers_loop.next
  822. .got_addr:
  823. ; 10d. Process record A, host address.
  824.         add     esi, 10
  825.         cmp     esi, ecx
  826.         ja      .ret.no_recovery
  827.         cmp     word [esi-6], 0x0400    ; RDATA for A records must be 4 bytes long
  828.         jnz     .ret.no_recovery
  829.         mov     eax, [.name]
  830.         mov     [.addrname], eax
  831. ; 10e. Create corresponding record in the answer.
  832.         push    ebx ecx esi
  833.         mov     eax, [esi-4]    ; IP address
  834.         mov     esi, [.res_list_tail]   ; pointer to result
  835.         test    esi, esi
  836.         jz      .no_result      ; do not save if .res is NULL
  837.         mov     ebx, [.reqdata] ; request data
  838.         call    getaddrinfo._.generate_data
  839.         mov     [.res_list_tail], esi
  840.         pop     esi ecx ebx
  841.         cmp     [.res_list_tail], 0
  842.         jnz     .answers_loop
  843. ; 10f. If generate_data failed (this means memory allocation failure), abort
  844.         jmp     .ret.no_memory
  845. .no_result:
  846.         pop     esi ecx ebx
  847.         jmp     .answers_loop
  848. .got_cname:
  849. ; 10g. Process record CNAME, main host name.
  850.         lea     eax, [esi+6]
  851.         mov     [.cname], eax
  852. .answers_loop.next:
  853. ; 10h. Skip other record fields, advance to next record.
  854.         lodsd   ; skip TTL
  855.         xor     eax, eax
  856.         lodsw   ; get length of RDATA field
  857.         xchg    al, ah  ; network -> Intel byte order
  858.         add     esi, eax
  859.         cmp     esi, ecx
  860.         ja      .ret.no_recovery
  861.         jmp     .answers_loop
  862. .answers_done:
  863. ; 11. Check that there is at least 1 answer.
  864.         mov     eax, [.res_list_tail]
  865.         cmp     [.res], eax
  866.         jz      .ret.no_data
  867. ; 12. If canonical name was required, add it now.
  868.         mov     eax, [.reqdata]
  869.         test    byte [eax+__gai_reqdata.flags], AI_CANONNAME
  870.         jz      .no_canon_name
  871. ; 12a. If at least one CNAME record is present, use name from last such record.
  872. ; Otherwise, use name from one of A records.
  873.         mov     esi, [.cname]
  874.         test    esi, esi
  875.         jnz     .has_cname
  876.         mov     esi, [.addrname]
  877. .has_cname:
  878. ; 12b. Calculate name length.
  879.         call    .get_name_length
  880.         jc      .ret.no_recovery
  881. ; 12c. Check that the caller really want to get data.
  882.         cmp     [.res], 0
  883.         jz      .no_canon_name
  884. ; 12d. Allocate memory for name.
  885.         call    getaddrinfo._.memalloc
  886.         test    edi, edi
  887.         jz      .ret.no_memory
  888. ; 12e. Make first entry in .res list point to canonical name.
  889.         mov     eax, [.res]
  890.         mov     eax, [eax]
  891.         mov     [eax+addrinfo.ai_canonname], edi
  892. ; 12f. Decode name.
  893.         call    .decode_name
  894. .no_canon_name:
  895. ; 13. Set status to success.
  896.         xor     eax, eax
  897.         jmp     .ret.close
  898. ; Handle errors.
  899. .ret.more_processing_required:
  900.         push    -1
  901.         pop     eax
  902.         jmp     .ret
  903. .ret.no_recovery:
  904.         push    EAI_FAIL
  905.         pop     eax
  906.         jmp     .ret.destroy
  907. .ret.no_memory:
  908.         push    EAI_MEMORY
  909.         pop     eax
  910.         jmp     .ret.destroy
  911. .ret.no_name:
  912. .ret.no_data:
  913.         push    EAI_NONAME
  914.         pop     eax
  915. .ret.destroy:
  916. ; 14. If an error occured, free memory acquired so far.
  917.         push    eax
  918.         mov     esi, [.res]
  919.         test    esi, esi
  920.         jz      @f
  921.         pushd   [esi]
  922.         call    freeaddrinfo
  923.         and     dword [esi], 0
  924. @@:
  925.         pop     eax
  926. .ret.close:
  927. ; 15. Close socketnum.
  928.         push    eax
  929.         mov     ecx, [.reqdata]
  930.         mov     ecx, [ecx+__gai_reqdata.socketnum]
  931.         mcall   75, 1
  932.         pop     eax
  933. ; 16. Restore used registers, destroy stack frame and return.
  934. .ret:
  935.         pop     edi esi ebx
  936.         mov     esp, ebp
  937.         pop     ebp
  938.         ret     8
  939.  
  940. ;;===========================================================================;;
  941. ;; Internal auxiliary function for skipping names in DNS packet.             ;;
  942. .skip_name:                                                                  ;;
  943. ;;---------------------------------------------------------------------------;;
  944. ;? Skips name in DNS packet.                                                 ;;
  945. ;;---------------------------------------------------------------------------;;
  946. ;> esi -> name                                                               ;;
  947. ;> ecx = end of packet                                                       ;;
  948. ;;---------------------------------------------------------------------------;;
  949. ;< esi -> end of name                                                        ;;
  950. ;;===========================================================================;;
  951.         xor     eax, eax
  952.         cmp     esi, ecx
  953.         jae     .skip_name.done
  954.         lodsb
  955.         test    al, al
  956.         jz      .skip_name.done
  957.         test    al, 0xC0
  958.         jnz     .skip_name.pointer
  959.         add     esi, eax
  960.         jmp     .skip_name
  961. .skip_name.pointer:
  962.         inc     esi
  963. .skip_name.done:
  964.         ret
  965.  
  966. ;;===========================================================================;;
  967. ;; Internal auxiliary function for calculating length of name in DNS packet. ;;
  968. .get_name_length:                                                            ;;
  969. ;;---------------------------------------------------------------------------;;
  970. ;? Calculate length of name (including terminating zero) in DNS packet.      ;;
  971. ;;---------------------------------------------------------------------------;;
  972. ;> edx = start of packet                                                     ;;
  973. ;> esi -> name                                                               ;;
  974. ;> ecx = end of packet                                                       ;;
  975. ;;---------------------------------------------------------------------------;;
  976. ;< eax = length of name                                                      ;;
  977. ;< CF set on error / cleared on success                                      ;;
  978. ;;===========================================================================;;
  979.         xor     ebx, ebx        ; ebx will hold data length
  980. .get_name_length.zero:
  981.         xor     eax, eax
  982. .get_name_length.loop:
  983.         cmp     esi, ecx
  984.         jae     .get_name_length.fail
  985.         lodsb
  986.         test    al, al
  987.         jz      .get_name_length.done
  988.         test    al, 0xC0
  989.         jnz     .get_name_length.pointer
  990.         add     esi, eax
  991.         inc     ebx
  992.         add     ebx, eax
  993.         cmp     ebx, 256
  994.         jbe     .get_name_length.loop
  995. .get_name_length.fail:
  996.         stc
  997.         ret
  998. .get_name_length.pointer:
  999.         and     al, 0x3F
  1000.         mov     ah, al
  1001.         lodsb
  1002.         lea     esi, [edx+eax]
  1003.         jmp     .get_name_length.zero
  1004. .get_name_length.done:
  1005.         test    ebx, ebx
  1006.         jz      .get_name_length.fail
  1007.         xchg    eax, ebx
  1008.         clc
  1009.         ret
  1010.  
  1011. ;;===========================================================================;;
  1012. ;; Internal auxiliary function for decoding DNS name.                        ;;
  1013. .decode_name:                                                                ;;
  1014. ;;---------------------------------------------------------------------------;;
  1015. ;? Decode name in DNS packet.                                                ;;
  1016. ;;---------------------------------------------------------------------------;;
  1017. ;> edx = start of packet                                                     ;;
  1018. ;> esi -> name in packet                                                     ;;
  1019. ;> edi -> buffer for decoded name                                            ;;
  1020. ;;===========================================================================;;
  1021.         xor     eax, eax
  1022.         lodsb
  1023.         test    al, al
  1024.         jz      .decode_name.done
  1025.         test    al, 0xC0
  1026.         jnz     .decode_name.pointer
  1027.         mov     ecx, eax
  1028.         rep     movsb
  1029.         mov     al, '.'
  1030.         stosb
  1031.         jmp     .decode_name
  1032. .decode_name.pointer:
  1033.         and     al, 0x3F
  1034.         mov     ah, al
  1035.         lodsb
  1036.         lea     esi, [edx+eax]
  1037.         jmp     .decode_name
  1038. .decode_name.done:
  1039.         mov     byte [edi-1], 0
  1040.         ret
  1041.  
  1042. ;;===========================================================================;;
  1043. ;; Internal auxiliary function for allocating memory for getaddrinfo.        ;;
  1044. getaddrinfo._.memalloc:                                                      ;;
  1045. ;;---------------------------------------------------------------------------;;
  1046. ;? Memory allocation.                                                        ;;
  1047. ;;---------------------------------------------------------------------------;;
  1048. ;> eax = size in bytes, must be less than page size.                         ;;
  1049. ;> [ebp-4] = .recent_page = last allocated page                              ;;
  1050. ;> [ebp-8] = .recent_restsize = bytes rest in last allocated page            ;;
  1051. ;;---------------------------------------------------------------------------;;
  1052. ;< edi -> allocated memory / NULL on error                                   ;;
  1053. ;;===========================================================================;;
  1054. ; 1. Set edi to result of function.
  1055.         mov     edi, [ebp-4]
  1056. ; 2. Check whether we need to allocate a new page.
  1057.         cmp     eax, [ebp-8]
  1058.         jbe     .no_new_page
  1059. ; 2. Allocate new page if need. Reset edi to new result.
  1060.         push    eax ebx
  1061.         mcall   68, 12, 0x1000
  1062.         xchg    edi, eax        ; put result to edi
  1063.         pop     ebx eax
  1064. ; 3. Check returned value of allocator. Fail if it failed.
  1065.         test    edi, edi
  1066.         jz      .ret
  1067. ; 4. Update .recent_page and .recent_restsize.
  1068.         add     edi, 4
  1069.         sub     ecx, 4
  1070.         mov     [ebp-4], edi
  1071.         mov     [ebp-8], ecx
  1072. .no_new_page:
  1073. ; 5. Increase number of objects on this page.
  1074.         push    eax
  1075.         mov     eax, edi
  1076.         and     eax, not 0xFFF
  1077.         inc     dword [eax]
  1078.         pop     eax
  1079. ; 6. Advance last allocated pointer, decrease memory size.
  1080.         add     [ebp-4], eax
  1081.         sub     [ebp-8], eax
  1082. ; 7. Return.
  1083. .ret:
  1084.         ret
  1085.  
  1086. ;;===========================================================================;;
  1087. ;; Internal auxiliary function for freeing memory for freeaddrinfo.          ;;
  1088. getaddrinfo._.memfree:                                                       ;;
  1089. ;;---------------------------------------------------------------------------;;
  1090. ;? Free memory.                                                              ;;
  1091. ;;---------------------------------------------------------------------------;;
  1092. ;> eax = pointer                                                             ;;
  1093. ;;===========================================================================;;
  1094. ; 1. Get start of page.
  1095.         mov     ecx, eax
  1096.         and     ecx, not 0xFFF
  1097. ; 2. Decrease number of objects.
  1098.         dec     dword [ecx]
  1099. ; 3. If it goes to zero, free the page.
  1100.         jnz     @f
  1101.         push    ebx
  1102.         mcall   68, 13
  1103.         pop     ebx
  1104. @@:
  1105. ; 4. Done.
  1106.         ret
  1107.  
  1108. ;;===========================================================================;;
  1109. getaddrinfo._.generate_data:                                                 ;;
  1110. ;;---------------------------------------------------------------------------;;
  1111. ;? Generate item(s) of getaddrinfo result list by one IP address.            ;;
  1112. ;;---------------------------------------------------------------------------;;
  1113. ;> eax = IP address                                                          ;;
  1114. ;> ebx = request data                                                        ;;
  1115. ;> esi = pointer to result                                                   ;;
  1116. ;> [ebp-4] = .recent_page = last allocated page                              ;;
  1117. ;> [ebp-8] = .recent_restsize = bytes rest in last allocated page            ;;
  1118. ;;---------------------------------------------------------------------------;;
  1119. ;< esi = pointer to next list item for result / NULL on error                ;;
  1120. ;;===========================================================================;;
  1121. ; 1. If no service is given, append one item with zero port.
  1122. ; append one item with zero socktype/protocol/port.
  1123.         cmp     [ebx+__gai_reqdata.service], -1
  1124.         jnz     .has_service
  1125.         call    .append_item
  1126. ; 1a. If neither protocol nor socktype were specified,
  1127. ;       leave zeroes in socktype and protocol.
  1128.         mov     cl, [ebx+__gai_reqdata.socktype]
  1129.         test    cl, cl
  1130.         jz      .no_socktype
  1131. ; 1b. Otherwise, set socktype and protocol to desired.
  1132.         call    .set_socktype
  1133. .no_socktype:
  1134.         ret
  1135. .has_service:
  1136. ; 2. If TCP is allowed, append item for TCP.
  1137.         cmp     [ebx+__gai_reqdata.socktype], 0
  1138.         jz      .tcp_ok
  1139.         cmp     [ebx+__gai_reqdata.socktype], SOCK_STREAM
  1140.         jnz     .tcp_disallowed
  1141. .tcp_ok:
  1142.         call    .append_item
  1143.         mov     cl, SOCK_STREAM
  1144.         call    .set_socktype
  1145.         call    .set_port
  1146. .tcp_disallowed:
  1147. ; 3. If UDP is allowed, append item for UDP.
  1148.         cmp     [ebx+__gai_reqdata.socktype], 0
  1149.         jz      .udp_ok
  1150.         cmp     [ebx+__gai_reqdata.socktype], SOCK_DGRAM
  1151.         jnz     .udp_disallowed
  1152. .udp_ok:
  1153.         call    .append_item
  1154.         mov     cl, SOCK_DGRAM
  1155.         call    .set_socktype
  1156.         call    .set_port
  1157. .udp_disallowed:
  1158.         ret
  1159.  
  1160. .append_item:
  1161. ; 1. Allocate memory for struct sockaddr_in and struct addrinfo.
  1162.         push    eax
  1163.         push    sizeof.addrinfo + sizeof.sockaddr_in
  1164.         pop     eax
  1165.         call    getaddrinfo._.memalloc
  1166. ; 2. Check for memory allocation fail.
  1167.         test    edi, edi
  1168.         jz      .no_memory
  1169. ; 3. Zero allocated memory.
  1170.         push    (sizeof.addrinfo + sizeof.sockaddr_in) / 4
  1171.         pop     ecx
  1172.         xor     eax, eax
  1173.         push    edi
  1174.         rep     stosd
  1175.         pop     edi
  1176. ; 4. Fill struct addrinfo.
  1177.         mov     eax, [ebx+__gai_reqdata.flags]
  1178.         mov     [edi+addrinfo.ai_flags], eax
  1179.         mov     byte [edi+addrinfo.ai_family], AF_INET4
  1180.         mov     byte [edi+addrinfo.ai_addrlen], sizeof.sockaddr_in
  1181.         lea     ecx, [edi+sizeof.addrinfo]
  1182.         mov     [edi+addrinfo.ai_addr], ecx
  1183. ; 5. Fill struct sockaddr_in.
  1184.         mov     byte [ecx+sockaddr_in.sin_family], AF_INET4
  1185.         pop     eax
  1186.         mov     [ecx+sockaddr_in.sin_addr], eax
  1187. ; 6. Append new item to the list.
  1188.         mov     [esi], edi
  1189.         lea     esi, [edi+addrinfo.ai_next]
  1190. ; 7. Return.
  1191.         ret
  1192. .no_memory:
  1193.         pop     eax
  1194.         xor     esi, esi
  1195.         ret
  1196.  
  1197. .set_socktype:
  1198. ; Set ai_socktype and ai_protocol fields by given socketnum type.
  1199.         mov     byte [edi+addrinfo.ai_socktype], cl
  1200.         dec     cl
  1201.         jnz     .set_udp
  1202. .set_tcp:
  1203.         mov     byte [edi+addrinfo.ai_protocol], IPPROTO_TCP
  1204.         ret
  1205. .set_udp:
  1206.         mov     byte [edi+addrinfo.ai_protocol], IPPROTO_UDP
  1207.         ret
  1208.  
  1209. .set_port:
  1210. ; Just copy port from input __gai_reqdata to output addrinfo.
  1211.         push    edx
  1212.         mov     edx, [ebx+__gai_reqdata.service]
  1213.         xchg    dl, dh  ; convert to network byte order     ;;;;; CHECKME
  1214.         mov     [edi+sizeof.addrinfo+sockaddr_in.sin_port], dx
  1215.         pop     edx
  1216.         ret
  1217.  
  1218. ;;===========================================================================;;
  1219. ;; void __stdcall getaddrinfo_abort(__in struct __gai_reqdata* reqdata);      ;;
  1220. getaddrinfo_abort:                                                           ;;
  1221. ;;---------------------------------------------------------------------------;;
  1222. ;? Abort process started by getaddrinfo_start, free all resources.           ;;
  1223. ;;---------------------------------------------------------------------------;;
  1224. ;> first parameter = pointer to struct __gai_reqdata filled by ..._start     ;;
  1225. ;;===========================================================================;;
  1226. ; 0. Save used registers for __stdcall.
  1227.         push    ebx
  1228. ; 1. Allocated resources: only socketnum, so close it and return.
  1229.         mov     eax, [esp+8]
  1230.         mov     ecx, [eax+__gai_reqdata.socketnum]
  1231.         mcall   75, 1
  1232. ; 2. Restore used registers and return.
  1233.         pop     ebx
  1234.         ret     4
  1235.  
  1236. ;;===========================================================================;;
  1237. ;; void __stdcall freeaddrinfo(__in struct addrinfo* ai);                    ;;
  1238. freeaddrinfo:                                                                ;;
  1239. ;;---------------------------------------------------------------------------;;
  1240. ;? Free one or more addrinfo structures returned by getaddrinfo.             ;;
  1241. ;;---------------------------------------------------------------------------;;
  1242. ;> first parameter = head of list of structures                              ;;
  1243. ;                    (may be arbitrary sublist of original)                  ;;
  1244. ;;===========================================================================;;
  1245. ; 1. Loop for all items in the list.
  1246.         mov     edx, [esp+4]    ; eax = ai
  1247. .loop:
  1248.         test    edx, edx
  1249.         jz      .done
  1250. ; 2. Free each item.
  1251. ; 2a. Free ai_canonname, if allocated.
  1252.         mov     eax, [edx+addrinfo.ai_canonname]
  1253.         test    eax, eax
  1254.         jz      .no_canon_name
  1255.         call    getaddrinfo._.memfree
  1256. .no_canon_name:
  1257. ; 2b. Remember next item
  1258. ;       (after freeing the field ai_next can became unavailable).
  1259.         pushd   [edx+addrinfo.ai_next]
  1260. ; 2c. Free item itself.
  1261.         xchg    eax, edx
  1262.         call    getaddrinfo._.memfree
  1263. ; 2d. Restore pointer to next item and continue loop.
  1264.         pop     edx
  1265.         jmp     .loop
  1266. .done:
  1267. ; 3. Done.
  1268.         ret     4
  1269.  
  1270. ;;===========================================================================;;
  1271. ;;///////////////////////////////////////////////////////////////////////////;;
  1272. ;;===========================================================================;;
  1273. ;! Exported functions section                                                ;;
  1274. ;;===========================================================================;;
  1275. ;;///////////////////////////////////////////////////////////////////////////;;
  1276. ;;===========================================================================;;
  1277.  
  1278.  
  1279. align 4
  1280. @EXPORT:
  1281. export  \
  1282.         lib_init                , 'lib_init'            , \
  1283.         0x00010001              , 'version'             , \
  1284.         inet_addr               , 'inet_addr'           , \
  1285.         inet_ntoa               , 'inet_ntoa'           , \
  1286.         getaddrinfo             , 'getaddrinfo'         , \
  1287.         getaddrinfo_start       , 'getaddrinfo_start'   , \
  1288.         getaddrinfo_process     , 'getaddrinfo_process' , \
  1289.         getaddrinfo_abort       , 'getaddrinfo_abort'   , \
  1290.         freeaddrinfo            , 'freeaddrinfo'
  1291.  
  1292. section '.data' data readable writable align 16
  1293. ; uninitialized data
  1294. mem.alloc   dd ?
  1295. mem.free    dd ?
  1296. mem.realloc dd ?
  1297. dll.load    dd ?
  1298.  
  1299. DNSrequestID    dd      ?
  1300.  
  1301. inet_ntoa.buffer        rb      16      ; static buffer for inet_ntoa
  1302.