Exemple #1
0
/*
 * Send an ACK packet (acknowledgement).
 */
int
send_ack(int fp, uint16_t block)
{
	struct tftphdr *tp;
	int size;
	char buf[MAXPKTSIZE];

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);

	DROPPACKETn("send_ack", 0);

	tp = (struct tftphdr *)buf;
	size = sizeof(buf) - 2;
	tp->th_opcode = htons((u_short)ACK);
	tp->th_block = htons((u_short)block);
	size = 4;

	if (sendto(fp, buf, size, 0,
	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
		return (1);
	}

	return (0);
}
Exemple #2
0
/*
 * Send an ERROR packet (error message).
 * Error code passed in is one of the
 * standard TFTP codes, or a UNIX errno
 * offset by 100.
 */
void
send_error(int peer, int error)
{
	struct tftphdr *tp;
	int length;
	struct errmsg *pe;
	char buf[MAXPKTSIZE];

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending ERROR %d", error);

	DROPPACKET("send_error");

	tp = (struct tftphdr *)buf;
	tp->th_opcode = htons((u_short)ERROR);
	tp->th_code = htons((u_short)error);
	for (pe = errmsgs; pe->e_code >= 0; pe++)
		if (pe->e_code == error)
			break;
	if (pe->e_code < 0) {
		pe->e_msg = strerror(error - 100);
		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
	}
	strcpy(tp->th_msg, pe->e_msg);
	length = strlen(pe->e_msg);
	tp->th_msg[length] = '\0';
	length += 5;

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);

	if (sendto(peer, buf, length, 0,
		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
}
Exemple #3
0
static void
tftp_recvfile(int peer, const char *mode)
{
	uint16_t block;
	struct timeval now1, now2;
	struct tftp_stats ts;

	gettimeofday(&now1, NULL);
	if (debug&DEBUG_SIMPLE)
		tftp_log(LOG_DEBUG, "Receiving file");

	write_init(0, file, mode);

	block = 0;
	tftp_receive(peer, &block, &ts, NULL, 0);

	write_close();
	gettimeofday(&now2, NULL);

	if (debug&DEBUG_SIMPLE) {
		double f;
		if (now1.tv_usec > now2.tv_usec) {
			now2.tv_usec += 1000000;
			now2.tv_sec--;
		}

		f = now2.tv_sec - now1.tv_sec +
		    (now2.tv_usec - now1.tv_usec) / 100000.0;
		tftp_log(LOG_INFO,
		    "Download of %jd bytes in %d blocks completed after %0.1f seconds\n",
		    (intmax_t)ts.amount, block, f);
	}

	return;
}
Exemple #4
0
static int
send_packet(int peer, uint16_t block, char *pkt, int size)
{
	int i;
	int t = 1;

	for (i = 0; i < 12 ; i++) {
		DROPPACKETn("send_packet", 0);

		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
		    peer_sock.ss_len) == size) {
			if (i)
				tftp_log(LOG_ERR,
				    "%s block %d, attempt %d successful",
		    		    packettype(ntohs(((struct tftphdr *)
				    (pkt))->th_opcode)), block, i);
			return (0);
		}
		tftp_log(LOG_ERR,
		    "%s block %d, attempt %d failed (Error %d: %s)", 
		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
		    block, i, errno, strerror(errno));
		sleep(t);
		if (t < 32)
			t <<= 1;
	}
	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
	return (1);
}
Exemple #5
0
/*
 * Send an RRQ packet (write request).
 */
