static void fill_inode(struct inode *inode, const struct ext2_inode *e_inode) { inode->mode = IFTODT(e_inode->i_mode); inode->size = e_inode->i_size; inode->atime = e_inode->i_atime; inode->ctime = e_inode->i_ctime; inode->mtime = e_inode->i_mtime; inode->dtime = e_inode->i_dtime; inode->blocks = e_inode->i_blocks; inode->flags = e_inode->i_flags; inode->file_acl = e_inode->i_file_acl; memcpy(PVT(inode)->i_block, e_inode->i_block, sizeof PVT(inode)->i_block); }
void free_socket(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */ free_inode(inode); }
/** * Get a fresh packet from a gPXE socket * @param: inode -> Inode pointer * */ static void gpxe_get_packet(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); static __lowmem struct s_PXENV_FILE_READ file_read; int err; while (1) { file_read.FileHandle = socket->tftp_remoteport; file_read.Buffer = FAR_PTR(packet_buf); file_read.BufferSize = PKTBUF_SIZE; err = pxe_call(PXENV_FILE_READ, &file_read); if (!err) /* successed */ break; if (file_read.Status != PXENV_STATUS_TFTP_OPEN) kaboom(); } memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize); socket->tftp_dataptr = socket->tftp_pktbuf; socket->tftp_bytesleft = file_read.BufferSize; socket->tftp_filepos += file_read.BufferSize; if (socket->tftp_bytesleft == 0) inode->size = socket->tftp_filepos; /* if we're done here, close the file */ if (inode->size > socket->tftp_filepos) return; /* Got EOF, close it */ socket->tftp_goteof = 1; gpxe_close_file(inode); }
static int iso_readdir(struct file *file, struct dirent *dirent) { struct fs_info *fs = file->fs; struct inode *inode = file->inode; const struct iso_dir_entry *de; const char *data = NULL; char *rr_name = NULL; int name_len, ret; while (1) { size_t offset = file->offset & (BLOCK_SIZE(fs) - 1); if (!data) { uint32_t i = file->offset >> BLOCK_SHIFT(fs); if (i >= inode->blocks) return -1; data = get_cache(fs->fs_dev, PVT(inode)->lba + i); } de = (const struct iso_dir_entry *)(data + offset); if (de->length < 33 || offset + de->length > BLOCK_SIZE(fs)) { file->offset = (file->offset + BLOCK_SIZE(fs)) & ~(BLOCK_SIZE(fs) - 1); /* Start of the next block */ data = NULL; continue; } break; }
static void tftp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); if (!socket->tftp_goteof) { tftp_error(inode, 0, "No error, file close"); } core_udp_close(socket); }
/* * 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 fill_buffer(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); if (socket->tftp_bytesleft || socket->tftp_goteof) return; return socket->ops->fill_buffer(inode); }
static void gpxe_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); static __lowmem struct s_PXENV_FILE_CLOSE file_close; file_close.FileHandle = socket->tftp_remoteport; pxe_call(PXENV_FILE_CLOSE, &file_close); }
static int pxe_readdir(struct file *file, struct dirent *dirent) { struct inode *inode = file->inode; struct pxe_pvt_inode *socket = PVT(inode); if (socket->ops->readdir) return socket->ops->readdir(inode, dirent); else return -1; /* No such operation */ }
/** * Send ACK packet. This is a common operation and so is worth canning. * * @param: inode, Inode pointer * @param: ack_num, Packet # to ack (host byte order) * */ static void ack_packet(struct inode *inode, uint16_t ack_num) { static uint16_t ack_packet_buf[2]; struct pxe_pvt_inode *socket = PVT(inode); /* Packet number to ack */ ack_packet_buf[0] = TFTP_ACK; ack_packet_buf[1] = htons(ack_num); core_udp_send(socket, ack_packet_buf, 4); }
static void pxe_close_file(struct file *file) { struct inode *inode = file->inode; struct pxe_pvt_inode *socket = PVT(inode); if (!inode) return; if (!socket->tftp_goteof) { socket->ops->close(inode); } free_socket(inode); }
static int ext2_readlink(struct inode *inode, char *buf) { struct fs_info *fs = inode->fs; int sec_per_block = 1 << (fs->block_shift - fs->sector_shift); bool fast_symlink; if (inode->size > BLOCK_SIZE(fs)) return -1; /* Error! */ fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks; if (fast_symlink) memcpy(buf, PVT(inode)->i_block, inode->size); else cache_get_file(inode, buf, inode->size); return inode->size; }
/* * Find a entry in the specified dir with name _dname_. */ static const struct iso_dir_entry * iso_find_entry(const char *dname, struct inode *inode) { struct fs_info *fs = inode->fs; block_t dir_block = PVT(inode)->lba; int i = 0, offset = 0; const char *de_name; int de_name_len, de_len; const struct iso_dir_entry *de; const char *data = NULL; dprintf("iso_find_entry: \"%s\"\n", dname); while (1) { if (!data) { dprintf("Getting block %d from block %llu\n", i, dir_block); if (++i > inode->blocks) return NULL; /* End of directory */ data = get_cache(fs->fs_dev, dir_block++); offset = 0; } de = (const struct iso_dir_entry *)(data + offset); de_len = de->length; offset += de_len; /* Make sure we have a full directory entry */ if (de_len < 33 || offset > BLOCK_SIZE(fs)) { /* * Zero = end of sector, or corrupt directory entry * * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end * in the Logical Sector in which it begins. */ data = NULL; continue; } de_name_len = de->name_len; de_name = de->name; if (iso_compare_name(de_name, de_name_len, dname)) { dprintf("Found.\n"); return de; } } }
/* * Read a single character from the specified pxe inode. * Very useful for stepping through http streams and * parsing their headers. */ int pxe_getc(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); unsigned char byte; while (!socket->tftp_bytesleft) { if (socket->tftp_goteof) return -1; socket->ops->fill_buffer(inode); } byte = *socket->tftp_dataptr; socket->tftp_bytesleft -= 1; socket->tftp_dataptr += 1; return byte; }
/** * Send an ERROR packet. This is used to terminate a connection. * * @inode: Inode structure * @errnum: Error number (network byte order) * @errstr: Error string (included in packet) */ static void tftp_error(struct inode *inode, uint16_t errnum, const char *errstr) { static struct { uint16_t err_op; uint16_t err_num; char err_msg[64]; } __packed err_buf; int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1); struct pxe_pvt_inode *socket = PVT(inode); err_buf.err_op = TFTP_ERROR; err_buf.err_num = errnum; memcpy(err_buf.err_msg, errstr, len); err_buf.err_msg[len] = '\0'; core_udp_send(socket, &err_buf, 4 + len + 1); }
/** * getfssec: Get multiple clusters from a file, given the starting cluster. * In this case, get multiple blocks from a specific TCP connection. * * @param: fs, the fs_info structure address, in pxe, we don't use this. * @param: buf, buffer to store the read data * @param: openfile, TFTP socket pointer * @param: blocks, 512-byte block count; 0FFFFh = until end of file * * @return: the bytes read * */ static uint32_t pxe_getfssec(struct file *file, char *buf, int blocks, bool *have_more) { struct inode *inode = file->inode; struct pxe_pvt_inode *socket = PVT(inode); int count = blocks; int chunk; int bytes_read = 0; count <<= TFTP_BLOCKSIZE_LG2; while (count) { fill_buffer(inode); /* If we have no 'fresh' buffer, get it */ if (!socket->tftp_bytesleft) break; chunk = count; if (chunk > socket->tftp_bytesleft) chunk = socket->tftp_bytesleft; socket->tftp_bytesleft -= chunk; memcpy(buf, socket->tftp_dataptr, chunk); socket->tftp_dataptr += chunk; buf += chunk; bytes_read += chunk; count -= chunk; } if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) { fill_buffer(inode); *have_more = 1; } else if (socket->tftp_goteof) { /* * The socket is closed and the buffer drained; the caller will * call close_file and therefore free the socket. */ *have_more = 0; } return bytes_read; }
static struct inode *iso_get_inode(struct fs_info *fs, const struct iso_dir_entry *de) { struct inode *inode = new_iso_inode(fs); int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs); if (!inode) return NULL; dprintf("Getting inode for: %.*s\n", de->name_len, de->name); inode->mode = get_inode_mode(de->flags); inode->size = de->size_le; PVT(inode)->lba = de->extent_le; inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs); /* We have a single extent for all data */ inode->next_extent.pstart = (sector_t)de->extent_le << blktosec; inode->next_extent.len = (sector_t)inode->blocks << blktosec; return inode; }
/** * Open a url using gpxe * * @param:inode, the inode to store our state in * @param:url, the url we want to open * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ void gpxe_open(struct inode *inode, const char *url) { static __lowmem struct s_PXENV_FILE_OPEN file_open; static char lowurl[2*FILENAME_MAX]; struct pxe_pvt_inode *socket = PVT(inode); int err; socket->tftp_pktbuf = malloc(PKTBUF_SIZE); if (!socket->tftp_pktbuf) return; snprintf(lowurl, sizeof lowurl, "%s", url); file_open.Status = PXENV_STATUS_BAD_FUNC; file_open.FileName = FAR_PTR(lowurl); err = pxe_call(PXENV_FILE_OPEN, &file_open); if (err) return; socket->fill_buffer = gpxe_get_packet; socket->close = gpxe_close_file; socket->tftp_remoteport = file_open.FileHandle; inode->size = -1; /* This is not an error */ }
/* * 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); } }
/* * Find a entry in the specified dir with name _dname_. */ static const struct iso_dir_entry * iso_find_entry(const char *dname, struct inode *inode) { struct fs_info *fs = inode->fs; block_t dir_block = PVT(inode)->lba; int i = 0, offset = 0; const char *de_name; int de_name_len, de_len, rr_name_len, ret; const struct iso_dir_entry *de; const char *data = NULL; char *rr_name = NULL; dprintf("iso_find_entry: \"%s\"\n", dname); while (1) { if (!data) { dprintf("Getting block %d from block %llu\n", i, dir_block); if (++i > inode->blocks) return NULL; /* End of directory */ data = get_cache(fs->fs_dev, dir_block++); offset = 0; } de = (const struct iso_dir_entry *)(data + offset); de_len = de->length; offset += de_len; /* Make sure we have a full directory entry */ if (de_len < 33 || offset > BLOCK_SIZE(fs)) { /* * Zero = end of sector, or corrupt directory entry * * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end * in the Logical Sector in which it begins. */ data = NULL; continue; } /* Try to get Rock Ridge name */ ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &rr_name_len); if (ret > 0) { if (strcmp(rr_name, dname) == 0) { dprintf("Found (by RR name).\n"); free(rr_name); return de; } free(rr_name); rr_name = NULL; continue; /* Rock Ridge was valid and did not match */ } /* Fall back to ISO name */ de_name_len = de->name_len; de_name = de->name; if (iso_compare_name(de_name, de_name_len, dname)) { dprintf("Found (by ISO name).\n"); return de; } } }
/** * Send a file to a TFTP 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 push * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ __export int tftp_put(struct url_info *url, int flags, struct inode *inode, const char **redir, char *data, int data_length) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; uint16_t buf_len; static const char wrq_tail[] = "octet"; char wrq_packet_buf[512+4+6]; char reply_packet_buf[PKTBUF_SIZE]; int err; int wrq_len; const uint8_t *timeout_ptr; jiffies_t timeout; jiffies_t oldtime; uint16_t opcode; uint16_t src_port = url->port; uint32_t src_ip; uint16_t seq = 0; size_t chunk = 0; int len = data_length; int return_code = -ntohs(TFTP_EUNDEF); (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 (!src_port) src_port = TFTP_PORT; // socket->ops = &tftp_conn_ops; if (core_udp_open(socket)) return return_code; buf = wrq_packet_buf; *(uint16_t *)buf = TFTP_WRQ; /* TFTP opcode */ buf += 2; buf += strlcpy(buf, url->path, 512); buf++; /* Point *past* the final NULL */ memcpy(buf, wrq_tail, sizeof wrq_tail); buf += sizeof wrq_tail; wrq_len = buf - wrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: timeout = *timeout_ptr++; if (!timeout) return return_code; /* No file available... */ oldtime = jiffies(); core_udp_sendto(socket, wrq_packet_buf, wrq_len, url->ip, src_port); /* If the WRITE call fails, we let the timeout take care of it... */ 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_put: 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; /* * Get the opcode type, and parse it */ opcode = *(uint16_t *)reply_packet_buf; switch (opcode) { case TFTP_ERROR: dprintf("tftp_push: received a TFTP_ERROR\n"); struct tftp_error *te = (struct tftp_error *)(reply_packet_buf+1); return_code = -ntohs(te->errcode); inode->size = 0; goto done; /* ERROR reply; don't try again */ case TFTP_ACK: dprintf("tftp_push: received a TFTP_ACK\n"); /* We received a ACK packet, sending the associated data packet */ /* If data was completly sent, we can stop here */ if (len == 0) { return_code = -ntohs(TFTP_OK); goto done; } /* If the server sequence is not aligned with our, we have an issue * Let's break the transmission for now but could be improved later */ uint16_t srv_seq = ntohs(*(uint16_t *)(reply_packet_buf+2)); if (srv_seq != seq) { printf("tftp_push: server sequence (%"PRIu16") is not aligned with our sequence (%"PRIu16"\n", srv_seq, seq); return_code = -ntohs(TFTP_EBADOP); goto done; } /* Let's transmit the data block */ chunk = len >= 512 ? 512 : len; buf = wrq_packet_buf; *(uint16_t *)buf = TFTP_DATA; /* TFTP opcode */ *((uint16_t *)(buf+2)) = htons(++seq); memcpy(buf+4, data, chunk); wrq_len = chunk + 4; data += chunk; len -= chunk; timeout_ptr = TimeoutTable; /* Reset timeout */ goto sendreq; default: dprintf("tftp_push: unknown opcode %d\n", ntohs(opcode)); return_code = -ntohs(TFTP_EOPTNEG); goto err_reply; } err_reply: /* Build the TFTP error packet */ dprintf("tftp_push: Failure\n"); tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error"); inode->size = 0; done: if (!inode->size) core_udp_close(socket); return return_code; }
/** * 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; }
void http_open(struct url_info *url, int flags, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); int header_bytes; const char *next; char field_name[20]; char field_value[1024]; size_t field_name_len, field_value_len; enum state { st_httpver, st_stcode, st_skipline, st_fieldfirst, st_fieldname, st_fieldvalue, st_skip_fieldname, st_skip_fieldvalue, st_eoh, } state; static char location[FILENAME_MAX]; uint32_t content_length; /* same as inode->size */ size_t response_size; int status; int pos; int err; (void)flags; if (!header_buf) return; /* http is broken... */ /* This is a straightforward TCP connection after headers */ socket->ops = &http_conn_ops; /* Reset all of the variables */ inode->size = content_length = -1; /* Start the http connection */ err = core_tcp_open(socket); if (err) return; if (!url->port) url->port = HTTP_PORT; err = core_tcp_connect(socket, url->ip, url->port); if (err) goto fail; strcpy(header_buf, "GET /"); header_bytes = 5; header_bytes += url_escape_unsafe(header_buf+5, url->path, header_len - 5); if (header_bytes >= header_len) goto fail; /* Buffer overflow */ header_bytes += snprintf(header_buf + header_bytes, header_len - header_bytes, " HTTP/1.0\r\n" "Host: %s\r\n" "User-Agent: Syslinux/" VERSION_STR "\r\n" "Connection: close\r\n" "%s" "\r\n", url->host, cookie_buf ? cookie_buf : ""); if (header_bytes >= header_len) goto fail; /* Buffer overflow */ err = core_tcp_write(socket, header_buf, header_bytes, false); if (err) goto fail; /* Parse the HTTP header */ state = st_httpver; pos = 0; status = 0; response_size = 0; field_value_len = 0; field_name_len = 0; while (state != st_eoh) { int ch = pxe_getc(inode); /* Eof before I finish paring the header */ if (ch == -1) goto fail; #if 0 printf("%c", ch); #endif response_size++; if (ch == '\r' || ch == '\0') continue; switch (state) { case st_httpver: if (ch == ' ') { state = st_stcode; pos = 0; } break; case st_stcode: if (ch < '0' || ch > '9') goto fail; status = (status*10) + (ch - '0'); if (++pos == 3) state = st_skipline; break; case st_skipline: if (ch == '\n') state = st_fieldfirst; break; case st_fieldfirst: if (ch == '\n') state = st_eoh; else if (isspace(ch)) { /* A continuation line */ state = st_fieldvalue; goto fieldvalue; } else if (is_token(ch)) { /* Process the previous field before starting on the next one */ if (strcasecmp(field_name, "Content-Length") == 0) { next = field_value; /* Skip leading whitespace */ while (isspace(*next)) next++; content_length = 0; for (;(*next >= '0' && *next <= '9'); next++) { if ((content_length * 10) < content_length) break; content_length = (content_length * 10) + (*next - '0'); } /* In the case of overflow or other error ignore * Content-Length. */ if (*next) content_length = -1; } else if (strcasecmp(field_name, "Location") == 0) { next = field_value; /* Skip leading whitespace */ while (isspace(*next)) next++; strlcpy(location, next, sizeof location); } /* Start the field name and field value afress */ field_name_len = 1; field_name[0] = ch; field_name[1] = '\0'; field_value_len = 0; field_value[0] = '\0'; state = st_fieldname; } else /* Bogus try to recover */ state = st_skipline; break; case st_fieldname: if (ch == ':' ) { state = st_fieldvalue; } else if (is_token(ch)) { if (!append_ch(field_name, sizeof field_name, &field_name_len, ch)) state = st_skip_fieldname; } /* Bogus cases try to recover */ else if (ch == '\n') state = st_fieldfirst; else state = st_skipline; break; case st_fieldvalue: if (ch == '\n') state = st_fieldfirst; else { fieldvalue: if (!append_ch(field_value, sizeof field_value, &field_value_len, ch)) state = st_skip_fieldvalue; } break; /* For valid fields whose names are longer than I choose to support. */ case st_skip_fieldname: if (ch == ':') state = st_skip_fieldvalue; else if (is_token(ch)) state = st_skip_fieldname; /* Bogus cases try to recover */ else if (ch == '\n') state = st_fieldfirst; else state = st_skipline; break; /* For valid fields whose bodies are longer than I choose to support. */ case st_skip_fieldvalue: if (ch == '\n') state = st_fieldfirst; break; case st_eoh: break; /* Should never happen */ } } if (state != st_eoh) status = 0; switch (status) { case 200: /* * All OK, need to mark header data consumed and set up a file * structure... */ /* Treat the remainder of the bytes as data */ socket->tftp_filepos -= response_size; break; case 301: case 302: case 303: case 307: /* A redirect */ if (!location[0]) goto fail; *redir = location; goto fail; default: goto fail; break; } return; fail: inode->size = 0; core_tcp_close_file(inode); return; }