Fix Winsock recvfrom() timeouts

This commit is contained in:
Joseph C. Lehner 2016-02-05 20:20:45 +02:00
parent 0fbee16f65
commit 590894deac
3 changed files with 79 additions and 69 deletions

View file

@ -29,12 +29,11 @@ struct ethsock
{
pcap_t *pcap;
#ifndef NMRPFLASH_WINDOWS
struct timeval timeout;
int fd;
#else
DWORD timeout;
HANDLE handle;
#endif
unsigned timeout;
uint8_t hwaddr[6];
};
@ -314,31 +313,34 @@ cleanup_malloc:
return NULL;
}
int select_fd(int fd, unsigned timeout)
{
struct timeval tv;
int status;
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = timeout / 1000;
tv.tv_usec = 1000 * (timeout % 1000);
status = select(fd + 1, &fds, NULL, NULL, &tv);
if (status < 0) {
sock_perror("select");
}
return status;
}
ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
{
struct pcap_pkthdr* hdr;
const u_char *capbuf;
int status;
#ifndef NMRPFLASH_WINDOWS
fd_set fds;
#else
#ifdef NMRPFLASH_WINDOWS
DWORD ret;
#endif
#ifndef NMRPFLASH_WINDOWS
if (sock->timeout.tv_sec || sock->timeout.tv_usec) {
FD_ZERO(&fds);
FD_SET(sock->fd, &fds);
status = select(sock->fd + 1, &fds, NULL, NULL, &sock->timeout);
if (status == -1) {
perror("select");
return -1;
} else if (status == 0) {
return 0;
}
}
#else
if (sock->timeout) {
ret = WaitForSingleObject(sock->handle, sock->timeout);
if (ret == WAIT_TIMEOUT) {
@ -348,6 +350,15 @@ ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
return -1;
}
}
#else
if (sock->timeout) {
status = select_fd(sock->fd, sock->timeout);
if (status < 0) {
return -1;
} else if (status == 0) {
return 0;
}
}
#endif
status = pcap_next_ex(sock->pcap, &hdr, &capbuf);
@ -392,14 +403,9 @@ int ethsock_close(struct ethsock *sock)
return 0;
}
int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
{
#ifndef NMRPFLASH_WINDOWS
sock->timeout.tv_sec = msec / 1000;
sock->timeout.tv_usec = (msec % 1000) * 1000;
#else
sock->timeout = msec;
#endif
return 0;
}

View file

@ -68,6 +68,14 @@ struct nmrpd_args {
int tftp_put(struct nmrpd_args *args);
int nmrp_do(struct nmrpd_args *args);
int select_fd(int fd, unsigned timeout);
#ifdef NMRPFLASH_WINDOWS
void win_perror2(const char *msg, DWORD err);
void sock_perror(const char *msg);
#else
#define sock_perror(x) perror(x)
#endif
extern int verbosity;

82
tftp.c
View file

@ -111,18 +111,22 @@ static inline void pkt_print(char *pkt, FILE *fp)
}
}
static ssize_t tftp_recvfrom(int sock, char *pkt, struct sockaddr_in *src)
static ssize_t tftp_recvfrom(int sock, char *pkt, struct sockaddr_in *src,
unsigned timeout)
{
ssize_t len;
len = select_fd(sock, timeout);
if (len < 0) {
return -1;
} else if (!len) {
return 0;
}
len = recvfrom(sock, pkt, TFTP_PKT_SIZE, 0, NULL, NULL);
if (len < 0) {
if (errno != EAGAIN) {
perror("recvfrom");
return -1;
}
return -2;
sock_perror("recvfrom");
return -1;
}
uint16_t opcode = pkt_num(pkt);
@ -176,54 +180,46 @@ static ssize_t tftp_sendto(int sock, char *pkt, size_t len,
sent = sendto(sock, pkt, len, 0, (struct sockaddr*)dst, sizeof(*dst));
if (sent < 0) {
perror("sendto");
sock_perror("sendto");
}
return sent;
}
static int sock_set_rx_timeout(int fd, unsigned msec)
#ifdef NMRPFLASH_WINDOWS
void sock_perror(const char *msg)
{
struct timeval tv;
if (msec) {
tv.tv_usec = (msec % 1000) * 1000;
tv.tv_sec = msec / 1000;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)) < 0) {
perror("setsockopt(SO_RCVTIMEO)");
return 1;
}
}
return 0;
win_perror2(msg, WSAGetLastError());
}
#else
inline void sock_perror(const char *msg)
{
perror(msg);
}
#endif
int tftp_put(struct nmrpd_args *args)
{
struct sockaddr_in addr;
uint16_t block;
ssize_t len;
int fd, sock, err, timeout, last_len;
int fd, sock, ret, timeout, last_len;
char rx[TFTP_PKT_SIZE], tx[TFTP_PKT_SIZE];
sock = -1;
ret = -1;
fd = open(args->filename, O_RDONLY);
if (fd < 0) {
perror("open");
err = fd;
ret = fd;
goto cleanup;
}
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
perror("socket");
err = sock;
goto cleanup;
}
err = sock_set_rx_timeout(sock, args->rx_timeout);
if (err) {
sock_perror("socket");
ret = sock;
goto cleanup;
}
@ -252,7 +248,7 @@ int tftp_put(struct nmrpd_args *args)
len = read(fd, tx + 4, 512);
if (len < 0) {
perror("read");
err = len;
ret = len;
goto cleanup;
} else if (!len) {
if (last_len != 512) {
@ -263,8 +259,8 @@ int tftp_put(struct nmrpd_args *args)
last_len = len;
}
err = tftp_sendto(sock, tx, len, &addr);
if (err < 0) {
ret = tftp_sendto(sock, tx, len, &addr);
if (ret < 0) {
goto cleanup;
}
} else if (pkt_num(rx) != ACK) {
@ -273,22 +269,22 @@ int tftp_put(struct nmrpd_args *args)
fprintf(stderr, "!\n");
}
err = tftp_recvfrom(sock, rx, &addr);
if (err < 0) {
if (err == -2) {
if (++timeout < 5) {
continue;
}
fprintf(stderr, "Timeout while waiting for ACK(%d).\n", block);
ret = tftp_recvfrom(sock, rx, &addr, args->rx_timeout);
if (ret < 0) {
goto cleanup;
} else if (!ret) {
if (++timeout < 5) {
continue;
}
fprintf(stderr, "Timeout while waiting for ACK(%d).\n", block);
goto cleanup;
} else {
timeout = 0;
err = 0;
ret = 0;
}
} while(1);
err = 0;
ret = 0;
cleanup:
if (fd >= 0) {
@ -305,5 +301,5 @@ cleanup:
#endif
}
return err;
return ret;
}