tftp diff

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

tftp diff

Marcus Glocker
Hello,

This diff applies the following changes to tftp:

- replaced setjmp(3) / alarm(3) with poll(2) in the network routines
  sendfile() and recvfile()
- open files descriptors were not closed when a file transfer was
  aborted by ctrl-c.  fixed
- added alias 'help' for the '?' command
- style(9)ed the code a bit for better readability

The setjmp(3) in main.c which is used when ctrl-c is catched (SIGINT)
is kept which is legitim IMO for that use and done this way by a lot
of command line programs (ftp, ed, restore etc.) to return to the
command prompt no matter which routine was active (e.g. blocking
fgets(3)).

I've tested the diff by transfering large files with put and get over
the network.  Further testing welcome.

Regards,
Marcus

--
Marcus Glocker, [hidden email], http://www.nazgul.ch -----------------
diff -urN src/usr.bin/tftp.orig/main.c src/usr.bin/tftp/main.c
--- src/usr.bin/tftp.orig/main.c Wed Apr 26 12:36:12 2006
+++ src/usr.bin/tftp/main.c Thu Apr 27 10:29:37 2006
@@ -48,12 +48,12 @@
 /*
  * TFTP User Program -- Command Interface.
  */
+
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/file.h>
 
 #include <netinet/in.h>
-
 #include <arpa/inet.h>
 
 #include <ctype.h>
@@ -72,10 +72,11 @@
 #define TIMEOUT 5 /* secs between rexmt's */
 #define LBUFLEN 200 /* size of input buffer */
 #define MAXARGV 20
+#define HELPINDENT (sizeof("connect"))
 
 struct sockaddr_in peeraddr;
 int f;
-short   port;
+short port;
 int trace;
 int verbose;
 int connected;
@@ -87,6 +88,10 @@
 jmp_buf toplevel;
 void intr(int);
 struct servent *sp;
+int rexmtval = TIMEOUT;
+int maxtimeout = 5 * TIMEOUT;
+char hostname[MAXHOSTNAMELEN];
+FILE *file = NULL;
 
 void get(int, char **);
 void help(int, char **);
@@ -109,8 +114,6 @@
 static void putusage(char *);
 static void settftpmode(char *);
 
