⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpd_c.txt

📁 基于UNIX的FTP源代码
💻 TXT
📖 第 1 页 / 共 5 页
字号:
	}

	/*
	 * Display a login message, if it exists.
	 * N.B. reply(230,) must follow the message.
	 */
	motd = login_getcapstr(lc, "welcome", NULL, NULL);
	if ((fp = fopen(motd ? motd : _PATH_FTPLOGINMESG, "r")) != NULL) {
		char line[LINE_MAX];

		while (fgets(line, sizeof(line), fp) != NULL) {
			line[strcspn(line, "\n")] = '\0';
			lreply(230, "%s", line);
		}
		(void) fflush(stdout);
		(void) fclose(fp);
	}
	if (motd != NULL)
		free(motd);
	if (guest) {
		if (ident != NULL)
			free(ident);
		ident = strdup(passwd);
		if (ident == NULL)
			fatal("Ran out of memory.");
		reply(230, "Guest login ok, access restrictions apply.");
#ifdef HASSETPROCTITLE
		snprintf(proctitle, sizeof(proctitle),
		    "%s: anonymous/%.*s", remotehost,
		    (int)(sizeof(proctitle) - sizeof(remotehost) -
		    sizeof(": anonymous/")), passwd);
		setproctitle("%s", proctitle);
#endif /* HASSETPROCTITLE */
		if (logging)
			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
			    remotehost, passwd);
	} else {
		reply(230, "User %s logged in.", pw->pw_name);
#ifdef HASSETPROCTITLE
		snprintf(proctitle, sizeof(proctitle),
		    "%s: %s", remotehost, pw->pw_name);
		setproctitle("%s", proctitle);
#endif /* HASSETPROCTITLE */
		if (logging)
			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
			    remotehost, pw->pw_name);
	}
	login_close(lc);
	lc = NULL;
	return (AUTH_SLAVE);
bad:
	/* Forget all about it... */
	login_close(lc);
	lc = NULL;
	end_login();
	return (AUTH_FAILED);
}

void
retrieve(char *cmd, char *name)
{
	FILE *fin, *dout;
	struct stat st;
	int (*closefunc)(FILE *);
	time_t start;

	if (cmd == 0) {
		fin = fopen(name, "r"), closefunc = fclose;
		st.st_size = 0;
	} else {
		char line[BUFSIZ];

		(void) snprintf(line, sizeof(line), cmd, name);
		name = line;
		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
		st.st_size = -1;
		st.st_blksize = BUFSIZ;
	}
	if (fin == NULL) {
		if (errno != 0) {
			perror_reply(550, name);
			if (cmd == 0) {
				LOGCMD("get", name);
			}
		}
		return;
	}
	byte_count = -1;
	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
		reply(550, "%s: not a plain file.", name);
		goto done;
	}
	if (restart_point) {
		if (type == TYPE_A) {
			off_t i, n;
			int c;

			n = restart_point;
			i = 0;
			while (i++ < n) {
				if ((c = getc(fin)) == EOF) {
					if (ferror(fin)) {
						perror_reply(550, name);
						goto done;
					} else
						break;
				}
				if (c == '\n')
					i++;
			}
		} else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
			perror_reply(550, name);
			goto done;
		}
	}
	dout = dataconn(name, st.st_size, "w");
	if (dout == NULL)
		goto done;
	time(&start);
	send_data(fin, dout, (off_t)st.st_blksize, st.st_size,
	    (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)));
	if ((cmd == 0) && stats)
		logxfer(name, byte_count, start);
	(void) fclose(dout);
	data = -1;
done:
	if (pdata >= 0)
		(void) close(pdata);
	pdata = -1;
	if (cmd == 0)
		LOGBYTES("get", name, byte_count);
	(*closefunc)(fin);
}

void
store(char *name, char *mode, int unique)
{
	FILE *fout, *din;
	int (*closefunc)(FILE *);
	struct stat st;
	int fd;

	if (restart_point && *mode != 'a')
		mode = "r+";

	if (unique && stat(name, &st) == 0) {
		char *nam;

		fd = guniquefd(name, &nam);
		if (fd == -1) {
			LOGCMD(*mode == 'w' ? "put" : "append", name);
			return;
		}
		name = nam;
		fout = fdopen(fd, mode);
	} else
		fout = fopen(name, mode);

	closefunc = fclose;
	if (fout == NULL) {
		perror_reply(553, name);
		LOGCMD(*mode == 'w' ? "put" : "append", name);
		return;
	}
	byte_count = -1;
	if (restart_point) {
		if (type == TYPE_A) {
			off_t i, n;
			int c;

			n = restart_point;
			i = 0;
			while (i++ < n) {
				if ((c = getc(fout)) == EOF) {
					if (ferror(fout)) {
						perror_reply(550, name);
						goto done;
					} else
						break;
				}
				if (c == '\n')
					i++;
			}
			/*
			 * We must do this seek to "current" position
			 * because we are changing from reading to
			 * writing.
			 */
			if (fseek(fout, 0L, SEEK_CUR) < 0) {
				perror_reply(550, name);
				goto done;
			}
		} else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
			perror_reply(550, name);
			goto done;
		}
	}
	din = dataconn(name, (off_t)-1, "r");
	if (din == NULL)
		goto done;
	if (receive_data(din, fout) == 0) {
		if (unique)
			reply(226, "Transfer complete (unique file name:%s).",
			    name);
		else
			reply(226, "Transfer complete.");
	}
	(void) fclose(din);
	data = -1;
	pdata = -1;
