Programmation de la couche Internet

STIK


Par FirST'



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 !



[Retour au sommaire]