From alexs@snark.rinet.ru  Sun May 11 07:43:46 2003
Return-Path: <alexs@snark.rinet.ru>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id A3F0537B401
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 11 May 2003 07:43:46 -0700 (PDT)
Received: from snark.rinet.ru (snark.rinet.ru [195.54.192.73])
	by mx1.FreeBSD.org (Postfix) with ESMTP id C498843FEA
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 11 May 2003 07:43:45 -0700 (PDT)
	(envelope-from alexs@snark.rinet.ru)
Received: from snark.rinet.ru (alexs@localhost [127.0.0.1])
	by snark.rinet.ru (8.12.8/8.12.7) with ESMTP id h4BEhhX8015013
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 11 May 2003 18:43:44 +0400 (MSD)
	(envelope-from alexs@snark.rinet.ru)
Received: (from alexs@localhost)
	by snark.rinet.ru (8.12.8/8.12.8/Submit) id h4BEhh3m015012;
	Sun, 11 May 2003 18:43:43 +0400 (MSD)
Message-Id: <200305111443.h4BEhh3m015012@snark.rinet.ru>
Date: Sun, 11 May 2003 18:43:43 +0400 (MSD)
From: Alex Semenyaka <alexs@ratmir.ru>
Reply-To: Alex Semenyaka <alex@semenyaka.ru>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Wrong behaviour of the ftpd when the OOB data received
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         52072
>Category:       bin
>Synopsis:       Wrong behaviour of the ftpd when the OOB data received
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    yar
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun May 11 07:50:09 PDT 2003
>Closed-Date:    Mon Feb 28 12:23:37 GMT 2005
>Last-Modified:  Mon Feb 28 12:23:37 GMT 2005
>Originator:     Alex Semenyaka
>Release:        FreeBSD 4.8-RC i386
>Organization:
Ratmir
>Environment:
System: FreeBSD snark.rinet.ru 4.8-RC FreeBSD 4.8-RC #1: Tue Mar 4 14:49:11 MSK 2003 root@snark.rinet.ru:/usr/obj/usr/src/sys/SNARK i386

>Description:

Some time ago from OpenBSD were moved to FreeBSD ftpd some changes, concerning
the treatment of the OOB data. Now ftpd set the flag recvurg when it receives
them:


a.sa_handler = sigurg;
sa.sa_flags = 0;                /* don't restart syscalls for SIGURG */
(void)sigaction(SIGURG, &sa, NULL);
[...some code...]
static void
sigurg(signo)
        int signo;
{
        recvurg = 1;
}


Then during the transfer this flag is checking and if it is set, transfer is
aborted:

        if (recvurg)
           goto got_oob;
        [...some code...]
got_oob:
        myoob();
        recvurg = 0;
        transflag = 0;
        return (-1);


Now, OOB data is the way to send ABRT or STAT message. In case of ABRT that
behaviour is perfect: transfer should be stopped immediately. But in the case
of STAT we should continue.

I marked the problem as non-critical since the current ftp implementations
(incluing native ftp, lukemftp, ncftp) to the best of my knowledge do not
use OOB data only to send ABRT, so this is only potentially wrong behaviour.
However that should be fixed I beleive in order to hadle future possible
situation if some client will use OOB data for the STAT request.

>How-To-Repeat:

Built into ftp(1) the possibility to send OOB data to request STAT info and
observe the abort of the connection.

>Fix:


--- ftpd.c.old	Sun May 11 18:05:55 2003
+++ ftpd.c	Sun May 11 18:41:50 2003
@@ -247,5 +247,5 @@
 static void	 ack __P((char *));
 static void	 sigurg __P((int));
-static void	 myoob __P((void));
+static int	 myoob __P((void));
 static int	 checkuser __P((char *, char *, int, char **));
 static FILE	*dataconn __P((char *, off_t, char *));