int
send_rrq(int peer, char *filename, char *mode)
{
	int n;
	struct tftphdr *tp;
	char *bp;
	char buf[MAXPKTSIZE];
	int size;

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
			filename, mode
		);

	DROPPACKETn("send_rrq", 1);

	tp = (struct tftphdr *)buf;
	tp->th_opcode = htons((u_short)RRQ);
	size = 2;

	bp = tp->th_stuff;
	strcpy(bp, filename);
	bp += strlen(filename);
	*bp = 0;
	bp++;
	size += strlen(filename) + 1;

	strcpy(bp, mode);
	bp += strlen(mode);
	*bp = 0;
	bp++;
	size += strlen(mode) + 1;

	if (options_rfc_enabled) {
		options[OPT_TSIZE].o_request = strdup("0");
		size += make_options(peer, bp, sizeof(buf) - size);
	}

	n = sendto(peer, buf, size, 0,
	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
	if (n != size) {
		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
		return (1);
	}
	return (0);
}
Exemple #6
0
int
read_close(void)
{

	if (fclose(file) != 0) {
		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
		return 1;
	}
	return 0;
}
Exemple #7
0
static void
tftp_xmitfile(int peer, const char *mode)
{
	uint16_t block;
	time_t now;
	struct tftp_stats ts;

	now = time(NULL);
	if (debug&DEBUG_SIMPLE)
		tftp_log(LOG_DEBUG, "Transmitting file");

	read_init(0, file, mode);
	block = 1;
	tftp_send(peer, &block, &ts);
	read_close();
	if (debug&DEBUG_SIMPLE)
		tftp_log(LOG_INFO, "Sent %jd bytes in %jd seconds",
		    (intmax_t)ts.amount, (intmax_t)time(NULL) - now);
}
Exemple #8
0
/*
 * Send an OACK packet (option acknowledgement).
 */
int
send_oack(int peer)
{
	struct tftphdr *tp;
	int size, i, n;
	char *bp;
	char buf[MAXPKTSIZE];

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending OACK");

	DROPPACKETn("send_oack", 0);

	/*
	 * Send back an options acknowledgement (only the ones with
	 * a reply for)
	 */
	tp = (struct tftphdr *)buf;
	bp = buf + 2;
	size = sizeof(buf) - 2;
	tp->th_opcode = htons((u_short)OACK);
	for (i = 0; options[i].o_type != NULL; i++) {
		if (options[i].o_reply != NULL) {
			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
				     0, options[i].o_reply);
			bp += n+1;
			size -= n+1;
			if (size < 0) {
				tftp_log(LOG_ERR, "oack: buffer overflow");
				exit(1);
			}
		}
	}
	size = bp - buf;

	if (sendto(peer, buf, size, 0,
		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
		return (1);
	}

	return (0);
}
Exemple #9
0
static char *
parse_header(int peer, char *recvbuffer, ssize_t size,
	char **filename, char **mode)
{
	char	*cp;
	int	i;
	struct formats *pf;

	*mode = NULL;
	cp = recvbuffer;

	i = get_field(peer, recvbuffer, size);
	if (i >= PATH_MAX) {
		tftp_log(LOG_ERR, "Bad option - filename too long");
		send_error(peer, EBADOP);
		exit(1);
	}
	*filename = recvbuffer;
	tftp_log(LOG_INFO, "Filename: '%s'", *filename);
	cp += i;

	i = get_field(peer, cp, size);
	*mode = cp;
	cp += i;

	/* Find the file transfer mode */
	for (cp = *mode; *cp; cp++)
		if (isupper(*cp))
			*cp = tolower(*cp);
	for (pf = formats; pf->f_mode; pf++)
		if (strcmp(pf->f_mode, *mode) == 0)
			break;
	if (pf->f_mode == NULL) {
		tftp_log(LOG_ERR,
		    "Bad option - Unknown transfer mode (%s)", *mode);
		send_error(peer, EBADOP);
		exit(1);
	}
	tftp_log(LOG_INFO, "Mode: '%s'", *mode);

	return (cp + 1);
}
Exemple #10
0
/*
 * WRQ - receive a file from the client
 */
