;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; 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