diff options
author | Anas Nashif <anas.nashif@intel.com> | 2013-01-15 13:31:42 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2013-01-15 13:31:42 -0800 |
commit | 42bf3037d458a330856a0be584200c1e41c3f417 (patch) | |
tree | 25b9be1088727757e52271e25a446e8a852357df /slirp | |
parent | 060629c6ef0b7e5c267d84c91600113264d33120 (diff) | |
download | qemu-42bf3037d458a330856a0be584200c1e41c3f417.tar.gz qemu-42bf3037d458a330856a0be584200c1e41c3f417.tar.bz2 qemu-42bf3037d458a330856a0be584200c1e41c3f417.zip |
Imported Upstream version 1.3.0upstream/1.3.0
Diffstat (limited to 'slirp')
-rw-r--r-- | slirp/Makefile.objs | 2 | ||||
-rw-r--r-- | slirp/arp_table.c | 4 | ||||
-rw-r--r-- | slirp/bootp.c | 12 | ||||
-rw-r--r-- | slirp/dnssearch.c | 314 | ||||
-rw-r--r-- | slirp/ip_icmp.h | 4 | ||||
-rw-r--r-- | slirp/ip_input.c | 1 | ||||
-rw-r--r-- | slirp/libslirp.h | 3 | ||||
-rw-r--r-- | slirp/misc.c | 14 | ||||
-rw-r--r-- | slirp/misc.h | 1 | ||||
-rw-r--r-- | slirp/slirp.c | 8 | ||||
-rw-r--r-- | slirp/slirp.h | 5 | ||||
-rw-r--r-- | slirp/tcp_input.c | 2 | ||||
-rw-r--r-- | slirp/tcp_subr.c | 8 | ||||
-rw-r--r-- | slirp/tftp.c | 104 | ||||
-rw-r--r-- | slirp/tftp.h | 2 | ||||
-rw-r--r-- | slirp/udp.c | 1 |
16 files changed, 414 insertions, 71 deletions
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs index bb43d3c08..2daa9dc58 100644 --- a/slirp/Makefile.objs +++ b/slirp/Makefile.objs @@ -1,3 +1,3 @@ -common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o +common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o diff --git a/slirp/arp_table.c b/slirp/arp_table.c index 5d7b8acd1..bf698c1ac 100644 --- a/slirp/arp_table.c +++ b/slirp/arp_table.c @@ -38,7 +38,9 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) ethaddr[3], ethaddr[4], ethaddr[5])); /* Check 0.0.0.0/8 invalid source-only addresses */ - assert((ip_addr & htonl(~(0xf << 28))) != 0); + if ((ip_addr & htonl(~(0xf << 28))) == 0) { + return; + } if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { /* Do not register broadcast addresses */ diff --git a/slirp/bootp.c b/slirp/bootp.c index 64eac7d10..b7db9fa33 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -287,6 +287,18 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) memcpy(q, slirp->client_hostname, val); q += val; } + + if (slirp->vdnssearch) { + size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); + val = slirp->vdnssearch_len; + if (val + 1 > spaceleft) { + g_warning("DHCP packet size exceeded, " + "omitting domain-search option."); + } else { + memcpy(q, slirp->vdnssearch, val); + q += val; + } + } } else { static const char nak_msg[] = "requested address not available"; diff --git a/slirp/dnssearch.c b/slirp/dnssearch.c new file mode 100644 index 000000000..4c9064ecb --- /dev/null +++ b/slirp/dnssearch.c @@ -0,0 +1,314 @@ +/* + * Domain search option for DHCP (RFC 3397) + * + * Copyright (c) 2012 Klaus Stengel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <glib.h> +#include "slirp.h" + +static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119; +static const uint8_t MAX_OPT_LEN = 255; +static const uint8_t OPT_HEADER_LEN = 2; +static const uint8_t REFERENCE_LEN = 2; + +struct compact_domain; + +typedef struct compact_domain { + struct compact_domain *self; + struct compact_domain *refdom; + uint8_t *labels; + size_t len; + size_t common_octets; +} CompactDomain; + +static size_t +domain_suffix_diffoff(const CompactDomain *a, const CompactDomain *b) +{ + size_t la = a->len, lb = b->len; + uint8_t *da = a->labels + la, *db = b->labels + lb; + size_t i, lm = (la < lb) ? la : lb; + + for (i = 0; i < lm; i++) { + da--; db--; + if (*da != *db) { + break; + } + } + return i; +} + +static int domain_suffix_ord(const void *cva, const void *cvb) +{ + const CompactDomain *a = cva, *b = cvb; + size_t la = a->len, lb = b->len; + size_t doff = domain_suffix_diffoff(a, b); + uint8_t ca = a->labels[la - doff]; + uint8_t cb = b->labels[lb - doff]; + + if (ca < cb) { + return -1; + } + if (ca > cb) { + return 1; + } + if (la < lb) { + return -1; + } + if (la > lb) { + return 1; + } + return 0; +} + +static size_t domain_common_label(CompactDomain *a, CompactDomain *b) +{ + size_t res, doff = domain_suffix_diffoff(a, b); + uint8_t *first_eq_pos = a->labels + (a->len - doff); + uint8_t *label = a->labels; + + while (*label && label < first_eq_pos) { + label += *label + 1; + } + res = a->len - (label - a->labels); + /* only report if it can help to reduce the packet size */ + return (res > REFERENCE_LEN) ? res : 0; +} + +static void domain_fixup_order(CompactDomain *cd, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + CompactDomain *cur = cd + i, *next = cd[i].self; + + while (!cur->common_octets) { + CompactDomain *tmp = next->self; /* backup target value */ + + next->self = cur; + cur->common_octets++; + + cur = next; + next = tmp; + } + } +} + +static void domain_mklabels(CompactDomain *cd, const char *input) +{ + uint8_t *len_marker = cd->labels; + uint8_t *output = len_marker; /* pre-incremented */ + const char *in = input; + char cur_chr; + size_t len = 0; + + if (cd->len == 0) { + goto fail; + } + cd->len++; + + do { + cur_chr = *in++; + if (cur_chr == '.' || cur_chr == '\0') { + len = output - len_marker; + if ((len == 0 && cur_chr == '.') || len >= 64) { + goto fail; + } + *len_marker = len; + + output++; + len_marker = output; + } else { + output++; + *output = cur_chr; + } + } while (cur_chr != '\0'); + + /* ensure proper zero-termination */ + if (len != 0) { + *len_marker = 0; + cd->len++; + } + return; + +fail: + g_warning("failed to parse domain name '%s'\n", input); + cd->len = 0; +} + +static void +domain_mkxrefs(CompactDomain *doms, CompactDomain *last, size_t depth) +{ + CompactDomain *i = doms, *target = doms; + + do { + if (i->labels < target->labels) { + target = i; + } + } while (i++ != last); + + for (i = doms; i != last; i++) { + CompactDomain *group_last; + size_t next_depth; + + if (i->common_octets == depth) { + continue; + } + + next_depth = -1; + for (group_last = i; group_last != last; group_last++) { + size_t co = group_last->common_octets; + if (co <= depth) { + break; + } + if (co < next_depth) { + next_depth = co; + } + } + domain_mkxrefs(i, group_last, next_depth); + + i = group_last; + if (i == last) { + break; + } + } + + if (depth == 0) { + return; + } + + i = doms; + do { + if (i != target && i->refdom == NULL) { + i->refdom = target; + i->common_octets = depth; + } + } while (i++ != last); +} + +static size_t domain_compactify(CompactDomain *domains, size_t n) +{ + uint8_t *start = domains->self->labels, *outptr = start; + size_t i; + + for (i = 0; i < n; i++) { + CompactDomain *cd = domains[i].self; + CompactDomain *rd = cd->refdom; + + if (rd != NULL) { + size_t moff = (rd->labels - start) + + (rd->len - cd->common_octets); + if (moff < 0x3FFFu) { + cd->len -= cd->common_octets - 2; + cd->labels[cd->len - 1] = moff & 0xFFu; + cd->labels[cd->len - 2] = 0xC0u | (moff >> 8); + } + } + + if (cd->labels != outptr) { + memmove(outptr, cd->labels, cd->len); + cd->labels = outptr; + } + outptr += cd->len; + } + return outptr - start; +} + +int translate_dnssearch(Slirp *s, const char **names) +{ + size_t blocks, bsrc_start, bsrc_end, bdst_start; + size_t i, num_domains, memreq = 0; + uint8_t *result = NULL, *outptr; + CompactDomain *domains = NULL; + const char **nameptr = names; + + while (*nameptr != NULL) { + nameptr++; + } + + num_domains = nameptr - names; + if (num_domains == 0) { + return -2; + } + + domains = g_malloc(num_domains * sizeof(*domains)); + + for (i = 0; i < num_domains; i++) { + size_t nlen = strlen(names[i]); + memreq += nlen + 2; /* 1 zero octet + 1 label length octet */ + domains[i].self = domains + i; + domains[i].len = nlen; + domains[i].common_octets = 0; + domains[i].refdom = NULL; + } + + /* reserve extra 2 header bytes for each 255 bytes of output */ + memreq += ((memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN) * OPT_HEADER_LEN; + result = g_malloc(memreq * sizeof(*result)); + + outptr = result; + for (i = 0; i < num_domains; i++) { + domains[i].labels = outptr; + domain_mklabels(domains + i, names[i]); + outptr += domains[i].len; + } + + if (outptr == result) { + g_free(domains); + g_free(result); + return -1; + } + + qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord); + domain_fixup_order(domains, num_domains); + + for (i = 1; i < num_domains; i++) { + size_t cl = domain_common_label(domains + i - 1, domains + i); + domains[i - 1].common_octets = cl; + } + + domain_mkxrefs(domains, domains + num_domains - 1, 0); + memreq = domain_compactify(domains, num_domains); + + blocks = (memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN; + bsrc_end = memreq; + bsrc_start = (blocks - 1) * MAX_OPT_LEN; + bdst_start = bsrc_start + blocks * OPT_HEADER_LEN; + memreq += blocks * OPT_HEADER_LEN; + + while (blocks--) { + size_t len = bsrc_end - bsrc_start; + memmove(result + bdst_start, result + bsrc_start, len); + result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH; + result[bdst_start - 1] = len; + bsrc_end = bsrc_start; + bsrc_start -= MAX_OPT_LEN; + bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN; + } + + g_free(domains); + s->vdnssearch = result; + s->vdnssearch_len = memreq; + return 0; +} diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h index 1a1af91e1..be4426b8e 100644 --- a/slirp/ip_icmp.h +++ b/slirp/ip_icmp.h @@ -92,8 +92,8 @@ struct icmp { /* * Lower bounds on packet lengths for various types. - * For the error advice packets must first insure that the - * packet is large enought to contain the returned ip header. + * For the error advice packets must first ensure that the + * packet is large enough to contain the returned ip header. * Only then can we do the check to see if 64 bits of packet * data have been returned, since we need to check the returned * ip header length. diff --git a/slirp/ip_input.c b/slirp/ip_input.c index ce24faf16..6f4cff8fd 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -213,7 +213,6 @@ ip_input(struct mbuf *m) return; bad: m_free(m); - return; } #define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink))) diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 9b471b505..49609c2ad 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -12,7 +12,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, struct in_addr vnetmask, struct in_addr vhost, const char *vhostname, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, void *opaque); + struct in_addr vnameserver, const char **vdnssearch, + void *opaque); void slirp_cleanup(Slirp *slirp); void slirp_update_timeout(uint32_t *timeout); diff --git a/slirp/misc.c b/slirp/misc.c index 0bee86433..664532a66 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -253,20 +253,6 @@ void lprint(const char *format, ...) va_end(args); } -void -u_sleep(int usec) -{ - struct timeval t; - fd_set fdset; - - FD_ZERO(&fdset); - - t.tv_sec = 0; - t.tv_usec = usec * 1000; - - select(0, &fdset, &fdset, &fdset, &t); -} - void slirp_connection_info(Slirp *slirp, Monitor *mon) { const char * const tcpstates[] = { diff --git a/slirp/misc.h b/slirp/misc.h index ed40a103c..cc36aeb95 100644 --- a/slirp/misc.h +++ b/slirp/misc.h @@ -64,7 +64,6 @@ void snooze_hup(int); void snooze(void); void relay(int); void add_emu(char *); -void u_sleep(int); void fd_nonblock(int); void fd_block(int); int rsh_exec(struct socket *, struct socket *, char *, char *, char *); diff --git a/slirp/slirp.c b/slirp/slirp.c index 38e0a2193..3395d509a 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -203,7 +203,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, struct in_addr vnetmask, struct in_addr vhost, const char *vhostname, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, void *opaque) + struct in_addr vnameserver, const char **vdnssearch, + void *opaque) { Slirp *slirp = g_malloc0(sizeof(Slirp)); @@ -233,6 +234,10 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; + if (vdnssearch) { + translate_dnssearch(slirp, vdnssearch); + } + slirp->opaque = opaque; register_savevm(NULL, "slirp", 0, 3, @@ -252,6 +257,7 @@ void slirp_cleanup(Slirp *slirp) ip_cleanup(slirp); m_cleanup(slirp); + g_free(slirp->vdnssearch); g_free(slirp->tftp_prefix); g_free(slirp->bootp_filename); g_free(slirp); diff --git a/slirp/slirp.h b/slirp/slirp.h index f2c5eca89..0107b07e6 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -235,6 +235,8 @@ struct Slirp { /* bootp/dhcp states */ BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; char *bootp_filename; + size_t vdnssearch_len; + uint8_t *vdnssearch; /* tcp states */ struct socket tcb; @@ -294,6 +296,9 @@ void lprint(const char *, ...) GCC_FMT_ATTR(1, 2); #define SO_OPTIONS DO_KEEPALIVE #define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL) +/* dnssearch.c */ +int translate_dnssearch(Slirp *s, const char ** names); + /* cksum.c */ int cksum(struct mbuf *m, int len); diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c index 942aaf44f..6440eae7f 100644 --- a/slirp/tcp_input.c +++ b/slirp/tcp_input.c @@ -1281,8 +1281,6 @@ drop: * Drop space held by incoming segment and return. */ m_free(m); - - return; } static void diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index 025b37436..1542e4361 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -114,9 +114,9 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, int win = 0; DEBUG_CALL("tcp_respond"); - DEBUG_ARG("tp = %lx", (long)tp); - DEBUG_ARG("ti = %lx", (long)ti); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("tp = %p", tp); + DEBUG_ARG("ti = %p", ti); + DEBUG_ARG("m = %p", m); DEBUG_ARG("ack = %u", ack); DEBUG_ARG("seq = %u", seq); DEBUG_ARG("flags = %x", flags); @@ -124,7 +124,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, if (tp) win = sbspace(&tp->t_socket->so_rcv); if (m == NULL) { - if ((m = m_get(tp->t_socket->slirp)) == NULL) + if (!tp || (m = m_get(tp->t_socket->slirp)) == NULL) return; tlen = 0; m->m_data += IF_MAXLINKHDR; 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) diff --git a/slirp/tftp.h b/slirp/tftp.h index 72e5e91be..51704e487 100644 --- a/slirp/tftp.h +++ b/slirp/tftp.h @@ -33,9 +33,11 @@ struct tftp_t { struct tftp_session { Slirp *slirp; char *filename; + int fd; struct in_addr client_ip; uint16_t client_port; + uint32_t block_nr; int timestamp; }; diff --git a/slirp/udp.c b/slirp/udp.c index ced509656..9286cb7d3 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -231,7 +231,6 @@ udp_input(register struct mbuf *m, int iphlen) return; bad: m_free(m); - return; } int udp_output2(struct socket *so, struct mbuf *m, |