-#define HELPINDENT (sizeof("connect"))
-
 struct cmd {
  char *name;
  char *help;
@@ -128,8 +131,8 @@
 char sthelp[] = "show current status";
 char xhelp[] = "set per-packet retransmission timeout";
 char ihelp[] = "set total retransmission timeout";
-char    ashelp[] = "set mode to netascii";
-char    bnhelp[] = "set mode to octet";
+char ashelp[] = "set mode to netascii";
+char bnhelp[] = "set mode to octet";
 
 struct cmd cmdtab[] = {
  { "connect", chelp, setpeer },
@@ -144,10 +147,24 @@
  { "ascii",      ashelp,         setascii },
  { "rexmt", xhelp, setrexmt },
  { "timeout", ihelp, settimeout },
+ { "help", hhelp, help },
  { "?", hhelp, help },
  { NULL, NULL, NULL }
 };
 
+struct modes {
+ char *m_name;
+ char *m_mode;
+} modes[] = {
+ { "ascii", "netascii" },
+ { "netascii", "netascii" },
+ { "binary", "octet" },
+ { "image", "octet" },
+ { "octet", "octet" },
+/* { "mail", "mail" }, */
+ { NULL, NULL }
+};
+
 struct cmd *getcmd(char *);
 char *tail(char *);
 
@@ -156,31 +173,42 @@
 {
  struct sockaddr_in s_in;
 
+ /* socket, bind */
  sp = getservbyname("tftp", "udp");
  if (sp == 0)
  errx(1, "udp/tftp: unknown service");
  f = socket(AF_INET, SOCK_DGRAM, 0);
  if (f < 0)
  err(3, "socket");
- bzero((char *)&s_in, sizeof (s_in));
+ bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family = AF_INET;
- if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0)
+ if (bind(f, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
  err(1, "bind");
- strlcpy(mode, "netascii", sizeof mode);
- signal(SIGINT, intr);
- if (argc > 1) {
- if (setjmp(toplevel) != 0)
- exit(0);
+
+ /* set default transfer mode */
+ strlcpy(mode, "netascii", sizeof(mode));
+
+ /* set peer if given */
+ if (argc > 1)
  setpeer(argc, argv);
- }
+
+ /* catch SIGINT */
+ signal(SIGINT, intr);
+
+ /* jump here on SIGINT, mostly ctrl-c */
  if (setjmp(toplevel) != 0)
- (void)putchar('\n');
+ (void) putchar('\n');
+
+ /* close open file */
+ if (file != NULL)
+ fclose(file);
+
+ /* command prompt */
  command();
+
  return (0);
 }
 
-char    hostname[MAXHOSTNAMELEN];
-
 void
 setpeer(int argc, char *argv[])
 {
@@ -227,19 +255,6 @@
  connected = 1;
 }
 
-struct modes {
- char *m_name;
- char *m_mode;
-} modes[] = {
- { "ascii", "netascii" },
- { "netascii",   "netascii" },
- { "binary",     "octet" },
- { "image",      "octet" },
- { "octet",     "octet" },
-/*      { "mail",       "mail" },       */
- { NULL, NULL }
-};
-
 void
 modecmd(int argc, char *argv[])
 {
@@ -276,14 +291,12 @@
 void
 setbinary(int argc, char *argv[])
 {
-
  settftpmode("octet");
 }
 
 void
 setascii(int argc, char *argv[])
 {
-
  settftpmode("netascii");
 }
 
@@ -295,7 +308,6 @@
  printf("mode set to %s\n", mode);
 }
 
-
 /*
  * Send file(s).
  */
@@ -309,7 +321,7 @@
  if (argc < 2) {
  strlcpy(line, "send ", sizeof line);
  printf("(file) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ fgets(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -362,8 +374,10 @@
  return;
  }
 
- /* this assumes the target is a directory */
- /* on a remote unix system.  hmmmm.  */
+ /*
+ * this assumes the target is a directory on
+ * on a remote unix system.  hmmmm.
+ */
  for (n = 1; n < argc - 1; n++) {
  if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
  err(1, "asprintf");
@@ -386,7 +400,8 @@
 putusage(char *s)
 {
  printf("usage: %s file [[host:]remotename]\n", s);
- printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
+ printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
+    s);
 }
 
 /*
@@ -474,8 +489,6 @@
  printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
 }
 
-int rexmtval = TIMEOUT;
-
 void
 setrexmt(int argc, char *argv[])
 {
@@ -501,8 +514,6 @@
  rexmtval = t;
 }
 
-int maxtimeout = 5 * TIMEOUT;
-
 void
 settimeout(int argc, char *argv[])
 {
@@ -544,9 +555,6 @@
 void
 intr(int signo)
 {
-
- signal(SIGALRM, SIG_IGN);
- alarm(0);
  longjmp(toplevel, -1);
 }
 
@@ -666,7 +674,6 @@
 void
 quit(int argc, char *argv[])
 {
-
  exit(0);
 }
 
diff -urN src/usr.bin/tftp.orig/tftp.c src/usr.bin/tftp/tftp.c
--- src/usr.bin/tftp.orig/tftp.c Tue Apr 25 22:08:37 2006
+++ src/usr.bin/tftp/tftp.c Thu Apr 27 12:02:19 2006
@@ -42,17 +42,16 @@
 /*
  * TFTP User Program -- Protocol Machines
  */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 
 #include <netinet/in.h>
-
 #include <arpa/tftp.h>
 
 #include <errno.h>
-#include <setjmp.h>
-#include <signal.h>
+#include <poll.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <string.h>
@@ -62,40 +61,57 @@
 #include "extern.h"
 #include "tftpsubs.h"
 
+#define PKTSIZE SEGSIZE + 4
 
-extern  struct sockaddr_in peeraddr; /* filled in by main */
-extern  int     f; /* the opened socket */
-extern  int     trace;
-extern  int     verbose;
-extern  int     rexmtval;
-extern  int     maxtimeout;
+extern struct sockaddr_in peeraddr; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+extern FILE *file;
 
-#define PKTSIZE    SEGSIZE+4
-char    ackbuf[PKTSIZE];
-int timeout;
-jmp_buf toplevel;
-jmp_buf timeoutbuf;
+char ackbuf[PKTSIZE];
 
-static void nak(int);
-static int makerequest(int, const char *, struct tftphdr *, const char *);
-static void printstats(const char *, unsigned long);
-static void startclock(void);
-static void stopclock(void);
-static void timer(int);
-static void tpacket(const char *, struct tftphdr *, int);
+struct timeval tstart;
+struct timeval tstop;
 
+struct errmsg {
+ int e_code;
+ char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { -1, NULL }
+};
+
+static int makerequest(int, const char *, struct tftphdr *, const char *);
+static void nak(int);
+static void tpacket(const char *, struct tftphdr *, int);
+static void startclock(void);
+static void stopclock(void);
+static void printstats(const char *, unsigned long);
+static void printtimeout(void);
+
 /*
  * Send the requested file.
  */
 void
 sendfile(int fd, char *name, char *mode)
 {
- struct tftphdr *dp, *ap;   /* data and ack packets */
- volatile int block, size, convert;
- volatile unsigned long amount;
+ struct tftphdr *dp, *ap; /* data and ack packets */
  struct sockaddr_in from;
- int n, fromlen;
- FILE *file;
+ struct pollfd pfd[1];
+ volatile int convert; /* true if converting crlf -> lf */
+ volatile int block, size;
+ volatile unsigned long amount;
+ int n, nfds, error, fromlen, timeouts;
 
  startclock(); /* start stat's clock */
  dp = r_init(); /* reset fillbuf/read-ahead code */
@@ -105,12 +121,11 @@
  block = 0;
  amount = 0;
 
- signal(SIGALRM, timer);
  do {
- if (block == 0)
+ /* read data from file */
+ if (!block)
  size = makerequest(WRQ, name, dp, mode) - 4;
  else {
- /* size = read(fd, dp->th_data, SEGSIZE); */
  size = readit(file, &dp, convert);
  if (size < 0) {
  nak(errno + 100);
@@ -119,61 +134,85 @@
  dp->th_opcode = htons((u_short)DATA);
  dp->th_block = htons((u_short)block);
  }
- timeout = 0;
- (void) setjmp(timeoutbuf);
-send_data:
- if (trace)
- tpacket("sent", dp, size + 4);
- n = sendto(f, dp, size + 4, 0,
-    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
- if (n != size + 4) {
- warn("sendto");
- goto abort;
- }
- read_ahead(file, convert);
- for ( ; ; ) {
- alarm(rexmtval);
- do {
- fromlen = sizeof(from);
- n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
-    (struct sockaddr *)&from, &fromlen);
- } while (n <= 0);
- alarm(0);
- if (n < 0) {
+
+ /* send data to server and wait for server ACK */
+ for (timeouts = 0, error = 0;;) {
+ if (timeouts == maxtimeout) {
+ printtimeout();
+ goto abort;
+ }
+
+ if (!error) {
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ if (sendto(f, dp, size + 4, 0,
+        (struct sockaddr *)&peeraddr,
+    sizeof(peeraddr)) != size + 4) {
+ warn("sendto");
+ goto abort;
+ }
+ read_ahead(file, convert);
+ }
+ error = 0;
+
+ pfd[0].fd = f;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts++;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("poll");
+ goto abort;
+ }
+ fromlen = sizeof(from);
+ n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+    (struct sockaddr *)&from, &fromlen);
+ if (n == 0) {
  warn("recvfrom");
  goto abort;
  }
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("recvfrom");
+ goto abort;
+ }
  peeraddr.sin_port = from.sin_port; /* added */
  if (trace)
  tpacket("received", ap, n);
- /* should verify packet came from server */
  ap->th_opcode = ntohs(ap->th_opcode);
  ap->th_block = ntohs(ap->th_block);
+
  if (ap->th_opcode == ERROR) {
- printf("Error code %d: %s\n", ap->th_code,
-    ap->th_msg);
+ printf("Error code %d: %s\n",
+    ap->th_code, ap->th_msg);
  goto abort;
  }
  if (ap->th_opcode == ACK) {
  int j;
-
- if (ap->th_block == block) {
+ if (ap->th_block == block)
  break;
- }
- /* On an error, try to synchronize
- * both sides.
- */
+ /* re-synchronize with other side */
  j = synchnet(f);
  if (j && trace)
  printf("discarded %d packets\n", j);
- if (ap->th_block == (block-1))
- goto send_data;
+ if (ap->th_block == (block - 1))
+ continue;
  }
+ error = 1; /* FALLTHROUGH */
  }
+
  if (block > 0)
  amount += size;
  block++;
  } while (size == SEGSIZE || block == 1);
+
 abort:
  fclose(file);
  stopclock();
@@ -187,25 +226,25 @@
 void
 recvfile(int fd, char *name, char *mode)
 {
- struct tftphdr *dp, *ap;
- volatile int block, size, firsttrip;
- volatile unsigned long amount;
+ struct tftphdr *dp, *ap; /* data and ack packets */
  struct sockaddr_in from;
- int n, fromlen;
- FILE *file;
+ struct pollfd pfd[1];
  volatile int convert; /* true if converting crlf -> lf */
+ volatile int block, size, firsttrip;
+ volatile unsigned long amount;
+ int n, nfds, error, fromlen, timeouts;
 
- startclock();
- dp = w_init();
+ startclock(); /* start stat's clock */
+ dp = w_init(); /* reset fillbuf/read-ahead code */
  ap = (struct tftphdr *)ackbuf;
  file = fdopen(fd, "w");
  convert = !strcmp(mode, "netascii");
  block = 1;
- firsttrip = 1;
  amount = 0;
+ firsttrip = 1;
 
- signal(SIGALRM, timer);
  do {
+ /* create new ACK packet */
  if (firsttrip) {
  size = makerequest(RRQ, name, ap, mode);
  firsttrip = 0;
@@ -215,58 +254,81 @@
  size = 4;
  block++;
  }
- timeout = 0;
- (void) setjmp(timeoutbuf);
-send_ack:
- if (trace)
- tpacket("sent", ap, size);
- if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
-    sizeof(peeraddr)) != size) {
- alarm(0);
- warn("sendto");
- goto abort;
- }
- write_behind(file, convert);
- for ( ; ; ) {
- alarm(rexmtval);
- do  {
- fromlen = sizeof(from);
- n = recvfrom(f, dp, PKTSIZE, 0,
-    (struct sockaddr *)&from, &fromlen);
- } while (n <= 0);
- alarm(0);
- if (n < 0) {
+
+ /* send ACK to server and wait for server data */
+ for (timeouts = 0, error = 0;;) {
+ if (timeouts == maxtimeout) {
+ printtimeout();
+ goto abort;
+ }
+
+ if (!error) {
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf, size, 0,
+        (struct sockaddr *)&peeraddr,
+    sizeof(peeraddr)) != size) {
+ warn("sendto");
+ goto abort;
+ }
+ write_behind(file, convert);
+ }
+ error = 0;
+
+ pfd[0].fd = f;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts++;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("poll");
+ goto abort;
+ }
+ fromlen = sizeof(from);
+ n = recvfrom(f, dp, PKTSIZE, 0,
+    (struct sockaddr *)&from, &fromlen);
+ if (n == 0) {
  warn("recvfrom");
  goto abort;
  }
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("recvfrom");
+ goto abort;
+ }
  peeraddr.sin_port = from.sin_port; /* added */
  if (trace)
  tpacket("received", dp, n);
- /* should verify client address */
  dp->th_opcode = ntohs(dp->th_opcode);
  dp->th_block = ntohs(dp->th_block);
+
  if (dp->th_opcode == ERROR) {
- printf("Error code %d: %s\n", dp->th_code,
-    dp->th_msg);
+ printf("Error code %d: %s\n",
+    dp->th_code, dp->th_msg);
  goto abort;
  }
  if (dp->th_opcode == DATA) {
  int j;
-
- if (dp->th_block == block) {
- break; /* have next packet */
- }
- /* On an error, try to synchronize
- * both sides.
- */
+ if (dp->th_block == block)
+ break;
+ /* re-synchronize with other side */
  j = synchnet(f);
  if (j && trace)
  printf("discarded %d packets\n", j);
- if (dp->th_block == (block-1))
- goto send_ack; /* resend ack */
+ if (dp->th_block == (block - 1))
+ continue;
  }
+ error = 1; /* FALLTHROUGH */
  }
- /* size = write(fd, dp->th_data, n - 4); */
+
+ /* write data to file */
  size = writeit(file, &dp, n - 4, convert);
  if (size < 0) {
  nak(errno + 100);
@@ -274,12 +336,15 @@
  }
  amount += size;
  } while (size == SEGSIZE);
-abort: /* ok to ack, since user */
- ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
+
+abort:
+ /* ok to ack, since user has seen err msg */
+ ap->th_opcode = htons((u_short)ACK);
  ap->th_block = htons((u_short)block);
  (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
     sizeof(peeraddr));
- write_behind(file, convert); /* flush last buffer */
+ write_behind(file, convert); /* flush last buffer */
+
  fclose(file);
  stopclock();
  if (amount > 0)
@@ -303,21 +368,6 @@
  return (cp + len - (char *)tp);
 }
 
