/****************************************************************************** * This is the mail loop when running over sockets, receiving packets and * sending responses. ******************************************************************************/ void sockets_thread(struct Core *conf) { int err; SOCKET fd; struct sockaddr_in6 sin; static const unsigned port = 53; /* * This software obtains its speed by bypassing the operating system * stack. Thus, running on top of 'sockets' is going to be a lot * slower */ fprintf(stderr, "WARNING: running in slow 'sockets' mode\n"); /* * Legacy Windows is legacy. */ #if defined(WIN32) {WSADATA x; WSAStartup(0x201, &x);} #endif /* * Create a socket for incoming UDP packets. By specifying IPv6, we are * actually going to allow both IPv4 and IPv6. */ fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd <= 0) { LOG(0, "FAIL: couldn't create socket %u\n", WSAGetLastError()); return; } /* * Set the 'reuse' feature of the socket, otherwise restarting the process * requires a wait before binding back to the same port number */ { int on = 1; err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,sizeof(on)); if (err < 0) { perror("setsockopt(SO_REUSEADDR) failed"); exit(1); } } /* * Enable both IPv4 and IPv6 to be used on the same sockets. This appears to * be needed for Windows, but not needed for Mac OS X. */ { int on = 0; err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)); if (err < 0) { perror("setsockopt(IPV6_V6ONLY) failed"); exit(1); } } /* * Listen on any IPv4 or IPv6 address in the system */ memset(&sin, 0, sizeof(sin)); sin.sin6_family = AF_INET6; sin.sin6_addr = in6addr_any; sin.sin6_port = htons(port); err = bind(fd, (struct sockaddr*)&sin, sizeof(sin)); if (err) { switch (WSAGetLastError()) { case WSA(EACCES): LOG(0, "FAIL: couldn't bind to port %u: %s\n", port, "access denied"); if (port <= 1024) LOG(0, " hint... need to be root for ports below 1024\n"); break; case WSA(EADDRINUSE): LOG(0, "FAIL: couldn't bind to port %u: %s\n", port, "address in use"); LOG(0, " hint... some other server is running on that port\n"); break; default: LOG(0, "FAIL: couldn't bind to port %u: %u\n", port, WSAGetLastError()); } exit(1); } else { fprintf(stderr, "UDP port: %u\n", port); } /* * Sit in loop processing incoming UDP packets */ for (;;) { unsigned char buf[2048]; int bytes_received; socklen_t sizeof_sin = sizeof(sin); struct DNS_Incoming request[1]; struct DNS_OutgoingResponse response[1]; struct Packet pkt; unsigned char buf2[2048]; /* * 1. receive 'packet' */ bytes_received = recvfrom(fd, (char*)buf, sizeof(buf), 0, (struct sockaddr*)&sin, &sizeof_sin); if (bytes_received == 0) continue; /* * 2. parse 'packet' into a 'request' */ proto_dns_parse(request, buf, 0, bytes_received); if (!request->is_valid) continue; /* * 3. resolve 'request' into a 'repsonse' */ resolver_init(response, request->query_name.name, request->query_name.length, request->query_type, request->id, request->opcode); resolver_algorithm(conf->db, response, request); /* * 4. format the 'response' into a 'packet' */ pkt.buf = buf2; pkt.max = sizeof(buf2); pkt.offset = 0; dns_format_response(response, &pkt); /* * 5. Transmit the 'packet' */ if (pkt.offset < pkt.max) { sendto(fd, (char*)pkt.buf, pkt.offset, 0, (struct sockaddr*)&sin, sizeof_sin); } } }
/* * Read a character from a connection w/ timeout */ ssize_t _fetch_read(conn_t *conn, char *buf, size_t len) { struct timeval now, timeout, wait; fd_set readfds; ssize_t rlen, total; int r; if (fetchTimeout) { FD_ZERO(&readfds); gettimeofday(&timeout, NULL); timeout.tv_sec += fetchTimeout; } total = 0; while (len > 0) { while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { FD_SET(conn->sd, &readfds); gettimeofday(&now, NULL); wait.tv_sec = timeout.tv_sec - now.tv_sec; wait.tv_usec = timeout.tv_usec - now.tv_usec; if (wait.tv_usec < 0) { wait.tv_usec += 1000000; wait.tv_sec--; } if (wait.tv_sec < 0) { errno = WSA(ETIMEDOUT); _fetch_syserr(); return (-1); } errno = 0; r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); if (r == -1) { if (errno == EINTR && fetchRestartCalls) continue; _fetch_syserr(); return (-1); } } #ifdef WITH_SSL if (conn->ssl != NULL) rlen = SSL_read(conn->ssl, buf, len); else #endif #ifdef flagWIN32 rlen = recv(conn->sd, buf, len, 0); #else rlen = read(conn->sd, buf, len); #endif if (rlen == 0) break; if (rlen < 0) { if (errno == EINTR && fetchRestartCalls) continue; return (-1); } len -= rlen; buf += rlen; total += rlen; } return (total); }
/* * Write a vector to a connection w/ timeout * Note: can modify the iovec. */ ssize_t _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) { struct timeval now, timeout, wait; fd_set writefds; ssize_t wlen, total; int r; if (fetchTimeout) { FD_ZERO(&writefds); gettimeofday(&timeout, NULL); timeout.tv_sec += fetchTimeout; } total = 0; while (iovcnt > 0) { while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { FD_SET(conn->sd, &writefds); gettimeofday(&now, NULL); wait.tv_sec = timeout.tv_sec - now.tv_sec; wait.tv_usec = timeout.tv_usec - now.tv_usec; if (wait.tv_usec < 0) { wait.tv_usec += 1000000; wait.tv_sec--; } if (wait.tv_sec < 0) { errno = WSA(ETIMEDOUT); _fetch_syserr(); return (-1); } errno = 0; r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); if (r == -1) { if (errno == WSA(EINTR) && fetchRestartCalls) continue; return (-1); } } errno = 0; #ifdef WITH_SSL if (conn->ssl != NULL) wlen = SSL_write(conn->ssl, iov->iov_base, iov->iov_len); else #endif #ifdef flagWIN32 wlen = send(conn->sd, iov->iov_base, iov->iov_len, 0); #else wlen = writev(conn->sd, iov, iovcnt); #endif if (wlen == 0) { /* we consider a short write a failure */ errno = EPIPE; _fetch_syserr(); return (-1); } if (wlen < 0) { if (errno == EINTR && fetchRestartCalls) continue; return (-1); } total += wlen; while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { wlen -= iov->iov_len; iov++; iovcnt--; } if (iovcnt > 0) { iov->iov_len -= wlen; iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; } }
/* * Set error code according to errno */ void _fetch_syserr(void) { switch (errno) { case 0: fetchLastErrCode = FETCH_OK; break; case EPERM: case WSA(EACCES): case EROFS: #ifndef flagWIN32 case EAUTH: case ENEEDAUTH: #endif fetchLastErrCode = FETCH_AUTH; break; case ENOENT: case EISDIR: /* XXX */ fetchLastErrCode = FETCH_UNAVAIL; break; case ENOMEM: fetchLastErrCode = FETCH_MEMORY; break; case EBUSY: case EAGAIN: fetchLastErrCode = FETCH_TEMP; break; case EEXIST: fetchLastErrCode = FETCH_EXISTS; break; case ENOSPC: fetchLastErrCode = FETCH_FULL; break; case WSA(EADDRINUSE): case WSA(EADDRNOTAVAIL): case WSA(ENETDOWN): case WSA(ENETUNREACH): case WSA(ENETRESET): case WSA(EHOSTUNREACH): fetchLastErrCode = FETCH_NETWORK; break; case WSA(ECONNABORTED): case WSA(ECONNRESET): fetchLastErrCode = FETCH_ABORT; break; case WSA(ETIMEDOUT): fetchLastErrCode = FETCH_TIMEOUT; break; case WSA(ECONNREFUSED): case WSA(EHOSTDOWN): fetchLastErrCode = FETCH_DOWN; break; default: fetchLastErrCode = FETCH_UNKNOWN; } snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); }