@@ -1970,5 +1970,5 @@
 	int isreg;
 {
-	int c, filefd, netfd;
+	int c, filefd, netfd, is_abrt;
 	char *buf;
 	off_t cnt;
@@ -1979,6 +1979,12 @@
 	case TYPE_A:
 		while ((c = getc(instr)) != EOF) {
-			if (recvurg)
-				goto got_oob;
+			if (recvurg) {
+				is_abrt = myoob();
+				recvurg = 0;
+				if (is_abrt) {
+					transflag = 0;
+					return (-1);
+				}
+			}
 			byte_count++;
 			if (c == '\n') {
@@ -1989,6 +1995,12 @@
 			(void) putc(c, outstr);
 		}
-		if (recvurg)
-			goto got_oob;
+		if (recvurg) {
+			is_abrt = myoob();
+			recvurg = 0;
+			if (is_abrt) {
+				transflag = 0;
+				return (-1);
+			}
+		}
 		fflush(outstr);
 		transflag = 0;
@@ -2024,6 +2036,12 @@
 				 */
 				byte_count += cnt;
-				if (recvurg)
-					goto got_oob;
+				if (recvurg) {
+					is_abrt = myoob();
+					recvurg = 0;
+					if (is_abrt) {
+						transflag = 0;
+						return (-1);
+					}
+				}
 				offset += cnt;
 				filesize -= cnt;
@@ -2077,9 +2095,4 @@
 	return (-1);
 
-got_oob:
-	myoob();
-	recvurg = 0;
-	transflag = 0;
-	return (-1);
 }
 
@@ -2095,5 +2108,5 @@
 {
 	int c;
-	int cnt, bare_lfs;
+	int cnt, bare_lfs, is_abrt;
 	char buf[BUFSIZ];
 
@@ -2106,12 +2119,24 @@
 	case TYPE_L:
 		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
-			if (recvurg)
-				goto got_oob;
+			if (recvurg) {
+				is_abrt = myoob();
+				recvurg = 0;
+				if (is_abrt) {
+					transflag = 0;
+					return (-1);
+				}
+			}
 			if (write(fileno(outstr), buf, cnt) != cnt)
 				goto file_err;
 			byte_count += cnt;
 		}
-		if (recvurg)
-			goto got_oob;
+		if (recvurg) {
+			is_abrt = myoob();
+			recvurg = 0;
+			if (is_abrt) {
+				transflag = 0;
+				return (-1);
+			}
+		}
 		if (cnt < 0)
 			goto data_err;
@@ -2126,6 +2151,12 @@
 	case TYPE_A:
 		while ((c = getc(instr)) != EOF) {
-			if (recvurg)
-				goto got_oob;
+			if (recvurg) {
+				is_abrt = myoob();
+				recvurg = 0;
+				if (is_abrt) {
+					transflag = 0;
+					return (-1);
+				}
+			}
 			byte_count++;
 			if (c == '\n')
@@ -2143,6 +2174,12 @@
 	contin2:	;
 		}
-		if (recvurg)
-			goto got_oob;
+		if (recvurg) {
+			is_abrt = myoob();
+			recvurg = 0;
+			if (is_abrt) {
+				transflag = 0;
+				return (-1);
+			}
+		}
 		fflush(outstr);
 		if (ferror(instr))
@@ -2174,9 +2211,4 @@
 	return (-1);
 
-got_oob:
-	myoob();
-	recvurg = 0;
-	transflag = 0;
-	return (-1);
 }
 
@@ -2633,12 +2665,15 @@
 }
 
-static void
+/* Returns non-zero in the case of ABRT */
+
+static int
 myoob()
 {
 	char *cp;
+	int is_abrt = 0;
 
 	/* only process if transfer occurring */
 	if (!transflag)
-		return;
+		return is_abrt;
 	cp = tmpline;
 	if (getline(cp, 7, stdin) == NULL) {
@@ -2651,4 +2686,5 @@
 		reply(426, "Transfer aborted. Data connection closed.");
 		reply(226, "Abort successful");
+		is_abrt = 1;
 	}
 	if (strcmp(cp, "STAT\r\n") == 0) {
@@ -2660,4 +2696,5 @@
 			reply(213, "Status: %qd bytes transferred", byte_count);
 	}
+	return is_abrt;
 }
 
@@ -2979,4 +3016,5 @@
 	int freeglob = 0;
 	glob_t gl;