-struct errmsg {
- int e_code;
- char *e_msg;
-} errmsgs[] = {
- { EUNDEF, "Undefined error code" },
- { ENOTFOUND, "File not found" },
- { EACCESS, "Access violation" },
- { ENOSPACE, "Disk full or allocation exceeded" },
- { EBADOP, "Illegal TFTP operation" },
- { EBADID, "Unknown transfer ID" },
- { EEXISTS, "File already exists" },
- { ENOUSER, "No such user" },
- { -1, NULL }
-};
-
 /*
  * Send a nak packet (error message).
  * Error code passed in is one of the
@@ -355,7 +405,7 @@
 tpacket(const char *s, struct tftphdr *tp, int n)
 {
  static char *opcodes[] =
-   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
+    { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
  char *cp, *file;
  u_short op = ntohs(tp->th_opcode);
 
@@ -363,8 +413,8 @@
  printf("%s opcode=%x ", s, op);
  else
  printf("%s %s ", s, opcodes[op]);
- switch (op) {
 
+ switch (op) {
  case RRQ:
  case WRQ:
  n -= 2;
@@ -372,36 +422,28 @@
  cp = strchr(cp, '\0');
  printf("<file=%s, mode=%s>\n", file, cp + 1);
  break;
-
  case DATA:
  printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
  break;
-
  case ACK:
  printf("<block=%d>\n", ntohs(tp->th_block));
  break;
-
  case ERROR:
  printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
  break;
  }
 }
 
-struct timeval tstart;
-struct timeval tstop;
-
 static void
 startclock(void)
 {
-
- (void)gettimeofday(&tstart, NULL);
+ (void) gettimeofday(&tstart, NULL);
 }
 
 static void
 stopclock(void)
 {
-
- (void)gettimeofday(&tstop, NULL);
+ (void) gettimeofday(&tstop, NULL);
 }
 
 static void
@@ -410,26 +452,17 @@
  double delta;
 
  /* compute delta in 1/10's second units */
- delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
- ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
- delta = delta/10.;      /* back to seconds */
+ delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) -
+    ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000));
+ delta = delta / 10.; /* back to seconds */
  printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
  if (verbose)
- printf(" [%.0f bits/sec]", (amount*8.)/delta);
+ printf(" [%.0f bits/sec]", (amount * 8.) / delta);
  putchar('\n');
 }
 
 static void
-timer(int sig)
+printtimeout(void)
 {
- int save_errno = errno;
-
- timeout += rexmtval;
- if (timeout >= maxtimeout) {
- printf("Transfer timed out.\n");
- errno = save_errno;
- longjmp(toplevel, -1);
- }
- errno = save_errno;
- longjmp(timeoutbuf, 1);
+ printf("Transfer timed out.\n");
 }

Reply | Threaded
Open this post in threaded view
|

Re: tftp diff

Theo de Raadt
> The setjmp(3) in main.c which is used when ctrl-c is catched (SIGINT)
> is kept which is legitim IMO for that use and done this way by a lot
> of command line programs (ftp, ed, restore etc.) to return to the
> command prompt no matter which routine was active (e.g. blocking
> fgets(3)).

Well, just because lots of programs use that method does not make it
legitimate, because in the end I must say this: IT IS STILL NOT SAFE.

Think about it.

When that ^C comes in, is it not possible that our code is currently running
inside malloc()?  Just picking an example here, but what about stdio?  Or
who knows what else?

So then the signal comes in, and we longjmp back into main, and now what?

Now your malloc heap is unsafe.  Or stdio is in an inconsistant state.
Or who knows what else.

And now the code WILL crash later on.  And there is no solution to
this besides adding signal blocking stubs all through the code, for
everytime you wish to do something which might conceiveably not be
re-entrant.

So while it might be what a lot of programs do, my point is that it
is 100% unsafe -- in all cases -- to ever use setjmp.   Well, I have
actually used it safely in one program before, but the costs are very
high because you really must do signal blocks around just about any
sequence of code which calls a function which is NOT LISTED in signal.h

This is what I call a reverse signal race.

Reply | Threaded
Open this post in threaded view
|

Re: tftp diff

Marcus Glocker
After reading your explanation I agree with you.

Attached the reworked diff which kills also the last setjmp() and
handles ctrl-c by catching it with a poll() loop wrapped in front
of the fgets() call.

Tested it and works OK for me.

On Thu, Apr 27, 2006 at 12:25:08PM -0600, Theo de Raadt wrote:

> > The setjmp(3) in main.c which is used when ctrl-c is catched (SIGINT)
> > is kept which is legitim IMO for that use and done this way by a lot
> > of command line programs (ftp, ed, restore etc.) to return to the
> > command prompt no matter which routine was active (e.g. blocking
> > fgets(3)).
>
> Well, just because lots of programs use that method does not make it
> legitimate, because in the end I must say this: IT IS STILL NOT SAFE.
>
> Think about it.
>
> When that ^C comes in, is it not possible that our code is currently running
> inside malloc()?  Just picking an example here, but what about stdio?  Or
> who knows what else?
>
> So then the signal comes in, and we longjmp back into main, and now what?
>
> Now your malloc heap is unsafe.  Or stdio is in an inconsistant state.
> Or who knows what else.
>
> And now the code WILL crash later on.  And there is no solution to
> this besides adding signal blocking stubs all through the code, for
> everytime you wish to do something which might conceiveably not be
> re-entrant.
>
> So while it might be what a lot of programs do, my point is that it
> is 100% unsafe -- in all cases -- to ever use setjmp.   Well, I have
> actually used it safely in one program before, but the costs are very
> high because you really must do signal blocks around just about any
> sequence of code which calls a function which is NOT LISTED in signal.h
>
> This is what I call a reverse signal race.

