Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
/**
 * 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);
}
Example #4
0
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;
    }
Example #5
0
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);
}
Example #6
0
/*
 * 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);
}
Example #7
0
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);
}
Example #8
0
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 */
}
Example #9
0
/**
 * 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);
}
Example #10
0
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);
}
Example #11
0
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;
}
Example #12
0
/*
 * 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;
	}
    }
}
Example #13
0
/*
 * 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;
}
Example #14
0
/**
 * 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);
}
Example #15
0
/**
 * 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;
}
Example #16
0
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;
}
Example #17
0
/**
 * 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 */
}
Example #18
0
/*
 * 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);
    }
}
Example #19
0
/*
 * 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;
	}
    }
}
Example #20
0
/**
 * 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;
}
Example #21
0
/**
 * 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;
}
Example #22
0
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;
}