STIK
Vous voulez faire un soft internet (browser, ftp, lecteur de
news, etc.) mais vous ne savez pas comment ça marche ? Cet article
est fait pour vous !! A la fin de celui-ci, STiK n'aura plus de
secrets pour vous !!
Les exemples vous seront donnés en C, bien qu'il est possible
d'utiliser STiK en Assembleur (GFA aussi ?). Il est à noter que
l'utilisation de STiK en Assembleur est relativement complexe (dû
aux structures du C utilisées par STiK) et n'apporte absolument
aucun gain de vitesse, un jsr du C n'est pas plus lent qu'un jsr
assembleur ;-)
NOTE : L'utilisation de STiK permet de faire un logiciel internet
fonctionnant évidemment sous STiK mais aussi sous STinG (non testé
personnellement) et MiNT-Net (testé avec succès), donc en PPP !
I./ Présentation du package de développement
Ce package doit pouvoir se trouver sur le site de LIP6
(ftp.lip6.fr/pub/atari).
Le fichier STIKSPEC.TXT contient une doc
sur les routines de STiK. Les fichiers d'include sont nécessaires
pour le développement en C.
Les fichiers SMTP sont des exemples d'applications : un
posteur d'E-Mail ...
C'est un peu mince, mais suffisant, d'ailleurs STiK est très simple d'emploi, mais un article en français vaut mieux que de nombreux essais ;-)
II./ Comment ca marche ??
STiK se présente sous une liste de routines dont on passe les
paramètres sur la pile (alors les fadas de l'Asm, on est content ?
;)).
Pour récupérer l'adresse de ces routines il suffit de :
- Passer en superviseur
- Recherche dans le cookie-jar "STiK"
- La valeur de ce cookie correspond à l'adresse d'une
structure DRV_LIST contenant diverses info sur la
version de STiK etc ... et un pointeur sur une structure
contenant l'adresse des routines.
Voilà voilà, donc vous comprennez bien que faire tout ça en
Asm, c'est relativement chaud et n'apporterait pas grand chose. Si
vous ne faîtes que de l'Assembleur et connaissez pas le C, aucun
problème, vous n'avez qu'à recopier le programme d'initialisation
fourni et faire vos routines en Assembleur, et linker le tout.
III./ Programme d'initialisation
Voici le bout de code pour initialiser STiK, il doit être
inséré dans tout programmes voulant utiliser STiK. il s'agit du
bout de code récupéré dans l'exemple avec des commentaires
traduits et/ou rajoutés ...
#include <string.h> #include <tos.h>
#include "e:\prog\pure_c\divers\stikspec\include\drivers.h" #include "e:\prog\pure_c\divers\stikspec\include\transprt.h"
static long init_drivers(void); static int initialise(void);
/* Ces définitions sont nécessaires */ DRV_LIST *drivers = (DRV_LIST *)NULL; TPL *tpl = (TPL *)NULL;
static long init_drivers(void) { long i = 0; ck_entry *jar = *((ck_entry **) 0x5a0);
while (jar[i].cktag) { if (!strncmp((char *)&jar[i].cktag, CJTAG, 4)) { drivers = (DRV_LIST *)jar[i].ckvalue; return (0); } ++i; } return (0); /* NULL si on ne trouve pas le cookie */ } static int initialise(void) { static long init_drivers(void);
Supexec(init_drivers);
/* Est-ce qu'on a qqchose de correct ? */
if (drivers == (DRV_LIST *)NULL) { Cconws("STiK is not loaded\r\n"); return (FALSE); }
/* Le nombre magic correspond-t-il ? */ if (strcmp(MAGIC, drivers->magic)) { Cconws("Magic string doesn't match\r\n"); return (FALSE); }
/* OK, maintenant nous pouvons récupérer l'adresse de la * couche "TRANSPORT". Si cela parait compliqué, c'est parce * que j'ai (NdF:l'auteur de STiK) essayé de créer maintenant * ce que j'utiliserais plus tard. Dans le futur, il y aura * plusieurs drivers accessible avec cette méthode. Avec de * la chance votre code continuera de fonctionner avec de * futures versions (NdF : arf, c'est rassurant ;)) */
tpl = (TPL *)get_dftab(TRANSPORT_DRIVER);
/* La couche réseau est-elle chargé ? */ if (tpl == (TPL *)NULL) { Cconws("Couche réseau *non* chargé\r\n"); return (FALSE); } Cconws("Couche réseau chargé, Auteur "); Cconws(tpl->author); Cconws(", version "); Cconws(tpl->version); Cconws("\r\n");
return (TRUE); }
main() { if ( initialise() == FALSE ) /* Si Erreur */ return 0; /* kassos ... */
... /* Votre programme */ ... /* Ici ... */ ...
return 0; }
NOTE: Le résultat de l'initialisation est affiché directement sur l'écran à coup de Cconws (à coup de PRINT quoi), il serait peut-être préférable de virer ces lignes ou de les remplacer.
IV./ Les fonctions
Vous avez initialisé STiK, récupéré l'adresse des fonctions,
etc ... il ne reste plus qu'à les utiliser ! Voici la principale
liste de fonctions (il y en a d'autre, mais je ne connais pas leur
intérêt direct ;-)) avec leur descriptions :
char * cdecl KRmalloc(int32) Alloue un bloc de mémoire. Ce bloc de mémoire vient du buffer interne à STiK et présente les particularités (très intéressantes) suivantes : - La mémoire alloué est sur une adresse multiple de 8 octets (gain en vitesse d'accès) - Un petit header est créé entre le KRmalloc et le KRfree pour permettre de voir si une corruption a eu lieu (dépassement de la mémoire allouée par exemple). La fonction retourne NULL si l'allocation n'a pu se faire.
void cdecl KRfree(char *); Libère un bloc alloué avec KRMalloc ou KRrealloc. ATTENTION KRfree ne teste pas le paramètre pour voir s'il est NULL !
int32 cdecl KRgetfree(int16 flag); Retourne le taille disponible du buffer interne à STiK. Si le flag est à TRUE, la fonction retourne la taille du bloc le plus large, sinon la fonction retourne la valeur totale du buffer.
char * cdecl KRrealloc(char *block, int32 newsize); Change la taille d'un bloc alloué. Si newsize est plus grand que l'ancienne taille, alors le contenu de l'ancien bloc est copié dans le nouveau. Si newsize = 0, alors le bloc est libéré et renvoie NULL. Si block = NULL alors retourne l'adresse d'un nouveau block d'une taille newsize et remplie de 0 celui-ci.
char * cdecl get_err_text(int16 code); Retourne un pointeur sur un texte décrivant l'erreur code. Ce texte est en anglais. Si l'erreur est inconnue, un pointeur sur "" est retourné.
char * cdecl getvstr(char *var); Retourne un pointeur sur la valeur d'une variable de configuration définie dans DEFAULT.CFG. Il n'y a pas de différence entre minuscules et majuscules. Le pointeur commence sur la valeur elle-même (c'est-à-dire après le '=' et après un espace eventuel) Si la variable n'existe pas un pointeur sur "0" est retourné. Si la variable n'a pas de valeur, un pointeur sur "1" Si la variable a un '=' mais pas de valeur, un pointeur sur "0" est retourné.
int16 cdecl carrier_detect(void); Si la variable de configuration CDVALID est FALSE alors 0 (inconnue) est retourné. Sinon la fonction retourne +1 pour une porteuse, et -1 dans le cas contraire.
int16 cdecl TCP_open(uint32 rhost, int16 rport, int16 tos, uint16 obsize); Essaye d'ouvrir une connexion to rhost sur rport tos = Type de Service. Aucun test n'a été fait avec une valeur différente de 0. obsize = taille du buffer de sortie à allouer, TCP_send met les données dans ce buffer. La taille dépend des besoins mais un gros buffer n'est pas forcément mieux. Un buffer de 500 à 2000 est correct. La fonction retourne un handle (>0) ou un code d'erreur négatif. La fonction sort immédiatement sans attendre que la connexion soit établie.
Ouverture passive : Si rhost est 0, alors la connexion devient un socket LISTEN et attend pour une requête de connexion venant d'un hôte. Dans ce cas, rport correspond au port local et non au port de l'hôte. Pour un complément d'information, se reporter au fichier STIKSPEC.TXT
int16 cdecl TCP_close(int16 cn, int16 timeout); Ferme une connexion cn = Handle timeout = temps maximum en secondes consacré à la fermeture, qui peut prendre un certain temps si le net est encombré. La fonction retourne 0 ou un code d'erreur négatif. Cette fonction DOIT être appelée dans l'ordre (des handles ?) pour éviter une fragmentation de la mémoire. Une valeur de 0 pour le timeout est acceptable pour une fermeture immédiate. Si la touche ESC est pressé pendant la période de timeout, TCP_close retourne immédiatement le code d'erreur E_USERTIMEOUT.
int16 cdecl TCP_send(int16 cn, char *buf, int16 len); Envoie d'octets cn = Handle buf = Pointeur sur les données à envoyer len = Nombre d'octets à envoyer. La fonction retourne E_NORMAL si tout est Ok Si l'erreur E_OBUFFULL est retourné il suffit de faire une boucle sur le TCP_send permettant d'attendre que le buffer de sortie ait une place. ATTENTION : Le buffer défini à la connexion doit être PLUS GRAND que le nombre d'octets que vous pouvez transférer en une fois, dans le cas contraire les données ne seront jamais envoyées !
int16 cdecl TCP_wait_state(int16 cn, int16 state, int16 timeout); Attend jusqu'au timeout que la connexion prenne un certain état. Sert par exemple pour attendre que la connexion soit établie après un TCP_open. cn = Handle state = Etat à attendre (TESTABLISH pour attendre la connexion) timeout = Temps maximum à ne pas dépasser Retourne E_NORMAL ou un code d'erreur. Si la touche ESC est appuyée pendant que la fonction attend, la fonction retourne E_USERTIMEOUT.
int16 cdecl TCP_ack_wait(int16 cn, int16 timeout); Attend que toutes les données contenus dans le buffer soient reçues par le destinataire. cn = Handle timeout = Temps maxi à ne pas dépasser (en millisecondes) La fonction retourne E_NORMAL si tout a été envoyé ou alors si le timeout à été atteint.
int16 cdecl CNbyte_count(int16 cn); Retourne le nombre d'octets disponibles sur le buffer d'entrer, ou un code d'erreur. cn = Handle
int16 cdecl CNget_char(int16 cn); Retourne le prochain caractère disponible sur le buffer d'entré. cn = Handle La fonction retourne E_NODATA s'il n'y a plus de caractère. Si vous utilisez la fonction CNget_char pour l'entrée de données, alors la boucle DOIT inclure CNbyte_count.
int16 cdecl resolve(char *dn, char **rdn, uint32 *alist, int16 lsize); Détermine l'adresse IP d'un hôte. dn = nom de domaine rdn = est le vrai nom de domaine, qui est retourné si dn est un alias (CNAME). alist est un pointeur sur un tableau où sont les adresses IP. lsize est la taille de ce tableau. Si les informations sont dans le cache local, alors la fonction se finit immédiatement, dans le cas contraire un algorithme pour contacter les nameservers est entrepris. Si rnd = NULL alors aucune valeur lui est assigné, sinon un pointeur sur le nom de domaine associé avec les adresses est assigné à *rdn. Ce pointeur DOIT être libéré avec KRfree. La fonction retourne le nombre d'adresses copié sur alist, ou un code d'erreur négatif.
void cdecl ser_disable(void); Cette fonction désactive le port série. Elle est nécessaire seulement pour les combinaisons baud/vitesse cpu qui n'autorise pas le DMA et les ports série à travailler ensemble. Cette fonction est INUTILE et n'effectue AUCUNE opération. L'auteur attendait d'avoir un report de bug pour faire quelque chose.
void cdecl ser_enable(void); Voir ci-dessus.
CIB * cdecl CNgetinfo(int16 cn); Retourne un pointeur sur une structure CIB contenant les informations sur la connexion de Handle cn. Ces infos sont : Le protocole Hôte Port de l'hôte Port local L'adresse du port local peut être trouvée avec : stik_cfg->client_ip Ne RIEN changer ici sauf si vous savez VRAIMENT ce que vous faîtes ...
V./ Exemple de connexion à un serveur IRC
Voici comment se connecter à un serveur IRC. ATTENTION !!! Il
n'y a AUCUNE gestion de faite, ce programme ne vous permettra pas
de discuter. En fait vous ne verrez rien. Cet exemple a juste pour
but d'établir une connexion.
Nous allons essayer de nous connecter au serveur IRC de
Bordeaux dont voici l'adresse : salambo.enserb.u-bordeaux. Les
connexions sur les serveurs IRC se font sur le port 6667.
Avant toute opération, il faut initialiser STiK, on peut
reprendre le programme d'initialisation précédent avec la fonction
main suivante :
#include <stdio.h> #include <ext.h>
int Connect_Irc(void); void Deconnect_Irc(void);
int Connect_Irc() { unsigned long rhost; int cn;
/* Pour établir une connexion avec un serveur, il faut connaître son adresse IP : */ printf("\RESOLUTION EN COURS ...");
ret = resolve("salambo.enserb.u-bordeaux.fr", (char **)NULL, &rhost, 1); if ( ret < 0 ) { /* Erreur */ printf("\nRESOLUTION ERREUR : %s", get_err_text(x)); return 0; } else /* Résolution correcte */ printf("\nADRESSE IP : %ld", rhost);
/* rhost contient maintenant l'adresse IP du serveur IRC. Il suffit maintenant d'ouvrir une connexion avec celui-ci. */ printf("\CONNEXION EN COURS ...");
if ((cn = TCP_open(rhost, 6667, 0, 2000)) < 0) printf("\nERREUR : %s", get_err_text(cn));
return cn; }
void Deconnect_Irc() { int tstat;
tstat = (int) TCP_close(cn, t); printf("\nFERMETURE : %s", get_err_text(tstat)); }
main() { int cn;
if ( initialise() == FALSE ) /* Si Erreur */ return 0; /* kassos ... */
cn = Connect_Irc(); /* Connexion */ if ( cn < 0 ) /* Si erreur ... */ return 0; /* Kassos ... */ else { getch(); /* Attend une touche */ Deconnect_Irc(); /* Déconnexion */ }
return 0; }
Et voilà !!!! On ne peut faire plus simple ... Oui c'est bien beau, mais comment fait on pour créer un lecteur de News, un client FTP, etc ... ??? Réponse :
VI./ Les RFC !!
Les services d'internet sont régis par des définitions très
précises. En effet rien n'est laissé au hasard, par exemple les
en-têtes des e-mails mais aussi les protocoles PPP, TCP/IP, SMTP,
POP3, FTP etc ... ont leurs règles !
Et bien toutes ces règles sont définies dans des documentations appelés
RFC (Request For Comments). Ces documentations sont
très facilement trouvables sur le Net et c'est gratos ! Pour
trouver le RFC décrivant le protocole SMTP (Simple Mail Transfert
Protocol, il s'agit du protocole utilisé pour envoyer des emails),
il suffit d'aller sur Altavista (par exemple) et de taper "RFC
SMTP" et hop ! Je ne puis vous donner d'adresse où trouver les RFC
car je n'en ai pas trouvé les contenant tous ...
VII./ Les ports
Pour faire un soft reader de news utilisant le protocole NNTP
(Net News Transfert Protocol), il faut se connecter à un serveur
de News. Oui mais voilà, pour se connecter il faut connaître le
numéro du port !! (ex : 6667 pour l'IRC), et bien j'ai trouvé une
liste dans le fichier /ETC/SERVICES contenu dans le PPPKIT, je
n'ai pas vérifié si toutes les références étaient exactes mais
celles que j'ai pu vérifier se sont révélées exactes ... Voici la
liste (certainement non exhaustive) des ports utilisés en mode TCP
(car il y a aussi le mode UDP, cf STIKSPEC.TXT) :
Services Ports Commentaires echo 7 Echo discard 9 Discard systat 11 Active Users daytime 13 Daytime qotd 17 Quote of the Day chargen 19 Character Generator ftp-data 20 File Transfer Protocol (Data) ftp 21 File Transfer Protocol (Control) telnet 23 Virtual Terminal Protocol smtp 25 Simple Mail Transfer Protocol time 37 Time whois 43 Who Is domain 53 Domain Name Service finger 79 Finger supdup 95 hostnames 101 NIC Host Name Server pop 109 Post Office Protocol - Version 2 pop 110 Post Office Protocol - Version 3 portmap 111 SUN Remote Procedure Call auth 113 Authentication Service sftp 115 Simple File Transfer Protocol uucp-path 117 UUCP Path Service nntp 119 Network News Transfer Protocol netbios_ns 137 NetBIOS Name Service netbios_dgm 138 NetBIOS Datagram Service netbios_ssn 139 NetBIOS Session Service bftp 152 Background File Transfer Protocol bgp 179 Border Gateway Protocol UNIX services exec 512 Remote execution, passwd required login 513 Remote login shell 514 Remote command, no passwd used printer 515 Remote print spooling DAServer 987 SQL distributed access rlb 1260 Remote loopback diagnostic nft 1536 NS network file transfer pmlockd 1889 SynerVision locking daemon netdist 2106 Update(1m) network distribution service rfa 4672 NS remote file access lanmgrx.osB 5696 LAN Manager/X for B.00.00 OfficeShare hcserver 5710 HP Cooperative Services grmd 5999 Graphics resource manager spc 6111 Sub-process control iasqlsvr 7489 Information Access recserv 7815 SharedX Receiver Service Kerberos (Project Athena/MIT) services klogin 543 Kerberos rlogin -kfall kshell 544 Kerberos remote shell -kfall ekshell 545 Kerberos encrypted remote shell -kfall kerberos 750 Kerberos (server) tcp -kfall krbupdate 760 Kerberos registration -kfall kpasswd 761 Kerberos "passwd" -kfall eklogin 2105 Kerberos encrypted rlogin -kfall
VIII./ Et voilà
Voilà la fin de cet article sur la programmation de STiK,
j'espère qu'il vous sera utile. Epluchez un peu le programme
d'exemple fourni avec STiK, il s'agit d'un soft pour poster le
courrier (par le protocole SMTP).
Si vous le désirez, je pourrais vous faire un autre article à
titre d'exemple : "Comment faire un client POP3 avec STiK".
Sur ce, bon Toxic Mag !