--
Marcus Glocker, [hidden email], http://www.nazgul.ch -----------------
diff -urN -x CVS src/usr.bin/tftp.orig/main.c src/usr.bin/tftp/main.c
--- src/usr.bin/tftp.orig/main.c Wed Apr 26 12:36:12 2006
+++ src/usr.bin/tftp/main.c Fri Apr 28 20:17:26 2006
@@ -48,34 +48,35 @@
 /*
  * TFTP User Program -- Command Interface.
  */
+
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/file.h>
 
 #include <netinet/in.h>
-
 #include <arpa/inet.h>
 
 #include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
-#include <setjmp.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <err.h>
+#include <poll.h>
 
 #include "extern.h"
 
 #define TIMEOUT 5 /* secs between rexmt's */
 #define LBUFLEN 200 /* size of input buffer */
 #define MAXARGV 20
+#define HELPINDENT (sizeof("connect"))
 
 struct sockaddr_in peeraddr;
 int f;
-short   port;
+short port;
 int trace;
 int verbose;
 int connected;
@@ -84,10 +85,15 @@
 int margc;
 char *margv[MAXARGV+1];
 char *prompt = "tftp";
-jmp_buf toplevel;
 void intr(int);
 struct servent *sp;
+int rexmtval = TIMEOUT;
+int maxtimeout = 5 * TIMEOUT;
+char hostname[MAXHOSTNAMELEN];
+FILE *file = NULL;
 
+volatile sig_atomic_t intrflag = 0;
+
 void get(int, char **);
 void help(int, char **);
 void modecmd(int, char **);
@@ -101,6 +107,7 @@
 void settrace(int, char **);
 void setverbose(int, char **);
 void status(int, char **);
+int readcmd(char *, int, FILE *);
 
 static __dead void command(void);
 
@@ -109,8 +116,6 @@
 static void putusage(char *);
 static void settftpmode(char *);
 
