Subversion Repositories Kolibri OS

Rev

Rev 2434 | Rev 3555 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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