done:
	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
	(*closefunc)(fout);
}

static FILE *
getdatasock(char *mode)
{
	int on = 1, s, t, tries;

	if (data >= 0)
		return (fdopen(data, mode));
	sigprocmask (SIG_BLOCK, &allsigs, NULL);
	s = monitor_socket(ctrl_addr.su_family);
	if (s < 0)
		goto bad;
	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
	    &on, sizeof(on)) < 0)
		goto bad;
	/* anchor socket to avoid multi-homing problems */
	data_source = ctrl_addr;
	data_source.su_port = htons(20); /* ftp-data port */
	for (tries = 1; ; tries++) {
		if (monitor_bind(s, (struct sockaddr *)&data_source,
		    data_source.su_len) >= 0)
			break;
		if (errno != EADDRINUSE || tries > 10)
			goto bad;
		sleep((unsigned int)tries);
	}
	sigprocmask (SIG_UNBLOCK, &allsigs, NULL);

#ifdef IP_TOS
	if (ctrl_addr.su_family == AF_INET) {
		on = IPTOS_THROUGHPUT;
		if (setsockopt(s, IPPROTO_IP, IP_TOS, &on,
		    sizeof(int)) < 0)
			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
	}
#endif
#ifdef TCP_NOPUSH
	/*
	 * Turn off push flag to keep sender TCP from sending short packets
	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
	 * to set the send buffer size as well, but that may not be desirable
	 * in heavy-load situations.
	 */
	on = 1;
	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof(on)) < 0)
		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
#endif
#ifdef SO_SNDBUF
	on = 65536;
	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof(on)) < 0)
		syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
#endif

	return (fdopen(s, mode));
bad:
	/* Return the real value of errno (close may change it) */
	t = errno;
	sigprocmask (SIG_UNBLOCK, &allsigs, NULL);
	if (s >= 0)
		(void) close(s);
	errno = t;
	return (NULL);
}

