diff --git a/SOURCES/tftp-doc.patch b/SOURCES/tftp-doc.patch
index 20352c9..f1cb511 100644
--- a/SOURCES/tftp-doc.patch
+++ b/SOURCES/tftp-doc.patch
@@ -25,3 +25,21 @@
  Specify the use of filename remapping.  The
  .I remap-file
  is a file containing the remapping rules.  See the section on filename
+@@ -243,7 +243,7 @@ option, but crash with an error if they
+ accepted by the server.
+ .SH "FILENAME REMAPPING"
+ The
+-.B \-\-mapfile
++.B \-\-map\-file
+ option specifies a file which contains filename remapping rules.  Each
+ non-comment line (comments begin with hash marks,
+ .BR # )
+@@ -395,7 +395,7 @@ flag is used to set up a chroot() enviro
+ once a connection has been set up.
+ .PP
+ Finally, the filename remapping
+-.RB ( \-\-mapfile
++.RB ( \-\-map\-file
+ flag) support can be used to provide a limited amount of additional
+ access control.
+ .SH "CONFORMING TO"
diff --git a/SOURCES/tftp-rewrite-macro.patch b/SOURCES/tftp-rewrite-macro.patch
new file mode 100644
index 0000000..98b0874
--- /dev/null
+++ b/SOURCES/tftp-rewrite-macro.patch
@@ -0,0 +1,44 @@
+Resolves: #1485943
+
+--- a/tftpd/tftpd.c       2017-08-28 09:12:11.681299000 +0200
++++ b/tftpd/tftpd.c       2017-08-28 13:30:03.109312000 +0200
+@@ -1388,24 +1388,25 @@
+             return strlen(p);
+ 
+     case 'x':
+-        if (output) {
+-            if (from.sa.sa_family == AF_INET) {
++        if (from.sa.sa_family == AF_INET) {
++            if (output) {
+                 sprintf(output, "%08lX",
+                     (unsigned long)ntohl(from.si.sin_addr.s_addr));
+-                l = 8;
++            }
++            l = 8;
+ #ifdef HAVE_IPV6
+-            } else {
+-                unsigned char *c = (unsigned char *)SOCKADDR_P(&from);
+-                p = tb;
+-                for (l = 0; l < 16; l++) {
+-                    sprintf(p, "%02X", *c);
+-                    c++;
+-                    p += 2;
+-                }
++        } else {
++            unsigned char *c = (unsigned char *)SOCKADDR_P(&from);
++            p = tb;
++            for (l = 0; l < 16; l++) {
++                sprintf(p, "%02X", *c);
++                c++;
++                p += 2;
++            }
++            if (output)
+                 strcpy(output, tb);
+-                l = strlen(tb);
++            l = strlen(tb);
+ #endif
+-            }
+         }
+         return l;
+ 
+
diff --git a/SOURCES/tftp-rfc7440-windowsize.patch b/SOURCES/tftp-rfc7440-windowsize.patch
new file mode 100644
index 0000000..d8d3163
--- /dev/null
+++ b/SOURCES/tftp-rfc7440-windowsize.patch
@@ -0,0 +1,1992 @@
+commit e46782908e7026f27ef92de52e47ec3720116125
+Author: Jan Synacek <jsynacek@redhat.com>
+Date:   Tue Nov 7 08:26:54 2017 +0100
+
+    implement rfc7440
+
+diff --git a/common/Makefile b/common/Makefile
+index a825213..d1e97b5 100644
+--- a/common/Makefile
++++ b/common/Makefile
+@@ -4,7 +4,7 @@ VERSION = $(shell cat ../version)
+ -include ../MCONFIG
+ include ../MRULES
+ 
+-OBJS = tftpsubs.$(O)
++OBJS = tftpsubs.$(O) common.$(O)
+ LIB  = libcommon.a
+ 
+ all: $(LIB)
+@@ -14,7 +14,7 @@ $(LIB): $(OBJS)
+ 	$(AR) $(LIB) $(OBJS)
+ 	$(RANLIB) $(LIB)
+ 
+-$(OBJS): tftpsubs.h
++$(OBJS): tftpsubs.h common.h
+ 
+ install:
+ 
+diff --git a/common/common.c b/common/common.c
+new file mode 100644
+index 0000000..a4e2fef
+--- /dev/null
++++ b/common/common.c
+@@ -0,0 +1,433 @@
++/*
++ * Copyright (c) 2017 Jan Synáček
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ *    must display the following acknowledgement:
++ *	This product includes software developed by the University of
++ *	California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include <poll.h>
++#include <stdarg.h>
++#include <syslog.h>
++#include "common.h"
++
++static char *pktbuf;
++static int verbose;
++
++const int SYNC_TIMEOUT = 50; /* ms */
++
++static void die(const char *fmt, ...)
++{
++    va_list ap;
++
++    va_start(ap, fmt);
++    fprintf(stderr, "fatal: ");
++    vfprintf(stderr, fmt, ap);
++    printf("\n");
++    va_end(ap);
++    exit(1);
++}
++
++int format_error(struct tftphdr *tp, char *error)
++{
++    int r = 0;
++
++    if (error)
++        r = snprintf(error, ERROR_MAXLEN, "Error code %d: %s", ntohs(tp->th_code), tp->th_msg);
++    return r;
++}
++
++void die_on_error(struct tftphdr *tp)
++{
++    char error[ERROR_MAXLEN];
++
++    snprintf(error, ERROR_MAXLEN, "Error code %d: %s", ntohs(tp->th_code), tp->th_msg);
++    fprintf(stderr, "%s\n", error);
++    exit(1);
++}
++
++void send_error(int sockfd, union sock_addr *to, const char *msg)
++{
++    char buf[516];
++    struct tftphdr *out = (struct tftphdr *)buf;
++    int len;
++
++    out->th_opcode = htons(ERROR);
++    out->th_code = htons(EUNDEF);
++
++    len = strlen(msg) + 1;
++    memset(buf, 0, 516);
++    memcpy(out->th_msg, msg, len > 511 ? 511 : len);
++    len += 4;
++
++    if (to) {
++        if (sendto(sockfd, out, len, 0, &to->sa, SOCKLEN(to)) != len)
++            die("send_error: sendto: %s", strerror(-errno));
++    } else {
++        if (send(sockfd, out, len, 0) != len)
++            die("send_error: send: %s", strerror(-errno));
++    }
++}
++
++static void _send_ack(int sockfd, union sock_addr *to, unsigned short block, int check_errors)
++{
++    struct tftphdr out;
++    out.th_opcode = htons(ACK);
++    out.th_block  = htons(block);
++
++    if (to) {
++        if (sendto(sockfd, &out, 4, 0, &to->sa, SOCKLEN(to)) != 4 && check_errors)
++            die("send_ack: sendto: %m");
++    } else {
++        if (send(sockfd, &out, 4, 0) != 4 && check_errors)
++            die("send_ack: send: %m");
++    }
++}
++
++void send_ack(int sockfd, union sock_addr *to, unsigned short block)
++{
++    _send_ack(sockfd, to, block, 1);
++}
++
++
++static size_t read_data(FILE *fp,
++                        int blocksize,
++                        unsigned short block,
++                        int seek,
++                        struct tftphdr *out)
++{
++    out->th_opcode = htons(DATA);
++    out->th_block  = htons(block);
++    if (seek)
++        (void) fseek(fp, seek, SEEK_CUR);
++    return fread(pktbuf + 4, 1, blocksize, fp);
++}
++
++static size_t write_data(FILE *fp, char *buf, size_t count, int convert)
++{
++    char wbuf[count];
++    size_t i = 0, cnt = 0;
++    static int was_cr = 0;
++
++    /* TODO: jsynacek: I don't think any conversion should take place...
++     * RFC 1350 says: "A host which receives netascii mode data must translate
++     * the data to its own format."
++     * That basically means nothing. What does "own format" even mean?
++     * The original implementation translated \r\n to \n and skipped \0 bytes,
++     * which aren't even legal in the netascii format.
++     * However, I believe that the file should remain as is before and after
++     * the transfer.
++     */
++    convert = 0;
++
++    if (convert == 0)
++        return fwrite(buf, 1, count, fp);
++
++    /* Working conversion. Leave it as dead code for now. */
++    if (was_cr && buf[0] == '\n') {
++        wbuf[cnt++] = '\n';
++        i = 1;
++        (void) fseek(fp, -1, SEEK_CUR);
++    }
++
++    while(i < count) {
++        if (buf[i] == '\r' && buf[i + 1] == '\n') {
++            wbuf[cnt++] = '\n';
++            ++i;
++        } else if (buf[i] == '\0') {
++            /* Skip it */
++        } else {
++            wbuf[cnt++] = buf[i];
++        }
++        ++i;
++    }
++    /* Preserve state between data chunks */
++    was_cr = buf[i - 1] == '\r';
++
++    if (fwrite(wbuf, 1, cnt, fp) == 0)
++        return 0;
++
++    return count;
++}
++
++void set_verbose(int v)
++{
++    verbose = v;
++}
++
++int recv_with_timeout(int s, void *in, int len, int timeout)
++{
++    return recvfrom_flags_with_timeout(s, in, len, NULL, timeout, 0);
++}
++
++int recvfrom_with_timeout(int s,
++                          void *in,
++                          size_t len,
++                          union sock_addr *from,
++                          int timeout)
++{
++    return recvfrom_flags_with_timeout(s, in, len, from, timeout, 0);
++}
++
++int recvfrom_flags_with_timeout(int s,
++                                void *in,
++                                size_t len,
++                                union sock_addr *from,
++                                int timeout,
++                                int flags)
++{
++    socklen_t fromlen = sizeof(from);
++    struct pollfd pfd;
++    int r;
++
++    pfd.fd = s;
++    pfd.events = POLLIN;
++    pfd.revents = 0;
++
++    r = poll(&pfd, 1, timeout);
++    if (r > 0) {
++        if (from) {
++            r = recvfrom(s, in, len, flags, &from->sa, &fromlen);
++            if (r < 0)
++                die("recvfrom_flags_with_timeout: recvfrom: %m");
++        } else {
++            r = recv(s, in, len, flags);
++            if (r < 0)
++                die("recvfrom_flags_with_timeout: recv: %m");
++        }
++    }
++
++    return r;
++}
++
++int receiver(int sockfd,
++             union sock_addr *server,
++             int blocksize,
++             int windowsize,
++             int timeout,
++             FILE *fp,
++             unsigned long *received,
++             char *error)
++{
++    struct tftphdr *tp;
++    unsigned short tp_opcode, tp_block;
++    unsigned short block = 1;
++    unsigned long amount = 0;
++    int pktsize = blocksize + 4;
++    int window = 1;
++    size_t size;
++    int retries;
++    int n, r = 0;
++
++    pktbuf = calloc(pktsize, 1);
++    if (!pktbuf)
++        die("Out of memory!");
++    tp = (struct tftphdr *)pktbuf;
++
++    retries = RETRIES;
++    do {
++        n = recvfrom_with_timeout(sockfd, tp, pktsize, server, timeout);
++        if (n == 0) {
++            if (--retries <= 0) {
++                r = E_TIMED_OUT;
++                goto abort;
++            }
++            continue;
++        }
++        retries = RETRIES;
++
++        tp_opcode = ntohs(tp->th_opcode);
++        tp_block  = ntohs(tp->th_block);
++
++        if (tp_opcode == DATA) {
++            if (tp_block == block) {
++                size = write_data(fp, tp->th_data, n - 4, 0);
++                if (size == 0 && ferror(fp)) {
++                    send_error(sockfd, server, "Failed to write data");
++                    r = E_FAILED_TO_WRITE;
++                    goto abort;
++                }
++                amount += size;
++
++                if (window++ >= windowsize || size != blocksize) {
++                    send_ack(sockfd, server, block);
++                    window = 1;
++                }
++
++                ++block;
++            } else {
++                do {
++                    n = recvfrom_with_timeout(sockfd, pktbuf, pktsize, server, SYNC_TIMEOUT);
++                } while (n > 0);
++                if (windowsize > 0) {
++                    send_ack(sockfd, server, block - 1);
++                }
++                window = 1;
++                n = 0;
++            }
++        } else if (tp_opcode == ERROR) {
++            format_error(tp, error);
++            r = E_RECEIVED_ERROR;
++            goto abort;
++        } else {
++            r = E_UNEXPECTED_PACKET;
++            goto abort;
++        }
++    } while (size == blocksize || n == 0);
++
++    /* Last ack can get lost, let's try and resend it twice
++     * to make it more likely that the ack gets to the sender.
++     */
++    --block;
++    for (n = 0; n < 2; ++n) {
++        usleep(SYNC_TIMEOUT * 1000);
++        _send_ack(sockfd, server, block, 0);
++    }
++
++    if (received)
++        *received = amount;
++abort:
++    free(pktbuf);
++    return r;
++}
++
++int sender(int sockfd,
++           union sock_addr *server,
++           int blocksize,
++           int windowsize,
++           int timeout,
++           int rollover,
++           FILE *fp,
++           unsigned long *sent)
++{
++    struct tftphdr *tp;
++    unsigned short tp_opcode, tp_block;
++    unsigned short block = 1;
++    unsigned long amount = 0;
++    int l_timeout = timeout;
++    int pktsize = blocksize + 4;
++    int window = 1;
++    int seek = 0;
++    int retries;
++    size_t size;
++    int done = 0;
++    int n, r = 0;
++
++    pktbuf = calloc(pktsize, 1);
++    if (!pktbuf)
++        die("Out of memory!");
++    tp = (struct tftphdr *)pktbuf;
++
++    retries = RETRIES;
++    do {
++        size = read_data(fp, blocksize, block, seek, tp);
++        if (size == 0 && ferror(fp)) {
++            send_error(sockfd, server, "Error while reading the file");
++            r = E_FAILED_TO_READ;
++            goto abort;
++        }
++        amount += size;
++        seek = 0;
++
++        if (server)
++            n = sendto(sockfd, tp, size + 4, 0, &server->sa, SOCKLEN(server));
++        else
++            n = send(sockfd, tp, size + 4, 0);
++        if (n != size + 4) {
++            syslog(LOG_WARNING, "tftpd: send: %m");
++            r = E_SYSTEM_ERROR;
++            goto abort;
++        }
++
++        if (window++ < windowsize) {
++            /* Even if the entire window is not sent, the server should check for incoming packets
++             * and react to out of order ACKs, or ERRORs.
++             */
++            l_timeout = 0;
++        } else {
++            l_timeout = timeout;
++            window = 1;
++        }
++        done = size != blocksize;
++        if (done) {
++            l_timeout = timeout;
++            window = 1;
++        }
++
++        n = recvfrom_with_timeout(sockfd, pktbuf, pktsize, server, l_timeout);
++        if (n < 0) {
++            syslog(LOG_WARNING, "tftpd: recv: %m");
++            r = E_SYSTEM_ERROR;
++            goto abort;
++        } else if (l_timeout > 0 && n == 0) {
++            seek = -size;
++            if (--retries <= 0) {
++                r = E_TIMED_OUT;
++                goto abort;
++            }
++            done = 0;
++            continue;
++        }
++
++        if (++block == 0)
++            block = rollover;
++
++        tp_opcode = ntohs(tp->th_opcode);
++        tp_block  = ntohs(tp->th_block);
++
++        if (tp_opcode == ACK) {
++            if (tp_block != (unsigned short)(block - 1)) {
++                int offset = tp_block;
++
++                done = 0;
++                do {
++                    n = recvfrom_with_timeout(sockfd, pktbuf, pktsize, server, SYNC_TIMEOUT);
++                } while (n > 0);
++                /* This is a bit of a hack. Mismatched packets that are "over the edge" of the unsigned short
++                 * have to be handled and a correct seek has to be issued. In theory, overflowing block number
++                 * should not even be supported, as it is impossible to distinguish correctly if more than 2^16
++                 * packets are sent, but the first ones are received later than the last ones.
++                 */
++                if (tp_block > 65000 && tp_block > (unsigned short)(block - 1))
++                    offset -= 65535;
++                seek = (offset - block + 2) * blocksize - size;
++            }
++            block = tp_block + 1;
++            retries = RETRIES;
++        } else if (tp_opcode == ERROR) {
++            r = E_RECEIVED_ERROR;
++            goto abort;
++        }
++    } while (!done);
++
++    if (sent)
++        *sent = amount;
++abort:
++    free(pktbuf);
++    return r;
++}
+diff --git a/common/common.h b/common/common.h
+new file mode 100644
+index 0000000..31c1b7c
+--- /dev/null
++++ b/common/common.h
+@@ -0,0 +1,97 @@
++/*
++ * Copyright (c) 2017 Jan Synáček
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ *    must display the following acknowledgement:
++ *	This product includes software developed by the University of
++ *	California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#ifndef _COMMON_H
++#define _COMMON_H
++
++#include <netinet/in.h>
++#include "config.h"
++
++#define TIMEOUT 1000 /* ms */
++#define RETRIES 6
++
++#define E_TIMED_OUT -1
++#define E_RECEIVED_ERROR -2
++#define E_UNEXPECTED_PACKET -3
++#define E_FAILED_TO_READ -4
++#define E_FAILED_TO_WRITE -5
++#define E_SYSTEM_ERROR -6
++#define ERROR_MAXLEN 511
++
++union sock_addr {
++    struct sockaddr     sa;
++    struct sockaddr_in  si;
++#ifdef HAVE_IPV6
++    struct sockaddr_in6 s6;
++#endif
++};
++
++#define SOCKLEN(sock) \
++    (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
++    (sizeof(struct sockaddr_in)) : \
++    (sizeof(union sock_addr)))
++
++const char *opcode_to_str(unsigned short opcode);
++int str_equal(const char *s1, const char *s2);
++void set_verbose(int v);
++
++int format_error(struct tftphdr *tp, char *error);
++void die_on_error(struct tftphdr *tp);
++void send_error(int sockfd, union sock_addr *to, const char *msg);
++void send_ack(int sockfd, union sock_addr *to, unsigned short block);
++int recv_with_timeout(int s, void *in, int len, int timeout);
++int recvfrom_with_timeout(int s, void *in, size_t len, union sock_addr *from, int timeout);
++int recvfrom_flags_with_timeout(int s,
++                                void *in,
++                                size_t len,
++                                union sock_addr *from,
++                                int timeout,
++                                int flags);
++int receiver(int sockfd,
++             union sock_addr *server,
++             int blocksize,
++             int windowsize,
++             int timeout,
++             FILE *fp,
++             unsigned long *received,
++             char *error);
++
++int sender(int sockfd,
++           union sock_addr *server,
++           int blocksize,
++           int windowsize,
++           int timeout,
++           int rollover,
++           FILE *fp,
++           unsigned long *sent);
++#endif
+diff --git a/common/tftpsubs.c b/common/tftpsubs.c
+index 8c999f6..6033e9b 100644
+--- a/common/tftpsubs.c
++++ b/common/tftpsubs.c
+@@ -404,3 +404,23 @@ char *strip_address(char *addr)
+     return addr;
+ }
+ #endif
++
++
++int str_equal(const char *s1, const char *s2)
++{
++    return !strcmp(s1, s2);
++}
++
++
++const char *opcode_to_str(unsigned short opcode)
++{
++    switch(opcode) {
++        case RRQ:   return "RRQ";
++        case WRQ:   return "WRQ";
++        case DATA:  return "DATA";
++        case ACK:   return "ACK";
++        case ERROR: return "ERROR";
++        case OACK:  return "OACK";
++    }
++    return "UNKNOWN";
++}
+diff --git a/common/tftpsubs.h b/common/tftpsubs.h
+index b3a3bf3..311b141 100644
+--- a/common/tftpsubs.h
++++ b/common/tftpsubs.h
+@@ -38,21 +38,9 @@
+ #ifndef TFTPSUBS_H
+ #define TFTPSUBS_H
+ 
++#include "common.h"
+ #include "config.h"
+ 
+-union sock_addr {
+-    struct sockaddr     sa;
+-    struct sockaddr_in  si;
+-#ifdef HAVE_IPV6
+-    struct sockaddr_in6 s6;
+-#endif
+-};
+-
+-#define SOCKLEN(sock) \
+-    (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
+-    (sizeof(struct sockaddr_in)) : \
+-    (sizeof(union sock_addr)))
+-
+ #ifdef HAVE_IPV6
+ #define SOCKPORT(sock) \
+     (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
+diff --git a/tftp/extern.h b/tftp/extern.h
+index 78474fc..dd0523f 100644
+--- a/tftp/extern.h
++++ b/tftp/extern.h
+@@ -34,7 +34,7 @@
+ #ifndef RECVFILE_H
+ #define RECVFILE_H
+ 
+-void tftp_recvfile(int, const char *, const char *);
+-void tftp_sendfile(int, const char *, const char *);
++void tftp_recvfile(int, const char *, const char *, int);
++void tftp_sendfile(int, const char *, const char *, int);
+ 
+ #endif
+diff --git a/tftp/main.c b/tftp/main.c
+index fcf5a25..5a7d3a6 100644
+--- a/tftp/main.c
++++ b/tftp/main.c
+@@ -49,7 +49,7 @@
+ 
+ #include "extern.h"
+ 
+-#define	TIMEOUT		5       /* secs between rexmt's */
++#define	RTIMEOUT	5       /* secs between rexmt's */
+ #define	LBUFLEN		200     /* size of input buffer */
+ 
+ struct modes {
+@@ -82,7 +82,7 @@ int ai_fam_sock = AF_INET;
+ union sock_addr peeraddr;
+ int f = -1;
+ u_short port;
+-int trace;
++int trace_opt;
+ int verbose;
+ int literal;
+ int connected;
+@@ -104,6 +104,7 @@ struct servent *sp;
+ int portrange = 0;
+ unsigned int portrange_from = 0;
+ unsigned int portrange_to = 0;
++int windowsize = -1;
+ 
+ void get(int, char **);
+ void help(int, char **);
+@@ -191,14 +192,14 @@ char *xstrdup(const char *);
+ 
+ const char *program;
+ 
+-static inline void usage(int errcode)
++static void usage(int errcode)
+ {
+     fprintf(stderr,
+ #ifdef HAVE_IPV6
+-            "Usage: %s [-4][-6][-v][-V][-l][-m mode] [-R port:port] "
++            "Usage: %s [-4][-6][-v][-V][-l][-m mode][-w size] [-R port:port] "
+ 			"[host [port]] [-c command]\n",
+ #else
+-            "Usage: %s [-v][-V][-l][-m mode] [-R port:port] "
++            "Usage: %s [-v][-V][-l][-m mode][-w size] [-R port:port] "
+ 			"[host [port]] [-c command]\n",
+ #endif
+             program);
+@@ -279,6 +280,15 @@ int main(int argc, char *argv[])
+                     }
+                     portrange = 1;
+                     break;
++                case 'w':
++                    if (++arg >= argc)
++                        usage(EX_USAGE);
++                    windowsize = atoi(argv[arg]);
++                    if (windowsize <= 0 || windowsize > 64) {
++                        fprintf(stderr, "Bad window size: %s (1-64)\n", argv[arg]);
++                        exit(EX_USAGE);
++                    }
++                    break;
+                 case 'h':
+                 default:
+                     usage(*optx == 'h' ? 0 : EX_USAGE);
+@@ -593,7 +603,7 @@ void put(int argc, char *argv[])
+             printf("putting %s to %s:%s [%s]\n",
+                    cp, hostname, targ, mode->m_mode);
+         sa_set_port(&peeraddr, port);
+-        tftp_sendfile(fd, targ, mode->m_mode);
++        tftp_sendfile(fd, targ, mode->m_mode, windowsize);
+         return;
+     }
+     /* this assumes the target is a directory */
+@@ -625,7 +635,7 @@ void put(int argc, char *argv[])
+             printf("putting %s to %s:%s [%s]\n",
+                    argv[n], hostname, remote_pth, mode->m_mode);
+         sa_set_port(&peeraddr, port);
+-        tftp_sendfile(fd, remote_pth, mode->m_mode);
++        tftp_sendfile(fd, remote_pth, mode->m_mode, windowsize);
+     }
+ }
+ 
+@@ -693,7 +703,7 @@ void get(int argc, char *argv[])
+                 printf("getting from %s:%s to %s [%s]\n",
+                        hostname, src, cp, mode->m_mode);
+             sa_set_port(&peeraddr, port);
+-            tftp_recvfile(fd, src, mode->m_mode);
++            tftp_recvfile(fd, src, mode->m_mode, windowsize);
+             break;
+         }
+         cp = tail(src);         /* new .. jdg */
+@@ -708,7 +718,7 @@ void get(int argc, char *argv[])
+             printf("getting from %s:%s to %s [%s]\n",
+                    hostname, src, cp, mode->m_mode);
+         sa_set_port(&peeraddr, port);
+-        tftp_recvfile(fd, src, mode->m_mode);
++        tftp_recvfile(fd, src, mode->m_mode, windowsize);
+     }
+ }
+ 
+@@ -718,7 +728,7 @@ static void getusage(char *s)
+     printf("       %s file file ... file if connected\n", s);
+ }
+ 
+-int rexmtval = TIMEOUT;
++int rexmtval = RTIMEOUT;
+ 
+ void setrexmt(int argc, char *argv[])
+ {
+@@ -741,7 +751,7 @@ void setrexmt(int argc, char *argv[])
+         rexmtval = t;
+ }
+ 
+-int maxtimeout = 5 * TIMEOUT;
++int maxtimeout = 5 * RTIMEOUT;
+ 
+ void settimeout(int argc, char *argv[])
+ {
+@@ -781,7 +791,7 @@ void status(int argc, char *argv[])
+     else
+         printf("Not connected.\n");
+     printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode,
+-           verbose ? "on" : "off", trace ? "on" : "off",
++           verbose ? "on" : "off", trace_opt ? "on" : "off",
+            literal ? "on" : "off");
+     printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
+            rexmtval, maxtimeout);
+@@ -969,8 +979,8 @@ void settrace(int argc, char *argv[])
+     (void)argc;
+     (void)argv;                 /* Quiet unused warning */
+ 
+-    trace = !trace;
+-    printf("Packet tracing %s.\n", trace ? "on" : "off");
++    trace_opt = !trace_opt;
++    printf("Packet tracing %s.\n", trace_opt ? "on" : "off");
+ }
+ 
+ void setverbose(int argc, char *argv[])
+diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in
+index b41f7b5..1779a43 100644
+--- a/tftp/tftp.1.in
++++ b/tftp/tftp.1.in
+@@ -82,6 +82,9 @@ Default to verbose mode.
+ .B \-V
+ Print the version number and configuration to standard output, then
+ exit gracefully.
++.TP
++.B \-w\fP \fIwindow-size\fP
++Set the "windowsize" TFTP option (RFC 7440) to the specified value.
+ .SH COMMANDS
+ Once
+ .B tftp
+diff --git a/tftp/tftp.c b/tftp/tftp.c
+index 9d15022..58ea597 100644
+--- a/tftp/tftp.c
++++ b/tftp/tftp.c
+@@ -31,355 +31,307 @@
+  * SUCH DAMAGE.
+  */
+ 
++#include <poll.h>
++#include <stdarg.h>
+ #include "common/tftpsubs.h"
+-
+-/*
+- * TFTP User Program -- Protocol Machines
+- */
+ #include "extern.h"
+ 
++/* TODO: This 'peeraddr' global should be removed. */
+ extern union sock_addr peeraddr; /* filled in by main */
+ extern int f;                    /* the opened socket */
+-extern int trace;
++extern int trace_opt;
+ extern int verbose;
+-extern int rexmtval;
+-extern int maxtimeout;
+-
++/* TODO: Adjust when blocksize is implemented. */
+ #define PKTSIZE    SEGSIZE+4
+-char ackbuf[PKTSIZE];
+-int timeout;
+-sigjmp_buf toplevel;
+-sigjmp_buf timeoutbuf;
++static char pktbuf[PKTSIZE];
+ 
+-static void nak(int, const char *);
+-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);
++static void timed_out(void)
++{
++    printf("client: timed out");
++    exit(1);
++}
++
++static void die(const char *fmt, ...)
++{
++    va_list ap;
++
++    va_start(ap, fmt);
++    fprintf(stderr, "fatal: client: ");
++    vfprintf(stderr, fmt, ap);
++    printf("\n");
++    va_end(ap);
++    exit(1);
++}
++
++static size_t make_request(unsigned short opcode,
++                           const char *name,
++                           const char *mode,
++                           int blocksize,
++                           int windowsize,
++                           struct tftphdr *out)
++{
++    char *cp, buf[16];
++    size_t len;
++
++    cp = (char *)&(out->th_stuff);
++
++    out->th_opcode = htons(opcode);
++
++    len = strlen(name) + 1;
++    memcpy(cp, name, len);
++    cp += len;
++
++    len = strlen(mode) + 1;
++    memcpy(cp, mode, len);
++    cp += len;
++
++    /* Don't include options with default values. */
++
++    /* TODO: TBI in a separate patch. */
++    (void) blocksize;
++    /*if (blocksize != SEGSIZE) {
++        len = strlen("blksize") + 1;
++        memcpy(cp, "blksize", len);
++        cp += len;
++        if (snprintf(buf, 16, "%u", blocksize) < 0)
++            die("out of memory");
++        len = strlen(buf) + 1;
++        memcpy(cp, buf, len);
++        cp += len;
++    }*/
++
++    if (windowsize > 0) {
++        len = strlen("windowsize") + 1;
++        memcpy(cp, "windowsize", len);
++        cp += len;
++        if (snprintf(buf, 16, "%u", windowsize) < 0)
++            die("out of memory");
++        len = strlen(buf) + 1;
++        memcpy(cp, buf, len);
++        cp += len;
++    }
++
++    return (cp - (char *)out);
++}
++
++static void send_request(int sock,
++                         union sock_addr *to,
++                         short request,
++                         const char *name,
++                         const char *mode,
++                         unsigned blocksize,
++                         unsigned windowsize)
++{
++    struct tftphdr *out;
++    size_t size;
++
++    out = (struct tftphdr *)pktbuf;
++    size = make_request(request, name, mode, blocksize, windowsize, out);
++
++    if (sendto(sock, out, size, 0, &to->sa, SOCKLEN(to)) != (unsigned)size)
++        die("send_request: sendto: %m");
++}
++
++static int wait_for_oack(int sock, union sock_addr *from, char **options, int *optlen)
++{
++    unsigned short in_opcode;
++    struct tftphdr *in;
++    int r;
++
++    r = recvfrom_with_timeout(sock, pktbuf, sizeof(pktbuf), from, TIMEOUT);
++    if (r == 0)
++        return r;
++
++    in = (struct tftphdr *)pktbuf;
++    in_opcode = ntohs(in->th_opcode);
++
++    if (in_opcode == ERROR)
++        die_on_error(in);
++    if (in_opcode != OACK)
++        return -in_opcode;
++
++    *options = pktbuf + 2;
++    *optlen = r - 2;
++
++    return 1;
++}
+ 
+ /*
+  * Send the requested file.
+  */
+-void tftp_sendfile(int fd, const char *name, const char *mode)
++void tftp_sendfile(int fd, const char *name, const char *mode, int windowsize)
+ {
+-    struct tftphdr *ap;         /* data and ack packets */
+-    struct tftphdr *dp;
+-    int n;
+-    volatile int is_request;
+-    volatile u_short block;
+-    volatile int size, convert;
+-    volatile off_t amount;
+-    union sock_addr from;
+-    socklen_t fromlen;
+-    FILE *file;
+-    u_short ap_opcode, ap_block;
+-
+-    startclock();               /* start stat's clock */
+-    dp = r_init();              /* reset fillbuf/read-ahead code */
+-    ap = (struct tftphdr *)ackbuf;
+-    convert = !strcmp(mode, "netascii");
+-    file = fdopen(fd, convert ? "rt" : "rb");
+-    block = 0;
+-    is_request = 1;             /* First packet is the actual WRQ */
+-    amount = 0;
+-
+-    bsd_signal(SIGALRM, timer);
++    union sock_addr server = peeraddr;
++    unsigned long amount = 0;
++    int blocksize = SEGSIZE;
++    char *options;
++    int optlen;
++    int retries;
++    FILE *fp;
++    int n, r;
++
++    set_verbose(trace_opt + verbose);
++
++    startclock();
++    send_request(f, &server, WRQ, name, mode, blocksize, windowsize);
++
++    /* If no windowsize was specified on the command line,
++     * don't bother with options.
++     * When blocksize is supported, this should actually only be called
++     * if no options were sent in the RRQ.
++     */
++    if (windowsize < 0)
++        goto no_options;
++    retries = RETRIES;
+     do {
+-        if (is_request) {
+-            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, NULL);
+-                break;
+-            }
+-            dp->th_opcode = htons((u_short) DATA);
+-            dp->th_block = htons((u_short) block);
++        r = wait_for_oack(f, &server, &options, &optlen);
++        if (r < 0) {
++            return;
+         }
+-        timeout = 0;
+-        (void)sigsetjmp(timeoutbuf, 1);
+-
+-        if (trace)
+-            tpacket("sent", dp, size + 4);
+-        n = sendto(f, dp, size + 4, 0,
+-                   &peeraddr.sa, SOCKLEN(&peeraddr));
+-        if (n != size + 4) {
+-            perror("tftp: sendto");
+-            goto abort;
+-        }
+-        read_ahead(file, convert);
+-        for (;;) {
+-            alarm(rexmtval);
+-            do {
+-                fromlen = sizeof(from);
+-                n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+-                             &from.sa, &fromlen);
+-            } while (n <= 0);
+-            alarm(0);
+-            if (n < 0) {
+-                perror("tftp: recvfrom");
+-                goto abort;
+-            }
+-            sa_set_port(&peeraddr, SOCKPORT(&from));  /* added */
+-            if (trace)
+-                tpacket("received", ap, n);
+-            /* should verify packet came from server */
+-            ap_opcode = ntohs((u_short) ap->th_opcode);
+-            ap_block = ntohs((u_short) ap->th_block);
+-            if (ap_opcode == ERROR) {
+-                printf("Error code %d: %s\n", ap_block, ap->th_msg);
+-                goto abort;
+-            }
+-            if (ap_opcode == ACK) {
+-                int j;
+ 
+-                if (ap_block == block) {
+-                    break;
++        /* Parse returned options. */
++        n = 0;
++        if (r != 0) {
++            char *opt, *val;
++            int got_ws = 0;
++            int v;
++
++            while (n < optlen) {
++                opt = options + n;
++                n += strlen(opt) + 1;
++                val = options + n;
++                if (str_equal(opt, "windowsize") && windowsize != 1) {
++                    v = atoi(val);
++                    if (v != windowsize)
++                        printf("client: server negotiated different windowsize: %d", v);
++                    /* Assumes v > 0, it probably shouldn't. */
++                    windowsize = v;
++                    got_ws = 1;
+                 }
+-                /* On an error, try to synchronize
+-                 * both sides.
+-                 */
+-                j = synchnet(f);
+-                if (j && trace) {
+-                    printf("discarded %d packets\n", j);
+-                }
+-                /*
+-                 * RFC1129/RFC1350: We MUST NOT re-send the DATA
+-                 * packet in response to an invalid ACK.  Doing so
+-                 * would cause the Sorcerer's Apprentice bug.
+-                 */
++                n += strlen(val) + 1;
++            }
++
++            if (got_ws == 0 && windowsize != 1) {
++                windowsize = 1;
++                printf("client: server didn't negotiate windowsize, continuing with windowsize=1");
+             }
+         }
+-        if (!is_request)
+-            amount += size;
+-        is_request = 0;
+-        block++;
+-    } while (size == SEGSIZE || block == 1);
+-  abort:
+-    fclose(file);
++    } while (r == 0 && --retries > 0);
++    if (retries <= 0)
++        timed_out();
++
++no_options:
++    if (windowsize < 0) {
++        struct tftphdr *tp = (struct tftphdr *)pktbuf;
++
++        retries = RETRIES;
++        do {
++            r = recvfrom_with_timeout(f, pktbuf, PKTSIZE, &server, TIMEOUT);
++            if (r == 0) {
++                /* Timed out. */
++                continue;
++            }
++            if (ntohs(tp->th_opcode) == ACK && ntohs(tp->th_block) == 0) {
++                break;
++            } else if (ntohs(tp->th_opcode) == ERROR) {
++                die_on_error(tp);
++            }
++        } while (r == 0 && --retries > 0);
++        if (retries <= 0)
++            timed_out();
++    }
++    fp = fdopen(fd, "r");
++    r = sender(f, &server, blocksize, windowsize, TIMEOUT, 0, fp, &amount);
++    if (r < 0)
++        exit(1);
++
+     stopclock();
+     if (amount > 0)
+         printstats("Sent", amount);
+ }
+ 
++
+ /*
+  * Receive a file.
+  */
+-void tftp_recvfile(int fd, const char *name, const char *mode)
++void tftp_recvfile(int fd, const char *name, const char *mode, int windowsize)
+ {
+-    struct tftphdr *ap;
+-    struct tftphdr *dp;
+-    int n;
+-    volatile u_short block;
+-    volatile int size, firsttrip;
+-    volatile unsigned long amount;
+-    union sock_addr from;
+-    socklen_t fromlen;
+-    FILE *file;
+-    volatile int convert;       /* true if converting crlf -> lf */
+-    u_short dp_opcode, dp_block;
++    union sock_addr server = peeraddr;
++    unsigned long amount = 0;
++    int blocksize = SEGSIZE;
++    char *options, error[ERROR_MAXLEN];
++    int optlen;
++    int retries;
++    FILE *fp;
++    int n, r;
++
++    set_verbose(trace_opt + verbose);
+ 
+     startclock();
+-    dp = w_init();
+-    ap = (struct tftphdr *)ackbuf;
+-    convert = !strcmp(mode, "netascii");
+-    file = fdopen(fd, convert ? "wt" : "wb");
+-    block = 1;
+-    firsttrip = 1;
+-    amount = 0;
+-
+-    bsd_signal(SIGALRM, timer);
++
++    send_request(f, &server, RRQ, name, mode, blocksize, windowsize);
++    /* If no windowsize was specified on the command line,
++     * don't bother with options.
++     * When blocksize is supported, this should actually only be called
++     * if no options were sent in the RRQ.
++     */
++    if (windowsize < 0)
++        goto no_options;
++    retries = RETRIES;
+     do {
+-        if (firsttrip) {
+-            size = makerequest(RRQ, name, ap, mode);
+-            firsttrip = 0;
+-        } else {
+-            ap->th_opcode = htons((u_short) ACK);
+-            ap->th_block = htons((u_short) block);
+-            size = 4;
+-            block++;
+-        }
+-        timeout = 0;
+-        (void)sigsetjmp(timeoutbuf, 1);
+-      send_ack:
+-        if (trace)
+-            tpacket("sent", ap, size);
+-        if (sendto(f, ackbuf, size, 0, &peeraddr.sa,
+-                   SOCKLEN(&peeraddr)) != size) {
+-            alarm(0);
+-            perror("tftp: sendto");
+-            goto abort;
++        r = wait_for_oack(f, &server, &options, &optlen);
++        if (r < 0) {
++            return;
+         }
+-        write_behind(file, convert);
+-        for (;;) {
+-            alarm(rexmtval);
+-            do {
+-                fromlen = sizeof(from);
+-                n = recvfrom(f, dp, PKTSIZE, 0,
+-                             &from.sa, &fromlen);
+-            } while (n <= 0);
+-            alarm(0);
+-            if (n < 0) {
+-                perror("tftp: recvfrom");
+-                goto abort;
+-            }
+-            sa_set_port(&peeraddr, SOCKPORT(&from));  /* added */
+-            if (trace)
+-                tpacket("received", dp, n);
+-            /* should verify client address */
+-            dp_opcode = ntohs((u_short) dp->th_opcode);
+-            dp_block = ntohs((u_short) dp->th_block);
+-            if (dp_opcode == ERROR) {
+-                printf("Error code %d: %s\n", dp_block, dp->th_msg);
+-                goto abort;
+-            }
+-            if (dp_opcode == DATA) {
+-                int j;
+ 
+-                if (dp_block == block) {
+-                    break;      /* have next packet */
+-                }
+-                /* On an error, try to synchronize
+-                 * both sides.
+-                 */
+-                j = synchnet(f);
+-                if (j && trace) {
+-                    printf("discarded %d packets\n", j);
+-                }
+-                if (dp_block == (block - 1)) {
+-                    goto send_ack;      /* resend ack */
++        /* Parse returned options. */
++        n = 0;
++        if (r != 0) {
++            char *opt, *val;
++            int got_ws = 0;
++            int v;
++
++            while (n < optlen) {
++                opt = options + n;
++                n += strlen(opt) + 1;
++                val = options + n;
++                if (str_equal(opt, "windowsize") && windowsize != 1) {
++                    v = atoi(val);
++                    if (v != windowsize)
++                        printf("client: server negotiated different windowsize: %d", v);
++                    /* Assumes v > 0, it probably shouldn't. */
++                    windowsize = v;
++                    got_ws = 1;
+                 }
++                n += strlen(val) + 1;
++            }
++
++            if (got_ws == 0 && windowsize != 1) {
++                windowsize = 1;
++                printf("client: server didn't negotiate windowsize, continuing with windowsize=1");
+             }
+         }
+-        /*      size = write(fd, dp->th_data, n - 4); */
+-        size = writeit(file, &dp, n - 4, convert);
+-        if (size < 0) {
+-            nak(errno + 100, NULL);
+-            break;
+-        }
+-        amount += size;
+-    } while (size == SEGSIZE);
+-  abort:                       /* ok to ack, since user */
+-    ap->th_opcode = htons((u_short) ACK);       /* has seen err msg */
+-    ap->th_block = htons((u_short) block);
+-    (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
+-                 SOCKLEN(&peeraddr));
+-    write_behind(file, convert);        /* flush last buffer */
+-    fclose(file);
++    } while (r == 0 && --retries > 0);
++    if (retries <= 0)
++        timed_out();
++
++    send_ack(f, &server, 0);
++
++no_options:
++    fp = fdopen(fd, "w");
++    r = receiver(f, &server, blocksize, windowsize, TIMEOUT, fp, &amount, error);
++    if (r < 0) {
++        fprintf(stderr, "%s\n", error);
++        exit(1);
++    }
++
+     stopclock();
+     if (amount > 0)
+         printstats("Received", amount);
+-}
+-
+-static int
+-makerequest(int request, const char *name,
+-            struct tftphdr *tp, const char *mode)
+-{
+-    char *cp;
+-    size_t len;
+-
+-    tp->th_opcode = htons((u_short) request);
+-    cp = (char *)&(tp->th_stuff);
+-    len = strlen(name) + 1;
+-    memcpy(cp, name, len);
+-    cp += len;
+-    len = strlen(mode) + 1;
+-    memcpy(cp, mode, len);
+-    cp += len;
+-    return (cp - (char *)tp);
+-}
+-
+-static const char *const errmsgs[] = {
+-    "Undefined error code",     /* 0 - EUNDEF */
+-    "File not found",           /* 1 - ENOTFOUND */
+-    "Access denied",            /* 2 - EACCESS */
+-    "Disk full or allocation exceeded", /* 3 - ENOSPACE */
+-    "Illegal TFTP operation",   /* 4 - EBADOP */
+-    "Unknown transfer ID",      /* 5 - EBADID */
+-    "File already exists",      /* 6 - EEXISTS */
+-    "No such user",             /* 7 - ENOUSER */
+-    "Failure to negotiate RFC2347 options"      /* 8 - EOPTNEG */
+-};
+-
+-#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *))
+-
+-/*
+- * Send a nak packet (error message).
+- * Error code passed in is one of the
+- * standard TFTP codes, or a UNIX errno
+- * offset by 100.
+- */
+-static void nak(int error, const char *msg)
+-{
+-    struct tftphdr *tp;
+-    int length;
+-
+-    tp = (struct tftphdr *)ackbuf;
+-    tp->th_opcode = htons((u_short) ERROR);
+-    tp->th_code = htons((u_short) error);
+-
+-    if (error >= 100) {
+-        /* This is a Unix errno+100 */
+-        if (!msg)
+-            msg = strerror(error - 100);
+-        error = EUNDEF;
+-    } else {
+-        if ((unsigned)error >= ERR_CNT)
+-            error = EUNDEF;
+-
+-        if (!msg)
+-            msg = errmsgs[error];
+-    }
+-
+-    tp->th_code = htons((u_short) error);
+-
+-    length = strlen(msg) + 1;
+-    memcpy(tp->th_msg, msg, length);
+-    length += 4;                /* Add space for header */
+-
+-    if (trace)
+-        tpacket("sent", tp, length);
+-    if (sendto(f, ackbuf, length, 0, &peeraddr.sa,
+-               SOCKLEN(&peeraddr)) != length)
+-        perror("nak");
+-}
+-
+-static void tpacket(const char *s, struct tftphdr *tp, int n)
+-{
+-    static const char *opcodes[] =
+-        { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
+-    char *cp, *file;
+-    u_short op = ntohs((u_short) tp->th_opcode);
+-
+-    if (op < RRQ || op > ERROR)
+-        printf("%s opcode=%x ", s, op);
+-    else
+-        printf("%s %s ", s, opcodes[op]);
+-    switch (op) {
+-
+-    case RRQ:
+-    case WRQ:
+-        n -= 2;
+-        file = cp = (char *)&(tp->th_stuff);
+-        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;
+-    }
++    fclose(fp);
+ }
+ 
+ struct timeval tstart;
+@@ -404,23 +356,9 @@ static void printstats(const char *direction, unsigned long amount)
+         (tstart.tv_sec + (tstart.tv_usec / 1000000.0));
+     if (verbose) {
+         printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
++        /* TODO: Change the statistics in a separate patch (bits???)! */
+         printf(" [%.0f bit/s]", (amount * 8.) / delta);
+         putchar('\n');
+     }
+ }
+ 
+-static void timer(int sig)
+-{
+-    int save_errno = errno;
+-
+-    (void)sig;                  /* Shut up unused warning */
+-
+-    timeout += rexmtval;
+-    if (timeout >= maxtimeout) {
+-        printf("Transfer timed out.\n");
+-        errno = save_errno;
+-        siglongjmp(toplevel, -1);
+-    }
+-    errno = save_errno;
+-    siglongjmp(timeoutbuf, 1);
+-}
+diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in
+index 321b8c6..46384d6 100644
+--- a/tftpd/tftpd.8.in
++++ b/tftpd/tftpd.8.in
+@@ -227,6 +227,11 @@ Set the time before the server retransmits a packet, in microseconds.
+ \fBrollover\fP (nonstandard)
+ Set the block number to resume at after a block number rollover.  The
+ default and recommended value is zero.
++.TP
++\fBwindowsize\fP (RFC 7440)
++Set the windowsize to a number of blocks that should be sent before
++expecting an ack. The default is 1, which means the same functionality
++as if windowsize wasn't used. Maximum is 64.
+ .PP
+ The
+ .B \-\-refuse
+@@ -408,6 +413,9 @@ RFC 2348,
+ .br
+ RFC 2349,
+ .IR "TFTP Timeout Interval and Transfer Size Options" .
++.br
++RFC 7440,
++.IR "TFTP Windowsize Option" .
+ .SH "AUTHOR"
+ This version of
+ .B tftpd
+diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
+index b7a5a95..98adbc1 100644
+--- a/tftpd/tftpd.c
++++ b/tftpd/tftpd.c
+@@ -48,6 +48,8 @@
+ #include <pwd.h>
+ #include <limits.h>
+ #include <syslog.h>
++#include <poll.h>
++#include <stdarg.h>
+ 
+ #include "common/tftpsubs.h"
+ #include "recvfrom.h"
+@@ -72,23 +74,16 @@ static int ai_fam = AF_UNSPEC;
+ static int ai_fam = AF_INET;
+ #endif
+ 
+-#define	TIMEOUT 1000000         /* Default timeout (us) */
+-#define TRIES   6               /* Number of attempts to send each packet */
+-#define TIMEOUT_LIMIT ((1 << TRIES)-1)
+-
+ const char *__progname;
+ static int peer;
+-static unsigned long timeout  = TIMEOUT;        /* Current timeout value */
+-static unsigned long rexmtval = TIMEOUT;       /* Basic timeout value */
+-static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT;
+-static int timeout_quit = 0;
+-static sigjmp_buf timeoutbuf;
+ static uint16_t rollover_val = 0;
++static int windowsize = 1;
+ 
+ #define	PKTSIZE	MAX_SEGSIZE+4
+ static char buf[PKTSIZE];
+-static char ackbuf[PKTSIZE];
++static char pktbuf[PKTSIZE];
+ static unsigned int max_blksize = MAX_SEGSIZE;
++#define MAX_WINDOWSIZE 64
+ 
+ static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p;
+ 
+@@ -113,8 +108,7 @@ static struct rule *rewrite_rules = NULL;
+ #endif
+ 
+ int tftp(struct tftphdr *, int);
+-static void nak(int, const char *);
+-static void timer(int);
++static void nak(int error, const char *msg);
+ static void do_opt(const char *, const char *, char **);
+ 
+ static int set_blksize(uintmax_t *);
+@@ -123,6 +117,9 @@ static int set_tsize(uintmax_t *);
+ static int set_timeout(uintmax_t *);
+ static int set_utimeout(uintmax_t *);
+ static int set_rollover(uintmax_t *);
++static int set_windowsize(uintmax_t *);
++
++int g_timeout = 1000; /* ms */
+ 
+ struct options {
+     const char *o_opt;
+@@ -134,6 +131,7 @@ struct options {
+     {"timeout",  set_timeout},
+     {"utimeout", set_utimeout},
+     {"rollover", set_rollover},
++    {"windowsize", set_windowsize},
+     {NULL, NULL}
+ };
+ 
+@@ -152,16 +150,6 @@ static void handle_exit(int sig)
+     exit_signal = sig;
+ }
+ 
+-/* Handle timeout signal or timeout event */
+-void timer(int sig)
+-{
+-    (void)sig;                  /* Suppress unused warning */
+-    timeout <<= 1;
+-    if (timeout >= maxtimeout || timeout_quit)
+-        exit(0);
+-    siglongjmp(timeoutbuf, 1);
+-}
+-
+ #ifdef WITH_REGEX
+ static struct rule *read_remap_rules(const char *file)
+ {
+@@ -229,64 +217,6 @@ static void pmtu_discovery_off(int fd)
+ #endif
+ }
+ 
+-/*
+- * Receive packet with synchronous timeout; timeout is adjusted
+- * to account for time spent waiting.
+- */
+-static int recv_time(int s, void *rbuf, int len, unsigned int flags,
+-                     unsigned long *timeout_us_p)
+-{
+-    fd_set fdset;
+-    struct timeval tmv, t0, t1;
+-    int rv, err;
+-    unsigned long timeout_us = *timeout_us_p;
+-    unsigned long timeout_left, dt;
+-
+-    gettimeofday(&t0, NULL);
+-    timeout_left = timeout_us;
+-
+-    for (;;) {
+-        FD_ZERO(&fdset);
+-        FD_SET(s, &fdset);
+-
+-        do {
+-            tmv.tv_sec = timeout_left / 1000000;
+-            tmv.tv_usec = timeout_left % 1000000;
+-
+-            rv = select(s + 1, &fdset, NULL, NULL, &tmv);
+-            err = errno;
+-
+-            gettimeofday(&t1, NULL);
+-
+-            dt = (t1.tv_sec - t0.tv_sec) * 1000000 +
+-		 (t1.tv_usec - t0.tv_usec);
+-            *timeout_us_p = timeout_left =
+-                (dt >= timeout_us) ? 1 : (timeout_us - dt);
+-        } while (rv == -1 && err == EINTR);
+-
+-        if (rv == 0) {
+-            timer(0);           /* Should not return */
+-            return -1;
+-        }
+-
+-        set_socket_nonblock(s, 1);
+-        rv = recv(s, rbuf, len, flags);
+-        err = errno;
+-        set_socket_nonblock(s, 0);
+-
+-        if (rv < 0) {
+-            if (E_WOULD_BLOCK(err) || err == EINTR) {
+-                continue;       /* Once again, with feeling... */
+-            } else {
+-                errno = err;
+-                return rv;
+-            }
+-        } else {
+-            return rv;
+-        }
+-    }
+-}
+-
+ static int split_port(char **ap, char **pp)
+ {
+     char *a, *p;
+@@ -325,7 +255,7 @@ static int split_port(char **ap, char **pp)
+ enum long_only_options {
+     OPT_VERBOSITY	= 256,
+ };
+-    
++
+ static struct option long_options[] = {
+     { "ipv4",        0, NULL, '4' },
+     { "ipv6",        0, NULL, '6' },
+@@ -389,7 +319,7 @@ int main(int argc, char **argv)
+     char envtz[10];
+     my_time = time(NULL);
+     p_tm = localtime(&my_time);
+-    snprintf(envtz, sizeof(envtz) - 1, "UTC%+d", (p_tm->tm_gmtoff * -1)/3600);
++    snprintf(envtz, sizeof(envtz) - 1, "UTC%+ld", (p_tm->tm_gmtoff * -1)/3600);
+     setenv("TZ", envtz, 0);
+ 
+     /* basename() is way too much of a pain from a portability standpoint */
+@@ -455,8 +385,7 @@ int main(int argc, char **argv)
+                     syslog(LOG_ERR, "Bad timeout value: %s", optarg);
+                     exit(EX_USAGE);
+                 }
+-                rexmtval = timeout = tov;
+-                maxtimeout = rexmtval * TIMEOUT_LIMIT;
++                g_timeout = tov;
+             }
+             break;
+         case 'R':
+@@ -1090,9 +1019,9 @@ int tftp(struct tftphdr *tp, int size)
+     u_short tp_opcode = ntohs(tp->th_opcode);
+ 
+     char *val = NULL, *opt = NULL;
+-    char *ap = ackbuf + 2;
++    char *ap = pktbuf + 2;
+ 
+-    ((struct tftphdr *)ackbuf)->th_opcode = htons(OACK);
++    ((struct tftphdr *)pktbuf)->th_opcode = htons(OACK);
+ 
+     origfilename = cp = (char *)&(tp->th_stuff);
+     argn = 0;
+@@ -1176,11 +1105,11 @@ int tftp(struct tftphdr *tp, int size)
+         exit(0);
+     }
+ 
+-    if (ap != (ackbuf + 2)) {
++    if (ap != (pktbuf + 2)) {
+         if (tp_opcode == WRQ)
+-            (*pf->f_recv) (pf, (struct tftphdr *)ackbuf, ap - ackbuf);
++            (*pf->f_recv) (pf, (struct tftphdr *)pktbuf, ap - pktbuf);
+         else
+-            (*pf->f_send) (pf, (struct tftphdr *)ackbuf, ap - ackbuf, origfilename);
++            (*pf->f_send) (pf, (struct tftphdr *)pktbuf, ap - pktbuf, origfilename);
+     } else {
+         if (tp_opcode == WRQ)
+             (*pf->f_recv) (pf, NULL, 0);
+@@ -1248,7 +1177,7 @@ static int set_blksize2(uintmax_t *vp)
+ static int set_rollover(uintmax_t *vp)
+ {
+     uintmax_t ro = *vp;
+-    
++
+     if (ro > 65535)
+ 	return 0;
+ 
+@@ -1287,8 +1216,7 @@ static int set_timeout(uintmax_t *vp)
+     if (to < 1 || to > 255)
+         return 0;
+ 
+-    rexmtval = timeout = to * 1000000UL;
+-    maxtimeout = rexmtval * TIMEOUT_LIMIT;
++    g_timeout = to * 1000;
+ 
+     return 1;
+ }
+@@ -1301,8 +1229,20 @@ static int set_utimeout(uintmax_t *vp)
+     if (to < 10000UL || to > 255000000UL)
+         return 0;
+ 
+-    rexmtval = timeout = to;
+-    maxtimeout = rexmtval * TIMEOUT_LIMIT;
++    g_timeout = to / 1000;
++
++    return 1;
++}
++
++/*
++ * Set window size (c.f. RFC7440)
++ */
++static int set_windowsize(uintmax_t *vp)
++{
++    if (*vp < 1 || *vp > MAX_WINDOWSIZE)
++        return 0;
++
++    windowsize = *vp;
+ 
+     return 1;
+ }
+@@ -1343,11 +1283,11 @@ static void do_opt(const char *opt, const char *val, char **ap)
+ 		optlen = strlen(opt);
+ 		retlen = sprintf(retbuf, "%"PRIuMAX, v);
+ 
+-                if (p + optlen + retlen + 2 >= ackbuf + sizeof(ackbuf)) {
++                if (p + optlen + retlen + 2 >= pktbuf + sizeof(pktbuf)) {
+                     nak(EOPTNEG, "Insufficient space for options");
+                     exit(0);
+                 }
+-		
++
+ 		memcpy(p, opt, optlen+1);
+ 		p += optlen+1;
+ 		memcpy(p, retbuf, retlen+1);
+@@ -1568,104 +1508,63 @@ static int validate_access(char *filename, int mode,
+  */
+ static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oacklen, char *filename)
+ {
+-    struct tftphdr *dp;
+-    struct tftphdr *ap;         /* ack packet */
+-    static u_short block = 1;   /* Static to avoid longjmp funnies */
+-    u_short ap_opcode, ap_block;
+-    unsigned long r_timeout;
+-    int size, n;
+-
++    struct tftphdr *tp = (struct tftphdr *)pktbuf;
++    unsigned short tp_opcode, tp_block;
++    int retries = RETRIES;
++    int timed_out = 0;
++    int n, r = 0;
++    (void) pf;
++
++    set_verbose(verbosity);
+     if (oap) {
+-        timeout = rexmtval;
+-        (void)sigsetjmp(timeoutbuf, 1);
+-      oack:
+-        r_timeout = timeout;
+-        if (send(peer, oap, oacklen, 0) != oacklen) {
+-            syslog(LOG_WARNING, "tftpd: oack: %m\n");
+-            goto abort;
+-        }
+-        for (;;) {
+-            n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout);
+-            if (n < 0) {
+-                syslog(LOG_WARNING, "tftpd: read: %m\n");
+-                goto abort;
+-            }
+-            ap = (struct tftphdr *)ackbuf;
+-            ap_opcode = ntohs((u_short) ap->th_opcode);
+-            ap_block = ntohs((u_short) ap->th_block);
+-
+-            if (ap_opcode == ERROR) {
+-                syslog(LOG_WARNING,
+-                       "tftp: client does not accept options\n");
++        do {
++            if (send(peer, oap, oacklen, 0) != oacklen) {
++                syslog(LOG_WARNING, "tftpd: oack: %m\n");
+                 goto abort;
+             }
+-            if (ap_opcode == ACK) {
+-                if (ap_block == 0)
+-                    break;
+-                /* Resynchronize with the other side */
+-                (void)synchnet(peer);
+-                goto oack;
+-            }
+-        }
+-    }
+ 
+-    dp = r_init();
+-    do {
+-        size = readit(file, &dp, pf->f_convert);
+-        if (size < 0) {
+-            nak(errno + 100, NULL);
+-            goto abort;
+-        }
+-        dp->th_opcode = htons((u_short) DATA);
+-        dp->th_block = htons((u_short) block);
+-        timeout = rexmtval;
+-        (void)sigsetjmp(timeoutbuf, 1);
+-
+-        r_timeout = timeout;
+-        if (send(peer, dp, size + 4, 0) != size + 4) {
+-            syslog(LOG_WARNING, "tftpd: write: %m");
+-            goto abort;
+-        }
+-        read_ahead(file, pf->f_convert);
+-        for (;;) {
+-            n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout);
++            n = recv_with_timeout(peer, pktbuf, sizeof(pktbuf), g_timeout);
+             if (n < 0) {
+-                syslog(LOG_WARNING, "tftpd: read(ack): %m");
++                syslog(LOG_WARNING, "tftpd: recv: %m");
+                 goto abort;
++            } else if (n == 0) {
++                if (--retries <= 0) {
++                    timed_out = 1;
++                    goto abort;
++                }
++                continue;
+             }
+-            ap = (struct tftphdr *)ackbuf;
+-            ap_opcode = ntohs((u_short) ap->th_opcode);
+-            ap_block = ntohs((u_short) ap->th_block);
+ 
+-            if (ap_opcode == ERROR)
+-                goto abort;
++            tp_opcode = ntohs(tp->th_opcode);
++            tp_block  = ntohs(tp->th_block);
+ 
+-            if (ap_opcode == ACK) {
+-                if (ap_block == block) {
+-                    break;
+-                }
+-                /* Re-synchronize with the other side */
+-                (void)synchnet(peer);
+-                /*
+-                 * RFC1129/RFC1350: We MUST NOT re-send the DATA
+-                 * packet in response to an invalid ACK.  Doing so
+-                 * would cause the Sorcerer's Apprentice bug.
+-                 */
++            if (tp_opcode == ERROR) {
++                char error[ERROR_MAXLEN];
++
++                format_error(tp, error);
++                syslog(LOG_WARNING, "%s", error);
++                goto abort;
++            } else if (!(tp_opcode == ACK && tp_block == 0)) {
++                syslog(LOG_WARNING, "unexpected packet %s block=%u", opcode_to_str(tp_opcode), tp_block);
++                send_error(peer, NULL, "Unexpected packet");
++                exit(1);
+             }
++        } while (n == 0);
++    }
++
++    r = sender(peer, NULL, segsize, windowsize, TIMEOUT, rollover_val, file, NULL);
+ 
+-        }
+-	if (!++block)
+-	  block = rollover_val;
+-    } while (size == segsize);
+     tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from),
+-                                          tmpbuf, INET6_ADDRSTRLEN);
++                              tmpbuf, INET6_ADDRSTRLEN);
+     if (!tmp_p) {
+             tmp_p = tmpbuf;
+             strcpy(tmpbuf, "???");
+     }
+-    syslog(LOG_NOTICE, "Client %s finished %s",tmp_p,filename);
+-  abort:
+-    (void)fclose(file);
++    syslog(LOG_NOTICE, "Client %s finished %s", tmp_p, filename);
++abort:
++    if (timed_out || r == E_TIMED_OUT)
++        syslog(LOG_NOTICE, "Client %s timed out", tmp_p);
++    fclose(file);
+ }
+ 
+ /*
+@@ -1673,90 +1572,44 @@ static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oac
+  */
+ static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen)
+ {
+-    struct tftphdr *dp;
+-    int n, size;
+-    /* These are "static" to avoid longjmp funnies */
+-    static struct tftphdr *ap;  /* ack buffer */
+-    static u_short block = 0;
+-    static int acksize;
+-    u_short dp_opcode, dp_block;
+-    unsigned long r_timeout;
+-
+-    dp = w_init();
+-    do {
+-        timeout = rexmtval;
+-
+-        if (!block && oap) {
+-            ap = (struct tftphdr *)ackbuf;
+-            acksize = oacklen;
+-        } else {
+-            ap = (struct tftphdr *)ackbuf;
+-            ap->th_opcode = htons((u_short) ACK);
+-            ap->th_block = htons((u_short) block);
+-            acksize = 4;
+-            /* If we're sending a regular ACK, that means we have successfully
+-             * sent the OACK. Clear oap so that we won't try to send another
+-             * OACK when the block number wraps back to 0. */
+-            oap = NULL;
+-        }
+-        if (!++block)
+-	  block = rollover_val;
+-        (void)sigsetjmp(timeoutbuf, 1);
+-      send_ack:
+-        r_timeout = timeout;
+-        if (send(peer, ackbuf, acksize, 0) != acksize) {
+-            syslog(LOG_WARNING, "tftpd: write(ack): %m");
+-            goto abort;
+-        }
+-        write_behind(file, pf->f_convert);
+-        for (;;) {
+-            n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout);
+-            if (n < 0) {        /* really? */
+-                syslog(LOG_WARNING, "tftpd: read: %m");
++    int retries = RETRIES;
++    int timed_out = 0;
++    int r;
++    (void) pf;
++
++    set_verbose(verbosity);
++    if (oap) {
++        do {
++            if (send(peer, oap, oacklen, 0) != oacklen) {
++                syslog(LOG_WARNING, "tftpd: oack: %m\n");
+                 goto abort;
+             }
+-            dp_opcode = ntohs((u_short) dp->th_opcode);
+-            dp_block = ntohs((u_short) dp->th_block);
+-            if (dp_opcode == ERROR)
+-                goto abort;
+-            if (dp_opcode == DATA) {
+-                if (dp_block == block) {
+-                    break;      /* normal */
++            r = recvfrom_flags_with_timeout(peer, pktbuf, sizeof(pktbuf), NULL, TIMEOUT, MSG_PEEK);
++            if (r == 0) {
++                if (--retries <= 0) {
++                    timed_out = 1;
++                    goto abort;
+                 }
+-                /* Re-synchronize with the other side */
+-                (void)synchnet(peer);
+-                if (dp_block == (block - 1))
+-                    goto send_ack;      /* rexmit */
+             }
+-        }
+-        /*  size = write(file, dp->th_data, n - 4); */
+-        size = writeit(file, &dp, n - 4, pf->f_convert);
+-        if (size != (n - 4)) {  /* ahem */
+-            if (size < 0)
+-                nak(errno + 100, NULL);
+-            else
+-                nak(ENOSPACE, NULL);
+-            goto abort;
+-        }
+-    } while (size == segsize);
+-    write_behind(file, pf->f_convert);
+-    (void)fclose(file);         /* close data file */
+-
+-    ap->th_opcode = htons((u_short) ACK);       /* send the "final" ack */
+-    ap->th_block = htons((u_short) (block));
+-    (void)send(peer, ackbuf, 4, 0);
+-
+-    timeout_quit = 1;           /* just quit on timeout */
+-    n = recv_time(peer, buf, sizeof(buf), 0, &timeout); /* normally times out and quits */
+-    timeout_quit = 0;
+-
+-    if (n >= 4 &&               /* if read some data */
+-        dp_opcode == DATA &&    /* and got a data block */
+-        block == dp_block) {    /* then my last ack was lost */
+-        (void)send(peer, ackbuf, 4, 0); /* resend final ack */
++        } while (r == 0);
++    } else {
++        do {
++            send_ack(peer, NULL, 0);
++            r = recvfrom_flags_with_timeout(peer, pktbuf, sizeof(pktbuf), NULL, TIMEOUT, MSG_PEEK);
++            if (r == 0) {
++                if (--retries <= 0) {
++                    timed_out = 1;
++                    goto abort;
++                }
++            }
++        } while (r == 0);
+     }
+-  abort:
+-    return;
++
++    r = receiver(peer, NULL, segsize, windowsize, TIMEOUT, file, NULL, NULL);
++abort:
++    if (timed_out || r == E_TIMED_OUT)
++        syslog(LOG_NOTICE, "Client %s timed out", tmp_p);
++    fclose(file);
+ }
+ 
+ static const char *const errmsgs[] = {
diff --git a/SPECS/tftp.spec b/SPECS/tftp.spec
index 6d1141f..b0809e6 100644
--- a/SPECS/tftp.spec
+++ b/SPECS/tftp.spec
@@ -4,7 +4,7 @@
 Summary: The client for the Trivial File Transfer Protocol (TFTP)
 Name: tftp
 Version: 5.2
-Release: 13%{?dist}
+Release: 22%{?dist}
 License: BSD
 Group: Applications/Internet
 URL: http://www.kernel.org/pub/software/network/tftp/
@@ -22,6 +22,8 @@ Patch7: tftp-hpa-0.49-stats.patch
 Patch8: tftp-hpa-5.2-pktinfo.patch
 Patch9: tftp-doc.patch
 Patch10: tftp-enhanced-logging.patch
+Patch11: tftp-rfc7440-windowsize.patch
+Patch12: tftp-rewrite-macro.patch
 
 BuildRequires: tcp_wrappers-devel readline-devel autoconf systemd-units
 
@@ -58,6 +60,8 @@ enabled unless it is expressly needed.
 %patch8 -p1 -b .pktinfo
 %patch9 -p1 -b .doc
 %patch10 -p1 -b .logging
+%patch11 -p1 -b .windowsize
+%patch12 -p1 -b .rewrite-macro
 
 %build
 autoreconf
@@ -108,6 +112,33 @@ rm -rf ${RPM_BUILD_ROOT}
 %{_unitdir}/*
 
 %changelog
+* Wed Feb  7 2018 Jan Synáček <jsynacek@redhat.com> - 5.2-22
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Wed Feb  7 2018 Jan Synáček <jsynacek@redhat.com> - 5.2-21
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Tue Jan 23 2018 Jan Synáček <jsynacek@redhat.com> - 5.2-20
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Tue Jan  9 2018 Jan Synáček <jsynacek@redhat.com> - 5.2-19
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Tue Nov 28 2017 Jan Synáček <jsynacek@redhat.com> - 5.2-18
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Thu Nov 16 2017 Jan Synáček <jsynacek@redhat.com> - 5.2.17
+- re: implement RFC7440 TFTP Windowsize Option (#1328827)
+
+* Mon Sep 11 2017 Jan Synáček <jsynacek@redhat.com> - 5.2-16
+- inconsistent --mapfile / --map-file option spelling in manual (#1490139)
+
+* Tue Sep  5 2017 Jan Synáček <jsynacek@redhat.com> - 5.2-15
+- memory corruption in tftpd when using filename remapping (#1485943)
+
+* Mon Aug 14 2017 Jan Synáček <jsynacek@redhat.com> - 5.2-14
+- implement RFC7440 TFTP Windowsize Option (#1328827)
+
 * Wed Mar  2 2016 Jan Synáček <jsynacek@redhat.com> - 5.2-13
 - enhance in.tftpd logging capabilities (#1311092)