------------------------------------------------------------------------------------------------RXSOCKET.LIBRARY versione 2.1 ------------------------------------------------------------------------------------------------AVVERTENZA IL SOFTWARE E LE INFORMAZIONI CONTENUTE IN QUESTO ARCHIVIO SONO FORNITE "COSI' COME E'". L'USO E' A TUO PROPRIO RISCHIO E NESSUNA RESPONSABILITA' E' ASSUNTA. NESSUNA GARANZIA E' DATA, ECCETTO CHE "QUESTO SOFTWARE E' DEL TUTTO PRIVO DI BACKDOORS". ------------------------------------------------------------------------------------------------INDICE 1. Introduzione 2. Installazione 3. Requisiti 4. Autore 5. Distribuzione 6. Termini 7. Bugs 8. Argomenti e risultati 9. Funzioni in ordine alfabetico 10. Ringraziamenti 11. Bibliografia 12. Da Fare 13. Note 14. Cambiamenti 15. Supporto inetd ------------------------------------------------------------------------------------ 1. Introduzione Lo scopo di questa libreria è offrire un insieme di funzioni socket alle macro ARexx, in modo da rendere l'accesso ad Internet da ARexx semplice ed immediato, senza perdere la complessità delle funzioni della bsdsocket.library. Le funzioni di questa libreria chiamano le corrispondenti, che d'ora in poi dirò "originali", funzioni della bsdsocket.library, conservando per quanto è possibile la loro sintassi, effetti e side-effects. Proprio per questo motivo, questo documento non vuole essere affatto una introduzione alle funzioni della bsdsocket.library ed alla programmazione TCP/IP, ma soltanto una guida all'uso delle funzioni della rxsocket.library. L'ambiente in cui le funzioni operano è "privato della macro". La prima chiamata ad una funzione crea un'entrata in una lista nella base della libreria e "segna" la macro ARexx chiamante in modo tale che alla sua uscita, normale o prematura, ogni risorsa allocata, tra le quali ovviamente la bsdsocket.library, verrà automaticamente rilasciata. E' necessario spendere subito qualche parola sul metodo usato per il passaggio degli argomenti ed il ritorno dei risultati: - quando la funzione originale richiede un argomento semplice, non una struttura, lo stesso argomento è passato alla funzione ARexx; - quando la funzione originale richiede una struttura come argomento, alla funzione ARexx viene passato un nome di variabile ARexx, detto qui stem, con vari campi settati; - quando la funzione originale ritorna un intero, lo stesso intero è ritornato dalla funzione ARexx; - quando la funzione originale ritorna una struttura, alla funzione ARexx viene passato uno stem, alcuni campi del quale verranno settati dalla funzione stessa. Questo metodologia è generale nelle mie librerie ARexx e tenta in qualche modo di emulare il passaggio degli argomenti per Tags tipico di AmigaOS. La libreria offre quasi tutte le funzioni della bsdsocket.library più alcune di livello superiore (hehe giusto un paio per ora ). La libreria non supporta direttamente pacchetti RAW o diversi da TCP ed UDP; in ogni caso, allocando un po' di memoria e settado a mano i vari campi del pacchetto, anche l'uso di altri protocolli e di pacchetti RAW diviene possibile. ------------------------------------------------------------------------------------------------ 2. Installazione Non è fornito, data la semplicità, alcuno script di installazione. Per installare la libreria: - copia rxsocket.library in LIBS: - apri una shell e scrivi: rx "call addlib('rxsocket.libary',0,-30)" ------------------------------------------------------------------------------------------------ 3. Requisiti La libreria gira su AmigaOS, versione >=2, ed ovviamente uno stack TCP/IP. In particolare è stata creata su Miami e funziona su AmiTCP. Per quanto riguarda TermiteTCP, la libreria è stata testata ed è risultata funzionante per la maggior parte delle funzioni; però, data la particolarità di TermiteTCP, non tutte le funzioni "vanno" e molte sono state appositamente disabilitate. Se uno stack non è in funzione, la libreria può essere comunque nella lista degli host ARexx, poichè ogni chiamata ad una sua funzione torna errore ARexx 1, ovvero come se la funzione non esistesse. ------------------------------------------------------------------------------------------------ 4. Autore L'autore di questo gioiello è: Alfonso Ranieri la sua e-mail è: alfier@iol.it Lo puoi trovare in irc su: - #amichat dalnet - #amigaita ircnet - #amyita ircnet L'ultima versione di questa libreria è reperibile su web a: http://users.iol.it/alfier/ ------------------------------------------------------------------------------------------------ 5. Distribuzione rxsocket.library è FREEWARE Chiunque può distribuirla fintanto che l'archivio originale rimane intatto. L'uso commerciale o l'inclusione in altro software sono proibiti previo consenso da parte dell'autore. ------------------------------------------------------------------------------------------------ 6. Termini - stem o stemName: è una valido nome di variabile ARexx come ad esempio var var.0 var.ciao e così via; - socket: è l'ambiente creato ad esempio dalla funzione socket(), su cui operano la maggior parte delle funzioni della bsdsocket.library; - socketfd: è l'interno, tornato ad esempio da socket(), che rappresenta il descrittore per un socket; lo si paragoni ad un descrittore di file in c; - addr o indirizzo: è un indirizzo Internet, nella forma dotted. Un indirizzo Internet è un intero a 32 bits, che viene rappresentato nella forma dotted "a.b.c.d" "a.b.c" "a.b" "a" oppure come un nome simbolico. In questa libreria si è scelto di usare come forma biù bassa solo indirizzi dotted, ad esempio resolve() ritorna la forma dotted oppure -1. Il mio scopo è mantenere la API orginale della bsdsocket.function, ma gli interi in ARexx non permettono un agevole l'uso di indirizzi a numero intero; - tipi degli argomenti: sono i tipi degli argomenti delle funzioni ARexx, rappresentati da una lettera maiuscola se obbligatori o minuscola altrimenti: - D qualsiasi tipo -- - N numerico /N - S simbolo ARexx /S - V nome variabile, come S ma di lunghezza inferiore a 20 caratteri /V ------------------------------------------------------------------------------------------------ 7. Bugs Hehe, ci sono bugs nel nostro sistema operativo. Ci sono bugs in software commerciali costosissimi. Vuoi che non ce ne siano qui? Attualmente, con Miami, che prendo come riferimento, una bug è "ben conosciuta": se un socket non connesso viene rilasciato, ottenuto e poi connesso, ovvero se su di esso si esegue la sequenza: - ReleaseSocket() - ObtainSocket() - connect() un hit di Enforcer viene fuori. Questo è un problema di Miami, non mio. Ti prego di segnalarmi al più presto ogni bugs che potessi scoprire. ------------------------------------------------------------------------------------------------ 8. Argomenti e Risultati Attualmente 4 strutture sono passate e tornate a/da le funzioni della libreria: - struct hostent - struct servent - struct protoent - struct sockaddr_in Come detto sopra, si cerca di emulare il passaggio od il ritorno di strutture per mezzo di uno stem. Un esempio aiuterà: vogliamo leggere la entry del servizio echo TCP, quindi invochiamo la funzione GetServByName(): if ~GetServByName("SE","echo","TCP") then do say "service echo non trovato" exit end say "Name: " se.servName say "Port: " se.servPort say "Proto:" se.servProto if se.servAliases.num=0 then say "echo non ha alias" else do say "alias di echo" do i = 0 to se.servAliases.num-1 say se.servAliases.i end end La funzione GetServByName(), se ha successo, setta alcuni campi dello stem che gli viene passato come primo argomento: - SERVNAME - SERVPORT - SERVPROTO - SERVALIASES.NUM - SERVALIASES.0 , ... , SERVALIASES.last (last = SERVALIASES.NUM-1) In generale lo stesso meccanismo viene applicato sia nel passaggio degli argomenti che nel ritorno dei risultati. Inoltre se una lista deve essere ritornata, il numero dei membri viene scritto in STEM.NOME.NUM ed i membri della lista si trovano, se STEM.NOME.NUM>0, in STEM.NOME.0, ..., STEM.NOME.last (last = STEM.NOME.NUM-1). Per ciascuna struttura, i campi letti o scritti dalle funzioni sono: - hostent ( settata da GetHostByName() GetHostByAddr() ) - HOSTNAME - HOSTADDRTYPE - HOSTLENGTH - HOSTALIASES.NUM - HOSTALIASES.INDEX (INDEX=0,...,HOSTALIASES.NUM-1) - HOSTADDRLIST.NUM - HOSTADDRLIST.INDEX (INDEX=0,...,HOSTADDRLIST.NUM-1) - servent ( settata da GetServByName() GetServByPort() ) - SERVNAME - SERVPORT - SERVPROTO - SERVALIASES.NUM - SERVALIASES.INDEX (INDEX=0,...,SERVALIASES.NUM-1) - protoent ( settata da GetProtoByName() GetProtoByNumber() ) - PROTONAME - PROTOPROTO - PROTOALIASES.NUM - PROTOALIASES.INDEX (INDEX=0,...,PROTOALIASES.NUM-1) - struct sockaddr_in ( passata e tornata a/da varie funzioni ) come argomento di una funzione: - ADDRFAMILY /D ( "INET" ) - ADDRPORT /N - ADDRADDR /N come risultato di una funzione: - ADDRLEN - ADDRFAMILY - ADDRPORT - ADDRADDR Per rendere la vita più semplice, molti degli argomenti da passare alle funzioni direttamente o come campi di uno stem, oltre al valore numerico, hanno una "forma umana". Specialmente: - FAMILY come in socket(family,...) o in ADDRFAMILY ha la forma "INET" e solamente "INET" in questa versione della libreria; - TYPE come in socket(family,type,...) può essere un intero oppure una tra le stringhe: - "STREAM" - "DGRAM" - "RAW" - "RDM" - "SEQPACKET" - PROTOCOLL come in socket(family,type,protocoll) può essere un intero oppure una tra le stringhe: - "IP" - "HOPOPTS" - "ICMP" - "IGMP" - "GGP" - "IPIP" - "TCP" - "EGP" - "PUP" - "UDP" - "IDP" - "TP" - "IPV6" - "ROUTING" - "FRAGMENT" - "RSVP" - "ESP" - "AH" - "ICMPV6" - "NONE" - "DSTOPTS" - "EON" - "ENCAP" - "DIVERT" - "RAW" Comunque....Solo alcuni protocolli funzioneranno qui, ma... Se sei molto bravo in TCP/IP ed ARexx, puoi allocare un po' di memoria nella macro, creare il tuo pacchetto a mano inserendo i giusti valori direttamente in memoria e spedire pacchetti ICMP o RAW a tuo piacere. Forse, una futura versione della libreria supporterà la creazione di pacchetti RAW. Quindi, tirando il fiato, quando nella descrizione di una funzione, scrivo: "lo stem va settato come sockaddr_in", intendo semplicemente che prima di chiamare la funzione, bisogna settare i campi - ADDRFAMILY "INET" D - ADDRPORT porta N - ADDRADDR ndirizzo N dello stem che si passerà alla funzione. Ugualmente, quando scrivo: "setta lo stem alla struttura hostest", intendo che la funzione setta i campi - HOSTNAME - HOSTADDRTYPE - HOSTLENGTH - HOSTALIASES.NUM - HOSTALIASES.INDEX - HOSTADDRLIST.NUM dello stem passatogli come argomento. ------------------------------------------------------------------------------------------------ 9. Funzioni in ordine alfabetico QUESTA NON E' UNA GUIDA ALLE FUNZIONI DELLA BSDSOCKET.LIBRARY, AL LORO USO ED AI LORO EFFETTI. SI LEGGA UN BUON LIBRO SUL TCP/IP, LE RFC OPPURE UN AUTODOC SULLA SOCKET.LIBRARY PER AMIGAOS. I nome delle funzioni sono NON case-sensitive; in ogni modo, io uso, per ragioni stilistiche, le lettere maiuscole se il nome è composto, per ogni parola che costituisce il nome della funzione, compresa la iniziale; nel caso degli argomenti lo stesso stile si applica, eccetto che la prima lettera della prima parola è sempre minuscola. La descrizione delle funzioni ha il formato: "NOME" nome della funzione tutti in maiuscole "Uso" chiamata tipica della funzione, con tutti gli argomenti "maschera argomenti" maschera argomenti, secondo lo stile AmigaDOS "Descrizione" descrizione della funzione. "Risultati" risultati, se non già contenuti in "Descrizione" "ESEMPIO" codice ARexx d'esempio La maschera degli argomenti è del tipo AmigaDOS; ad esempio: <stem/V> significa argomento obbligatorio di tipo V; [flags] significa argomento facoltativo di tipo D; {value/N} significa nessuno o più argomenti di tipo N. Se un argomento è facoltativo, può essere omesso (hummm tautologia rulez :-). Gli argomenti facoltativi si trovano DOPO gli argomenti obbligatori. Ad esempio: res=SendTo(socketfd,"Ciao",4) omessi [flag] e [stem] res=SendTo(socketfd,"Ciao",4,,"REMOTE") omesso [flag] sono usi validi della funzione SendTo(). A volte un argomento facoltativo può divenire obbligatorio in una particolare forma della funzione (ciò viene sempre specificato nella descrizione della funzione). L'omissione di un argomento ha un effetto dipendente dalla funzione. Ovviamente ogni funzione può causare errori ARexx; ad esempio: res=SendTo("ciao","A",1) causa un errore ARexx 17, perché il primo argomento di SendTo() deve essere di tipo N. A volte ci si imbatte in errori ARexx di cui non si riesce subito a trovare la causa; si ricordi che: - anche gli argomenti settati negli stem sono sottoposti al controllo del tipo; - se si passa come stem un nome minuscolo, lo stesso nome minuscolo deve essere settato: res=bind(socketf,"locale") legge nello stem "locale" e non "LOCALE" i campi di sockaddr_in; - conviene sempre mettere tra "" i nomi di stem e degli argomenti in "forma umana": inet=129 sockfd=socket(INET,"STREAM","IP") causa errore 18 (invalid argument to a function), perché INET vale 129 e 129 è un valore non valido come primo argomento della funzione socket(). Questa è la ragione per nomi così "buffi" per le strutture, tipo ADDRADDR o SERVPORT, al posto di più naturali ADDR o PORT; infatti facilmente si assegnano addr e port all'interno di una macro ed il risultato è, ad esempio: port=4000 locale.port=port /* diviene locale.4000=4000 */ ------------------------------------------------------------------------------------ ACCEPT Uso: sockfd=accept(socketfd,remote) <socketfd/N>,<remote/V> Accetta una connessione su un socket dopo un bind() ed un listen(). Crea un nuovo socket e torna il suo id, settando remote al sockaddr_in del peer connessosi al socket. La funzione è di solito usata all'interno di un service. Ritorna -1 in caso di fallimento od un intero >=0 in caso di successo. ------------------------------------------------------------------------------------ ADDR2C Uso: packetAddr=Addr2C(addr) <addr/N> Converte da addr a packed-char. Utile quando si deve settare una zona di memoria con un indirizzo Internet. ------------------------------------------------------------------------------------ BIND Uso: res=bind(socketfd,locale) <socketfd/N>,<locale/V> Prova a "collegare" il socket ad una porta. locale deve essere settato come un socketaddr_in, di solito con il campo ADDRADDR posto a 0. Ritorna -1 in caso d'errore ------------------------------------------------------------------------------------ CLOSESOCKET Uso: res=CloseSocket(socketfd) <socketfd/N> Chiude un socket e rilascia tutte le risorse ad esso connesse. Come i dati da ricevere o da sendare vengono trattati, in caso di socket di tipo orientato alla connessione (TCP), dipende dal settaggio di LINGER. Ritorna -1 in caso d'errore ------------------------------------------------------------------------------------ CONNECT Uso: res=connect(socketfd,remote) <sockeft/N>,<remote/V> Connette il socket ad un socketaddr_in definito in remote, che contiene un indirizzo ed una porta, ovvero il quarto e quinto membro della cinquina che rappresenta una connessione Internet. Ritorna -1 in caso d'errore ESEMPIO /* "risolviamo" l'indirizzo www.unina.it */ addr=resolve("www.unina.it") if addr==-1 then do say "indirizzo www.unina.it non trovato:" HostErrorno() exit end /* allochiamo un socket */ socketfd=socket("INET","STREAM","IP") if socketfd==-1 then do say "socket non creato:" errno() exit end /* settiamo DOVE connetterci */ remote.ADDRFAMILY="INET" /* sempre e solo "INET" */ remote.ADDRPORT=80 /* service http */ remote.ADDRADDR=addr /* connettiamoci */ if ~connect(socketfd,"REMOTE") then do say "connessione fallita:" errno() exit end /* connessione stabilita ... */ ----------------------------------------------------------------------------------- DUP2SOCKET Uso: sockfd=Dup2Socket(socketfd) <socketfd/N> Duplica un socket e torna il descrittore del nuovo socket o -1 in caso d'errore. Chiama la dup2socket() della bsdsocket.library con secondo argomento -1. ------------------------------------------------------------------------------------ ERRNO Uso: error=errno() - Ritorna il codice errore corrente. Utile dopo quasi ogni funzione, salvo se diversamente specificato, per scoprire la causa del fallimento. ------------------------------------------------------------------------------------ ERRORSTRING Uso: string=ErrorString(code) <code/N> Ritorna la stringa associata all'errore indicato da code. La stringa è non localizzata. Dovrebbe essere lo stack a localizzarla, non io. ------------------------------------------------------------------------------------ GETHOST Uso: res=getHost(host,host) <host/V>,<host/N> Setta host come una struttura hostent di un host passato per indirizzo o nome. Questa funzione, di solito, se non diversamente settato nello stack o se addr è presente in un database di host, causa l'interrogazione del server DNS da parte dello stack. In pratica questa funzione è l'unione di GetHostByName() e GetHostByAddr(). Ritorna un booleano indicante l'esito dell'operazione. In questo caso HostErrerno() può essere usata in caso di fallimento per scoprirne la causa. ------------------------------------------------------------------------------------ GETHOSTBYADDR Uso: res=getHostByAddr(host,addr) <host/V>,<addr,N> Setta host come una struttura hostent di un host passato per indirizzo. Questa funzione, di solito, se non diversamente settato nello stack o se addr è presente in un database di host, causa l'interrogazione del server DNS da parte dello stack. In pratica questa funzione è l'inversa di GetHostByName(). Ritorna un booleano indicante l'esito dell'operazione. In questo caso HostErrerno() può essere usata in caso di fallimento per scoprirne la causa. ------------------------------------------------------------------------------------ GETHOSTBYNAME Uso: res=GetHostByName(host,hostName) <host/V>,<hostName> Setta host come una struttura hostest di un host passato per nome. Questa funzione, di solito, se non diversamente settato nello stack o se hostName è presente in un database di host, causa l'interrogazione del server DNS da parte dello stack. In pratica questa funzione è l'inversa di GetHostByAddr(). Ritorna un booleano indicante l'esito dell'operazione. In questo caso HostErrno() può essere usata in caso di fallimento per scoprirne la causa. ------------------------------------------------------------------------------------ GETHOSTID Uso: id=GetHostID() - Ritorna l'id dello host corrente. ------------------------------------------------------------------------------------ GETHOSTNAME Uso: res=GetHostName(name) <name/S> Scrive in name il nome dello host corrente. Ritorna un booleano ad indicare l'esito dell'operazione. ------------------------------------------------------------------------------------ GETPEERNAME Uso: res=GetPeerName(socketfd,remote) <socketfd/N>,<remote/V> Setta remote come una struttura sockaddr_in relativo al peer connesso con il socket. Ritorna un booleano indicante l'esito dell'operazione. Disabilitata su TermiteTCP. ------------------------------------------------------------------------------------ GETPROTOBYNAME Uso: res=GetProtoByName(stem,protoName) <stem/V>,<protoName> Setta stem come una protoent relativa a proto passato per nome. La funzione è usata per sapere se un protocollo è disponibile nello stack in uso e per conoscere il suo id. Ritorna un booleano indicante l'esito dell'operazione. ------------------------------------------------------------------------------------ GETPROTOBYNUMBER Uso: res=GetProtoByNumber(stem,protoID) <stem/V>,<protoID/N> Setta stem come una protoent relativa al protocollo passato per numero. La funzione è usata per conoscere il nome di un protocollo. Ritorna un booleano indicante l'esito dell'operazione. ------------------------------------------------------------------------------------ GETSERVBYNAME Uso: res=GetServByName(stem,serviceName,protoName) <stem/V>,<serviceName>,<protoName> Setta stem come una servent relative a service passato per nome e protocollo. La servent viene letta localmente, dal database dei services dello stack. Ritorna un booleano indicante l'esito dell'operazione. Disabilitata su TermiteTCP (non avendo TermiteTCP un database di services). ------------------------------------------------------------------------------------ GETSERVBYPORT Uso: res=GetServByPort(stem,portNumber,protoName) <stem/V>,<potNumber/N>,<protoName> Setta stem come una servent relativa al service passato per numero di porta e protocollo. La servent viene letta localmente, dal database dei services dello stack. Ritorna un booleano indicante l'esito dell'operazione. Disabilitata su TermiteTCP (non avendo TermiteTCP un database di services). ------------------------------------------------------------------------------------ GETSOCKETBASE Uso: res=GetSocketBase(stem) <stem/V> Legge alcuni parametri globali della base della libreria bsdsocket.library. La funzione originale SocketBaseTagList(), che legge e setta, è qui divisa in 2 funzioni GetSocketBase(), che legge, e SetSocketBase(), che setta. Bisogna settare in stem i campi che si vogliono leggere, quindi chiamare la funzione, che per ogni campo che trova settato, leggerà il parametro corrispondente. I campi sono (si vedano gli autodoc di socket.library per il loro significato): - "BREAKMASK" - "DTABLESIZE" - "ERRNO" - "ERRNOSTRPTR" - "HERRNOSTRPTR" - "HERRNO" - "SIGIOMASK - "SIGURGMASK - "LOGFACILITY" - "LOGMASK" - "LOGSTAT" Ritorna -1 in caso di fallimento. ESEMPIO drop a. /* per essere sicuri di leggere solo i parametri desiderati */ a.ERRNOSTRPTR=40 /* errore di codice 40 */ a.BREAKMASK=1 /* maschera di segnali exec */ a.HERRNOSTRPTR=2 /* errore di host-lookup di codice 2 */ call GetSocketBase("A") say a.ERRNOSTRPTR ----->"Message too long" say a.BREAKMASK ----->4096 say a.HERRNOSTRPTR ----->"Host name lookup failure" ------------------------------------------------------------------------------------ GETSOCKETEVENTS Uso: res=GetSocketEvents(stem) <stem/V> Ritorna gli eventi asincroni accaduti ai sockets, settando i campi dello stem passato come argomento con un booleano. I campi settati sono: - ACCEPT - CLOSE - CONNECT - ERROR - OOB - READ - WRITE Ritorna il socketfd su cui sono accaduti gli eventi asincroni o -1 se nessuno sockets è interessato ad eventi asincroni. In questo caso Errno() NON PUO' essere usato in caso di errore. ------------------------------------------------------------------------------------ GETSOCKNAME Uso: res=GetSockName(socketfd,stem) <socketfd/N>,<stem/V> Setta stem come una sockaddr_in relativa al socket. Ritorna -1 in caso di fallimento. ------------------------------------------------------------------------------------ GETSOCKOPT Uso: res=GetSockOpt(socketfd,level,parm,stem) <socketfd/N>,<level>,<parm>,<stem/V> Scrive in stem il valore della parametro parm del socket al livello level. Attualmente il livello è uno tra: - "SOCKET" - "IP" Parametri per "SOCKET" sono: - "DEBUG" - "REUSEADDR" - "REUSEPORT" - "KEEPALIVE" - "DONTROUTE" - "LINGER" - "BROADCAST" - "OOBINLINE" - "TYPE" - "ERROR" Il valore è scritto direttamente in stem, salvo che per "LINGER", nel qual caso sono settati i campi di stem: - "ONOFF" - "LINGER" Parametri per "IP" sono: - "HDRINCL" - "IPOPTIONS" - "TTL" - "TOS" Il valore è scritto direttamente in stem; nel caso di "IPOPTIONS" il valore scritto in stem è un booleano non una stringa contenente le opzioni dello header IP. Ritorna -1 in caso d'errore. ------------------------------------------------------------------------------------ HELP Uso: argsMask=help(funName) <funName> Ritorna la maschera dei tipi degli argomenti relativa della funzione funName. ------------------------------------------------------------------------------------ HOSTERRORNO Uso: error=HostErrorno() - Ritorna il codice d'errore corrente di host-lookup. ------------------------------------------------------------------------------------ HOSTERRORSTRING Uso: errorString=HostErrorString(code) <code/N> Ritorna la stringa di errore di host-lookup associata al codice code. La stringa è non localizzata. Dovrebbe essere lo stack a localizzarla, non io. ------------------------------------------------------------------------------------ INETCKSUM Uso: cksum=InetCksum(data,len) <data>,[len/N] Calcola la checksum Internet su data per len bytes. Se len è omesso, la ckecksum viene calcolata su tutto data. La checksum calcolata è quella che, ad esempio, è nello header IP, ovvero: "il complemento ad uno della parola a 16 bits somma del complemento ad uno di ciascuna parola di 16 bits di 'data', per una lunghezza di 'len' bytes"; se 'len' è dispari, un byte di pad posto a 0 è aggiunto in coda a 'data' nel computo della checksum. Facilita la creazione di pacchetti RAW. ------------------------------------------------------------------------------------ IOCTLSOCKET Uso: res=IOCtlSocket(socketfd,parm,value) <socketfd/N>,<parm>,value/N> Controlla i parametri di un socket. Attualmente parm può essere uno tra: - "FIOASYNC" - "FIONBIO" - "FIONREAD" - "SIOCATMARK" Ritorna -1 in caso d'errore. ESEMPIO res=IOCtlSocket(socketfd,"FIONBIO",1) if res==-1 then say "errore nel settare il socket come non bloccante" else say "socket settato come non bloccante" ------------------------------------------------------------------------------------ ISDOTADDR Uso: res=IsDotAddr(addr) <addr> Testa se un addr è un indirizzo Internet dotted e ben formato oppure no. Ritorna un booleano. ESEMPIO say IsDotAddr('127.0.0.1') ----> 1 say IsDotAddr('.0.2') ----> 0 say IsDotAddr('www.iol.it') ----> 0 ------------------------------------------------------------------------------------ ISLIBON Uso: res=IsLibON(name) <name> Testa su che stack si è o se una libreria è presente. Attualmente name può essere uno tra: - "MIAMI" Miami è lo stack - "TTCP" TermiteTCP è lo stack - "USERGROUP" la usergroup.library è presente (inutile per ora) Ritorna un booleano. ------------------------------------------------------------------------------------ ISSOCKET Uso: res=IsSocket(socketfd) <socketfd/N&lg Testa su che il socket esiste. Ritorna un booleano. ------------------------------------------------------------------------------------ LISTEN Uso: res=listen(socketfd,backlog) <socketfd/N>,<backlog/N> Avverte il sistema che il socket vuole ricevere connessioni remote. Backlog è il numero di connessioni massime accettate. Un valore di backlog pari a 5 significa che si accettano connessioni in numero massimo che, di solito, lo stack permette di definire in numero maggiore di 5. Ritorna -1 in caso d'errore. ------------------------------------------------------------------------------------ MIAMIDISALLOWDNS Usage: MiamiDisallowDNS(status) [status/N] FUNZIONE SOLO PER MIAMI Attiva/disattiva il lookup DNS. Il valore di default per status è 0. Returns always 1. ------------------------------------------------------------------------------------ MIAMIGETPID Usage pid = MiamiGetPid() - FUNZIONE SOLO PER MIAMI Ritorna il pid del processo come packet chars. ------------------------------------------------------------------------------------ MIAMIONOFFLINE Usage: MiamiOnOffline(interface,status) <interface>,[statu/N] FUNZIONE SOLO PER MIAMI Cambia lo stato dell'interfaccia specificata. Il valore di default per status è 0. iterface è una tra: - "mi0" - "lo0" La funzione non attende il cambia e torna subito 1. ------------------------------------------------------------------------------------ MIAMIISONLINE Usage: res = MiamiIsOnline(interface) <interface> FUNZIONE SOLO PER MIAMI testa se l'interfaccia è online. iterface è una tra: - "mi0" - "lo0" Ritorna un booleano ARexx. ------------------------------------------------------------------------------------ MIAMISUPPORTSIPV6 Usage: res = MiamiSupportsIPV6() - FUNZIONE SOLO PER MIAMI Testa la versione di Miami supporta il protocollo IPV6. Ritorna un booleano ARexx. ------------------------------------------------------------------------------------ OBTAINSOCKET Uso: sockfd=ObtainSocket(key,family,type,protocol) <key><family>,<type>,<protocol> La funzione serve per il passaggio dei socket tra una macro e l'altra. Ottiene un socket rilasciato altrove con ReleaseSocket() o ReleaseCopyOfSocket(). Key deve essere la stringa ritornata proprio da una di quelle due funzioni. Il meccanismo usato per rilasciare i socket è il seguente: - alla chiamata di ReleaseSocket() o ReleaseCopyOfSocket() il socket fa ancora parte dell'ambiente della macro in cui è stato creato; - se il socket non viene ottenuto per mezzo di ObtainSocket(), all'uscita della macro in cui il socket è stato creato, esso viene deallocato; - se invece il socket viene ottenuto per mezzo di ObtainSocket(), tutte le risorse ad esso connesse vengono passate alla macro chiamante ObtainSocket() ed è come se il socket fosse stato creato all'interno della macro chiamante ObtainSocket(); - al momento della chiamata a ObtainSocket() originale, se il socket non può essere ottenuto o c'è poca memoria, esso viene lasciato nell'ambiente della macro in cui è stato creato; - una volta rilasciato, il socket non può essere più usato nella macro ove è stato creato, salvo che non venga prima ottenuto; - la key è un pacchetto di caratteri di lunghezza 8 in caso di successo o lunghezza 4, paragonabile a null(), come per i messaggi che arrivano ad una porta ARexx; diversi controlli sono effettuati per verificare la coerenza di una key e dovrebbe essere difficile "imbrogliare" la ObtainSocket(), passandogli una key falsa; in ogni caso, si consiglia di non "giochicchiare" con le key ritornate da ReleaseSocket() o ReleaseCopyOfSocket() e passate ad ObtainSocket(). L'uso di questa funzione è comune all'interno di un service "concorrente", che rilascia il socket ritornato da accept() con ReleaseSocket() e lancia una macro che si occupa della gestione della connessione, passandogli come argomento proprio la key ottenuta da ReleaseSocket(). La prima operazione che la macro lanciata dal service dovrebbe effettuare dovrebbe essere proprio ottenere il socket con ObtainSocket() ed avvisare la macro "padre" del risultato dell'operazione (ad esempio inviandole un messaggio su una porta ARexx). Ritorna un intero >=0 indicante il socketfd in caso di successo o -1 in caso di fallimento. ------------------------------------------------------------------------------------ OPENCONNECTION Uso: sockfd=OpenConnection(proto,localPort,host,remotePort,stem) <proto>,<localPort>,[host],[remotePort],[stem/V] Crea socket, fa un bind(), tenta una connessione. Esempi sulle sue diverse forme aiuteranno a capire il suo uso (proto è una fra "TCP" o "UDP"): res=OpenConnection(proto,4050) crea un socket fa un Bind() del socket sulla porta 4050 res=OpenConnection(proto,"funnyService") cerca un service di nome "funnyService" crea un socket fa un Bind() del socket sulla porta relativa a "funnyService" res=OpenConnection("TCP","echo","www.nasa.org") cerca un service di nome "echo" risolve l'indirizizzo www.nasa.org crea un socket connette il socket a www.nasa.org:echo res=OpenConnection("UDP","echo","www.nasa.org") "Errore Arexx 18" res=OpenConnection("TCP",4000,"www.nasa.org") "Errore Arexx 18" res=OpenConnection("UDP",4000,"www.nasa.org","echo") cerca un service di nome "echo" risolve www.nasa.org crea un socket fa un bind() del socket sulla porta 4000 connette il socket a www.nasa.org:echo Il socket viene creato di type e protocol dipendenti dal proto specificato. In pratica la funzione crea un socket e lo "binda" o "connette" a seconda del protocollo specificato. Il quinto argomento è uno stem, che se specificato, ed in caso di connessione, viene settato a sockaddr_in. A parte la confusione che ora avrai, l'uso della funzione è semplicissimo e velocizza le procedure di connessione, ad esempio con res=OpenConnection("TCP","http","ftp.unina.it/pub/aminet") ci si connette al service http aminet presso unina, saltando completamente, la risoluzione dell'indirizzo, la creazione esplicita del socket e la connessione. In caso di errore il socket viene deallocato. In pratica, la funzione accetta da 2 a 5 argomenti: - la forma con 2 argomenti: - protocollo "TCP" oppure "UDP" - porta nome del service o numero porta crea un socket e lo "binda" alla porta specificata. - la forma con 3 argomenti - protocollo "TCP" - porta nome del service - indirizzo indirizzo a cui connettersi crea un socket e lo connette al service dell'indirizzo specificato. - la forma con 4 argomenti - protocollo "UDP" - portaLocale nome del service o numero porta - indirizzo indirizzo a cui connettersi - portaRemota nome del service o numero porta crea un socket, lo "binda" a portaLocale lo connette al service portaRemota dell'indirizzo specificato. Ogni altra forma genera errore ARexx 18. Ritorna un intero: - >=0 socketfd in caso di successo; - -3 in caso di service non trovato; - -2 in caso di errore host-lookup; - -1 in caso di errore generico di bsdsocket.library. Disabilitata su TermiteTCP. ------------------------------------------------------------------------------------ RECV Uso: res=recv(socketf,buff,len,flags) <socketfd/N>,<buff/S>,<len/N>,[flags] Riceve massimo len bytes da un socket connesso e li scrive in buff. Flags è una giustapposizione di: - "OOB" - "PEEK" - "WAITALL" ovvero una stringa formata da una o più delle precedenti "parole" separate da spazi, come ad esempio "OOB PEEK". In caso di 0 byte ricevuti, buff viene posto a "". Ritorna il numero di bytes ricevuti o -1 in caso d'errore. ------------------------------------------------------------------------------------ RECVFROM Uso: res=RecvFrom(socketfd,buff,len,flags,stem) <sockefd/N>,<bufferStem/S>,<len/N>,[flags],[stem/V] Riceve len bytes da un socket connesso o meno e li scrive in buff. Flags è una giustapposizione di: - "OOB" - "PEEK" - "WAITALL" ovvero una stringa formata da una o più delle precedenti "parole" separate da spazi, come ad esempio "OOB PEEK". Se il socket è connesso, stem può essere omesso. Altrimenti stem deve essere presente e settato a sockaddr_in (ma il controllo non viene effettuato dalla funzione, perchè questa versione della libreria non tiene traccia dei socket connessi). In caso di 0 byte ricevuti, buff viene posto a "". Ritorna il numero di bytes ricevuti o -1 in caso d'errore. ------------------------------------------------------------------------------------ RECVLINE Uso: res=RecvLine(socketfd,buff,len,flags,stem) <socketfd/N>,<buffer/S>,len/N>,[flags],[stem] Riceve una linea di max len bytes da un socket e la scrive in buff. Questa funzione è una ReadLine() abbastanza "stupida" e non bufferizzata". Si veda RecvFrom(). ------------------------------------------------------------------------------ RELEASECOPYOFSOCKET Uso: key=ReleaseCopySocket(socketfd) <socketfd/N> Rilascia una copia di un socket. Il socket è ancora attivo all'interno della macro in cui è stato creato. Ritorna una key stringa di 8 caratteri in caso di successo o 4, confrontabile con null(), in caso di fallimento. Si veda ObtainSocket(). ESEMPIO key=ReleaseCopyOfSocket(socketfd) if key==null() then ... /* errore */ else ... /* successo */ ------------------------------------------------------------------------------ RELEASESOCKET Uso: key=ReleaseSocket(socketfd) <socketfd/N> Rilascia un socket. Il socket non è più attivo all'interno della macro in cui è stato creato. Ritorna una key stringa di 8 caratteri in caso di successo o 4, confrontabile con null(), in caso di fallimento. Si veda ObtainSocket(). ESEMPIO key=ReleaseSocket(socketfd) if key==null() then ... /* errore */ else ... /* successo */ ------------------------------------------------------------------------------ RESOLVE Uso: addrValue=resolve(host) <host> Converte da nome host ad indirizzo Internet. Chiama in sequenza inet_addr() e se questa fallisce, gethostbyname(). Ritorna l'indirizzo Internet di host oppure -1. ESEMPIO addr=resolve("www.AlfonsoRanieri.edu") say addr -----> -1 Hehe ------------------------------------------------------------------------------------ SEND Uso: res=send(socketfd,data,flags) <socketfd>,<data>,[flags] Invia data ad un socket connesso. Flags è una giustapposizione di: - "OOB" - "DONTROUTE" ovvero una stringa formata da una o più delle precedenti "parole" separate da spazi, come ad esempio "OOB DONTROUTE". Ritorna il numero dei bytes inviati o -1 in caso d'errore. ------------------------------------------------------------------------------------ SENDTO Uso: res=SendTo(socketfd,data,flags,stem) <socketfd/N>,<data>,[flags],[stem/V] Invia data ad un socket. Flags è una giustapposizione di: - "OOB" - "DONTROUTE" Ovvero una stringa formata da una o più delle precedenti "parole" separate da spazi, come ad esempio "OOB DONTROUTE". Se il socket è connesso, stem può essere omesso. Altrimenti deve essere settato a sockaddr_in. Ritorna il numero dei bytes inviati o -1 in caso d'errore. ------------------------------------------------------------------------------------ SETSOCKETBASE Uso: res=SetSocketBase(stem) <stem/V> Setta alcuni parametri globali della base della libreria bsdsocket.library. La funzione originale della bsdsocket.library è SocketBaseTagList(), che è divisa in GetSocketBase() e SetSocketBase() in questa libreria. Di stem si devono impostare i campi corrispondenti ai parametri che si vogliono settare. I parametri attualmente ammessi sono: - "BREAKMASK" - "DTABLESIZE" - "ERRNO" - "HERRNO" - "SIGEVENTMASK" - "SIGIOMASK" - "SIGURGMASK" - "LOGFACILITY" - "LOGMASK" - "LOGSTAT" Ritorna -1 in caso di errore. ------------------------------------------------------------------------------ SETSOCKETSIGNALS Uso: SetSocketSignals(intrMask,ioMask,urgMask) [intrMask/N],[ioMask/N],[urgMask/N] Idica allo stack quale segnali usare in corrispondenza di SIGINT, SIGIO and SIGURG. la stessa cosa può essere fatto con SetSocketBase(). L'uso di questa funzione è deprecato negli autodoc di Miami. Ritorna sempre 1. ------------------------------------------------------------------------------ SETSOCKOPT Uso: res=SetSockOpt(socketfd,level,parm,value,value2) <socketfd/N>,<level>,<parm>,<value>,[value2/N] Setta il parametro parm del socket al livello level al valore value (e value2, se ammesso). Attualmente il livello può essere uno tra: - "SOCKET" - "IP" - "TCP" Parametri per "SOCKET" (con accanto il tipo) sono: - "DEBUG" N - "REUSEADDR" N - "REUSEPORT" N - "KEEPALIVE" N - "DONTROUTE" N - "LINGER" N - "BROADCAST" N - "OOBINLINE" N - "SNDBUF" N - "RCVBUF" N - "SNDLOWAT" N - "RCVLOWAT" N - "SNDTIMEO" N - "RCVTIMEO" N - "TYPE" N - "ERROR" N - "EVENTMASK" D Nel caso di "EVENTMASK", value è una giustapposizione di: - "ACCEPT" - "CLOSE" - "CONNECT" - "ERROR" - "OOB" - "READ" - "WRITE" ovvero una stringa formata da una o più delle precedenti "parole" separate da spazi, come ad esempio "CONNECT ERROR". Nel caso di "LINGER", "SNDTMEO" e "RCVTIMEO" anche value2 può essere passato. Parametri per "IP" sono: - "HDRINCL" N - "TTL" N - "TOS" N Parametri per TCP sono: - "NODELAY" N - "MAXSEG" N - "NOPUSH" N - "NOOPT" N Ritorna -1 in caso d'errore. ESEMPIO /* settiamo il socket per informarlo che i dati scritti su di esso contengono già lo header ip */ res=SetSockOpt(socketfd,"IP","HDRINCL",1) if res==-1 then do say "non posso settare il socket come HDRINCL" exit end /* fatto. Ora sono guai vostri :-) */ ------------------------------------------------------------------------------ SHUTDOWN Uso: res=ShutDown(socketfd,how) <socketfd/N>,<how/N> Causa la chiusura di una parte o di tutta una connessione full-duplex. Se how è 0 la ricezione è chiusa. Se how è 1 la trasmissione è chiusa. Se how è 2 sia la ricezione che la trasmissione sono chiuse. Dealloca il socket. Ritorna -1 in caso d'errore. ------------------------------------------------------------------------------ SYSLOG Uso: res=SysLog(text) <text> Scrive text in syslog. ------------------------------------------------------------------------------ SOCKET Uso: sockfd=socket(family,type,protocol) <family>,<type>,<protocol> Crea un socket e tutte le risorse ad esso associate. Si ricordi che solo le funzioni - accept() - Dup2Socket() - ObtainSocket() - OpenConnection() - socket() creano un socket, che è la base di ogni operazione di trasmissione e ricezione. Family per ora è solo "INET". Non tutte le combinazioni di type e protocol hanno senso e/o funzionano su gli stack AmigaOS. Ritorna il socketfd come un intero >=0 o -1 in caso d'errore. ------------------------------------------------------------------------------ SOCKATMARK Usage: res = SockatMark(socketfd) <socketfd/N> FUNZIONE SOLO PER MIAMI Testa se il socket è out of bans. Ritorna un booleano ARexx. ------------------------------------------------------------------------------------ WAITSELECT Uso: res=WaitSelect(stem,secs,micros,signals) <stem/V>,[secs/N],[micros/N],[signals/N] Attende eventi su sockets o timeout o segnali exec. Un esempio aiuterà a capire il suo funzionamento: supponiamo di avere due socket i cui socketfd sono sfd1 e sfd2; vogliamo attendere che qualcosa capiti ai sockets, oppure un timeout di 10 secondi, oppure un segnale exec specificato nella maschera signals (ad esempio un segnale di una porta oppure un segnale allocato precedentemente): WAIT.READ.0 = sfd1 /* per attendere l'evento "pronto a ricevere" */ WAIT.READ.1 = sfd2 WAIT.WRITE.0 = sfd1 /* per attendere l'evento "pronto a sendare" */ WAIT.WRITE.1 = sfd1 WAIT.EX.0 = sfd1 /* per attendere una eccezione */ WAIT.EX.1 = sfd2 res = WaitSelect("WAIT",10,0,signals) /* a questo punto res è un intero: <0 errore =0 nessuno evento sui sockets >0 numero sei sockets su cui sono occorsi eventi A parte res, un qualcosa è occorso e possiamo testare ogni socket per vedere se qualcosa è accaduto su di esso */ if wait.0.read then ... /* il socket sfd1 è pronto per ricevere */ if wait.1.write then ... /* il socket ssfd2 è pronto a sendare */ if wait.0.ex then ... /* una eccezione è occorsa sul socket sfd1 */ Ritorna -1 in caso d'errore. ------------------------------------------------------------------------------------------------ 10. Ringraziamenti - grazie a shido per il suo regalo "Grazie shido. Tanti ovetti per te :-)"; - grazie ad [X_MaN] che mi ha introdotto nel mondo delle irc ed in generale in Internet; - grazie a poing per il suo aiuto; - grazie ad Amiga "Possa vivere per sempre!"; - grazie a Kruse per il suo meraviglioso Miami "Si, mi registro al più presto. Tu però togli le backdoors, OK? Hehe" ------------------------------------------------------------------------------------------------ 11. Bibliografia - Quasi tutte le rfc (<nic.nordu.net:21> - ftp anonimo); - "Unix Network Programming" - W. Richard Stevens PTR Prentice Hall; - socket.library autodoc di MiamiSDK, AmiTCPSDK, TermiteTCPSDK, prelevabili liberamente su aminet o sui siti delle rispettive case produttrici dei tre stacks. ------------------------------------------------------------------------------------------------ 12. Da Fare - un serio debugging, con l'aiuto di qualche betatester volenteroso; - una serie di funzioni di basso livello, per spedire pacchetti RAW; - una serie di funzioni di livello superiore (tipo OpenConnection(), anche se il mio scopo rimane quello di mantenere la API originale di bsdsocket.library); - una serie di funzioni della miami.library e della usergroup.library; ------------------------------------------------------------------------------------------------ 13. Note Ho detto più volte che questo documento non vuole in nessun modo essere una guida all'uso delle funzioni della bsdsocket.library, ma voglio richiamare alcuni semplici elementi (e prego gli esperti in TCP/IP di scusare la mia superficialità). Per realizzare una connessione o in generale una trasmissione in Internet si deve specificare una cinquina: {protocollo, hostMittente, portaMittente, hostDesinatario, portaDestinatario}. La creazione di un socket con Socket() specifica solo il primo elemento di questa cinquina. Il secondo ed il terzo sono specificati in maniera diversa a seconda del protocollo. Ad esempio, in TCP lo stack li assegna automaticamente, in UDP bisogna fare un bind() ad una porta. Gli ultimi due devono essere forniti e per farlo è necessario passare la struttura sockaddr_in, che non è altro che un indirizzo Internet ed una porta, al momento della connessione a Connect() od al momento della trasmissione con SendTO(). In pratica, la sequenza minima di operazioni da eseguire è: Per un service TCP: socket() bind() listen() Accept() Per un client TCP: socket() connect() Per un service UDP: socket() bind() recv() Per un client UDP: socket() bind() ( connect() ) SendTo() E' tutto abbastanza semplice salvo quando si deve cominciare a pensare alla gestione di diversi sockets ed alla contemporanea ricezioni di segnali exec, cose per cui rimando alla bibliografia citata ed in piccola parte ai miei esempi acclusi nell'archivio. Questa parte della API di bsdsocket.library è "veramente brutta" (IMHO). Si ricordi che ogni service a cui ci si collega è regolato da un ben determinato protocollo che bisogna rispettare. Ad esempio un service pop3 (quello da cui si preleva la posta) è regolato dal protocollo POP3, un service irc è regolato dal protocollo IRC e così via. Le fonti sono le rfc (in particolare la rfc1920 per avere una lista delle ultime versioni dei vari protocolli Internet, anche so credo che la 1920 sia ormai obsoleta, comunque...). Nessun esempio di creazione di pacchetti "a mano" è accluso. Per la maggior parte, essi non sono esempi "buoni". In qualsiasi momento si può uscire da una macro senza chiudere un bel niente. In ogni modo, è meglio rilasciare al più presto sockets non usati. Non ci si preoccupi se alcuni sockets restano in stato di attesa, dopo la loro chiusura, (cosa leggibile ad esempio con miaminetstat su Miami), perché è "normale". La deallocazione automatica delle risorse è realizzata sfruttando i campi pr_ExitCode e pr_ExitData della struttura Process connessa con la macro ARexx. All'uscita una catena di pr_EXitCode(pr_EXitData) viene chiamata. Questo può essere un problema se si usano altre librerie che sfruttano questi campi della struttura Process (non è un problema con altre mie librerie, perché come detto una catena di queste funzioni viene chiamata e per lo stesso motivo neanche con la init_inetd() di AmiTCP sorgono particolari problemi). Si è scelto di rinunciare ad una maggiore velocità, aprendo e chiudendo la bsdsocket.library quando necessario, cosicché in ogni momento si può chiudere lo stack senza problemi; alla chiusura dello stack, se qualche macro ancora usa la bsdsocket.library, essa viene segnalata dallo stack stesso con un CTRL-C; stai quindi ben attento alla ricezione di questo segnale, che oltre ad indicare che l'utente vuole uscire dalla macro, può anche indicare che lo stack stesso sta per chiudere; in ogni caso la macro può rifiutarsi di ubbidire al CTRL-C da parte dello stack senza alcun problema. ------------------------------------------------------------------------------------------------ 14. Cambiamenti Cambiamenti rispetto alle versioni < 1.9: - gli indirizzi Internet erano ritornati e passati come interi ARexx. Ciò causava non pochi problemi e si è scelto di usare indirizzi Internet in forma dotted, ovvero gli indirizzi tornati da resolve() o settati dalle funzioni di get host sono nella forma XXX.XXX.XXX.XXX (vedere ad esempio socket.doc/inet_addr). Sono state eliminate le funzioni InetAddr() ed InetNtoA(). E' stata aggiunta la funzione IsDotAddr() che torna un booleano indicante se l'indirizzo passato è nella forma dotted o no. In ogni caso si può risalire all'intero rappresentante l'indirizzo Internet attraverso la funzione Addr2C(). Non dovrebbe cambiare un granchè, poichè resolve() torna la stringa "-1" se l'indirizzo è errato o non esiste, ma diverse modifiche sono necessarie. Cambiamenti rispetto alle versioni < 2.0: - le funzioni rmh sono ora nella rmh.library, parte dell'archivio rxsocket.lha. ------------------------------------------------------------------------------------------------ 15. Supporto inetd Il supporto inet di AmiTCP e Miami (TermiteTCP non ha inetd) è realizzato in questa maniera: - come nome servizio in inetd va inserito il nome di un programma (vedi dopo) che lancia la macro; - poiche in entrambi gli stack, inetd passa i dati necessari per ottenere il socket accettato da inetd nei campi pr_ExitCode e pr_ExitData, non è possibile lanciare la macro un il programma "rx", ma si deve usare un programma che provi ad ottenere il socket, crei una struttura socket privata nella base della libreria e chiami la macro; il programma è fornito nell'archivo e si chiama rxs, va usato solo in inetd; riceve un solo argomento, il nome della macro da lanciare; - se la macro non esiste, il socket viene chiuso; se la macro esiste, il socket relativo alla connessione che deve gestire è quello di id 0; per verificare all'interno della macro, se il socket 0 esiste si può usare "IsSocket(0)"; ------------------------------------------------------------------------------------------------ Castel di Sangro (FORZA CASTELLO, ANCHE IN C1 :-) 1.5.98