10f66f451Sopenharmony_ci/* tftp.c - TFTP client. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com> 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * No Standard. 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ciUSE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN)) 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciconfig TFTP 110f66f451Sopenharmony_ci bool "tftp" 120f66f451Sopenharmony_ci default n 130f66f451Sopenharmony_ci help 140f66f451Sopenharmony_ci usage: tftp [OPTIONS] HOST [PORT] 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ci Transfer file from/to tftp server. 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci -l FILE Local FILE 190f66f451Sopenharmony_ci -r FILE Remote FILE 200f66f451Sopenharmony_ci -g Get file 210f66f451Sopenharmony_ci -p Put file 220f66f451Sopenharmony_ci -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464) 230f66f451Sopenharmony_ci*/ 240f66f451Sopenharmony_ci#define FOR_tftp 250f66f451Sopenharmony_ci#include "toys.h" 260f66f451Sopenharmony_ci 270f66f451Sopenharmony_ciGLOBALS( 280f66f451Sopenharmony_ci char *local_file; 290f66f451Sopenharmony_ci char *remote_file; 300f66f451Sopenharmony_ci long block_size; 310f66f451Sopenharmony_ci 320f66f451Sopenharmony_ci struct sockaddr_storage inaddr; 330f66f451Sopenharmony_ci int af; 340f66f451Sopenharmony_ci) 350f66f451Sopenharmony_ci 360f66f451Sopenharmony_ci#define TFTP_BLKSIZE 512 370f66f451Sopenharmony_ci#define TFTP_RETRIES 3 380f66f451Sopenharmony_ci#define TFTP_DATAHEADERSIZE 4 390f66f451Sopenharmony_ci#define TFTP_MAXPACKETSIZE (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE) 400f66f451Sopenharmony_ci#define TFTP_PACKETSIZE TFTP_MAXPACKETSIZE 410f66f451Sopenharmony_ci#define TFTP_DATASIZE (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE) 420f66f451Sopenharmony_ci#define TFTP_IOBUFSIZE (TFTP_PACKETSIZE+8) 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_ci#define TFTP_OP_RRQ 1 /* Read Request RFC 1350, RFC 2090 */ 450f66f451Sopenharmony_ci#define TFTP_OP_WRQ 2 /* Write Request RFC 1350 */ 460f66f451Sopenharmony_ci#define TFTP_OP_DATA 3 /* Data chunk RFC 1350 */ 470f66f451Sopenharmony_ci#define TFTP_OP_ACK 4 /* Acknowledgement RFC 1350 */ 480f66f451Sopenharmony_ci#define TFTP_OP_ERR 5 /* Error Message RFC 1350 */ 490f66f451Sopenharmony_ci#define TFTP_OP_OACK 6 /* Option acknowledgment RFC 2347 */ 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci#define TFTP_ER_ILLEGALOP 4 /* Illegal TFTP operation */ 520f66f451Sopenharmony_ci#define TFTP_ER_UNKID 5 /* Unknown transfer ID */ 530f66f451Sopenharmony_ci 540f66f451Sopenharmony_ci#define TFTP_ES_NOSUCHFILE "File not found" 550f66f451Sopenharmony_ci#define TFTP_ES_ACCESS "Access violation" 560f66f451Sopenharmony_ci#define TFTP_ES_FULL "Disk full or allocation exceeded" 570f66f451Sopenharmony_ci#define TFTP_ES_ILLEGALOP "Illegal TFTP operation" 580f66f451Sopenharmony_ci#define TFTP_ES_UNKID "Unknown transfer ID" 590f66f451Sopenharmony_ci#define TFTP_ES_EXISTS "File already exists" 600f66f451Sopenharmony_ci#define TFTP_ES_UNKUSER "No such user" 610f66f451Sopenharmony_ci#define TFTP_ES_NEGOTIATE "Terminate transfer due to option negotiation" 620f66f451Sopenharmony_ci 630f66f451Sopenharmony_ci// Initializes SERVER with ADDR and returns socket. 640f66f451Sopenharmony_cistatic int init_tftp(struct sockaddr_storage *server) 650f66f451Sopenharmony_ci{ 660f66f451Sopenharmony_ci struct timeval to = { .tv_sec = 10, //Time out 670f66f451Sopenharmony_ci .tv_usec = 0 }; 680f66f451Sopenharmony_ci const int set = 1; 690f66f451Sopenharmony_ci int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP); 700f66f451Sopenharmony_ci 710f66f451Sopenharmony_ci xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval)); 720f66f451Sopenharmony_ci xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set)); 730f66f451Sopenharmony_ci 740f66f451Sopenharmony_ci if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535); 750f66f451Sopenharmony_ci memset(server, 0, sizeof(struct sockaddr_storage)); 760f66f451Sopenharmony_ci if (TT.af == AF_INET6) { 770f66f451Sopenharmony_ci ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6; 780f66f451Sopenharmony_ci ((struct sockaddr_in6 *)server)->sin6_addr = 790f66f451Sopenharmony_ci ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr; 800f66f451Sopenharmony_ci ((struct sockaddr_in6 *)server)->sin6_port = htons(port); 810f66f451Sopenharmony_ci } 820f66f451Sopenharmony_ci else { 830f66f451Sopenharmony_ci ((struct sockaddr_in *)server)->sin_family = AF_INET; 840f66f451Sopenharmony_ci ((struct sockaddr_in *)server)->sin_addr.s_addr = 850f66f451Sopenharmony_ci ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr; 860f66f451Sopenharmony_ci ((struct sockaddr_in *)server)->sin_port = htons(port); 870f66f451Sopenharmony_ci } 880f66f451Sopenharmony_ci return sd; 890f66f451Sopenharmony_ci} 900f66f451Sopenharmony_ci 910f66f451Sopenharmony_ci/* 920f66f451Sopenharmony_ci * Makes a request packet in BUFFER with OPCODE and file PATH of MODE 930f66f451Sopenharmony_ci * and returns length of packet. 940f66f451Sopenharmony_ci */ 950f66f451Sopenharmony_cistatic int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode) 960f66f451Sopenharmony_ci{ 970f66f451Sopenharmony_ci buffer[0] = opcode >> 8; 980f66f451Sopenharmony_ci buffer[1] = opcode & 0xff; 990f66f451Sopenharmony_ci if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long"); 1000f66f451Sopenharmony_ci return sprintf((char*) &buffer[2], "%s%c%s", path, 0, 1010f66f451Sopenharmony_ci (mode ? "octet" : "netascii")) + 3; 1020f66f451Sopenharmony_ci} 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci/* 1050f66f451Sopenharmony_ci * Makes an acknowledgement packet in BUFFER of BLOCNO 1060f66f451Sopenharmony_ci * and returns packet length. 1070f66f451Sopenharmony_ci */ 1080f66f451Sopenharmony_cistatic int mkpkt_ack(uint8_t *buffer, uint16_t blockno) 1090f66f451Sopenharmony_ci{ 1100f66f451Sopenharmony_ci buffer[0] = TFTP_OP_ACK >> 8; 1110f66f451Sopenharmony_ci buffer[1] = TFTP_OP_ACK & 0xff; 1120f66f451Sopenharmony_ci buffer[2] = blockno >> 8; 1130f66f451Sopenharmony_ci buffer[3] = blockno & 0xff; 1140f66f451Sopenharmony_ci return 4; 1150f66f451Sopenharmony_ci} 1160f66f451Sopenharmony_ci 1170f66f451Sopenharmony_ci/* 1180f66f451Sopenharmony_ci * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG. 1190f66f451Sopenharmony_ci * and returns packet length. 1200f66f451Sopenharmony_ci */ 1210f66f451Sopenharmony_cistatic int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg) 1220f66f451Sopenharmony_ci{ 1230f66f451Sopenharmony_ci buffer[0] = TFTP_OP_ERR >> 8; 1240f66f451Sopenharmony_ci buffer[1] = TFTP_OP_ERR & 0xff; 1250f66f451Sopenharmony_ci buffer[2] = errorcode >> 8; 1260f66f451Sopenharmony_ci buffer[3] = errorcode & 0xff; 1270f66f451Sopenharmony_ci strcpy((char*) &buffer[4], errormsg); 1280f66f451Sopenharmony_ci return strlen(errormsg) + 5; 1290f66f451Sopenharmony_ci} 1300f66f451Sopenharmony_ci 1310f66f451Sopenharmony_ci/* 1320f66f451Sopenharmony_ci * Recieves data from server in BUFF with socket SD and updates FROM 1330f66f451Sopenharmony_ci * and returns read length. 1340f66f451Sopenharmony_ci */ 1350f66f451Sopenharmony_cistatic int read_server(int sd, void *buf, int len, 1360f66f451Sopenharmony_ci struct sockaddr_storage *from) 1370f66f451Sopenharmony_ci{ 1380f66f451Sopenharmony_ci socklen_t alen; 1390f66f451Sopenharmony_ci ssize_t nb; 1400f66f451Sopenharmony_ci 1410f66f451Sopenharmony_ci for (;;) { 1420f66f451Sopenharmony_ci memset(buf, 0, len); 1430f66f451Sopenharmony_ci alen = sizeof(struct sockaddr_storage); 1440f66f451Sopenharmony_ci nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen); 1450f66f451Sopenharmony_ci if (nb < 0) { 1460f66f451Sopenharmony_ci if (errno == EAGAIN) { 1470f66f451Sopenharmony_ci perror_msg("server read timed out"); 1480f66f451Sopenharmony_ci return nb; 1490f66f451Sopenharmony_ci }else if (errno != EINTR) { 1500f66f451Sopenharmony_ci perror_msg("server read failed"); 1510f66f451Sopenharmony_ci return nb; 1520f66f451Sopenharmony_ci } 1530f66f451Sopenharmony_ci }else return nb; 1540f66f451Sopenharmony_ci } 1550f66f451Sopenharmony_ci return nb; 1560f66f451Sopenharmony_ci} 1570f66f451Sopenharmony_ci 1580f66f451Sopenharmony_ci/* 1590f66f451Sopenharmony_ci * sends data to server TO from BUFF of length LEN through socket SD 1600f66f451Sopenharmony_ci * and returns successfully send bytes number. 1610f66f451Sopenharmony_ci */ 1620f66f451Sopenharmony_cistatic ssize_t write_server(int sd, void *buf, size_t len, 1630f66f451Sopenharmony_ci struct sockaddr_storage *to) 1640f66f451Sopenharmony_ci{ 1650f66f451Sopenharmony_ci ssize_t nb; 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_ci for (;;) { 1680f66f451Sopenharmony_ci nb = sendto(sd, buf, len, 0, (struct sockaddr *)to, 1690f66f451Sopenharmony_ci sizeof(struct sockaddr_storage)); 1700f66f451Sopenharmony_ci if (nb < 0) { 1710f66f451Sopenharmony_ci if (errno != EINTR) { 1720f66f451Sopenharmony_ci perror_msg("server write failed"); 1730f66f451Sopenharmony_ci return nb; 1740f66f451Sopenharmony_ci } 1750f66f451Sopenharmony_ci } else return nb; 1760f66f451Sopenharmony_ci } 1770f66f451Sopenharmony_ci return nb; 1780f66f451Sopenharmony_ci} 1790f66f451Sopenharmony_ci 1800f66f451Sopenharmony_ci// checks packet for data and updates block no 1810f66f451Sopenharmony_cistatic inline int check_data( uint8_t *packet, uint16_t *opcode, 1820f66f451Sopenharmony_ci uint16_t *blockno) 1830f66f451Sopenharmony_ci{ 1840f66f451Sopenharmony_ci *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1]; 1850f66f451Sopenharmony_ci if (*opcode == TFTP_OP_DATA) { 1860f66f451Sopenharmony_ci *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3]; 1870f66f451Sopenharmony_ci return 0; 1880f66f451Sopenharmony_ci } 1890f66f451Sopenharmony_ci return -1; 1900f66f451Sopenharmony_ci} 1910f66f451Sopenharmony_ci 1920f66f451Sopenharmony_ci// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO 1930f66f451Sopenharmony_cistatic int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno) 1940f66f451Sopenharmony_ci{ 1950f66f451Sopenharmony_ci off_t tmp; 1960f66f451Sopenharmony_ci int nbytesread; 1970f66f451Sopenharmony_ci 1980f66f451Sopenharmony_ci packet[0] = TFTP_OP_DATA >> 8; 1990f66f451Sopenharmony_ci packet[1] = TFTP_OP_DATA & 0xff; 2000f66f451Sopenharmony_ci packet[2] = blockno >> 8; 2010f66f451Sopenharmony_ci packet[3] = blockno & 0xff; 2020f66f451Sopenharmony_ci tmp = lseek(fd, offset, SEEK_SET); 2030f66f451Sopenharmony_ci if (tmp == (off_t) -1) { 2040f66f451Sopenharmony_ci perror_msg("lseek failed"); 2050f66f451Sopenharmony_ci return -1; 2060f66f451Sopenharmony_ci } 2070f66f451Sopenharmony_ci nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE); 2080f66f451Sopenharmony_ci if (nbytesread < 0) return -1; 2090f66f451Sopenharmony_ci return nbytesread + TFTP_DATAHEADERSIZE; 2100f66f451Sopenharmony_ci} 2110f66f451Sopenharmony_ci 2120f66f451Sopenharmony_ci// Receives ACK responses from server and updates blockno 2130f66f451Sopenharmony_cistatic int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server, 2140f66f451Sopenharmony_ci uint16_t *port, uint16_t *blockno) 2150f66f451Sopenharmony_ci{ 2160f66f451Sopenharmony_ci struct sockaddr_storage from; 2170f66f451Sopenharmony_ci int nbytes; 2180f66f451Sopenharmony_ci uint16_t opcode, rblockno; 2190f66f451Sopenharmony_ci int packetlen, retry; 2200f66f451Sopenharmony_ci 2210f66f451Sopenharmony_ci for (retry = 0; retry < TFTP_RETRIES; retry++) { 2220f66f451Sopenharmony_ci for (;;) { 2230f66f451Sopenharmony_ci nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from); 2240f66f451Sopenharmony_ci if (nbytes < 4) { // Ack headersize = 4 2250f66f451Sopenharmony_ci if (nbytes == 0) error_msg("Connection lost."); 2260f66f451Sopenharmony_ci else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes); 2270f66f451Sopenharmony_ci else error_msg("Server read ACK failure."); 2280f66f451Sopenharmony_ci break; 2290f66f451Sopenharmony_ci } else { 2300f66f451Sopenharmony_ci if (!*port) { 2310f66f451Sopenharmony_ci *port = ((struct sockaddr_in *)&from)->sin_port; 2320f66f451Sopenharmony_ci ((struct sockaddr_in *)server)->sin_port = 2330f66f451Sopenharmony_ci ((struct sockaddr_in *)&from)->sin_port; 2340f66f451Sopenharmony_ci } 2350f66f451Sopenharmony_ci if (((struct sockaddr_in *)server)->sin_addr.s_addr != 2360f66f451Sopenharmony_ci ((struct sockaddr_in *)&from)->sin_addr.s_addr) { 2370f66f451Sopenharmony_ci error_msg("Invalid address in DATA."); 2380f66f451Sopenharmony_ci continue; 2390f66f451Sopenharmony_ci } 2400f66f451Sopenharmony_ci if (*port != ((struct sockaddr_in *)server)->sin_port) { 2410f66f451Sopenharmony_ci error_msg("Invalid port in DATA."); 2420f66f451Sopenharmony_ci packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID); 2430f66f451Sopenharmony_ci (void) write_server(sd, packet, packetlen, server); 2440f66f451Sopenharmony_ci continue; 2450f66f451Sopenharmony_ci } 2460f66f451Sopenharmony_ci opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1]; 2470f66f451Sopenharmony_ci rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3]; 2480f66f451Sopenharmony_ci 2490f66f451Sopenharmony_ci if (opcode != TFTP_OP_ACK) { 2500f66f451Sopenharmony_ci error_msg("Bad opcode."); 2510f66f451Sopenharmony_ci if (opcode > 5) { 2520f66f451Sopenharmony_ci packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP); 2530f66f451Sopenharmony_ci (void) write_server(sd, packet, packetlen, server); 2540f66f451Sopenharmony_ci } 2550f66f451Sopenharmony_ci break; 2560f66f451Sopenharmony_ci } 2570f66f451Sopenharmony_ci if (blockno) *blockno = rblockno; 2580f66f451Sopenharmony_ci return 0; 2590f66f451Sopenharmony_ci } 2600f66f451Sopenharmony_ci } 2610f66f451Sopenharmony_ci } 2620f66f451Sopenharmony_ci error_msg("Timeout, Waiting for ACK."); 2630f66f451Sopenharmony_ci return -1; 2640f66f451Sopenharmony_ci} 2650f66f451Sopenharmony_ci 2660f66f451Sopenharmony_ci// receives file from server. 2670f66f451Sopenharmony_cistatic int file_get(void) 2680f66f451Sopenharmony_ci{ 2690f66f451Sopenharmony_ci struct sockaddr_storage server, from; 2700f66f451Sopenharmony_ci uint8_t *packet; 2710f66f451Sopenharmony_ci uint16_t blockno = 0, opcode, rblockno = 0; 2720f66f451Sopenharmony_ci int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1; 2730f66f451Sopenharmony_ci 2740f66f451Sopenharmony_ci sd = init_tftp(&server); 2750f66f451Sopenharmony_ci 2760f66f451Sopenharmony_ci packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE); 2770f66f451Sopenharmony_ci fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); 2780f66f451Sopenharmony_ci 2790f66f451Sopenharmony_ci len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1); 2800f66f451Sopenharmony_ci ret = write_server(sd, packet, len, &server); 2810f66f451Sopenharmony_ci if (ret != len){ 2820f66f451Sopenharmony_ci unlink(TT.local_file); 2830f66f451Sopenharmony_ci goto errout_with_sd; 2840f66f451Sopenharmony_ci } 2850f66f451Sopenharmony_ci if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0; 2860f66f451Sopenharmony_ci else ((struct sockaddr_in *)&server)->sin_port = 0; 2870f66f451Sopenharmony_ci 2880f66f451Sopenharmony_ci do { 2890f66f451Sopenharmony_ci blockno++; 2900f66f451Sopenharmony_ci for (retry = 0 ; retry < TFTP_RETRIES; retry++) { 2910f66f451Sopenharmony_ci nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from); 2920f66f451Sopenharmony_ci if (nbytesrecvd > 0) { 2930f66f451Sopenharmony_ci if ( ((TT.af == AF_INET) && 2940f66f451Sopenharmony_ci memcmp(&((struct sockaddr_in *)&server)->sin_addr, 2950f66f451Sopenharmony_ci &((struct sockaddr_in *)&from)->sin_addr, 2960f66f451Sopenharmony_ci sizeof(struct in_addr))) || 2970f66f451Sopenharmony_ci ((TT.af == AF_INET6) && 2980f66f451Sopenharmony_ci memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr, 2990f66f451Sopenharmony_ci &((struct sockaddr_in6 *)&from)->sin6_addr, 3000f66f451Sopenharmony_ci sizeof(struct in6_addr)))) { 3010f66f451Sopenharmony_ci error_msg("Invalid address in DATA."); 3020f66f451Sopenharmony_ci retry--; 3030f66f451Sopenharmony_ci continue; 3040f66f451Sopenharmony_ci } 3050f66f451Sopenharmony_ci if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port 3060f66f451Sopenharmony_ci && (((struct sockaddr_in *)&server)->sin_port != 3070f66f451Sopenharmony_ci ((struct sockaddr_in *)&from)->sin_port)) || 3080f66f451Sopenharmony_ci ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port 3090f66f451Sopenharmony_ci && (((struct sockaddr_in6 *)&server)->sin6_port != 3100f66f451Sopenharmony_ci ((struct sockaddr_in6 *)&from)->sin6_port))) { 3110f66f451Sopenharmony_ci error_msg("Invalid port in DATA."); 3120f66f451Sopenharmony_ci len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID); 3130f66f451Sopenharmony_ci ret = write_server(sd, packet, len, &from); 3140f66f451Sopenharmony_ci retry--; 3150f66f451Sopenharmony_ci continue; 3160f66f451Sopenharmony_ci } 3170f66f451Sopenharmony_ci if (nbytesrecvd < TFTP_DATAHEADERSIZE) { 3180f66f451Sopenharmony_ci error_msg("Tiny data packet ignored."); 3190f66f451Sopenharmony_ci continue; 3200f66f451Sopenharmony_ci } 3210f66f451Sopenharmony_ci if (check_data(packet, &opcode, &rblockno) != 0 3220f66f451Sopenharmony_ci || blockno != rblockno) { 3230f66f451Sopenharmony_ci 3240f66f451Sopenharmony_ci if (opcode == TFTP_OP_ERR) { 3250f66f451Sopenharmony_ci char *message = "DATA Check failure."; 3260f66f451Sopenharmony_ci char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS, 3270f66f451Sopenharmony_ci TFTP_ES_FULL, TFTP_ES_ILLEGALOP, 3280f66f451Sopenharmony_ci TFTP_ES_UNKID, TFTP_ES_EXISTS, 3290f66f451Sopenharmony_ci TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE}; 3300f66f451Sopenharmony_ci if (rblockno && (rblockno < 9)) message = arr[rblockno - 1]; 3310f66f451Sopenharmony_ci error_msg_raw(message); 3320f66f451Sopenharmony_ci } 3330f66f451Sopenharmony_ci else if (blockno == 1 && opcode == TFTP_OP_OACK) { 3340f66f451Sopenharmony_ci len = mkpkt_ack(packet, 0); 3350f66f451Sopenharmony_ci ret = write_server(sd, packet, len, &from); 3360f66f451Sopenharmony_ci if (ret != len){ 3370f66f451Sopenharmony_ci unlink(TT.local_file); 3380f66f451Sopenharmony_ci goto errout_with_sd; 3390f66f451Sopenharmony_ci } 3400f66f451Sopenharmony_ci } 3410f66f451Sopenharmony_ci else if (opcode > 5) { 3420f66f451Sopenharmony_ci len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP); 3430f66f451Sopenharmony_ci ret = write_server(sd, packet, len, &from); 3440f66f451Sopenharmony_ci } 3450f66f451Sopenharmony_ci continue; 3460f66f451Sopenharmony_ci } 3470f66f451Sopenharmony_ci if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port) 3480f66f451Sopenharmony_ci ((struct sockaddr_in6 *)&server)->sin6_port = 3490f66f451Sopenharmony_ci ((struct sockaddr_in6 *)&from)->sin6_port; 3500f66f451Sopenharmony_ci else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port) 3510f66f451Sopenharmony_ci ((struct sockaddr_in *)&server)->sin_port = 3520f66f451Sopenharmony_ci ((struct sockaddr_in *)&from)->sin_port; 3530f66f451Sopenharmony_ci break; 3540f66f451Sopenharmony_ci } 3550f66f451Sopenharmony_ci } 3560f66f451Sopenharmony_ci if (retry == TFTP_RETRIES) { 3570f66f451Sopenharmony_ci error_msg("Retry limit exceeded."); 3580f66f451Sopenharmony_ci unlink(TT.local_file); 3590f66f451Sopenharmony_ci goto errout_with_sd; 3600f66f451Sopenharmony_ci } 3610f66f451Sopenharmony_ci ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE; 3620f66f451Sopenharmony_ci if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){ 3630f66f451Sopenharmony_ci unlink(TT.local_file); 3640f66f451Sopenharmony_ci goto errout_with_sd; 3650f66f451Sopenharmony_ci } 3660f66f451Sopenharmony_ci len = mkpkt_ack(packet, blockno); 3670f66f451Sopenharmony_ci ret = write_server(sd, packet, len, &server); 3680f66f451Sopenharmony_ci if (ret != len){ 3690f66f451Sopenharmony_ci unlink(TT.local_file); 3700f66f451Sopenharmony_ci goto errout_with_sd; 3710f66f451Sopenharmony_ci } 3720f66f451Sopenharmony_ci } while (ndatabytes >= TFTP_DATASIZE); 3730f66f451Sopenharmony_ci 3740f66f451Sopenharmony_ci result = 0; 3750f66f451Sopenharmony_ci 3760f66f451Sopenharmony_cierrout_with_sd: xclose(sd); 3770f66f451Sopenharmony_ci free(packet); 3780f66f451Sopenharmony_ci return result; 3790f66f451Sopenharmony_ci} 3800f66f451Sopenharmony_ci 3810f66f451Sopenharmony_ci// Sends file to server. 3820f66f451Sopenharmony_ciint file_put(void) 3830f66f451Sopenharmony_ci{ 3840f66f451Sopenharmony_ci struct sockaddr_storage server; 3850f66f451Sopenharmony_ci uint8_t *packet; 3860f66f451Sopenharmony_ci off_t offset = 0; 3870f66f451Sopenharmony_ci uint16_t blockno = 1, rblockno, port = 0; 3880f66f451Sopenharmony_ci int packetlen, sd, fd, retry = 0, ret, result = -1; 3890f66f451Sopenharmony_ci 3900f66f451Sopenharmony_ci sd = init_tftp(&server); 3910f66f451Sopenharmony_ci packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE); 3920f66f451Sopenharmony_ci fd = xopenro(TT.local_file); 3930f66f451Sopenharmony_ci 3940f66f451Sopenharmony_ci for (;;) { //first loop for request send and confirmation from server. 3950f66f451Sopenharmony_ci packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1); 3960f66f451Sopenharmony_ci ret = write_server(sd, packet, packetlen, &server); 3970f66f451Sopenharmony_ci if (ret != packetlen) goto errout_with_sd; 3980f66f451Sopenharmony_ci if (read_ack(sd, packet, &server, &port, NULL) == 0) break; 3990f66f451Sopenharmony_ci if (++retry > TFTP_RETRIES) { 4000f66f451Sopenharmony_ci error_msg("Retry count exceeded."); 4010f66f451Sopenharmony_ci goto errout_with_sd; 4020f66f451Sopenharmony_ci } 4030f66f451Sopenharmony_ci } 4040f66f451Sopenharmony_ci for (;;) { // loop for data sending and receving ack from server. 4050f66f451Sopenharmony_ci packetlen = mkpkt_data(fd, offset, packet, blockno); 4060f66f451Sopenharmony_ci if (packetlen < 0) goto errout_with_sd; 4070f66f451Sopenharmony_ci 4080f66f451Sopenharmony_ci ret = write_server(sd, packet, packetlen, &server); 4090f66f451Sopenharmony_ci if (ret != packetlen) goto errout_with_sd; 4100f66f451Sopenharmony_ci 4110f66f451Sopenharmony_ci if (read_ack(sd, packet, &server, &port, &rblockno) == 0) { 4120f66f451Sopenharmony_ci if (rblockno == blockno) { 4130f66f451Sopenharmony_ci if (packetlen < TFTP_PACKETSIZE) break; 4140f66f451Sopenharmony_ci blockno++; 4150f66f451Sopenharmony_ci offset += TFTP_DATASIZE; 4160f66f451Sopenharmony_ci retry = 0; 4170f66f451Sopenharmony_ci continue; 4180f66f451Sopenharmony_ci } 4190f66f451Sopenharmony_ci } 4200f66f451Sopenharmony_ci if (++retry > TFTP_RETRIES) { 4210f66f451Sopenharmony_ci error_msg("Retry count exceeded."); 4220f66f451Sopenharmony_ci goto errout_with_sd; 4230f66f451Sopenharmony_ci } 4240f66f451Sopenharmony_ci } 4250f66f451Sopenharmony_ci result = 0; 4260f66f451Sopenharmony_ci 4270f66f451Sopenharmony_cierrout_with_sd: close(sd); 4280f66f451Sopenharmony_ci free(packet); 4290f66f451Sopenharmony_ci return result; 4300f66f451Sopenharmony_ci} 4310f66f451Sopenharmony_ci 4320f66f451Sopenharmony_civoid tftp_main(void) 4330f66f451Sopenharmony_ci{ 4340f66f451Sopenharmony_ci struct addrinfo *info, rp, *res=0; 4350f66f451Sopenharmony_ci int ret; 4360f66f451Sopenharmony_ci 4370f66f451Sopenharmony_ci if (FLAG(r)) { 4380f66f451Sopenharmony_ci if (!FLAG(l)) { 4390f66f451Sopenharmony_ci char *slash = strrchr(TT.remote_file, '/'); 4400f66f451Sopenharmony_ci TT.local_file = (slash) ? slash + 1 : TT.remote_file; 4410f66f451Sopenharmony_ci } 4420f66f451Sopenharmony_ci } else if (FLAG(l)) TT.remote_file = TT.local_file; 4430f66f451Sopenharmony_ci else error_exit("Please provide some files."); 4440f66f451Sopenharmony_ci 4450f66f451Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 4460f66f451Sopenharmony_ci rp.ai_family = AF_UNSPEC; 4470f66f451Sopenharmony_ci rp.ai_socktype = SOCK_STREAM; 4480f66f451Sopenharmony_ci ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info); 4490f66f451Sopenharmony_ci if (!ret) { 4500f66f451Sopenharmony_ci for (res = info; res; res = res->ai_next) 4510f66f451Sopenharmony_ci if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break; 4520f66f451Sopenharmony_ci } 4530f66f451Sopenharmony_ci if (!res) 4540f66f451Sopenharmony_ci error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret)); 4550f66f451Sopenharmony_ci TT.af = info->ai_family; 4560f66f451Sopenharmony_ci 4570f66f451Sopenharmony_ci memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen); 4580f66f451Sopenharmony_ci freeaddrinfo(info); 4590f66f451Sopenharmony_ci 4600f66f451Sopenharmony_ci if (FLAG(g)) file_get(); 4610f66f451Sopenharmony_ci if (FLAG(p)) file_put(); 4620f66f451Sopenharmony_ci} 463