-#define HELPINDENT (sizeof("connect"))
-
 struct cmd {
  char *name;
  char *help;
@@ -128,8 +133,8 @@
 char sthelp[] = "show current status";
 char xhelp[] = "set per-packet retransmission timeout";
 char ihelp[] = "set total retransmission timeout";
-char    ashelp[] = "set mode to netascii";
-char    bnhelp[] = "set mode to octet";
+char ashelp[] = "set mode to netascii";
+char bnhelp[] = "set mode to octet";
 
 struct cmd cmdtab[] = {
  { "connect", chelp, setpeer },
@@ -144,10 +149,24 @@
  { "ascii",      ashelp,         setascii },
  { "rexmt", xhelp, setrexmt },
  { "timeout", ihelp, settimeout },
+ { "help", hhelp, help },
  { "?", hhelp, help },
  { NULL, NULL, NULL }
 };
 
+struct modes {
+ char *m_name;
+ char *m_mode;
+} modes[] = {
+ { "ascii", "netascii" },
+ { "netascii", "netascii" },
+ { "binary", "octet" },
+ { "image", "octet" },
+ { "octet", "octet" },
+/* { "mail", "mail" }, */
+ { NULL, NULL }
+};
+
 struct cmd *getcmd(char *);
 char *tail(char *);
 
@@ -156,31 +175,34 @@
 {
  struct sockaddr_in s_in;
 
+ /* socket, bind */
  sp = getservbyname("tftp", "udp");
  if (sp == 0)
  errx(1, "udp/tftp: unknown service");
  f = socket(AF_INET, SOCK_DGRAM, 0);
  if (f < 0)
  err(3, "socket");
- bzero((char *)&s_in, sizeof (s_in));
+ bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family = AF_INET;
- if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0)
+ if (bind(f, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
  err(1, "bind");
- strlcpy(mode, "netascii", sizeof mode);
- signal(SIGINT, intr);
- if (argc > 1) {
- if (setjmp(toplevel) != 0)
- exit(0);
+
+ /* set default transfer mode */
+ strlcpy(mode, "netascii", sizeof(mode));
+
+ /* set peer if given */
+ if (argc > 1)
  setpeer(argc, argv);
- }
- if (setjmp(toplevel) != 0)
- (void)putchar('\n');
+
+ /* catch SIGINT */
+ signal(SIGINT, intr);
+
+ /* command prompt */
  command();
+
  return (0);
 }
 
-char    hostname[MAXHOSTNAMELEN];
-
 void
 setpeer(int argc, char *argv[])
 {
@@ -189,7 +211,7 @@
  if (argc < 2) {
  strlcpy(line, "Connect ", sizeof line);
  printf("(to) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ readcmd(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -227,19 +249,6 @@
  connected = 1;
 }
 
-struct modes {
- char *m_name;
- char *m_mode;
-} modes[] = {
- { "ascii", "netascii" },
- { "netascii",   "netascii" },
- { "binary",     "octet" },
- { "image",      "octet" },
- { "octet",     "octet" },
-/*      { "mail",       "mail" },       */
- { NULL, NULL }
-};
-
 void
 modecmd(int argc, char *argv[])
 {
@@ -276,14 +285,12 @@
 void
 setbinary(int argc, char *argv[])
 {
-
  settftpmode("octet");
 }
 
 void
 setascii(int argc, char *argv[])
 {
-
  settftpmode("netascii");
 }
 
@@ -295,7 +302,6 @@
  printf("mode set to %s\n", mode);
 }
 
-
 /*
  * Send file(s).
  */
@@ -309,7 +315,7 @@
  if (argc < 2) {
  strlcpy(line, "send ", sizeof line);
  printf("(file) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -362,8 +368,10 @@
  return;
  }
 
- /* this assumes the target is a directory */
- /* on a remote unix system.  hmmmm.  */
+ /*
+ * this assumes the target is a directory on
+ * on a remote unix system.  hmmmm.
+ */
  for (n = 1; n < argc - 1; n++) {
  if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
  err(1, "asprintf");
@@ -386,7 +394,8 @@
 putusage(char *s)
 {
  printf("usage: %s file [[host:]remotename]\n", s);
- printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
+ printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
+    s);
 }
 
 /*
@@ -403,7 +412,7 @@
  if (argc < 2) {
  strlcpy(line, "get ", sizeof line);
  printf("(files) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ readcmd(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -474,8 +483,6 @@
  printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
 }
 
-int rexmtval = TIMEOUT;
-
 void
 setrexmt(int argc, char *argv[])
 {
@@ -484,7 +491,7 @@
  if (argc < 2) {
  strlcpy(line, "Rexmt-timeout ", sizeof line);
  printf("(value) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ readcmd(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -501,8 +508,6 @@
  rexmtval = t;
 }
 
-int maxtimeout = 5 * TIMEOUT;
-
 void
 settimeout(int argc, char *argv[])
 {
@@ -511,7 +516,7 @@
  if (argc < 2) {
  strlcpy(line, "Maximum-timeout ", sizeof line);
  printf("(value) ");
- fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
+ readcmd(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
  if (makeargv())
  return;
  argc = margc;
@@ -544,10 +549,7 @@
 void
 intr(int signo)
 {
-
- signal(SIGALRM, SIG_IGN);
- alarm(0);
- longjmp(toplevel, -1);
+ intrflag = 1;
 }
 
 char *
@@ -576,13 +578,8 @@
 
  for (;;) {
  printf("%s> ", prompt);
- if (fgets(line, LBUFLEN, stdin) == 0) {
- if (feof(stdin)) {
- exit(0);
- } else {
- continue;
- }
- }
+ if (readcmd(line, LBUFLEN, stdin) < 1)
+ continue;
  if ((line[0] == 0) || (line[0] == '\n'))
  continue;
  if (makeargv())
@@ -612,6 +609,7 @@
  longest = 0;
  nmatches = 0;
  found = 0;
+ intrflag = 0;
  for (c = cmdtab; (p = c->name) != NULL; c++) {
  for (q = name; *q == *p++; q++)
  if (*q == 0) /* exact match? */
@@ -666,7 +664,6 @@
 void
 quit(int argc, char *argv[])
 {
-
  exit(0);
 }
 
@@ -709,4 +706,34 @@
 {
  verbose = !verbose;
  printf("Verbose mode %s.\n", verbose ? "on" : "off");
+}
+
+int
+readcmd(char *input, int len, FILE *stream)
+{
+ int nfds;
+ struct pollfd pfd[1];
+
+ fflush(stdout);
+
+ pfd[0].fd = 0;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, -1);
+ if (nfds == -1) {
+ if (intrflag) {
+ intrflag = 0;
+ putchar('\n');
+ return (0);
+ }
+ exit(1);
+ }
+
+ if (fgets(input, len, stream) == NULL) {
+ if (feof(stdin))
+ exit(0);
+ else
+ return (-1);
+ }
+
+ return (1);
 }
diff -urN -x CVS src/usr.bin/tftp.orig/tftp.c src/usr.bin/tftp/tftp.c
--- src/usr.bin/tftp.orig/tftp.c Tue Apr 25 22:08:37 2006
+++ src/usr.bin/tftp/tftp.c Fri Apr 28 20:17:46 2006
@@ -42,16 +42,16 @@
 /*
  * TFTP User Program -- Protocol Machines
  */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 
 #include <netinet/in.h>
-
 #include <arpa/tftp.h>
 
 #include <errno.h>
-#include <setjmp.h>
+#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stddef.h>
@@ -62,40 +62,57 @@
 #include "extern.h"
 #include "tftpsubs.h"
 
+#define PKTSIZE SEGSIZE + 4
 
-extern  struct sockaddr_in peeraddr; /* filled in by main */
-extern  int     f; /* the opened socket */
-extern  int     trace;
-extern  int     verbose;
-extern  int     rexmtval;
-extern  int     maxtimeout;
+extern struct sockaddr_in peeraddr; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+extern FILE *file;
+extern volatile sig_atomic_t intrflag;
 
-#define PKTSIZE    SEGSIZE+4
-char    ackbuf[PKTSIZE];
-int timeout;
-jmp_buf toplevel;
-jmp_buf timeoutbuf;
+char ackbuf[PKTSIZE];
 
-static void nak(int);
-static int makerequest(int, const char *, struct tftphdr *, const char *);
-static void printstats(const char *, unsigned long);
-static void startclock(void);
-static void stopclock(void);
-static void timer(int);
-static void tpacket(const char *, struct tftphdr *, int);
+struct timeval tstart;
+struct timeval tstop;
 
+struct errmsg {
+ int e_code;
+ char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { -1, NULL }
+};
+
+static int makerequest(int, const char *, struct tftphdr *, const char *);
+static void nak(int);
+static void tpacket(const char *, struct tftphdr *, int);
+static void startclock(void);
+static void stopclock(void);
+static void printstats(const char *, unsigned long);
+static void printtimeout(void);
+
 /*
  * Send the requested file.
  */
 void
 sendfile(int fd, char *name, char *mode)
 {
- struct tftphdr *dp, *ap;   /* data and ack packets */
- volatile int block, size, convert;
- volatile unsigned long amount;
+ struct tftphdr *dp, *ap; /* data and ack packets */
  struct sockaddr_in from;
- int n, fromlen;
- FILE *file;
+ struct pollfd pfd[1];
+ unsigned long amount;
+ int convert; /* true if converting crlf -> lf */
+ int n, nfds, error, fromlen, timeouts, block, size;
 
  startclock(); /* start stat's clock */
  dp = r_init(); /* reset fillbuf/read-ahead code */
@@ -105,12 +122,11 @@
  block = 0;
  amount = 0;
 
- signal(SIGALRM, timer);
  do {
- if (block == 0)
+ /* read data from file */
+ if (!block)
  size = makerequest(WRQ, name, dp, mode) - 4;
  else {
- /* size = read(fd, dp->th_data, SEGSIZE); */
  size = readit(file, &dp, convert);
  if (size < 0) {
  nak(errno + 100);
@@ -119,66 +135,93 @@
  dp->th_opcode = htons((u_short)DATA);
  dp->th_block = htons((u_short)block);
  }
- timeout = 0;
- (void) setjmp(timeoutbuf);
-send_data:
- if (trace)
- tpacket("sent", dp, size + 4);
- n = sendto(f, dp, size + 4, 0,
-    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
- if (n != size + 4) {
- warn("sendto");
- goto abort;
- }
- read_ahead(file, convert);
- for ( ; ; ) {
- alarm(rexmtval);
- do {
- fromlen = sizeof(from);
- n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
-    (struct sockaddr *)&from, &fromlen);
- } while (n <= 0);
- alarm(0);
- if (n < 0) {
+
+ /* send data to server and wait for server ACK */
+ for (timeouts = 0, error = 0; !intrflag;) {
+ if (timeouts == maxtimeout) {
+ printtimeout();
+ goto abort;
+ }
+
+ if (!error) {
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ if (sendto(f, dp, size + 4, 0,
+        (struct sockaddr *)&peeraddr,
+    sizeof(peeraddr)) != size + 4) {
+ warn("sendto");
+ goto abort;
+ }
+ read_ahead(file, convert);
+ }
+ error = 0;
+
+ pfd[0].fd = f;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts++;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("poll");
+ goto abort;
+ }
+ fromlen = sizeof(from);
+ n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+    (struct sockaddr *)&from, &fromlen);
+ if (n == 0) {
  warn("recvfrom");
  goto abort;
  }
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("recvfrom");
+ goto abort;
+ }
  peeraddr.sin_port = from.sin_port; /* added */
  if (trace)
  tpacket("received", ap, n);
- /* should verify packet came from server */
  ap->th_opcode = ntohs(ap->th_opcode);
  ap->th_block = ntohs(ap->th_block);
+
  if (ap->th_opcode == ERROR) {
- printf("Error code %d: %s\n", ap->th_code,
-    ap->th_msg);
+ printf("Error code %d: %s\n",
+    ap->th_code, ap->th_msg);
  goto abort;
  }
  if (ap->th_opcode == ACK) {
  int j;
-
- if (ap->th_block == block) {
+ if (ap->th_block == block)
  break;
- }
- /* On an error, try to synchronize
- * both sides.
- */
+ /* re-synchronize with other side */
  j = synchnet(f);
  if (j && trace)
  printf("discarded %d packets\n", j);
- if (ap->th_block == (block-1))
- goto send_data;
+ if (ap->th_block == (block - 1))
+ continue;
  }
+ error = 1; /* received packet does not match */
  }
+
  if (block > 0)
  amount += size;
  block++;
- } while (size == SEGSIZE || block == 1);
+ } while ((size == SEGSIZE || block == 1) && !intrflag);
+
 abort:
  fclose(file);
  stopclock();
- if (amount > 0)
+ if (amount > 0) {
+ if (intrflag)
+ putchar('\n');
  printstats("Sent", amount);
+ }
 }
 
 /*
@@ -187,25 +230,25 @@
 void
 recvfile(int fd, char *name, char *mode)
 {
- struct tftphdr *dp, *ap;
- volatile int block, size, firsttrip;
- volatile unsigned long amount;
+ struct tftphdr *dp, *ap; /* data and ack packets */
  struct sockaddr_in from;
- int n, fromlen;
- FILE *file;
- volatile int convert; /* true if converting crlf -> lf */
+ struct pollfd pfd[1];
+ unsigned long amount;
+ int convert; /* true if converting crlf -> lf */
+ int n, nfds, error, fromlen, timeouts, block, size, firsttrip;
 
- startclock();
- dp = w_init();
+ startclock(); /* start stat's clock */
+ dp = w_init(); /* reset fillbuf/read-ahead code */
  ap = (struct tftphdr *)ackbuf;
  file = fdopen(fd, "w");
  convert = !strcmp(mode, "netascii");
+ n = 0;
  block = 1;
- firsttrip = 1;
  amount = 0;
+ firsttrip = 1;
 
- signal(SIGALRM, timer);
  do {
+ /* create new ACK packet */
  if (firsttrip) {
  size = makerequest(RRQ, name, ap, mode);
  firsttrip = 0;
@@ -215,75 +258,104 @@
  size = 4;
  block++;
  }
- timeout = 0;
- (void) setjmp(timeoutbuf);
-send_ack:
- if (trace)
- tpacket("sent", ap, size);
- if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
-    sizeof(peeraddr)) != size) {
- alarm(0);
- warn("sendto");
- goto abort;
- }
- write_behind(file, convert);
- for ( ; ; ) {
- alarm(rexmtval);
- do  {
- fromlen = sizeof(from);
- n = recvfrom(f, dp, PKTSIZE, 0,
-    (struct sockaddr *)&from, &fromlen);
- } while (n <= 0);
- alarm(0);
- if (n < 0) {
+
+ /* send ACK to server and wait for server data */
+ for (timeouts = 0, error = 0; !intrflag;) {
+ if (timeouts == maxtimeout) {
+ printtimeout();
+ goto abort;
+ }
+
+ if (!error) {
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf, size, 0,
+        (struct sockaddr *)&peeraddr,
+    sizeof(peeraddr)) != size) {
+ warn("sendto");
+ goto abort;
+ }
+ write_behind(file, convert);
+ }
+ error = 0;
+
+ pfd[0].fd = f;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts++;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("poll");
+ goto abort;
+ }
+ fromlen = sizeof(from);
+ n = recvfrom(f, dp, PKTSIZE, 0,
+    (struct sockaddr *)&from, &fromlen);
+ if (n == 0) {
  warn("recvfrom");
  goto abort;
  }
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ warn("recvfrom");
+ goto abort;
+ }
  peeraddr.sin_port = from.sin_port; /* added */
  if (trace)
  tpacket("received", dp, n);
- /* should verify client address */
  dp->th_opcode = ntohs(dp->th_opcode);
  dp->th_block = ntohs(dp->th_block);
+
  if (dp->th_opcode == ERROR) {
- printf("Error code %d: %s\n", dp->th_code,
-    dp->th_msg);
+ printf("Error code %d: %s\n",
+    dp->th_code, dp->th_msg);
  goto abort;
  }
  if (dp->th_opcode == DATA) {
  int j;
-
- if (dp->th_block == block) {
- break; /* have next packet */
- }
- /* On an error, try to synchronize
- * both sides.
- */
+ if (dp->th_block == block)
+ break;
+ /* re-synchronize with other side */
  j = synchnet(f);
  if (j && trace)
  printf("discarded %d packets\n", j);
- if (dp->th_block == (block-1))
- goto send_ack; /* resend ack */
+ if (dp->th_block == (block - 1))
+ continue;
  }
+ error = 1; /* received packet does not match */
  }
