kolibrios/kernel/branches/net/network/socket.inc
hidnplayr 8a7ebf6b32 fix in ARPcfg application
added pci ids of dec21x4x cards to netcfg
bugfixes in dex21x4x driver, pcnet32 driver, rtl8139 driver and sis900 driver
new network program (ICMP) to ping computers, uses new RAW socket code (experimental)

git-svn-id: svn://kolibrios.org@1541 a494cfbc-eb01-0410-851d-a64ba20cac60
2010-07-30 21:54:27 +00:00

1650 lines
32 KiB
PHP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; SOCKET.INC ;;
;; ;;
;; Written by hidnplayr@kolibrios.org, ;;
;; and Clevermouse. ;;
;; ;;
;; Based on code by mike.dld ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
virtual at 0
SOCKET:
.NextPtr dd ? ; pointer to next socket in list
.PrevPtr dd ? ; pointer to previous socket in list
.Number dd ? ; socket number
.lock dd ? ; lock mutex
.PID dd ? ; application process id
.Domain dd ? ; INET/UNIX/..
.Type dd ? ; RAW/UDP/TCP/...
.Protocol dd ? ; ICMP/IPv4/ARP/
.errorcode dd ?
.options dd ?
.state dd ?
.snd_proc dd ?
.rcv_proc dd ?
.end:
end virtual
virtual at SOCKET.end
IP_SOCKET:
.LocalIP dd ?
rd 3 ; for IPv6 addresses
.RemoteIP dd ?
rd 3 ; for IPv6 addresses
.end:
end virtual
virtual at IP_SOCKET.end
TCP_SOCKET:
.LocalPort dw ? ; In INET byte order
.RemotePort dw ? ; In INET byte order
.backlog dw ? ; Backlog
.backlog_cur dw ? ; current size of queue for un-accept-ed connections
.OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state)
.OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
.t_state dd ? ; TCB state
.t_rxtshift dd ?
.t_rxtcur dd ?
.t_dupacks dd ?
.t_maxseg dd ?
.t_force dd ?
.t_flags dd ?
;---------------
; RFC783 page 21
; send sequence
.SND_UNA dd ? ; sequence number of unack'ed sent Packets
.SND_NXT dd ? ; next send sequence number to use
.SND_UP dd ?
.SND_WL1 dd ? ; window minus one
.SND_WL2 dd ? ;
.ISS dd ? ; initial send sequence number
.SND_WND dd ? ; send window
; receive sequence
.RCV_WND dw ? ; receive window
.RCV_NXT dd ? ; next receive sequence number to use
.RCV_UP dd ?
.IRS dd ? ; initial receive sequence number
;---------------------
; Additional variables
; receive variables
.RCV_ADV dd ?
; retransmit variables
.SND_MAX dd ?
; congestion control
.SND_CWND dd ?
.SND_SSTHRESH dd ?
;----------------------
; Transmit timing stuff
.t_idle dd ?
.t_rtt dd ?
.t_rtseq dd ?
.t_srtt dd ?
.t_rttvar dd ?
.t_rttmin dd ?
.max_sndwnd dd ?
;-----------------
; Out-of-band data
.t_oobflags dd ?
.t_iobc dd ?
.t_softerror dd ?
;---------
; RFC 1323
.SND_SCALE db ? ; Scale factor
.RCV_SCALE db ?
.request_r_scale db ?
.requested_s_scale dd ?
.ts_recent dd ?
.ts_recent_age dd ?
.last_ack_sent dd ?
;-------
; Timers
.timer_retransmission dw ? ; rexmt
.timer_ack dw ?
.timer_persist dw ?
.timer_keepalive dw ? ; keepalive/syn timeout
.timer_timed_wait dw ? ; also used as 2msl timer
.end:
end virtual
virtual at IP_SOCKET.end
UDP_SOCKET:
.LocalPort dw ? ; In INET byte order
.RemotePort dw ? ; In INET byte order
.firstpacket db ?
.end:
end virtual
virtual at IP_SOCKET.end
ICMP_SOCKET:
.Identifier dw ? ;
.end:
end virtual
struc RING_BUFFER {
.start_ptr dd ? ; Pointer to start of buffer
.end_ptr dd ? ; pointer to end of buffer
.read_ptr dd ? ; Read pointer
.write_ptr dd ? ; Write pointer
.size dd ? ; Number of bytes buffered
.end:
}
virtual at 0
RING_BUFFER RING_BUFFER
end virtual
virtual at TCP_SOCKET.end
STREAM_SOCKET:
.rcv rd RING_BUFFER.end/4
.snd rd RING_BUFFER.end/4
.end:
end virtual
struct socket_queue_entry
.data_ptr dd ?
.buf_ptr dd ?
.data_size dd ?
.size:
ends
SOCKETBUFFSIZE equ 4096 ; in bytes
SOCKET_QUEUE_SIZE equ 10 ; maximum number ofincoming packets queued for 1 socket
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
SOCKET_QUEUE_LOCATION equ SOCKETBUFFSIZE - SOCKET_QUEUE_SIZE*socket_queue_entry.size - queue.data
uglobal
net_sockets rd 4
last_UDP_port dw ? ; These values give the number of the last used ephemeral port
last_TCP_port dw ? ;
endg
;-----------------------------------------------------------------
;
; SOCKET_init
;
;-----------------------------------------------------------------
macro SOCKET_init {
xor eax, eax
mov edi, net_sockets
mov ecx, 4
rep stosd
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jl @r
cmp ax, MAX_EPHEMERAL_PORT
jg @r
mov [last_UDP_port], ax
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jl @r
cmp ax, MAX_EPHEMERAL_PORT
jg @r
mov [last_TCP_port], ax
}
;-----------------------------------------------------------------
;
; Socket API (function 74)
;
;-----------------------------------------------------------------
align 4
sys_socket:
cmp ebx, 8 ; highest possible number
jg @f
lea ebx, [sock_sysfn_table + 4*ebx]
jmp dword [ebx]
@@:
cmp ebx, 255
jz SOCKET_debug
s_error:
DEBUGF 1,"socket error\n"
mov dword [esp+32], -1
ret
align 4
sock_sysfn_table:
dd SOCKET_open ; 0
dd SOCKET_close ; 1
dd SOCKET_bind ; 2
dd SOCKET_listen ; 3
dd SOCKET_connect ; 4
dd SOCKET_accept ; 5
dd SOCKET_send ; 6
dd SOCKET_receive ; 7
dd SOCKET_get_opt ; 8
; dd SOCKET_set_opt ; 9
;-----------------------------------------------------------------
;
; SOCKET_open
;
; IN: domain in ecx
; type in edx
; protocol in esi
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_open:
DEBUGF 1,"SOCKET_open: domain: %u, type: %u protocol: %x\n", ecx, edx, esi
call SOCKET_alloc
jz s_error
mov [eax + SOCKET.Domain], ecx
mov [eax + SOCKET.Type], edx
mov [eax + SOCKET.Protocol], esi
mov [esp+32], edi ; return socketnumber
cmp ecx, AF_INET4
jne .no_inet4
push [IP_LIST]
pop [eax + IP_SOCKET.LocalIP] ; fill in local ip number
call SOCKET_find_port ; fill in a local port number, application may change it later, or use this one
cmp edx, IP_PROTO_UDP
je .udp
cmp edx, IP_PROTO_TCP
je .tcp
cmp edx, SOCK_RAW
je .raw
.no_inet4:
ret
.tcp:
mov ebx, eax
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_create
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create
mov [ebx + SOCKET.snd_proc], SOCKET_send_tcp
mov [ebx + SOCKET.rcv_proc], SOCKET_receive_tcp
ret
.udp:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION)
pop eax
mov [eax + SOCKET.snd_proc], SOCKET_send_udp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
.raw:
; test esi, esi
; jz .ip
cmp esi, IP_PROTO_ICMP
je .icmp
ret
; .ip:
; push eax
; init_queue (eax + SOCKET_QUEUE_LOCATION)
; pop eax
;
; mov [eax + SOCKET.snd_proc], SOCKET_send_ip
; mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
;
; ret
.icmp:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION)
pop eax
mov [eax + SOCKET.snd_proc], SOCKET_send_icmp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
;-----------------------------------------------------------------
;
; SOCKET_bind
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------------------------
align 4
SOCKET_bind:
DEBUGF 1,"socket_bind: socknum: %u sockaddr: %x, length: %u\n", ecx, edx, esi
call SOCKET_num_to_ptr
jz s_error
cmp esi, 2
jl s_error
cmp word [edx], AF_INET4
je .af_inet4
cmp word [edx], AF_UNIX
je .af_unix
jmp s_error
.af_unix:
; TODO: write code here
mov dword [esp+32], 0
ret
.af_inet4:
DEBUGF 1,"af_inet4\n"
cmp esi, 6
jl s_error
mov ecx, [eax + SOCKET.Type]
mov bx, word [edx + 2]
test bx, bx
jz .use_preset_port
call SOCKET_check_port
jz s_error
DEBUGF 1,"using local port: %u\n", bx
mov word [eax + UDP_SOCKET.LocalPort], bx
.use_preset_port:
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\
[eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\
[eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_connect
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------------------------
align 4
SOCKET_connect:
DEBUGF 1,"SOCKET_connect: socknum: %u sockaddr: %x, length: %u\n", ecx, edx, esi
call SOCKET_num_to_ptr
jz s_error
cmp esi, 8
jl s_error
cmp word [edx], AF_INET4
je .af_inet4
jmp s_error
.af_inet4:
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
cmp [eax + SOCKET.Type], SOCK_RAW
je .raw
jmp s_error
.udp:
mov bx , word [edx + 2]
mov word [eax + UDP_SOCKET.RemotePort], bx
mov [eax + UDP_SOCKET.firstpacket], 0
DEBUGF 1,"remote port: %u\n",bx
mov ebx, dword [edx + 4]
mov dword [eax + IP_SOCKET.RemoteIP], ebx
DEBUGF 1,"remote ip: %u.%u.%u.%u\n",[edx+4]:1,[edx+5]:1,[edx+6]:1,[edx+7]:1
mov dword [esp+32], 0
ret
.tcp:
lea ebx, [eax + SOCKET.lock]
call wait_mutex
; fill in remote port and IP
mov bx , word [edx + 2]
mov [eax + TCP_SOCKET.RemotePort], bx
DEBUGF 1,"remote port: %u\n", bx
mov ebx, dword [edx + 4]
mov [eax + IP_SOCKET.RemoteIP], ebx
;;;;;
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
mov ebx, [TCP_sequence_num]
add [TCP_sequence_num], 6400
mov [eax + TCP_SOCKET.ISS], ebx
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_init
TCP_sendseqinit eax
;;;; mov [ebx + TCP_SOCKET.timer_retransmission], ;; todo: create macro to set retransmission timer
push eax
call TCP_output
pop eax
mov [eax + SOCKET.lock], 0
mov dword [esp+32], 0 ; success!
ret
.raw:
push dword [edx + 4]
pop dword [eax + IP_SOCKET.RemoteIP]
mov dword [esp+32], 0 ; success!
ret
;-----------------------------------------------------------------
;
; SOCKET_listen
;
; IN: socket number in ecx
; backlog in edx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_listen:
DEBUGF 1,"SOCKET_listen: socknum: %u backlog: %u\n", ecx, edx
call SOCKET_num_to_ptr
jz s_error
cmp word [eax + SOCKET.Domain], AF_INET4
jne s_error
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jne s_error
; TODO: check local port number
cmp edx, MAX_backlog
jle .ok
mov edx, MAX_backlog
.ok:
mov [eax + TCP_SOCKET.backlog], dx
mov [eax + TCP_SOCKET.t_state], TCB_LISTEN
or [eax + SOCKET.options], SO_ACCEPTCON
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_accept
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_accept:
DEBUGF 1,"SOCKET_accept: socknum: %u sockaddr: %x, length: %u\n", ecx, edx, esi
call SOCKET_num_to_ptr
jz s_error
cmp word [eax + SOCKET.Domain], AF_INET4
je .af_inet4
jmp s_error
.af_inet4:
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
jmp s_error
.tcp:
lea ebx, [eax + SOCKET.lock]
call wait_mutex
movzx ebx, [eax + TCP_SOCKET.backlog_cur]
test ebx, ebx
jz .unlock_err
dec [eax + TCP_SOCKET.backlog_cur] ;;;;
mov eax, [eax + TCP_SOCKET.end + (ebx-1)*4] ;;;;;
mov [eax + SOCKET.lock], 0 ;;;;
mov dword [esp+32], 0 ;;;;
call TCP_output ;;;;;
ret
.unlock_err:
mov [eax + SOCKET.lock], 0
jmp s_error
;-----------------------------------------------------------------
;
; SOCKET_close
;
; IN: socket number in ecx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_close:
DEBUGF 1,"SOCKET_close: socknum: %u\n", ecx
call SOCKET_num_to_ptr
jz s_error
cmp [eax + SOCKET.Domain], AF_INET4
jne s_error
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .free
cmp [eax + SOCKET.Type], IP_PROTO_ICMP
je .free
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
jmp s_error
.tcp:
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ; state must be LISTEN, SYN_SENT or CLOSED
jl .free
call TCP_output
mov dword [esp+32], 0
ret
.free:
call SOCKET_free
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_receive
;
; IN: socket number in ecx
; addr to buffer in edx
; length of buffer in esi
; flags in edi
; OUT: eax is number of bytes copied, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_receive:
DEBUGF 1,"SOCKET_receive: socknum: %u bufaddr: %x, buflength: %u, flags: %x, ", ecx, edx, esi, edi
call SOCKET_num_to_ptr
jz s_error
jmp [eax + SOCKET.rcv_proc]
align 4
SOCKET_receive_dgram:
DEBUGF 1,"SOCKET_receive: DGRAM\n"
mov ebx, esi
mov edi, edx ; addr to buffer
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, s_error ; destroys esi and ecx
mov ecx, [esi + socket_queue_entry.data_size]
DEBUGF 1,"Got %u bytes of data\n", ecx
cmp ecx, ebx
jg .too_small
push [esi + socket_queue_entry.buf_ptr] ; save the buffer addr so we can clear it later
mov esi, [esi + socket_queue_entry.data_ptr]
DEBUGF 1,"Source buffer: %x, real addr: %x\n", [esp], esi
mov dword[esp+32+4], ecx ; return number of bytes copied
; copy the data
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
call kernel_free ; remove the packet
ret
.too_small:
DEBUGF 1,"Buffer too small...\n"
jmp s_error
align 4
SOCKET_receive_tcp:
DEBUGF 1,"SOCKET_receive: TCP\n"
mov ecx, esi
mov edi, edx
add eax, STREAM_SOCKET.rcv
call SOCKET_ring_read
call SOCKET_ring_free
mov dword[esp+32], ecx ; return number of bytes copied
ret
;-----------------------------------------------------------------
;
; SOCKET_send
;
;
; IN: socket number in ecx
; pointer to data in edx
; datalength in esi
; flags in edi
; OUT: -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_send:
DEBUGF 1,"SOCKET_send: socknum: %u data ptr: %x, length: %u, flags: %x, ", ecx, edx, esi, edi
call SOCKET_num_to_ptr
jz s_error
jmp [eax + SOCKET.snd_proc]
align 4
SOCKET_send_udp:
DEBUGF 1,"SOCKET_send: UDP\n"
mov ecx, esi
mov esi, edx
call UDP_output
mov dword [esp+32], 0
ret
align 4
SOCKET_send_tcp:
DEBUGF 1,"SOCKET_send: TCP\n"
push eax
mov ecx, esi
mov esi, edx
add eax, STREAM_SOCKET.snd
call SOCKET_ring_write
pop eax
call TCP_output
mov [esp+32], eax
ret
;align 4
;SOCKET_send_ip:
;
; DEBUGF 1,"type: IP\n"
;
; mov ecx, esi
; mov esi, edx
;
; call IPv4_output_raw
;
; mov dword [esp+32], eax
; ret
align 4
SOCKET_send_icmp:
DEBUGF 1,"SOCKET_send: ICMP\n"
mov ecx, esi
call ICMP_output_raw
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_get_options
;
; IN: ecx = socket number
; edx = pointer to the options:
; dd level, optname, optval, optlen
; OUT: -1 on error
;
; At moment, uses only pseudo-optname -2 for get last_ack_number for TCP.
; TODO: find best way to notify that send()'ed data were acknowledged
; Also pseudo-optname -3 is valid and returns socket state, one of TCB_*.
;
;-----------------------------------------------------------------
align 4
SOCKET_get_opt:
DEBUGF 1,"SOCKET_get_opt\n"
call SOCKET_num_to_ptr
jz s_error
cmp dword [edx], IP_PROTO_TCP
jnz s_error
cmp dword [edx+4], -2
jz @f
cmp dword [edx+4], -3
jnz s_error
@@:
; mov eax, [edx+12]
; test eax, eax
; jz .fail
; cmp dword [eax], 4
; mov dword [eax], 4
; jb .fail
; stdcall net_socket_num_to_addr, ecx
; test eax, eax
; jz .fail
; ; todo: check that eax is really TCP socket
; mov ecx, [eax + TCP_SOCKET.last_ack_number]
; cmp dword [edx+4], -2
; jz @f
; mov ecx, [eax + TCP_SOCKET.state]
@@:
mov eax, [edx+8]
test eax, eax
jz @f
mov [eax], ecx
@@:
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_debug
;
; Copies socket variables to application buffer
;
; IN: ecx = socket number
; edx = pointer to buffer
;
; OUT: -1 on error
;-----------------------------------------------------------------
align 4
SOCKET_debug:
DEBUGF 1,"socket_debug\n"
call SOCKET_num_to_ptr
jz s_error
mov esi, eax
mov edi, edx
mov ecx, SOCKETBUFFSIZE/4
rep movsd
mov dword [esp+32], 0
ret
;-----------------------------------------------------------------
;
; SOCKET_find_port
;
; Fills in the local port number for TCP and UDP sockets
; This procedure always works because the number of sockets is
; limited to a smaller number then the number of possible ports
;
; IN: eax = socket pointer
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_find_port:
DEBUGF 1,"SOCKET_find_port\n"
push ebx esi ecx
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
jmp .error
.done:
mov [eax + UDP_SOCKET.LocalPort], bx
.error:
pop ecx esi ebx
ret
.udp:
mov bx, [last_UDP_port]
call .findit
mov [last_UDP_port], bx
jmp .done
.tcp:
mov bx, [last_TCP_port]
call .findit
mov [last_TCP_port], bx
jmp .done
.restart:
mov bx, MIN_EPHEMERAL_PORT
.findit:
inc bx
cmp bx, MAX_EPHEMERAL_PORT
jz .restart
call SOCKET_check_port
jz .findit
ret
;-----------------------------------------------------------------
;
; SOCKET_check_port
;
; Checks if a local port number is unused
; If the proposed port number is unused, it is filled in in the socket structure
;
; IN: eax = socket ptr (to find out if its a TCP/UDP socket)
; bx = proposed socket number
;
; OUT: ZF = cleared on error
;
;-----------------------------------------------------------------
align 4
SOCKET_check_port:
DEBUGF 1,"SOCKET_check_port\n"
mov ecx, [eax + SOCKET.Type]
mov esi, net_sockets
.next_socket:
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .port_ok
cmp [esi + SOCKET.Type], ecx
jne .next_socket
cmp [esi + UDP_SOCKET.LocalPort], bx
jne .next_socket
DEBUGF 1,"local port %u already in use\n", bx
ret
.port_ok:
mov [eax + UDP_SOCKET.LocalPort], bx
or bx, bx ; set the zero-flag
ret
;-----------------------------------------------------------------
;
; SOCKET_input
;
; Updates a (stateless) socket with received data
;
; Note: the mutex should already be set !
;
; IN: eax = socket ptr
; ebx = pointer to device struct
; ecx = data size
; esi = ptr to data
; [esp] = ptr to buf
; [esp + 4] = buf size
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_input:
DEBUGF 1,"SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
mov dword[esp+4], ecx
push esi
mov esi, esp
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, SOCKET_input.full
DEBUGF 1,"SOCKET_input: queued packet successfully\n"
add esp, socket_queue_entry.size
mov [eax + SOCKET.lock], 0
jmp SOCKET_notify_owner
.full:
DEBUGF 2,"SOCKET_input: socket %x is full!\n", eax
mov [eax + SOCKET.lock], 0
call kernel_free
add esp, 8
ret
;--------------------------
;
; eax = ptr to ring struct (just a buffer of the right size)
;
align 4
SOCKET_ring_create:
mov esi, eax
stdcall create_ring_buffer, SOCKET_MAXDATA, PG_SW
DEBUGF 1,"SOCKET_ring_created: %x\n", eax
mov [esi + RING_BUFFER.start_ptr], eax
mov [esi + RING_BUFFER.write_ptr], eax
mov [esi + RING_BUFFER.read_ptr], eax
mov [esi + RING_BUFFER.size], 0
add eax, SOCKET_MAXDATA
mov [esi + RING_BUFFER.end_ptr], eax
ret
;-----------------------------------------------------------------
;
; SOCKET_ring_write
;
; Adds data to a stream socket, and updates write pointer and size
;
; IN: eax = ptr to ring struct
; ecx = data size
; esi = ptr to data
;
; OUT: ecx = number of bytes stored
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_write:
DEBUGF 1,"SOCKET_ring_write: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx
add [eax + RING_BUFFER.size], ecx
cmp [eax + RING_BUFFER.size], SOCKET_MAXDATA
jg .too_large
.copy:
mov edi, [eax + RING_BUFFER.write_ptr]
DEBUGF 2,"SOCKET_ring_write: %u bytes from %x to %x\n", ecx, esi, edi
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
cmp edi, [eax + RING_BUFFER.end_ptr]
jge .wrap
mov [eax + RING_BUFFER.write_ptr], edi
ret
.wrap:
sub edi, SOCKET_MAXDATA
mov [eax + RING_BUFFER.write_ptr], edi
ret
.too_large:
mov ecx, SOCKET_MAXDATA ; calculate number of bytes available in buffer
sub ecx, [eax + RING_BUFFER.size]
jge .full
mov [eax + RING_BUFFER.size], SOCKET_MAXDATA ; update size, we will fill buffer completely
jmp .copy
.full:
DEBUGF 2,"SOCKET_ring_write: ring buffer is full!\n"
xor ecx, ecx
ret
;-----------------------------------------------------------------
;
; SOCKET_ring_read
;
; reads the data, BUT DOES NOT CLEAR IT FROM MEMORY YET
;
; IN: eax = ptr to ring struct
; ecx = buffer size
; edi = ptr to buffer
;
; OUT: ecx = number of bytes read
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_read:
DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u\n", eax, edi, ecx
cmp ecx, [eax + RING_BUFFER.size]
jg .less_data
.copy:
mov esi, [eax + RING_BUFFER.read_ptr]
DEBUGF 2,"SOCKET_ring_read: %u bytes from %x to %x\n", ecx, esi, edi
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
; .no_data_at_all:
ret
.less_data:
mov ecx, [eax + RING_BUFFER.size]
test ecx, ecx
; jz .no_data_at_all
jmp .copy
;-----------------------------------------------------------------
;
; SOCKET_ring_free
;
; Free's some bytes from the ringbuffer
;
; IN: eax = ptr to ring struct
; ecx = data size
;
; OUT: ecx = number of bytes free-ed
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_free:
DEBUGF 1,"SOCKET_ring_free: %u bytes from ring %x\n", ecx, eax
sub [eax + RING_BUFFER.size], ecx
jl .sumthinwong
add [eax + RING_BUFFER.read_ptr], ecx
mov edx, [eax + RING_BUFFER.end_ptr]
cmp [eax + RING_BUFFER.read_ptr], edx
jl @f
sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA
@@:
ret
.sumthinwong: ; we could free all available bytes, but that would be stupid, i guess..
add [eax + RING_BUFFER.size], ecx
xor ecx, ecx
ret
;-----------------------------------------------------------------
;
; SOCKET_notify_owner
;
; notify's the owner of a socket that something happened
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_notify_owner:
DEBUGF 1,"SOCKET_notify_owner: %x\n", eax
call SOCKET_check
jz .error
push ecx esi
; socket exists, now try to flag an event to the application
mov eax, [eax + SOCKET.PID]
mov ecx, 1
mov esi, TASK_DATA + TASKDATA.pid
.next_pid:
cmp [esi], eax
je .found_pid
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next_pid
; PID not found, TODO: close socket!
jmp .error2
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK
mov [check_idle_semaphore], 200
DEBUGF 1,"SOCKET_notify_owner: succes!\n"
.error2:
pop esi ecx
.error:
ret
;--------------------------------------------------------------------
;
; SOCKET_alloc
;
; 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).
;
; IN: /
; OUT: eax = 0 on error, socket ptr otherwise
; edi = socket number
; ZF = cleared on error
;
;--------------------------------------------------------------------
align 4
SOCKET_alloc:
push ecx ebx
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax
or eax, eax
jz .exit
; zero-initialize allocated memory
push eax edi
mov edi, eax
mov ecx, SOCKETBUFFSIZE / 4
xor eax, eax
rep stosd
pop edi eax
; set send-and receive procedures to return -1
mov [eax + SOCKET.snd_proc], s_error
mov [eax + SOCKET.rcv_proc], s_error
; find first free socket number and use it
xor ecx, ecx
.next_socket_number:
inc ecx
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .last_socket
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
jmp .next_socket_number
.last_socket:
mov [eax + SOCKET.Number], ecx
DEBUGF 1, "SOCKET_alloc: number=%u\n", ecx
mov edi, ecx
; Fill in PID
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
; add socket to the list by re-arranging some pointers
mov ebx, [net_sockets + SOCKET.NextPtr]
mov [eax + SOCKET.PrevPtr], net_sockets
mov [eax + SOCKET.NextPtr], ebx
test ebx, ebx
jz @f
add ebx, SOCKET.lock ; lock the next socket
call wait_mutex
sub ebx, SOCKET.lock
mov [ebx + SOCKET.PrevPtr], eax
mov [ebx + SOCKET.lock], 0 ; and unlock it again
@@:
mov [net_sockets + SOCKET.NextPtr], eax
or eax, eax ; used to clear zero flag
.exit:
pop ebx ecx
ret
;----------------------------------------------------
;
; SOCKET_free
;
; Free socket data memory and remove socket from the list
;
; IN: eax = socket ptr
; OUT: /
;
;----------------------------------------------------
align 4
SOCKET_free:
DEBUGF 1, "SOCKET_free: %x\n", eax
call SOCKET_check
jz .error
push ebx
lea ebx, [eax + SOCKET.lock]
call wait_mutex
DEBUGF 1, "SOCKET_free: freeing socket..\n"
cmp [eax + SOCKET.Domain], AF_INET4
jnz .no_tcp
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jnz .no_tcp
mov ebx, eax
stdcall kernel_free, [ebx + STREAM_SOCKET.rcv + RING_BUFFER.start_ptr]
stdcall kernel_free, [ebx + STREAM_SOCKET.snd + RING_BUFFER.start_ptr]
mov eax, ebx
.no_tcp:
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
DEBUGF 1, "SOCKET_free: linking socket %x to socket %x\n", eax, ebx
test eax, eax
jz @f
mov [eax + SOCKET.NextPtr], ebx
@@:
test ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
@@:
call kernel_free
pop ebx
DEBUGF 1, "SOCKET_free: success!\n"
.error:
ret
; IN: socket nr in ebx
; OUT: socket nr in eax
; preserves edx
align 4
SOCKET_fork:
;; Exit if backlog queue is full
; mov ax, [ebx + TCP_SOCKET.backlog_cur]
; cmp ax, [ebx + TCP_SOCKET.backlog]
; jae .exit
; Allocate new socket
call SOCKET_alloc
;;; jz .fail
; Copy structure from current socket to new, (including lock!)
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket
lea esi, [edx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4
rep movsd
;; Push pointer to new socket to queue
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur]
; inc [ebx + TCP_SOCKET.backlog_cur]
; mov [ebx + TCP_SOCKET.end + ecx*4], eax
;;;; mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address
ret
;---------------------------------------------------
;
; SOCKET_num_to_ptr
;
; Get socket structure address by its number
;
; IN: ecx = socket number
; OUT: eax = 0 on error, socket ptr otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_num_to_ptr:
DEBUGF 1,"SOCKET_num_to_ptr: %u ", ecx
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .error
cmp [eax + SOCKET.Number], ecx
jne .next_socket
test eax, eax
DEBUGF 1,"(%x)\n", eax
.error:
ret
;---------------------------------------------------
;
; SOCKET_ptr_to_num
;
; Get socket number by its address
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, socket num otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_ptr_to_num:
DEBUGF 1,"SOCKET_ptr_to_num: %x ", eax
call SOCKET_check
jz .error
mov eax, [eax + SOCKET.Number]
DEBUGF 1,"(%u)\n", eax
.error:
ret
;---------------------------------------------------
;
; SOCKET_check
;
; checks if the given value is really a socket ptr
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, unchanged otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_check:
DEBUGF 1,"SOCKET_check: %x\n", eax
push ebx
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .done
cmp ebx, eax
jnz .next_socket
.done:
mov eax, ebx
test eax, eax
pop ebx
ret
;---------------------------------------------------
;
; SOCKET_check_owner
;
; checks if the caller application owns the socket
;
; IN: eax = socket ptr
; OUT: ZF = true/false
;
;---------------------------------------------------
align 4
SOCKET_check_owner:
DEBUGF 1,"SOCKET_check_owner: %x\n", eax
push ebx
mov ebx, [TASK_BASE]
mov ebx, [ecx + TASKDATA.pid]
cmp [eax + SOCKET.PID], ebx
pop ebx
ret
;---------------------------------------------------
;
; SOCKET_process_end
;
; Kernel calls this function when a certain process ends
; This function will check if the process had any open sockets
; And update them accordingly
;
; IN: eax = pid
; OUT: /
;
;------------------------------------------------------
align 4
SOCKET_process_end:
DEBUGF 1,"SOCKET_process_end: %x\n", eax
push ebx
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
.test_socket:
test ebx, ebx
jz .done
cmp [ebx + SOCKET.PID], eax
jne .next_socket
DEBUGF 1,"closing socket %x", eax, ebx
mov [ebx + SOCKET.PID], 0
cmp [ebx + SOCKET.Type], IP_PROTO_UDP
je .udp
cmp [ebx + SOCKET.Type], IP_PROTO_TCP
je .tcp
jmp .next_socket ; kill all sockets for given PID
.udp:
mov eax, ebx
mov ebx, [ebx + SOCKET.NextPtr]
call SOCKET_free
jmp .test_socket
.tcp:
jmp .next_socket
.done:
pop ebx
ret