void
tftp_wrq(int peer, char *recvbuffer, ssize_t size)
{
	char *cp;
	int has_options = 0, ecode;
	char *filename, *mode;
	char fnbuf[PATH_MAX];

	cp = parse_header(peer, recvbuffer, size, &filename, &mode);
	size -= (cp - recvbuffer) + 1;

	strlcpy(fnbuf, filename, sizeof(fnbuf));
	reduce_path(fnbuf);
	filename = fnbuf;

	if (size > 0) {
		if (options_rfc_enabled)
			has_options = !parse_options(peer, cp, size);
		else
			tftp_log(LOG_INFO, "Options found but not enabled");
	}

	ecode = validate_access(peer, &filename, WRQ);
	if (ecode == 0) {
		if (has_options)
			send_oack(peer);
		else
			send_ack(peer, 0);
	}
	if (logging) {
		tftp_log(LOG_INFO, "%s: write request for %s: %s", peername,
			    filename, errtomsg(ecode));
	}

	if (ecode) {
		send_error(peer, ecode);
		exit(1);
	}
	tftp_recvfile(peer, mode);
	exit(0);
}
Exemple #11
0
static size_t
convert_from_net(char *buffer, size_t count)
{
	size_t i, n;

	/*
	 * Convert all CR/LF to LF and all CR,NUL to CR
	 */

	n = 0;
	for (i = 0; i < count; i++) {

		if (gotcr == 0) {
			convbuffer[n++] = buffer[i];
			gotcr = (buffer[i] == '\r');
			continue;
		}

		/* CR, NULL -> CR */
		if (buffer[i] == '\0') {
			gotcr = 0;
			continue;
		}

		/* CR, LF -> LF */
		if (buffer[i] == '\n') {
			if (n == 0) {
				if (ftell(file) != 0) {
					fseek(file, -1, SEEK_END);
					convbuffer[n++] = '\n';
				} else {
					/* This shouldn't happen */
					tftp_log(LOG_ERR,
					    "Received LF as first character");
					abort();
				}
			} else
				convbuffer[n-1] = '\n';
			gotcr = 0;
			continue;
		}

		/* Everything else just accept as is */
		convbuffer[n++] = buffer[i];
		gotcr = (buffer[i] == '\r');
		continue;
	}

	return fwrite(convbuffer, 1, n, file);
}
Exemple #12
0
/* Get a field from a \0 separated string */
ssize_t
get_field(int peer, char *buffer, ssize_t size)
{
	char *cp = buffer;

	while (cp < buffer + size) {
		if (*cp == '\0') break;
		cp++;
	}
	if (*cp != '\0') {
		tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
		send_error(peer, EBADOP);
		exit(1);
	}
	return (cp - buffer + 1);
}
Exemple #13
0
int
write_init(int fd, FILE *f, const char *mode)
{

	if (f == NULL) {
		file = fdopen(fd, "w");
		if (file == NULL) {
			int en = errno;
			tftp_log(LOG_ERR, "fdopen() failed: %s",
			    strerror(errno));
			return en;
		}
	} else
		file = f;
	convert = !strcmp(mode, "netascii");
	return 0;
}
Exemple #14
0
/*
 * Send a DATA packet
 */
