diff options
Diffstat (limited to 'slirp/tftp.c')
-rw-r--r-- | slirp/tftp.c | 104 |
1 files changed, 62 insertions, 42 deletions
diff --git a/slirp/tftp.c b/slirp/tftp.c index b78765f3a..1a79c45cf 100644 --- a/slirp/tftp.c +++ b/slirp/tftp.c @@ -37,6 +37,10 @@ static inline void tftp_session_update(struct tftp_session *spt) static void tftp_session_terminate(struct tftp_session *spt) { + if (spt->fd >= 0) { + close(spt->fd); + spt->fd = -1; + } g_free(spt->filename); spt->slirp = NULL; } @@ -54,7 +58,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) /* sessions time out after 5 inactive seconds */ if ((int)(curtime - spt->timestamp) > 5000) { - g_free(spt->filename); + tftp_session_terminate(spt); goto found; } } @@ -64,6 +68,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) found: memset(spt, 0, sizeof(*spt)); memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); + spt->fd = -1; spt->client_port = tp->udp.uh_sport; spt->slirp = slirp; @@ -92,37 +97,36 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp) return -1; } -static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr, +static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr, uint8_t *buf, int len) { - int fd; - int bytes_read = 0; + int bytes_read = 0; - fd = open(spt->filename, O_RDONLY | O_BINARY); + if (spt->fd < 0) { + spt->fd = open(spt->filename, O_RDONLY | O_BINARY); + } - if (fd < 0) { - return -1; - } + if (spt->fd < 0) { + return -1; + } - if (len) { - lseek(fd, block_nr * 512, SEEK_SET); + if (len) { + lseek(spt->fd, block_nr * 512, SEEK_SET); - bytes_read = read(fd, buf, len); - } - - close(fd); + bytes_read = read(spt->fd, buf, len); + } - return bytes_read; + return bytes_read; } static int tftp_send_oack(struct tftp_session *spt, - const char *key, uint32_t value, + const char *keys[], uint32_t values[], int nb, struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; - int n = 0; + int i, n = 0; m = m_get(spt->slirp); @@ -136,10 +140,12 @@ static int tftp_send_oack(struct tftp_session *spt, m->m_data += sizeof(struct udpiphdr); tp->tp_op = htons(TFTP_OACK); - n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", - key) + 1; - n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", - value) + 1; + for (i = 0; i < nb; i++) { + n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", + keys[i]) + 1; + n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", + values[i]) + 1; + } saddr.sin_addr = recv_tp->ip.ip_dst; saddr.sin_port = recv_tp->udp.uh_dport; @@ -193,23 +199,18 @@ out: tftp_session_terminate(spt); } -static int tftp_send_data(struct tftp_session *spt, - uint16_t block_nr, - struct tftp_t *recv_tp) +static void tftp_send_next_block(struct tftp_session *spt, + struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; int nobytes; - if (block_nr < 1) { - return -1; - } - m = m_get(spt->slirp); if (!m) { - return -1; + return; } memset(m->m_data, 0, m->m_size); @@ -219,7 +220,7 @@ static int tftp_send_data(struct tftp_session *spt, m->m_data += sizeof(struct udpiphdr); tp->tp_op = htons(TFTP_DATA); - tp->x.tp_data.tp_block_nr = htons(block_nr); + tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff); saddr.sin_addr = recv_tp->ip.ip_dst; saddr.sin_port = recv_tp->udp.uh_dport; @@ -227,7 +228,7 @@ static int tftp_send_data(struct tftp_session *spt, daddr.sin_addr = spt->client_ip; daddr.sin_port = spt->client_port; - nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512); + nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512); if (nobytes < 0) { m_free(m); @@ -236,7 +237,7 @@ static int tftp_send_data(struct tftp_session *spt, tftp_send_error(spt, 1, "File not found", tp); - return -1; + return; } m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - @@ -251,7 +252,7 @@ static int tftp_send_data(struct tftp_session *spt, tftp_session_terminate(spt); } - return 0; + spt->block_nr++; } static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) @@ -260,6 +261,9 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) int s, k; size_t prefix_len; char *req_fname; + const char *option_name[2]; + uint32_t option_value[2]; + int nb_options = 0; /* check if a session already exists and if so terminate it */ s = tftp_session_find(slirp, tp); @@ -337,7 +341,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) return; } - while (k < pktlen) { + while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) { const char *key, *value; key = &tp->x.tp_buf[k]; @@ -364,12 +368,32 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) } } - tftp_send_oack(spt, "tsize", tsize, tp); - return; + option_name[nb_options] = "tsize"; + option_value[nb_options] = tsize; + nb_options++; + } else if (strcasecmp(key, "blksize") == 0) { + int blksize = atoi(value); + + /* If blksize option is bigger than what we will + * emit, accept the option with our packet size. + * Otherwise, simply do as we didn't see the option. + */ + if (blksize >= 512) { + option_name[nb_options] = "blksize"; + option_value[nb_options] = 512; + nb_options++; + } } } - tftp_send_data(spt, 1, tp); + if (nb_options > 0) { + assert(nb_options <= ARRAY_SIZE(option_name)); + tftp_send_oack(spt, option_name, option_value, nb_options, tp); + return; + } + + spt->block_nr = 0; + tftp_send_next_block(spt, tp); } static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen) @@ -382,11 +406,7 @@ static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen) return; } - if (tftp_send_data(&slirp->tftp_sessions[s], - ntohs(tp->x.tp_data.tp_block_nr) + 1, - tp) < 0) { - return; - } + tftp_send_next_block(&slirp->tftp_sessions[s], tp); } static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen) |