- /* size = write(fd, dp->th_data, n - 4); */
+
+ /* write data to file */
  size = writeit(file, &dp, n - 4, convert);
  if (size < 0) {
  nak(errno + 100);
  break;
  }
  amount += size;
- } while (size == SEGSIZE);
-abort: /* ok to ack, since user */
- ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
+ } while (size == SEGSIZE && !intrflag);
+
+abort:
+ /* ok to ack, since user has seen err msg */
+ ap->th_opcode = htons((u_short)ACK);
  ap->th_block = htons((u_short)block);
  (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
     sizeof(peeraddr));
- write_behind(file, convert); /* flush last buffer */
+ write_behind(file, convert); /* flush last buffer */
+
  fclose(file);
  stopclock();
- if (amount > 0)
+ if (amount > 0) {
+ if (intrflag)
+ putchar('\n');
  printstats("Received", amount);
+ }
 }
 
 static int
@@ -303,21 +375,6 @@
  return (cp + len - (char *)tp);
 }
 
-struct errmsg {
- int e_code;
- char *e_msg;
-} errmsgs[] = {
- { EUNDEF, "Undefined error code" },
- { ENOTFOUND, "File not found" },
- { EACCESS, "Access violation" },
- { ENOSPACE, "Disk full or allocation exceeded" },
- { EBADOP, "Illegal TFTP operation" },
- { EBADID, "Unknown transfer ID" },
- { EEXISTS, "File already exists" },
- { ENOUSER, "No such user" },
- { -1, NULL }
-};
-
 /*
  * Send a nak packet (error message).
  * Error code passed in is one of the
@@ -355,7 +412,7 @@
 tpacket(const char *s, struct tftphdr *tp, int n)
 {
  static char *opcodes[] =
-   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
+    { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
  char *cp, *file;
  u_short op = ntohs(tp->th_opcode);
 
@@ -363,8 +420,8 @@
  printf("%s opcode=%x ", s, op);
  else
  printf("%s %s ", s, opcodes[op]);
- switch (op) {
 
+ switch (op) {
  case RRQ:
  case WRQ:
  n -= 2;
@@ -372,36 +429,28 @@
  cp = strchr(cp, '\0');
  printf("<file=%s, mode=%s>\n", file, cp + 1);
  break;
-
  case DATA:
  printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
  break;
-
  case ACK:
  printf("<block=%d>\n", ntohs(tp->th_block));
  break;
-
  case ERROR:
  printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
  break;
  }
 }
 
-struct timeval tstart;
-struct timeval tstop;
-
 static void
 startclock(void)
 {
-
- (void)gettimeofday(&tstart, NULL);
+ (void) gettimeofday(&tstart, NULL);
 }
 
 static void
 stopclock(void)
 {
-
- (void)gettimeofday(&tstop, NULL);
+ (void) gettimeofday(&tstop, NULL);
 }
 
 static void
@@ -410,26 +459,17 @@
  double delta;
 
  /* compute delta in 1/10's second units */
- delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
- ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
- delta = delta/10.;      /* back to seconds */
+ delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) -
+    ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000));
+ delta = delta / 10.; /* back to seconds */
  printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
  if (verbose)
- printf(" [%.0f bits/sec]", (amount*8.)/delta);
+ printf(" [%.0f bits/sec]", (amount * 8.) / delta);
  putchar('\n');
 }
 
 static void
-timer(int sig)
+printtimeout(void)
 {
- int save_errno = errno;
-
- timeout += rexmtval;
- if (timeout >= maxtimeout) {
- printf("Transfer timed out.\n");
- errno = save_errno;
- longjmp(toplevel, -1);
- }
- errno = save_errno;
- longjmp(timeoutbuf, 1);
+ printf("Transfer timed out.\n");
 }