void *listen_for_ack(void* data) { printf("listening thread is started ...\n"); receive_socket = establish_receive_connection(); struct sockaddr_storage their_addr; socklen_t addr_len = sizeof their_addr; while (1) { unsigned char buf[INT_SIZE]; int akc_seq; if ((recvfrom(receive_socket, buf, INT_SIZE, 0, (struct sockaddr *) &their_addr, &addr_len)) == -1) { perror("ack recv"); exit(1); } memcpy(&akc_seq, buf, INT_SIZE); ack_packet(akc_seq); } }
/* * Get a fresh packet if the buffer is drained, and we haven't hit * EOF yet. The buffer should be filled immediately after draining! */ static void tftp_get_packet(struct inode *inode) { uint16_t last_pkt; const uint8_t *timeout_ptr; uint8_t timeout; uint16_t buffersize; uint16_t serial; jiffies_t oldtime; struct tftp_packet *pkt = NULL; uint16_t buf_len; struct pxe_pvt_inode *socket = PVT(inode); uint16_t src_port; uint32_t src_ip; int err; /* * Start by ACKing the previous packet; this should cause * the next packet to be sent. */ timeout_ptr = TimeoutTable; timeout = *timeout_ptr++; oldtime = jiffies(); ack_again: ack_packet(inode, socket->tftp_lastpkt); while (timeout) { buf_len = socket->tftp_blksize + 4; err = core_udp_recv(socket, socket->tftp_pktbuf, &buf_len, &src_ip, &src_port); if (err) { jiffies_t now = jiffies(); if (now-oldtime >= timeout) { oldtime = now; timeout = *timeout_ptr++; if (!timeout) break; goto ack_again; } continue; } if (buf_len < 4) /* Bad size for a DATA packet */ continue; pkt = (struct tftp_packet *)(socket->tftp_pktbuf); if (pkt->opcode != TFTP_DATA) /* Not a data packet */ continue; /* If goes here, recevie OK, break */ break; } /* time runs out */ if (timeout == 0) kaboom(); last_pkt = socket->tftp_lastpkt; last_pkt++; serial = ntohs(pkt->serial); if (serial != last_pkt) { /* * Wrong packet, ACK the packet and try again. * This is presumably because the ACK got lost, * so the server just resent the previous packet. */ #if 0 printf("Wrong packet, wanted %04x, got %04x\n", \ htons(last_pkt), htons(*(uint16_t *)(data+2))); #endif goto ack_again; } /* It's the packet we want. We're also EOF if the size < blocksize */ socket->tftp_lastpkt = last_pkt; /* Update last packet number */ buffersize = buf_len - 4; /* Skip TFTP header */ socket->tftp_dataptr = socket->tftp_pktbuf + 4; socket->tftp_filepos += buffersize; socket->tftp_bytesleft = buffersize; if (buffersize < socket->tftp_blksize) { /* it's the last block, ACK packet immediately */ ack_packet(inode, serial); /* Make sure we know we are at end of file */ inode->size = socket->tftp_filepos; socket->tftp_goteof = 1; tftp_close_file(inode); } }
/** * Open a TFTP connection to the server * * @param:inode, the inode to store our state in * @param:ip, the ip to contact to get the file * @param:filename, the file we wanna open * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ void tftp_open(struct url_info *url, int flags, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; uint16_t buf_len; char *p; char *options; char *data; static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408"; char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail]; char reply_packet_buf[PKTBUF_SIZE]; int err; int buffersize; int rrq_len; const uint8_t *timeout_ptr; jiffies_t timeout; jiffies_t oldtime; uint16_t opcode; uint16_t blk_num; uint64_t opdata; uint16_t src_port; uint32_t src_ip; (void)redir; /* TFTP does not redirect */ (void)flags; if (url->type != URL_OLD_TFTP) { /* * The TFTP URL specification allows the TFTP to end with a * ;mode= which we just ignore. */ url_unescape(url->path, ';'); } if (!url->port) url->port = TFTP_PORT; socket->ops = &tftp_conn_ops; if (core_udp_open(socket)) return; buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; buf = stpcpy(buf, url->path); buf++; /* Point *past* the final NULL */ memcpy(buf, rrq_tail, sizeof rrq_tail); buf += sizeof rrq_tail; rrq_len = buf - rrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: timeout = *timeout_ptr++; if (!timeout) return; /* No file available... */ oldtime = jiffies(); core_udp_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port); /* If the WRITE call fails, we let the timeout take care of it... */ wait_pkt: for (;;) { buf_len = sizeof(reply_packet_buf); err = core_udp_recv(socket, reply_packet_buf, &buf_len, &src_ip, &src_port); if (err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { /* Make sure the packet actually came from the server and is long enough for a TFTP opcode */ dprintf("tftp_open: got packet buflen=%d from server %u.%u.%u.%u(%u.%u.%u.%u)\n", buf_len, ((uint8_t *)&src_ip)[0], ((uint8_t *)&src_ip)[1], ((uint8_t *)&src_ip)[2], ((uint8_t *)&src_ip)[3], ((uint8_t *)&url->ip)[0], ((uint8_t *)&url->ip)[1], ((uint8_t *)&url->ip)[2], ((uint8_t *)&url->ip)[3]); if ((src_ip == url->ip) && (buf_len >= 2)) break; } } core_udp_disconnect(socket); core_udp_connect(socket, src_ip, src_port); /* filesize <- -1 == unknown */ inode->size = -1; socket->tftp_blksize = TFTP_BLOCKSIZE; buffersize = buf_len - 2; /* bytes after opcode */ /* * Get the opcode type, and parse it */ opcode = *(uint16_t *)reply_packet_buf; switch (opcode) { case TFTP_ERROR: inode->size = 0; goto done; /* ERROR reply; don't try again */ case TFTP_DATA: /* * If the server doesn't support any options, we'll get a * DATA reply instead of OACK. Stash the data in the file * buffer and go with the default value for all options... * * We got a DATA packet, meaning no options are * suported. Save the data away and consider the * length undefined, *unless* this is the only * data packet... */ buffersize -= 2; if (buffersize < 0) goto wait_pkt; data = reply_packet_buf + 2; blk_num = ntohs(*(uint16_t *)data); data += 2; if (blk_num != 1) goto wait_pkt; socket->tftp_lastpkt = blk_num; if (buffersize > TFTP_BLOCKSIZE) goto err_reply; /* Corrupt */ socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4); if (!socket->tftp_pktbuf) goto err_reply; /* Internal error */ if (buffersize < TFTP_BLOCKSIZE) { /* * This is the final EOF packet, already... * We know the filesize, but we also want to * ack the packet and set the EOF flag. */ inode->size = buffersize; socket->tftp_goteof = 1; ack_packet(inode, blk_num); } socket->tftp_bytesleft = buffersize; socket->tftp_dataptr = socket->tftp_pktbuf; memcpy(socket->tftp_pktbuf, data, buffersize); goto done; case TFTP_OACK: /* * Now we need to parse the OACK packet to get the transfer * and packet sizes. */ options = reply_packet_buf + 2; p = options; while (buffersize) { const char *opt = p; /* * If we find an option which starts with a NUL byte, * (a null option), we're either seeing garbage that some * TFTP servers add to the end of the packet, or we have * no clue how to parse the rest of the packet (what is * an option name and what is a value?) In either case, * discard the rest. */ if (!*opt) goto done; while (buffersize) { if (!*p) break; /* Found a final null */ *p++ |= 0x20; buffersize--; } if (!buffersize) break; /* Unterminated option */ /* Consume the terminal null */ p++; buffersize--; if (!buffersize) break; /* No option data */ opdata = 0; /* do convert a number-string to decimal number, just like atoi */ while (buffersize--) { uint8_t d = *p++; if (d == '\0') break; /* found a final null */ d -= '0'; if (d > 9) goto err_reply; /* Not a decimal digit */ opdata = opdata*10 + d; } if (!strcmp(opt, "tsize")) inode->size = opdata; else if (!strcmp(opt, "blksize")) socket->tftp_blksize = opdata; else goto err_reply; /* Non-negotitated option returned, no idea what it means ...*/ } if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE) goto err_reply; /* Parsing successful, allocate buffer */ socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4); if (!socket->tftp_pktbuf) goto err_reply; else goto done; default: printf("TFTP unknown opcode %d\n", ntohs(opcode)); goto err_reply; } err_reply: /* Build the TFTP error packet */ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error"); inode->size = 0; done: if (!inode->size) core_udp_close(socket); return; }
int tftp_rcv(char* file, int socketFd) { /* create file with given name */ /* send first request */ /* get tid and change port number as per tid * if it is first run then store it is some variable otherwise compare with it * and it they are not equal then crate error */ /* subsequent packet might be data/error packet * start timer and wait for data packet until timeout * extract data and block number from that * using block number create ack request * send that ack and write data into file * if data packet is less then 512 then it means it is last packet */ /* local variables */ int server_len, opcode, tid = 0; int i, j, n; unsigned short int count = 0, rcount = 0; unsigned char filebuf[DATA_SIZE + 1]; unsigned char packetbuf[DATA_SIZE + 12]; extern int errno; char filename[128], mode[12]; char *bufptr; struct sockaddr_in data; FILE *fp; /* pointer to the file we will be getting */ strcpy (mode, "netascii"); fp = fopen (file, "w"); /* open the file for writing */ if (fp == NULL) { //if the pointer is null then the file can't be opened - Bad perms printf ("Client requested bad file: cannot open for writing (%s)\n", file); return -1; } printf ("Getting file... (destination: %s) \n", file); rw_req_packet(socketFd, READ, file, "netascii"); memset (filebuf, 0, sizeof (filebuf)); n = DATA_SIZE + 4; do { memset (packetbuf, 0, sizeof (packetbuf)); if (n != (DATA_SIZE + 4)) { printf("Last chunk detected (file chunk size: %d). exiting while loop\n",n - 4); printf("Last packet recieved"); ack_packet(socketFd, count, buf); //fclose (fp); printf ("The Client has sent an ACK for packet\n"); goto done; } count++; for (j = 0; j < RTCOUNT; j++) { server_len = sizeof (data); errno = EAGAIN; n = -1; for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++) { n = recvfrom (socketFd, packetbuf, sizeof (packetbuf) - 1, MSG_DONTWAIT, (struct sockaddr *) &data, (socklen_t *) & server_len); usleep (1000); } if (!tid) { tid = ntohs (data.sin_port); server.sin_port = htons (tid); } if (n < 0 && errno == EAGAIN) /* If timeout occurs */ { printf("Client packet got lost"); printf ("Timeout waiting for data"); } else { bufptr = (char *) packetbuf; /*start our pointer going*/ if (bufptr++[0] != 0x00) printf ("bad first nullbyte!\n"); opcode = *bufptr++; rcount = *bufptr++ << 8; rcount &= 0xff00; rcount += (*bufptr++ & 0x00ff); memcpy ((char *) filebuf, bufptr, n - 4); if (opcode != 3) { printf("Badly ordered/invalid data packet (Got OP: %d Block: %d) (Wanted Op: 3 Block: %d)\n", opcode, rcount, count); if (opcode > 5) { error_code = 0x04; //error_packet(socketFd, error_code, err_msg[error_code]); } } else { ack_packet(socketFd, count, buf); break; } //end of else } } if (j == RTCOUNT) { printf ("Data recieve Timeout. Aborting transfer\n"); fclose (fp); return -1; } } while (fwrite (filebuf, 1, n - 4, fp) == n - 4); done: fclose (fp); sync (); printf ("fclose and sync successful. File received successfully\n"); return 0; }
/* Create an ACK packet */ LSP_Packet MessageProcessor::create_ack_packet(LSP_Packet& packet) const { LSP_Packet ack_packet(packet.getConnId(), packet.getSeqNo(), 0, NULL); return ack_packet; }