2beb55bd3a
git-svn-id: svn://kolibrios.org@1288 a494cfbc-eb01-0410-851d-a64ba20cac60
1108 lines
27 KiB
PHP
1108 lines
27 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; SOCKET.INC ;;
|
|
;; ;;
|
|
;; Sockets constants, structures and functions ;;
|
|
;; ;;
|
|
;; This file contains the following: ;;
|
|
;; is_localport_unused ;;
|
|
;; get_free_socket ;;
|
|
;; socket_open ;;
|
|
;; socket_open_tcp ;;
|
|
;; socket_close ;;
|
|
;; socket_close_tcp ;;
|
|
;; socket_poll ;;
|
|
;; socket_status ;;
|
|
;; socket_read ;;
|
|
;; socket_write ;;
|
|
;; socket_write_tcp ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; Changes history: ;;
|
|
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;;
|
|
;; 11.11.2006 - [Johnny_B] and [smb] ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
$Revision$
|
|
|
|
; socket data structure
|
|
struct SOCKET
|
|
.PrevPtr dd ? ; pointer to previous socket in list
|
|
.NextPtr dd ? ; pointer to next socket in list
|
|
.Number dd ? ; socket number (unique within single process)
|
|
.PID dd ? ; application process id
|
|
.LocalIP dd ? ; local IP address
|
|
.LocalPort dw ? ; local port
|
|
.RemoteIP dd ? ; remote IP address
|
|
.RemotePort dw ? ; remote port
|
|
.OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state)
|
|
.OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
|
|
.rxDataCount dd ? ; rx data count
|
|
.TCBState dd ? ; TCB state
|
|
.TCBTimer dd ? ; TCB timer (seconds)
|
|
.ISS dd ? ; initial send sequence
|
|
.IRS dd ? ; initial receive sequence
|
|
.SND_UNA dd ? ; sequence number of unack'ed sent packets
|
|
.SND_NXT dd ? ; bext send sequence number to use
|
|
.SND_WND dd ? ; send window
|
|
.RCV_NXT dd ? ; next receive sequence number to use
|
|
.RCV_WND dd ? ; receive window
|
|
.SEG_LEN dd ? ; segment length
|
|
.SEG_WND dd ? ; segment window
|
|
.wndsizeTimer dd ? ; window size timer
|
|
.lock dd ? ; lock mutex
|
|
.rxData dd ? ; receive data buffer here
|
|
ends
|
|
|
|
; TCP opening modes
|
|
SOCKET_PASSIVE = 0
|
|
SOCKET_ACTIVE = 1
|
|
|
|
; socket types
|
|
SOCK_STREAM = 1
|
|
SOCK_DGRAM = 2
|
|
|
|
; pointer to bitmap of free ports (1=free, 0=used)
|
|
uglobal
|
|
align 4
|
|
network_free_ports dd ?
|
|
endg
|
|
|
|
iglobal
|
|
align 4
|
|
network_free_hint dd 1024/8
|
|
endg
|
|
|
|
;; Allocate memory for socket data and put new socket into the list
|
|
; Newly created socket is initialized with calling PID and number and
|
|
; put into beginning of list (which is a fastest way).
|
|
;
|
|
; @return socket structure address in EAX
|
|
;;
|
|
proc net_socket_alloc stdcall uses ebx ecx edx edi
|
|
stdcall kernel_alloc, SOCKETBUFFSIZE
|
|
DEBUGF 1, "K : net_socket_alloc (0x%x)\n", eax
|
|
; check if we can allocate needed amount of memory
|
|
or eax, eax
|
|
jz .exit
|
|
|
|
; zero-initialize allocated memory
|
|
push eax
|
|
mov edi, eax
|
|
mov ecx, SOCKETBUFFSIZE / 4
|
|
cld
|
|
xor eax, eax
|
|
rep stosd
|
|
pop eax
|
|
|
|
; add socket to the list by changing pointers
|
|
mov ebx, net_sockets
|
|
push [ebx + SOCKET.NextPtr]
|
|
mov [ebx + SOCKET.NextPtr], eax
|
|
mov [eax + SOCKET.PrevPtr], ebx
|
|
pop ebx
|
|
mov [eax + SOCKET.NextPtr], ebx
|
|
or ebx, ebx
|
|
jz @f
|
|
mov [ebx + SOCKET.PrevPtr], eax
|
|
|
|
@@: ; set socket owner PID to the one of calling process
|
|
mov ebx, [TASK_BASE]
|
|
mov ebx, [ebx + TASKDATA.pid]
|
|
mov [eax + SOCKET.PID], ebx
|
|
|
|
; find first free socket number and use it
|
|
;mov edx, ebx
|
|
mov ebx, net_sockets
|
|
xor ecx, ecx
|
|
.next_socket_number:
|
|
inc ecx
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .last_socket_number
|
|
cmp [ebx + SOCKET.Number], ecx
|
|
jne .next_socket
|
|
;cmp [ebx + SOCKET.PID], edx
|
|
;jne .next_socket
|
|
mov ebx, net_sockets
|
|
jmp .next_socket_number
|
|
|
|
.last_socket_number:
|
|
mov [eax + SOCKET.Number], ecx
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
;; Free socket data memory and pop socket off the list
|
|
;
|
|
; @param sockAddr is a socket structure address
|
|
;;
|
|
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
|
|
mov eax, [sockAddr]
|
|
DEBUGF 1, "K : net_socket_free (0x%x)\n", eax
|
|
; check if we got something similar to socket structure address
|
|
or eax, eax
|
|
jz .error
|
|
|
|
; make sure sockAddr is one of the socket addresses in the list
|
|
mov ebx, net_sockets
|
|
;mov ecx, [TASK_BASE]
|
|
;mov ecx, [ecx + TASKDATA.pid]
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .error
|
|
cmp ebx, eax
|
|
jne .next_socket
|
|
;cmp [ebx + SOCKET.PID], ecx
|
|
;jne .next_socket
|
|
|
|
; okay, we found the correct one
|
|
; mark local port as unused
|
|
movzx ebx, [eax + SOCKET.LocalPort]
|
|
push eax
|
|
mov eax, [network_free_ports]
|
|
xchg bl, bh
|
|
lock bts [eax], ebx
|
|
pop eax
|
|
; remove it from the list first, changing pointers
|
|
mov ebx, [eax + SOCKET.NextPtr]
|
|
mov eax, [eax + SOCKET.PrevPtr]
|
|
mov [eax + SOCKET.NextPtr], ebx
|
|
or ebx, ebx
|
|
jz @f
|
|
mov [ebx + SOCKET.PrevPtr], eax
|
|
|
|
@@: ; and finally free the memory structure used
|
|
stdcall kernel_free, [sockAddr]
|
|
ret
|
|
|
|
.error:
|
|
DEBUGF 1, "K : failed\n"
|
|
ret
|
|
endp
|
|
|
|
;; Get socket structure address by its number
|
|
; Scan through sockets list to find the socket with specified number.
|
|
; This proc uses SOCKET.PID indirectly to check if socket is owned by
|
|
; calling process.
|
|
;
|
|
; @param sockNum is a socket number
|
|
; @return socket structure address or 0 (not found) in EAX
|
|
;;
|
|
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
|
|
mov eax, [sockNum]
|
|
; check if we got something similar to socket number
|
|
or eax, eax
|
|
jz .error
|
|
|
|
; scan through sockets list
|
|
mov ebx, net_sockets
|
|
;mov ecx, [TASK_BASE]
|
|
;mov ecx, [ecx + TASKDATA.pid]
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .error
|
|
cmp [ebx + SOCKET.Number], eax
|
|
jne .next_socket
|
|
;cmp [ebx + SOCKET.PID], ecx
|
|
;jne .next_socket
|
|
|
|
; okay, we found the correct one
|
|
mov eax, ebx
|
|
ret
|
|
|
|
.error:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
;; Get socket number by its structure address
|
|
; Scan through sockets list to find the socket with specified address.
|
|
; This proc uses SOCKET.PID indirectly to check if socket is owned by
|
|
; calling process.
|
|
;
|
|
; @param sockAddr is a socket structure address
|
|
; @return socket number (SOCKET.Number) or 0 (not found) in EAX
|
|
;;
|
|
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
|
|
mov eax, [sockAddr]
|
|
; check if we got something similar to socket structure address
|
|
or eax, eax
|
|
jz .error
|
|
|
|
; scan through sockets list
|
|
mov ebx, net_sockets
|
|
;mov ecx, [TASK_BASE]
|
|
;mov ecx, [ecx + TASKDATA.pid]
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .error
|
|
cmp ebx, eax
|
|
jne .next_socket
|
|
;cmp [ebx + SOCKET.PID], ecx
|
|
;jne .next_socket
|
|
|
|
; okay, we found the correct one
|
|
mov eax, [ebx + SOCKET.Number]
|
|
ret
|
|
|
|
.error:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
;; [53.9] Check if local port is used by any socket in the system.
|
|
; Scan through sockets list, checking SOCKET.LocalPort.
|
|
; Useful when you want a to generate a unique local port number.
|
|
; This proc doesn't guarantee that after calling it and trying to use
|
|
; the port reported being free in calls to socket_open/socket_open_tcp it'll
|
|
; still be free or otherwise it'll still be used if reported being in use.
|
|
;
|
|
; @param BX is a port number
|
|
; @return 1 (port is free) or 0 (port is in use) in EAX
|
|
;;
|
|
proc is_localport_unused stdcall
|
|
movzx ebx, bx
|
|
mov eax, [network_free_ports]
|
|
bt [eax], ebx
|
|
setc al
|
|
movzx eax, al
|
|
ret
|
|
endp
|
|
|
|
;======================================
|
|
set_local_port:
|
|
;--------------------------------------
|
|
;? Set local port in socket structure.
|
|
;--------------------------------------
|
|
;> eax -> struct SOCKET
|
|
;> bx = local port, or 0 if the kernel must select it itself
|
|
;--------------------------------------
|
|
;< CF set on error / cleared on success
|
|
;< [eax+SOCKET.LocalPort] filled on success
|
|
;======================================
|
|
; 0. Prepare: save registers, make eax point to ports table, expand port to ebx.
|
|
push eax ecx
|
|
mov eax, [network_free_ports]
|
|
movzx ebx, bx
|
|
; 1. Test, whether the kernel should choose port itself. If no, proceed to 5.
|
|
test ebx, ebx
|
|
jnz .given
|
|
; 2. Yes, it should. Set ecx = limit of table, eax = start value
|
|
lea ecx, [eax+0x10000/8]
|
|
add eax, [network_free_hint]
|
|
; 3. First scan loop: from free hint to end of table.
|
|
.scan1:
|
|
; 3a. For each dword, find bit set to 1
|
|
bsf ebx, [eax]
|
|
jz .next1
|
|
; 3b. If such bit has been found, atomically test again and clear it.
|
|
lock btr [eax], ebx
|
|
; 3c. If the bit was still set (usual case), we have found and reserved one port.
|
|
; Proceed to 6.
|
|
jc .found
|
|
; 3d. Otherwise, someone has reserved it between bsf and btr, so retry search.
|
|
jmp .scan1
|
|
.next1:
|
|
; 3e. All bits are cleared, so advance to next dword.
|
|
add eax, 4
|
|
; 3f. Check limit and continue loop.
|
|
cmp eax, ecx
|
|
jb .scan1
|
|
; 4. Second scan loop: from port 1024 (start of non-system ports) to free hint.
|
|
mov eax, [network_free_ports]
|
|
mov ecx, eax
|
|
add ecx, [network_free_hint]
|
|
add eax, 1024/8
|
|
; 4a. Test whether there is something to scan.
|
|
cmp eax, ecx
|
|
jae .fail
|
|
; 4b. Enter the loop, the process is same as for 3.
|
|
.scan2:
|
|
bsf ebx, [eax]
|
|
jz .next2
|
|
lock btr [eax], ebx
|
|
jc .found
|
|
jmp .scan2
|
|
.next2:
|
|
add eax, 4
|
|
cmp eax, ecx
|
|
jb .scan2
|
|
; 4c. None found. Fail.
|
|
.fail:
|
|
pop ecx eax
|
|
stc
|
|
ret
|
|
; 5. No, the kernel should reserve selected port.
|
|
.given:
|
|
; 5a. Atomically test old value and clear bit.
|
|
lock btr [eax], ebx
|
|
; 5b. If the bit was set, reservation is successful. Proceed to 8.
|
|
jc .set
|
|
; 5c. Otherwise, fail.
|
|
jmp .fail
|
|
.found:
|
|
; 6. We have found the bit set to 1, convert the position to port number.
|
|
sub eax, [network_free_ports]
|
|
lea ebx, [ebx+eax*8]
|
|
; 7. Update free hint.
|
|
add eax, 4
|
|
cmp eax, 65536/8
|
|
jb @f
|
|
mov eax, 1024/8
|
|
@@:
|
|
mov [network_free_hint], eax
|
|
.set:
|
|
; 8. Restore eax, set SOCKET.LocalPort and return.
|
|
pop ecx eax
|
|
xchg bl, bh ; Intel -> network byte order
|
|
mov [eax + SOCKET.LocalPort], bx
|
|
clc
|
|
ret
|
|
|
|
;; [53.0] Open DGRAM socket (connectionless, unreliable)
|
|
;
|
|
; @param BX is local port number
|
|
; @param CX is remote port number
|
|
; @param EDX is remote IP address
|
|
; @return socket number or -1 (error) in EAX
|
|
;;
|
|
proc socket_open stdcall
|
|
call net_socket_alloc
|
|
or eax, eax
|
|
jz .error
|
|
|
|
DEBUGF 1, "K : socket_open (0x%x)\n", eax
|
|
|
|
push eax
|
|
|
|
call set_local_port
|
|
jc .error.free
|
|
xchg ch, cl
|
|
mov [eax + SOCKET.RemotePort], cx
|
|
mov ebx, [stack_ip]
|
|
mov [eax + SOCKET.LocalIP], ebx
|
|
mov [eax + SOCKET.RemoteIP], edx
|
|
|
|
;pop eax ; Get the socket number back, so we can return it
|
|
stdcall net_socket_addr_to_num
|
|
ret
|
|
|
|
.error.free:
|
|
stdcall net_socket_free;, eax
|
|
|
|
.error:
|
|
DEBUGF 1, "K : socket_open (fail)\n"
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way)
|
|
;
|
|
; @param BX is local port number
|
|
; @param CX is remote port number
|
|
; @param EDX is remote IP address
|
|
; @param ESI is open mode (SOCKET_ACTIVE, SOCKET_PASSIVE)
|
|
; @return socket number or -1 (error) in EAX
|
|
;;
|
|
proc socket_open_tcp stdcall
|
|
local sockAddr dd ?
|
|
|
|
cmp esi, SOCKET_PASSIVE
|
|
jne .skip_port_check
|
|
|
|
push ebx
|
|
mov eax, ebx
|
|
xchg al, ah
|
|
mov ebx, net_sockets
|
|
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .last_socket
|
|
cmp [ebx + SOCKET.TCBState], TCB_LISTEN
|
|
jne .next_socket
|
|
cmp [ebx + SOCKET.LocalPort], ax
|
|
jne .next_socket
|
|
|
|
xchg al, ah
|
|
DEBUGF 1, "K : error: port %u is listened by 0x%x\n", ax, ebx
|
|
pop ebx
|
|
jmp .error
|
|
|
|
.last_socket:
|
|
pop ebx
|
|
|
|
.skip_port_check:
|
|
call net_socket_alloc
|
|
or eax, eax
|
|
jz .error
|
|
|
|
DEBUGF 1, "K : socket_open_tcp (0x%x)\n", eax
|
|
|
|
mov [sockAddr], eax
|
|
|
|
; TODO - check this works!
|
|
;mov [eax + SOCKET.wndsizeTimer], 0 ; Reset the window timer.
|
|
|
|
call set_local_port
|
|
jc .error.free
|
|
xchg ch, cl
|
|
mov [eax + SOCKET.RemotePort], cx
|
|
mov [eax + SOCKET.OrigRemotePort], cx
|
|
mov ebx, [stack_ip]
|
|
mov [eax + SOCKET.LocalIP], ebx
|
|
mov [eax + SOCKET.RemoteIP], edx
|
|
mov [eax + SOCKET.OrigRemoteIP], edx
|
|
|
|
mov ebx, TCB_LISTEN
|
|
cmp esi, SOCKET_PASSIVE
|
|
je @f
|
|
mov ebx, TCB_SYN_SENT
|
|
@@: mov [eax + SOCKET.TCBState], ebx ; Indicate the state of the TCB
|
|
|
|
cmp ebx, TCB_LISTEN
|
|
je .exit
|
|
|
|
; Now, if we are in active mode, then we have to send a SYN to the specified remote port
|
|
mov eax, EMPTY_QUEUE
|
|
call dequeue
|
|
cmp ax, NO_BUFFER
|
|
je .exit
|
|
|
|
push eax
|
|
|
|
mov bl, TH_SYN
|
|
xor ecx, ecx
|
|
stdcall build_tcp_packet, [sockAddr]
|
|
|
|
mov eax, NET1OUT_QUEUE
|
|
mov edx, [stack_ip]
|
|
mov ecx, [sockAddr]
|
|
cmp edx, [ecx + SOCKET.RemoteIP]
|
|
jne .not_local
|
|
mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
call queue
|
|
|
|
mov esi, [sockAddr]
|
|
|
|
; increment SND.NXT in socket
|
|
add esi, SOCKET.SND_NXT
|
|
call inc_inet_esi
|
|
|
|
.exit:
|
|
; Get the socket number back, so we can return it
|
|
stdcall net_socket_addr_to_num, [sockAddr]
|
|
ret
|
|
|
|
.error.free:
|
|
stdcall net_socket_free, eax
|
|
|
|
.error:
|
|
DEBUGF 1, "K : socket_open_tcp (fail)\n"
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
;; [53.1] Close DGRAM socket
|
|
;
|
|
; @param EBX is socket number
|
|
; @return 0 (closed successfully) or -1 (error) in EAX
|
|
;;
|
|
proc socket_close stdcall
|
|
DEBUGF 1, "K : socket_close (0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
stdcall net_socket_free, eax
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
.error:
|
|
DEBUGF 1, "K : socket_close (fail)\n"
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
;; [53.8] Close STREAM socket
|
|
; Closing TCP sockets takes time, so when you get successful return code
|
|
; from this function doesn't always mean that socket is actually closed.
|
|
;
|
|
; @param EBX is socket number
|
|
; @return 0 (closed successfully) or -1 (error) in EAX
|
|
;;
|
|
proc socket_close_tcp stdcall
|
|
local sockAddr dd ?
|
|
|
|
DEBUGF 1, "K : socket_close_tcp (0x%x)\n", ebx
|
|
; first, remove any resend entries
|
|
pusha
|
|
|
|
mov esi, resendQ
|
|
mov ecx, 0
|
|
|
|
.next_resendq:
|
|
cmp ecx, NUMRESENDENTRIES
|
|
je .last_resendq ; None left
|
|
cmp [esi + 4], ebx
|
|
je @f ; found one
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: mov dword[esi + 4], 0
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
.last_resendq:
|
|
popa
|
|
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov ebx, eax
|
|
mov [sockAddr], eax
|
|
|
|
cmp [ebx + SOCKET.TCBState], TCB_LISTEN
|
|
je .destroy_tcb
|
|
cmp [ebx + SOCKET.TCBState], TCB_SYN_SENT
|
|
je .destroy_tcb
|
|
cmp [ebx + SOCKET.TCBState], TCB_CLOSED
|
|
je .destroy_tcb
|
|
|
|
; Now construct the response, and queue for sending by IP
|
|
mov eax, EMPTY_QUEUE
|
|
call dequeue
|
|
cmp ax, NO_BUFFER
|
|
je .error
|
|
|
|
push eax
|
|
|
|
mov bl, TH_FIN+TH_ACK
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
stdcall build_tcp_packet, [sockAddr]
|
|
|
|
mov ebx, [sockAddr]
|
|
; increament SND.NXT in socket
|
|
lea esi, [ebx + SOCKET.SND_NXT]
|
|
call inc_inet_esi
|
|
|
|
; Get the socket state
|
|
mov eax, [ebx + SOCKET.TCBState]
|
|
cmp eax, TCB_SYN_RECEIVED
|
|
je .fin_wait_1
|
|
cmp eax, TCB_ESTABLISHED
|
|
je .fin_wait_1
|
|
|
|
; assume CLOSE WAIT
|
|
; Send a fin, then enter last-ack state
|
|
mov [ebx + SOCKET.TCBState], TCB_LAST_ACK
|
|
jmp .send
|
|
|
|
.fin_wait_1:
|
|
; Send a fin, then enter finwait2 state
|
|
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
|
|
|
|
.send:
|
|
mov eax, NET1OUT_QUEUE
|
|
mov edx, [stack_ip]
|
|
mov ecx, [sockAddr]
|
|
cmp edx, [ecx + SOCKET.RemoteIP]
|
|
jne .not_local
|
|
mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
call queue
|
|
jmp .exit
|
|
|
|
.destroy_tcb:
|
|
|
|
; Clear the socket variables
|
|
stdcall net_socket_free, ebx
|
|
|
|
.exit:
|
|
xor eax, eax
|
|
ret
|
|
|
|
.error:
|
|
DEBUGF 1, "K : socket_close_tcp (fail)\n"
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
;; [53.2] Poll socket
|
|
;
|
|
; @param EBX is socket number
|
|
; @return count or bytes in rx buffer or 0 (error) in EAX
|
|
;;
|
|
proc socket_poll stdcall
|
|
; DEBUGF 1, "socket_poll(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov eax, [eax + SOCKET.rxDataCount]
|
|
ret
|
|
|
|
.error:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
;; [53.6] Get socket TCB state
|
|
;
|
|
; @param EBX is socket number
|
|
; @return socket TCB state or 0 (error) in EAX
|
|
;;
|
|
proc socket_status stdcall
|
|
;; DEBUGF 1, "socket_status(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov eax, [eax + SOCKET.TCBState]
|
|
ret
|
|
|
|
.error:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
;; [53.3] Get one byte from rx buffer
|
|
; This function can return 0 in two cases: if there's one byte read and
|
|
; non left, and if an error occured. Behavior should be changed and function
|
|
; shouldn't be used for now. Consider using [53.11] instead.
|
|
;
|
|
; @param EBX is socket number
|
|
; @return number of bytes left in rx buffer or 0 (error) in EAX
|
|
; @return byte read in BL
|
|
;;
|
|
proc socket_read stdcall
|
|
; DEBUGF 1, "socket_read(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
lea ebx, [eax + SOCKET.lock]
|
|
call wait_mutex
|
|
|
|
mov ebx, eax
|
|
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes
|
|
test eax, eax
|
|
jz .error_release
|
|
|
|
dec eax
|
|
mov esi, ebx ; esi is address of socket
|
|
mov [ebx + SOCKET.rxDataCount], eax ; store new count
|
|
movzx eax, byte[ebx + SOCKET.rxData] ; get the byte
|
|
|
|
mov ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
|
|
lea edi, [esi + SOCKET.rxData]
|
|
lea esi, [edi + 1]
|
|
cld
|
|
push ecx
|
|
shr ecx, 2
|
|
rep movsd
|
|
pop ecx
|
|
and ecx, 3
|
|
rep movsb
|
|
|
|
mov [ebx + SOCKET.lock], 0
|
|
mov ebx, eax
|
|
|
|
ret
|
|
|
|
.error_release:
|
|
mov [ebx + SOCKET.lock], 0
|
|
.error:
|
|
xor ebx, ebx
|
|
ret
|
|
endp
|
|
|
|
;; [53.11] Get specified number of bytes from rx buffer
|
|
; Number of bytes in rx buffer can be less than requested size. In this case,
|
|
; only available number of bytes is read.
|
|
; This function can return 0 in two cases: if there's no data to read, and if
|
|
; an error occured. Behavior should be changed.
|
|
;
|
|
; @param EBX is socket number
|
|
; @param ECX is pointer to application buffer
|
|
; @param EDX is application buffer size (number of bytes to read)
|
|
; @return number of bytes read or 0 (error) in EAX
|
|
;;
|
|
proc socket_read_packet stdcall
|
|
; DEBUGF 1, "socket_read_packet(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx ; get real socket address
|
|
or eax, eax
|
|
jz .error
|
|
|
|
lea ebx, [eax + SOCKET.lock]
|
|
call wait_mutex
|
|
|
|
mov ebx, eax
|
|
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes
|
|
test eax, eax ; if count of bytes is zero..
|
|
jz .exit ; exit function (eax will be zero)
|
|
|
|
test edx, edx ; if buffer size is zero, copy all data
|
|
jz .copy_all_bytes
|
|
cmp edx, eax ; if buffer size is larger then the bytes of data, copy all data
|
|
jge .copy_all_bytes
|
|
|
|
sub eax, edx ; store new count (data bytes in buffer - bytes we're about to copy)
|
|
mov [ebx + SOCKET.rxDataCount], eax ;
|
|
push eax
|
|
mov eax, edx ; number of bytes we want to copy must be in eax
|
|
call .start_copy ; copy to the application
|
|
|
|
mov esi, ebx ; now we're going to copy the remaining bytes to the beginning
|
|
add esi, SOCKET.rxData ; we dont need to copy the header
|
|
mov edi, esi ; edi is where we're going to copy to
|
|
add esi, edx ; esi is from where we copy
|
|
pop ecx ; count of bytes we have left
|
|
push ecx ; push it again so we can re-use it later
|
|
shr ecx, 2 ; divide eax by 4
|
|
cld
|
|
rep movsd ; copy all full dwords
|
|
pop ecx
|
|
and ecx, 3
|
|
rep movsb ; copy remaining bytes
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET.lock], 0
|
|
ret ; at last, exit
|
|
|
|
.error:
|
|
xor eax, eax
|
|
ret
|
|
|
|
.copy_all_bytes:
|
|
xor esi, esi
|
|
mov [ebx + SOCKET.rxDataCount], esi ; store new count (zero)
|
|
call .start_copy
|
|
mov [ebx + SOCKET.lock], 0
|
|
ret
|
|
|
|
.start_copy:
|
|
mov edi, ecx
|
|
mov esi, ebx
|
|
add esi, SOCKET.rxData ; we dont need to copy the header
|
|
mov ecx, eax ; eax is count of bytes
|
|
push ecx
|
|
shr ecx, 2 ; divide eax by 4
|
|
cld ; copy all full dwords
|
|
rep movsd
|
|
pop ecx
|
|
and ecx, 3
|
|
rep movsb ; copy the rest bytes
|
|
retn ; exit, or go back to shift remaining bytes if any
|
|
endp
|
|
|
|
;; [53.4] Send data through DGRAM socket
|
|
;
|
|
; @param EBX is socket number
|
|
; @param ECX is application data size (number of bytes to send)
|
|
; @param EDX is pointer to application data buffer
|
|
; @return 0 (sent successfully) or -1 (error) in EAX
|
|
;;
|
|
proc socket_write stdcall
|
|
; DEBUGF 1, "socket_write(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx ; get real socket address
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov ebx, eax
|
|
|
|
mov eax, EMPTY_QUEUE
|
|
call dequeue
|
|
cmp ax, NO_BUFFER
|
|
je .error
|
|
|
|
; Save the queue entry number
|
|
push eax
|
|
|
|
; save the pointers to the data buffer & size
|
|
push edx
|
|
push ecx
|
|
|
|
; convert buffer pointer eax to the absolute address
|
|
mov ecx, IPBUFFSIZE
|
|
mul ecx
|
|
add eax, IPbuffs
|
|
|
|
mov edx, eax
|
|
|
|
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
|
|
|
|
; Fill in the IP header (some data is in the socket descriptor)
|
|
mov eax, [ebx + SOCKET.LocalIP]
|
|
mov [edx + IP_PACKET.SourceAddress], eax
|
|
mov eax, [ebx + SOCKET.RemoteIP]
|
|
mov [edx + IP_PACKET.DestinationAddress], eax
|
|
|
|
mov [edx + IP_PACKET.VersionAndIHL], 0x45
|
|
mov [edx + IP_PACKET.TypeOfService], 0
|
|
|
|
pop eax ; Get the UDP data length
|
|
push eax
|
|
|
|
add eax, 20 + 8 ; add IP header and UDP header lengths
|
|
xchg al, ah
|
|
mov [edx + IP_PACKET.TotalLength], ax
|
|
xor eax, eax
|
|
mov [edx + IP_PACKET.Identification], ax
|
|
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
|
|
mov [edx + IP_PACKET.TimeToLive], 0x20
|
|
mov [edx + IP_PACKET.Protocol], PROTOCOL_UDP
|
|
|
|
; Checksum left unfilled
|
|
mov [edx + IP_PACKET.HeaderChecksum], ax
|
|
|
|
; Fill in the UDP header (some data is in the socket descriptor)
|
|
mov ax, [ebx + SOCKET.LocalPort]
|
|
mov [edx + 20 + UDP_PACKET.SourcePort], ax
|
|
|
|
mov ax, [ebx + SOCKET.RemotePort]
|
|
mov [edx + 20 + UDP_PACKET.DestinationPort], ax
|
|
|
|
pop eax
|
|
push eax
|
|
|
|
add eax, 8
|
|
xchg al, ah
|
|
mov [edx + 20 + UDP_PACKET.Length], ax
|
|
|
|
; Checksum left unfilled
|
|
xor eax, eax
|
|
mov [edx + 20 + UDP_PACKET.Checksum], ax
|
|
|
|
pop ecx ; count of bytes to send
|
|
mov ebx, ecx ; need the length later
|
|
pop eax ; get callers ptr to data to send
|
|
|
|
; Get the address of the callers data
|
|
mov edi, [TASK_BASE]
|
|
add edi, TASKDATA.mem_start
|
|
add eax, [edi]
|
|
mov esi, eax
|
|
|
|
mov edi, edx
|
|
add edi, 28
|
|
cld
|
|
rep movsb ; copy the data across
|
|
|
|
; we have edx as IPbuffer ptr.
|
|
; Fill in the UDP checksum
|
|
; First, fill in pseudoheader
|
|
mov eax, [edx + IP_PACKET.SourceAddress]
|
|
mov [pseudoHeader], eax
|
|
mov eax, [edx + IP_PACKET.DestinationAddress]
|
|
mov [pseudoHeader + 4], eax
|
|
mov word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0 ; 0 + protocol
|
|
add ebx, 8
|
|
mov eax, ebx
|
|
xchg al, ah
|
|
mov [pseudoHeader + 10], ax
|
|
|
|
mov eax, pseudoHeader
|
|
mov [checkAdd1], eax
|
|
mov [checkSize1], word 12
|
|
mov eax, edx
|
|
add eax, 20
|
|
mov [checkAdd2], eax
|
|
mov eax, ebx
|
|
mov [checkSize2], ax ; was eax!! mjh 8/7/02
|
|
|
|
call checksum
|
|
|
|
; store it in the UDP checksum ( in the correct order! )
|
|
mov ax, [checkResult]
|
|
|
|
; If the UDP checksum computes to 0, we must make it 0xffff
|
|
; (0 is reserved for 'not used')
|
|
test ax, ax
|
|
jnz @f
|
|
mov ax, 0xffff
|
|
|
|
@@: xchg al, ah
|
|
mov [edx + 20 + UDP_PACKET.Checksum], ax
|
|
|
|
; Fill in the IP header checksum
|
|
GET_IHL ecx,edx ; get IP-Header length
|
|
stdcall checksum_jb,edx,ecx ; buf_ptr, buf_size
|
|
xchg al, ah
|
|
mov [edx + IP_PACKET.HeaderChecksum], ax
|
|
|
|
; Check destination IP address.
|
|
; If it is the local host IP, route it back to IP_RX
|
|
|
|
pop ebx
|
|
|
|
mov eax, NET1OUT_QUEUE
|
|
mov ecx, [edx + SOCKET.RemoteIP]
|
|
mov edx, [stack_ip]
|
|
cmp edx, ecx
|
|
jne .not_local
|
|
mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
call queue
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
.error:
|
|
or eax, -1
|
|
ret
|
|
endp
|
|
|
|
;; [53.7] Send data through STREAM socket
|
|
;
|
|
; @param EBX is socket number
|
|
; @param ECX is application data size (number of bytes to send)
|
|
; @param EDX is pointer to application data buffer
|
|
; @return 0 (sent successfully) or -1 (error) in EAX
|
|
;;
|
|
proc socket_write_tcp stdcall
|
|
local sockAddr dd ?
|
|
|
|
; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov ebx, eax
|
|
mov [sockAddr], ebx
|
|
|
|
; If the sockets window timer is nonzero, do not queue packet
|
|
cmp [ebx + SOCKET.wndsizeTimer], 0
|
|
jne .error
|
|
|
|
mov eax, EMPTY_QUEUE
|
|
call dequeue
|
|
cmp ax, NO_BUFFER
|
|
je .error
|
|
|
|
push eax
|
|
|
|
; Get the address of the callers data
|
|
mov edi, [TASK_BASE]
|
|
add edi, TASKDATA.mem_start
|
|
add edx, [edi]
|
|
mov esi, edx
|
|
|
|
pop eax
|
|
push eax
|
|
|
|
push ecx
|
|
mov bl, TH_ACK
|
|
stdcall build_tcp_packet, [sockAddr]
|
|
pop ecx
|
|
|
|
; Check destination IP address.
|
|
; If it is the local host IP, route it back to IP_RX
|
|
|
|
pop ebx
|
|
push ecx
|
|
|
|
mov eax, NET1OUT_QUEUE
|
|
mov edx, [stack_ip]
|
|
mov ecx, [sockAddr]
|
|
cmp edx, [ecx + SOCKET.RemoteIP]
|
|
jne .not_local
|
|
mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
pop ecx
|
|
push ebx ; save ipbuffer number
|
|
|
|
call queue
|
|
|
|
mov esi, [sockAddr]
|
|
|
|
; increament SND.NXT in socket
|
|
; Amount to increment by is in ecx
|
|
add esi, SOCKET.SND_NXT
|
|
call add_inet_esi
|
|
|
|
pop ebx
|
|
|
|
; Copy the IP buffer to a resend queue
|
|
; If there isn't one, dont worry about it for now
|
|
mov esi, resendQ
|
|
mov ecx, 0
|
|
|
|
.next_resendq:
|
|
cmp ecx, NUMRESENDENTRIES
|
|
je .exit ; None found
|
|
cmp dword[esi + 4], 0
|
|
je @f ; found one
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: push ebx
|
|
|
|
; OK, we have a buffer descriptor ptr in esi.
|
|
; resend entry # in ecx
|
|
; Populate it
|
|
; socket #
|
|
; retries count
|
|
; retry time
|
|
; fill IP buffer associated with this descriptor
|
|
|
|
stdcall net_socket_addr_to_num, [sockAddr]
|
|
mov [esi + 4], eax
|
|
mov byte[esi + 1], TCP_RETRIES
|
|
mov word[esi + 2], TCP_TIMEOUT
|
|
|
|
inc ecx
|
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
|
mov edi, resendBuffer - IPBUFFSIZE
|
|
|
|
@@: add edi, IPBUFFSIZE
|
|
loop @b
|
|
|
|
; we have dest buffer location in edi
|
|
pop eax
|
|
; convert source buffer pointer eax to the absolute address
|
|
mov ecx, IPBUFFSIZE
|
|
mul ecx
|
|
add eax, IPbuffs
|
|
mov esi, eax
|
|
|
|
; do copy
|
|
mov ecx, IPBUFFSIZE
|
|
cld
|
|
rep movsb
|
|
|
|
.exit:
|
|
xor eax, eax
|
|
ret
|
|
|
|
.error:
|
|
or eax, -1
|
|
ret
|
|
endp
|