File atftp-0.7-sorcerers_apprentice.patch of Package atftp

Index: tftp_file.c
===================================================================
--- tftp_file.c.orig
+++ tftp_file.c
@@ -49,6 +49,7 @@
 #define NB_BLOCK        2048
 
 extern int tftp_cancel;
+extern int tftp_prevent_sas;
 
 /*
  * Find a hole in the file bitmap.
@@ -629,6 +631,7 @@ int tftp_send_file(struct client_data *d
      int timeout_state = state; /* what state should we go on when timeout */
      int result;
      long block_number = 0;
+     long last_requested_block = -1;
      long last_block = -1;
      int data_size;             /* size of data received */
      int sockfd = data->sockfd; /* just to simplify calls */
@@ -791,6 +794,18 @@ int tftp_send_file(struct client_data *d
                     }
 		    block_number = tftp_rollover_blocknumber(
 			ntohs(tftphdr->th_block), prev_block_number, 0);
+
+                    /* if turned on, check whether the block request isn't already fulfilled */
+                    if (tftp_prevent_sas) {
+                         if (last_requested_block >= block_number) {
+                              if (data->trace)
+                                   fprintf(stderr, "received duplicated ACK <block: %ld >= %ld>\n",
+                                           last_requested_block, block_number);
+                              break;
+                         } else
+                              last_requested_block = block_number;
+                    }
+
                     if (data->trace)
                          fprintf(stderr, "received ACK <block: %ld>\n",
                                  block_number);
Index: tftpd_file.c
===================================================================
--- tftpd_file.c.orig
+++ tftpd_file.c
@@ -55,6 +55,7 @@
 extern char directory[MAXLEN];
 /* read only except for the main thread */
 extern int tftpd_cancel;
+extern int tftpd_prevent_sas;
 
 #ifdef HAVE_PCRE
 extern tftpd_pcre_self_t *pcre_top;
@@ -407,6 +408,7 @@ int tftpd_send_file(struct thread_data *
      int timeout_state = state;
      int result;
      long block_number = 0;
+     long last_requested_block = -1;
      long last_block = -1;
      int data_size;
      struct sockaddr_storage *sa = &data->client_info->client;
@@ -818,6 +820,10 @@ int tftpd_send_file(struct thread_data *
                                           sockaddr_get_port(
                                                &client_info->client));
                                    sa = &client_info->client;
+
+                                   /* rewind the last_requested_block counter */
+                                   last_requested_block = -1;
+
                                    state = S_SEND_OACK;
                                    break;
                               }