static FILE *
dataconn(char *name, off_t size, char *mode)
{
	char sizebuf[32];
	FILE *file = NULL;
	int retry = 0;
	in_port_t *p;
	u_char *fa, *ha;
	size_t alen;
	int error;

	file_size = size;
	byte_count = 0;
	if (size != (off_t) -1) {
		(void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)",
		    size);
	} else
		sizebuf[0] = '\0';
	if (pdata >= 0) {
		union sockunion from;
		int s;
		socklen_t fromlen = sizeof(from);

		(void) alarm ((unsigned) timeout);
		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
		(void) alarm (0);
		if (s < 0) {
			reply(425, "Can't open data connection.");
			(void) close(pdata);
			pdata = -1;
			return (NULL);
		}
		switch (from.su_family) {
		case AF_INET:
			p = (in_port_t *)&from.su_sin.sin_port;
			fa = (u_char *)&from.su_sin.sin_addr;
			ha = (u_char *)&his_addr.su_sin.sin_addr;
			alen = sizeof(struct in_addr);
			break;
		case AF_INET6:
			p = (in_port_t *)&from.su_sin6.sin6_port;
			fa = (u_char *)&from.su_sin6.sin6_addr;
			ha = (u_char *)&his_addr.su_sin6.sin6_addr;
			alen = sizeof(struct in6_addr);
			break;
		default:
			reply(425, "Can't build data connection: "
			    "unknown address family");
			(void) close(pdata);
			(void) close(s);
			pdata = -1;
			return (NULL);
		}
		if (from.su_family != his_addr.su_family ||
		    ntohs(*p) < IPPORT_RESERVED) {
			reply(425, "Can't build data connection: "
			    "address family or port error");
			(void) close(pdata);
			(void) close(s);
			pdata = -1;
			return (NULL);
		}
		if (portcheck && memcmp(fa, ha, alen) != 0) {
			reply(435, "Can't build data connection: "
			    "illegal port number");
			(void) close(pdata);
			(void) close(s);
			pdata = -1;
			return (NULL);
		}
		(void) close(pdata);
		pdata = s;
		reply(150, "Opening %s mode data connection for '%s'%s.",
		    type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
		return (fdopen(pdata, mode));
	}
	if (data >= 0) {
		reply(125, "Using existing data connection for '%s'%s.",
		    name, sizebuf);
		usedefault = 1;
		return (fdopen(data, mode));
	}
	if (usedefault)
		data_dest = his_addr;
	usedefault = 1;
	do {
		if (file != NULL)
			(void) fclose(file);
		file = getdatasock(mode);
		if (file == NULL) {
			char hbuf[MAXHOSTNAMELEN], pbuf[10];

			error = getnameinfo((struct sockaddr *)&data_source,
			    data_source.su_len, hbuf, sizeof(hbuf), pbuf,
			    sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV);
			if (error != 0)
				reply(425, "Can't create data socket: %s.",
				    strerror(errno));
			else
				reply(425,
				    "Can't create data socket (%s,%s): %s.",
				    hbuf, pbuf, strerror(errno));
			return (NULL);
		}

		/*
		 * attempt to connect to reserved port on client machine;
		 * this looks like an attack
		 */
		switch (data_dest.su_family) {
		case AF_INET:
			p = (in_port_t *)&data_dest.su_sin.sin_port;
			fa = (u_char *)&data_dest.su_sin.sin_addr;
			ha = (u_char *)&his_addr.su_sin.sin_addr;
			alen = sizeof(struct in_addr);
			break;
		case AF_INET6:
			p = (in_port_t *)&data_dest.su_sin6.sin6_port;
			fa = (u_char *)&data_dest.su_sin6.sin6_addr;
			ha = (u_char *)&his_addr.su_sin6.sin6_addr;
			alen = sizeof(struct in6_addr);
			break;
		default:
			reply(425, "Can't build data connection: "
			    "unknown address family");
			(void) fclose(file);
			pdata = -1;
			return (NULL);
		}
		if (data_dest.su_family != his_addr.su_family ||
		    ntohs(*p) < IPPORT_RESERVED || ntohs(*p) == 2049) { /* XXX */
			reply(425, "Can't build data connection: "
			    "address family or port error");
			(void) fclose(file);
			return NULL;
		}
		if (portcheck && memcmp(fa, ha, alen) != 0) {
			reply(435, "Can't build data connection: "
			    "illegal port number");
			(void) fclose(file);
			return NULL;
		}

		if (connect(fileno(file), (struct sockaddr *)&data_dest,
		    data_dest.su_len) == 0) {
			reply(150, "Opening %s mode data connection for '%s'%s.",
			    type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
			data = fileno(file);
			return (file);
		}
		if (errno != EADDRINUSE)
			break;
		sleep((unsigned) swaitint);
		retry += swaitint;
	} while (retry <= swaitmax);
	perror_reply(425, "Can't build data connection");
	(void) fclose(file);
	return (NULL);
}

/*
 * Transfer the contents of "instr" to "outstr" peer using the appropriate
 * encapsulation of the data subject to Mode, Structure, and Type.
 *
 * NB: Form isn't handled.
 */
static int
send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
{
	int c, cnt, filefd, netfd;
	char *buf, *bp;
	size_t len;

	transflag++;
	switch (type) {

	case TYPE_A:
		while ((c = getc(instr)) != EOF) {
			if (recvurg)
				goto got_oob;
			byte_count++;
			if (c == '\n') {
				if (ferror(outstr))
					goto data_err;
				(void) putc('\r', outstr);
			}
			(void) putc(c, outstr);
		}
		fflush(outstr);
		transflag = 0;
		if (ferror(instr))
			goto file_err;
		if (ferror(outstr))
			goto data_err;
		reply(226, "Transfer complete.");
		return(0);

	case TYPE_I:
	case TYPE_L:
		/*
		 * isreg is only set if we are not doing restart and we
		 * are sending a regular file
		 */
		netfd = fileno(outstr);
		filefd = fileno(instr);

		if (isreg && filesize < (off_t)16 * 1024 * 1024) {
			size_t fsize = (size_t)filesize;

			buf = mmap(0, fsize, PROT_READ, MAP_SHARED, filefd,
			    (off_t)0);
			if (buf == MAP_FAILED) {
				syslog(LOG_WARNING, "mmap(%llu): %m",
				    (unsigned long long)fsize);
				goto oldway;
			}
			bp = buf;
			len = fsize;
			do {
				cnt = write(netfd, bp, len);
				if (recvurg) {
					munmap(buf, fsize);
					goto got_oob;
				}
				len -= cnt;
				bp += cnt;
				if (cnt > 0)
					byte_count += cnt;
			} while(cnt > 0 && len > 0);

			transflag = 0;
			munmap(buf, fsize);
			if (cnt < 0)
				goto data_err;
			reply(226, "Transfer complete.");
			return(0);
		}

oldway:
		if ((buf = malloc((size_t)blksize)) == NULL) {
			transflag = 0;
			perror_reply(451, "Local resource failure: malloc");
			return(-1);
		}

		while ((cnt = read(filefd, buf, (size_t)blksize)) > 0 &&
		    write(netfd, buf, cnt) == cnt)
			byte_count += cnt;
		transflag = 0;
		(void)free(buf);
		if (cnt != 0) {
			if (cnt < 0)
				goto file_err;
			goto data_err;
		}
		reply(226, "Transfer complete.");
		return(0);
	default:
		transflag = 0;
		reply(550, "Unimplemented TYPE %d in send_data", type);
		return(-1);
	}

data_err:
	transflag = 0;
	reply(426, "Data connection");
	return(-1);

file_err:
	transflag = 0;
	reply(551, "Error on input file");
	return(-1);

got_oob:
	myoob();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -