コード例 #1
0
ファイル: tftp.c プロジェクト: k0059/yard-ice
int tftp_recv_netascii(struct udp_pcb * udp, struct sockaddr_in * sin, 
					   int block, char * buf, int len)
{
	char * line;
	FILE * f;
	int ret;
	int n;
	int i;


	f = null_fopen(NULL);
	DCC_LOG1(LOG_TRACE, "1. f=%p", f);

	i = 0;
	n = 1;
	ret = 0;
	while (i < len) {
		for (line = &buf[i]; i < len; i++) {
			/* CR or [CR, LF] */
			if (buf[i] == '\r') {
				buf[i++] = '\0';
				if (buf[i] == '\n')
					i++;
				break;
			}
			/* LF or [LF, CR] */
			if (buf[i] == '\n') {
				buf[i++] = '\0';
				if (buf[i] == '\r')
					i++;
				break;
			}
		}

		if (i == len)
			buf[i] = '\0';


		if ((ret = exec(f, yard_ice_cmd_tab, line)) < 0) {
			DCC_LOG1(LOG_ERROR, "shell_exec(): %d", ret);
			break;
		}
		n++;
		DCC_LOG1(LOG_TRACE, "line %d", n);
	}

	if (ret < 0) {
		sprintf(buf, "ERROR %d on line %d: %s", -ret, n, target_strerror(ret));
		DCC_LOG1(LOG_TRACE, "2. f=%p", f);
		ret = tftp_error(udp, sin, TFTP_EACCESS, buf);
		DCC_LOG1(LOG_TRACE, "3. f=%p", f);
	} else {
		ret = tftp_ack(udp, block, sin);
	}

	DCC_LOG1(LOG_TRACE, "f=%p", f);
	fclose(f);

	return ret;
}
コード例 #2
0
ファイル: tftp.c プロジェクト: Happy-Ferret/syslinux
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);
}
コード例 #3
0
ファイル: tftp.c プロジェクト: Happy-Ferret/syslinux
/**
 * 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;
}
コード例 #4
0
ファイル: tftp.c プロジェクト: Happy-Ferret/syslinux
/**
 * 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;
}
コード例 #5
0
ファイル: tftp.c プロジェクト: k0059/yard-ice
void __attribute__((noreturn)) tftp_daemon_task(struct debugger * dbg)
{
	uint8_t buf[MAX_TFTP_MSG];
	struct tftphdr * hdr = (struct tftphdr *)buf;
	char * msg = (char *)buf;
	struct sockaddr_in sin;
	struct udp_pcb * udp;
	struct tftp_req req;
	int state = TFTPD_IDLE;
	unsigned int addr_start = 0;
	unsigned int addr_end = 0;
	int block = 0;
	int opc;
	int len;
	int blksize = TFTP_SEGSIZE; 

	DCC_LOG1(LOG_TRACE, "thread: %d", __os_thread_self());

	if ((udp = udp_alloc()) == NULL) {
		DCC_LOG(LOG_WARNING, "udp_alloc() fail!");
		abort();
	}

	if (udp_bind(udp, INADDR_ANY, htons(IPPORT_TFTP)) < 0) {
		DCC_LOG(LOG_WARNING, "udp_bind() fail!");
		abort();
	}

	for (;;) {
		if ((len = udp_recv(udp, buf, MAX_TFTP_MSG, &sin)) < 0) {
			if (len == -ECONNREFUSED) {
				DCC_LOG(LOG_WARNING, "udp_rcv ICMP error: ECONNREFUSED");
			}
			if (len == -EFAULT) {
				DCC_LOG(LOG_WARNING, "udp_rcv error: EFAULT");
			}
			if (len == -ENOTCONN) {
				DCC_LOG(LOG_WARNING, "udp_rcv error: ENOTCONN");
			}
			continue;
		}


		opc = htons(hdr->th_opcode);
		if ((opc != TFTP_RRQ) && (opc != TFTP_WRQ)) {
			DCC_LOG1(LOG_WARNING, "invalid opc: %d", opc);
			WARN("TFTP: invalid opc: %d", opc);
			continue;
		}

		if (udp_connect(udp, sin.sin_addr.s_addr, sin.sin_port) < 0) {
			DCC_LOG(LOG_WARNING, "udp_connect() error");
			WARN("TFTP: UDP connect failed!");
			continue;
		}

		DCC_LOG2(LOG_TRACE, "Connected to: %I.%d", sin.sin_addr.s_addr, 
				 ntohs(sin.sin_port));

//		INF("Connected to: %08x.%d", sin.sin_addr.s_addr, 
//			ntohs(sin.sin_port));

		for (;;) {
			DCC_LOG3(LOG_INFO, "%I.%d %d", 
					 sin.sin_addr.s_addr, ntohs(sin.sin_port), len);

			DCC_LOG2(LOG_INFO, "len=%d, opc=%s", len, tftp_opc[opc]);

			switch (opc) {
			case TFTP_RRQ:
				DCC_LOG(LOG_TRACE, "read request: ...");

				tftp_req_parse((char *)&(hdr->th_stuff), &req);
				blksize = req.blksize;

				INF("TFTP: RRQ '%s' '%s' blksize=%d", req.fname, 
					   tftp_mode[req.mode], req.blksize);

				if (tftp_decode_fname(dbg, req.fname) < 0) {
					ERR("TFTP: bad address.");
					tftp_error(udp, &sin, TFTP_ENOTFOUND, "BAD ADDR.");
					break;
				}

				/* set the transfer info */
				addr_start = dbg->transf.base;
				addr_end = addr_start + dbg->transf.size;
				block = 0;

				DCC_LOG2(LOG_TRACE, "start=0x%08x end=0x%08x", 
						 addr_start, addr_end);

				DBG("TFTP: start=0x%08x end=0x%08x", 
						 addr_start, addr_end);

				if (req.mode == TFTP_NETASCII) {
					state = TFTPD_SEND_NETASCII;
				} else if (req.mode == TFTP_OCTET) {
					state = TFTPD_SEND_OCTET;
				} else {
					tftp_error(udp, &sin, TFTP_EUNDEF, NULL);
					break;
				}

				if (req.opt_len) {
					tftp_oack(udp, &sin, req.opt, req.opt_len);
					break;
				}

				if (req.mode == TFTP_NETASCII)
					goto send_netascii;

				if (req.mode == TFTP_OCTET)
					goto send_octet;

				break;

			case TFTP_WRQ:
				/* Write Request */
				DCC_LOG(LOG_TRACE, "write request...");

				tftp_req_parse((char *)&(hdr->th_stuff), &req);
				blksize = req.blksize;

				INF("WRQ '%s' '%s' blksize=%d", req.fname, 
					   tftp_mode[req.mode], req.blksize);

				if (tftp_decode_fname(dbg, req.fname) < 0) {
					tftp_error(udp, &sin, TFTP_ENOTFOUND, "BAD ADDR.");
					break;
				}

				/* set the transfer info */
				addr_start = dbg->transf.base;
				addr_end = addr_start + dbg->transf.size;
				block = 0;

				DCC_LOG2(LOG_TRACE, "start=0x%08x end=0x%08x", 
						 addr_start, addr_end);

				DBG("TFTP: start=0x%08x end=0x%08x", 
						 addr_start, addr_end);

				if ((req.mode == TFTP_NETASCII) || (req.mode == TFTP_OCTET)) {
					state = (req.mode == TFTP_NETASCII) ? 
						TFTPD_RECV_NETASCII : TFTPD_RECV_OCTET;

					if (req.opt_len) 
						tftp_oack(udp, &sin, req.opt, req.opt_len);
					else
						tftp_ack(udp, block, &sin);

					break;
				} 

				tftp_error(udp, &sin, TFTP_EUNDEF, NULL);
				break;

			case TFTP_ACK:
				block = htons(hdr->th_block);
				DCC_LOG1(LOG_TRACE, "ACK: %d.", block);

				if (state == TFTPD_SEND_NETASCII) {
					unsigned int addr;
					int rem;
					int n;

send_netascii:
					addr = addr_start + (block * 256);
					rem = addr_end - addr;
					if (rem < 0) {
						state = TFTPD_IDLE;
						DCC_LOG1(LOG_TRACE, "eot: %d bytes sent.", 
								 addr_end - addr_start);
						break;
					}

					n = (rem < 256) ? rem : 256;

					DCC_LOG2(LOG_TRACE, "send netascii: addr=0x%08x n=%d", 
							 addr, n);

					/* build the packet */
					len = tftp_hex(addr, hdr->th_data, n);

					goto send_data;
				}

				if (state == TFTPD_SEND_OCTET) {
					unsigned int addr;
					int rem;
					int n;

send_octet:
					addr = addr_start + (block * blksize);
					rem = addr_end - addr;
					if (rem < 0) {
						state = TFTPD_IDLE;
						DCC_LOG1(LOG_TRACE, "eot: %d bytes sent.", 
								 addr_end - addr_start);
						break;
					}
					n = (rem < blksize) ? rem : blksize;

					DCC_LOG2(LOG_TRACE, "send octet: addr=0x%08x n=%d", 
							 addr, n);

					/* build the packet */
					len = target_mem_read(addr, hdr->th_data, n);

					if (len < 0) {
						DCC_LOG(LOG_WARNING, "target memory read error.");
						len = 0;
					}

send_data:
					hdr->th_opcode = htons(TFTP_DATA);
					hdr->th_block = htons(block + 1);

					DCC_LOG2(LOG_TRACE, "block %d: %d bytes.", 
							 block + 1,  len);

					if (udp_sendto(udp, hdr, 
								   sizeof(struct tftphdr) + len, &sin) < 0) {
						DCC_LOG(LOG_WARNING, "udp_sendto() fail");
						state = TFTPD_IDLE;
						break;
					}

					break;
				}

				DCC_LOG(LOG_WARNING, "state invalid!");
				break;

			case TFTP_DATA:
				/* skip the header */
				len -= 4;
				DCC_LOG2(LOG_TRACE, "block=%d len=%d", 
						 htons(hdr->th_block), len);

				DBG("TFTP: DATA block=%d len=%d", htons(hdr->th_block), len);

				if (htons(hdr->th_block) != (block + 1)) {
					/* retransmission, just ack */
					DCC_LOG2(LOG_WARNING, "retransmission, block=%d len=%d", 
							 block, len);
					tftp_ack(udp, block, &sin);
					break;
				}

				if (state == TFTPD_RECV_OCTET) {
					unsigned int addr;
					int n;

					addr = addr_start + (block * blksize);

					block++;

					if (len != blksize) {
						DCC_LOG(LOG_TRACE, "last packet...");
						state = TFTPD_IDLE;
						if (len == 0) {
							tftp_ack(udp, block, &sin);
							break;
						}
					} else {
						DCC_LOG2(LOG_TRACE, "rcvd octet: addr=0x%08x n=%d", 
								 addr, len);
						/* ACK the packet before writing to
						   speed up the transfer, errors are postponed... */
						tftp_ack(udp, block, &sin);
					}

					n = target_mem_write(addr, hdr->th_data, len);

					if (n < len) {
						if (n < 0) {
							DCC_LOG(LOG_ERROR, "target_mem_write()!");
							sprintf(msg, "TARGET WRITE FAIL: %08x", addr);
							WARN("memory write failed at "
								   "addr=0x%08x, code %d", addr, n);
						} else {
							DCC_LOG2(LOG_WARNING, "short writ: "
									 "ret(%d) < len(%d)!", n, len);
							sprintf(msg, "TARGET SHORT WRITE: %08x", 
									addr + n);
						}
						tftp_error(udp, &sin, TFTP_EUNDEF, msg);
						state = TFTPD_RECV_ERROR;
					} else {
						if (n > len) {
							DCC_LOG2(LOG_WARNING, "long write: "
									 "ret(%d) < len(%d)!", n, len);
						}
						if (state == TFTPD_IDLE) {
							/* ack the last packet ... */
							tftp_ack(udp, block, &sin);
						}
					}
					break;
				}

				if (state == TFTPD_RECV_ERROR) {
//					tftp_error(udp, &sin, TFTP_EUNDEF, "TARGET WRITE FAIL.");
					state = TFTPD_IDLE;
					break;
				}

				if (state == TFTPD_RECV_NETASCII) {
					block++;
					if (len != blksize) {
						state = TFTPD_IDLE;
						if (len == 0) {
							tftp_ack(udp, block, &sin);
							break;
						}
					}
					DCC_LOG1(LOG_TRACE, "ASCII recv %d...", len);
					tftp_recv_netascii(udp, &sin, block, 
									   (char *)hdr->th_data, len);
					break;
				}

				tftp_error(udp, &sin, TFTP_EUNDEF, NULL);
				break;

			case TFTP_ERROR:
				DCC_LOG2(LOG_TRACE, "error: %d: %s.", 
						 htons(hdr->th_code), hdr->th_data);
				break;

			}

			if (state == TFTPD_IDLE) {
				DCC_LOG(LOG_TRACE, "[IDLE]");
				break;
			}

			DBG("TFTP: UDP receive ...");

			if ((len = udp_recv_tmo(udp, buf, MAX_TFTP_MSG, &sin, 5000)) < 0) {
				if (len == -ETIMEDOUT) {
					DCC_LOG(LOG_WARNING, "udp_recv_tmo() timeout!");
					WARN("TFTP: UDP receive timeout!");
				} else {
					if (len == -ECONNREFUSED) {
						DCC_LOG(LOG_WARNING, "udp_recv_tmo() lost peer!");
						WARN("TFTP: UDP peer lost!");
					} else {
						DCC_LOG(LOG_WARNING, "udp_recv_tmo() failed!");
						WARN("TFTP: UDP receive failed!");
					}
				}
				/* break the inner loop */
				break;
			}

			opc = htons(hdr->th_opcode);
		}

		/* disconnect */
		DCC_LOG(LOG_TRACE, "disconnecting.");
		udp_connect(udp, INADDR_ANY, 0);
	}
}