THIS SOFTWARE AND INFORMATION ARE PROVIDED *AS IS*.
ALL USE IS AT YOUR OWN RISK, AND NO LIABILITY OR
RESPONSIBILITY IS ASSUMED. NO WARRANTY IS MADE,
ECCEPT THAT *THIS CODE IS TOTALLY BACKDOORS FREE*.
You are free to detribute it as long as the original archive is kept intact.
Commercial use or its inclusion in other software package is prohibited without
prior consens from the Author.
This library is a complete bridge to bsdsocket.library ,
so it is a powerfull ARexx API for internet comunication.
The functions of this library directly call bsdsocket.library
functions, which I'll name "original functions", but this doc
is not an introduction to bsdsocket.library but just a guide
to rxsocket.library functions.
The environment is macro-private: each macro opens bsdsocket.library
and what else must be private and stores a list of "things" that must
be freed on exit.
The way used to handle arguments and results is:
This is a general policy in my ARexx libraries to try to emulate the AmigaOS
tags programming way.
The main structures passed or returned to/from functions are:
As I said above, I emulate structure as ARexx stemName; an example will help:
we want to get the local protoent of the echo TCP service, so we need a call to
GetServByName() passing it a stemName (see Terms):
if ~GetGervByName("SE","echo","TCP") then do
/* failure */
say "no echo TCP service"
exit
end
/* success */
say "Name:" se.ServName
say "Port:" se.ServPort
say "Proto:" se.ServProto
if se.ServAliases.num=0 then say "No alias"
else do
say "Aliases"
do i = 0 to se.ServAliases.num-1
say se.ServAliases.i
end
end
As you can see, GetServByName() sets, on success, the fields:
of the ARexx stem that has as root part that stemName you give the function as
the first arguments.
If an array is part of the structure then the number of members are returned in
X.Y.NUM and the mebers can be found in X.Y.0 , .... , X.Y.last last = X.Y.NUM-1;
so if X.Y.NUM == 0 the array is empty.
For each structure the fields read or set by functions are:
To make life easier a lot of arguments have their human form and can be passed
to functions (directly or in as stem field) as string.
They are expecially:
FAMILY as in socket(family,type,protocol) or ADDRFAMILY the address
family that actually has just the string form "INET" can be passed as integer too.
type as in socket(family,type,protocol) the type of the socket has the string forms
"STREAM"
"DGRAM"
"RAW"
"RDM"
"SEQPACKET"
can be passed as integer too.
protocoll as in socket(family,type,protocol) the protocol of the socket has the string forms
Accepts a connection on a socket after a bind and a listen .
Creates a new socket for the new connection and returns its socketfd.
Fills remote with the sockaddr_in fields of the connected peer.
Returns the socketfd, an integer >=0, or -1 for failure.
Closes the global rxsocket console.
attemp can be 0 or a non negative integer (1) .
If no attemp is specified it is assumed to be 0 .
if attempt is 0, the function wait till all the console users
release it. If attemp is 0, the funtion doesn't wait and the
console is closed iff it is not busy .
Connects the socket to the socketaddr_in as defined in remote .
Returns -1 for failure.
EXAMPLE
sin.addrFamily = "INET"
sin.addrPort = 80
sin.addrAddr = addr /* from a call to resolve() */
if connect(sockfd,"SIN")<0 then do
say "connect: error" Errno()
exit
end
Duplicates an existing socket and returns the new socketfd.
A new internal socket resource is allocated.
It calls the original dup2socket() function with the second
argument as -1.
Gets some global parameters in the bsdsocket.library base.
The original bsdsocket.library function is SocketBaseTagList, which is used to
get/set; here we split it in 2 as GetSocketBase() and SetSocketBase().
You must set the field of stem you want to get, then call the function.
The function fills the fields you choosed with their current value.
The fields accepted are:
"BREAKMASK"
"DTABLESIZE"
"ERRNO"
"ERRNOSTRPTR"
"HERRNOSTRPTR"
"HERRNO"
"SIGIOMASK
"SIGURGMASK
"LOGFACILITY"
"LOGMASK"
"LOGSTAT"
Return -1 for failure.
EXAMPLE
drop a. /* to be sure we don't make a mass :-) */
a.ERRNOSTRPTR=40 /* must be numeric */
a.BREAKMASK=1 /* can be whatever you want */
a.HERRNOSTRPTR=2 /* must be numeric */
call GetSocketBase("A")
say a.ERRNOSTRPTR ----->"Message too long"
say a.BREAKMASK ----->4096
say a.HERRNOSTRPTR ----->"Host name lookup failure"
See GetSocketBase().
Just as GetSocketBase() but get a single option.
var is where the value will be written.
value is the value for ERRNOSTRPTR and HERRNOSTRPTR.
Computes an Internet checksum on data for len bytes.
If no len, chekcsum is computed on all data.
The chekcsum is "the 16 bit one's complement of the
one's complement sum of all 16 bit words of 'data'
for 'len' bytes"; if 'len' is odd a padding byte is
added at the end of data.
the function must be used in the form
res = IoctlSocket(socketfd,attr,value)
<socketfd/N>,<attrs>,<value>
e.g.
res = IoctlSocket(sock,"FIONBIO",1)
sets as socket non blocking
To get a socket attribute:
SIOCATMARK
FIONREAD
the function must be used in the form
res = IoctlSocket(socketfd,attr,var)
<socketfd/N>,<attr>,<var/V>
e.g.
res = IoctlSocket(s,"FIONREAD","A")
gets the number ready to be read in a
To get an interface attribute:
SIOCGIFADDR
SIOCGIFDSTADDR
SIOCGIFBRDADDR
SIOCGIFNETMASK
SIOCGIFFLAGS
SIOCGIFMETRIC
SIOCGIFMTU
SIOCGIFPHYS
the function must be used in the form
res = IoctlSocket(socketfd,attr,name,var)
<socketfd/N>,<attr>,<name>,<var/V>
e.g.
res = IoctlSocket(s,"SIOCGIFADDR","mi0","A")
gets the IFAddr of mi0 (if it exists) and write it in a as a dotted addr.
Returns -1 for failure.
EXAMPLES:
look at ioctl.rexx in examples drawner.
NOTE:
I didn't want to let user set any interface attributes.
I think this is dangerous. Anyway it might change.
Returns the last socketfd active in the macro, or -1 if none.
This function is usefull at a beginning of a macro that is supposed
to be started by RxsCall() or inetd, to check if it has a socket
already opened.
The function is needed when you want to pass a socket from a macro to another.
It obtains a previously released socket.
Only rxsocket.library released sockets can be obtained.
Key is the key returned by ReleaseSocket() or ReleaseCopyOfSocket().
The way used to handle a safe socket release-obtain is:
when a socket is released by ReleaseSocket() or ReleaseCopyOfSocket(), the
socket is still is linked in a list that belongs to the macro where it was created.
if a released socket is not obtained it is freed at the exit of the macro where
it was created;
if a release socket was obtained with ObtainSocket() it belongs to the
environment of the macro where it was obtained;
if ObtainSocket() fails for any reasones, the socket is still in the macro where it
was created;
when a socket is released, it can't be used before it is obtained.
key is the result of ReleaseSocket() , ReleaseCopyOfSocket() or NextRxsReleased ()
and consists of a packed char of length 8, or 4 on failure.
Key can be tested with a comparation to null() as we usually do with messages
from an ARexx port. Key passed to ObtainSocket() is checked to test its coerence,
anyway, please, don't "play" with it.
Usally ReleaseSocket() is used in a "concurrent service" after a accept() and the
key is given as argument of a macro that must handle the new connection. The
first thing that macro should do is to obtain the socket with a call to
ObtainSocket() and tell the "parent macro" about the result of the operation
(e.g. with an ARexx message on an ARexx port).
If you specify one or more of family , type , proto , the socket is obtained
only if it matches them .
Returns -1 for failure or the socketfd of the obtained socket.
A function that creates a socket binds and/or connects it.
Let's see the different forms:
proto can be the string "TCP" or "UDP"
port1 and port2 are service name or port numbers
if and only if they are service names, they are resolve by getserbyname()
res=OpenConnection(proto,port1)
resolve port1 if it is a service name
create a socket
bind the socket to port1
res=OpenConnection(proto,port1,hostName)
resolve port1 if it is a service name
resolve hostName
create a socket
connect the socket to hostName:port1
res=OpenConnection(proto,port1,hostName,port2)
resolve port1 if it is a service name
resolve port2 if it is a service name
resolve hostName
create a socket
bind the socket to port1
connect the socket to hostName:port2
Did you understand?
Take a look at the examples.
Read some docs for the differeces beetwen conneting a socket of type TCP or UDP.
Have fun!
Returns:
-5 socket can't be bound, e.g. port already bound
-4 port2 not resolved
-3 port1 not resolved
-2 host lookup failure
-1 bsdbsdsocket.library error
>=0 socket number id, if success
If present as the 5th argument and on connection, stem is filled as socketaddr_in.
Reads the interface list and writes interfaces attributes in stem .
Returns the number of the found interfaces, so 0 means none, or -1
that means that the function was not able to create the DGRAM socket
needed for the query. Anyway a positive results means success.
If res is positive, interface attributes are written in
stem.i,...,stem.j where j=res-1, e.g.
res = QueryInterface("INTERFACES")
if res>=0 then do
say "Found" res "interface(s)"
do i=0 to res-1
say interfaces.i.name
end
end
else say "not able to find any interface"
The attributes are:
Name
Family the family of the interface as integer (2 stands for INET)
Receives data from a socket until the stopData data occurr.
Very funny function that wait for a string to uccur and then
returns the data received till that string (but without that string).
Next recv of any kind will return data AFTER the stop string.
It receives max len bytes and fills buff with the data
received.
If present, remote must be set as sockaddr_in.
Flags is one or more of:
"OOB"
"PEEK"
"DONTROUTE"
"EOR"
"TRUNC"
"CTRUNC"
"WAITALL"
"DONTWAIT"
"EOF"
"COMPAT"
e.g. "OOB PEEK".
Returns -1 for failure or bytes read length +1, so 1 means eof.
Receives a line from a socket. It receives max len bytes and fills buff with
the data received.
If len is not specified it is assumed to be 256 .
If present stem must be set as sockaddr_in.
Flags is one or more of:
"OOB"
"PEEK"
"DONTROUTE"
"EOR"
"TRUNC"
"CTRUNC"
"WAITALL"
"DONTWAIT"
"EOF"
"COMPAT"
e.g. "OOB PEEK".
Returns -1 for failure or bytes read length.
This is a relly bad non buffered readline. Don't use it so much!
This function doesn't work on MiamiDx 0.9.
It was patched, so that if no remote is given, it uses recv() rather
then recvfrom(). So if ou are using this function with a STREAM
socket don't pass it remote.
Usage: res = RxsCall(macro,socketfd,flags)
<macro>,[socketfd/N],flags
This function calls macro , and creates a socket by releasing a copy of
socketfd and calling a special ARexx macro process handler that tries to
obtain that socket.
The socketfd in the called macro is usually DIFFERENT from the one passed.
The function LastSocket() returns the last socketfd created in the macro,
so if the macro was started by this function, LastSocket() always returns a
value>=0.
If socketfd is negative (-1) or it is not specified, no socket is passed.
Local vars, e.g. created bye rmh.library/SetVar() are passed to the child macro.
Let's name the macro in which this function is used "parent" and the macro
called "child" .
flags can be one or more of:
SYNC usually the child is called async; if you specify this flag,
the parent waits for the child to end;
note that other flags may force it;
STRING child is a macro-string rather than a macro file name;
FUN child is called as a function;
RESULT a result is expected from child; SYNC is forced;
OBTAIN every socket released in child, but not obtained at child exit,
is passed to parent, that can obtain it via NextRxsReleased() ;
this is the suggested way to share sockets among macros;
NOERR if an error occurs in child, it is usually reported to the parent;
with this flag, you will not be bored by errors occurred in
child, and if an error occurred, it is written in RC; note that
this has sense only if SYNC was specified.
OPENCON if child has no stdin/stdout, forces the global rxsocket console
to be opened, so that it will be the stdin/stdout of the macro
NOTA BENE: the socket is always duplicate, so if parents does not need it
my suggestion is to immediatly close it, expecially if it is a
STREAM one.
Returns:
a result from child if RESULT was specified
0 if SYNC was not specified and there was no error in child
Sets global parameter in the bsdsocket.library base.
The original bdsocket.library function is SocketBaseTagList, which is use to
get/set; here we split it in 2 as GetSocketBase() and SetSocketBase().
You must set the field of "stem" with the value you want to set, then call the
function.
The fields are:
Causes all or part of a full-duplex connection on the socket to be shut down.
If how is 0, further receives will be disallowed.
If how is 1, further sends will be disallowed.
If how is 2, further sends and receives will be disallowed.
Creates an endpoint for communication and returns a descriptor.
Adds to the local-macro list of open sockets a new link so that
resource can be freed at macro exit.
Returns a socketfd as integer that can be used in every function
wich needs a "socketfd" argument.
Write msg to the global rxsocket console.
If the console was not opened, it is opened .
If msg doesn't end with a newline ( '\n' , "A"x ), a newline
is added.
Pointers to deallocate the local environment in the library base is saved
in the fields pr_ExitCode and pr_ExitData of the Process structure of
the macro. At exit a chain of pr_ExitCode(pr_ExitData) is called.
Details are avaible on request.
When a function is not avaible, the user is informed via a requester
and an ARexx error 15 (function not found) is returned.
IsLibOn() can be used to test the environment.
rxsocket.library offers an API for other ARexx libraries that needs
to use bsdsocket.library functions in a clean way.
rxsocket.library SDK are avaible on request.
With rxsocket.library you can easly create full functional inetd
server.
A little program called "rxs" is supplied. It should be installed
in c: (as the install script does) or in SYS:Rexxc .
It launches an ARexx macro in a special way, so that it can
obtain the socket from inetd.
In inetd database you must
use rxs (complete path) as "Service"
use rxs as "Name"
specify the name of the macro (and its arguments) as "Args"
rxs template is:
"CON=CONSOLE/S,MACRO/A/F"
CONSOLE macros called from inetd have no stdin/stdout, anyway
they can be forced to use rxsocket global console as stdin/stdout
for debugging porpouses. This switch forces the global rxsocket
console to be opened, if it is not. See OpenRxsCon .
rxsco is a litle program to open the console via shell;
rxscc close the console.
MACRO is the name of the macro with its arguments
In the macro, you can obtain the socket passed by inetd, via
LastSocket() (NOTA BENE: this must be called BEFORE other
sockets are created):
...
s=LastSocket()
if s>=0 then /* ok I was called from inet and the socket is s*/
...
If LastSocket() returns -1, the macro was NOT started from inetd
and has no socket. In this event, the macro can choose to run as
a stand-alone service rather than an inetd service.