diff --git a/.gitignore b/.gitignore index 3de2fdf..b2b5636 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,16 @@ dist/ build/ aminet/ Disk.info +private-test/ stuff/ +patches/ +.vamosrc +activate +test/report.txt +test/TestSuite.info +src/report.txt src/BSDSocket_Labels.s src/BSDSocket_Size.s src/bsdsocket_lib.fd src/bsdsocket_lvo.i src/*.Lib -.vamosrc -activate -test/ diff --git a/API.md b/API.md index 760c670..f4dab04 100644 --- a/API.md +++ b/API.md @@ -3,7 +3,9 @@ Most functions will return -2 if the bsdsocket.library is not open. -### Setup +--- + +## Setup #### ADDR=Socket Library Open @@ -16,11 +18,15 @@ Try to open bsdsocket.library version 4. * If needed, you'll be able to directly access library functions using this address. +--- + #### Socket Library Close Close bsdsocket.library. This is safe to call if the library is not open +--- + #### RESULT=Socket Set Nonblocking(Socket, IsNonblocking BOOL) Make a socket blocking (False, default), or nonblocking (True). @@ -29,6 +35,8 @@ Make a socket blocking (False, default), or nonblocking (True). * Result of IoctlSocket call. +--- + #### RESULT=Socket Reuse Addr(Socket) Make a listening socket reuse the address it's trying to bind to. @@ -38,9 +46,9 @@ You probably want to call this right before Socket Listen. * Result of setsockopt call. +--- - -### Connections +## Connections #### SOCKET=Socket Create Inet Socket @@ -51,10 +59,23 @@ Create a new Internet socket for reading or writing. * Socket number on success * -1 on failure +--- + #### RESULT=Socket Connect(Socket to IPAddress$, Port) -Attempt to connect to a remote host. Currently doesn't -support DNS lookups. +Attempt to connect to a remote IP address. + +##### Emulation vs. Physical/FPGA Amiga note + +On emulated Amigas (FS-UAE) with emulated `bsdsocket.library`, a failure in +`Socket Connect` due to giving the function a bad IP address allows you to reuse +the socket. On real or FPGA-based Amiga running real TCP/IP stacks (MiamiDX, +Roadshow), the socket will be made unusable and you'll need to +`Socket Close Socket` and reopen the socket with whatever parameters necessary. +Closing and opening `bsdsocket.library` has the same effect. + +Always close and re-open a socket if you have any `Socket Connect` failure and +any non-blocking polling loops time out! ##### Returns @@ -64,19 +85,24 @@ support DNS lookups. the socket with Socket Select and Socket Getsockopt Int to see if the connection succeeded * -11 port out of range +* -13 IP address has zero length + +--- #### RESULT=Socket Reuse Addr(Socket) Set a server socket to reuse an interface and port that had been used recently. You likely want this if you're building something that listens on a port for connections. This calls -setsockopt() for you. +`setsockopt()` for you. ##### Returns The result of calling setsockopt() while setting your socket to reuse addresses. +--- + #### RESULT=Socket Bind(Socket to IPAddress, Port) Attempt to bind a socket to a network interface. Use @@ -89,6 +115,8 @@ interfaces. * -1 on other error * -11 port out of range +--- + #### RESULT=Socket Listen(Socket) Start listening for connections. @@ -98,6 +126,8 @@ Start listening for connections. * 0 on success * -1 on failure +--- + #### NEW_SOCKET=Socket Accept(Socket) Get the socket that connected to this one. Wait for a connect @@ -115,11 +145,13 @@ socket non-blocking and use Fdsets and Select! * The remote socket number on success * -1 on failure -#### RESULT=Socket Async Wait Reading(Socket, Wait_ms) +--- + +#### RESULT=Socket Wait Async Reading(Socket, Wait_ms) Wait the given number of milliseconds for the nonblocking socket to be ready for reading. -Use this when you're waiting for a client to connect to you, or if you're waiting for -a remote socket to send you data. +Use on a listen socket to await new connections, or on a connected socket to await +incoming data packets. ##### Returns @@ -127,7 +159,9 @@ a remote socket to send you data. * -1 on error. Use `Socket Errno` for more detail. * 1 on success. -#### RESULT=Socket Async Wait Writing(Socket, Wait_ms) +--- + +#### RESULT=Socket Wait Async Writing(Socket, Wait_ms) Wait the given number of milliseconds for the nonblocking socket to be ready for writing. Use this when you're connecting to a remote server and want to know if the connection @@ -143,6 +177,8 @@ has been completed. checks will return 1. * 1 on success. +--- + #### RESULT=Socket Set Timeout(Socket, Wait_ms) Set a socket to timeout after Wait_ms milliseconds if reading or writing doesn't complete. @@ -152,18 +188,21 @@ Set a socket to timeout after Wait_ms milliseconds if reading or writing doesn't * 0 on success * -1 on error +--- + #### RESULT=Socket Close Socket(Socket) Close a socket. + ##### Returns * 0 on success * -1 on error +--- - -### Data Transfers +## Data Transfers #### SENT=Socket Send$(Socket, String$) @@ -194,6 +233,8 @@ End Proc * Number of characters sent * -1 on other error +--- + #### SENT=Socket Send(Socket, Data Pointer, Length) Send a block of data to a connected socket. @@ -203,6 +244,8 @@ Send a block of data to a connected socket. * Number of characters sent * -1 on other error +--- + #### DATA$=Socket Recv$(Socket, MaxLength) Retrieve at most MaxLength bytes from Socket, and put them into a string. @@ -212,7 +255,9 @@ If Len(DATA$) < MaxLength, you've read the last bit of data from the socket. * String of data, which is blank if there is no more data. -### LENGTH=Socket Recv(Socket to Dataptr, MaxLength) +--- + +#### LENGTH=Socket Recv(Socket to Dataptr, MaxLength) Retrieve at most MaxLength bytes from Socket, and put them into the memory address at Dataptr. @@ -222,9 +267,9 @@ address at Dataptr. * Count of bytes read * -1 on error +--- - -### Informational +## Informational #### HOST=Socket Get Host(Socket) @@ -234,6 +279,8 @@ Get the IPv4 (Long) host value the given socket is using. * Host as a long value +--- + #### PORT=Socket Get Port(Socket) Get the 16-bit port (Word) value the given socket is using. @@ -242,6 +289,8 @@ Get the 16-bit port (Word) value the given socket is using. * Port as a word value +--- + #### RESULT$=Socket Inet Ntoa$(Host) Turn a long Host address into a string. @@ -250,6 +299,8 @@ Turn a long Host address into a string. * IP address as string +--- + #### RESULT=Socket Errno Get the error from the last command. Note that this is @@ -260,6 +311,8 @@ not cleared on a successful command! Error number from last call. Look in for more details. +--- + #### RESULT=Socket Herrno Get the error from the last DNS resolver command. @@ -268,6 +321,8 @@ Get the error from the last DNS resolver command. Resolver error number (`h_errno`) from last call. +--- + #### RESULT$=Dns Get Address By Name$(Domain Name$) Look up the first IP address associated with this hostname. @@ -282,32 +337,8 @@ out. There's no way to set this timeout, or cancel or override it via AMOS. String with IP address, or blank string on error. -#### RESULT=Socket Status(Socket) -Returns basic connection information about a socket. - -##### Warning - -Since none of the socket processing of this extension happens in the -background, you're likely better off using other means to detect -socket status. - -* Disconnected: Try a recv and get zero bytes back. Check Socket Errno. -* Broken: Try a send. If it fails, check Socket Errno. -* Ready: Use the Async Wait functions. - -##### Returns - -Status of socket: - -* 0 = Closed -* 2 = Listening -* 6 = Connecting -* 7 = Connected - - - -### Low Level +## Low Level #### RESULT=Socket Setsockopt Int(Socket, Option, Value) @@ -319,6 +350,8 @@ Socket Reuse Addr(). * Result of setsockopt call +--- + #### RESULT=Socket Getsockopt Int(Socket, Option) Get a socket option. You probably want SO_ERROR, @@ -329,6 +362,8 @@ attempt a connection with a non-blocking socket. * Result of getsockopt call +--- + #### ADDR=Socket Fdset Zero(fd_set) Clear out the specified fd_set. @@ -338,6 +373,8 @@ Clear out the specified fd_set. * Address to that particular fd_set * -1 if fd_set out of range. You get 16 of them. +--- + #### ADDR=Socket Fdset Set(fd_set, Socket to Value BOOL) Set or clear a socket bit in an fd_set. diff --git a/README.md b/README.md index 4774d39..aa7d094 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Socket Library Close * [Hop to the Top: Bunny's Revenge](https://rabbit.robsmithdev.co.uk/) * The game uses this extension to send and receive high score information. I also did some of the art for the game! +* [Gopherized](https://allanon71.itch.io/gopherized) Doing something cool with the extension? [Contact me](https://theindustriousrabbit.com/about) and I'll add it to the list! @@ -168,10 +169,12 @@ throw a link to theindustriousrabbit.com somewhere! You can also find a donate link on [the About section on The Industrious Rabbit](https://theindustriousrabbit.com/about). -## Feedback? Bug reports? +## Feedback? Bug reports? Patches? -Go to the [About section on The Industrious Rabbit](https://theindustriousrabbit.com/about) -to contact me. +First, run `test/TestSuite` to generate a `report.txt` file. + +Then, go to the [About section on The Industrious Rabbit](https://theindustriousrabbit.com/about) +to contact me, send the `report.txt` along with the details on your issue. ## Changelog @@ -207,7 +210,20 @@ to contact me. * `Dns Get Address By Name$` * `Socket Recv$` -# Development +### 1.1.3 + +Internal release. + +### 1.1.4 (2024-05-02) + +* Fix bug in fdset macro where using D3 for a parameter could cause corruption. +* Copy a null-terminated copy of IP address for `SocketIPAddressPortToSockaddr`. +* Add test suite to exercise extension functionality. +* Fix several crash bugs found due to the test suite. +* Retructure API docs for easier reading. +* Improve build and release tooling. + +## Development ### Environment @@ -239,9 +255,25 @@ Run `bin/setup` to do most of the setups above. ### Debugging +#### Cross-platform + Modify data in the `DebugArea` and read it by `Peek`/`Deek`/`Leek`ing from the base address provided by `Socket Get Debug Area`. +#### WinUAE/FS-UAE + +In the debugger, set a memory breakpoint at `$100` for two written bytes: + +``` +w 0 100 2 +``` + +Then, in your code, clear those two bytes to stop execution at that point: + +```asm + CLR.W $100 +``` + ### Releasing #### Ubuntu/Debian diff --git a/bin/rebuild_raw_source b/bin/rebuild_raw_source index 2a1b19b..c6fa8e2 100755 --- a/bin/rebuild_raw_source +++ b/bin/rebuild_raw_source @@ -1,6 +1,6 @@ #!/bin/bash -for i in examples/*.amos; do +for i in examples/*.amos test/*.amos; do echo "$i" - ~/Projects/amostools/listamos -e AMOSPro_BSDSocket.Lib "${i}" > "${i%.amos}.basic" 2>/dev/null + ~/Projects/amostools/listamos -e src/AMOSPro_BSDSocket.Lib "${i}" > "${i%.amos}.basic" 2>/dev/null done diff --git a/bin/release b/bin/release index 43a82db..d62f406 100755 --- a/bin/release +++ b/bin/release @@ -35,7 +35,7 @@ FileUtils.rm_rf 'aminet' FileUtils.mkdir_p build_dir FileUtils.mkdir_p 'aminet' -FileUtils.mv 'src/AMOSPro_BSDSocket.Lib', 'AMOSPro_BSDSocket.Lib' if File.exist?('src/AMOSPro_BSDSocket.Lib') +FileUtils.cp 'src/AMOSPro_BSDSocket.Lib', 'AMOSPro_BSDSocket.Lib' hackerbun_target = File.expand_path("dist/AMOSPro_BSDSocket_#{version}.lha") diff --git a/src/AMOSPro_BSDSocket.Lib b/src/AMOSPro_BSDSocket.Lib new file mode 100644 index 0000000..7995e0a Binary files /dev/null and b/src/AMOSPro_BSDSocket.Lib differ diff --git a/src/BSDSocket.s b/src/BSDSocket.s index 62f69ec..09885c6 100644 --- a/src/BSDSocket.s +++ b/src/BSDSocket.s @@ -7,7 +7,7 @@ ; extension number 18 ExtNb equ 18-1 Version MACRO - dc.b "1.1.2-20240318" + dc.b "1.1.4-20240426" ENDM VerNumber equ $1 @@ -22,23 +22,190 @@ VerNumber equ $1 Include "+AMOS_Includes.s" Include "bsdsocket_lvo.i" - Include "LongDivide.s" - Include "Constants.s" - Include "fd_set.s" - Include "AMOSMacros.s" +; get the effective address of something in extension memory +Dlea MACRO + MOVE.L ExtAdr+ExtNb*16(A5),\2 + ADD.W #\1-MB,\2 + ENDM +; load the base of extension memory into a register +Dload MACRO + MOVE.L ExtAdr+ExtNb*16(A5),\1 + ENDM ; call an AmigaOS function via LVO(A6) CALLLIB MACRO JSR _LVO\1(A6) ENDM +; bsdsocket library stuff +; ported from the various C include headers +SOCK_STREAM EQU 1 +PF_INET EQU 2 +AF_INET EQU PF_INET +IPPROTO_TCP EQU 6 -; get the BSDSocket library ready for CALLLIB -LoadBSDSocketBaseFromA3 MACRO +INADDR_ANY EQU 0 + +FIONBIO EQU $8004667E +FIONASYNC EQU $8004667D + +SOL_SOCKET EQU $FFFF +SO_REUSEADDR EQU $4 + +MAX_SOCKETS EQU 64 + +len_sockaddr_in EQU 16 +sockaddr_in_sin_family EQU 1 +sockaddr_in_sin_port EQU 2 +sockaddr_in_sin_addr EQU 4 + +; global errors +Error_OtherError EQU -1 +Error_LibraryNotOpen EQU -2 +Error_PortOutOfRange EQU -11 +Error_FdsetOutOfRange EQU -11 +Error_UnableToBind EQU -12 + +; socket herrno and tag lists +; built from: +; * https://wiki.amigaos.net/amiga/autodocs/bsdsocket.doc.txt +; * https://github.com/deplinenoise/amiga-sdk/blob/master/netinclude/amitcp/socketbasetags.h +; * http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_2._guide/node012E.html + +TAG_USER EQU (1<<31) +SBTF_REF EQU $8000 +SBTB_CODE EQU 1 +SBTS_CODE EQU $3FFF +SBTC_HERRNO EQU 6 + +HerrnoTag EQU (TAG_USER|SBTF_REF|((SBTC_HERRNO&SBTS_CODE)<(_CONTEXT_INDEX-1) + _CONTEXT_STRING$=_CONTEXT_STRING$+"/" + End If + Next I + _OUTPUT$="["+_CONTEXT_STRING$+"] "+MESSAGE$ + Print #_FILE_HANDLE,_OUTPUT$ + Print _OUTPUT$ + ' ensure line is printed on screen to catch crashes + Wait Vbl +End Proc + +Procedure _BEGIN_CONTEXT[NAME$] + _CONTEXT$(_CONTEXT_INDEX)=NAME$ + _CONTEXT_INDEX=_CONTEXT_INDEX+1 +End Proc + +Procedure _END_CONTEXT + _CONTEXT_INDEX=_CONTEXT_INDEX-1 +End Proc + +Open Out _FILE_HANDLE,"report.txt" + +_BEGIN_CONTEXT["Socket Library Open"] +_BEGIN_CONTEXT["Open Library"] + +_LIBRARY_ADDRESS=Socket Library Open +_ADD_TO_REPORT[Str$(_LIBRARY_ADDRESS)] + +_END_CONTEXT + +If _LIBRARY_ADDRESS=0 + _ADD_TO_REPORT["bsdsocket.library not available, ending tests"] + _END_CONTEXT + Goto _FINISH_SUITE +End If + +_BEGIN_CONTEXT["Double open"] +_NEW_LIBRARY_ADDRESS=Socket Library Open +_ADD_TO_REPORT[Str$(_NEW_LIBRARY_ADDRESS)] +_END_CONTEXT + +_END_CONTEXT + + +_BEGIN_CONTEXT["Socket Create Inet Socket"] +SOCKET=Socket Create Inet Socket +_ADD_TO_REPORT[Str$(SOCKET)] +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Reuse Addr"] +RESULT=Socket Reuse Addr(SOCKET) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Set Nonblocking"] +_BEGIN_CONTEXT["False"] +RESULT=Socket Set Nonblocking(SOCKET,False) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["True"] +RESULT=Socket Set Nonblocking(SOCKET,True) +_ADD_TO_REPORT[Str$(RESULT)] +_ADD_TO_REPORT["Preserving this state for rest of tests"] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Dns Get Address By Name$"] + +_BEGIN_CONTEXT["Bad domain name"] +IP$=Dns Get Address By Name$("u8dsf93jsdfoiodsoi.sdfjdsoifueru.sadfasdsdasd") +Print IP$ +_ADD_TO_REPORT[IP$] +_END_CONTEXT + +_BEGIN_CONTEXT["localhost"] +IP$=Dns Get Address By Name$("localhost") +_ADD_TO_REPORT[IP$] +_END_CONTEXT + +_BEGIN_CONTEXT["aminet.net"] +IP$=Dns Get Address By Name$("aminet.net") +Print IP$ +_ADD_TO_REPORT[IP$] +_ADD_TO_REPORT["Using this IP address for connection tests"] +_END_CONTEXT + +_BEGIN_CONTEXT["Returned string can be manipulated"] +_ADD_TO_REPORT["Cat"+IP$+"Dog"] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Connect"] +_BEGIN_CONTEXT["port too high"] +RESULT=Socket Connect(SOCKET To IP$,999999) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["blank ip"] +RESULT=Socket Connect(SOCKET To "",80) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["invalid ip -- requires reopening failed socket"] +RESULT=Socket Connect(SOCKET To "999.999.999.999",80) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Close Socket"] +RESULT=Socket Close Socket(SOCKET) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +' re-set up this socket after a failed connection +SOCKET=Socket Create Inet Socket +_=Socket Set Nonblocking(SOCKET,True) + +_BEGIN_CONTEXT["aminet IP ("+IP$+") from above"] +RESULT=Socket Connect(SOCKET To IP$,80) +_ADD_TO_REPORT[Str$(RESULT)+" (ok to be -1, will Wait Async Writing next)"] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Wait Async Writing"] + +_BEGIN_CONTEXT["With unconnected socket"] +OTHER_SOCKET=Socket Create Inet Socket +_=Socket Set Nonblocking(OTHER_SOCKET,True) + +_BEGIN_CONTEXT["2 second timeout"] +RESULT=Socket Wait Async Writing(OTHER_SOCKET,2000) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Zero timeout"] +RESULT=Socket Wait Async Writing(OTHER_SOCKET,0) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Connected socket"] +_BEGIN_CONTEXT["Two second timeout"] +RESULT=Socket Wait Async Writing(SOCKET,2000) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Zero timeout"] +RESULT=Socket Wait Async Writing(SOCKET,0) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Attempt to get actual connection"] +For TRIES=0 To 10 + RESULT=Socket Wait Async Writing(SOCKET,500) + _ADD_TO_REPORT[Str$(RESULT)] + If RESULT>0 + Exit + End If +Next TRIES +_END_CONTEXT +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Get Host"] +_BEGIN_CONTEXT["socket id too high"] +RESULT=Socket Get Host(999999) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["socket id is valid"] +RESULT=Socket Get Host(SOCKET) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Get Port"] +_BEGIN_CONTEXT["socket id too high"] +RESULT=Socket Get Port(999999) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["socket id is valid"] +RESULT=Socket Get Port(SOCKET) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Inet Ntoa$"] +_BEGIN_CONTEXT["Use the socket host"] +RESULT$=Socket Inet Ntoa$(Socket Get Host(SOCKET)) +_ADD_TO_REPORT[RESULT$] +_END_CONTEXT + +_BEGIN_CONTEXT["String can be manipulated"] +_ADD_TO_REPORT["Cat"+RESULT$+"Dog"] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Send and Socket Send$"] +HTTP_REQUEST_HEADERS$="GET / HTTP/1.0"+Chr$(10)+"Host: amiga"+Chr$(10)+Chr$(10) + +_BEGIN_CONTEXT["Socket send$ first half"] +FIRST_HALF$=Left$(HTTP_REQUEST_HEADERS$,10) +RESULT=Socket Send$(SOCKET,FIRST_HALF$) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Socket send second half"] +SECOND_HALF_START=Varptr(HTTP_REQUEST_HEADERS$)+10 +SECOND_HALF_LENGTH=Len(HTTP_REQUEST_HEADERS$)-10 +RESULT=Socket Send(SOCKET,SECOND_HALF_START,SECOND_HALF_LENGTH) +_ADD_TO_REPORT[Str$(RESULT)] + +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Socket wait Async Reading"] +_BEGIN_CONTEXT["With unconnected socket"] +_BEGIN_CONTEXT["2 second timeout"] +RESULT=Socket Wait Async Reading(OTHER_SOCKET,2000) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_BEGIN_CONTEXT["Zero timeout"] +RESULT=Socket Wait Async Reading(OTHER_SOCKET,0) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["With aminet socket"] +_BEGIN_CONTEXT["2 second timeout"] +RESULT=Socket Wait Async Reading(SOCKET,2000) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Zero timeout"] +RESULT=Socket Wait Async Reading(SOCKET,0) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT +_END_CONTEXT + +_END_CONTEXT + +'Goto _FINISH_SUITE + +_BEGIN_CONTEXT["Socket Recv$ and Socket Recv"] +_BEGIN_CONTEXT["Socket Recv$"] +_BEGIN_CONTEXT["Read 20 characters"] +RESULT$=Socket Recv$(SOCKET,20) +_ADD_TO_REPORT[RESULT$] +_END_CONTEXT + +_BEGIN_CONTEXT["String can be manipulated"] +_ADD_TO_REPORT["Cat"+RESULT$+"Dog"] +_END_CONTEXT + +_END_CONTEXT + +_BEGIN_CONTEXT["Socket Read"] +Reserve As Work 20,20 + +_BEGIN_CONTEXT["Read 20 bytes"] +RESULT=Socket Recv(SOCKET To Start(20),20) +_ADD_TO_REPORT[Str$(RESULT)] +_END_CONTEXT + +_BEGIN_CONTEXT["Data was read"] +BASE$="12345678901234567890" +For I=0 To 19 + Mid$(BASE$,I+1,1)=Chr$(Peek(Start(20)+I)) +Next I +_ADD_TO_REPORT[BASE$] +_END_CONTEXT + +Erase 20 +_END_CONTEXT +_END_CONTEXT + +_FINISH_SUITE: + +_BEGIN_CONTEXT["Socket Library Close"] +_BEGIN_CONTEXT["Close"] +Socket Library Close +_ADD_TO_REPORT["Success"] +_END_CONTEXT + +_BEGIN_CONTEXT["Double close"] +Socket Library Close +_ADD_TO_REPORT["Success"] +_END_CONTEXT +_END_CONTEXT + +_BEGIN_CONTEXT["Call functions with library closed"] +_=Socket Set Nonblocking(0,True) +_ADD_TO_REPORT["Socket Set Nonblocking"] +_=Socket Reuse Addr(0) +_ADD_TO_REPORT["Socket Reuse Addr"] +_=Socket Create Inet Socket +_ADD_TO_REPORT["Socket Create Inet Socket"] +_=Socket Connect(0 To "",0) +_ADD_TO_REPORT["Socket Connect"] +_=Socket Bind(0 To "",0) +_ADD_TO_REPORT["Socket Bind"] +_=Socket Listen(0) +_ADD_TO_REPORT["Socket Listen"] +_=Socket Accept(0) +_ADD_TO_REPORT["Socket Accept"] +_=Socket Wait Async Reading(0,0) +_ADD_TO_REPORT["Socket Wait Async Reading"] +_=Socket Wait Async Writing(0,0) +_ADD_TO_REPORT["Socket Wait Async Writing"] +_=Socket Set Timeout(0,0) +_ADD_TO_REPORT["Socket Set Timeout"] +_=Socket Close Socket(0) +_ADD_TO_REPORT["Socket Close Socket"] +_=Socket Send$(0,"") +_ADD_TO_REPORT["Socket Send$"] +_=Socket Send(0,0,0) +_ADD_TO_REPORT["Socket Send"] +_$=Socket Recv$(0,0) +_ADD_TO_REPORT["Socket Recv$"] +_=Socket Recv(0 To 0,0) +_ADD_TO_REPORT["Socket Recv"] +_=Socket Get Host(0) +_ADD_TO_REPORT["Socket Get Host"] +_=Socket Get Port(0) +_ADD_TO_REPORT["Socket Get Port"] +_$=Socket Inet Ntoa$(0) +_ADD_TO_REPORT["Socket Inet Ntoa$"] +_=Socket Errno +_ADD_TO_REPORT["Socket Errno"] +_=Socket Herrno +_ADD_TO_REPORT["Socket Herrno"] +_$=Dns Get Address By Name$("") +_ADD_TO_REPORT["Dns Get Address By Name$"] +_END_CONTEXT + +Close 1