Transmission Control Protocol (TCP) és un protocol orientat a la connexió dintre del nivell de transport del model OSI que permet l'entrega de paquets de manera fiable, en el cas de TCP anomenats segments. Això significa que abans de poder transmetre cap dada, és necessari establir una connexió entre els dos nodes que es volen comunicar. Un cop establerta la connexió, el protocol s'encarrega de garantir que les dades arriben de manera correcta, ordenada i sense duplicats al punt de destinació. Un cop finalitza la transmissió és necessari tancar la connexió. Les aplicacions que utilitzen TCP per comunicar-se no s'han de preocupar de la integritat de la informació, no han de fer cap mena de control d'errors atès que poden assumir que tot el que reben és correcte, el mateix protocol s'encarrega de les tasques de control de flux i d'errors.
La fiabilitat associada a aquest protocol té un cost en la quantitat de recursos necessaris que el fan inadequat per alguns usos, com les retransmissions en temps real de vídeo, en què és preferible descartar un paquet que no ha arribat o que ho ha fet en mal estat atès que per a l'emissor pot resultar difícil retransmetre'l i per al receptor seria un problema esperar. Per a aquests casos existeix el protocol UDP.
TCP, és un dels protocols fonamentals d'Internet. Va ser creat entre els anys 1973 i 1974 per Vint Cerf i Robert Kahn. TCP dona suport a moltes de les aplicacions més populars d'Internet (navegadors, intercanvi de fitxers, clients ftp, ...) i protocols d'aplicació com per exemple HTTP, SMTP, SSH i FTP.
Informació tècnica
El TCP és un protocol de comunicació orientat a la connexió fiable del nivell de transport, actualment documentat per l'IETF al document RFC 793.[1] És un protocol de capa 4 segons el model OSI.
Funcions de TCP
A la pila de protocols TCP/IP, TCP és la capa intermèdia entre el protocol d'Internet (IP) i l'aplicació. Les aplicacions necessiten que la comunicació sigui fiable i, atès que la capa IP aporta un servei de datagrama no fiable (sense confirmació), TCP afegeix les funcions necessàries per a prestar un servei que permeti que la comunicació entre dos sistemes s'efectuï lliure d'errors, sense pèrdues i amb seguretat.
Els serveis proveïts per TCP corren a l'amfitrió (host) de qualsevol dels extrems d'una connexió, no a la xarxa. Per tant, TCP és un protocol per a manejar connexions d'extrem a extrem, aquestes connexions poden existir a través d'una sèrie de connexions punt a punt, de manera que aquestes connexions extrem-extrem són cridades en circuits virtuals.
- Orientat a la connexió: dos computadors estableixen una connexió per a intercanviar dades. Els sistemes dels extrems es sincronitzen amb l'altre per a manejar el flux de paquets i adaptar-se a la congestió de la xarxa.
- Operació Full-Dúplex: una connexió TCP és un parell de circuits virtuals, cadascun en una direcció. Només els dos sistemes finals sincronitzats poden utilitzar la connexió.
- Error Checking: una tècnica de checksum és utilitzada per a verificar que els paquets no estiguin corruptes.
- Acknowledgements: sobre la recepció d'un o més paquets, el receptor torna un acknowledgement (reconeixement) al transmissor indicant que ha rebut els paquets. Si els paquets no són notificats, el transmissor pot reenviar els paquets o acabar la connexió en cas que el transmissor cregui que el receptor ja no està a la connexió.
- Control de flux: si el transmissor està desbordant el buffer del receptor per transmetre massa ràpid, el receptor descarta paquets. Els acknowledgement fallits que arriben al transmissor l'alerten per a baixar la taxa de transferència o deixar de transmetre.
- Servei de recuperació de Paquets: el receptor pot demanar la retransmissió d'un paquet. Si el paquet no és notificat com a rebut (ACK), el transmissor envia de nou el paquet.
Els serveis fiables d'entrega de dades són crítics per a aplicacions de transferència d'arxius (per exemple FTP), serveis de bases de dades, processos de transaccions i altres aplicacions de missió crítica en les quals s'ha de garantir que cada paquet s'ha d'haver entregat al seu destí.
Funcionament del protocol amb detall
Les connexions TCP es componen en tres etapes: establiment de la connexió, transferència de dades i fi de la connexió. Per establir la connexió es fa servir el procediment anomenat de les 3 passes (3-way handshake). Per a la desconnexió, es fa servir el procediment anomenat de les 4 passes (4-way handshake). Durant l'establiment de la connexió, alguns paràmetres com el nombre de seqüència són configurats per assegurar l'entrega ordenada de les dades.
Procés de connexió
- 1.- El client envia una sol·licitud de connexió al servidor per mitjà d'un datagrama amb l'indicador SYN
- 2.- Si el servidor accepta la connexió amb el client li envia un datagrama amb els indicadors SYN i ACK
- 3.- El client confirma la connexió enviant al servidor un datagrama amb l'indicador ACK.
Transmissió de dades
A part de les característiques comunes amb la connexió UDP, una connexió TCP aporta altres elements:
- Ordre de transmissió: diferents comptadors determinen l'ordre d'entrega de la informació.
- Retransmissió dels paquets perduts: qualsevol paquet que no obtingui una resposta de confirmació serà reenviat.
- Eliminació de paquets duplicats.
- Sistema d'eliminació/correcció d'errors.
- Control de flux: limita la velocitat de transmissió entre emissor i receptor.
- Control de congestió de flux
Durant l'etapa de transferència de dades, una sèrie de mecanismes claus determinant la fiabilitat i fortalesa del protocol. Entre ells estan inclosos l'ús del número de seqüència per ordenar els segments TCP rebuts i detectar paquets duplicats, checksums per detectar errors i temporitzadors per detectar pèrdues i endarreriments.
Durant l'establiment de connexió TCP, els números inicials de seqüència són intercanviats entre les dos entitats TCP. Aquests números de seqüència són utilitzats per identificar les dades dintre del flux de bytes, i poder identificar (i comptar) els bytes de les dades de l'aplicació. Sempre hi ha un parell de números de seqüència inclosos en tot segment TCP, referits al número de seqüència i el nombre d'assentaments. Un emissor TCP es refereix el seu propi número de seqüència quan es parla del número de seqüència, mentre que amb el nombre d'assentaments es refereix al número de seqüència del receptor. Per mantenir la fiabilitat, un receptor accepta els segments TCP indicats que ha rebut una part del flux continu de bytes. Una millora de TCP, anomenada assentament selectiu (SACK, Selective Acknowledgement) permet a un receptor TCP sentir les dades que s'han rebut de tal manera que el remitent només retransmet els segments de dades que falten.
A través de l'ús de nombres de seqüència i assentiment, TCP pot passar els segments rebuts en l'ordre correcte dins del flux de bytes a l'aplicació receptora. Els nombres de seqüència són de 32 bits (sense signe), que en torna zero després del següent byte després del 232-1. Una de les claus per mantenir la robustesa i la seguretat de les connexions TCP és la selecció del número|nombre inicial de seqüència (ISN, Initial Sequence Number).
Mida del paquet TCP
La mida del paquet de recepció TCP és la quantitat de dades rebudes (en bytes) que poden cabre en la memòria intermèdia de recepció durant la connexió. L'entitat emissora pot enviar una quantitat determinada de dades però abans ha d'esperar una confirmació amb l'actualització de la mida de la finestra per part del receptor.
Un exemple seria el següent: un receptor comença amb una mida de finestra X i rep Y bytes, llavors, la seva mida de finestra serà (X-Y) i el transmissor només podrà enviar paquets amb una mida màxima de dades de (X-Y) bytes.
Escalat el paquet TCP
Per a una major eficiència en xarxes amb un gran amplada de banda, s'ha d'utilitzar una mida de finestra major. El camp TCP de mida de finestra controla el moviment de dades i està limitat a 16 bits, és a dir, a una mida de finestra de 65.535 bytes. Com el camp de finestra no pot expandir-se s'utilitza un factor d'escalat. L'escala de finestra TCP (TCP window scale) és una opció usada per incrementar la mida màxima de finestra des de 65.535 bytes, a 1 Gigabyte. L'opció d'escala de finestra TCP és usada només durant la negociació en tres passos que constitueixen el començament de la connexió. El valor de l'escala representa el nombre de bits desplaçats a l'esquerre dels 16 bits que formen el camp de la mida de finestra. El valor de l'escala pot anar des de 0 (sense desplaçament) fins a 14. S'ha de recordar que un nombre binari desplaçat un bit a l'esquerra és com si el multipliquéssim en base decimal per 2.
Procés de desconnexió
El final de la connexió pot ser a iniciativa del client o del servidor de manera indiferent.
- 1.- Qui desitja finalitzar la connexió envia a l'altre un datagrama amb els indicadors FIN i ACK.
- 2.- L'altre li retorna un datagrama amb l'indicador ACK. I tot seguit li envia un altre amb els indicadors FIN i ACK.
- 3.- Qui ha iniciat la seqüència de desconnexió envia un datagrama amb l'indicador ACK.
Estructura del datagrama
L'estructura del datagrama TCP és com segueix:[2]
Bit offset | Bits 0–3 | 4–7 | 8–15 | 16–31 | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Port origen | Port destinació | ||||||||||||||||||||||||||||||
32 | Número de seqüència | |||||||||||||||||||||||||||||||
64 | Número d'Acknowledgment | |||||||||||||||||||||||||||||||
96 | Offset dades | Reservat | CWR | ECE | URG | ACK | PSH | RST | SYN | FIN | Finestra | |||||||||||||||||||||
128 | Checksum | Urgent pointer | ||||||||||||||||||||||||||||||
160 | Opcions (opcional) | |||||||||||||||||||||||||||||||
160/192+ | Dades |
- Port d'origen (16 bits): Identifica el port a través del que s'envia.
- Port destí (16 bits): Identifica el port del receptor.
- Número de seqüència (32 bits): Serveix per comprovar que cap segment s'ha perdut, i que arriben en l'ordre correcte. El seu significat varia depenent del valor de SYN:
- Si el flag SYN està actiu (1), llavors aquest camp indica el número inicial de seqüència
- Si el flag SYN no està actiu (0), llavors aquest camp indica el número de seqüència del primer byte de dades.
Ports TCP
TCP utilitza el concepte de número de port per identificar les aplicacions emissores i receptores. Cada costat de la connexió TCP té associat un número de port (de 16 bits sense signe, de manera que hi ha 65.536 ports possibles) assignat per l'aplicació emissora o receptora. Els ports són classificats en tres categories: ben coneguts, registrades i dinàmics / privats. Els ports ben coneguts són assignats per la Internet Assigned Numbers Authority (IANA), van del 0 al 1023 i són usats normalment pel sistema o per processos amb privilegis. Les aplicacions que utilitzen aquest tipus de ports són executades com servidors i es queden a l'escolta de connexions. Alguns exemples són: FTP (21), SSH (22), Telnet (23), SMTP (25) i HTTP (80). Els ports registrats són normalment utilitzats per les aplicacions d'usuari de forma temporal quan connecten amb els servidors, però també poden representar serveis que hagin estat registrats per un tercer (rang de ports registrats: 1024 a 49.151). Els ports dinàmics / privats també poden ser usats per les aplicacions d'usuari, però aquest cas és menys comú. Els ports dinàmics / privats no tenen significat fora de la connexió TCP en què van ser usats (rang de ports dinàmics / privats: 49152 a 65535, recordem que el rang total de 2 elevat a la potència 16, cobreix 65.536 nombres, del 0 al 65535)
Biblioteques de sockets TCP
Es llisten algunes de les biblioteques de comunicacions existents, que utilitzen els protocols TCP i UDP per a diferents sistemes operatius.
- SolarSockets Arxivat 2006-07-20 a Wayback Machine. Biblioteca per C++ Multiplataforma i Mutithread, gratuïta per a projectes lliures. Fàcil d'usar i amb diversos Exemples Arxivat 2007-05-19 a Wayback Machine..
- SDL NET Proporciona funcions i tipus de dada multiplataforma per a programar aplicacions que treballin amb xarxes.
- C++ Sockets Library Aquesta és una biblioteca de classes en C++ sota llicència GPL que 'mapeja' el berkeley sockets C API, i funciona tant en alguns sistemes Unix com en win32.
- GNU Common C++ Arxivat 2007-04-16 a Wayback Machine. Biblioteca de propòsit general que inclou funcions de xarxa.
- HackNet Biblioteca de comunicacions per crear jocs multiintèrpret.
- DirectX - DirectPlay Simplifica l'accés de les aplicacions als serveis de comunicació.
- Winsock Biblioteca de Dll's que implementen TCP/IP sota Windows.
Detecció d'errors
Els números de seqüència juntament amb els missatges de confirmació ACK permet descartar paquets duplicats, retransmetre paquets perduts i ordenar les dades rebudes. Per assegurar que la informació no hagi patit modificacions s'inclou un camp de control anomenat checksum que detecta errors produïts durant la transmissió.
El camp checksum del protocol TCP realitza una detecció feble dels errors produïts segons les normes modernes. Les capes d'enllaç de dades amb altes taxes d'error de bits poden requerir la incorporació de camps extres per la detecció d'errors. El punt feble del cheksum pot ser solucionat usant mètodes addicionals com el CRC o millorar la integritat en la capa 2, per sota dels protocols TCP i IP, com és el cas del PPP o el Ethernet. Això no significa que els 16 bits del cheksum del TCP sigui redundant: sorprenentment, sovint es produeixen errors en els paquets entre la CRC-protegit és freqüent, però la majoria d'aquests errors simples són captats amb el cheksum mitjançant el principi d'extrem a extrem. Un altre problema que té el protocol TCP és la falsa admissió de paquets. Tot i que ja no és gaire habitual, encara pot arribar un paquet amb aquest protocol, ser incorrecte, i que el sistema l'agafi com a correcte. Això es deu al fet que l'errada en el paquet li fa semblar a un paquet correcte, tot i ser-hi incorrecte. Aquest és un problema a evitar, ja que de vegades, si un paquet falla, el fitxer sencer falla.
Exemple de connexió en C
EXEMPLE 1
Client
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#DEFINE PORT 3600
#DEFINE MAXDATASIZE 100
int main(int argc, char *argv[])
{
int fd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struck sockaddr_in server;
if(argc!=2){
printf("Usa:%<Direccion IP>\n", argv[0];
esit(-1);
}
if(he=gethostbyname(argv[1]))==NULL){
printf("error en gethosbyname\n");
exit(-1);
}
if((fd=socket(AF_INET, sock_stream,0)==-|){
printf("error en socket\n";
exit(-1);
}
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr=*((struct in_addr*) he->h_addr);
bzero(&(server.sin_zero),0);
if(connect(fd,(struct sockaddr*)&server,
sizeof(struct sockaddr))==-1){
printf("error en connect\n";
exit(-1);
}
if (numbytes=recv(fd,buf,MAXDATASIZE,0))==-1){
printf("error en recv\n";
exit(-1);
}
buf[numbytes]='0';
printf("Missatge del servidor:%\n",buf);
close(fd);
}
Servidor
#include<stdio.h>
#include<sys/tpes.h>
#incuude<sys/socket.h>
#include<netinet/in.h>
#DEFINE PORT 3500
#DEFINE BACKLOG 2
main()
{
int fd, fd2;
struct sockaddr_in server;
struct sockaddr_in client;
int sn_size;
if ((fd=socket(AF_INET, SOCK_STREAM, 0))==1)
{
printf ("Error en socket\n";
exit(-1);
}
server.sin_family = AF_INET;
server.sin_port=htons(PORT);
server.sinaddrs.s_addr=INADDR_ANY;
bzero(&(server.sin_zero),8);
if (bind(fd,(strut sockaddr *) &server,
sizeof(struct sockaddr))== -1)
{
printf("Error en bind\n";
exit(-1)
}
if (listen(fd, BAKLOG) == -1)
{
printf("Error en listen\n";
exit(-1);
}
while (1)
{
sin_size = sizeof(struct sockaddr_in);
if ((fd2=accept(fd,(struct sockaddr *)&client, &sin_size)) == -1)
{printf ("error en accept \n";
exit(-1);
}
printf("Se conectaron desde %s\n", inet_ntoa(client.sin_addr));
send(fd2, "Benvingut al servidor!\n",26, 0);
close(fd2);
}
}
EXEMPLE 2
Client
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/**********************************************************/
/* funció MAIN */
/* Ordre Parametres: IP desti, port, missatge */
/* */
/**********************************************************/
main(int argc, char *argv[]) {
int s;
struct sockaddr_in bs,des;
char resp[255];
int *sd;
if (argc == 4) {
// Creem el socket
s = socket(AF_INET,SOCK_STREAM,0);
if (s != -1) {
bs.sin_family = AF_INET;
bs.sin_port = htons(0); //Assigna un port disponible de la màquina
bs.sin_addr.s_addr = htonl(INADDR_ANY); //Assigna una IP de la màquina
//Assigna un nom local al socket
if(bind(s,(struct sockaddr*)&bs, sizeof(bs)) != -1) {
//Es prepara el nom de la màquina remota
des.sin_family = AF_INET;
des.sin_addr.s_addr = inet_addr(argv[1]);
des.sin_port = htons(atoi(argv[2]));
//Estableix la connexió amb la màquina remota
connect(s,(struct sockaddr*)&des, sizeof(des));
//Envía el misatge
send(s, argv[3], strlen(argv[3])+1,0);
printf("\n\n->Enviant: %s, a: %s en el port: %s \n",argv[3], argv[1], argv[2]);
//Rep la resposta
recv(s,resp, sizeof(resp),0);
printf("<-Rebut: %s\n",resp);
//Es tanca la connexió (socket)
close(s);
} else {
printf("ERROR al nombrar el socket\n";
}
} else {
printf("ERROR: El socket no s'ha creat correctament!\n";
}
} else {
printf("\n\n\aEl nombre de paràmetres és incorrecte\n\n");
}
}
Servidor
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/**********************************************************/
/* funció MAIN */
/* Ordre Parametres: Port */
/* */
/**********************************************************/
main(int argc, char *argv[]) {
int s;
struct sockaddr_in bs, in;
char entrada[255];
int sd;
if (argc == 2)
{
// Creem el socket
s = socket(AF_INET,SOCK_DGRAM,0);
if (s != -1) {
bs.sin_family = AF_INET;
bs.sin_port = htons(atoi(argv[1])); //Assigna el port específic per la línia d'ordres
bs.sin_addr.s_addr = htonl(INADDR_ANY); //IP qualsevol de la màquina
//Assigna un nom local al socket
if(bind(s,(struct sockaddr*)&bs, sizeof(bs)) != -1) {
printf("\n\aServidor ACTIVO escuchando en el puerto: %s\n",argv[1]);
//El while serveix per servir a múltiples clients
while (1) {
//Rep la cadena del client
if (recvfrom(s,entrada, sizeof(entrada),0,(struct sockaddr*) &in, &sd)== -1)
perror("Error en recvfrom";
//Torna la cadena al client
if (sendto(s,strcat(entrada,"\0",strlen(entrada)+1,0,(struct sockaddr*) &in, sizeof(in)) == -1)
perror("Error en sendto";
}
//Es tanca el socket
close(s);
} else {
printf("ERROR al nombrar el socket\n");
}
} else {
printf("ERROR: El socket no s'ha creat correctament!\n";
}
} else {
printf("\n\n\aEl nombre de paràmetres és incorrecte\n\n");
}
}
Vulnerabilitats
Denegació de servei
Mitjançant l'ús d'una adreça IP falsa i repetir l'enviament de paquets SYN reunits a propòsit, els atacants poden provocar que el servidor consumeixi grans quantitats de recursos i perdre de vista les connexions fraudulentes. Això es coneix com un atac d'inundació SYN. Propostes de solució a aquest problema són les galetes i puzles criptogràfics SYN. Sockstress és un atac similar, contra la qual no es coneix encara la defensa. Un atac de denegació d'avançades relacionades amb l'explotació del temporitzador TCP persisteixen es va analitzar en Phrack #66.
Connexió de segrest
Un atacant que és capaç d'interceptar una sessió TCP i paquets de redirecció pot segrestar una connexió TCP. Per a això, l'atacant aprèn el nombre de seqüència de la comunicació en curs i forja un segment de falsos que s'assembla al següent segment a la riera. Tal segrest simple pot resultar en un paquet que s'accepta per error en un extrem. Quan el host receptor reconeix el segment extra a l'altra banda de la connexió, la sincronització es perd. Segrest podria combinar-se amb ARP o encaminament atacs que permeten prendre el control del flux de paquets, per tal d'obtenir el control permanent dels segrestats connexió TCP.
Fer-se passar per una adreça IP diferent era possible abans de RFC 1948, quan el nombre de seqüència inicial era fàcil d'endevinar. Això va permetre que un atacant enviés cegament una seqüència de paquets que el receptor pensava que venia d'una adreça IP diferent, sense la necessitat de desplegar atacs ARP: és suficient per garantir que l'amfitrió legítim de l'adreça IP és suplantat. O portar a aquesta condició usant denegacions de l'atac. Per això, el nombre de seqüència inicial es tria a l'atzar.
Desenvolupament de TCP
TCP és un protocol molt desenvolupat i complex. No obstant això, mentre millores significatives han estat proposades i dutes a terme al llarg dels anys, ha conservat les operacions més bàsiques sense canvis des del RFC 793, publicat a 1981. El document RFC 1122 (Host Requirements for Internet Hosts), especifica el nombre de requisits d'una implementació del protocol TCP. El RFC 2581 (Control de Congestió TCP) és un dels més importants documents relatius a TCP dels últims anys, descriu nous algoritmes per evitar la congestió excessiva. El 2001, el RFC 3168 va ser escrit per a descriure la notificació de Congestió Explícita (ECN), una forma d'eludir la congestió amb mecanismes de senyalització. Al començament del segle xxi, TCP és utilitzat en el 95% de tots els paquets que circulen per Internet. Entre les aplicacions més comunes que utilitzen TCP hi ha HTTP / HTTPS (World Wide Web), SMTP / POP3 / IMAP (correu electrònic) i FTP (transferència de fitxers). La seva àmplia extensió ha estat la prova per als desenvolupadors originals que la seva creació estava excepcionalment ben feta.
Recentment, un nou algorisme de control de congestió va ser desenvolupat i nomenat com FAST TCP (Fast Active queue management Scalable Transmission Control Protocol) pels científics de Caltech (California Institute of Technology). És similar a TCP Vegas en el fet que tots dos detecten la congestió a partir dels retards en les cues que pateixen els paquets en ser enviats al seu destí. Encara hi ha un debat obert sobre si aquest és un símptoma apropiat per al control de la congestió.
Implementacions
Depuració
Un capturador de paquets (packet sniffer) intercepta el tràfic TCP en un enllaç de xarxa i pot ser molt útil depurant xarxes, implementacions de xarxa i aplicacions que facin servir TCP mostrant a l'usuari els paquets que passen per un enllaç. Algunes implementacions de xarxa suporten l'opció de socket SO_DEBUG, la qual pot ser activada al socket fent servir la crida setsockopt. Aquesta opció aboca tots els paquets, estats TCP i events en aquest socket, la qual cosa és molt útil per depurar. Netstat és una altra utilitat que pot ser útil a l'hora de depurar.
TCP sobre xarxes sense fils
TCP ha estat optimitzada per a les xarxes cablejades. Qualsevol pèrdua de paquets és considerat com el resultat de la congestió i la mida de la finestra de congestió es redueix dramàticament com a mesura de precaució. No obstant això, els enllaços sense fils se sap que experimenten pèrdues esporàdiques. Després de la (errònia) de back-off de la mida de la finestra de congestió, a causa de la pèrdua de paquets sense fils, no hi pot haver una fase de prevenció de congestió amb una disminució en la mida de la finestra conservadora. La investigació extensa ha estat realitzada sobre el tema de com combatre aquests efectes nocius. Les solucions proposades es poden classificar com a solucions d'extrem a extrem (que requereixen modificacions en el client i / o servidor), les solucions de la capa d'enllaç (com ara CDMA2000 en RLT), o les solucions basades en proxy (que requereixen alguns canvis en la xarxa sense necessitat de modificar nodes finals).
Vegeu també
Referències
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.