static void thread_worker(void *p) { struct CoreWorkerThread *t = (struct CoreWorkerThread *)p; struct Core *core = t->core; while (!t->should_end) { unsigned i; struct CoreSocketSet *sockets; fd_set readfds; int nfds = 0; int x; struct timeval ts; /* [SYNCHRONIZATION POINT] * mark the fact we are using the new socket-set */ sockets = (struct CoreSocketSet *)core->socket_run; t->loop_count++; /* During startup, the sockets argument may be NULL for a time. * if that's the case, then just wait for a little bit, and try * again */ if (sockets == NULL) { /* Sleep for a 10th of a second */ pixie_mssleep(10); continue; } /* * See if there are any packets waiting */ FD_ZERO(&readfds); for (i=0; i<sockets->count; i++) { FD_SET(sockets->list[i].fd, &readfds); if (nfds < sockets->list[i].fd) nfds = sockets->list[i].fd; } ts.tv_sec = 0; ts.tv_usec = 1000; /* one millisecond */ x = select(nfds, &readfds, 0, 0, &ts); if (x < 0) { LOG_ERR(C_NETWORK, "select() returned error %u\n", WSAGetLastError()); pixie_mssleep(1000); /* at this point, the errors are probably unrecoverable * until the system is manually reconfigured */ continue; } if (x == 0) continue; /* nothing found */ /* * Process any packets that have arrived */ for (i=0; i<sockets->count; i++) { struct sockaddr_storage sin; socklen_t sizeof_sin = sizeof(sin); unsigned char buf[2048]; unsigned char buf2[2048]; int bytes_received; struct DNS_Incoming request[1]; struct DNS_OutgoingResponse response[1]; struct Packet pkt; int fd; fd = sockets->list[i].fd; if (!FD_ISSET(fd, &readfds)) continue; /* * 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(core->db_run, 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); } } } }
/****************************************************************************** * 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); } } }
static int grab_dns_response(struct Catalog *catalog, const unsigned char *px, unsigned offset, unsigned max) { struct DNS_Incoming dns[1]; unsigned i; struct DomainPointer domain; unsigned char domain_buffer[256]; char unsigned tmp_buffer[65536]; unsigned tmp_offset; unsigned src_offset; /* kludge */ px += offset; offset = 0; max -= offset; domain.name = domain_buffer; proto_dns_parse(dns, px, offset, max); if (!dns->is_valid) return Failure; if (!dns->qr) return Failure; for (i=dns->qdcount; i<dns->rr_count; i++) { int type; int xclass; unsigned ttl; unsigned rdlength; const unsigned char *rdata; offset = dns->rr_offset[i]; /* extract the domain name */ dns_extract_name(px, offset, max, &domain); offset = dns_name_skip(px, offset, max); /* extract the data */ if (offset + 10 > max) continue; type = px[offset+0]<<8 | px[offset+1]; xclass = px[offset+2]<<8 | px[offset+3]; if (xclass != 1) continue; ttl = px[offset+4]<<24 | px[offset+5]<<16 | px[offset+6]<<8 | px[offset+7]; rdlength = px[offset+8]<<8 | px[offset+9]; rdata = &px[offset + 10]; offset += 10; /* insert the data into the catalog */ if (offset + rdlength > max) continue; else offset += rdlength; tmp_offset = 0; src_offset = (unsigned)(rdata - px); switch (type) { case 41: /*OPT*/ continue; case TYPE_NS: case TYPE_CNAME: extract_name( tmp_buffer, &tmp_offset, sizeof(tmp_buffer), px, &src_offset, max); rdata = tmp_buffer; rdlength = tmp_offset; break; case TYPE_SOA: extract_name( tmp_buffer, &tmp_offset, sizeof(tmp_buffer), px, &src_offset, max); extract_name( tmp_buffer, &tmp_offset, sizeof(tmp_buffer), px, &src_offset, max); extract_copy( tmp_buffer, &tmp_offset, sizeof(tmp_buffer), px, &src_offset, max, offset-src_offset); rdata = tmp_buffer; rdlength = tmp_offset; break; case TYPE_RRSIG: //printf("."); break; } zonefile_load( domain, root, type, ttl, rdlength, rdata, 10000, catalog); } return Success; }