2007-03-21 23:58:33 +01:00
|
|
|
$Revision$
|
2007-01-06 02:15:21 +01:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;
|
|
|
|
;; SOCKET.INC
|
|
|
|
;;
|
|
|
|
;; Sockets constants, structures and functions
|
|
|
|
;;
|
|
|
|
;; Last revision: 11.11.2006
|
|
|
|
;;
|
|
|
|
;; 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
|
2007-02-28 09:52:06 +01:00
|
|
|
;;
|
|
|
|
;;
|
2007-01-06 02:15:21 +01:00
|
|
|
;; Changes history:
|
|
|
|
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net
|
|
|
|
;; 11.11.2006 - [Johnny_B] and [smb]
|
|
|
|
;;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
;
|
|
|
|
; Socket Descriptor + Buffer
|
|
|
|
;
|
|
|
|
; 0 1 2 3
|
|
|
|
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
|
|
;
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 0| Status ( of this buffer ) |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 4| Application Process ID |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 8| Local IP Address |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 12| Local IP Port | Unused ( set to 0 ) |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 16| Remote IP Address |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 20| Remote IP Port | Unused ( set to 0 ) |
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 24| Rx Data Count INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 28| TCB STATE INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 32| TCB Timer (seconds) INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 36| ISS (Inital Sequence # used by this connection ) INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 40| IRS ( Inital Receive Sequence # ) INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 44| SND.UNA Seq # of unack'ed sent packets INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 48| SND.NXT Next send seq # to use INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 52| SND.WND Send window INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 56| RCV.NXT Next expected receive sequence # INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 60| RCV.WND Receive window INET format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 64| SEG.LEN Segment length INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 68| SEG.WND Segment window INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
; 72| Retransmit queue # NOW WINDOW SIZE TIMER INTEL format|
|
|
|
|
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
2007-02-28 09:52:06 +01:00
|
|
|
; 76| RX offset from
|
2007-01-06 02:15:21 +01:00
|
|
|
; 76| RX Data |
|
|
|
|
; +-+-+-.......... -+
|
|
|
|
|
|
|
|
|
|
|
|
; so, define struct
|
|
|
|
struc SOCKET
|
2007-02-28 09:52:06 +01:00
|
|
|
{ .Status dd ? ;+00 - Status ( of this buffer )
|
|
|
|
.PID dd ? ;+04 - Application Process ID
|
2007-02-06 20:29:54 +01:00
|
|
|
.LocalIP dd ? ;+08 - Local IP Address
|
|
|
|
.LocalPort dw ? ;+12 - Local Port
|
|
|
|
.UnusedL dw ? ;+14 - may be removed in future
|
|
|
|
.RemoteIP dd ? ;+16 - Remote IP Address
|
|
|
|
.RemotePort dw ? ;+20 - Remote Port
|
|
|
|
.UnusedR dw ? ;+22 - may be removed in future
|
|
|
|
.rxDataCount dd ? ;+24 - Rx Data Count
|
|
|
|
.TCBState dd ? ;+28 - TCB STATE
|
2007-02-28 09:52:06 +01:00
|
|
|
.TCBTimer dd ? ;+32 - TCB Timer (seconds)
|
2007-02-06 20:29:54 +01:00
|
|
|
.ISS dd ? ;+36 - Initial Send Sequence
|
|
|
|
.IRS dd ? ;+40 - Initial Receive Sequence
|
|
|
|
.SND_UNA dd ? ;+44 - Sequence number of unack'ed sent packets
|
2007-02-28 09:52:06 +01:00
|
|
|
.SND_NXT dd ? ;+48 - Next send sequence number to use
|
2007-02-06 20:29:54 +01:00
|
|
|
.SND_WND dd ? ;+52 - Send window
|
|
|
|
.RCV_NXT dd ? ;+56 - Next receive sequence number to use
|
|
|
|
.RCV_WND dd ? ;+60 - Receive window
|
|
|
|
.SEG_LEN dd ? ;+64 - Segment length
|
|
|
|
.SEG_WND dd ? ;+68 - Segment window
|
2007-02-28 09:52:06 +01:00
|
|
|
.wndsizeTimer dd ? ;+72 - Retransmit queue # NOW WINDOW SIZE TIMER
|
2007-02-06 20:29:54 +01:00
|
|
|
.rxData dd ? ;+76 - receive data buffer here
|
2007-01-06 02:15:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual at 0
|
|
|
|
SOCKET SOCKET
|
|
|
|
end virtual
|
|
|
|
|
2007-02-28 09:52:06 +01:00
|
|
|
; simple macro calcing real memory address of SOCKET struct by socket's
|
2007-01-06 02:15:21 +01:00
|
|
|
macro Index2RealAddr reg
|
|
|
|
{
|
2007-02-06 20:29:54 +01:00
|
|
|
shl reg, 12
|
2007-02-28 09:52:06 +01:00
|
|
|
add reg, sockets
|
2007-01-06 02:15:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
;Constants
|
|
|
|
; current socket statuses
|
2007-02-06 20:29:54 +01:00
|
|
|
SOCK_EMPTY equ 0 ; socket not in use
|
|
|
|
SOCK_OPEN equ 1 ; open issued, but no data sent
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; TCP opening modes
|
2007-02-06 20:29:54 +01:00
|
|
|
SOCKET_PASSIVE equ 0
|
|
|
|
SOCKET_ACTIVE equ 1
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; is_localport_unused
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; scans through all the active sockets , looking to see if the
|
|
|
|
; port number specified in bx is in use as a localport number.
|
|
|
|
; This is useful when you want a to generate a unique local port
|
|
|
|
; number.
|
|
|
|
; On return, eax = 1 for free, 0 for in use
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
is_localport_unused:
|
|
|
|
mov al, bh
|
|
|
|
mov ah, bl
|
|
|
|
mov bx, ax
|
|
|
|
|
|
|
|
mov edx, SOCKETBUFFSIZE * NUM_SOCKETS
|
|
|
|
mov ecx, NUM_SOCKETS
|
2007-02-06 20:29:54 +01:00
|
|
|
mov eax, 0 ; Assume the return value is 'in use'
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
ilu1:
|
|
|
|
sub edx, SOCKETBUFFSIZE
|
|
|
|
cmp [edx + sockets + SOCKET.LocalPort], bx
|
2007-02-06 20:29:54 +01:00
|
|
|
loopnz ilu1 ; Return back if the socket is occupied
|
2007-01-06 02:15:21 +01:00
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
jz ilu_exit
|
|
|
|
inc eax ; return port not in use
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
ilu_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; get_free_socket
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
get_free_socket:
|
|
|
|
push ecx
|
|
|
|
mov eax, SOCKETBUFFSIZE * NUM_SOCKETS
|
|
|
|
mov ecx, NUM_SOCKETS
|
|
|
|
|
|
|
|
gfs1:
|
|
|
|
sub eax, SOCKETBUFFSIZE
|
|
|
|
cmp [eax + sockets + SOCKET.Status], dword SOCK_EMPTY
|
2007-02-06 20:29:54 +01:00
|
|
|
loopnz gfs1 ; Return back if the socket is occupied
|
2007-01-06 02:15:21 +01:00
|
|
|
mov eax, ecx
|
|
|
|
pop ecx
|
2007-02-06 20:29:54 +01:00
|
|
|
jz gfs_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
mov eax, 0xFFFFFFFF
|
|
|
|
|
|
|
|
gfs_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_open
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; find a free socket
|
|
|
|
; local port in ebx
|
|
|
|
; remote port in ecx
|
|
|
|
; remote ip in edx
|
|
|
|
; return socket # in eax, -1 if none available
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_open:
|
|
|
|
call get_free_socket
|
|
|
|
|
|
|
|
cmp eax, 0xFFFFFFFF
|
2007-02-06 20:29:54 +01:00
|
|
|
jz so_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; ax holds the socket number that is free. Get real address
|
|
|
|
push eax
|
|
|
|
Index2RealAddr eax
|
|
|
|
|
|
|
|
mov [eax + SOCKET.Status], dword SOCK_OPEN
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
xchg bh, bl
|
|
|
|
mov [eax + SOCKET.LocalPort], bx
|
|
|
|
xchg ch, cl
|
|
|
|
mov [eax + SOCKET.RemotePort], cx
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
mov ebx, [stack_ip]
|
2007-02-28 09:52:06 +01:00
|
|
|
mov [eax + SOCKET.LocalIP], ebx
|
|
|
|
mov [eax + SOCKET.RemoteIP], edx
|
2007-01-06 02:15:21 +01:00
|
|
|
mov [eax + SOCKET.rxDataCount], dword 0 ; recieved data count
|
|
|
|
|
2007-02-28 09:52:06 +01:00
|
|
|
mov esi, [TASK_BASE]
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ebx, [esi+TASKDATA.pid]
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [eax + SOCKET.PID], ebx ; save the process ID
|
2007-01-06 02:15:21 +01:00
|
|
|
pop eax ; Get the socket number back, so we can return it
|
|
|
|
|
|
|
|
so_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_open_tcp
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; Opens a TCP socket in PASSIVE or ACTIVE mode
|
|
|
|
; find a free socket
|
|
|
|
; local port in ebx ( intel format )
|
|
|
|
; remote port in ecx ( intel format )
|
|
|
|
; remote ip in edx ( in Internet byte order )
|
|
|
|
; Socket open mode in esi ( SOCKET_PASSIVE or SOCKET_ACTIVE )
|
|
|
|
; return socket # in eax, -1 if none available
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_open_tcp:
|
|
|
|
call get_free_socket
|
|
|
|
|
|
|
|
cmp eax, 0xFFFFFFFF
|
2007-02-06 20:29:54 +01:00
|
|
|
jz so_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; ax holds the socket number that is free. Get real address
|
|
|
|
push eax
|
|
|
|
Index2RealAddr eax
|
|
|
|
|
|
|
|
mov [sktAddr], eax
|
|
|
|
mov [eax], dword SOCK_OPEN
|
|
|
|
|
|
|
|
; TODO - check this works!
|
|
|
|
mov [eax + SOCKET.wndsizeTimer], dword 0 ; Reset the window timer.
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
xchg bh, bl
|
2007-02-28 09:52:06 +01:00
|
|
|
mov [eax + SOCKET.LocalPort], bx
|
2007-01-06 02:15:21 +01:00
|
|
|
; mov [eax + 12], byte bh ; Local port ( LS 16 bits )
|
|
|
|
; mov [eax + 13], byte bl ; Local port ( LS 16 bits )
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
xchg ch, cl
|
2007-02-28 09:52:06 +01:00
|
|
|
mov [eax + SOCKET.RemotePort], cx
|
2007-01-06 02:15:21 +01:00
|
|
|
; mov [eax + 20], ch ; Remote Port ( LS 16 bits )
|
|
|
|
; mov [eax + 21], cl ; Remote Port ( LS 16 bits )
|
|
|
|
|
|
|
|
mov ebx, [stack_ip]
|
2007-02-28 09:52:06 +01:00
|
|
|
mov [eax + SOCKET.LocalIP], ebx
|
|
|
|
mov [eax + SOCKET.RemoteIP], edx
|
|
|
|
mov [eax + SOCKET.rxDataCount], dword 0
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; Now fill in TCB state
|
|
|
|
mov ebx, TCB_LISTEN
|
|
|
|
cmp esi, SOCKET_PASSIVE
|
2007-02-06 20:29:54 +01:00
|
|
|
jz sot_001
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ebx, TCB_SYN_SENT
|
|
|
|
|
|
|
|
sot_001:
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [eax + SOCKET.TCBState], ebx ; Indicate the state of the TCB
|
2007-01-06 02:15:21 +01:00
|
|
|
|
2007-02-28 09:52:06 +01:00
|
|
|
mov esi, [TASK_BASE]
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ecx, [esi+TASKDATA.pid]
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [eax + SOCKET.PID], ecx ; save the process ID
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
cmp ebx, TCB_LISTEN
|
2007-02-06 20:29:54 +01:00
|
|
|
je sot_done
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; 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
|
2007-02-06 20:29:54 +01:00
|
|
|
je sot_done
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
push eax
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
mov bl, 0x02 ; SYN
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ecx, 0
|
|
|
|
|
|
|
|
call buildTCPPacket
|
|
|
|
|
|
|
|
mov eax, NET1OUT_QUEUE
|
|
|
|
|
|
|
|
mov edx, [stack_ip]
|
|
|
|
mov ecx, [sktAddr ]
|
|
|
|
mov ecx, [ecx + 16]
|
|
|
|
cmp edx, ecx
|
|
|
|
jne sot_notlocal
|
|
|
|
mov eax, IPIN_QUEUE
|
|
|
|
|
|
|
|
sot_notlocal:
|
|
|
|
; Send it.
|
|
|
|
pop ebx
|
|
|
|
call queue
|
|
|
|
|
|
|
|
mov esi, [sktAddr]
|
|
|
|
|
|
|
|
; increment SND.NXT in socket
|
|
|
|
add esi, 48
|
|
|
|
call inc_inet_esi
|
|
|
|
|
|
|
|
sot_done:
|
|
|
|
pop eax ; Get the socket number back, so we can return it
|
|
|
|
|
|
|
|
sot_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_close
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; returns 0 for ok, -1 for socket not open (fail)
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_close:
|
|
|
|
Index2RealAddr ebx
|
2007-02-06 20:29:54 +01:00
|
|
|
mov eax, 0xFFFFFFFF ; assume this operation will fail..
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp [ebx + SOCKET.Status], dword SOCK_EMPTY
|
2007-02-06 20:29:54 +01:00
|
|
|
jz sc_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; Clear the socket varaibles
|
|
|
|
xor eax, eax
|
|
|
|
mov edi, ebx
|
|
|
|
mov ecx, SOCKETHEADERSIZE
|
|
|
|
cld
|
|
|
|
rep stosb
|
|
|
|
|
|
|
|
sc_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_close_tcp
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; returns 0 for ok, -1 for socket not open (fail)
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_close_tcp:
|
|
|
|
; first, remove any resend entries
|
|
|
|
pusha
|
|
|
|
|
|
|
|
mov esi, resendQ
|
|
|
|
mov ecx, 0
|
|
|
|
|
|
|
|
sct001:
|
|
|
|
cmp ecx, NUMRESENDENTRIES
|
2007-02-06 20:29:54 +01:00
|
|
|
je sct003 ; None left
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp [esi], bl
|
2007-02-06 20:29:54 +01:00
|
|
|
je sct002 ; found one
|
2007-01-06 02:15:21 +01:00
|
|
|
inc ecx
|
|
|
|
add esi, 4
|
|
|
|
jmp sct001
|
|
|
|
|
|
|
|
sct002:
|
|
|
|
|
|
|
|
mov [esi], byte 0xFF
|
|
|
|
jmp sct001
|
|
|
|
|
|
|
|
sct003:
|
|
|
|
popa
|
|
|
|
|
|
|
|
Index2RealAddr ebx
|
|
|
|
mov [sktAddr], ebx
|
2007-02-06 20:29:54 +01:00
|
|
|
mov eax, 0xFFFFFFFF ; assume this operation will fail..
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp [ebx + SOCKET.Status], dword SOCK_EMPTY
|
2007-02-06 20:29:54 +01:00
|
|
|
jz sct_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; Now construct the response, and queue for sending by IP
|
|
|
|
mov eax, EMPTY_QUEUE
|
|
|
|
call dequeue
|
|
|
|
cmp ax, NO_BUFFER
|
2007-02-06 20:29:54 +01:00
|
|
|
je stl_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
push eax
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
mov bl, 0x11 ; FIN + ACK
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ecx, 0
|
|
|
|
mov esi, 0
|
|
|
|
|
|
|
|
call buildTCPPacket
|
|
|
|
|
|
|
|
mov ebx, [sktAddr]
|
|
|
|
|
|
|
|
; increament SND.NXT in socket
|
|
|
|
mov esi, 48
|
|
|
|
add esi, ebx
|
|
|
|
call inc_inet_esi
|
|
|
|
|
|
|
|
|
|
|
|
; Get the socket state
|
|
|
|
mov eax, [ebx + SOCKET.TCBState]
|
|
|
|
cmp eax, TCB_LISTEN
|
2007-02-06 20:29:54 +01:00
|
|
|
je destroyTCB
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp eax, TCB_SYN_SENT
|
2007-02-06 20:29:54 +01:00
|
|
|
je destroyTCB
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp eax, TCB_SYN_RECEIVED
|
2007-02-06 20:29:54 +01:00
|
|
|
je sct_finwait1
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp eax, TCB_ESTABLISHED
|
2007-02-06 20:29:54 +01:00
|
|
|
je sct_finwait1
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; assume CLOSE WAIT
|
|
|
|
; Send a fin, then enter last-ack state
|
|
|
|
mov eax, TCB_LAST_ACK
|
|
|
|
mov [ebx + SOCKET.TCBState], eax
|
|
|
|
xor eax, eax
|
|
|
|
jmp sct_send
|
|
|
|
|
|
|
|
sct_finwait1:
|
|
|
|
; Send a fin, then enter finwait2 state
|
|
|
|
mov eax, TCB_FIN_WAIT_1
|
|
|
|
mov [ebx + SOCKET.TCBState], eax
|
|
|
|
xor eax, eax
|
|
|
|
|
|
|
|
sct_send:
|
|
|
|
mov eax, NET1OUT_QUEUE
|
|
|
|
|
|
|
|
mov edx, [stack_ip]
|
|
|
|
mov ecx, [sktAddr ]
|
|
|
|
mov ecx, [ecx + 16]
|
|
|
|
cmp edx, ecx
|
|
|
|
jne sct_notlocal
|
|
|
|
mov eax, IPIN_QUEUE
|
|
|
|
|
|
|
|
sct_notlocal:
|
|
|
|
; Send it.
|
|
|
|
pop ebx
|
|
|
|
call queue
|
|
|
|
jmp sct_exit
|
|
|
|
|
|
|
|
destroyTCB:
|
|
|
|
pop eax
|
|
|
|
; Clear the socket varaibles
|
|
|
|
xor eax, eax
|
|
|
|
mov edi, ebx
|
|
|
|
mov ecx, SOCKETHEADERSIZE
|
|
|
|
cld
|
|
|
|
rep stosb
|
|
|
|
|
|
|
|
sct_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_poll
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; returns count in eax.
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_poll:
|
|
|
|
Index2RealAddr ebx
|
|
|
|
mov eax, [ebx + SOCKET.rxDataCount]
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_status
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; returns TCB state in eax.
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_status:
|
|
|
|
Index2RealAddr ebx
|
|
|
|
mov eax, [ebx + SOCKET.TCBState]
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_read
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; returns # of bytes remaining in eax, data in bl
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_read:
|
|
|
|
Index2RealAddr ebx
|
2007-02-06 20:29:54 +01:00
|
|
|
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes
|
2007-01-06 02:15:21 +01:00
|
|
|
mov ecx, 1
|
|
|
|
test eax, eax
|
2007-02-06 20:29:54 +01:00
|
|
|
jz sr2
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
dec eax
|
2007-02-06 20:29:54 +01:00
|
|
|
mov esi, ebx ; esi is address of socket
|
|
|
|
mov [ebx + SOCKET.rxDataCount], eax ; store new count
|
2007-01-06 02:15:21 +01:00
|
|
|
;movzx ebx, byte [ebx + SOCKET.rxData] ; get the byte
|
2007-02-06 20:29:54 +01:00
|
|
|
movzx ebx, byte [ebx + SOCKETHEADERSIZE] ; get the byte
|
2007-01-06 02:15:21 +01:00
|
|
|
add esi, SOCKETHEADERSIZE
|
|
|
|
mov edi, esi
|
|
|
|
inc esi
|
|
|
|
|
|
|
|
mov ecx, (SOCKETBUFFSIZE - SOCKETHEADERSIZE) / 4
|
|
|
|
cld
|
|
|
|
rep movsd
|
|
|
|
xor ecx, ecx
|
|
|
|
|
|
|
|
sr1:
|
|
|
|
jmp sor_exit
|
|
|
|
|
|
|
|
sr2:
|
|
|
|
xor bl, bl
|
|
|
|
|
|
|
|
sor_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_read_packet
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket # in ebx
|
|
|
|
; datapointer # in ecx
|
|
|
|
; buffer size in edx
|
|
|
|
; returns # of bytes copied in eax
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_read_packet:
|
|
|
|
Index2RealAddr ebx ; get real socket address
|
|
|
|
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 .copyallbytes
|
|
|
|
cmp edx, eax ; if buffer size is larger then the bytes of data, copy all data
|
|
|
|
jge .copyallbytes
|
|
|
|
|
|
|
|
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 .startcopy ; copy to the application
|
|
|
|
|
|
|
|
mov esi, ebx ; now we're going to copy the remaining bytes to the beginning
|
|
|
|
add esi, SOCKETHEADERSIZE ; 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
|
|
|
|
|
|
|
|
ret ; at last, exit
|
|
|
|
|
|
|
|
.copyallbytes:
|
|
|
|
xor esi, esi
|
|
|
|
mov [ebx + SOCKET.rxDataCount], esi ; store new count (zero)
|
|
|
|
|
|
|
|
.startcopy:
|
|
|
|
mov edi, ecx ;
|
|
|
|
add edi, std_application_base_address ; get data pointer to buffer in application
|
|
|
|
|
|
|
|
mov esi, ebx ;
|
|
|
|
add esi, SOCKETHEADERSIZE ; 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
|
|
|
|
|
|
|
|
.exit:
|
|
|
|
ret ; exit, or go back to shift remaining bytes if any
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_write
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket in ebx
|
|
|
|
; # of bytes to write in ecx
|
|
|
|
; pointer to data in edx
|
|
|
|
; returns 0 in eax ok, -1 == failed ( invalid socket, or
|
|
|
|
; could not queue IP packet )
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_write:
|
|
|
|
Index2RealAddr ebx
|
|
|
|
|
|
|
|
mov eax, 0xFFFFFFFF
|
|
|
|
; If the socket is invalid, return with an error code
|
|
|
|
cmp [ebx], dword SOCK_EMPTY
|
2007-02-06 20:29:54 +01:00
|
|
|
je sw_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
mov eax, EMPTY_QUEUE
|
|
|
|
call dequeue
|
|
|
|
cmp ax, NO_BUFFER
|
2007-02-06 20:29:54 +01:00
|
|
|
je sw_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; 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 + 8]
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [edx + 12], eax ; source IP
|
2007-01-06 02:15:21 +01:00
|
|
|
mov eax, [ebx + 16]
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [edx + 16], eax ; Destination IP
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
mov al, 0x45
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [edx], al ; Version, IHL
|
2007-01-06 02:15:21 +01:00
|
|
|
xor al, al
|
|
|
|
mov [edx + 1], al ; Type of service
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
pop eax ; Get the UDP data length
|
2007-01-06 02:15:21 +01:00
|
|
|
push eax
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
add eax, 20 + 8 ; add IP header and UDP header lengths
|
2007-01-06 02:15:21 +01:00
|
|
|
mov [edx + 2], ah
|
|
|
|
mov [edx + 3], al
|
|
|
|
xor al, al
|
|
|
|
mov [edx + 4], al
|
|
|
|
mov [edx + 5], al
|
|
|
|
mov al, 0x40
|
|
|
|
mov [edx + 6], al
|
|
|
|
xor al, al
|
|
|
|
mov [edx + 7], al
|
|
|
|
mov al, 0x20
|
|
|
|
mov [edx + 8], al
|
|
|
|
mov al, 17
|
|
|
|
mov [edx + 9], al
|
|
|
|
|
|
|
|
; Checksum left unfilled
|
|
|
|
xor ax, ax
|
|
|
|
mov [edx + 10], ax
|
|
|
|
|
|
|
|
; Fill in the UDP header ( some data is in the socket descriptor)
|
|
|
|
mov ax, [ebx + 12]
|
|
|
|
mov [edx + 20], ax
|
|
|
|
|
|
|
|
mov ax, [ebx + 20]
|
|
|
|
mov [edx + 20 + 2], ax
|
|
|
|
|
|
|
|
pop eax
|
|
|
|
push eax
|
|
|
|
|
|
|
|
add eax, 8
|
|
|
|
mov [edx + 20 + 4], ah
|
|
|
|
mov [edx + 20 + 5], al
|
|
|
|
|
|
|
|
; Checksum left unfilled
|
|
|
|
xor ax, ax
|
|
|
|
mov [edx + 20 + 6], ax
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
pop ecx ; count of bytes to send
|
|
|
|
mov ebx, ecx ; need the length later
|
|
|
|
pop eax ; get callers ptr to data to send
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; Get the address of the callers data
|
2007-02-28 09:52:06 +01:00
|
|
|
mov edi, [TASK_BASE]
|
2007-01-06 02:15:21 +01:00
|
|
|
add edi, TASKDATA.mem_start
|
|
|
|
add eax, [edi]
|
|
|
|
mov esi, eax
|
|
|
|
|
|
|
|
mov edi, edx
|
|
|
|
add edi, 28
|
|
|
|
cld
|
2007-02-06 20:29:54 +01:00
|
|
|
rep movsb ; copy the data across
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; we have edx as IPbuffer ptr.
|
|
|
|
; Fill in the UDP checksum
|
|
|
|
; First, fill in pseudoheader
|
|
|
|
mov eax, [edx + 12]
|
|
|
|
mov [pseudoHeader], eax
|
|
|
|
mov eax, [edx + 16]
|
|
|
|
mov [pseudoHeader+4], eax
|
2007-02-06 20:29:54 +01:00
|
|
|
mov ax, 0x1100 ; 0 + protocol
|
2007-01-06 02:15:21 +01:00
|
|
|
mov [pseudoHeader+8], ax
|
|
|
|
add ebx, 8
|
|
|
|
mov eax, ebx
|
|
|
|
mov [pseudoHeader+10], ah
|
|
|
|
mov [pseudoHeader+11], al
|
|
|
|
|
|
|
|
mov eax, pseudoHeader
|
|
|
|
mov [checkAdd1], eax
|
|
|
|
mov [checkSize1], word 12
|
|
|
|
mov eax, edx
|
|
|
|
add eax, 20
|
|
|
|
mov [checkAdd2], eax
|
|
|
|
mov eax, ebx
|
2007-02-06 20:29:54 +01:00
|
|
|
mov [checkSize2], ax ; was eax!! mjh 8/7/02
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
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')
|
|
|
|
cmp ax, 0
|
|
|
|
jne sw_001
|
|
|
|
mov ax, 0xffff
|
|
|
|
|
|
|
|
sw_001:
|
|
|
|
mov [edx + 20 + 6], ah
|
|
|
|
mov [edx + 20 + 7], al
|
|
|
|
|
|
|
|
; Fill in the IP header checksum
|
2007-02-06 20:29:54 +01:00
|
|
|
GET_IHL ecx,edx ; get IP-Header length
|
2007-01-06 02:15:21 +01:00
|
|
|
stdcall checksum_jb,edx,ecx ; buf_ptr, buf_size
|
|
|
|
|
|
|
|
mov [edx + 10], ah
|
|
|
|
mov [edx + 11], al
|
|
|
|
|
|
|
|
; 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 + 16]
|
|
|
|
mov edx, [stack_ip]
|
|
|
|
cmp edx, ecx
|
|
|
|
jne sw_notlocal
|
|
|
|
mov eax, IPIN_QUEUE
|
|
|
|
|
|
|
|
sw_notlocal:
|
|
|
|
; Send it.
|
|
|
|
call queue
|
|
|
|
|
|
|
|
xor eax, eax
|
|
|
|
|
|
|
|
sw_exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
|
|
; Function
|
|
|
|
; socket_write_tcp
|
|
|
|
;
|
|
|
|
; Description
|
|
|
|
; socket in ebx
|
|
|
|
; # of bytes to write in ecx
|
|
|
|
; pointer to data in edx
|
|
|
|
; returns 0 in eax ok, -1 == failed ( invalid socket, or
|
|
|
|
; could not queue IP packet )
|
|
|
|
;
|
|
|
|
;***************************************************************************
|
|
|
|
socket_write_tcp:
|
|
|
|
Index2RealAddr ebx
|
|
|
|
|
|
|
|
mov [sktAddr], ebx
|
|
|
|
|
|
|
|
mov eax, 0xFFFFFFFF
|
|
|
|
; If the socket is invalid, return with an error code
|
|
|
|
cmp [ebx], dword SOCK_EMPTY
|
2007-02-06 20:29:54 +01:00
|
|
|
je swt_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; If the sockets window timer is nonzero, do not queue packet
|
|
|
|
; TODO - done
|
|
|
|
cmp [ebx + SOCKET.wndsizeTimer], dword 0
|
|
|
|
jne swt_exit
|
|
|
|
|
|
|
|
mov eax, EMPTY_QUEUE
|
|
|
|
call dequeue
|
|
|
|
cmp ax, NO_BUFFER
|
2007-02-06 20:29:54 +01:00
|
|
|
je swt_exit
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
push eax
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
mov bl, 0x10 ; ACK
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
; Get the address of the callers data
|
2007-02-28 09:52:06 +01:00
|
|
|
mov edi, [TASK_BASE]
|
2007-01-06 02:15:21 +01:00
|
|
|
add edi, TASKDATA.mem_start
|
|
|
|
add edx, [edi]
|
|
|
|
mov esi, edx
|
|
|
|
|
|
|
|
pop eax
|
|
|
|
push eax
|
|
|
|
|
|
|
|
push ecx
|
|
|
|
call buildTCPPacket
|
|
|
|
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, [sktAddr ]
|
|
|
|
mov ecx, [ecx + 16]
|
|
|
|
cmp edx, ecx
|
|
|
|
jne swt_notlocal
|
|
|
|
mov eax, IPIN_QUEUE
|
|
|
|
|
|
|
|
swt_notlocal:
|
|
|
|
pop ecx
|
|
|
|
|
2007-02-06 20:29:54 +01:00
|
|
|
push ebx ; save ipbuffer number
|
2007-01-06 02:15:21 +01:00
|
|
|
|
|
|
|
call queue
|
|
|
|
|
|
|
|
mov esi, [sktAddr]
|
|
|
|
|
|
|
|
; increament SND.NXT in socket
|
|
|
|
; Amount to increment by is in ecx
|
|
|
|
add esi, 48
|
|
|
|
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
|
|
|
|
|
|
|
|
swt003:
|
|
|
|
cmp ecx, NUMRESENDENTRIES
|
2007-02-06 20:29:54 +01:00
|
|
|
je swt001 ; None found
|
2007-01-06 02:15:21 +01:00
|
|
|
cmp [esi], byte 0xFF
|
2007-02-06 20:29:54 +01:00
|
|
|
je swt002 ; found one
|
2007-01-06 02:15:21 +01:00
|
|
|
inc ecx
|
|
|
|
add esi, 4
|
|
|
|
jmp swt003
|
|
|
|
|
|
|
|
swt002:
|
|
|
|
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
|
|
|
|
|
|
|
|
mov eax, [sktAddr]
|
|
|
|
sub eax, sockets
|
2007-02-06 20:29:54 +01:00
|
|
|
shr eax, 12 ; get skt #
|
2007-01-06 02:15:21 +01:00
|
|
|
mov [esi], al
|
|
|
|
mov [esi + 1], byte TCP_RETRIES
|
|
|
|
mov [esi + 2], word TCP_TIMEOUT
|
|
|
|
|
|
|
|
inc ecx
|
|
|
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
|
|
|
mov edi, resendBuffer - IPBUFFSIZE
|
|
|
|
swt002a:
|
|
|
|
add edi, IPBUFFSIZE
|
|
|
|
loop swt002a
|
|
|
|
|
|
|
|
; 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
|
|
|
|
|
|
|
|
swt001:
|
|
|
|
xor eax, eax
|
|
|
|
|
|
|
|
swt_exit:
|
|
|
|
ret
|
|
|
|
|