+	int is_abrt;
 
 	if (strpbrk(whichf, "~{[*?") != NULL) {
@@ -3044,8 +3082,10 @@
 
 			if (recvurg) {
-				myoob();
+				is_abrt = myoob();
 				recvurg = 0;
-				transflag = 0;
-				goto out;
+				if (is_abrt) {
+					transflag = 0;
+					goto out;
+				}
 			}
 
>Release-Note:
>Audit-Trail:

From: Alex Semenyaka <alexs@ratmir.ru>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Sun, 11 May 2003 19:23:36 +0400

 Small code clean-up. The idea is the same but the code is much more compact.
 New patch:
 
 --- ftpd.c.old	Sun May 11 18:05:55 2003
 +++ ftpd.c	Sun May 11 19:21:10 2003
 @@ -192,4 +192,7 @@
  char	*pid_file = NULL;
  
 +#define NO_ABRT 0
 +#define IS_ABRT 1
 +
  /*
   * Limit number of pathnames that glob can return.
 @@ -247,5 +250,5 @@
  static void	 ack __P((char *));
  static void	 sigurg __P((int));
 -static void	 myoob __P((void));
 +static int	 myoob __P((void));
  static int	 checkuser __P((char *, char *, int, char **));
  static FILE	*dataconn __P((char *, off_t, char *));
 @@ -1979,6 +1982,6 @@
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 -			if (recvurg)
 -				goto got_oob;
 +			if (recvurg && IS_ABRT == myoob())
 +				return (-1);
  			byte_count++;
  			if (c == '\n') {
 @@ -1989,6 +1992,6 @@
  			(void) putc(c, outstr);
  		}
 -		if (recvurg)
 -			goto got_oob;
 +		if (recvurg && IS_ABRT == myoob())
 +			return (-1);
  		fflush(outstr);
  		transflag = 0;
 @@ -2024,6 +2027,6 @@
  				 */
  				byte_count += cnt;
 -				if (recvurg)
 -					goto got_oob;
 +				if (recvurg && IS_ABRT == myoob())
 +					return (-1);
  				offset += cnt;
  				filesize -= cnt;
 @@ -2077,9 +2080,4 @@
  	return (-1);
  
 -got_oob:
 -	myoob();
 -	recvurg = 0;
 -	transflag = 0;
 -	return (-1);
  }
  
 @@ -2106,12 +2104,12 @@
  	case TYPE_L:
  		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
 -			if (recvurg)
 -				goto got_oob;
 +			if (recvurg  && IS_ABRT == myoob())
 +				return (-1);
  			if (write(fileno(outstr), buf, cnt) != cnt)
  				goto file_err;
  			byte_count += cnt;
  		}
 -		if (recvurg)
 -			goto got_oob;
 +		if (recvurg  && IS_ABRT == myoob())
 +			return (-1);
  		if (cnt < 0)
  			goto data_err;
 @@ -2126,6 +2124,6 @@
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 -			if (recvurg)
 -				goto got_oob;
 +			if (recvurg  && IS_ABRT == myoob())
 +				return (-1);
  			byte_count++;
  			if (c == '\n')
 @@ -2143,6 +2141,6 @@
  	contin2:	;
  		}
 -		if (recvurg)
 -			goto got_oob;
 +		if (recvurg && IS_ABRT == myoob())
 +			return (-1);
  		fflush(outstr);
  		if (ferror(instr))
 @@ -2174,9 +2172,4 @@
  	return (-1);
  
 -got_oob:
 -	myoob();
 -	recvurg = 0;
 -	transflag = 0;
 -	return (-1);
  }
  
 @@ -2633,12 +2626,17 @@
  }
  
 -static void
 +/* Returns IS_ABRT in the case of ABRT */
 +
 +static int
  myoob()
  {
  	char *cp;
 +	int is_abrt = NO_ABRT;
 +
 +	recvurg = 0;
  
  	/* only process if transfer occurring */
  	if (!transflag)
 -		return;
 +		return is_abrt;
  	cp = tmpline;
  	if (getline(cp, 7, stdin) == NULL) {
 @@ -2651,4 +2649,6 @@
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 +		is_abrt = IS_ABRT;
 +		transflag = 0;
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
 @@ -2660,4 +2660,5 @@
  			reply(213, "Status: %qd bytes transferred", byte_count);
  	}
 +	return is_abrt;
  }
  
 @@ -3043,10 +3044,6 @@
  			char nbuf[MAXPATHLEN];
  
 -			if (recvurg) {
 -				myoob();
 -				recvurg = 0;
 -				transflag = 0;
 +			if (recvurg && IS_ABRT == myoob())
  				goto out;
 -			}
  
  			if (dir->d_name[0] == '.' && dir->d_namlen == 1)