int
send_data(int peer, uint16_t block, char *data, int size)
{
	char buf[MAXPKTSIZE];
	struct tftphdr *pkt;
	int n;

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
			block, size);

	DROPPACKETn("send_data", 0);

	pkt = (struct tftphdr *)buf;

	pkt->th_opcode = htons((u_short)DATA);
	pkt->th_block = htons((u_short)block);
	memcpy(pkt->th_data, data, size);

	n = send_packet(peer, block, (char *)pkt, size + 4);
	return (n);
}
Exemple #15
0
int
receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
    int thistimeout)
{
	struct tftphdr *pkt;
	struct sockaddr_storage from_local;
	struct sockaddr_storage *pfrom;
	socklen_t fromlen;
	int n;
	static int waiting;

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG,
		    "Waiting %d seconds for packet", timeoutpacket);

	pkt = (struct tftphdr *)data;

	waiting = 0;
	signal(SIGALRM, timeout);
	setjmp(timeoutbuf);
	alarm(thistimeout);

	if (waiting > 0) {
		alarm(0);
		return (RP_TIMEOUT);
	}

	if (waiting > 0) {
		tftp_log(LOG_ERR, "receive_packet: timeout");
		alarm(0);
		return (RP_TIMEOUT);
	}

	waiting++;
	pfrom = (from == NULL) ? &from_local : from;
	fromlen = sizeof(*pfrom);
	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);

	alarm(0);

	DROPPACKETn("receive_packet", RP_TIMEOUT);

	if (n < 0) {
		tftp_log(LOG_ERR, "receive_packet: timeout");
		return (RP_TIMEOUT);
	}

	alarm(0);

	if (n < 0) {
		/* No idea what could have happened if it isn't a timeout */
		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
		return (RP_RECVFROM);
	}
	if (n < 4) {
		tftp_log(LOG_ERR,
		    "receive_packet: packet too small (%d bytes)", n);
		return (RP_TOOSMALL);
	}

	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
	if (pkt->th_opcode == DATA ||
	    pkt->th_opcode == ACK)
		pkt->th_block = ntohs((u_short)pkt->th_block);

	if (pkt->th_opcode == DATA && n > pktsize) {
		tftp_log(LOG_ERR, "receive_packet: packet too big");
		return (RP_TOOBIG);
	}

	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
		tftp_log(LOG_ERR,
			"receive_packet: received packet from wrong source");
		return (RP_WRONGSOURCE);
	}

	if (pkt->th_opcode == ERROR) {
		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
		    "Got ERROR packet: %s", pkt->th_msg);
		return (RP_ERROR);
	}

	if (debug&DEBUG_PACKETS)
		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
			n, packettype(pkt->th_opcode));

	return n - 4;
}
Exemple #16
0
int
main(int argc, char *argv[])
{
	struct tftphdr *tp;
	int		peer;
	socklen_t	peerlen, len;
	ssize_t		n;
	int		ch;
	char		*chroot_dir = NULL;
	struct passwd	*nobody;
	const char	*chuser = "******";
	char		recvbuffer[MAXPKTSIZE];
	int		allow_ro = 1, allow_wo = 1;

	tzset();			/* syslog in localtime */
	acting_as_client = 0;

	tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
	while ((ch = getopt(argc, argv, "cCd:F:lnoOp:s:u:U:wW")) != -1) {
		switch (ch) {
		case 'c':
			ipchroot = 1;
			break;
		case 'C':
			ipchroot = 2;
			break;
		case 'd':
			if (atoi(optarg) != 0)
				debug += atoi(optarg);
			else
				debug |= debug_finds(optarg);
			break;
		case 'F':
			newfile_format = optarg;
			break;
		case 'l':
			logging = 1;
			break;
		case 'n':
			suppress_naks = 1;
			break;
		case 'o':
			options_rfc_enabled = 0;
			break;
		case 'O':
			options_extra_enabled = 0;
			break;
		case 'p':
			packetdroppercentage = atoi(optarg);
			tftp_log(LOG_INFO,
			    "Randomly dropping %d out of 100 packets",
			    packetdroppercentage);
			break;
		case 's':
			chroot_dir = optarg;
			break;
		case 'u':
			chuser = optarg;
			break;
		case 'U':
			mask = strtol(optarg, NULL, 0);
			break;
		case 'w':
			create_new = 1;
			break;
		case 'W':
			create_new = 1;
			increase_name = 1;
			break;
		default:
			tftp_log(LOG_WARNING,
				"ignoring unknown option -%c", ch);
		}
	}
	if (optind < argc) {
		struct dirlist *dirp;

		/* Get list of directory prefixes. Skip relative pathnames. */
		for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
		     optind++) {
			if (argv[optind][0] == '/') {
				dirp->name = argv[optind];
				dirp->len  = strlen(dirp->name);
				dirp++;
			}
		}
	}
	else if (chroot_dir) {
		dirs->name = "/";
		dirs->len = 1;
	}
	if (ipchroot > 0 && chroot_dir == NULL) {
		tftp_log(LOG_ERR, "-c requires -s");
		exit(1);
	}

	umask(mask);

	{
		int on = 1;
		if (ioctl(0, FIONBIO, &on) < 0) {
			tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno));
			exit(1);
		}
	}

	/* Find out who we are talking to and what we are going to do */
	peerlen = sizeof(peer_sock);
	n = recvfrom(0, recvbuffer, MAXPKTSIZE, 0,
	    (struct sockaddr *)&peer_sock, &peerlen);
	if (n < 0) {
		tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno));
		exit(1);
	}
	getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len,
	    peername, sizeof(peername), NULL, 0, NI_NUMERICHOST);

	/*
	 * Now that we have read the message out of the UDP
	 * socket, we fork and exit.  Thus, inetd will go back
	 * to listening to the tftp port, and the next request
	 * to come in will start up a new instance of tftpd.
	 *
	 * We do this so that inetd can run tftpd in "wait" mode.
	 * The problem with tftpd running in "nowait" mode is that
	 * inetd may get one or more successful "selects" on the
	 * tftp port before we do our receive, so more than one
	 * instance of tftpd may be started up.  Worse, if tftpd
	 * break before doing the above "recvfrom", inetd would
	 * spawn endless instances, clogging the system.
	 */
	{
		int i, pid;

		for (i = 1; i < 20; i++) {
		    pid = fork();
		    if (pid < 0) {
				sleep(i);
				/*
				 * flush out to most recently sent request.
				 *
				 * This may drop some request, but those
				 * will be resent by the clients when
				 * they timeout.  The positive effect of
				 * this flush is to (try to) prevent more
				 * than one tftpd being started up to service
				 * a single request from a single client.
				 */
				peerlen = sizeof peer_sock;
				i = recvfrom(0, recvbuffer, MAXPKTSIZE, 0,
				    (struct sockaddr *)&peer_sock, &peerlen);
				if (i > 0) {
					n = i;
				}
		    } else {
				break;
		    }
		}
		if (pid < 0) {
			tftp_log(LOG_ERR, "fork: %s", strerror(errno));
			exit(1);
		} else if (pid != 0) {
			exit(0);
		}
	}

	/*
	 * See if the client is allowed to talk to me.
	 * (This needs to be done before the chroot())
	 */
	{
		struct request_info req;

		request_init(&req, RQ_CLIENT_ADDR, peername, 0);
		request_set(&req, RQ_DAEMON, "tftpd", 0);

		if (hosts_access(&req) == 0) {
			if (debug&DEBUG_ACCESS)
				tftp_log(LOG_WARNING,
				    "Access denied by 'tftpd' entry "
				    "in /etc/hosts.allow");

			/*
			 * Full access might be disabled, but maybe the
			 * client is allowed to do read-only access.
			 */
			request_set(&req, RQ_DAEMON, "tftpd-ro", 0);
			allow_ro = hosts_access(&req);

			request_set(&req, RQ_DAEMON, "tftpd-wo", 0);
			allow_wo = hosts_access(&req);

			if (allow_ro == 0 && allow_wo == 0) {
				tftp_log(LOG_WARNING,
				    "Unauthorized access from %s", peername);
				exit(1);
			}

			if (debug&DEBUG_ACCESS) {
				if (allow_ro)
					tftp_log(LOG_WARNING,
					    "But allowed readonly access "
					    "via 'tftpd-ro' entry");
				if (allow_wo)
					tftp_log(LOG_WARNING,
					    "But allowed writeonly access "
					    "via 'tftpd-wo' entry");
			}
		} else
			if (debug&DEBUG_ACCESS)
				tftp_log(LOG_WARNING,
				    "Full access allowed"
				    "in /etc/hosts.allow");
	}

	/*
	 * Since we exit here, we should do that only after the above
	 * recvfrom to keep inetd from constantly forking should there
	 * be a problem.  See the above comment about system clogging.
	 */
	if (chroot_dir) {
		if (ipchroot > 0) {
			char *tempchroot;
			struct stat sb;
			int statret;
			struct sockaddr_storage ss;
			char hbuf[NI_MAXHOST];

			statret = -1;
			memcpy(&ss, &peer_sock, peer_sock.ss_len);
			unmappedaddr((struct sockaddr_in6 *)&ss);
			getnameinfo((struct sockaddr *)&ss, ss.ss_len,
				    hbuf, sizeof(hbuf), NULL, 0,
				    NI_NUMERICHOST);
			asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf);
			if (ipchroot == 2)
				statret = stat(tempchroot, &sb);
			if (ipchroot == 1 ||
			    (statret == 0 && (sb.st_mode & S_IFDIR)))
				chroot_dir = tempchroot;
		}
		/* Must get this before chroot because /etc might go away */
		if ((nobody = getpwnam(chuser)) == NULL) {
			tftp_log(LOG_ERR, "%s: no such user", chuser);
			exit(1);
		}
		if (chroot(chroot_dir)) {
			tftp_log(LOG_ERR, "chroot: %s: %s",
			    chroot_dir, strerror(errno));
			exit(1);
		}
		chdir("/");
		setgroups(1, &nobody->pw_gid);
		if (setuid(nobody->pw_uid) != 0) {
			tftp_log(LOG_ERR, "setuid failed");
			exit(1);
		}
	}

	len = sizeof(me_sock);
	if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) {
		switch (me_sock.ss_family) {
		case AF_INET:
			((struct sockaddr_in *)&me_sock)->sin_port = 0;
			break;
		case AF_INET6:
			((struct sockaddr_in6 *)&me_sock)->sin6_port = 0;
			break;
		default:
			/* unsupported */
			break;
		}
	} else {
		memset(&me_sock, 0, sizeof(me_sock));
		me_sock.ss_family = peer_sock.ss_family;
		me_sock.ss_len = peer_sock.ss_len;
	}
	close(0);
	close(1);
	peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0);
	if (peer < 0) {
		tftp_log(LOG_ERR, "socket: %s", strerror(errno));
		exit(1);
	}
	if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) {
		tftp_log(LOG_ERR, "bind: %s", strerror(errno));
		exit(1);
	}

	tp = (struct tftphdr *)recvbuffer;
	tp->th_opcode = ntohs(tp->th_opcode);
	if (tp->th_opcode == RRQ) {
		if (allow_ro)
			tftp_rrq(peer, tp->th_stuff, n - 1);
		else {
			tftp_log(LOG_WARNING,
			    "%s read access denied", peername);
			exit(1);
		}
	}
	if (tp->th_opcode == WRQ) {
		if (allow_wo)
			tftp_wrq(peer, tp->th_stuff, n - 1);
		else {
			tftp_log(LOG_WARNING,
			    "%s write access denied", peername);
			exit(1);
		}
	}
	exit(1);
}
Exemple #17
0
/*
 * RRQ - send a file to the client
 */
void
tftp_rrq(int peer, char *recvbuffer, ssize_t size)
{
	char *cp;
	int has_options = 0, ecode;
	char *filename, *mode;
	char	fnbuf[PATH_MAX];

	cp = parse_header(peer, recvbuffer, size, &filename, &mode);
	size -= (cp - recvbuffer) + 1;

	strcpy(fnbuf, filename);
	reduce_path(fnbuf);
	filename = fnbuf;

	if (size > 0) {
		if (options_rfc_enabled)
			has_options = !parse_options(peer, cp, size);
		else
			tftp_log(LOG_INFO, "Options found but not enabled");
	}

	ecode = validate_access(peer, &filename, RRQ);
	if (ecode == 0) {
		if (has_options) {
			int n;
			char lrecvbuffer[MAXPKTSIZE];
			struct tftphdr *rp = (struct tftphdr *)lrecvbuffer;

			send_oack(peer);
			n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE,
				NULL, timeoutpacket);
			if (n < 0) {
				if (debug&DEBUG_SIMPLE)
					tftp_log(LOG_DEBUG, "Aborting: %s",
					    rp_strerror(n));
				return;
			}
			if (rp->th_opcode != ACK) {
				if (debug&DEBUG_SIMPLE)
					tftp_log(LOG_DEBUG,
					    "Expected ACK, got %s on OACK",
					    packettype(rp->th_opcode));
				return;
			}
		}
	}

	if (logging)
		tftp_log(LOG_INFO, "%s: read request for %s: %s", peername,
			    filename, errtomsg(ecode));

	if (ecode) {
		/*
		 * Avoid storms of naks to a RRQ broadcast for a relative
		 * bootfile pathname from a diskless Sun.
		 */
		if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
			exit(0);
		send_error(peer, ecode);
		exit(1);
	}
	tftp_xmitfile(peer, mode);
}
Exemple #18
0
/*
 * Send a file via the TFTP data session.
 */
