1/* tftpd.c - TFTP server. 2 * 3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321@gmail.com> 5 * 6 * No Standard. 7 8USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN)) 9 10config TFTPD 11 bool "tftpd" 12 default n 13 help 14 usage: tftpd [-cr] [-u USER] [DIR] 15 16 Transfer file from/to tftp server. 17 18 -r read only 19 -c Allow file creation via upload 20 -u run as USER 21 -l Log to syslog (inetd mode requires this) 22*/ 23 24#define FOR_tftpd 25#include "toys.h" 26 27GLOBALS( 28 char *user; 29 30 long sfd; 31 struct passwd *pw; 32) 33 34#define TFTPD_BLKSIZE 512 // as per RFC 1350. 35 36// opcodes 37#define TFTPD_OP_RRQ 1 // Read Request RFC 1350, RFC 2090 38#define TFTPD_OP_WRQ 2 // Write Request RFC 1350 39#define TFTPD_OP_DATA 3 // Data chunk RFC 1350 40#define TFTPD_OP_ACK 4 // Acknowledgement RFC 1350 41#define TFTPD_OP_ERR 5 // Error Message RFC 1350 42#define TFTPD_OP_OACK 6 // Option acknowledgment RFC 2347 43 44// Error Codes: 45#define TFTPD_ER_NOSUCHFILE 1 // File not found 46#define TFTPD_ER_ACCESS 2 // Access violation 47#define TFTPD_ER_FULL 3 // Disk full or allocation exceeded 48#define TFTPD_ER_ILLEGALOP 4 // Illegal TFTP operation 49#define TFTPD_ER_UNKID 5 // Unknown transfer ID 50#define TFTPD_ER_EXISTS 6 // File already exists 51#define TFTPD_ER_UNKUSER 7 // No such user 52#define TFTPD_ER_NEGOTIATE 8 // Terminate transfer due to option negotiation 53 54/* TFTP Packet Formats 55 * Type Op # Format without header 56 * 2 bytes string 1 byte string 1 byte 57 * ----------------------------------------------- 58 * RRQ/ | 01/02 | Filename | 0 | Mode | 0 | 59 * WRQ ----------------------------------------------- 60 * 2 bytes 2 bytes n bytes 61 * --------------------------------- 62 * DATA | 03 | Block # | Data | 63 * --------------------------------- 64 * 2 bytes 2 bytes 65 * ------------------- 66 * ACK | 04 | Block # | 67 * -------------------- 68 * 2 bytes 2 bytes string 1 byte 69 * ---------------------------------------- 70 * ERROR | 05 | ErrorCode | ErrMsg | 0 | 71 * ---------------------------------------- 72 */ 73 74static char *g_errpkt = toybuf + TFTPD_BLKSIZE; 75 76// Create and send error packet. 77static void send_errpkt(struct sockaddr *dstaddr, 78 socklen_t socklen, char *errmsg) 79{ 80 error_msg_raw(errmsg); 81 g_errpkt[1] = TFTPD_OP_ERR; 82 strcpy(g_errpkt + 4, errmsg); 83 if (sendto(TT.sfd, g_errpkt, strlen(errmsg)+5, 0, dstaddr, socklen) < 0) 84 perror_exit("sendto failed"); 85} 86 87// Advance to the next option or value. Returns NULL if there are no 88// more options. 89static char *next_token(char *at, char *end) 90{ 91 if (at == NULL) return NULL; 92 93 for (; at < end; at++) { 94 if (*at == '\0') { 95 at++; 96 break; 97 } 98 } 99 return (at < end) ? at : NULL; 100} 101 102// Used to send / receive packets. 103static void do_action(struct sockaddr *srcaddr, struct sockaddr *dstaddr, 104 socklen_t socklen, char *file, int opcode, int tsize, int blksize) 105{ 106 int fd, done = 0, retry_count = 12, timeout = 100, len; 107 uint16_t blockno = 1, pktopcode, rblockno; 108 char *ptr, *spkt, *rpkt; 109 struct pollfd pollfds[1]; 110 111 spkt = xzalloc(blksize + 4); 112 rpkt = xzalloc(blksize + 4); 113 ptr = spkt+2; //point after opcode. 114 115 pollfds[0].fd = TT.sfd; 116 // initialize groups, setgid and setuid 117 if (TT.pw) xsetuser(TT.pw); 118 119 if (opcode == TFTPD_OP_RRQ) fd = open(file, O_RDONLY, 0666); 120 else fd = open(file, 121 FLAG(c) ? (O_WRONLY|O_TRUNC|O_CREAT) : (O_WRONLY|O_TRUNC), 0666); 122 if (fd < 0) { 123 g_errpkt[3] = TFTPD_ER_NOSUCHFILE; 124 send_errpkt(dstaddr, socklen, "can't open file"); 125 goto CLEAN_APP; 126 } 127 // For download -> blockno will be 1. 128 // 1st ACK will be from dst,which will have blockno-=1 129 // Create and send ACK packet. 130 if (blksize != TFTPD_BLKSIZE || tsize) { 131 pktopcode = TFTPD_OP_OACK; 132 // add "blksize\000blksize_val\000" in send buffer. 133 if (blksize != TFTPD_BLKSIZE) { 134 strcpy(ptr, "blksize"); 135 ptr += strlen("blksize") + 1; 136 ptr += snprintf(ptr, 6, "%d", blksize) + 1; 137 } 138 if (tsize) {// add "tsize\000tsize_val\000" in send buffer. 139 struct stat sb; 140 141 sb.st_size = 0; 142 fstat(fd, &sb); 143 strcpy(ptr, "tsize"); 144 ptr += strlen("tsize") + 1; 145 ptr += sprintf(ptr, "%lu", (unsigned long)sb.st_size)+1; 146 } 147 goto SEND_PKT; 148 } 149 // upload -> ACK 1st packet with filename, as it has blockno 0. 150 if (opcode == TFTPD_OP_WRQ) blockno = 0; 151 152 // Prepare DATA and/or ACK pkt and send it. 153 for (;;) { 154 int poll_ret; 155 156 retry_count = 12, timeout = 100, pktopcode = TFTPD_OP_ACK; 157 ptr = spkt+2; 158 *((uint16_t*)ptr) = htons(blockno); 159 blockno++; 160 ptr += 2; 161 if (opcode == TFTPD_OP_RRQ) { 162 pktopcode = TFTPD_OP_DATA; 163 len = readall(fd, ptr, blksize); 164 if (len < 0) { 165 send_errpkt(dstaddr, socklen, "read-error"); 166 break; 167 } 168 if (len != blksize) done = 1; //last pkt. 169 ptr += len; 170 } 171SEND_PKT: 172 // 1st ACK will be from dst, which will have blockno-=1 173 *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode. 174RETRY_SEND: 175 if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0) 176 perror_exit("sendto failed"); 177 // if "block size < 512", send ACK and exit. 178 if ((pktopcode == TFTPD_OP_ACK) && done) break; 179 180POLL_INPUT: 181 pollfds[0].events = POLLIN; 182 pollfds[0].fd = TT.sfd; 183 poll_ret = poll(pollfds, 1, timeout); 184 if (poll_ret < 0 && (errno == EINTR || errno == ENOMEM)) goto POLL_INPUT; 185 if (!poll_ret) { 186 if (!--retry_count) { 187 error_msg("timeout"); 188 break; 189 } 190 timeout += 150; 191 goto RETRY_SEND; 192 } else if (poll_ret == 1) { 193 len = read(pollfds[0].fd, rpkt, blksize + 4); 194 if (len < 0) { 195 send_errpkt(dstaddr, socklen, "read-error"); 196 break; 197 } 198 if (len < 4) goto POLL_INPUT; 199 } else { 200 perror_msg("poll"); 201 break; 202 } 203 // Validate receive packet. 204 pktopcode = ntohs(((uint16_t*)rpkt)[0]); 205 rblockno = ntohs(((uint16_t*)rpkt)[1]); 206 if (pktopcode == TFTPD_OP_ERR) { 207 char *message = "DATA Check failure."; 208 char *arr[] = {"File not found", "Access violation", 209 "Disk full or allocation exceeded", "Illegal TFTP operation", 210 "Unknown transfer ID", "File already exists", 211 "No such user", "Terminate transfer due to option negotiation"}; 212 213 if (rblockno && (rblockno < 9)) message = arr[rblockno - 1]; 214 error_msg_raw(message); 215 break; // Break the for loop. 216 } 217 218 // if download requested by client, 219 // server will send data pkt and will receive ACK pkt from client. 220 if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) { 221 if (rblockno == (uint16_t) (blockno - 1)) { 222 if (!done) continue; // Send next chunk of data. 223 break; 224 } 225 } 226 227 // server will receive DATA pkt and write the data. 228 if ((opcode == TFTPD_OP_WRQ) && (pktopcode == TFTPD_OP_DATA)) { 229 if (rblockno == blockno) { 230 int nw = writeall(fd, &rpkt[4], len-4); 231 if (nw != len-4) { 232 g_errpkt[3] = TFTPD_ER_FULL; 233 send_errpkt(dstaddr, socklen, "write error"); 234 break; 235 } 236 237 if (nw != blksize) done = 1; 238 } 239 continue; 240 } 241 goto POLL_INPUT; 242 } // end of loop 243 244CLEAN_APP: 245 if (CFG_TOYBOX_FREE) { 246 free(spkt); 247 free(rpkt); 248 close(fd); 249 } 250} 251 252void tftpd_main(void) 253{ 254 int fd = 0, recvmsg_len, opcode, blksize = TFTPD_BLKSIZE, tsize = 0, set =1, bflag = 0; 255 struct sockaddr_storage srcaddr, dstaddr; 256 socklen_t socklen = sizeof(struct sockaddr_storage); 257 char *buf = toybuf; 258 char *end; 259 260 memset(&srcaddr, 0, sizeof(srcaddr)); 261 if (getsockname(0, (struct sockaddr *)&srcaddr, &socklen)) help_exit(0); 262 263 if (TT.user) TT.pw = xgetpwnam(TT.user); 264 if (*toys.optargs) xchroot(*toys.optargs); 265 266 recvmsg_len = recvfrom(fd, toybuf, blksize, 0, (void *)&dstaddr, &socklen); 267 end = toybuf + recvmsg_len; 268 269 TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0); 270 if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set, 271 sizeof(set)) < 0) perror_exit("setsockopt failed"); 272 xbind(TT.sfd, (void *)&srcaddr, socklen); 273 xconnect(TT.sfd, (void *)&dstaddr, socklen); 274 // Error condition. 275 if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) { 276 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error"); 277 return; 278 } 279 280 // request is either upload or Download. 281 opcode = buf[1]; 282 if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ)) 283 || ((opcode == TFTPD_OP_WRQ) && FLAG(r))) { 284 send_errpkt((struct sockaddr*)&dstaddr, socklen, 285 (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error"); 286 return; 287 } 288 289 buf += 2; 290 if (*buf == '.' || strstr(buf, "/.")) { 291 send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename"); 292 return; 293 } 294 295 buf = next_token(buf, end); 296 // As per RFC 1350, mode is case in-sensitive. 297 if (buf == NULL || strcasecmp(buf, "octet")) { 298 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error"); 299 return; 300 } 301 302 //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0" 303 for (buf = next_token(buf, end); buf != NULL; buf = next_token(buf, end)) { 304 char *opt = buf; 305 buf = next_token(buf, end); 306 if (buf == NULL) break; // Missing value. 307 308 if (!bflag && !strcasecmp(opt, "blksize")) { 309 errno = 0; 310 blksize = strtoul(buf, NULL, 10); 311 if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE; 312 bflag ^= 1; 313 } else if (!tsize && !strcasecmp(opt, "tsize")) tsize ^= 1; 314 } 315 316 tsize &= (opcode == TFTPD_OP_RRQ); 317 318 //do send / receive file. 319 do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr, 320 socklen, toybuf + 2, opcode, tsize, blksize); 321 if (CFG_TOYBOX_FREE) close(0); 322} 323