@@ -903,6 +909,28 @@ int tftpd_send_file(struct thread_data *
                     if (data->trace)
                          logger(LOG_DEBUG, "received ACK <block: %ld>",
                                 block_number);
+
+                    /* if turned on, check whether the block request isn't already fulfilled */
+                    if (tftpd_prevent_sas) {
+                         /* multicast, block numbers could contain gaps */
+                         if (multicast) {
+                              if (last_requested_block >= block_number) {
+                                   if (data->trace)
+                                        logger(LOG_DEBUG, "received duplicated ACK <block: %d >= %d>", last_requested_block, block_number);
+                                   break;
+                              } else
+                                   last_requested_block = block_number;
+                              /* unicast, blocks should be requested one after another */
+                         } else {
+                              if (last_requested_block + 1 != block_number && last_requested_block != -1) {
+                                   if (data->trace)
+                                        logger(LOG_DEBUG, "received out of order ACK <block: %d != %d>", last_requested_block + 1, block_number);
+                                   break;
+                              } else
+                                   last_requested_block = block_number;
+                         }
+                    }
+
                     if ((last_block != -1) && (block_number > last_block))
                     {
                          state = S_END;
@@ -1001,6 +1029,8 @@ int tftpd_send_file(struct thread_data *
                          /* nedd to send an oack to that client */
                          state = S_SEND_OACK;                
                          fseek(fp, 0, SEEK_SET);
+			 /* reset the last block received counter */
+			 last_requested_block = -1;
                     }
                     else
                     {
Index: tftpd.c
===================================================================
--- tftpd.c.orig
+++ tftpd.c
@@ -65,6 +65,7 @@ int listen_local = 0;
 
 int tftpd_daemon = 0;           /* By default we are started by inetd */
 int tftpd_daemon_no_fork = 0;   /* For who want a false daemon mode */
+int tftpd_prevent_sas = 0;      /* For who don't want the sorcerer's apprentice syndrome */
 short tftpd_port = 69;          /* Port atftpd listen to */
 char tftpd_addr[MAXLEN] = "";   /* IP address atftpd binds to */
 
@@ -922,6 +923,7 @@ int tftpd_cmd_line_options(int argc, cha
           { "mtftp", 1, NULL, OPT_MTFTP },
           { "mtftp-port", 1, NULL, OPT_MTFTP_PORT },
 #endif
+          { "prevent-sas", 0, NULL, 'X' },
           { "no-source-port-checking", 0, NULL, OPT_PORT_CHECK },
           { "mcast-switch-client", 0, NULL, OPT_MCAST_SWITCH },
           { "version", 0, NULL, 'V' },
@@ -991,6 +993,9 @@ int tftpd_cmd_line_options(int argc, cha
           case 'N':
                tftpd_daemon_no_fork = 1;
                break;
+          case 'X':
+               tftpd_prevent_sas = 1;
+               break;
           case 'U':
                tmp = strtok(optarg, ".");
                if (tmp != NULL)
@@ -1223,6 +1228,7 @@ void tftpd_usage(void)
             "  --listen-local             : force listen on local network address\n"
             "  --daemon                   : run atftpd standalone (no inetd)\n"
             "  --no-fork                  : run as a daemon, don't fork\n"
+            "  --prevent-sas              : prevent Sorcerer's Apprentice Syndrome\n"
             "  --user <user[.group]>      : default is nobody\n"
             "  --group <group>            : default is nogroup\n"
             "  --port <port>              : port on which atftp listen\n"
Index: tftp.c
===================================================================
--- tftp.c.orig
+++ tftp.c
@@ -58,6 +58,7 @@
 /* defined as extern in tftp_file.c and mtftp_file.c, set by the signal
    handler */
 int tftp_cancel = 0;
+int tftp_prevent_sas = 0;
 
 /* local flags */
 int interactive = 1;            /* if false, we run in batch mode */
@@ -1006,6 +1007,7 @@ int tftp_cmd_line_options(int argc, char
 #endif
           { "mtftp", 1, NULL, '1'},
           { "no-source-port-checking", 0, NULL, '0'},
+          { "prevent-sas", 0, NULL, 'X'},
           { "verbose", 0, NULL, 'v'},
           { "trace", 0, NULL, 'd'},
 #if DEBUG
@@ -1115,6 +1117,9 @@ int tftp_cmd_line_options(int argc, char
           case '0':
                data.checkport = 0;
                break;
+          case 'X':
+               tftp_prevent_sas = 1;
+               break;
           case 'v':
                snprintf(string, sizeof(string), "verbose on");
                make_arg(string, &ac, &av);
@@ -1226,6 +1231,7 @@ void tftp_usage(void)
              "  --mtftp <\"name value\">   : set mtftp variable to value\n"
 #endif
              "  --no-source-port-checking: violate RFC, see man page\n"
+             "  --prevent-sas            : prevent Sorcerer's Apprentice Syndrome\n"
              "  --verbose                : set verbose mode on\n"
              "  --trace                  : set trace mode on\n"
 #if DEBUG
Index: atftpd.8
===================================================================
--- atftpd.8.orig
+++ atftpd.8
@@ -181,6 +181,14 @@ implication. Be aware that this option v
 option has effect only for non-multicast transfer.
 
 .TP
+.B \-\-prevent\-sas
+Address the Sorcerer's Apprentice Syndrome situation as requested by RFC 1350.
+This RFC requires repeated responses to a single packet to be
+rejected. Thus a block will only get retransmitted on a timeout.
+For backward compatibility, the default stays to ignore this RFC.
+So blocks get transmitted on every request.
+
+.TP
 .B \-\-mcast\-switch\-client
 This option allow the server to proceed with the next multicast client
 as soon as the current client timeout. When the current master client
Index: atftp.1
===================================================================
--- atftp.1.orig
+++ atftp.1
@@ -88,6 +88,14 @@ to configure client side port to use.
 See atftpd's man page.
 
 .TP
+.B \-\-prevent\-sas
+Address the Sorcerer's Apprentice Syndrome situation as requested by RFC 1350.
+This RFC requires repeated responses to a single packet to be
+rejected. Thus a block will only get retransmitted on a timeout.
+For backward compatibility, the default stays to ignore this RFC.
+So blocks get transmitted on every request.
+
+.TP
 .B \-\-verbose
 Instruct atftp to be verbose. It will print more information about
 what's going on.