void
tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
{
	struct tftphdr *rp;
	int size, n_data, n_ack, try;
	uint16_t oldblock;
	char sendbuffer[MAXPKTSIZE];
	char recvbuffer[MAXPKTSIZE];

	rp = (struct tftphdr *)recvbuffer;
	*block = 1;
	ts->amount = 0;
	do {
		if (debug&DEBUG_SIMPLE)
			tftp_log(LOG_DEBUG, "Sending block %d", *block);

		size = read_file(sendbuffer, segsize);
		if (size < 0) {
			tftp_log(LOG_ERR, "read_file returned %d", size);
			send_error(peer, errno + 100);
			goto abort;
		}

		for (try = 0; ; try++) {
			n_data = send_data(peer, *block, sendbuffer, size);
			if (n_data > 0) {
				if (try == maxtimeouts) {
					tftp_log(LOG_ERR,
					    "Cannot send DATA packet #%d, "
					    "giving up", *block);
					return;
				}
				tftp_log(LOG_ERR,
				    "Cannot send DATA packet #%d, trying again",
				    *block);
				continue;
			}

			n_ack = receive_packet(peer, recvbuffer,
			    MAXPKTSIZE, NULL, timeoutpacket);
			if (n_ack < 0) {
				if (n_ack == RP_TIMEOUT) {
					if (try == maxtimeouts) {
						tftp_log(LOG_ERR,
						    "Timeout #%d send ACK %d "
						    "giving up", try, *block);
						return;
					}
					tftp_log(LOG_WARNING,
					    "Timeout #%d on ACK %d",
					    try, *block);
					continue;
				}

				/* Either read failure or ERROR packet */
				if (debug&DEBUG_SIMPLE)
					tftp_log(LOG_ERR, "Aborting: %s",
					    rp_strerror(n_ack));
				goto abort;
			}
			if (rp->th_opcode == ACK) {
				ts->blocks++;
				if (rp->th_block == *block) {
					ts->amount += size;
					break;
				}

				/* Re-synchronize with the other side */
				(void) synchnet(peer);
				if (rp->th_block == (*block - 1)) {
					ts->retries++;
					continue;
				}
			}

		}
		oldblock = *block;
		(*block)++;
		if (oldblock > *block) {
			if (options[OPT_ROLLOVER].o_request == NULL) {
				/*
				 * "rollover" option not specified in
				 * tftp client.  Default to rolling block
				 * counter to 0.
				 */
				*block = 0;
			} else {
				*block = atoi(options[OPT_ROLLOVER].o_request);
			}

			ts->rollovers++;
		}
		gettimeofday(&(ts->tstop), NULL);
	} while (size == segsize);
abort:
	return;
}

