summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/ipc.c
diff options
context:
space:
mode:
authorJakob Schlyter <jakob@cvs.openbsd.org>2010-01-15 19:25:09 +0000
committerJakob Schlyter <jakob@cvs.openbsd.org>2010-01-15 19:25:09 +0000
commitd2081ac134d2d350ed92374b12613330132619c9 (patch)
tree511f9a412b24160516c7d3d29111801f9fa1bf35 /usr.sbin/nsd/ipc.c
parentefe15de128add506fb4055690c47221eb73d6346 (diff)
NSD v3.2.4
Diffstat (limited to 'usr.sbin/nsd/ipc.c')
-rw-r--r--usr.sbin/nsd/ipc.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c
new file mode 100644
index 00000000000..449c0514fc1
--- /dev/null
+++ b/usr.sbin/nsd/ipc.c
@@ -0,0 +1,839 @@
+/*
+ * ipc.c - Interprocess communication routines. Handlers read and write.
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "ipc.h"
+#include "buffer.h"
+#include "xfrd-tcp.h"
+#include "nsd.h"
+#include "namedb.h"
+#include "xfrd.h"
+#include "xfrd-notify.h"
+
+/* set is_ok for the zone according to the zone message */
+static zone_type* handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet);
+/* write ipc ZONE_STATE message into the buffer */
+static void write_zone_state_packet(buffer_type* packet, zone_type* zone);
+/* attempt to send NSD_STATS command to child fd */
+static void send_stat_to_child(struct main_ipc_handler_data* data, int fd);
+/* write IPC expire notification msg to a buffer */
+static void xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone);
+/* send reload request over the IPC channel */
+static void xfrd_send_reload_req(xfrd_state_t* xfrd);
+/* send quit request over the IPC channel */
+static void xfrd_send_quit_req(xfrd_state_t* xfrd);
+/* get SOA INFO out of IPC packet buffer */
+static void xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet);
+/* perform read part of handle ipc for xfrd */
+static void xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd);
+
+static zone_type*
+handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet)
+{
+ uint8_t ok;
+ const dname_type *dname;
+ domain_type *domain;
+ zone_type *zone;
+
+ ok = buffer_read_u8(packet);
+ dname = (dname_type*)buffer_current(packet);
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "handler zone state %s is %s",
+ dname_to_string(dname, NULL), ok?"ok":"expired"));
+ /* find in zone_types, if does not exist, we cannot serve anyway */
+ /* find zone in config, since that one always exists */
+ domain = domain_table_find(nsd->db->domains, dname);
+ if(!domain) {
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (domain %s)",
+ dname_to_string(dname, NULL)));
+ return NULL;
+ }
+ zone = domain_find_zone(domain);
+ if(!zone || dname_compare(domain_dname(zone->apex), dname) != 0) {
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (zone %s)",
+ dname_to_string(dname, NULL)));
+ return NULL;
+ }
+ assert(zone);
+ /* only update zone->is_ok if needed to minimize copy-on-write
+ of memory pages shared after fork() */
+ if(ok && !zone->is_ok)
+ zone->is_ok = 1;
+ if(!ok && zone->is_ok)
+ zone->is_ok = 0;
+ return zone;
+}
+
+void
+child_handle_parent_command(netio_type *ATTR_UNUSED(netio),
+ netio_handler_type *handler,
+ netio_event_types_type event_types)
+{
+ sig_atomic_t mode;
+ int len;
+ struct ipc_handler_conn_data *data =
+ (struct ipc_handler_conn_data *) handler->user_data;
+ if (!(event_types & NETIO_EVENT_READ)) {
+ return;
+ }
+
+ if(data->conn->is_reading) {
+ int ret = conn_read(data->conn);
+ if(ret == -1) {
+ log_msg(LOG_ERR, "handle_parent_command: error in conn_read: %s",
+ strerror(errno));
+ data->conn->is_reading = 0;
+ return;
+ }
+ if(ret == 0) {
+ return; /* continue later */
+ }
+ /* completed */
+ data->conn->is_reading = 0;
+ buffer_flip(data->conn->packet);
+ (void)handle_xfrd_zone_state(data->nsd, data->conn->packet);
+ return;
+ }
+
+ if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
+ log_msg(LOG_ERR, "handle_parent_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if (len == 0)
+ {
+ /* parent closed the connection. Quit */
+ data->nsd->mode = NSD_QUIT;
+ return;
+ }
+
+ switch (mode) {
+ case NSD_STATS:
+ case NSD_QUIT:
+ data->nsd->mode = mode;
+ break;
+ case NSD_ZONE_STATE:
+ data->conn->is_reading = 1;
+ data->conn->total_bytes = 0;
+ data->conn->msglen = 0;
+ data->conn->fd = handler->fd;
+ buffer_clear(data->conn->packet);
+ break;
+ default:
+ log_msg(LOG_ERR, "handle_parent_command: bad mode %d",
+ (int) mode);
+ break;
+ }
+}
+
+void
+parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio),
+ netio_handler_type *handler,
+ netio_event_types_type event_types)
+{
+ sig_atomic_t mode;
+ int len;
+ struct ipc_handler_conn_data *data =
+ (struct ipc_handler_conn_data *) handler->user_data;
+ if (!(event_types & NETIO_EVENT_READ)) {
+ return;
+ }
+
+ if(data->conn->is_reading) {
+ /* handle ZONE_STATE forward to children */
+ int ret = conn_read(data->conn);
+ size_t i;
+ zone_type* zone;
+ if(ret == -1) {
+ log_msg(LOG_ERR, "main xfrd listener: error in conn_read: %s",
+ strerror(errno));
+ data->conn->is_reading = 0;
+ return;
+ }
+ if(ret == 0) {
+ return; /* continue later */
+ }
+ /* completed */
+ data->conn->is_reading = 0;
+ buffer_flip(data->conn->packet);
+ zone = handle_xfrd_zone_state(data->nsd, data->conn->packet);
+ if(!zone)
+ return;
+ /* forward to all children */
+ for (i = 0; i < data->nsd->child_count; ++i) {
+ if(!zone->dirty[i]) {
+ zone->dirty[i] = 1;
+ stack_push(data->nsd->children[i].dirty_zones, zone);
+ data->nsd->children[i].handler->event_types |=
+ NETIO_EVENT_WRITE;
+ }
+ }
+ return;
+ }
+
+ if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
+ log_msg(LOG_ERR, "handle_xfrd_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if (len == 0)
+ {
+ DEBUG(DEBUG_IPC,1, (LOG_ERR, "handle_xfrd_command: xfrd closed channel."));
+ close(handler->fd);
+ handler->fd = -1;
+ return;
+ }
+
+ switch (mode) {
+ case NSD_RELOAD:
+ data->nsd->signal_hint_reload = 1;
+ break;
+ case NSD_QUIT:
+ data->nsd->mode = mode;
+ break;
+ case NSD_REAP_CHILDREN:
+ data->nsd->signal_hint_child = 1;
+ break;
+ case NSD_ZONE_STATE:
+ data->conn->is_reading = 1;
+ data->conn->total_bytes = 0;
+ data->conn->msglen = 0;
+ data->conn->fd = handler->fd;
+ buffer_clear(data->conn->packet);
+ break;
+ default:
+ log_msg(LOG_ERR, "handle_xfrd_command: bad mode %d",
+ (int) mode);
+ break;
+ }
+}
+
+static void
+write_zone_state_packet(buffer_type* packet, zone_type* zone)
+{
+ sig_atomic_t cmd = NSD_ZONE_STATE;
+ uint8_t ok = zone->is_ok;
+ uint16_t sz;
+ if(!zone->apex) {
+ return;
+ }
+ sz = dname_total_size(domain_dname(zone->apex)) + 1;
+ sz = htons(sz);
+
+ buffer_clear(packet);
+ buffer_write(packet, &cmd, sizeof(cmd));
+ buffer_write(packet, &sz, sizeof(sz));
+ buffer_write(packet, &ok, sizeof(ok));
+ buffer_write(packet, domain_dname(zone->apex),
+ dname_total_size(domain_dname(zone->apex)));
+ buffer_flip(packet);
+}
+
+static void
+send_stat_to_child(struct main_ipc_handler_data* data, int fd)
+{
+ sig_atomic_t cmd = NSD_STATS;
+ if(write(fd, &cmd, sizeof(cmd)) == -1) {
+ if(errno == EAGAIN || errno == EINTR)
+ return; /* try again later */
+ log_msg(LOG_ERR, "svrmain: problems sending stats to child %d command: %s",
+ (int)data->child->pid, strerror(errno));
+ return;
+ }
+ data->child->need_to_send_STATS = 0;
+}
+
+int packet_read_query_section(buffer_type *packet, uint8_t* dest, uint16_t* qtype, uint16_t* qclass);
+static void
+debug_print_fwd_name(int ATTR_UNUSED(len), buffer_type* packet, int acl_num)
+{
+ uint8_t qnamebuf[MAXDOMAINLEN];
+ uint16_t qtype, qclass;
+ const dname_type* dname;
+ region_type* tempregion = region_create(xalloc, free);
+
+ size_t bufpos = buffer_position(packet);
+ buffer_rewind(packet);
+ buffer_skip(packet, 12);
+ if(packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) {
+ dname = dname_make(tempregion, qnamebuf, 1);
+ log_msg(LOG_INFO, "main: fwd packet for %s, acl %d",
+ dname_to_string(dname,0), acl_num);
+ } else {
+ log_msg(LOG_INFO, "main: fwd packet badqname, acl %d", acl_num);
+ }
+ buffer_set_position(packet, bufpos);
+ region_destroy(tempregion);
+}
+
+static void
+send_quit_to_child(struct main_ipc_handler_data* data, int fd)
+{
+ sig_atomic_t cmd = NSD_QUIT;
+ if(write(fd, &cmd, sizeof(cmd)) == -1) {
+ if(errno == EAGAIN || errno == EINTR)
+ return; /* try again later */
+ log_msg(LOG_ERR, "svrmain: problems sending quit to child %d command: %s",
+ (int)data->child->pid, strerror(errno));
+ return;
+ }
+ data->child->need_to_send_QUIT = 0;
+ DEBUG(DEBUG_IPC,2, (LOG_INFO, "main: sent quit to child %d",
+ (int)data->child->pid));
+}
+
+void
+parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
+ netio_handler_type *handler,
+ netio_event_types_type event_types)
+{
+ sig_atomic_t mode;
+ int len;
+ struct main_ipc_handler_data *data =
+ (struct main_ipc_handler_data*)handler->user_data;
+
+ /* do a nonblocking write to the child if it is ready. */
+ if (event_types & NETIO_EVENT_WRITE) {
+ if(!data->busy_writing_zone_state &&
+ !data->child->need_to_send_STATS &&
+ !data->child->need_to_send_QUIT &&
+ !data->child->need_to_exit &&
+ data->child->dirty_zones->num > 0) {
+ /* create packet from next dirty zone */
+ zone_type* zone = (zone_type*)stack_pop(data->child->dirty_zones);
+ assert(zone);
+ zone->dirty[data->child_num] = 0;
+ data->busy_writing_zone_state = 1;
+ write_zone_state_packet(data->write_conn->packet, zone);
+ data->write_conn->msglen = buffer_limit(data->write_conn->packet);
+ data->write_conn->total_bytes = sizeof(uint16_t); /* len bytes already in packet */
+ data->write_conn->fd = handler->fd;
+ }
+ if(data->busy_writing_zone_state) {
+ /* write more of packet */
+ int ret = conn_write(data->write_conn);
+ if(ret == -1) {
+ log_msg(LOG_ERR, "handle_child_cmd %d: could not write: %s",
+ (int)data->child->pid, strerror(errno));
+ data->busy_writing_zone_state = 0;
+ } else if(ret == 1) {
+ data->busy_writing_zone_state = 0; /* completed */
+ }
+ } else if(data->child->need_to_send_STATS &&
+ !data->child->need_to_exit) {
+ send_stat_to_child(data, handler->fd);
+ } else if(data->child->need_to_send_QUIT) {
+ send_quit_to_child(data, handler->fd);
+ if(!data->child->need_to_send_QUIT)
+ handler->event_types = NETIO_EVENT_READ;
+ }
+ if(!data->busy_writing_zone_state &&
+ !data->child->need_to_send_STATS &&
+ !data->child->need_to_send_QUIT &&
+ data->child->dirty_zones->num == 0) {
+ handler->event_types = NETIO_EVENT_READ;
+ }
+ }
+
+ if (!(event_types & NETIO_EVENT_READ)) {
+ return;
+ }
+
+ if (data->forward_mode) {
+ int got_acl;
+ /* forward the data to xfrd */
+ DEBUG(DEBUG_IPC,2, (LOG_INFO,
+ "main passed packet readup %d", (int)data->got_bytes));
+ if(data->got_bytes < sizeof(data->total_bytes))
+ {
+ if ((len = read(handler->fd,
+ (char*)&data->total_bytes+data->got_bytes,
+ sizeof(data->total_bytes)-data->got_bytes)) == -1) {
+ log_msg(LOG_ERR, "handle_child_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if(len == 0) {
+ /* EOF */
+ data->forward_mode = 0;
+ return;
+ }
+ data->got_bytes += len;
+ if(data->got_bytes < sizeof(data->total_bytes))
+ return;
+ data->total_bytes = ntohs(data->total_bytes);
+ buffer_clear(data->packet);
+ if(data->total_bytes > buffer_capacity(data->packet)) {
+ log_msg(LOG_ERR, "internal error: ipc too large");
+ exit(1);
+ }
+ return;
+ }
+ /* read the packet */
+ if(data->got_bytes-sizeof(data->total_bytes) < data->total_bytes) {
+ if((len = read(handler->fd, buffer_current(data->packet),
+ data->total_bytes - (data->got_bytes-sizeof(data->total_bytes))
+ )) == -1 ) {
+ log_msg(LOG_ERR, "handle_child_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if(len == 0) {
+ /* EOF */
+ data->forward_mode = 0;
+ return;
+ }
+ data->got_bytes += len;
+ buffer_skip(data->packet, len);
+ /* read rest later */
+ return;
+ }
+ /* read the acl number */
+ got_acl = data->got_bytes - sizeof(data->total_bytes) - data->total_bytes;
+ if((len = read(handler->fd, (char*)&data->acl_num+got_acl,
+ sizeof(data->acl_num)-got_acl)) == -1 ) {
+ log_msg(LOG_ERR, "handle_child_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if(len == 0) {
+ /* EOF */
+ data->forward_mode = 0;
+ return;
+ }
+ got_acl += len;
+ data->got_bytes += len;
+ if(got_acl >= (int)sizeof(data->acl_num)) {
+ uint16_t len = htons(data->total_bytes);
+ DEBUG(DEBUG_IPC,2, (LOG_INFO,
+ "main fwd passed packet write %d", (int)data->got_bytes));
+#ifndef NDEBUG
+ if(nsd_debug_level >= 2)
+ debug_print_fwd_name(len, data->packet, data->acl_num);
+#endif
+ data->forward_mode = 0;
+ mode = NSD_PASS_TO_XFRD;
+ if(!write_socket(*data->xfrd_sock, &mode, sizeof(mode)) ||
+ !write_socket(*data->xfrd_sock, &len, sizeof(len)) ||
+ !write_socket(*data->xfrd_sock, buffer_begin(data->packet),
+ data->total_bytes) ||
+ !write_socket(*data->xfrd_sock, &data->acl_num,
+ sizeof(data->acl_num))) {
+ log_msg(LOG_ERR, "error in ipc fwd main2xfrd: %s",
+ strerror(errno));
+ }
+ }
+ return;
+ }
+
+ /* read command from ipc */
+ if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
+ log_msg(LOG_ERR, "handle_child_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if (len == 0)
+ {
+ size_t i;
+ if(handler->fd > 0) close(handler->fd);
+ for(i=0; i<data->nsd->child_count; ++i)
+ if(data->nsd->children[i].child_fd == handler->fd) {
+ data->nsd->children[i].child_fd = -1;
+ data->nsd->children[i].has_exited = 1;
+ DEBUG(DEBUG_IPC,1, (LOG_INFO,
+ "server %d closed cmd channel",
+ (int) data->nsd->children[i].pid));
+ }
+ handler->fd = -1;
+ parent_check_all_children_exited(data->nsd);
+ return;
+ }
+
+ switch (mode) {
+ case NSD_QUIT:
+ data->nsd->mode = mode;
+ break;
+ case NSD_STATS:
+ data->nsd->signal_hint_stats = 1;
+ break;
+ case NSD_REAP_CHILDREN:
+ data->nsd->signal_hint_child = 1;
+ break;
+ case NSD_PASS_TO_XFRD:
+ /* set mode for handle_child_command; echo to xfrd. */
+ data->forward_mode = 1;
+ data->got_bytes = 0;
+ data->total_bytes = 0;
+ break;
+ default:
+ log_msg(LOG_ERR, "handle_child_command: bad mode %d",
+ (int) mode);
+ break;
+ }
+}
+
+void
+parent_check_all_children_exited(struct nsd* nsd)
+{
+ size_t i;
+ for(i=0; i < nsd->child_count; i++) {
+ if(!nsd->children[i].need_to_exit)
+ return;
+ if(!nsd->children[i].has_exited)
+ return;
+ }
+ nsd->mode = NSD_QUIT_SYNC;
+ DEBUG(DEBUG_IPC,2, (LOG_INFO, "main: all children exited. quit sync."));
+}
+
+void
+parent_handle_reload_command(netio_type *ATTR_UNUSED(netio),
+ netio_handler_type *handler,
+ netio_event_types_type event_types)
+{
+ sig_atomic_t mode;
+ int len;
+ size_t i;
+ struct nsd *nsd = (struct nsd*) handler->user_data;
+ if (!(event_types & NETIO_EVENT_READ)) {
+ return;
+ }
+ /* read command from ipc */
+ if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
+ log_msg(LOG_ERR, "handle_reload_command: read: %s",
+ strerror(errno));
+ return;
+ }
+ if (len == 0)
+ {
+ if(handler->fd > 0) {
+ close(handler->fd);
+ handler->fd = -1;
+ }
+ log_msg(LOG_ERR, "handle_reload_cmd: reload closed cmd channel");
+ return;
+ }
+ switch (mode) {
+ case NSD_QUIT_SYNC:
+ /* set all children to exit, only then notify xfrd. */
+ /* so that buffered packets to pass to xfrd can arrive. */
+ for(i=0; i < nsd->child_count; i++) {
+ nsd->children[i].need_to_exit = 1;
+ if(nsd->children[i].pid > 0 &&
+ nsd->children[i].child_fd > 0) {
+ nsd->children[i].need_to_send_QUIT = 1;
+ nsd->children[i].handler->event_types
+ |= NETIO_EVENT_WRITE;
+ } else {
+ if(nsd->children[i].child_fd == -1)
+ nsd->children[i].has_exited = 1;
+ }
+ }
+ parent_check_all_children_exited(nsd);
+ break;
+ default:
+ log_msg(LOG_ERR, "handle_reload_command: bad mode %d",
+ (int) mode);
+ break;
+ }
+}
+
+static void
+xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone)
+{
+ sig_atomic_t cmd = NSD_ZONE_STATE;
+ uint8_t ok = 1;
+ uint16_t sz = dname_total_size(zone->apex) + 1;
+ sz = htons(sz);
+ if(zone->state == xfrd_zone_expired)
+ ok = 0;
+
+ DEBUG(DEBUG_IPC,1, (LOG_INFO,
+ "xfrd encoding ipc zone state msg for zone %s state %d.",
+ zone->apex_str, (int)zone->state));
+
+ buffer_clear(buffer);
+ buffer_write(buffer, &cmd, sizeof(cmd));
+ buffer_write(buffer, &sz, sizeof(sz));
+ buffer_write(buffer, &ok, sizeof(ok));
+ buffer_write(buffer, zone->apex, dname_total_size(zone->apex));
+ buffer_flip(buffer);
+}
+
+static void
+xfrd_send_reload_req(xfrd_state_t* xfrd)
+{
+ sig_atomic_t req = NSD_RELOAD;
+ /* ask server_main for a reload */
+ if(write(xfrd->ipc_handler.fd, &req, sizeof(req)) == -1) {
+ if(errno == EAGAIN || errno == EINTR)
+ return; /* try again later */
+ log_msg(LOG_ERR, "xfrd: problems sending reload command: %s",
+ strerror(errno));
+ return;
+ }
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: asked nsd to reload new updates"));
+ xfrd_prepare_zones_for_reload();
+ xfrd->reload_cmd_last_sent = xfrd_time();
+ xfrd->need_to_send_reload = 0;
+ xfrd->can_send_reload = 0;
+}
+
+static void
+xfrd_send_quit_req(xfrd_state_t* xfrd)
+{
+ sig_atomic_t cmd = NSD_QUIT;
+ xfrd->ipc_send_blocked = 1;
+ xfrd->ipc_handler.event_types &= (~NETIO_EVENT_WRITE);
+ xfrd->sending_zone_state = 0;
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc send ackreload(quit)"));
+ if(write_socket(xfrd->ipc_handler.fd, &cmd, sizeof(cmd)) == -1) {
+ log_msg(LOG_ERR, "xfrd: error writing ack to main: %s",
+ strerror(errno));
+ }
+ xfrd->need_to_send_quit = 0;
+}
+
+static void
+xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet)
+{
+ xfrd_soa_t soa;
+ xfrd_soa_t* soa_ptr = &soa;
+ xfrd_zone_t* zone;
+ /* dname is sent in memory format */
+ const dname_type* dname = (const dname_type*)buffer_begin(packet);
+
+ /* find zone and decode SOA */
+ zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname);
+ buffer_skip(packet, dname_total_size(dname));
+
+ if(!buffer_available(packet, sizeof(uint32_t)*6 + sizeof(uint8_t)*2)) {
+ /* NSD has zone without any info */
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s lost zone",
+ dname_to_string(dname,0)));
+ soa_ptr = NULL;
+ } else {
+ /* read soa info */
+ memset(&soa, 0, sizeof(soa));
+ /* left out type, klass, count for speed */
+ soa.type = htons(TYPE_SOA);
+ soa.klass = htons(CLASS_IN);
+ soa.ttl = htonl(buffer_read_u32(packet));
+ soa.rdata_count = htons(7);
+ soa.prim_ns[0] = buffer_read_u8(packet);
+ if(!buffer_available(packet, soa.prim_ns[0]))
+ return;
+ buffer_read(packet, soa.prim_ns+1, soa.prim_ns[0]);
+ soa.email[0] = buffer_read_u8(packet);
+ if(!buffer_available(packet, soa.email[0]))
+ return;
+ buffer_read(packet, soa.email+1, soa.email[0]);
+
+ soa.serial = htonl(buffer_read_u32(packet));
+ soa.refresh = htonl(buffer_read_u32(packet));
+ soa.retry = htonl(buffer_read_u32(packet));
+ soa.expire = htonl(buffer_read_u32(packet));
+ soa.minimum = htonl(buffer_read_u32(packet));
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %u",
+ dname_to_string(dname,0), ntohl(soa.serial)));
+ }
+
+ if(!zone) {
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s master zone updated",
+ dname_to_string(dname,0)));
+ notify_handle_master_zone_soainfo(xfrd->notify_zones,
+ dname, soa_ptr);
+ return;
+ }
+ xfrd_handle_incoming_soa(zone, soa_ptr, xfrd_time());
+}
+
+void
+xfrd_handle_ipc(netio_type* ATTR_UNUSED(netio),
+ netio_handler_type *handler,
+ netio_event_types_type event_types)
+{
+ xfrd_state_t* xfrd = (xfrd_state_t*)handler->user_data;
+ if ((event_types & NETIO_EVENT_READ))
+ {
+ /* first attempt to read as a signal from main
+ * could block further send operations */
+ xfrd_handle_ipc_read(handler, xfrd);
+ }
+ if ((event_types & NETIO_EVENT_WRITE))
+ {
+ if(xfrd->ipc_send_blocked) { /* wait for SOA_END */
+ handler->event_types = NETIO_EVENT_READ;
+ return;
+ }
+ /* if necessary prepare a packet */
+ if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) &&
+ !xfrd->need_to_send_quit &&
+ !xfrd->sending_zone_state &&
+ xfrd->dirty_zones->num > 0) {
+ xfrd_zone_t* zone = (xfrd_zone_t*)stack_pop(xfrd->dirty_zones);
+ assert(zone);
+ zone->dirty = 0;
+ xfrd->sending_zone_state = 1;
+ xfrd_write_expire_notification(xfrd->ipc_conn_write->packet, zone);
+ xfrd->ipc_conn_write->msglen = buffer_limit(xfrd->ipc_conn_write->packet);
+ /* skip length bytes; they are encoded in the packet, after cmd */
+ xfrd->ipc_conn_write->total_bytes = sizeof(uint16_t);
+ }
+ /* write a bit */
+ if(xfrd->sending_zone_state) {
+ /* call conn_write */
+ int ret = conn_write(xfrd->ipc_conn_write);
+ if(ret == -1) {
+ log_msg(LOG_ERR, "xfrd: error in write ipc: %s", strerror(errno));
+ xfrd->sending_zone_state = 0;
+ }
+ else if(ret == 1) { /* done */
+ xfrd->sending_zone_state = 0;
+ }
+ } else if(xfrd->need_to_send_quit) {
+ xfrd_send_quit_req(xfrd);
+ } else if(xfrd->can_send_reload && xfrd->need_to_send_reload) {
+ xfrd_send_reload_req(xfrd);
+ }
+ if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) &&
+ !xfrd->need_to_send_quit &&
+ !xfrd->sending_zone_state &&
+ xfrd->dirty_zones->num == 0) {
+ handler->event_types = NETIO_EVENT_READ; /* disable writing for now */
+ }
+ }
+
+}
+
+static void
+xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
+{
+ sig_atomic_t cmd;
+ int len;
+
+ if(xfrd->ipc_conn->is_reading==2) {
+ buffer_type* tmp = xfrd->ipc_pass;
+ uint32_t acl_num;
+ /* read acl_num */
+ int ret = conn_read(xfrd->ipc_conn);
+ if(ret == -1) {
+ log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno));
+ xfrd->ipc_conn->is_reading = 0;
+ return;
+ }
+ if(ret == 0)
+ return;
+ buffer_flip(xfrd->ipc_conn->packet);
+ xfrd->ipc_pass = xfrd->ipc_conn->packet;
+ xfrd->ipc_conn->packet = tmp;
+ xfrd->ipc_conn->is_reading = 0;
+ acl_num = buffer_read_u32(xfrd->ipc_pass);
+ xfrd_handle_passed_packet(xfrd->ipc_conn->packet, acl_num);
+ return;
+ }
+ if(xfrd->ipc_conn->is_reading) {
+ /* reading an IPC message */
+ int ret = conn_read(xfrd->ipc_conn);
+ if(ret == -1) {
+ log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno));
+ xfrd->ipc_conn->is_reading = 0;
+ return;
+ }
+ if(ret == 0)
+ return;
+ buffer_flip(xfrd->ipc_conn->packet);
+ if(xfrd->ipc_is_soa) {
+ xfrd->ipc_conn->is_reading = 0;
+ xfrd_handle_ipc_SOAINFO(xfrd, xfrd->ipc_conn->packet);
+ } else {
+ /* use ipc_conn to read remaining data as well */
+ buffer_type* tmp = xfrd->ipc_pass;
+ xfrd->ipc_conn->is_reading=2;
+ xfrd->ipc_pass = xfrd->ipc_conn->packet;
+ xfrd->ipc_conn->packet = tmp;
+ xfrd->ipc_conn->total_bytes = sizeof(xfrd->ipc_conn->msglen);
+ xfrd->ipc_conn->msglen = sizeof(uint32_t);
+ buffer_clear(xfrd->ipc_conn->packet);
+ buffer_set_limit(xfrd->ipc_conn->packet, xfrd->ipc_conn->msglen);
+ }
+ return;
+ }
+
+ if((len = read(handler->fd, &cmd, sizeof(cmd))) == -1) {
+ log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
+ strerror(errno));
+ return;
+ }
+ if(len == 0)
+ {
+ /* parent closed the connection. Quit */
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main closed connection."));
+ xfrd->shutdown = 1;
+ return;
+ }
+
+ switch(cmd) {
+ case NSD_QUIT:
+ case NSD_SHUTDOWN:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main send shutdown cmd."));
+ xfrd->shutdown = 1;
+ break;
+ case NSD_SOA_BEGIN:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_BEGIN"));
+ /* reload starts sending SOA INFOs; don't block */
+ xfrd->parent_soa_info_pass = 1;
+ /* reset the nonblocking ipc write;
+ the new parent does not want half a packet */
+ xfrd->sending_zone_state = 0;
+ break;
+ case NSD_SOA_INFO:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_INFO"));
+ assert(xfrd->parent_soa_info_pass);
+ xfrd->ipc_is_soa = 1;
+ xfrd->ipc_conn->is_reading = 1;
+ break;
+ case NSD_SOA_END:
+ /* reload has finished */
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_END"));
+ xfrd->can_send_reload = 1;
+ xfrd->parent_soa_info_pass = 0;
+ xfrd->ipc_send_blocked = 0;
+ handler->event_types |= NETIO_EVENT_WRITE;
+ xfrd_reopen_logfile();
+ xfrd_check_failed_updates();
+ xfrd_send_expy_all_zones();
+ break;
+ case NSD_PASS_TO_XFRD:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv PASS_TO_XFRD"));
+ xfrd->ipc_is_soa = 0;
+ xfrd->ipc_conn->is_reading = 1;
+ break;
+ case NSD_RELOAD:
+ /* main tells us that reload is done, stop ipc send to main */
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD"));
+ handler->event_types |= NETIO_EVENT_WRITE;
+ xfrd->need_to_send_quit = 1;
+ break;
+ default:
+ log_msg(LOG_ERR, "xfrd_handle_ipc: bad mode %d (%d)", (int)cmd,
+ ntohl(cmd));
+ break;
+ }
+
+ if(xfrd->ipc_conn->is_reading) {
+ /* setup read of info */
+ xfrd->ipc_conn->total_bytes = 0;
+ xfrd->ipc_conn->msglen = 0;
+ buffer_clear(xfrd->ipc_conn->packet);
+ }
+}
+