1 #include <minmax.h> 2 #include <net.h> 3 #include "pxe.h" 4 #include "url.h" 5 #include "tftp.h" 6 7 const uint8_t TimeoutTable[] = { 8 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 9 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0 10 }; 11 struct tftp_packet { 12 uint16_t opcode; 13 uint16_t serial; 14 char data[]; 15 }; 16 17 static void tftp_error(struct inode *file, uint16_t errnum, 18 const char *errstr); 19 20 static void tftp_close_file(struct inode *inode) 21 { 22 struct pxe_pvt_inode *socket = PVT(inode); 23 if (!socket->tftp_goteof) { 24 tftp_error(inode, 0, "No error, file close"); 25 } 26 core_udp_close(socket); 27 } 28 29 /** 30 * Send an ERROR packet. This is used to terminate a connection. 31 * 32 * @inode: Inode structure 33 * @errnum: Error number (network byte order) 34 * @errstr: Error string (included in packet) 35 */ 36 static void tftp_error(struct inode *inode, uint16_t errnum, 37 const char *errstr) 38 { 39 static struct { 40 uint16_t err_op; 41 uint16_t err_num; 42 char err_msg[64]; 43 } __packed err_buf; 44 int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1); 45 struct pxe_pvt_inode *socket = PVT(inode); 46 47 err_buf.err_op = TFTP_ERROR; 48 err_buf.err_num = errnum; 49 memcpy(err_buf.err_msg, errstr, len); 50 err_buf.err_msg[len] = '\0'; 51 52 core_udp_send(socket, &err_buf, 4 + len + 1); 53 } 54 55 /** 56 * Send ACK packet. This is a common operation and so is worth canning. 57 * 58 * @param: inode, Inode pointer 59 * @param: ack_num, Packet # to ack (host byte order) 60 * 61 */ 62 static void ack_packet(struct inode *inode, uint16_t ack_num) 63 { 64 static uint16_t ack_packet_buf[2]; 65 struct pxe_pvt_inode *socket = PVT(inode); 66 67 /* Packet number to ack */ 68 ack_packet_buf[0] = TFTP_ACK; 69 ack_packet_buf[1] = htons(ack_num); 70 71 core_udp_send(socket, ack_packet_buf, 4); 72 } 73 74 /* 75 * Get a fresh packet if the buffer is drained, and we haven't hit 76 * EOF yet. The buffer should be filled immediately after draining! 77 */ 78 static void tftp_get_packet(struct inode *inode) 79 { 80 uint16_t last_pkt; 81 const uint8_t *timeout_ptr; 82 uint8_t timeout; 83 uint16_t buffersize; 84 uint16_t serial; 85 jiffies_t oldtime; 86 struct tftp_packet *pkt = NULL; 87 uint16_t buf_len; 88 struct pxe_pvt_inode *socket = PVT(inode); 89 uint16_t src_port; 90 uint32_t src_ip; 91 int err; 92 93 /* 94 * Start by ACKing the previous packet; this should cause 95 * the next packet to be sent. 96 */ 97 timeout_ptr = TimeoutTable; 98 timeout = *timeout_ptr++; 99 oldtime = jiffies(); 100 101 ack_again: 102 ack_packet(inode, socket->tftp_lastpkt); 103 104 while (timeout) { 105 buf_len = socket->tftp_blksize + 4; 106 err = core_udp_recv(socket, socket->tftp_pktbuf, &buf_len, 107 &src_ip, &src_port); 108 if (err) { 109 jiffies_t now = jiffies(); 110 111 if (now-oldtime >= timeout) { 112 oldtime = now; 113 timeout = *timeout_ptr++; 114 if (!timeout) 115 break; 116 goto ack_again; 117 } 118 continue; 119 } 120 121 if (buf_len < 4) /* Bad size for a DATA packet */ 122 continue; 123 124 pkt = (struct tftp_packet *)(socket->tftp_pktbuf); 125 if (pkt->opcode != TFTP_DATA) /* Not a data packet */ 126 continue; 127 128 /* If goes here, recevie OK, break */ 129 break; 130 } 131 132 /* time runs out */ 133 if (timeout == 0) 134 kaboom(); 135 136 last_pkt = socket->tftp_lastpkt; 137 last_pkt++; 138 serial = ntohs(pkt->serial); 139 if (serial != last_pkt) { 140 /* 141 * Wrong packet, ACK the packet and try again. 142 * This is presumably because the ACK got lost, 143 * so the server just resent the previous packet. 144 */ 145 #if 0 146 printf("Wrong packet, wanted %04x, got %04x\n", \ 147 htons(last_pkt), htons(*(uint16_t *)(data+2))); 148 #endif 149 goto ack_again; 150 } 151 152 /* It's the packet we want. We're also EOF if the size < blocksize */ 153 socket->tftp_lastpkt = last_pkt; /* Update last packet number */ 154 buffersize = buf_len - 4; /* Skip TFTP header */ 155 socket->tftp_dataptr = socket->tftp_pktbuf + 4; 156 socket->tftp_filepos += buffersize; 157 socket->tftp_bytesleft = buffersize; 158 if (buffersize < socket->tftp_blksize) { 159 /* it's the last block, ACK packet immediately */ 160 ack_packet(inode, serial); 161 162 /* Make sure we know we are at end of file */ 163 inode->size = socket->tftp_filepos; 164 socket->tftp_goteof = 1; 165 tftp_close_file(inode); 166 } 167 } 168 169 const struct pxe_conn_ops tftp_conn_ops = { 170 .fill_buffer = tftp_get_packet, 171 .close = tftp_close_file, 172 }; 173 174 /** 175 * Open a TFTP connection to the server 176 * 177 * @param:inode, the inode to store our state in 178 * @param:ip, the ip to contact to get the file 179 * @param:filename, the file we wanna open 180 * 181 * @out: open_file_t structure, stores in file->open_file 182 * @out: the lenght of this file, stores in file->file_len 183 * 184 */ 185 void tftp_open(struct url_info *url, int flags, struct inode *inode, 186 const char **redir) 187 { 188 struct pxe_pvt_inode *socket = PVT(inode); 189 char *buf; 190 uint16_t buf_len; 191 char *p; 192 char *options; 193 char *data; 194 static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408"; 195 char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail]; 196 char reply_packet_buf[PKTBUF_SIZE]; 197 int err; 198 int buffersize; 199 int rrq_len; 200 const uint8_t *timeout_ptr; 201 jiffies_t timeout; 202 jiffies_t oldtime; 203 uint16_t opcode; 204 uint16_t blk_num; 205 uint64_t opdata; 206 uint16_t src_port; 207 uint32_t src_ip; 208 209 (void)redir; /* TFTP does not redirect */ 210 (void)flags; 211 212 if (url->type != URL_OLD_TFTP) { 213 /* 214 * The TFTP URL specification allows the TFTP to end with a 215 * ;mode= which we just ignore. 216 */ 217 url_unescape(url->path, ';'); 218 } 219 220 if (!url->port) 221 url->port = TFTP_PORT; 222 223 socket->ops = &tftp_conn_ops; 224 if (core_udp_open(socket)) 225 return; 226 227 buf = rrq_packet_buf; 228 *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ 229 buf += 2; 230 231 buf = stpcpy(buf, url->path); 232 233 buf++; /* Point *past* the final NULL */ 234 memcpy(buf, rrq_tail, sizeof rrq_tail); 235 buf += sizeof rrq_tail; 236 237 rrq_len = buf - rrq_packet_buf; 238 239 timeout_ptr = TimeoutTable; /* Reset timeout */ 240 sendreq: 241 timeout = *timeout_ptr++; 242 if (!timeout) 243 return; /* No file available... */ 244 oldtime = jiffies(); 245 246 core_udp_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port); 247 248 /* If the WRITE call fails, we let the timeout take care of it... */ 249 wait_pkt: 250 for (;;) { 251 buf_len = sizeof(reply_packet_buf); 252 253 err = core_udp_recv(socket, reply_packet_buf, &buf_len, 254 &src_ip, &src_port); 255 if (err) { 256 jiffies_t now = jiffies(); 257 if (now - oldtime >= timeout) 258 goto sendreq; 259 } else { 260 /* Make sure the packet actually came from the server and 261 is long enough for a TFTP opcode */ 262 dprintf("tftp_open: got packet buflen=%d\n", buf_len); 263 if ((src_ip == url->ip) && (buf_len >= 2)) 264 break; 265 } 266 } 267 268 core_udp_disconnect(socket); 269 core_udp_connect(socket, src_ip, src_port); 270 271 /* filesize <- -1 == unknown */ 272 inode->size = -1; 273 socket->tftp_blksize = TFTP_BLOCKSIZE; 274 buffersize = buf_len - 2; /* bytes after opcode */ 275 276 /* 277 * Get the opcode type, and parse it 278 */ 279 opcode = *(uint16_t *)reply_packet_buf; 280 switch (opcode) { 281 case TFTP_ERROR: 282 inode->size = 0; 283 goto done; /* ERROR reply; don't try again */ 284 285 case TFTP_DATA: 286 /* 287 * If the server doesn't support any options, we'll get a 288 * DATA reply instead of OACK. Stash the data in the file 289 * buffer and go with the default value for all options... 290 * 291 * We got a DATA packet, meaning no options are 292 * suported. Save the data away and consider the 293 * length undefined, *unless* this is the only 294 * data packet... 295 */ 296 buffersize -= 2; 297 if (buffersize < 0) 298 goto wait_pkt; 299 data = reply_packet_buf + 2; 300 blk_num = ntohs(*(uint16_t *)data); 301 data += 2; 302 if (blk_num != 1) 303 goto wait_pkt; 304 socket->tftp_lastpkt = blk_num; 305 if (buffersize > TFTP_BLOCKSIZE) 306 goto err_reply; /* Corrupt */ 307 308 socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4); 309 if (!socket->tftp_pktbuf) 310 goto err_reply; /* Internal error */ 311 312 if (buffersize < TFTP_BLOCKSIZE) { 313 /* 314 * This is the final EOF packet, already... 315 * We know the filesize, but we also want to 316 * ack the packet and set the EOF flag. 317 */ 318 inode->size = buffersize; 319 socket->tftp_goteof = 1; 320 ack_packet(inode, blk_num); 321 } 322 323 socket->tftp_bytesleft = buffersize; 324 socket->tftp_dataptr = socket->tftp_pktbuf; 325 memcpy(socket->tftp_pktbuf, data, buffersize); 326 goto done; 327 328 case TFTP_OACK: 329 /* 330 * Now we need to parse the OACK packet to get the transfer 331 * and packet sizes. 332 */ 333 334 options = reply_packet_buf + 2; 335 p = options; 336 337 while (buffersize) { 338 const char *opt = p; 339 340 /* 341 * If we find an option which starts with a NUL byte, 342 * (a null option), we're either seeing garbage that some 343 * TFTP servers add to the end of the packet, or we have 344 * no clue how to parse the rest of the packet (what is 345 * an option name and what is a value?) In either case, 346 * discard the rest. 347 */ 348 if (!*opt) 349 goto done; 350 351 while (buffersize) { 352 if (!*p) 353 break; /* Found a final null */ 354 *p++ |= 0x20; 355 buffersize--; 356 } 357 if (!buffersize) 358 break; /* Unterminated option */ 359 360 /* Consume the terminal null */ 361 p++; 362 buffersize--; 363 364 if (!buffersize) 365 break; /* No option data */ 366 367 opdata = 0; 368 369 /* do convert a number-string to decimal number, just like atoi */ 370 while (buffersize--) { 371 uint8_t d = *p++; 372 if (d == '\0') 373 break; /* found a final null */ 374 d -= '0'; 375 if (d > 9) 376 goto err_reply; /* Not a decimal digit */ 377 opdata = opdata*10 + d; 378 } 379 380 if (!strcmp(opt, "tsize")) 381 inode->size = opdata; 382 else if (!strcmp(opt, "blksize")) 383 socket->tftp_blksize = opdata; 384 else 385 goto err_reply; /* Non-negotitated option returned, 386 no idea what it means ...*/ 387 388 389 } 390 391 if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE) 392 goto err_reply; 393 394 /* Parsing successful, allocate buffer */ 395 socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4); 396 if (!socket->tftp_pktbuf) 397 goto err_reply; 398 else 399 goto done; 400 401 default: 402 printf("TFTP unknown opcode %d\n", ntohs(opcode)); 403 goto err_reply; 404 } 405 406 err_reply: 407 /* Build the TFTP error packet */ 408 tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error"); 409 inode->size = 0; 410 411 done: 412 if (!inode->size) 413 core_udp_close(socket); 414 415 return; 416 } 417