/*
 * Receive a file via the TFTP data session.
 *
 * - It could be that the first block has already arrived while
 *   trying to figure out if we were receiving options or not. In
 *   that case it is passed to this function.
 */
void
tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
    struct tftphdr *firstblock, size_t fb_size)
{
	struct tftphdr *rp;
	uint16_t oldblock;
	int n_data, n_ack, writesize, i, retry;
	char recvbuffer[MAXPKTSIZE];

	ts->amount = 0;

	if (firstblock != NULL) {
		writesize = write_file(firstblock->th_data, fb_size);
		ts->amount += writesize;
		for (i = 0; ; i++) {
			n_ack = send_ack(peer, *block);
			if (n_ack > 0) {
				if (i == maxtimeouts) {
					tftp_log(LOG_ERR,
					    "Cannot send ACK packet #%d, "
					    "giving up", *block);
					return;
				}
				tftp_log(LOG_ERR,
				    "Cannot send ACK packet #%d, trying again",
				    *block);
				continue;
			}

			break;
		}

		if (fb_size != segsize) {
			gettimeofday(&(ts->tstop), NULL);
			return;
		}
	}

	rp = (struct tftphdr *)recvbuffer;
	do {
		oldblock = *block;
		(*block)++;
		if (oldblock > *block) {
			if (options[OPT_ROLLOVER].o_request == NULL) {
				/*
				 * "rollover" option not specified in
				 * tftp client.  Default to rolling block
				 * counter to 0.
				 */
				*block = 0;
			} else {
				*block = atoi(options[OPT_ROLLOVER].o_request);
			}

			ts->rollovers++;
		}

		for (retry = 0; ; retry++) {
			if (debug&DEBUG_SIMPLE)
				tftp_log(LOG_DEBUG,
				    "Receiving DATA block %d", *block);

			n_data = receive_packet(peer, recvbuffer,
			    MAXPKTSIZE, NULL, timeoutpacket);
			if (n_data < 0) {
				if (retry == maxtimeouts) {
					tftp_log(LOG_ERR,
					    "Timeout #%d on DATA block %d, "
					    "giving up", retry, *block);
					return;
				}
				if (n_data == RP_TIMEOUT) {
					tftp_log(LOG_WARNING,
					    "Timeout #%d on DATA block %d",
					    retry, *block);
					send_ack(peer, oldblock);
					continue;
				}

				/* Either read failure or ERROR packet */
				if (debug&DEBUG_SIMPLE)
					tftp_log(LOG_DEBUG, "Aborting: %s",
					    rp_strerror(n_data));
				goto abort;
			}
			if (rp->th_opcode == DATA) {
				ts->blocks++;

				if (rp->th_block == *block)
					break;

				tftp_log(LOG_WARNING,
				    "Expected DATA block %d, got block %d",
				    *block, rp->th_block);

				/* Re-synchronize with the other side */
				(void) synchnet(peer);
				if (rp->th_block == (*block-1)) {
					tftp_log(LOG_INFO, "Trying to sync");
					*block = oldblock;
					ts->retries++;
					goto send_ack;	/* rexmit */
				}

			} else {
				tftp_log(LOG_WARNING,
				    "Expected DATA block, got %s block",
				    packettype(rp->th_opcode));
			}
		}

		if (n_data > 0) {
			writesize = write_file(rp->th_data, n_data);
			ts->amount += writesize;
			if (writesize <= 0) {
				tftp_log(LOG_ERR,
				    "write_file returned %d", writesize);
				if (writesize < 0)
					send_error(peer, errno + 100);
				else
					send_error(peer, ENOSPACE);
				goto abort;
			}
		}

send_ack:
		for (i = 0; ; i++) {
			n_ack = send_ack(peer, *block);
			if (n_ack > 0) {

				if (i == maxtimeouts) {
					tftp_log(LOG_ERR,
					    "Cannot send ACK packet #%d, "
					    "giving up", *block);
					return;
				}

				tftp_log(LOG_ERR,
				    "Cannot send ACK packet #%d, trying again",
				    *block);
				continue;
			}

			break;
		}
		gettimeofday(&(ts->tstop), NULL);
	} while (n_data == segsize);

