Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;  SOCKET.INC                                                  ;;
  7. ;;                                                              ;;
  8. ;;  Sockets constants, structures and functions                 ;;
  9. ;;                                                              ;;
  10. ;;  This file contains the following:                           ;;
  11. ;;    is_localport_unused                                       ;;
  12. ;;    get_free_socket                                           ;;
  13. ;;    socket_open                                               ;;
  14. ;;    socket_open_tcp                                           ;;
  15. ;;    socket_close                                              ;;
  16. ;;    socket_close_tcp                                          ;;
  17. ;;    socket_poll                                               ;;
  18. ;;    socket_status                                             ;;
  19. ;;    socket_read                                               ;;
  20. ;;    socket_write                                              ;;
  21. ;;    socket_write_tcp                                          ;;
  22. ;;                                                              ;;
  23. ;;                                                              ;;
  24. ;;  Changes history:                                            ;;
  25. ;;   22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net          ;;
  26. ;;   11.11.2006 - [Johnny_B] and [smb]                          ;;
  27. ;;                                                              ;;
  28. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  29.  
  30. $Revision: 1284 $
  31.  
  32. ; socket data structure
  33. struct SOCKET
  34.   .PrevPtr        dd ? ; pointer to previous socket in list
  35.   .NextPtr        dd ? ; pointer to next socket in list
  36.   .Number         dd ? ; socket number (unique within single process)
  37.   .PID            dd ? ; application process id
  38.   .LocalIP        dd ? ; local IP address
  39.   .LocalPort      dw ? ; local port
  40.   .RemoteIP       dd ? ; remote IP address
  41.   .RemotePort     dw ? ; remote port
  42.   .OrigRemoteIP   dd ? ; original remote IP address (used to reset to LISTEN state)
  43.   .OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
  44.   .rxDataCount    dd ? ; rx data count
  45.   .TCBState       dd ? ; TCB state
  46.   .TCBTimer       dd ? ; TCB timer (seconds)
  47.   .ISS            dd ? ; initial send sequence
  48.   .IRS            dd ? ; initial receive sequence
  49.   .SND_UNA        dd ? ; sequence number of unack'ed sent packets
  50.   .SND_NXT        dd ? ; bext send sequence number to use
  51.   .SND_WND        dd ? ; send window
  52.   .RCV_NXT        dd ? ; next receive sequence number to use
  53.   .RCV_WND        dd ? ; receive window
  54.   .SEG_LEN        dd ? ; segment length
  55.   .SEG_WND        dd ? ; segment window
  56.   .wndsizeTimer   dd ? ; window size timer
  57.   .lock           dd ? ; lock mutex
  58.   .rxData         dd ? ; receive data buffer here
  59. ends
  60.  
  61. ; TCP opening modes
  62. SOCKET_PASSIVE = 0
  63. SOCKET_ACTIVE  = 1
  64.  
  65. ; socket types
  66. SOCK_STREAM = 1
  67. SOCK_DGRAM  = 2
  68.  
  69. ; pointer to bitmap of free ports (1=free, 0=used)
  70. uglobal
  71. align 4
  72. network_free_ports      dd      ?
  73. endg
  74.  
  75. iglobal
  76. align 4
  77. network_free_hint       dd      1024/8
  78. endg
  79.  
  80. ;; Allocate memory for socket data and put new socket into the list
  81. ; Newly created socket is initialized with calling PID and number and
  82. ; put into beginning of list (which is a fastest way).
  83. ;
  84. ; @return socket structure address in EAX
  85. ;;
  86. proc net_socket_alloc stdcall uses ebx ecx edx edi
  87.         stdcall kernel_alloc, SOCKETBUFFSIZE
  88.         DEBUGF  1, "K : net_socket_alloc (0x%x)\n", eax
  89.         ; check if we can allocate needed amount of memory
  90.         or      eax, eax
  91.         jz      .exit
  92.  
  93.         ; zero-initialize allocated memory
  94.         push    eax
  95.         mov     edi, eax
  96.         mov     ecx, SOCKETBUFFSIZE / 4
  97.         cld
  98.         xor     eax, eax
  99.         rep     stosd
  100.         pop     eax
  101.  
  102.         ; add socket to the list by changing pointers
  103.         mov     ebx, net_sockets
  104.         push    [ebx + SOCKET.NextPtr]
  105.         mov     [ebx + SOCKET.NextPtr], eax
  106.         mov     [eax + SOCKET.PrevPtr], ebx
  107.         pop     ebx
  108.         mov     [eax + SOCKET.NextPtr], ebx
  109.         or      ebx, ebx
  110.         jz      @f
  111.         mov     [ebx + SOCKET.PrevPtr], eax
  112.  
  113.     @@: ; set socket owner PID to the one of calling process
  114.         mov     ebx, [TASK_BASE]
  115.         mov     ebx, [ebx + TASKDATA.pid]
  116.         mov     [eax + SOCKET.PID], ebx
  117.  
  118.         ; find first free socket number and use it
  119.         ;mov     edx, ebx
  120.         mov     ebx, net_sockets
  121.         xor     ecx, ecx
  122.   .next_socket_number:
  123.         inc     ecx
  124.   .next_socket:
  125.         mov     ebx, [ebx + SOCKET.NextPtr]
  126.         or      ebx, ebx
  127.         jz      .last_socket_number
  128.         cmp     [ebx + SOCKET.Number], ecx
  129.         jne     .next_socket
  130.         ;cmp     [ebx + SOCKET.PID], edx
  131.         ;jne     .next_socket
  132.         mov     ebx, net_sockets
  133.         jmp     .next_socket_number
  134.  
  135.   .last_socket_number:
  136.         mov     [eax + SOCKET.Number], ecx
  137.  
  138.   .exit:
  139.         ret
  140. endp
  141.  
  142. ;; Free socket data memory and pop socket off the list
  143. ;
  144. ; @param sockAddr is a socket structure address
  145. ;;
  146. proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
  147.         mov     eax, [sockAddr]
  148.         DEBUGF  1, "K : net_socket_free (0x%x)\n", eax
  149.         ; check if we got something similar to socket structure address
  150.         or      eax, eax
  151.         jz      .error
  152.  
  153.         ; make sure sockAddr is one of the socket addresses in the list
  154.         mov     ebx, net_sockets
  155.         ;mov     ecx, [TASK_BASE]
  156.         ;mov     ecx, [ecx + TASKDATA.pid]
  157.   .next_socket:
  158.         mov     ebx, [ebx + SOCKET.NextPtr]
  159.         or      ebx, ebx
  160.         jz      .error
  161.         cmp     ebx, eax
  162.         jne     .next_socket
  163.         ;cmp     [ebx + SOCKET.PID], ecx
  164.         ;jne     .next_socket
  165.  
  166.         ; okay, we found the correct one
  167.         ; mark local port as unused
  168.         movzx   ebx, [eax + SOCKET.LocalPort]
  169.         push    eax
  170.         mov     eax, [network_free_ports]
  171.         xchg    bl, bh
  172. lock    bts     [eax], ebx
  173.         pop     eax
  174.         ; remove it from the list first, changing pointers
  175.         mov     ebx, [eax + SOCKET.NextPtr]
  176.         mov     eax, [eax + SOCKET.PrevPtr]
  177.         mov     [eax + SOCKET.NextPtr], ebx
  178.         or      ebx, ebx
  179.         jz      @f
  180.         mov     [ebx + SOCKET.PrevPtr], eax
  181.  
  182.     @@: ; and finally free the memory structure used
  183.         stdcall kernel_free, [sockAddr]
  184.         ret
  185.  
  186.   .error:
  187.         DEBUGF  1, "K :   failed\n"
  188.         ret
  189. endp
  190.  
  191. ;; Get socket structure address by its number
  192. ; Scan through sockets list to find the socket with specified number.
  193. ; This proc uses SOCKET.PID indirectly to check if socket is owned by
  194. ; calling process.
  195. ;
  196. ; @param sockNum is a socket number
  197. ; @return socket structure address or 0 (not found) in EAX
  198. ;;
  199. proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
  200.         mov     eax, [sockNum]
  201.         ; check if we got something similar to socket number
  202.         or      eax, eax
  203.         jz      .error
  204.  
  205.         ; scan through sockets list
  206.         mov     ebx, net_sockets
  207.         ;mov     ecx, [TASK_BASE]
  208.         ;mov     ecx, [ecx + TASKDATA.pid]
  209.   .next_socket:
  210.         mov     ebx, [ebx + SOCKET.NextPtr]
  211.         or      ebx, ebx
  212.         jz      .error
  213.         cmp     [ebx + SOCKET.Number], eax
  214.         jne     .next_socket
  215.         ;cmp     [ebx + SOCKET.PID], ecx
  216.         ;jne     .next_socket
  217.  
  218.         ; okay, we found the correct one
  219.         mov     eax, ebx
  220.         ret
  221.  
  222.   .error:
  223.         xor     eax, eax
  224.         ret
  225. endp
  226.  
  227. ;; Get socket number by its structure address
  228. ; Scan through sockets list to find the socket with specified address.
  229. ; This proc uses SOCKET.PID indirectly to check if socket is owned by
  230. ; calling process.
  231. ;
  232. ; @param sockAddr is a socket structure address
  233. ; @return socket number (SOCKET.Number) or 0 (not found) in EAX
  234. ;;
  235. proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
  236.         mov     eax, [sockAddr]
  237.         ; check if we got something similar to socket structure address
  238.         or      eax, eax
  239.         jz      .error
  240.  
  241.         ; scan through sockets list
  242.         mov     ebx, net_sockets
  243.         ;mov     ecx, [TASK_BASE]
  244.         ;mov     ecx, [ecx + TASKDATA.pid]
  245.   .next_socket:
  246.         mov     ebx, [ebx + SOCKET.NextPtr]
  247.         or      ebx, ebx
  248.         jz      .error
  249.         cmp     ebx, eax
  250.         jne     .next_socket
  251.         ;cmp     [ebx + SOCKET.PID], ecx
  252.         ;jne     .next_socket
  253.  
  254.         ; okay, we found the correct one
  255.         mov     eax, [ebx + SOCKET.Number]
  256.         ret
  257.  
  258.   .error:
  259.         xor     eax, eax
  260.         ret
  261. endp
  262.  
  263. ;; [53.9] Check if local port is used by any socket in the system.
  264. ; Scan through sockets list, checking SOCKET.LocalPort.
  265. ; Useful when you want a to generate a unique local port number.
  266. ; This proc doesn't guarantee that after calling it and trying to use
  267. ; the port reported being free in calls to socket_open/socket_open_tcp it'll
  268. ; still be free or otherwise it'll still be used if reported being in use.
  269. ;
  270. ; @param BX is a port number
  271. ; @return 1 (port is free) or 0 (port is in use) in EAX
  272. ;;
  273. proc is_localport_unused stdcall
  274.         movzx   ebx, bx
  275.         mov     eax, [network_free_ports]
  276.         bt      [eax], ebx
  277.         setc    al
  278.         movzx   eax, al
  279.         ret
  280. endp
  281.  
  282. ;======================================
  283. set_local_port:
  284. ;--------------------------------------
  285. ;? Set local port in socket structure.
  286. ;--------------------------------------
  287. ;> eax -> struct SOCKET
  288. ;> bx = local port, or 0 if the kernel must select it itself
  289. ;--------------------------------------
  290. ;< CF set on error / cleared on success
  291. ;< [eax+SOCKET.LocalPort] filled on success
  292. ;======================================
  293. ; 0. Prepare: save registers, make eax point to ports table, expand port to ebx.
  294.         push    eax ecx
  295.         mov     eax, [network_free_ports]
  296.         movzx   ebx, bx
  297. ; 1. Test, whether the kernel should choose port itself. If no, proceed to 5.
  298.         test    ebx, ebx
  299.         jnz     .given
  300. ; 2. Yes, it should. Set ecx = limit of table, eax = start value
  301.         lea     ecx, [eax+0x10000/8]
  302.         add     eax, [network_free_hint]
  303. ; 3. First scan loop: from free hint to end of table.
  304. .scan1:
  305. ; 3a. For each dword, find bit set to 1
  306.         bsf     ebx, [eax]
  307.         jz      .next1
  308. ; 3b. If such bit has been found, atomically test again and clear it.
  309. lock    btr     [eax], ebx
  310. ; 3c. If the bit was still set (usual case), we have found and reserved one port.
  311. ; Proceed to 6.
  312.         jc      .found
  313. ; 3d. Otherwise, someone has reserved it between bsf and btr, so retry search.
  314.         jmp     .scan1
  315. .next1:
  316. ; 3e. All bits are cleared, so advance to next dword.
  317.         add     eax, 4
  318. ; 3f. Check limit and continue loop.
  319.         cmp     eax, ecx
  320.         jb      .scan1
  321. ; 4. Second scan loop: from port 1024 (start of non-system ports) to free hint.
  322.         mov     eax, [network_free_ports]
  323.         mov     ecx, eax
  324.         add     ecx, [network_free_hint]
  325.         add     eax, 1024/8
  326. ; 4a. Test whether there is something to scan.
  327.         cmp     eax, ecx
  328.         jae     .fail
  329. ; 4b. Enter the loop, the process is same as for 3.
  330. .scan2:
  331.         bsf     ebx, [eax]
  332.         jz      .next2
  333. lock    btr     [eax], ebx
  334.         jc      .found
  335.         jmp     .scan2
  336. .next2:
  337.         add     eax, 4
  338.         cmp     eax, ecx
  339.         jb      .scan2
  340. ; 4c. None found. Fail.
  341. .fail:
  342.         pop     ecx eax
  343.         stc
  344.         ret
  345. ; 5. No, the kernel should reserve selected port.
  346. .given:
  347. ; 5a. Atomically test old value and clear bit.
  348. lock    btr     [eax], ebx
  349. ; 5b. If the bit was set, reservation is successful. Proceed to 8.
  350.         jc      .set
  351. ; 5c. Otherwise, fail.
  352.         jmp     .fail
  353. .found:
  354. ; 6. We have found the bit set to 1, convert the position to port number.
  355.         sub     eax, [network_free_ports]
  356.         lea     ebx, [ebx+eax*8]
  357. ; 7. Update free hint.
  358.         add     eax, 4
  359.         cmp     eax, 65536/8
  360.         jb      @f
  361.         mov     eax, 1024/8
  362. @@:
  363.         mov     [network_free_hint], eax
  364. .set:
  365. ; 8. Restore eax, set SOCKET.LocalPort and return.
  366.         pop     ecx eax
  367.         xchg    bl, bh  ; Intel -> network byte order
  368.         mov     [eax + SOCKET.LocalPort], bx
  369.         clc
  370.         ret
  371.  
  372. ;; [53.0] Open DGRAM socket (connectionless, unreliable)
  373. ;
  374. ; @param BX is local port number
  375. ; @param CX is remote port number
  376. ; @param EDX is remote IP address
  377. ; @return socket number or -1 (error) in EAX
  378. ;;
  379. proc socket_open stdcall
  380.         call    net_socket_alloc
  381.         or      eax, eax
  382.         jz      .error
  383.  
  384.         DEBUGF  1, "K : socket_open (0x%x)\n", eax
  385.  
  386.         push    eax
  387.  
  388.         call    set_local_port
  389.         jc      .error.free
  390.         xchg    ch, cl
  391.         mov     [eax + SOCKET.RemotePort], cx
  392.         mov     ebx, [stack_ip]
  393.         mov     [eax + SOCKET.LocalIP], ebx
  394.         mov     [eax + SOCKET.RemoteIP], edx
  395.  
  396.         ;pop     eax      ; Get the socket number back, so we can return it
  397.         stdcall net_socket_addr_to_num
  398.         ret
  399.  
  400.   .error.free:
  401.         stdcall net_socket_free;, eax
  402.  
  403.   .error:
  404.         DEBUGF  1, "K : socket_open (fail)\n"
  405.         or      eax, -1
  406.         ret
  407. endp
  408.  
  409. ;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way)
  410. ;
  411. ; @param BX is local port number
  412. ; @param CX is remote port number
  413. ; @param EDX is remote IP address
  414. ; @param ESI is open mode (SOCKET_ACTIVE, SOCKET_PASSIVE)
  415. ; @return socket number or -1 (error) in EAX
  416. ;;
  417. proc socket_open_tcp stdcall
  418. local sockAddr dd ?
  419.  
  420.         cmp     esi, SOCKET_PASSIVE
  421.         jne     .skip_port_check
  422.  
  423.         push    ebx
  424.         mov     eax, ebx
  425.         xchg    al, ah
  426.         mov     ebx, net_sockets
  427.  
  428.   .next_socket:
  429.         mov     ebx, [ebx + SOCKET.NextPtr]
  430.         or      ebx, ebx
  431.         jz      .last_socket
  432.         cmp     [ebx + SOCKET.TCBState], TCB_LISTEN
  433.         jne     .next_socket
  434.         cmp     [ebx + SOCKET.LocalPort], ax
  435.         jne     .next_socket
  436.  
  437.         xchg    al, ah
  438.         DEBUGF  1, "K : error: port %u is listened by 0x%x\n", ax, ebx
  439.         pop     ebx
  440.         jmp     .error
  441.  
  442.   .last_socket:
  443.         pop     ebx
  444.  
  445.   .skip_port_check:
  446.         call    net_socket_alloc
  447.         or      eax, eax
  448.         jz      .error
  449.  
  450.         DEBUGF  1, "K : socket_open_tcp (0x%x)\n", eax
  451.  
  452.         mov     [sockAddr], eax
  453.  
  454.         ; TODO - check this works!
  455.         ;mov     [eax + SOCKET.wndsizeTimer], 0     ; Reset the window timer.
  456.  
  457.         call    set_local_port
  458.         jc      .error.free
  459.         xchg    ch, cl
  460.         mov     [eax + SOCKET.RemotePort], cx
  461.         mov     [eax + SOCKET.OrigRemotePort], cx
  462.         mov     ebx, [stack_ip]
  463.         mov     [eax + SOCKET.LocalIP], ebx
  464.         mov     [eax + SOCKET.RemoteIP], edx
  465.         mov     [eax + SOCKET.OrigRemoteIP], edx
  466.  
  467.         mov     ebx, TCB_LISTEN
  468.         cmp     esi, SOCKET_PASSIVE
  469.         je      @f
  470.         mov     ebx, TCB_SYN_SENT
  471.     @@: mov     [eax + SOCKET.TCBState], ebx            ; Indicate the state of the TCB
  472.  
  473.         cmp     ebx, TCB_LISTEN
  474.         je      .exit
  475.  
  476.         ; Now, if we are in active mode, then we have to send a SYN to the specified remote port
  477.         mov     eax, EMPTY_QUEUE
  478.         call    dequeue
  479.         cmp     ax, NO_BUFFER
  480.         je      .exit
  481.  
  482.         push    eax
  483.  
  484.         mov     bl, TH_SYN
  485.         xor     ecx, ecx
  486.         stdcall build_tcp_packet, [sockAddr]
  487.  
  488.         mov     eax, NET1OUT_QUEUE
  489.         mov     edx, [stack_ip]
  490.         mov     ecx, [sockAddr]
  491.         cmp     edx, [ecx + SOCKET.RemoteIP]
  492.         jne     .not_local
  493.         mov     eax, IPIN_QUEUE
  494.  
  495.   .not_local:
  496.         ; Send it.
  497.         pop     ebx
  498.         call    queue
  499.  
  500.         mov     esi, [sockAddr]
  501.  
  502.         ; increment SND.NXT in socket
  503.         add     esi, SOCKET.SND_NXT
  504.         call    inc_inet_esi
  505.  
  506.   .exit:
  507.         ; Get the socket number back, so we can return it
  508.         stdcall net_socket_addr_to_num, [sockAddr]
  509.         ret
  510.  
  511.   .error.free:
  512.         stdcall net_socket_free, eax
  513.  
  514.   .error:
  515.         DEBUGF  1, "K : socket_open_tcp (fail)\n"
  516.         or      eax, -1
  517.         ret
  518. endp
  519.  
  520. ;; [53.1] Close DGRAM socket
  521. ;
  522. ; @param EBX is socket number
  523. ; @return 0 (closed successfully) or -1 (error) in EAX
  524. ;;
  525. proc socket_close stdcall
  526.         DEBUGF  1, "K : socket_close (0x%x)\n", ebx
  527.         stdcall net_socket_num_to_addr, ebx
  528.         or      eax, eax
  529.         jz      .error
  530.  
  531.         stdcall net_socket_free, eax
  532.  
  533.         xor     eax, eax
  534.         ret
  535.  
  536.   .error:
  537.         DEBUGF  1, "K : socket_close (fail)\n"
  538.         or      eax, -1
  539.         ret
  540. endp
  541.  
  542. ;; [53.8] Close STREAM socket
  543. ; Closing TCP sockets takes time, so when you get successful return code
  544. ; from this function doesn't always mean that socket is actually closed.
  545. ;
  546. ; @param EBX is socket number
  547. ; @return 0 (closed successfully) or -1 (error) in EAX
  548. ;;
  549. proc socket_close_tcp stdcall
  550. local sockAddr dd ?
  551.  
  552.         DEBUGF  1, "K : socket_close_tcp (0x%x)\n", ebx
  553.         ; first, remove any resend entries
  554.         pusha
  555.  
  556.         mov     esi, resendQ
  557.         mov     ecx, 0
  558.  
  559.   .next_resendq:
  560.         cmp     ecx, NUMRESENDENTRIES
  561.         je      .last_resendq       ; None left
  562.         cmp     [esi + 4], ebx
  563.         je      @f                  ; found one
  564.         inc     ecx
  565.         add     esi, 8
  566.         jmp     .next_resendq
  567.  
  568.     @@: mov     dword[esi + 4], 0
  569.         inc     ecx
  570.         add     esi, 8
  571.         jmp     .next_resendq
  572.  
  573.   .last_resendq:
  574.         popa
  575.  
  576.         stdcall net_socket_num_to_addr, ebx
  577.         or      eax, eax
  578.         jz      .error
  579.  
  580.         mov     ebx, eax
  581.         mov     [sockAddr], eax
  582.  
  583.         cmp     [ebx + SOCKET.TCBState], TCB_LISTEN
  584.         je      .destroy_tcb
  585.         cmp     [ebx + SOCKET.TCBState], TCB_SYN_SENT
  586.         je      .destroy_tcb
  587.  
  588.         ; Now construct the response, and queue for sending by IP
  589.         mov     eax, EMPTY_QUEUE
  590.         call    dequeue
  591.         cmp     ax, NO_BUFFER
  592.         je      .error
  593.  
  594.         push    eax
  595.  
  596.         mov     bl, TH_FIN+TH_ACK
  597.         xor     ecx, ecx
  598.         xor     esi, esi
  599.         stdcall build_tcp_packet, [sockAddr]
  600.  
  601.         mov      ebx, [sockAddr]
  602.         ; increament SND.NXT in socket
  603.         lea     esi, [ebx + SOCKET.SND_NXT]
  604.         call    inc_inet_esi
  605.  
  606.         ; Get the socket state
  607.         mov     eax, [ebx + SOCKET.TCBState]
  608.         cmp     eax, TCB_SYN_RECEIVED
  609.         je      .fin_wait_1
  610.         cmp     eax, TCB_ESTABLISHED
  611.         je      .fin_wait_1
  612.  
  613.         ; assume CLOSE WAIT
  614.         ; Send a fin, then enter last-ack state
  615.         mov     [ebx + SOCKET.TCBState], TCB_LAST_ACK
  616.         jmp     .send
  617.  
  618.   .fin_wait_1:
  619.         ; Send a fin, then enter finwait2 state
  620.         mov     [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
  621.  
  622.   .send:
  623.         mov     eax, NET1OUT_QUEUE
  624.         mov     edx, [stack_ip]
  625.         mov     ecx, [sockAddr]
  626.         cmp     edx, [ecx + SOCKET.RemoteIP]
  627.         jne     .not_local
  628.         mov     eax, IPIN_QUEUE
  629.  
  630.   .not_local:
  631.         ; Send it.
  632.         pop     ebx
  633.         call    queue
  634.         jmp     .exit
  635.  
  636.   .destroy_tcb:
  637.  
  638.         ; Clear the socket variables
  639.         stdcall net_socket_free, ebx
  640.  
  641.   .exit:
  642.         xor     eax, eax
  643.         ret
  644.  
  645.   .error:
  646.         DEBUGF  1, "K : socket_close_tcp (fail)\n"
  647.         or      eax, -1
  648.         ret
  649. endp
  650.  
  651. ;; [53.2] Poll socket
  652. ;
  653. ; @param EBX is socket number
  654. ; @return count or bytes in rx buffer or 0 (error) in EAX
  655. ;;
  656. proc socket_poll stdcall
  657. ;        DEBUGF  1, "socket_poll(0x%x)\n", ebx
  658.         stdcall net_socket_num_to_addr, ebx
  659.         or      eax, eax
  660.         jz      .error
  661.  
  662.         mov     eax, [eax + SOCKET.rxDataCount]
  663.         ret
  664.  
  665.   .error:
  666.         xor     eax, eax
  667.         ret
  668. endp
  669.  
  670. ;; [53.6] Get socket TCB state
  671. ;
  672. ; @param EBX is socket number
  673. ; @return socket TCB state or 0 (error) in EAX
  674. ;;
  675. proc socket_status stdcall
  676. ;;       DEBUGF  1, "socket_status(0x%x)\n", ebx
  677.         stdcall net_socket_num_to_addr, ebx
  678.         or      eax, eax
  679.         jz      .error
  680.  
  681.         mov     eax, [eax + SOCKET.TCBState]
  682.         ret
  683.  
  684.   .error:
  685.         xor     eax, eax
  686.         ret
  687. endp
  688.  
  689. ;; [53.3] Get one byte from rx buffer
  690. ; This function can return 0 in two cases: if there's one byte read and
  691. ; non left, and if an error occured. Behavior should be changed and function
  692. ; shouldn't be used for now. Consider using [53.11] instead.
  693. ;
  694. ; @param EBX is socket number
  695. ; @return number of bytes left in rx buffer or 0 (error) in EAX
  696. ; @return byte read in BL
  697. ;;
  698. proc socket_read stdcall
  699. ;        DEBUGF  1, "socket_read(0x%x)\n", ebx
  700.         stdcall net_socket_num_to_addr, ebx
  701.         or      eax, eax
  702.         jz      .error
  703.  
  704.         lea     ebx, [eax + SOCKET.lock]
  705.         call    wait_mutex
  706.  
  707.         mov     ebx, eax
  708.         mov     eax, [ebx + SOCKET.rxDataCount]         ; get count of bytes
  709.         test    eax, eax
  710.         jz      .error_release
  711.  
  712.         dec     eax
  713.         mov     esi, ebx                                ; esi is address of socket
  714.         mov     [ebx + SOCKET.rxDataCount], eax         ; store new count
  715.         movzx   eax, byte[ebx + SOCKET.rxData]          ; get the byte
  716.  
  717.         mov     ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
  718.         lea     edi, [esi + SOCKET.rxData]
  719.         lea     esi, [edi + 1]
  720.         cld
  721.         push    ecx
  722.         shr     ecx, 2
  723.         rep     movsd
  724.         pop     ecx
  725.         and     ecx, 3
  726.         rep     movsb
  727.  
  728.         mov     [ebx + SOCKET.lock], 0
  729.         mov     ebx, eax
  730.  
  731.         ret
  732.  
  733.   .error_release:
  734.         mov     [ebx + SOCKET.lock], 0
  735.   .error:
  736.         xor     ebx, ebx
  737.         ret
  738. endp
  739.  
  740. ;; [53.11] Get specified number of bytes from rx buffer
  741. ; Number of bytes in rx buffer can be less than requested size. In this case,
  742. ; only available number of bytes is read.
  743. ; This function can return 0 in two cases: if there's no data to read, and if
  744. ; an error occured. Behavior should be changed.
  745. ;
  746. ; @param EBX is socket number
  747. ; @param ECX is pointer to application buffer
  748. ; @param EDX is application buffer size (number of bytes to read)
  749. ; @return number of bytes read or 0 (error) in EAX
  750. ;;
  751. proc socket_read_packet stdcall
  752. ;        DEBUGF  1, "socket_read_packet(0x%x)\n", ebx
  753.         stdcall net_socket_num_to_addr, ebx                ; get real socket address
  754.         or      eax, eax
  755.         jz      .error
  756.  
  757.         lea     ebx, [eax + SOCKET.lock]
  758.         call    wait_mutex
  759.  
  760.         mov     ebx, eax
  761.         mov     eax, [ebx + SOCKET.rxDataCount]            ; get count of bytes
  762.         test    eax, eax                                   ; if count of bytes is zero..
  763.         jz      .exit                                      ; exit function (eax will be zero)
  764.  
  765.         test    edx, edx                                   ; if buffer size is zero, copy all data
  766.         jz      .copy_all_bytes
  767.         cmp     edx, eax                                   ; if buffer size is larger then the bytes of data, copy all data
  768.         jge     .copy_all_bytes
  769.  
  770.         sub     eax, edx                                   ; store new count (data bytes in buffer - bytes we're about to copy)
  771.         mov     [ebx + SOCKET.rxDataCount], eax            ;
  772.         push    eax
  773.         mov     eax, edx                                   ; number of bytes we want to copy must be in eax
  774.         call    .start_copy                                ; copy to the application
  775.  
  776.         mov     esi, ebx                                   ; now we're going to copy the remaining bytes to the beginning
  777.         add     esi, SOCKET.rxData                         ; we dont need to copy the header
  778.         mov     edi, esi                                   ; edi is where we're going to copy to
  779.         add     esi, edx                                   ; esi is from where we copy
  780.         pop     ecx                                        ; count of bytes we have left
  781.         push    ecx                                        ; push it again so we can re-use it later
  782.         shr     ecx, 2                                     ; divide eax by 4
  783.         cld
  784.         rep     movsd                                      ; copy all full dwords
  785.         pop     ecx
  786.         and     ecx, 3
  787.         rep     movsb                                      ; copy remaining bytes
  788.  
  789.   .exit:
  790.         mov     [ebx + SOCKET.lock], 0
  791.         ret                                                ; at last, exit
  792.  
  793.   .error:
  794.         xor     eax, eax
  795.         ret
  796.  
  797.   .copy_all_bytes:
  798.         xor     esi, esi
  799.         mov     [ebx + SOCKET.rxDataCount], esi            ; store new count (zero)
  800.         call    .start_copy
  801.         mov     [ebx + SOCKET.lock], 0
  802.         ret
  803.  
  804.   .start_copy:
  805.         mov     edi, ecx
  806.         mov     esi, ebx
  807.         add     esi, SOCKET.rxData                         ; we dont need to copy the header
  808.         mov     ecx, eax                                   ; eax is count of bytes
  809.         push    ecx
  810.         shr     ecx, 2                                     ; divide eax by 4
  811.         cld                                                ; copy all full dwords
  812.         rep     movsd
  813.         pop     ecx
  814.         and     ecx, 3
  815.         rep     movsb                                      ; copy the rest bytes
  816.         retn                                               ; exit, or go back to shift remaining bytes if any
  817. endp
  818.  
  819. ;; [53.4] Send data through DGRAM socket
  820. ;
  821. ; @param EBX is socket number
  822. ; @param ECX is application data size (number of bytes to send)
  823. ; @param EDX is pointer to application data buffer
  824. ; @return 0 (sent successfully) or -1 (error) in EAX
  825. ;;
  826. proc socket_write stdcall
  827. ;        DEBUGF  1, "socket_write(0x%x)\n", ebx
  828.         stdcall net_socket_num_to_addr, ebx                ; get real socket address
  829.         or      eax, eax
  830.         jz      .error
  831.  
  832.         mov     ebx, eax
  833.  
  834.         mov     eax, EMPTY_QUEUE
  835.         call    dequeue
  836.         cmp     ax, NO_BUFFER
  837.         je      .error
  838.  
  839.         ; Save the queue entry number
  840.         push    eax
  841.  
  842.         ; save the pointers to the data buffer & size
  843.         push    edx
  844.         push    ecx
  845.  
  846.         ; convert buffer pointer eax to the absolute address
  847.         mov     ecx, IPBUFFSIZE
  848.         mul     ecx
  849.         add     eax, IPbuffs
  850.  
  851.         mov     edx, eax
  852.  
  853.         ; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
  854.  
  855.         ; Fill in the IP header (some data is in the socket descriptor)
  856.         mov     eax, [ebx + SOCKET.LocalIP]
  857.         mov     [edx + IP_PACKET.SourceAddress], eax
  858.         mov     eax, [ebx + SOCKET.RemoteIP]
  859.         mov     [edx + IP_PACKET.DestinationAddress], eax
  860.  
  861.         mov     [edx + IP_PACKET.VersionAndIHL], 0x45
  862.         mov     [edx + IP_PACKET.TypeOfService], 0
  863.  
  864.         pop     eax                   ; Get the UDP data length
  865.         push    eax
  866.  
  867.         add     eax, 20 + 8           ; add IP header and UDP header lengths
  868.         xchg    al, ah
  869.         mov     [edx + IP_PACKET.TotalLength], ax
  870.         xor     eax, eax
  871.         mov     [edx + IP_PACKET.Identification], ax
  872.         mov     [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
  873.         mov     [edx + IP_PACKET.TimeToLive], 0x20
  874.         mov     [edx + IP_PACKET.Protocol], PROTOCOL_UDP
  875.  
  876.         ; Checksum left unfilled
  877.         mov     [edx + IP_PACKET.HeaderChecksum], ax
  878.  
  879.         ; Fill in the UDP header (some data is in the socket descriptor)
  880.         mov     ax, [ebx + SOCKET.LocalPort]
  881.         mov     [edx + 20 + UDP_PACKET.SourcePort], ax
  882.  
  883.         mov     ax, [ebx + SOCKET.RemotePort]
  884.         mov     [edx + 20 + UDP_PACKET.DestinationPort], ax
  885.  
  886.         pop     eax
  887.         push    eax
  888.  
  889.         add     eax, 8
  890.         xchg    al, ah
  891.         mov     [edx + 20 + UDP_PACKET.Length], ax
  892.  
  893.         ; Checksum left unfilled
  894.         xor     eax, eax
  895.         mov     [edx + 20 + UDP_PACKET.Checksum], ax
  896.  
  897.         pop     ecx                  ; count of bytes to send
  898.         mov     ebx, ecx             ; need the length later
  899.         pop     eax                  ; get callers ptr to data to send
  900.  
  901.         ; Get the address of the callers data
  902.         mov     edi, [TASK_BASE]
  903.         add     edi, TASKDATA.mem_start
  904.         add     eax, [edi]
  905.         mov     esi, eax
  906.  
  907.         mov     edi, edx
  908.         add     edi, 28
  909.         cld
  910.         rep     movsb               ; copy the data across
  911.  
  912.         ; we have edx as IPbuffer ptr.
  913.         ; Fill in the UDP checksum
  914.         ; First, fill in pseudoheader
  915.         mov     eax, [edx + IP_PACKET.SourceAddress]
  916.         mov     [pseudoHeader], eax
  917.         mov     eax, [edx + IP_PACKET.DestinationAddress]
  918.         mov     [pseudoHeader + 4], eax
  919.         mov     word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0      ; 0 + protocol
  920.         add     ebx, 8
  921.         mov     eax, ebx
  922.         xchg    al, ah
  923.         mov     [pseudoHeader + 10], ax
  924.  
  925.         mov     eax, pseudoHeader
  926.         mov     [checkAdd1], eax
  927.         mov     [checkSize1], word 12
  928.         mov     eax, edx
  929.         add     eax, 20
  930.         mov     [checkAdd2], eax
  931.         mov     eax, ebx
  932.         mov     [checkSize2], ax      ; was eax!! mjh 8/7/02
  933.  
  934.         call    checksum
  935.  
  936.         ; store it in the UDP checksum ( in the correct order! )
  937.         mov     ax, [checkResult]
  938.  
  939.         ; If the UDP checksum computes to 0, we must make it 0xffff
  940.         ; (0 is reserved for 'not used')
  941.         test    ax, ax
  942.         jnz     @f
  943.         mov     ax, 0xffff
  944.  
  945.     @@: xchg    al, ah
  946.         mov     [edx + 20 + UDP_PACKET.Checksum], ax
  947.  
  948.         ; Fill in the IP header checksum
  949.         GET_IHL ecx,edx              ; get IP-Header length
  950.         stdcall checksum_jb,edx,ecx  ; buf_ptr, buf_size
  951.         xchg    al, ah
  952.         mov     [edx + IP_PACKET.HeaderChecksum], ax
  953.  
  954.         ; Check destination IP address.
  955.         ; If it is the local host IP, route it back to IP_RX
  956.  
  957.         pop     ebx
  958.  
  959.         mov     eax, NET1OUT_QUEUE
  960.         mov     ecx, [edx + SOCKET.RemoteIP]
  961.         mov     edx, [stack_ip]
  962.         cmp     edx, ecx
  963.         jne     .not_local
  964.         mov     eax, IPIN_QUEUE
  965.  
  966.   .not_local:
  967.         ; Send it.
  968.         call    queue
  969.  
  970.         xor     eax, eax
  971.         ret
  972.  
  973.   .error:
  974.         or      eax, -1
  975.         ret
  976. endp
  977.  
  978. ;; [53.7] Send data through STREAM socket
  979. ;
  980. ; @param EBX is socket number
  981. ; @param ECX is application data size (number of bytes to send)
  982. ; @param EDX is pointer to application data buffer
  983. ; @return 0 (sent successfully) or -1 (error) in EAX
  984. ;;
  985. proc socket_write_tcp stdcall
  986. local sockAddr dd ?
  987.  
  988. ;        DEBUGF  1, "socket_write_tcp(0x%x)\n", ebx
  989.         stdcall net_socket_num_to_addr, ebx
  990.         or      eax, eax
  991.         jz      .error
  992.  
  993.         mov     ebx, eax
  994.         mov     [sockAddr], ebx
  995.  
  996.         ; If the sockets window timer is nonzero, do not queue packet
  997.         cmp     [ebx + SOCKET.wndsizeTimer], 0
  998.         jne     .error
  999.  
  1000.         mov     eax, EMPTY_QUEUE
  1001.         call    dequeue
  1002.         cmp     ax, NO_BUFFER
  1003.         je      .error
  1004.  
  1005.         push    eax
  1006.  
  1007.         ; Get the address of the callers data
  1008.         mov     edi, [TASK_BASE]
  1009.         add     edi, TASKDATA.mem_start
  1010.         add     edx, [edi]
  1011.         mov     esi, edx
  1012.  
  1013.         pop     eax
  1014.         push    eax
  1015.  
  1016.         push    ecx
  1017.         mov     bl, TH_ACK
  1018.         stdcall build_tcp_packet, [sockAddr]
  1019.         pop     ecx
  1020.  
  1021.         ; Check destination IP address.
  1022.         ; If it is the local host IP, route it back to IP_RX
  1023.  
  1024.         pop     ebx
  1025.         push    ecx
  1026.  
  1027.         mov     eax, NET1OUT_QUEUE
  1028.         mov     edx, [stack_ip]
  1029.         mov     ecx, [sockAddr]
  1030.         cmp     edx, [ecx + SOCKET.RemoteIP]
  1031.         jne     .not_local
  1032.         mov     eax, IPIN_QUEUE
  1033.  
  1034.   .not_local:
  1035.         pop     ecx
  1036.         push    ebx                 ; save ipbuffer number
  1037.  
  1038.         call    queue
  1039.  
  1040.         mov     esi, [sockAddr]
  1041.  
  1042.         ; increament SND.NXT in socket
  1043.         ; Amount to increment by is in ecx
  1044.         add     esi, SOCKET.SND_NXT
  1045.         call    add_inet_esi
  1046.  
  1047.         pop     ebx
  1048.  
  1049.         ; Copy the IP buffer to a resend queue
  1050.         ; If there isn't one, dont worry about it for now
  1051.         mov     esi, resendQ
  1052.         mov     ecx, 0
  1053.  
  1054.   .next_resendq:
  1055.         cmp     ecx, NUMRESENDENTRIES
  1056.         je      .exit              ; None found
  1057.         cmp     dword[esi + 4], 0
  1058.         je      @f                 ; found one
  1059.         inc     ecx
  1060.         add     esi, 8
  1061.         jmp     .next_resendq
  1062.  
  1063.     @@: push    ebx
  1064.  
  1065.         ; OK, we have a buffer descriptor ptr in esi.
  1066.         ; resend entry # in ecx
  1067.         ;  Populate it
  1068.         ;  socket #
  1069.         ;  retries count
  1070.         ;  retry time
  1071.         ;  fill IP buffer associated with this descriptor
  1072.  
  1073.         stdcall net_socket_addr_to_num, [sockAddr]
  1074.         mov     [esi + 4], eax
  1075.         mov     byte[esi + 1], TCP_RETRIES
  1076.         mov     word[esi + 2], TCP_TIMEOUT
  1077.  
  1078.         inc     ecx
  1079.         ; Now get buffer location, and copy buffer across. argh! more copying,,
  1080.         mov     edi, resendBuffer - IPBUFFSIZE
  1081.  
  1082.     @@: add     edi, IPBUFFSIZE
  1083.         loop    @b
  1084.  
  1085.         ; we have dest buffer location in edi
  1086.         pop     eax
  1087.         ; convert source buffer pointer eax to the absolute address
  1088.         mov     ecx, IPBUFFSIZE
  1089.         mul     ecx
  1090.         add     eax, IPbuffs
  1091.         mov     esi, eax
  1092.  
  1093.         ; do copy
  1094.         mov     ecx, IPBUFFSIZE
  1095.         cld
  1096.         rep     movsb
  1097.  
  1098.   .exit:
  1099.         xor     eax, eax
  1100.         ret
  1101.  
  1102.   .error:
  1103.         or      eax, -1
  1104.         ret
  1105. endp
  1106.