Example #1
0
static ssize_t
recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft,
    unsigned short *rtype)
{
	struct iodesc *d = h->iodesc;
	struct tftphdr *t;

	errno = 0;

	len = readudp(d, pkt, len, tleft);

	if (len < 4)
		return (-1);

	t = (struct tftphdr *) pkt;
	*rtype = ntohs(t->th_opcode);
	switch (ntohs(t->th_opcode)) {
	case DATA: {
		int got;

		if (htons(t->th_block) != d->xid) {
			/*
			 * Expected block?
			 */
			return (-1);
		}
		if (d->xid == 1) {
			/*
			 * First data packet from new port.
			 */
			struct udphdr *uh;
			uh = (struct udphdr *) pkt - 1;
			d->destport = uh->uh_sport;
		} /* else check uh_sport has not changed??? */
		got = len - (t->th_data - (char *) t);
		return got;
	}
	case ERROR:
		if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
			printf("illegal tftp error %d\n", ntohs(t->th_code));
			errno = EIO;
		} else {
#ifdef TFTP_DEBUG
			printf("tftp-error %d\n", ntohs(t->th_code));
#endif
			errno = tftperrors[ntohs(t->th_code)];
		}
		return (-1);
	case OACK: {
		struct udphdr *uh;
		int tftp_oack_len;

		/* 
		 * Unexpected OACK. TFTP transfer already in progress. 
		 * Drop the pkt.
		 */
		if (d->xid != 1) {
			return (-1);
		}

		/*
		 * Remember which port this OACK came from, because we need
		 * to send the ACK or errors back to it.
		 */
		uh = (struct udphdr *) pkt - 1;
		d->destport = uh->uh_sport;
		
		/* Parse options ACK-ed by the server. */
		tftp_oack_len = len - sizeof(t->th_opcode);
		if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
			tftp_senderr(h, EOPTNEG, "Malformed OACK");
			errno = EIO;
			return (-1);
		}
		return (0);
	}
	default:
#ifdef TFTP_DEBUG
		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
#endif
		return (-1);
	}
}
Example #2
0
static void tftp_handler(void *ctx, char *packet, unsigned len)
{
	struct file_priv *priv = ctx;
	uint16_t proto;
	uint16_t *s;
	char *pkt = net_eth_to_udp_payload(packet);
	struct udphdr *udp = net_eth_to_udphdr(packet);

	len = net_eth_to_udplen(packet);
	if (len < 2)
		return;

	len -= 2;

	s = (uint16_t *)pkt;
	proto = *s++;
	pkt = (unsigned char *)s;

	debug("%s: proto 0x%04x\n", __func__, proto);

	switch (ntohs(proto)) {
	case TFTP_RRQ:
	case TFTP_WRQ:
	default:
		break;
	case TFTP_ACK:
		if (!priv->push)
			break;

		priv->block = ntohs(*(uint16_t *)pkt);
		if (priv->block != priv->last_block) {
			debug("ack %d != %d\n", priv->block, priv->last_block);
			break;
		}

		priv->block++;

		tftp_timer_reset(priv);

		if (priv->state == STATE_LAST) {
			priv->state = STATE_DONE;
			break;
		}
		priv->tftp_con->udp->uh_dport = udp->uh_sport;
		priv->state = STATE_WDATA;
		break;

	case TFTP_OACK:
		tftp_parse_oack(priv, pkt, len);
		priv->server_port = ntohs(udp->uh_sport);
		priv->tftp_con->udp->uh_dport = udp->uh_sport;

		if (priv->push) {
			/* send first block */
			priv->state = STATE_WDATA;
			priv->block = 1;
		} else {
			/* send ACK */
			priv->state = STATE_OACK;
			priv->block = 0;
			tftp_send(priv);
		}

		break;
	case TFTP_DATA:
		if (len < 2)
			return;
		len -= 2;
		priv->block = ntohs(*(uint16_t *)pkt);

		if (priv->state == STATE_RRQ || priv->state == STATE_OACK) {
			/* first block received */
			priv->state = STATE_RDATA;
			priv->tftp_con->udp->uh_dport = udp->uh_sport;
			priv->server_port = ntohs(udp->uh_sport);
			priv->last_block = 0;

			if (priv->block != 1) {	/* Assertion */
				printf("error: First block is not block 1 (%d)\n",
					priv->block);
				priv->err = -EINVAL;
				priv->state = STATE_DONE;
				break;
			}
		}

		if (priv->block == priv->last_block)
			/* Same block again; ignore it. */
			break;

		priv->last_block = priv->block;

		tftp_timer_reset(priv);

		kfifo_put(priv->fifo, pkt + 2, len);

		if (len < priv->blocksize) {
			tftp_send(priv);
			priv->err = 0;
			priv->state = STATE_DONE;
		}

		break;

	case TFTP_ERROR:
		debug("\nTFTP error: '%s' (%d)\n",
				pkt + 2, ntohs(*(uint16_t *)pkt));
		switch (ntohs(*(uint16_t *)pkt)) {
		case 1:
			priv->err = -ENOENT;
			break;
		case 2:
			priv->err = -EACCES;
			break;
		default:
			priv->err = -EINVAL;
			break;
		}
		priv->state = STATE_DONE;
		break;
	}
}