diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-10-31 10:10:52 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-10-31 10:10:52 +0200 |
commit | 72b0e2ae885e60d7530e483e98d61a64061fd13d (patch) | |
tree | 9900c925176a90bb1e68eb03e140770e0fafded8 | |
parent | b746d5746013cc2795df3e535085d23c25519c75 (diff) | |
download | nsu-72b0e2ae885e60d7530e483e98d61a64061fd13d.tar.gz nsu-72b0e2ae885e60d7530e483e98d61a64061fd13d.tar.bz2 |
Introduce functions for sending requests
* Makefile: Add io.c
* io.c: New file.
* nsu.h (nsu_sockaddr): New data type.
(nsu_nsend): New proto.
* main.c (send_update_query): Use nsu_nsend instead of res_nsend.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | io.c | 218 | ||||
-rw-r--r-- | main.c | 85 | ||||
-rw-r--r-- | nsu.h | 21 |
5 files changed, 304 insertions, 28 deletions
@@ -5,6 +5,7 @@ nsu tmp/ .gdbinit +TAGS TODO core lex.yy.c @@ -3,7 +3,7 @@ CFLAGS=-ggdb APPSRC=main.c y.tab.c lex.yy.c APPOBJ=${APPSRC:.c=.o} -NSUSRC=nsu.c typestr.c alg.c log.c +NSUSRC=nsu.c typestr.c alg.c log.c io.c NSUOBJ=${NSUSRC:.c=.o} OBJS=$(NSUOBJ) $(APPOBJ) @@ -27,3 +27,8 @@ clean: allclean: clean rm -f lex.yy.c y.tab.c y.tab.h +ETAGS = etags +ETAGSFLAGS= + +tags: + $(ETAGS) $(ETAGSFLAGS) $(NSUSRC) main.c grammar.y lexer.l @@ -0,0 +1,218 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <poll.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include "nsu.h" + +static int +nsu_nsend_dg(res_state statp, nsu_sockaddr *a, u_char *qbuf, size_t qlen, + u_char *rbuf, size_t rlen, size_t *return_len) +{ + struct pollfd pfd; + UPDATE_HEADER *uhp; + int retry = 0; + int result = NSU_SEND_SYSERR; + int ec; + + nsu_log(NSU_LOG_DEBUG, "sending query %d via UDP", + ((HEADER*)qbuf)->id); + pfd.fd = socket(a->addr.sa.sa_family, SOCK_DGRAM, 0); + if (pfd.fd == -1) { + return NSU_SEND_SYSERR; + } + pfd.events = POLLOUT; + while (1) { + ssize_t n; + int rc; + + rc = poll(&pfd, 1, statp->retrans * 1000); + if (rc < 0) { + result = NSU_SEND_SYSERR; + break; + } + if (rc == 0) { + if (retry == statp->retry) { + result = NSU_SEND_TIMEOUT; + break; + } + retry++; + continue; + } + if (pfd.revents & POLLOUT) { + n = sendto(pfd.fd, qbuf, qlen, MSG_NOSIGNAL, + &a->addr.sa, a->len); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + result = NSU_SEND_SYSERR; + break; + } + pfd.events = POLLIN; + } else if (pfd.revents & POLLIN) { + nsu_sockaddr fromaddr; + HEADER *hp; + + fromaddr.len = sizeof(fromaddr.addr); + n = recvfrom(pfd.fd, rbuf, rlen, 0, + &fromaddr.addr.sa, &fromaddr.len); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + result = NSU_SEND_SYSERR; + break; + } + pfd.events = 0; + if (!(statp->options & RES_INSECURE1) && + !(fromaddr.len == a->len && + memcmp(&fromaddr.addr, &a->addr, a->len) == 0)) { + nsu_log(NSU_LOG_DEBUG, "%s", + "response from the wrong server"); + //FIXME + result = NSU_SEND_BADSERV; + break; + } + + hp = (HEADER*)rbuf; + if (hp->tc) { + result = NSU_SEND_RETRYTCP; + break; + } + *return_len = n; + result = NSU_SEND_OK; + break; + } else if (pfd.revents & POLLERR) { + result = NSU_SEND_SYSERR; + break; + } else if (pfd.revents & POLLHUP) { + result = NSU_SEND_PEERCLOSE; + break; + } else if (pfd.revents & POLLNVAL) { + result = NSU_SEND_SYSERR; + errno = EINVAL; + break; + } else + abort(); + } + + if (result == NSU_SEND_SYSERR) + ec = errno; + close(pfd.fd); + if (result == NSU_SEND_SYSERR) + errno = ec; + + return result; +} + +static int +nsu_nsend_vc(res_state statp, nsu_sockaddr *a, u_char *qbuf, size_t qlen, + u_char *rbuf, size_t rlen, size_t *return_len) +{ + struct pollfd pfd; + int retry = 0; + int result = NSU_SEND_SYSERR; + int ec; + + nsu_log(NSU_LOG_DEBUG, "sending query %d via TCP", + ((HEADER*)qbuf)->id); + pfd.fd = socket(a->addr.sa.sa_family, SOCK_STREAM, 0); + if (pfd.fd == -1) { + return NSU_SEND_SYSERR; + } + pfd.events = POLLOUT; + + if (connect(pfd.fd, &a->addr.sa, a->len)) { + ec = errno; + close(pfd.fd); + errno = ec; + return NSU_SEND_SYSERR; + } + + //FIXME: bind? + + while (1) { + ssize_t n; + int rc; + + rc = poll(&pfd, 1, statp->retrans * 1000); + if (rc < 0) { + result = NSU_SEND_SYSERR; + break; + } + if (rc == 0) { + if (retry == statp->retry) { + result = NSU_SEND_TIMEOUT; + break; + } + retry++; + continue; + } + if (pfd.revents & POLLOUT) { + n = write(pfd.fd, qbuf, qlen); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + result = NSU_SEND_SYSERR; + break; + } + pfd.events = POLLIN; + } else if (pfd.revents & POLLIN) { + n = read(pfd.fd, rbuf, rlen); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + result = NSU_SEND_SYSERR; + break; + } + *return_len = n; + result = NSU_SEND_OK; + break; + } else if (pfd.revents & POLLERR) { + result = NSU_SEND_SYSERR; + break; + } else if (pfd.revents & POLLHUP) { + result = NSU_SEND_PEERCLOSE; + break; + } else if (pfd.revents & POLLNVAL) { + result = NSU_SEND_SYSERR; + errno = EINVAL; + break; + } else + abort(); + } + + if (result == NSU_SEND_SYSERR) + ec = errno; + close(pfd.fd); + if (result == NSU_SEND_SYSERR) + errno = ec; + + return result; +} + +int +nsu_nsend(res_state statp, nsu_sockaddr *a, u_char *qbuf, size_t qlen, + u_char *rbuf, size_t rlen, size_t *return_len) +{ + int result; + + if (!qbuf || !rbuf || qlen < sizeof(UPDATE_HEADER) || rlen < sizeof(UPDATE_HEADER)) { + errno = EINVAL; + return NSU_SEND_SYSERR; + } + + result = nsu_nsend_dg(statp, a, qbuf, qlen, rbuf, rlen, return_len); + if (result == NSU_SEND_RETRYTCP && (statp->options & RES_USEVC)) + result = nsu_nsend_vc(statp, a, qbuf, qlen, rbuf, rlen, return_len); + + return result; +} + + @@ -411,38 +411,70 @@ enum { update_retry }; +static char const * +nsu_sockaddr_str(nsu_sockaddr *nsa, char *name, size_t namelen) +{ + getnameinfo(&nsa->addr.sa, nsa->len, + name, namelen, + NULL, 0, + NI_NUMERICHOST); + return name; +} + int -send_update_query(res_state state, char const *zone, u_char *qbuf, int qlen) +send_update_query(res_state state, nsu_sockaddr *nsa, + char const *zone, u_char *qbuf, int qlen) { union { UPDATE_HEADER hdr; - u_char buf[NS_PACKETSZ*2]; + u_char buf[NS_PACKETSZ]; } response; - int rlen; + size_t rlen; ns_msg handle; int rcode; + char name[NS_MAXDNAME]; + char const *nsname = NULL; + int rc; if (nsu_log_enabled(NSU_LOG_DEBUG) || state->options & RES_DEBUG) { - char name[NS_MAXDNAME]; - - getnameinfo((struct sockaddr*)state->nsaddr_list, - sizeof(state->nsaddr_list[0]), - name, sizeof(name), - NULL, 0, - NI_NUMERICHOST); + nsname = nsu_sockaddr_str(nsa, name, sizeof(name)); nsu_log_msg(NSU_LOG_DEBUG, "trying nameserver %s", name); } - rlen = res_nsend(state, qbuf, qlen, - (u_char *) &response, - sizeof(response)); - - if (rlen < 0) { - if (errno == ECONNREFUSED) { /* no server on the host */ - panic("connection refused"); - } else { - /* anything else: no response */ - panic("no response"); + rc = nsu_nsend(state, nsa, qbuf, qlen, + (u_char *) &response, + sizeof(response), + &rlen); + + if (rc == NSU_SEND_OK) { + + } else { + if (!nsname) + nsname = nsu_sockaddr_str(nsa, name, sizeof(name)); + switch (rc) { + case NSU_SEND_SYSERR: + panic("%s: can't send: %s", nsname, strerror(errno)); + break; + + case NSU_SEND_TIMEOUT: + panic("%s: server timeout", nsname); + break; + + case NSU_SEND_BADSERV: + panic("%s: response from another server", nsname); + break; + + case NSU_SEND_RETRYTCP: + panic("%s: response too long; retry using TCP", + nsname); + break; + + case NSU_SEND_PEERCLOSE: + panic("%s: server closed connection", nsname); + break; + + default: + abort(); } return update_fail; } @@ -471,9 +503,8 @@ send_update_query(res_state state, char const *zone, u_char *qbuf, int qlen) if (nsu_log_enabled(NSU_LOG_DEBUG) || state->options & RES_DEBUG) { char name[NS_MAXDNAME]; - - getnameinfo((struct sockaddr*)state->nsaddr_list, - sizeof(state->nsaddr_list[0]), + + getnameinfo(&nsa->addr.sa, nsa->len, name, sizeof(name), NULL, 0, NI_NUMERICHOST); @@ -504,6 +535,7 @@ run_update(nsu_rr *prereq, int num_prereq, int rc; int i; struct __res_state rstate; + nsu_sockaddr addr; if (!domain_name) { char *p = update->name; @@ -546,11 +578,10 @@ run_update(nsu_rr *prereq, int num_prereq, res_ninit(&rstate); - rstate.nscount = 1; + addr.len = sizeof(struct sockaddr_in); for (i = 0; i < nscount; i++) { - memcpy(&rstate.nsaddr_list[0], nstab + i, - sizeof(rstate.nsaddr_list[0])); - rc = send_update_query(&rstate, zone, query.buf, qlen); + memcpy(&addr.addr, &nstab[i], sizeof(nstab[0])); + rc = send_update_query(&rstate, &addr, zone, query.buf, qlen); if (rc != update_retry) break; } @@ -177,6 +177,27 @@ int nsu_keyfile_private_read(char const *filename, uint8_t **pkey, size_t *pkey_len); int nsu_keyfile_read(char const *name, struct nsu_tsig **ptsig); + +typedef struct { + socklen_t len; + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + } addr; +} nsu_sockaddr; + +enum { + NSU_SEND_OK, + NSU_SEND_SYSERR, + NSU_SEND_TIMEOUT, + NSU_SEND_BADSERV, + NSU_SEND_RETRYTCP, + NSU_SEND_PEERCLOSE +}; + +int nsu_nsend(res_state statp, nsu_sockaddr *a, u_char *qbuf, size_t qlen, + u_char *rbuf, size_t rlen, size_t *return_len); |