diff --git a/src/blockdata.c b/src/blockdata.c index 9669876..9768369 100644 --- a/src/blockdata.c +++ b/src/blockdata.c @@ -101,7 +101,7 @@ static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) memcpy(block->key, data, blen); data += blen; } - else if (!read_write(fd, block->key, blen, 1)) + else if (!read_write(fd, block->key, blen, RW_READ)) { /* failed read free partial chain */ blockdata_free(ret); @@ -228,7 +228,7 @@ void blockdata_write(struct blockdata *block, size_t len, int fd) for (; len > 0 && block; block = block->next) { size_t blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; - read_write(fd, block->key, blen, 0); + read_write(fd, block->key, blen, RW_WRITE); len -= blen; } } diff --git a/src/cache.c b/src/cache.c index 05b666b..7acefc2 100644 --- a/src/cache.c +++ b/src/cache.c @@ -798,11 +798,11 @@ void cache_end_insert(void) u16 class = new_chain->uid; #endif - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)name, m, RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), RW_WRITE); if (flags & F_RR) { @@ -813,12 +813,12 @@ void cache_end_insert(void) #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { - read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE); blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent); } else if (flags & F_DS) { - read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE); /* A negative DS entry is possible and has no data, obviously. */ if (!(flags & F_NEG)) blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent); @@ -835,16 +835,16 @@ void cache_end_insert(void) { ssize_t m = -1; - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); #ifdef HAVE_DNSSEC /* Sneak out possibly updated crypto HWM values. */ m = daemon->metrics[METRIC_CRYPTO_HWM]; - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); m = daemon->metrics[METRIC_SIG_FAIL_HWM]; - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); m = daemon->metrics[METRIC_WORK_HWM]; - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); #endif } @@ -867,22 +867,22 @@ int cache_recv_insert(time_t now, int fd) while (1) { - if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m == -1) { #ifdef HAVE_DNSSEC /* Sneak in possibly updated crypto HWM. */ - if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = m; - if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_SIG_FAIL_HWM]) daemon->metrics[METRIC_SIG_FAIL_HWM] = m; - if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_WORK_HWM]) daemon->metrics[METRIC_WORK_HWM] = m; @@ -902,23 +902,23 @@ int cache_recv_insert(time_t now, int fd) struct frec *forward; - if (!read_write(fd, (unsigned char *)&status, sizeof(status), 1)) + if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), 1)) + if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, 1)) + if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), 1)) + if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), 1)) + if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), 1)) + if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), 1)) + if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), 1)) + if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ)) return 0; - if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), 1)) + if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ)) return 0; /* There's a tiny chance that the frec may have been freed @@ -940,10 +940,10 @@ int cache_recv_insert(time_t now, int fd) } #endif - if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) || - !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) || - !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1) || - !read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) + if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) || + !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) || + !read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) || + !read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ)) return 0; daemon->namebuff[m] = 0; @@ -980,13 +980,13 @@ int cache_recv_insert(time_t now, int fd) #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) || !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) return 0; } else if (flags & F_DS) { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) || (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) return 0; } diff --git a/src/config.h b/src/config.h index 5964c23..5e65df6 100644 --- a/src/config.h +++ b/src/config.h @@ -18,6 +18,7 @@ #define MAX_PROCS 20 /* default max no children for TCP requests */ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */ +#define TCP_TIMEOUT 5 /* timeout waiting to connect to an upstream server - double this for answer */ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ #define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 0c0e082..c14240e 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -683,7 +683,7 @@ int main (int argc, char **argv) if (getuid() == 0 && ent_pw && ent_pw->pw_uid != 0 && fchown(fd, ent_pw->pw_uid, ent_pw->pw_gid) == -1) chown_warn = errno; - if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0)) + if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), RW_WRITE)) err = 1; else { @@ -1396,14 +1396,14 @@ static int read_event(int fd, struct event_desc *evp, char **msg) { char *buf; - if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), 1)) + if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), RW_READ)) return 0; *msg = NULL; if (evp->msg_sz != 0 && (buf = malloc(evp->msg_sz + 1)) && - read_write(fd, (unsigned char *)buf, evp->msg_sz, 1)) + read_write(fd, (unsigned char *)buf, evp->msg_sz, RW_READ)) { buf[evp->msg_sz] = 0; *msg = buf; @@ -1977,7 +1977,7 @@ static void check_dns_listeners(time_t now) netlink socket. */ unsigned char a; - read_write(pipefd[0], &a, 1, 1); + read_write(pipefd[0], &a, 1, RW_READ); #endif /* i holds index of free slot */ @@ -2025,7 +2025,7 @@ static void check_dns_listeners(time_t now) unsigned char a = 0; close(daemon->netlinkfd); - read_write(pipefd[1], &a, 1, 0); + read_write(pipefd[1], &a, 1, RW_WRITE); #endif alarm(CHILD_LIFETIME); close(pipefd[0]); /* close read end in child. */ @@ -2118,7 +2118,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header single byte comes back up the pipe, which is sent by the child after it has closed the netlink socket. */ - read_write(pipefd[0], &a, 1, 1); + read_write(pipefd[0], &a, 1, RW_READ); #endif /* i holds index of free slot */ @@ -2140,7 +2140,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header #ifdef HAVE_LINUX_NETWORK /* See comment above re: netlink socket. */ close(daemon->netlinkfd); - read_write(pipefd[1], &a, 1, 0); + read_write(pipefd[1], &a, 1, RW_WRITE); #endif close(pipefd[0]); /* close read end in child. */ daemon->pipe_to_parent = pipefd[1]; @@ -2164,16 +2164,16 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header ssize_t m = -2; /* tell our parent we're done, and what the result was then exit. */ - read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&forward, sizeof(forward), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&forward->uid, sizeof(forward->uid), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)keycount, sizeof(*keycount), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), 0); - read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), 0); + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&forward, sizeof(forward), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&forward->uid, sizeof(forward->uid), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)keycount, sizeof(*keycount), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), RW_WRITE); + read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), RW_WRITE); close(daemon->pipe_to_parent); flush_log(); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 11daae4..a9019ee 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -453,6 +453,11 @@ struct host_record { #define INP4 4 #define INP6 8 +#define RW_WRITE 0 +#define RW_READ 1 +#define RW_WRITE_ONCE 2 +#define RW_READ_ONCE 3 + struct interface_name { char *name; /* domain name */ char *intr; /* interface name */ diff --git a/src/dump.c b/src/dump.c index 5911c90..38dca7e 100644 --- a/src/dump.c +++ b/src/dump.c @@ -64,18 +64,18 @@ void dump_init(void) if (errno != ENOENT || (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 || - !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0)) + !read_write(daemon->dumpfd, (void *)&header, sizeof(header), RW_WRITE)) die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE); } else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 || - !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1)) + !read_write(daemon->dumpfd, (void *)&header, sizeof(header), RW_READ)) die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE); else if (header.magic_number != 0xa1b2c3d4) die(_("bad header in %s"), daemon->dump_file, EC_FILE); else { /* count existing records */ - while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1)) + while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), RW_READ)) { lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR); packet_count++; @@ -280,10 +280,10 @@ static void do_dump_packet(int mask, void *packet, size_t len, pcap_header.ts_usec = time.tv_usec; if (rc == -1 || - !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) || - !read_write(daemon->dumpfd, iphdr, ipsz, 0) || - (proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) || - !read_write(daemon->dumpfd, (void *)packet, len, 0)) + !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), RW_WRITE) || + !read_write(daemon->dumpfd, iphdr, ipsz, RW_WRITE) || + (proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), RW_WRITE)) || + !read_write(daemon->dumpfd, (void *)packet, len, RW_WRITE)) my_syslog(LOG_ERR, _("failed to write packet dump")); else if (option_bool(OPT_EXTRALOG) && (mask & 0x00ff)) my_syslog(LOG_INFO, _("%u dumping packet %u mask 0x%04x"), daemon->log_display_id, ++packet_count, mask); diff --git a/src/forward.c b/src/forward.c index 4c43fb3..d84b599 100644 --- a/src/forward.c +++ b/src/forward.c @@ -1879,11 +1879,11 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, u16 *length = (u16 *)packet; unsigned char *payload = &packet[2]; struct dns_header *header = (struct dns_header *)payload; - unsigned char c1, c2; unsigned int rsize; int class, rclass, type, rtype; unsigned char *p; struct blockdata *saved_question; + struct timeval tv; (void)mark; (void)have_mark; @@ -1901,7 +1901,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, while (1) { - int data_sent = 0, timedout = 0; + int data_sent = 0, fatal = 0; struct server *serv; if (firstsendto == -1) @@ -1942,12 +1942,17 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, continue; } -#ifdef TCP_SYNCNT - /* TCP connections by default take ages to time out. - At least on Linux, we can reduce that to only two attempts - to get a reply. For DNS, that's more sensible. */ - mark = 2; - setsockopt(serv->tcpfd, IPPROTO_TCP, TCP_SYNCNT, &mark, sizeof(unsigned int)); +#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO) + /* TCP connections by default take ages to time out. + Set shorter timeouts more appropriate for a DNS server. + We set the recieve timeout as twice the send timeout; we + want to fail quickly on a non-responsive server, but give it time to get an + answer. */ + tv.tv_sec = TCP_TIMEOUT; + tv.tv_usec = 0; + setsockopt(serv->tcpfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + tv.tv_sec += TCP_TIMEOUT; + setsockopt(serv->tcpfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); #endif #ifdef MSG_FASTOPEN @@ -1955,13 +1960,13 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, if (errno == 0) data_sent = 1; - else if (errno == ETIMEDOUT || errno == EHOSTUNREACH) - timedout = 1; + else if (errno == ETIMEDOUT || errno == EHOSTUNREACH || errno == EINPROGRESS || errno == ECONNREFUSED) + fatal = 1; #endif /* If fastopen failed due to lack of reply, then there's no point in trying again in non-FASTOPEN mode. */ - if (timedout || (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1)) + if (fatal || (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1)) { close(serv->tcpfd); serv->tcpfd = -1; @@ -1972,10 +1977,11 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, serv->flags &= ~SERV_GOT_TCP; } - if ((!data_sent && !read_write(serv->tcpfd, packet, qsize + sizeof(u16), 0)) || - !read_write(serv->tcpfd, &c1, 1, 1) || - !read_write(serv->tcpfd, &c2, 1, 1) || - !read_write(serv->tcpfd, payload, (rsize = (c1 << 8) | c2), 1)) + /* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket + and wish to abort if the whole data is not read/written within the timeout. */ + if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) || + !read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) || + !read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE)) { close(serv->tcpfd); serv->tcpfd = -1; @@ -2210,7 +2216,7 @@ unsigned char *tcp_request(int confd, time_t now, /* Max TCP packet + slop + size */ unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); unsigned char *payload = &packet[2]; - unsigned char c1, c2; + u16 tcp_len; /* largest field in header is 16-bits, so this is still sufficiently aligned */ struct dns_header *header = (struct dns_header *)payload; u16 *length = (u16 *)packet; @@ -2283,9 +2289,9 @@ unsigned char *tcp_request(int confd, time_t now, if (query_count >= TCP_MAX_QUERIES) break; - if (!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || - !(size = c1 << 8 | c2) || - !read_write(confd, payload, size, 1)) + if (!read_write(confd, (unsigned char *)&tcp_len, sizeof(tcp_len), RW_READ) || + !(size = ntohs(tcp_len)) || + !read_write(confd, payload, size, RW_READ)) break; } @@ -2584,7 +2590,7 @@ unsigned char *tcp_request(int confd, time_t now, if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) report_addresses(header, m, mark); #endif - if (!read_write(confd, packet, m + sizeof(u16), 0)) + if (!read_write(confd, packet, m + sizeof(u16), RW_WRITE)) break; /* If we answered with stale data, this process will now try and get fresh data into diff --git a/src/helper.c b/src/helper.c index b9da225..45316d4 100644 --- a/src/helper.c +++ b/src/helper.c @@ -196,7 +196,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) } /* we read zero bytes when pipe closed: this is our signal to exit */ - if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) + if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), RW_READ)) { #ifdef HAVE_LUASCRIPT if (daemon->luascript) @@ -258,7 +258,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) continue; if (!read_write(pipefd[0], buf, - data.hostname_len + data.ed_len + data.clid_len, 1)) + data.hostname_len + data.ed_len + data.clid_len, RW_READ)) continue; /* CLID into packet */ diff --git a/src/tftp.c b/src/tftp.c index 4421cf9..c52537b 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -845,7 +845,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer) mess->block = htons((unsigned short)(transfer->block)); if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 || - !read_write(transfer->file->fd, mess->data, size, 1)) + !read_write(transfer->file->fd, mess->data, size, RW_READ)) return -1; transfer->expansion = 0; diff --git a/src/util.c b/src/util.c index c527381..89ee968 100644 --- a/src/util.c +++ b/src/util.c @@ -46,8 +46,8 @@ void rand_init() int fd = open(RANDFILE, O_RDONLY); if (fd == -1 || - !read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) || - !read_write(fd, (unsigned char *)&in, sizeof(in), 1)) + !read_write(fd, (unsigned char *)&seed, sizeof(seed), RW_READ) || + !read_write(fd, (unsigned char *)&in, sizeof(in), RW_READ)) die(_("failed to seed the random number generator: %s"), NULL, EC_MISC); close(fd); @@ -765,6 +765,14 @@ int retry_send(ssize_t rc) return 0; } +/* rw = 0 -> write + rw = 1 -> read + rw = 2 -> read once + rw = 3 -> write once + + "once" fail if all the data doesn't arrive/go in a single read/write. + This indicates a timeout of a TCP socket. +*/ int read_write(int fd, unsigned char *packet, int size, int rw) { ssize_t n, done; @@ -772,17 +780,25 @@ int read_write(int fd, unsigned char *packet, int size, int rw) for (done = 0; done < size; done += n) { do { - if (rw) + if (rw & 1) n = read(fd, &packet[done], (size_t)(size - done)); else n = write(fd, &packet[done], (size_t)(size - done)); if (n == 0) return 0; - - } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS); - if (errno != 0) + if (n == -1 && errno == EINTR) + continue; + + /* "once" variant */ + if ((rw & 2) && n != size) + return 0; + + } while (n == -1 && (errno == EINTR || errno == ENOMEM || errno == ENOBUFS || + errno == EAGAIN || errno == EWOULDBLOCK)); + + if (n == -1) return 0; }