; _______________________________________________________________________________________
;| |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_______________________________________________________________________________________|
;
; Description: Network library.
;
; Target OS: Any
;
; Dependencies:
;
; Notes:
;_________________________________________________________________________________________
module "Network library"
struct TSocketAddress
.saFamily dw ?
.saAddress rb 14
ends
struct TSocketAddressIn
.saFamily dw ?
.saPort dw ?
.saAddress dd ?
.saZero rb 8
ends
struct TAddrIn6
label .addr8 byte
label .addr16 word
.addr32 rd 4
ends
struct TSocketAddressIn6
.saFamily dw ?
.saPort dw ?
.saFlowInfo dd ?
.saAddress TAddrIn6
.saScopeID dd ?
ends
struct TSocketAddressUn
.saFamily dw ?
.saPath rb UNIX_PATH_MAX
ends
soReuseAddr = SO_REUSEADDR
soDontRoute = SO_DONTROUTE
soRecvBuffer = SO_RCVBUF
soSendBuffer = SO_SNDBUF
soRecvTimeout = SO_RCVTIMEO ; timeout in [ms] for all OSes
soSendTimeout = SO_SNDTIMEO ; timeout in [ms] for all OSes
soLinger = SO_LINGER ; timout in [s]
interface SocketCreate, .protocol_family, .socket_type, .protocol
interface SocketClose, .hSocket
interface SocketConnect, .hSocket, .pAddress
interface SocketBind, .hSocket, .pAddress
interface SocketListen, .hSocket, .maxPending
interface SocketAccept, .hSocket, .pAddress
interface SocketSend, .hSocket, .pBuffer, .DataLen, .flags
interface SocketReceive, .hSocket, .pBuffer, .BufferSize, .flags
interface SocketSendTo, .hSocket, .pBuffer, .DataLen, .flags, .pAddressTo
interface SocketReceiveFrom, .hSocket, .pBuffer, .BufferSize, .flags, .pAddressFrom
interface SocketGetOption, .hSocket, .idOption
interface SocketSetOption, .hSocket, .idOption, .Value
include "%TargetOS%/network.asm"
proc SocketSendStr, .hSocket, .hString
begin
pushad
stdcall StrLen, [.hString]
push eax
stdcall StrPtr, [.hString]
stdcall SocketSendAll, [.hSocket], eax ; remaining arguments from the stack.
popad
return
endp
proc SocketSendAll, .hSocket, .pData, .Size
begin
pushad
; stdcall GetTimestamp
; mov edi, eax
mov esi, [.pData]
mov ecx, [.Size]
jecxz .finish_ok
.loop:
stdcall SocketSend, [.hSocket], esi, ecx, 0
jc .finish_err
; OutputValue "Send bytes:", eax, 10, -1
add esi, eax
sub ecx, eax
jnz .loop
.finish_ok:
; stdcall GetTimestamp
; sub eax, edi
;
; OutputValue "SendAll time: ", eax, 10, -1
clc
popad
return
.finish_err:
mov [esp+4*regEAX], eax
popad
return
endp
struct __s_readln
.capacity = 1024
.buffer rb .capacity
.len dd ? ; the count of valid bytes in the buffer.
ends
; Reads a line from the socket and concatenate it to the provided
; string hString.
;
; If there is no data for a whole line, and the timeout expires,
; returns what has been read.
;
; All possible combinations of CR/LF are recognized as a line end.
; After end of line is detected, the remaining data read is returned
; in the buffer and the same pointer should be passed the next time
; the function is call.
;
; Arguments:
; .hSocket - the socket to be read;
; .pBuffer - the buffer, returned from the previous call to the procedure.
; should be NULL on the first call.
; .timeout - timeout in ms for detection of end-of-transmission.
;
; Returns:
; eax - the string with the line.
; edx - pointer to the buffer that to be passed to the procedure next time.
; can be NULL, if there is no more data to be processed. The buffer is
; allocated on the first call to the procedure. Should be free with FreeMem
; when not needed.
; CF = 1 on socket read error. In this case, ECX returns the error code.
;
proc SocketReadLine, .hSocket, .pBuffer, .timeout
begin
pushad
xor edx, edx
mov [esp+4*regEAX], edx
stdcall SocketSetOption, [.hSocket], soRecvTimeout, [.timeout]
stdcall StrNew
mov ebx, eax
mov edi, [.pBuffer]
test edi, edi
jnz .scan_buffer
stdcall GetMem, sizeof.__s_readln
mov edi, eax
mov [edi+__s_readln.len], eax
.read_loop:
stdcall SocketReceive, [.hSocket], edi, __s_readln.capacity, 0
jc .socket_error
mov [edi+__s_readln.len], eax
test eax, eax
jz .free_buffer ; the socket has been closed from the remote side and the buffer is empty.
.scan_buffer:
xor ecx, ecx
mov eax, edx
test eax, eax
jnz .eol_ok
.inner_loop:
mov al, [edi+ecx]
inc ecx
cmp al, $0d
je .end_of_line
cmp al, $0a
je .end_of_line
cmp ecx, [edi+__s_readln.len]
jne .inner_loop
;end of buffer
inc ecx
xor eax, eax
.end_of_line:
dec ecx
jz .cat_ok
stdcall StrCatMem, ebx, edi, ecx
.cat_ok:
mov [esp+4*regEAX], ebx
inc ecx
cmp ecx, [edi+__s_readln.len]
jb .eol_ok
mov edx, eax
jmp .read_loop
.eol_ok:
xor al, $0d xor $0a
cmp [edi+ecx], al
jne .line_ok
inc ecx
.line_ok:
; copy the buffer to the beginning.
lea esi, [edi+ecx]
sub ecx, [edi+__s_readln.len]
jnz .copy_buffer
.free_buffer:
stdcall FreeMem, edi
xor edi, edi
jmp .finish
.copy_buffer:
neg ecx
mov [edi+__s_readln.len], ecx
cmp esi, edi
je .finish
push edi
rep movsb
pop edi
.finish:
clc
.err:
pushf
cmp dword [esp+4*regEAX+4], 0
jne .str_ok
stdcall StrDel, ebx
.str_ok:
popf
mov [esp+4*regEDX], edi
popad
return
.socket_error:
cmp eax, serrTimeout
je .free_buffer
mov [esp+4*regECX], eax
stc
jmp .err
endp
proc SocketReadAllLines, .hSocket, .timeout
.pbuffer dd ?
.result dd ?
.eol dd ?
begin
pushad
stdcall SocketSetOption, [.hSocket], soRecvTimeout, [.timeout]
stdcall CreateArray, 4
mov edx, eax
stdcall GetMem, 1028
mov [.pbuffer], eax
stdcall StrNew
mov [.result], eax
xor eax, eax
mov [.eol], eax
.read_loop:
mov edi, [.pbuffer]
stdcall SocketReceive, [.hSocket], edi, 1024, 0
jnc .no_error
cmp eax, EAGAIN
jne .error_receive
jmp .finalize_ok
.no_error:
OutputValue "Socket receive:", eax, 10, -1
test eax, eax
jz .finalize_ok
mov esi, edi
lea ebx, [edi+eax] ; the end of the buffer.
mov eax, [.eol]
test eax, eax
jnz .eol_ok
.inner_loop:
cmp esi, ebx
je .end_of_buffer
lodsb
cmp al, $0d
je .end_of_line
cmp al, $0a
je .end_of_line
jmp .inner_loop
.end_of_buffer:
mov eax, esi
sub eax, edi
stdcall StrCatMem, [.result], edi, eax
jmp .read_loop
.end_of_line:
push eax
lea eax, [esi-1]
sub eax, edi
stdcall StrCatMem, [.result], edi, eax
stdcall AddArrayItems, edx, 1
pushd [.result]
popd [eax]
stdcall StrNew
mov [.result], eax
pop eax
cmp esi, ebx
jne .eol_ok
mov [.eol], eax
jmp .read_loop
.eol_ok:
xor al, $0d xor $0a
cmp [esi], al
jne .line_ok
inc esi
.line_ok:
xor eax, eax
mov [.eol], eax
mov edi, esi ; move the start of the string.
jmp .inner_loop
.finalize_ok:
clc
.finalize:
pushf
stdcall StrLen, [.result]
test eax, eax
jnz .add_last
stdcall StrDel, [.result]
jmp .finish
.add_last:
stdcall AddArrayItems, edx, 1
pushd [.result]
popd [eax]
.finish:
stdcall FreeMem, [.pbuffer]
popf
mov [esp+4*regEAX], edx
popad
return
.error_receive:
DebugMsg "Error receive"
stdcall GetErrorString, eax
mov ebx, eax
stdcall FileWriteString, [STDERR], eax
stdcall StrDel, ebx
stc
jmp .finalize
endp
endmodule