	/* Don't do late packet management for the client implementation */
	if (acting_as_client)
		return;

	for (i = 0; ; i++) {
		n_data = receive_packet(peer, (char *)rp, pktsize,
		    NULL, timeoutpacket);
		if (n_data <= 0)
			break;
		if (n_data > 0 &&
		    rp->th_opcode == DATA &&	/* and got a data block */
		    *block == rp->th_block)	/* then my last ack was lost */
			send_ack(peer, *block);	/* resend final ack */
	}

abort:
	return;
}
Exemple #19
0
/*
 * Receive a file.
 */
void
recvfile(int peer, char *port, int fd, char *name, char *mode)
{
	struct tftphdr *rp;
	uint16_t block;
	char recvbuffer[MAXPKTSIZE];
	int n, i;
	struct tftp_stats tftp_stats;

	stats_init(&tftp_stats);

	rp = (struct tftphdr *)recvbuffer;

	if (port == NULL) {
		struct servent *se;
		se = getservbyname("tftp", "udp");
		((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port;
	} else
		((struct sockaddr_in *)&peer_sock)->sin_port =
		    htons(atoi(port));

	for (i = 0; i < 12; i++) {
		struct sockaddr_storage from;

		/* Tell the other side what we want to do */
		if (debug&DEBUG_SIMPLE)
			printf("Requesting %s\n", name);

		n = send_rrq(peer, name, mode);
		if (n > 0) {
			printf("Cannot send RRQ packet\n");
			return;
		}

		/*
		 * The first packet we receive has the new destination port
		 * we have to send the next packets to.
		 */
		n = receive_packet(peer, recvbuffer,
		    MAXPKTSIZE, &from, timeoutpacket);

		/* We got something useful! */
		if (n >= 0) {
			((struct sockaddr_in *)&peer_sock)->sin_port =
			    ((struct sockaddr_in *)&from)->sin_port;
			break;
		}

		/* We should retry if this happens */
		if (n == RP_TIMEOUT) {
			printf("Try %d, didn't receive answer from remote.\n",
			    i + 1);
			continue;
		}

		/* Otherwise it is a fatal error */
		break;
	}
	if (i == 12) {
		printf("Transfer timed out.\n");
		return;
	}
	if (rp->th_opcode == ERROR) {
		tftp_log(LOG_ERR, "Error code %d: %s", rp->th_code, rp->th_msg);
		return;
	}

	if (write_init(fd, NULL, mode) < 0) {
		warn("write_init");
		return;
	}

	/*
	 * If the first packet is an OACK packet instead of an DATA packet,
	 * handle it different.
	 */
	if (rp->th_opcode == OACK) {
		if (!options_rfc_enabled) {
			printf("Got OACK while options are not enabled!\n");
			send_error(peer, EBADOP);
			return;
		}

		parse_options(peer, rp->th_stuff, n + 2);

		n = send_ack(peer, 0);
		if (n > 0) {
			printf("Cannot send ACK on OACK.\n");
			return;
		}
		block = 0;
		tftp_receive(peer, &block, &tftp_stats, NULL, 0);
	} else {
		block = 1;
		tftp_receive(peer, &block, &tftp_stats, rp, n);
	}

	write_close();
	if (tftp_stats.amount > 0)
		printstats("Received", verbose, &tftp_stats);
	return;
}