0,0 → 1,1107 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; 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 |
Property changes: |
Added: svn:keywords |
+Rev |
\ No newline at end of property |