From: Nick Leuta <skynick@stu.lipetsk.ru>
To: freebsd-gnats-submit@FreeBSD.org, alexs@ratmir.ru
Cc:  
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Mon, 12 May 2003 02:12:57 +0400

 There is another approach to the solution. I don't know what's better...
 
 This patch tries to fix a potential incomplite write() call - SIGURG signal
 may be received at a time of writing a data to a socket. Also I don't know
 the reasons for "sa.sa_flags = 0;", but it's possible that I simple don't
 know them...
 
 --- ftpd.ori/ftpd.c	Tue Feb 11 17:10:48 2003
 +++ ftpd/ftpd.c	Mon May 12 01:42:35 2003
 @@ -239,8 +239,7 @@
  static void	selecthost(union sockunion *);
  #endif
  static void	 ack(char *);
 -static void	 sigurg(int);
 -static void	 myoob(void);
 +static void	 myoob(int signo);
  static int	 checkuser(char *, char *, int, char **);
  static FILE	*dataconn(char *, off_t, char *);
  static void	 dolog(struct sockaddr *);
 @@ -546,8 +545,8 @@
  	sa.sa_handler = SIG_DFL;
  	(void)sigaction(SIGCHLD, &sa, NULL);
  
 -	sa.sa_handler = sigurg;
 -	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
 +	sa.sa_handler = myoob;
 +	sa.sa_flags = SA_RESTART;
  	(void)sigaction(SIGURG, &sa, NULL);
  
  	sigfillset(&sa.sa_mask);	/* block all signals in handler */
 @@ -1991,8 +1990,8 @@
  send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
  {
  	int c, filefd, netfd;
 -	char *buf;
 -	off_t cnt;
 +	char *buf, *bp;
 +	off_t cnt = 0, len;
  
  	transflag++;
  	switch (type) {
 @@ -2069,16 +2068,23 @@
  			return (-1);
  		}
  
 -		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
 -		    write(netfd, buf, cnt) == cnt)
 -			byte_count += cnt;
 +		while ((len = read(filefd, buf, (u_int)blksize)) > 0) {
 +			bp = buf;
 +			do {
 +				cnt = write(netfd, bp, len);
 +				len -= cnt;
 +				bp += cnt;
 +				if (cnt > 0) byte_count += cnt;
 +				if (recvurg)
 +					goto got_oob;
 +			} while (cnt > 0 && len > 0);
 +		}
  		transflag = 0;
  		(void)free(buf);
 -		if (cnt != 0) {
 -			if (cnt < 0)
 -				goto file_err;
 -			goto data_err;
 -		}
 +		if (len < 0)
 +		    goto file_err;
 +		if (cnt < 0)
 +		    goto data_err;
  		reply(226, "Transfer complete.");
  		return (0);
  	default:
 @@ -2098,7 +2104,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2194,7 +2199,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2612,14 +2616,7 @@
  }
  
  static void
 -sigurg(int signo)
 -{
 -
 -	recvurg = 1;
 -}
 -
 -static void
 -myoob(void)
 +myoob(int signo)
  {
  	char *cp;
  
 @@ -2636,6 +2633,9 @@
  		tmpline[0] = '\0';
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 +
 +		recvurg = 1;
 +		return;
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
  		tmpline[0] = '\0';
 @@ -3022,7 +3022,6 @@
  			char nbuf[MAXPATHLEN];
  
  			if (recvurg) {
 -				myoob();
  				recvurg = 0;
  				transflag = 0;
  				goto out;

From: Alex Semenyaka <alexs@ratmir.ru>
To: Nick Leuta <skynick@stu.lipetsk.ru>
Cc: freebsd-gnats-submit@FreeBSD.org, alexs@ratmir.ru
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Mon, 12 May 2003 15:59:46 +0400

 On Mon, May 12, 2003 at 02:12:57AM +0400, Nick Leuta wrote:
 > There is another approach to the solution. I don't know what's better...
 
 No, it is not another approach :) This should be applied _as_ _well_.
 I just forgot to check if syscalls have made restartable or not. Of course
 they should.
 
 > This patch tries to fix a potential incomplite write() call - SIGURG signal
 
 It does not. This will help only if we are already in the syscall but no
 data were transferred yet. If even the only byte is passed - the write() will
 be interrupted. Same situation with read(). I do not remember though how about
 sendfile()... should be the same there.
 
 								SY, Alex
Responsible-Changed-From-To: freebsd-bugs->maxim 
Responsible-Changed-By: maxim 
Responsible-Changed-When: Mon May 12 05:07:42 PDT 2003 
Responsible-Changed-Why:  
Will take a look. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52072 

From: Nick Leuta <skynick@stu.lipetsk.ru>
To: freebsd-gnats-submit@FreeBSD.org, alexs@ratmir.ru
Cc:  
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Sat, 26 Jul 2003 21:11:35 +0400

 It's another approach to the handling of SIGURG :-), in particular, to the
 implementation of signal handler.
 
 "Partial success" of read()/write()/sendfile() calls must be processed
 as well, yes.
 
 Next patch is the same as previous, but it's intended for handling
 "partial success" for sendfile() too, and it's applicable for
 ftpd from 5-CURRENT (Sat Jul 26 21:05:28 MSD 2003).
 
 
 --- ftpd.ori/ftpd.c	Tue Feb 11 17:10:48 2003
 +++ ftpd/ftpd.c	Thu Jul 24 02:05:23 2003
 @@ -239,8 +239,7 @@
  static void	selecthost(union sockunion *);
  #endif
  static void	 ack(char *);
 -static void	 sigurg(int);
 -static void	 myoob(void);
 +static void	 myoob(int signo);
  static int	 checkuser(char *, char *, int, char **);
  static FILE	*dataconn(char *, off_t, char *);
  static void	 dolog(struct sockaddr *);
 @@ -546,8 +545,8 @@
  	sa.sa_handler = SIG_DFL;
  	(void)sigaction(SIGCHLD, &sa, NULL);
  
 -	sa.sa_handler = sigurg;
 -	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
 +	sa.sa_handler = myoob;
 +	sa.sa_flags = SA_RESTART;
  	(void)sigaction(SIGURG, &sa, NULL);
  
  	sigfillset(&sa.sa_mask);	/* block all signals in handler */
 @@ -1991,8 +1990,8 @@
  send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
  {
  	int c, filefd, netfd;
 -	char *buf;
 -	off_t cnt;
 +	char *buf, *bp;
 +	off_t cnt = 0, len;
  
  	transflag++;
  	switch (type) {
 @@ -2050,6 +2049,10 @@
  				filesize -= cnt;
  
  				if (err == -1) {
 +					if (errno == EAGAIN) {
 +					    err = 0;
 +					    continue;
 +					}
  					if (!cnt)
  						goto oldway;
  
 @@ -2069,16 +2072,23 @@
  			return (-1);
  		}
  
 -		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
 -		    write(netfd, buf, cnt) == cnt)
 -			byte_count += cnt;
 +		while ((len = read(filefd, buf, (u_int)blksize)) > 0) {
 +			bp = buf;
 +			do {
 +				cnt = write(netfd, bp, len);
 +				len -= cnt;
 +				bp += cnt;
 +				if (cnt > 0) byte_count += cnt;
 +				if (recvurg)
 +					goto got_oob;
 +			} while (cnt > 0 && len > 0);
 +		}
  		transflag = 0;
  		(void)free(buf);
 -		if (cnt != 0) {
 -			if (cnt < 0)
 -				goto file_err;
 -			goto data_err;
 -		}
 +		if (len < 0)
 +		    goto file_err;
 +		if (cnt < 0)
 +		    goto data_err;
  		reply(226, "Transfer complete.");
  		return (0);
  	default:
 @@ -2098,7 +2108,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2194,7 +2203,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2612,14 +2620,7 @@
  }
  
  static void
 -sigurg(int signo)
 -{
 -
 -	recvurg = 1;
 -}
 -
 -static void
 -myoob(void)
 +myoob(int signo)
  {
  	char *cp;
  
 @@ -2636,6 +2637,9 @@
  		tmpline[0] = '\0';
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 +
 +		recvurg = 1;
 +		return;
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
  		tmpline[0] = '\0';
 @@ -3022,7 +3026,6 @@
  			char nbuf[MAXPATHLEN];
  
  			if (recvurg) {
 -				myoob();
  				recvurg = 0;
  				transflag = 0;
  				goto out;

From: Nick Leuta <skynick@stu.lipetsk.ru>
To: freebsd-gnats-submit@FreeBSD.org, alexs@ratmir.ru
Cc:  
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Fri, 6 Feb 2004 13:42:31 +0300

 The revised version of the patch:
 1. New possible value of errno variable is now supported for sendfile(2);
 2. Unlike previous versions of the patch, now using the current implementation
 of signal handler, which doesn't use non-signalsafe functions.
 
 diff -urN ftpd.ORI/ftpd.c ftpd/ftpd.c
 --- ftpd.ORI/ftpd.c	Mon Jan 19 00:29:33 2004
 +++ ftpd/ftpd.c	Fri Feb  6 02:29:47 2004
 @@ -239,7 +239,7 @@
  #endif
  static void	 ack(char *);
  static void	 sigurg(int);
 -static void	 myoob(void);
 +static int	 myoob(void);
  static int	 checkuser(char *, char *, int, char **);
  static FILE	*dataconn(char *, off_t, char *);
  static void	 dolog(struct sockaddr *);
 @@ -525,7 +525,7 @@
  	(void)sigaction(SIGCHLD, &sa, NULL);
  
  	sa.sa_handler = sigurg;
 -	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
 +	sa.sa_flags = SA_RESTART;
  	(void)sigaction(SIGURG, &sa, NULL);
  
  	sigfillset(&sa.sa_mask);	/* block all signals in handler */
 @@ -1972,8 +1972,8 @@
  send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
  {
  	int c, cp, filefd, netfd;
 -	char *buf;
 -	off_t cnt;
 + 	char *buf, *bp;
 + 	off_t cnt = 0, len;
  
  	transflag++;
  	switch (type) {
 @@ -1982,7 +1982,8 @@
  		cp = '\0';
  		while ((c = getc(instr)) != EOF) {
  			if (recvurg)
 -				goto got_oob;
 + 				if (myoob())
 +					goto got_oob;
  			byte_count++;
  			if (c == '\n' && cp != '\r') {
  				if (ferror(outstr))
 @@ -1993,7 +1994,8 @@
  			cp = c;
  		}
  		if (recvurg)
 -			goto got_oob;
 +			if (myoob())
 +				goto got_oob;
  		fflush(outstr);
  		transflag = 0;
  		if (ferror(instr))
 @@ -2028,11 +2030,16 @@
  				 */
  				byte_count += cnt;
  				if (recvurg)
 -					goto got_oob;
 +					if (myoob())
 +						goto got_oob;
  				offset += cnt;
  				filesize -= cnt;
  
  				if (err == -1) {
 +					if (errno == EAGAIN || errno == EINTR) {
 +					    err = 0;
 +					    continue;
 +					}
  					if (!cnt)
  						goto oldway;
  
 @@ -2052,16 +2059,26 @@
  			return (-1);
  		}
  
 -		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
 -		    write(netfd, buf, cnt) == cnt)
 -			byte_count += cnt;
 +		while ((len = read(filefd, buf, (u_int)blksize)) > 0) {
 +			bp = buf;
 +			do {
 +				cnt = write(netfd, bp, len);
 +				len -= cnt;
 +				bp += cnt;
 +				if (cnt > 0) byte_count += cnt;
 +				if (recvurg)
 +					if (myoob()) {
 +						(void)free(buf);
 +						goto got_oob;
 +					}
 +			} while (cnt > 0 && len > 0);
 +		}
  		transflag = 0;
  		(void)free(buf);
 -		if (cnt != 0) {
 -			if (cnt < 0)
 -				goto file_err;
 -			goto data_err;
 -		}
 +		if (len < 0)
 +		    goto file_err;
 +		if (cnt < 0)
 +		    goto data_err;
  		reply(226, "Transfer complete.");
  		return (0);
  	default:
 @@ -2081,7 +2098,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2109,13 +2125,15 @@
  	case TYPE_L:
  		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
  			if (recvurg)
 -				goto got_oob;
 +				if (myoob())
 +					goto got_oob;
  			if (write(fileno(outstr), buf, cnt) != cnt)
  				goto file_err;
  			byte_count += cnt;
  		}
  		if (recvurg)
 -			goto got_oob;
 +			if (myoob())
 +				goto got_oob;
  		if (cnt < 0)
  			goto data_err;
  		transflag = 0;
 @@ -2129,7 +2147,8 @@
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
  			if (recvurg)
 -				goto got_oob;
 +				if (myoob())
 +					goto got_oob;
  			byte_count++;
  			if (c == '\n')
  				bare_lfs++;
 @@ -2146,7 +2165,8 @@
  	contin2:	;
  		}
  		if (recvurg)
 -			goto got_oob;
 +			if (myoob())
 +				goto got_oob;
  		fflush(outstr);
  		if (ferror(instr))
  			goto data_err;
 @@ -2177,7 +2197,6 @@
  	return (-1);
  
  got_oob:
 -	myoob();
  	recvurg = 0;
  	transflag = 0;
  	return (-1);
 @@ -2597,18 +2616,23 @@
  static void
  sigurg(int signo)
  {
 +	/* only process if transfer occurring */
 +	if (!transflag)
 +		return;
  
  	recvurg = 1;
  }
  
 -static void
 +static int
  myoob(void)
  {
  	char *cp;
  
  	/* only process if transfer occurring */
 -	if (!transflag)
 -		return;
 +	if (!transflag) {
 +		recvurg = 0;
 +		return 0;
 +	}
  	cp = tmpline;
  	if (getline(cp, 7, stdin) == NULL) {
  		reply(221, "You could at least say goodbye.");
 @@ -2619,6 +2643,8 @@
  		tmpline[0] = '\0';
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 +
 +		return 1;
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
  		tmpline[0] = '\0';
 @@ -2628,6 +2654,8 @@
  		else
  			reply(213, "Status: %qd bytes transferred", byte_count);
  	}
 +	recvurg = 0;
 +	return 0;
  }
  
  /*
 @@ -3004,12 +3032,12 @@
  		while ((dir = readdir(dirp)) != NULL) {
  			char nbuf[MAXPATHLEN];
  
 -			if (recvurg) {
 -				myoob();
 -				recvurg = 0;
 -				transflag = 0;
 -				goto out;
 -			}
 +			if (recvurg)
 +				if (myoob()) {
 +					recvurg = 0;
 +					transflag = 0;
 +					goto out;
 +				}
  
  			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
  				continue;
Responsible-Changed-From-To: maxim->freebsd-bugs 
Responsible-Changed-By: maxim 
Responsible-Changed-When: Fri Apr 9 10:49:14 PDT 2004 
Responsible-Changed-Why:  
Be honest - I do not have a free time to work on this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52072 
Responsible-Changed-From-To: freebsd-bugs->yar 
Responsible-Changed-By: yar 
Responsible-Changed-When: Fri Dec 17 13:02:48 GMT 2004 
Responsible-Changed-Why:  
I woke up yesterday's early morning with a practical idea of how 
to approach this problem and cast it into code sooner than I had 
my breakfast. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52072 

From: Yar Tikhiy <yar@FreeBSD.org>
To: freebsd-gnats-submit@FreeBSD.org, alexs@snark.rinet.ru
Cc:  
Subject: Re: bin/52072: Wrong behaviour of the ftpd when the OOB data received
Date: Fri, 17 Dec 2004 16:02:19 +0300

 Do you still have the code implementing OOB STAT in ftp(1)?
 I made some changes to ftpd in order to fix its OOB data handling,
 but unfortunately, I have no ready tests for the case.
 
 -- 
 Yar
State-Changed-From-To: open->patched 
State-Changed-By: yar 
State-Changed-When: Wed Jan 19 10:33:45 GMT 2005 
State-Changed-Why:  
A fix committed to CURRENT. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52072 
State-Changed-From-To: patched->closed 
State-Changed-By: yar 
State-Changed-When: Mon Feb 28 12:20:44 GMT 2005 
State-Changed-Why:  
The fix has been merged to RELENG_5.  I have no plans for merging 
it to RELENG_4 because it's too big while the practical impact of 
the problem is rather small.  Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52072 
>Unformatted:
