Permalänk

Problem med tftp-klient

Sitter och kodar på en tftp-klient nu men har fått lite problem.
Testar funktionerna för att skicka och ta emot datapaket men när jag skickar en read request till serven så får jag något konstigt tillbaka, jag får rätt opcode och blocknr på paket men inget mer.

Jag skickar alltså en read request med sendto() om att jag vill hämta en fil till tftp-servern.
Jag får tillbaka ett paket med recvfrom() som har opcoden 3 vilket stämmer för datapaket.
Men paket jag får tillbaka är bara på 4 byte.

Jag undrar om felet ligger i att jag kanske tar emot paketet på port 69, som är tftp-porten, vilken bara ska användas till read- och write-request. Kan det vara felet? Har jag missuppfattat en del hur socketen fungerar?

Eller kan det vara så att jag skickar fel read request till servern, fast då borde jag väl få errorpaket tillbaka, det får jag om skickar trams som read request.

Har ni några bra råd att ge. Kanske ser ni om jag glömmer något steg.
Tycker som att det där borde fungera.

Detta är en gammal skoluppift så jag har en skelettkod att jobba vidare på.

I deras skelett gör de följande:

tc->sock = socket(PF_INET, SOCK_DGRAM, 0); tc->peer_addr.sin_family = PF_INET; tc->peer_addr.sin_port = htons(69); struct hostent *hent; hent = gethostbyname(hostname); memcpy(&tc->peer_addr.sin_addr, hent->h_addr_list[0], sizeof(struct in_addr)); tc->addrlen = sizeof(struct sockaddr_in); tc->sock = socket(PF_INET, SOCK_DGRAM, 0); Sedan gör jag detta. char concat[] = ""; ´ strcat(concat,tc->fname); // fname innehåller strängen "name" strcat(concat,"0"); strcat(concat,tc->mode); // mode innehåller strängen "octet" strcat(concat,"0"); int s = TFTP_RRQ_HDR_LEN + TFTP_RRQ_LEN(tc->fname,tc->mode); printf("s = %d\n",s); struct tftp_rrq *rrq = malloc(s); rrq->opcode = htons(1); strcpy(rrq->req, concat); struct tftp_data *td = malloc(512); int send = sendto(tc->sock, rrq, s, 0, (struct sockaddr *)&(tc->peer_addr),tc->addrlen); int recv; recv = recvfrom(tc->sock,td,512,0,(struct sockaddr *)&(tc->peer_addr), &(tc->addrlen)); printf("opcode = %d\n",td->opcode); printf("Blocknr= %d\n",td->blocknr); printf("%s\n",concat); printf("size of revc = %d\n",recv); printf("%d\n",tc->peer_addr.sin_port); structarna rrq och td ser ut så här: struct tftp_rrq { u_int16_t opcode; char req[0]; }; struct tftp_data { u_int16_t opcode; u_int16_t blocknr; char data[0]; };

Permalänk
Medlem

Mycket riktigt så sker dataöverföringen inte på port 69.

Citat:

What's different about TFTP, however, is that the server also selects a pseudo-random TID that it uses for sending responses back to the client; it doesn't send them from port number 69. The reason this is done is that by using unique client port number and source port number, multiple TFTP exchanges can be conducted simultaneously by a server. Each transfer is identified automatically by the source and destination port number, so there is no need to identify in data messages the transfer to which each block data belongs. This keeps the TFTP header size down, allowing more of each UDP message to contain actual data.

For example, suppose the TFTP client selects a TID of 3,145 for its initial message. It would send a UDP transmission from its port 3,145 to the server's port 69. Say the server selects a TID of 1,114. It would send its reply from its port 1,114 to the client's port 3,145. From then on, the client would send messages back to server port 1,114 until the TFTP session was completed.

Visa signatur

Bra, snabbt, billigt; välj två.

Ljud
PC → ODAC/O2 → Sennheiser HD650/Ultrasone PRO 900/...
PC → S.M.S.L SA300 → Bowers & Wilkins 607

Permalänk

Men gör den det i min kod? Skickas verkligen datan till port 69 där? Jag tror inte det?
Eller måste jag byta port på nåt sätt?

Permalänk
Medlem

Din dator skapar en socket med random port, du skickar till serverns port 69, och den svarar till din socket med data från sin random-port, och du sparar undan denna nya address. Såvitt jag kan se så gör du helt rätt.

Men är du dock säker på vad filen du begär innehåller? Är det en tom fil så borde det väl vara korrekt med 0byte data.
Är du säker på att serverimplementationen är korrekt?

Och så lite småsaker:

struct tftp_data *td = malloc(512); ... recv = recvfrom(tc->sock,td,512,0,(struct sockaddr *)&(tc->peer_addr), &(tc->addrlen));

Din buffer borde väl vara minst 516byte (ett data-paket är (enligt RFC1350) 2byte opcode, 2byte block-id, 0-512byte data.)

tc->sock = socket(PF_INET, SOCK_DGRAM, 0); ... tc->sock = socket(PF_INET, SOCK_DGRAM, 0);

Du kör socket() två gånger.

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk

Jo jag vet att det är en korrekt fil jag ska hämta. Det är som sagt en skoluppgift så vi har fått instruktioner om vilken server vi ska testa med och fil vi ska hämta, har även testat med vanliga TFTP i Unix och det fungerar perfekt.

Jag kör inte socket() två gånger, det är jag som har kopierat koden lite klumpigt när jag skulle ta med allt som är relevant för det här.
Jag ska testa att skicka att te emot med en större buffer men jag tvivlar på att det kan ha med saken att göra.

Några annan som har någon ide?

Permalänk
Medlem

strcat(concat,tc->fname); // fname innehåller strängen "name" strcat(concat,"0"); strcat(concat,tc->mode); // mode innehåller strängen "octet" strcat(concat,"0"); int s = TFTP_RRQ_HDR_LEN + TFTP_RRQ_LEN(tc->fname,tc->mode);

Nollorna ska väl vara null-terminatorer? Isf. måste du väl lägga in riktiga nollor och inte ascii nollor.

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk

Kan kanske vara nåt ja, själva utseendet på read requesten är jag väldigt osäker på.
Men hur bygger jag strängen så att den innehåller två null bytes?
strcat() tar ju inte null som ett argument.

Permalänk
Medlem

Om du ändrar:

char concat[] = ""; ´ strcat(concat,tc->fname); // fname innehåller strängen "name" strcat(concat,"0"); strcat(concat,tc->mode); // mode innehåller strängen "octet" strcat(concat,"0"); int s = TFTP_RRQ_HDR_LEN + TFTP_RRQ_LEN(tc->fname,tc->mode); printf("s = %d\n",s); struct tftp_rrq *rrq = malloc(s); rrq->opcode = htons(1); strcpy(rrq->req, concat);

till:

struct tftp_rrq *rrq = malloc(512); rrq->opcode = htons(1); int s=sprintf(rrq->req,"%s%c%s",tc->fname,0,tc->mode)+3; //+1 för att sprintf räknar inte med avslutande null-terminering. +2 för opcode.

Så borde det bli rätt. Dock borde du väl kontrollera returvärdet från malloc, eller ändra:

struct tftp_rrq { u_int16_t opcode; char req[0]; };

till:

struct tftp_rrq { u_int16_t opcode; char req[512]; };

Så slipper du ju malloc helt och hållet..

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk

Tack ska testa detta.
Får dock inte ändra i struct tftp_rrq eftersom den tillhör skelettkoden.

Permalänk

int s=sprintf(rrq->req,"%s%c%s",tc->fname,0,tc->mode)+3; //+1 för att sprintf räknar inte med avslutande null-terminering. +2 för opcode.

Det där får jag inte alls att fungera.

Testade lite med detta i main

char a[] = "test.txt";
char b[] = "octet";

char c[30];

int s=sprintf(c,"%s%c%s",a,0,b)+3;

printf("%s\n",a);
printf("%s\n",b);
printf("%s\n",c);

Men när jag printar c så blir det bara "test.txt".

Permalänk
Medlem

En printning av c som innehåller "test.txt\0octet" (\0 är NULL) ska ge "test.txt".

printf:s tankeprocess vid printf("%s\n",c):

Okej, jag ska skriva ut strängen c följt av en radbrytning. Läs tecken från sträng c. läs tecken, t, skriv ut läs tecken, e, skriv ut läs tecken, s, skriv ut läs tecken, t, skriv ut läs tecken, ., skriv ut läs tecken, t, skriv ut läs tecken, x, skriv ut läs tecken, t, skriv ut läs tecken, NULL, strängen är slut skriv ut radbrytning Färdig.

Det är alltså så nullterminerade strängar fungerar..

Om du vill skriva ut det som kommer efter den första nulltermineringen kan du väl göra nått i stil med:

printf("%s\n",c+strlen(c)+1);

Detta borde väl ge dig "octet", dock inte så intressant iom. att din socket inte bryr sig om saker som strängar och nulltermineringar utan bara kastar ut det antal bytes som du sagt åt den..

Om det är nått som du inte får att fungera så måste du vara lite mer specifik..

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk

Aha jag hade missförstått lite om hur sprintf fungerade.
Testade det i klienten nu men det fungerade inte, verkade inte vara rätt sätt att göra det på heller eftersom jag fick fel opcode tillbaka nu.
Har mailat läraren om detta också men han verkar vara ledig nu

Permalänk
Medlem

[code]
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <netinet/in.h>

#define BUFFER_SIZE 512

void handleMsgError(char* buffer);
void handleMsgData(char* buffer);

int main(int argc,char** argv) {

char buffer[BUFFER_SIZE];
size_t buffer_size=0;

int socket_fd = socket(AF_INET,SOCK_DGRAM,0);

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(69);

//Build a Read Request packet
int16_t opcode = htons(1);
memcpy(buffer,&opcode,2);
buffer_size=3; //opcode (2byte) + nullterminator (1byte)
buffer_size+=sprintf(buffer+2,"%s%c%s","hello.txt",0,"octet");

//Send request
sendto(socket_fd,buffer,buffer_size,0,(struct sockaddr*)&server_addr,sizeof(server_addr));

struct sockaddr_in server2_addr;
socklen_t server2_socklen;
buffer_size=recvfrom(socket_fd,buffer,BUFFER_SIZE,0,(struct sockaddr*)&server2_addr,&server2_socklen);

//Null terminate the packet
buffer[buffer_size]=0;

memcpy(&opcode,buffer,2);
opcode=ntohs(opcode);

switch(opcode) {
case 3:
handleMsgData(buffer+2);
break;
case 5:
handleMsgError(buffer+2);
break;
case 1:
case 2:
case 4:
fprintf(stderr,"Unsupported opcode: %d\n",opcode);
default:
fprintf(stderr,"Illegal opcode: %d\n",opcode);
break;
}

}

void handleMsgError(char* buffer) {
int16_t errcode;
memcpy(&errcode,buffer,2);
errcode=ntohs(errcode);

fprintf(stderr,"Error msg received.

[msg="\"%s\""]\n",errcode,buffer+2); } void handleMsgData(char* buffer) { int16_t block; memcpy(&block,buffer,2); block = ntohs(block); fprintf(stderr,"Data msg received. [block="%d"]",block); printf("%s",buffer+2); }

Ovanstående kod fungerar iaf.

Visa signatur

The difference between stupidity and genius - the latter has limits