struct fast_message *fast_session_recv(struct fast_session *self, int flags) { struct buffer *buffer = self->rx_buffer; struct fast_message *msg; size_t size; ssize_t nr; msg = fast_message_decode(self); if (msg) return msg; size = buffer_remaining(buffer); if (size <= FAST_MESSAGE_MAX_SIZE) buffer_compact(buffer); /* * If buffer's capacity is at least * 2 times FAST_MESSAGE_MAX_SIZE then, * remaining > FAST_MESSAGE_MAX_SIZE */ nr = self->recv(buffer, self->sockfd, FAST_MESSAGE_MAX_SIZE, flags); if (nr <= 0) return NULL; return fast_message_decode(self); }
/** * Send notify over udp. * */ static int notify_send_udp(notify_type* notify, buffer_type* buffer) { struct sockaddr_storage to; socklen_t to_len = 0; int fd = -1; int family = PF_INET; ssize_t nb = 0; ods_log_assert(buffer); ods_log_assert(notify); ods_log_assert(notify->secondary); ods_log_assert(notify->secondary->address); /* this will set the remote port to acl->port or TCP_PORT */ to_len = xfrd_acl_sockaddr_to(notify->secondary, &to); /* get the address family of the remote host */ if (notify->secondary->family == AF_INET6) { family = PF_INET6; } /* create socket */ fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { ods_log_error("[%s] unable to send data over udp to %s: " "socket() failed (%s)", notify_str, notify->secondary->address, strerror(errno)); return -1; } /* bind it? */ /* send it (udp) */ ods_log_deeebug("[%s] send %d bytes over udp to %s", notify_str, buffer_remaining(buffer), notify->secondary->address); nb = sendto(fd, buffer_current(buffer), buffer_remaining(buffer), 0, (struct sockaddr*)&to, to_len); if (nb == -1) { ods_log_error("[%s] unable to send data over udp to %s: " "sendto() failed (%s)", notify_str, notify->secondary->address, strerror(errno)); close(fd); return -1; } return fd; }
static int readable_cb(int fd, void *arg) { struct argos_net_conn *conn = arg; /* * We are ready to read if we are connected and have room in the input * buffer. */ return ((conn->state == ARGOS_NET_CONN_CONNECTED) && (buffer_remaining(conn->inbuf) > 0)); }
ssize_t buffer_inflate(struct buffer *comp_buf, struct buffer *uncomp_buf, z_stream *stream) { unsigned long nr; ssize_t ret; int err; nr = buffer_size(comp_buf); if (!nr) return 0; if (nr > INFLATE_SIZE) nr = INFLATE_SIZE; stream->avail_in = nr; stream->avail_out = buffer_remaining(uncomp_buf); stream->next_out = (void *) buffer_end(uncomp_buf); retry: err = inflate(stream, Z_NO_FLUSH); switch (err) { case Z_STREAM_END: case Z_BUF_ERROR: case Z_OK: /* OK to continue */ break; default: return -1; } if (!err && !stream->avail_out) goto retry; buffer_advance(comp_buf, nr - stream->avail_in); ret = buffer_remaining(uncomp_buf) - stream->avail_out; uncomp_buf->end += ret; return ret; }
int xfrd_send_udp(acl_options_t* acl, buffer_type* packet, acl_options_t* ifc) { #ifdef INET6 struct sockaddr_storage to; #else struct sockaddr_in to; #endif /* INET6 */ int fd, family; /* this will set the remote port to acl->port or TCP_PORT */ socklen_t to_len = xfrd_acl_sockaddr_to(acl, &to); /* get the address family of the remote host */ if(acl->is_ipv6) { #ifdef INET6 family = PF_INET6; #else return -1; #endif /* INET6 */ } else { family = PF_INET; } fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); if(fd == -1) { log_msg(LOG_ERR, "xfrd: cannot create udp socket to %s: %s", acl->ip_address_spec, strerror(errno)); return -1; } /* bind it */ if (!xfrd_bind_local_interface(fd, ifc, acl, 0)) { log_msg(LOG_ERR, "xfrd: cannot bind outgoing interface '%s' to " "udp socket: No matching ip addresses found", ifc->ip_address_spec); return -1; } /* send it (udp) */ if(sendto(fd, buffer_current(packet), buffer_remaining(packet), 0, (struct sockaddr*)&to, to_len) == -1) { log_msg(LOG_ERR, "xfrd: sendto %s failed %s", acl->ip_address_spec, strerror(errno)); return -1; } return fd; }
ssize_t argos_net_send_packet(struct argos_net_conn *conn, const struct pcap_pkthdr *h, const u_char *sp, uint8_t channel) { if (conn->state == ARGOS_NET_CONN_DEAD) { orion_log_err("unable to send on DEAD network handle"); errno = EBADF; return -1; } assert(conn->state != ARGOS_NET_CONN_IDLE); if (conn->shutdown) { /* illegal to try to send after argos_net_shutdown() is called */ errno = EPIPE; return -1; } struct argos_net_pcap_msg msg; size_t reqlen = sizeof(msg) + h->caplen; msg.msgtype = htons(ARGOS_NET_PCAP_MSGTYPE); msg.msglen = htonl(reqlen); msg.channel = channel; msg.ts_sec = htonl(h->ts.tv_sec); msg.ts_usec = htonl(h->ts.tv_usec); msg.msglen = htonl(reqlen); msg.pktlen = htonl(h->len); msg.caplen = htonl(h->caplen); if (buffer_remaining(conn->pktbuf) < reqlen) { errno = ENOBUFS; return -1; } int rv = buffer_write(conn->pktbuf, &msg, sizeof(msg)); assert(rv >= 0); rv = buffer_write(conn->pktbuf, sp, h->caplen); assert(rv >= 0); if (conn->compress_evt_reg == NULL) { static const struct timeval timeout = COMPRESS_DELAY_MAX; conn->compress_evt_reg = async_schedule(&timeout, compression_timeout, conn, 0); } return reqlen; }
ssize_t buffer_xread(struct buffer *buf, int fd) { size_t count; ssize_t len; void *end; end = buffer_end(buf); count = buffer_remaining(buf); len = xread(fd, end, count); if (len < 0) return len; buf->end += len; return len; }
static void read_cb(int fd, void *arg) { struct argos_net_conn *conn = arg; /* * We only want to do reads if conn->state == NET_CONN_CONNECTED, but we * don't assert() this because its possible for this socket to be selected * simultaneously for both a read and a write and then for our state to * change during the write attempt. */ if (conn->state != ARGOS_NET_CONN_CONNECTED) return; ssize_t len = recv(conn->sock, buffer_tail(conn->inbuf), buffer_remaining(conn->inbuf), 0); if (len == -1) { if (IS_NETWORK_ERROR(errno)) { /* network error; reset our connection */ orion_log_warn_errno("recv"); reset_connection(conn, 0); } else if (errno == EINTR) { /* don't care; ignore it */ } else { /* anything else is a fatal error */ orion_log_crit_errno("recv"); kill_connection(conn); orion_log_crit("unexpected recv() error; connection is now dead"); } } else if (len == 0) { /* EOF received (maybe other end is shutting down?) */ orion_log_info("EOF received from remote end - closing socket"); if (buffer_len(conn->inbuf) > 0) orion_log_warn("incomplete message received (inbuflen=%d)", buffer_len(conn->inbuf)); reset_connection(conn, 1 /* flush buffers */); } else { /* ok, we read some data into the inbuf; update the buffer */ int rv = buffer_expand(conn->inbuf, len); if (rv == -1) KABOOM("buffer_expand"); conn->bytes_recv += len; /* now process (i.e. look for complete messages in) the inbuf */ process_inbuf(conn); } }
int xfrd_udp_read_packet(buffer_type* packet, int fd) { ssize_t received; /* read the data */ buffer_clear(packet); received = recvfrom(fd, buffer_begin(packet), buffer_remaining(packet), 0, NULL, NULL); if(received == -1) { log_msg(LOG_ERR, "xfrd: recvfrom failed: %s", strerror(errno)); return 0; } buffer_set_limit(packet, received); return 1; }
/** * Read packet from udp. * */ static int notify_udp_read_packet(notify_type* notify) { xfrhandler_type* xfrhandler = NULL; ssize_t received = 0; ods_log_assert(notify); xfrhandler = (xfrhandler_type*) notify->xfrhandler; ods_log_assert(xfrhandler); buffer_clear(xfrhandler->packet); received = recvfrom(notify->handler.fd, buffer_begin(xfrhandler->packet), buffer_remaining(xfrhandler->packet), 0, NULL, NULL); if (received == -1) { ods_log_error("[%s] unable to read packet: recvfrom() failed fd %d " "(%s)", notify_str, notify->handler.fd, strerror(errno)); return 0; } buffer_set_limit(xfrhandler->packet, received); return 1; }
ssize_t argos_net_send_errmsg(struct argos_net_conn *conn, uint16_t errnum, const char *errmsg) { if (conn->state == ARGOS_NET_CONN_DEAD) { orion_log_err("unable to send on DEAD network handle"); errno = EBADF; return -1; } if (conn->shutdown) { /* illegal to try to send after argos_net_shutdown() is called */ errno = EPIPE; return -1; } int slen = strlen(errmsg); if (slen > (1 << 16)) { errno = EMSGSIZE; return -1; } struct argos_net_error_msg msg; msg.msgtype = htons(ARGOS_NET_ERROR_MSGTYPE); msg.msglen = htonl(sizeof(struct argos_net_error_msg) + slen); msg.errnum = htons(errnum); /* error messages enqueue directly into the outbuf */ size_t reqlen = sizeof(msg) + slen; if (buffer_remaining(conn->outbuf) < reqlen) { errno = ENOBUFS; return -1; } int rv = buffer_write(conn->outbuf, &msg, sizeof(msg)); assert(rv >= 0); rv = buffer_write(conn->outbuf, errmsg, slen); assert(rv >= 0); return reqlen; }
ssize_t buffer_recv(struct buffer *buf, int sockfd, size_t size, int flags) { size_t count; ssize_t len; void *end; end = buffer_end(buf); count = buffer_remaining(buf); if (count > size) count = size; len = io_recv(sockfd, end, count, flags); if (len < 0) return len; buf->end += len; return len; }
bool buffer_printf(struct buffer *buf, const char *format, ...) { size_t size; va_list ap; char *end; int len; end = buffer_end(buf); size = buffer_remaining(buf); va_start(ap, format); len = vsnprintf(end, size, format, ap); va_end(ap); if (len < 0 || len >= size) return false; buf->end += len; return true; }
int soupbin3_session_recv(struct soupbin3_session *session, struct soupbin3_packet *packet) { uint16_t len; ssize_t nr; if (buffer_size(session->rx_buffer) > 0) goto decode; buffer_reset(session->rx_buffer); nr = buffer_read(session->rx_buffer, session->sockfd); if (nr <= 0) return -1; decode: len = buffer_get_be16(session->rx_buffer); if (buffer_remaining(session->rx_buffer) < len) assert(0); return soupbin3_packet_decode(session->rx_buffer, len, packet); }
static int rdata_services_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* ATTR_UNUSED(rr)) { int result = 0; buffer_type packet; buffer_create_from( &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); if (buffer_available(&packet, 1)) { uint8_t protocol_number = buffer_read_u8(&packet); ssize_t bitmap_size = buffer_remaining(&packet); uint8_t *bitmap = buffer_current(&packet); struct protoent *proto = getprotobynumber(protocol_number); if (proto) { int i; buffer_printf(output, "%s", proto->p_name); for (i = 0; i < bitmap_size * 8; ++i) { if (get_bit(bitmap, i)) { struct servent *service = getservbyport((int)htons(i), proto->p_name); if (service) { buffer_printf(output, " %s", service->s_name); } else { buffer_printf(output, " %d", i); } } } buffer_skip(&packet, bitmap_size); result = 1; } } return result; }
/* return value 0: syntaxerror,badIXFR, 1:OK, 2:done_and_skip_it */ static int apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos, const char* zone, uint32_t serialno, nsd_options_t* opt, uint16_t id, uint32_t seq_nr, uint32_t seq_total, int* is_axfr, int* delete_mode, int* rr_count, size_t child_count) { uint32_t filelen, msglen, pkttype, timestamp[2]; int qcount, ancount, counter; buffer_type* packet; region_type* region; int i; uint16_t rrlen; const dname_type *dname_zone, *dname; zone_type* zone_db; domain_type* last_in_list; char file_zone_name[3072]; uint32_t file_serial, file_seq_nr; uint16_t file_id; off_t mempos; memmove(&mempos, startpos, sizeof(off_t)); if(fseeko(in, mempos, SEEK_SET) == -1) { log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); return 0; } /* read ixfr packet RRs and apply to in memory db */ if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) { log_msg(LOG_ERR, "could not read type or wrong type"); return 0; } if(!diff_read_32(in, ×tamp[0]) || !diff_read_32(in, ×tamp[1])) { log_msg(LOG_ERR, "could not read timestamp"); return 0; } if(!diff_read_32(in, &filelen)) { log_msg(LOG_ERR, "could not read len"); return 0; } /* read header */ if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) { log_msg(LOG_ERR, "msg too short"); return 0; } region = region_create(xalloc, free); if(!region) { log_msg(LOG_ERR, "out of memory"); return 0; } if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) || !diff_read_32(in, &file_serial) || !diff_read_16(in, &file_id) || !diff_read_32(in, &file_seq_nr)) { log_msg(LOG_ERR, "could not part data"); region_destroy(region); return 0; } if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial || id != file_id || seq_nr != file_seq_nr) { log_msg(LOG_ERR, "internal error: reading part with changed id"); region_destroy(region); return 0; } msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t) - strlen(file_zone_name); packet = buffer_create(region, QIOBUFSZ); dname_zone = dname_parse(region, zone); zone_db = find_zone(db, dname_zone, opt, child_count); if(!zone_db) { log_msg(LOG_ERR, "no zone exists"); region_destroy(region); /* break out and stop the IXFR, ignore it */ return 2; } if(msglen > QIOBUFSZ) { log_msg(LOG_ERR, "msg too long"); region_destroy(region); return 0; } buffer_clear(packet); if(fread(buffer_begin(packet), msglen, 1, in) != 1) { log_msg(LOG_ERR, "short fread: %s", strerror(errno)); region_destroy(region); return 0; } buffer_set_limit(packet, msglen); /* only answer section is really used, question, additional and authority section RRs are skipped */ qcount = QDCOUNT(packet); ancount = ANCOUNT(packet); buffer_skip(packet, QHEADERSZ); /* skip queries */ for(i=0; i<qcount; ++i) if(!packet_skip_rr(packet, 1)) { log_msg(LOG_ERR, "bad RR in question section"); region_destroy(region); return 0; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: started packet for zone %s", dname_to_string(dname_zone, 0))); /* first RR: check if SOA and correct zone & serialno */ if(*rr_count == 0) { DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parse first RR", dname_to_string(dname_zone, 0))); dname = dname_make_from_packet(region, packet, 1, 1); if(!dname) { log_msg(LOG_ERR, "could not parse dname"); region_destroy(region); return 0; } if(dname_compare(dname_zone, dname) != 0) { log_msg(LOG_ERR, "SOA dname %s not equal to zone", dname_to_string(dname,0)); log_msg(LOG_ERR, "zone dname is %s", dname_to_string(dname_zone,0)); region_destroy(region); return 0; } if(!buffer_available(packet, 10)) { log_msg(LOG_ERR, "bad SOA RR"); region_destroy(region); return 0; } if(buffer_read_u16(packet) != TYPE_SOA || buffer_read_u16(packet) != CLASS_IN) { log_msg(LOG_ERR, "first RR not SOA IN"); region_destroy(region); return 0; } buffer_skip(packet, sizeof(uint32_t)); /* ttl */ if(!buffer_available(packet, buffer_read_u16(packet)) || !packet_skip_dname(packet) /* skip prim_ns */ || !packet_skip_dname(packet) /* skip email */) { log_msg(LOG_ERR, "bad SOA RR"); region_destroy(region); return 0; } if(buffer_read_u32(packet) != serialno) { buffer_skip(packet, -4); log_msg(LOG_ERR, "SOA serial %d different from commit %d", buffer_read_u32(packet), serialno); region_destroy(region); return 0; } buffer_skip(packet, sizeof(uint32_t)*4); counter = 1; *rr_count = 1; *is_axfr = 0; *delete_mode = 0; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } else counter = 0; last_in_list = zone_db->apex; for(; counter < ancount; ++counter,++(*rr_count)) { uint16_t type, klass; uint32_t ttl; if(!(dname=dname_make_from_packet(region, packet, 1,1))) { log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count); region_destroy(region); return 0; } if(!buffer_available(packet, 10)) { log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count); region_destroy(region); return 0; } type = buffer_read_u16(packet); klass = buffer_read_u16(packet); ttl = buffer_read_u32(packet); rrlen = buffer_read_u16(packet); if(!buffer_available(packet, rrlen)) { log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d", *rr_count, rrlen, (int)buffer_remaining(packet)); region_destroy(region); return 0; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); if(*rr_count == 1 && type != TYPE_SOA) { /* second RR: if not SOA: this is an AXFR; delete all zone contents */ delete_zone_rrs(db, zone_db); /* add everything else (incl end SOA) */ *delete_mode = 0; *is_axfr = 1; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } if(*rr_count == 1 && type == TYPE_SOA) { /* if the serial no of the SOA equals the serialno, then AXFR */ size_t bufpos = buffer_position(packet); uint32_t thisserial; if(!packet_skip_dname(packet) || !packet_skip_dname(packet) || buffer_remaining(packet) < sizeof(uint32_t)*5) { log_msg(LOG_ERR, "bad xfr SOA RR formerr."); region_destroy(region); return 0; } thisserial = buffer_read_u32(packet); if(thisserial == serialno) { /* AXFR */ delete_zone_rrs(db, zone_db); *delete_mode = 0; *is_axfr = 1; } /* must have stuff in memory for a successful IXFR, * the serial number of the SOA has been checked * previously (by check_for_bad_serial) if it exists */ if(!*is_axfr && !domain_find_rrset(zone_db->apex, zone_db, TYPE_SOA)) { log_msg(LOG_ERR, "%s SOA serial %d is not " "in memory, skip IXFR", zone, serialno); region_destroy(region); /* break out and stop the IXFR, ignore it */ return 2; } buffer_set_position(packet, bufpos); } if(type == TYPE_SOA && !*is_axfr) { /* switch from delete-part to add-part and back again, just before soa - so it gets deleted and added too */ /* this means we switch to delete mode for the final SOA */ *delete_mode = !*delete_mode; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } if(type == TYPE_TSIG || type == TYPE_OPT) { /* ignore pseudo RRs */ buffer_skip(packet, rrlen); continue; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s", *delete_mode?"del":"add", dname_to_string(dname,0), rrtype_to_string(type))); if(*delete_mode) { /* delete this rr */ if(!*is_axfr && type == TYPE_SOA && counter==ancount-1 && seq_nr == seq_total-1) { continue; /* do not delete final SOA RR for IXFR */ } if(!delete_RR(db, dname, type, klass, last_in_list, packet, rrlen, zone_db, region, *is_axfr)) { region_destroy(region); return 0; } if (!*is_axfr && last_in_list->nextdiff) { last_in_list = last_in_list->nextdiff; } } else { /* add this rr */ if(!add_RR(db, dname, type, klass, ttl, packet, rrlen, zone_db, *is_axfr)) { region_destroy(region); return 0; } } } fix_empty_terminals(zone_db); region_destroy(region); return 1; }
int print_rr(FILE *out, struct state_pretty_rr *state, rr_type *record) { region_type *region = region_create(xalloc, free); buffer_type *output = buffer_create(region, MAX_RDLENGTH); rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(record->type); int result; const dname_type *owner = domain_dname(record->owner); const dname_type *owner_origin = dname_origin(region, owner); int owner_changed = (!state->previous_owner || dname_compare(state->previous_owner, owner) != 0); if (owner_changed) { int origin_changed = (!state->previous_owner_origin || dname_compare( state->previous_owner_origin, owner_origin) != 0); if (origin_changed) { buffer_printf( output, "$ORIGIN %s\n", dname_to_string(owner_origin, NULL)); } set_previous_owner(state, owner); buffer_printf(output, "%s", dname_to_string(owner, state->previous_owner_origin)); } buffer_printf(output, "\t%lu\t%s\t%s", (unsigned long) record->ttl, rrclass_to_string(record->klass), rrtype_to_string(record->type)); result = print_rdata(output, descriptor, record); if (!result) { /* * Some RDATA failed to print, so print the record's * RDATA in unknown format. */ result = rdata_atoms_to_unknown_string(output, descriptor, record->rdata_count, record->rdatas); } if (result) { buffer_printf(output, "\n"); buffer_flip(output); (void)write_data(out, buffer_current(output), buffer_remaining(output)); /* fflush(out); */ } region_destroy(region); return result; }
static void handle_udp(netio_type *ATTR_UNUSED(netio), netio_handler_type *handler, netio_event_types_type event_types) { struct udp_handler_data *data = (struct udp_handler_data *) handler->user_data; int received, sent; struct query *q = data->query; if (!(event_types & NETIO_EVENT_READ)) { return; } /* Account... */ #ifdef BIND8_STATS if (data->socket->addr->ai_family == AF_INET) { STATUP(data->nsd, qudp); } else if (data->socket->addr->ai_family == AF_INET6) { STATUP(data->nsd, qudp6); } #endif /* Initialize the query... */ query_reset(q, UDP_MAX_MESSAGE_LEN, 0); received = recvfrom(handler->fd, buffer_begin(q->packet), buffer_remaining(q->packet), 0, (struct sockaddr *)&q->addr, &q->addrlen); if (received == -1) { if (errno != EAGAIN && errno != EINTR) { log_msg(LOG_ERR, "recvfrom failed: %s", strerror(errno)); STATUP(data->nsd, rxerr); /* No zone statup */ } } else { buffer_skip(q->packet, received); buffer_flip(q->packet); /* Process and answer the query... */ if (server_process_query(data->nsd, q) != QUERY_DISCARDED) { #ifdef BIND8_STATS if (RCODE(q->packet) == RCODE_OK && !AA(q->packet)) { STATUP(data->nsd, nona); ZTATUP(q->zone, nona); } # ifdef USE_ZONE_STATS if (data->socket->addr->ai_family == AF_INET) { ZTATUP(q->zone, qudp); } else if (data->socket->addr->ai_family == AF_INET6) { ZTATUP(q->zone, qudp6); } # endif #endif /* Add EDNS0 and TSIG info if necessary. */ query_add_optional(q, data->nsd); buffer_flip(q->packet); sent = sendto(handler->fd, buffer_begin(q->packet), buffer_remaining(q->packet), 0, (struct sockaddr *) &q->addr, q->addrlen); if (sent == -1) { log_msg(LOG_ERR, "sendto failed: %s", strerror(errno)); STATUP(data->nsd, txerr); ZTATUP(q->zone, txerr); } else if ((size_t) sent != buffer_remaining(q->packet)) { log_msg(LOG_ERR, "sent %d in place of %d bytes", sent, (int) buffer_remaining(q->packet)); #ifdef BIND8_STATS } else { /* Account the rcode & TC... */ STATUP2(data->nsd, rcode, RCODE(q->packet)); ZTATUP2(q->zone, rcode, RCODE(q->packet)); if (TC(q->packet)) { STATUP(data->nsd, truncated); ZTATUP(q->zone, truncated); } #endif /* BIND8_STATS */ } #ifdef BIND8_STATS } else { STATUP(data->nsd, dropped); # ifdef USE_ZONE_STATS if (q->zone) { ZTATUP(q->zone, dropped); } # endif #endif } } }
static void handle_tcp_writing(netio_type *netio, netio_handler_type *handler, netio_event_types_type event_types) { struct tcp_handler_data *data = (struct tcp_handler_data *) handler->user_data; ssize_t sent; struct query *q = data->query; if (event_types & NETIO_EVENT_TIMEOUT) { /* Connection timed out. */ cleanup_tcp_handler(netio, handler); return; } assert(event_types & NETIO_EVENT_WRITE); if (data->bytes_transmitted < sizeof(q->tcplen)) { /* Writing the response packet length. */ uint16_t n_tcplen = htons(q->tcplen); sent = write(handler->fd, (const char *) &n_tcplen + data->bytes_transmitted, sizeof(n_tcplen) - data->bytes_transmitted); if (sent == -1) { if (errno == EAGAIN || errno == EINTR) { /* * Write would block, wait until * socket becomes writable again. */ return; } else { #ifdef ECONNRESET if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ #ifdef EPIPE if(verbosity >= 2 || errno != EPIPE) #endif /* EPIPE 'broken pipe' */ log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; } } data->bytes_transmitted += sent; if (data->bytes_transmitted < sizeof(q->tcplen)) { /* * Writing not complete, wait until socket * becomes writable again. */ return; } assert(data->bytes_transmitted == sizeof(q->tcplen)); } assert(data->bytes_transmitted < q->tcplen + sizeof(q->tcplen)); sent = write(handler->fd, buffer_current(q->packet), buffer_remaining(q->packet)); if (sent == -1) { if (errno == EAGAIN || errno == EINTR) { /* * Write would block, wait until * socket becomes writable again. */ return; } else { #ifdef ECONNRESET if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ #ifdef EPIPE if(verbosity >= 2 || errno != EPIPE) #endif /* EPIPE 'broken pipe' */ log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; } } buffer_skip(q->packet, sent); data->bytes_transmitted += sent; if (data->bytes_transmitted < q->tcplen + sizeof(q->tcplen)) { /* * Still more data to write when socket becomes * writable again. */ return; } assert(data->bytes_transmitted == q->tcplen + sizeof(q->tcplen)); if (data->query_state == QUERY_IN_AXFR) { /* Continue processing AXFR and writing back results. */ buffer_clear(q->packet); data->query_state = query_axfr(data->nsd, q); if (data->query_state != QUERY_PROCESSED) { query_add_optional(data->query, data->nsd); /* Reset data. */ buffer_flip(q->packet); q->tcplen = buffer_remaining(q->packet); data->bytes_transmitted = 0; /* Reset timeout. */ handler->timeout->tv_sec = data->nsd->tcp_timeout; handler->timeout->tv_nsec = 0; timespec_add(handler->timeout, netio_current_time(netio)); /* * Write data if/when the socket is writable * again. */ return; } } /* * Done sending, wait for the next request to arrive on the * TCP socket by installing the TCP read handler. */ if (data->nsd->tcp_query_count > 0 && data->query_count >= data->nsd->tcp_query_count) { (void) shutdown(handler->fd, SHUT_WR); } data->bytes_transmitted = 0; handler->timeout->tv_sec = data->nsd->tcp_timeout; handler->timeout->tv_nsec = 0; timespec_add(handler->timeout, netio_current_time(netio)); handler->event_types = NETIO_EVENT_READ | NETIO_EVENT_TIMEOUT; handler->event_handler = handle_tcp_reading; }
static int logg_internal(const enum loglevel level, const char *file, const char *func, const unsigned int line, int log_errno, const char *fmt, va_list args ) { struct big_buff *logg_buff; int ret_val, old_errno, retry_cnt; old_errno = errno; /* get our tls-print buf */ if(!(logg_buff = logg_get_buf())) { /* * hmmm, something went wrong, lets see, if we can get the message to * the user by other means */ return do_vlogging(level, fmt, args); } /* add time, if wanted, to buffer */ if(server.settings.logging.add_date_time) logg_buff->pos += add_time_to_buffer(buffer_start(*logg_buff), buffer_remaining(*logg_buff)); /* put out the "extra" stuff, if there */ if(file) { retry_cnt = 0; prefetch(strlpcpy); prefetch(file); prefetch(func); /* * calling snprintf for 2 * strcpy + an itoa is "nice" * but goes round the full stdio bloat: * snprintf->vsnprintf->vfprintf-> myriads of funcs to print */ do { char *sptr, *stmp, *rstart; size_t remaining; stmp = sptr = buffer_start(*logg_buff); remaining = buffer_remaining(*logg_buff); rstart = strlpcpy(sptr, file, remaining); remaining -= rstart - sptr; if(unlikely(remaining < 7)) goto realloc; sptr = rstart; *sptr++ = ':'; remaining--; rstart = strlpcpy(sptr, func, remaining); remaining -= rstart - sptr; if(unlikely(remaining < 18)) /* make sure we have enough space */ goto realloc; sptr = rstart; *sptr++ = '('; *sptr++ = ')'; *sptr++ = '@'; remaining -= 3; rstart = put_dec_trunc(sptr, line); /* 99,999 lines should be... */ strreverse(sptr, rstart - 1); remaining -= rstart - sptr; if(unlikely(remaining < 2)) goto realloc; sptr = rstart; *sptr++ = ':'; *sptr++ = ' '; logg_buff->pos += sptr - stmp; break; realloc: /* now we are in a slow path, no need to hurry */ { struct big_buff *tmp_buff; size_t len = strlen(file) + strlen(func) + 6 + 30 + strlen(fmt) * 3; len = ROUND_ALIGN(len * 2, 2048) + logg_buff->capacity; tmp_buff = realloc(logg_buff, sizeof(*logg_buff) + len); if(tmp_buff) { logg_buff = tmp_buff; logg_buff->limit = logg_buff->capacity = len; } else break; retry_cnt++; } } while(retry_cnt < 4); } ret_val = 0; retry_cnt = 0; /* format the message in tls */ do { size_t len = logg_buff->capacity; va_list tmp_valist; if(ret_val < 0) { len *= 2; if(++retry_cnt > 4) { ret_val = 0; break; } } else if((size_t)ret_val > buffer_remaining(*logg_buff)) len = ROUND_ALIGN((size_t)ret_val * 2, 1024); /* align to a k */ if(unlikely(len != logg_buff->capacity)) { struct big_buff *tmp_buf = realloc(logg_buff, sizeof(*logg_buff) + len); if(tmp_buf) { logg_buff = tmp_buf; logg_buff->limit = logg_buff->capacity = len; } else { ret_val = buffer_remaining(*logg_buff); break; } } /* put msg printed out in buffer */ va_copy(tmp_valist, args); ret_val = my_vsnprintf(buffer_start(*logg_buff), buffer_remaining(*logg_buff), fmt, tmp_valist); va_end(tmp_valist); /* error? repeat */ } while(unlikely(ret_val < 0 || (size_t)ret_val > buffer_remaining(*logg_buff))); logg_buff->pos += (size_t)ret_val; /* add errno string if wanted */ if(log_errno) { if(buffer_remaining(*logg_buff) < STRERROR_R_SIZE + 4) { size_t len = logg_buff->capacity * 2; struct big_buff *tmp_buff = realloc(logg_buff, sizeof(*logg_buff) + len); if(!tmp_buff) goto no_errno; logg_buff = tmp_buff; logg_buff->limit = logg_buff->capacity += len; } *buffer_start(*logg_buff) = ':'; logg_buff->pos++; *buffer_start(*logg_buff) = ' '; logg_buff->pos++; { #if defined STRERROR_R_CHAR_P || defined HAVE_MTSAFE_STRERROR || WIN32 || !(defined HAVE_STRERROR_R || HAVE_DECL_STRERROR_R-0 > 0) size_t err_str_len; # ifdef WIN32 const char *s = buffer_start(*logg_buff); if(!(err_str_len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */ 0, /* pointer to other source */ old_errno, /* msg id */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* language */ buffer_start(*logg_buff), /* buffer */ buffer_remaining(*logg_buff)-1, /* size */ 0 /* va_args */ ))) { s = "Unknown system error"; err_str_len = strlen(s) < buffer_remaining(*logg_buff)-2 ? strlen(s) : buffer_remaining(*logg_buff)-2; } # else # ifdef STRERROR_R_CHAR_P /* * the f***ing GNU-Version of strerror_r wich returns * a char * to the buffer.... * This sucks especially in conjunction with strnlen, * wich needs a #define __GNU_SOURCE, but conflicts * with this... */ const char *s = strerror_r(old_errno, buffer_start(*logg_buff), buffer_remaining(*logg_buff)-2); # else /* * Ol Solaris seems to have a static msgtable, so * strerror is threadsafe and we don't have a * _r version */ /* * we also simply fall into here if strerror is not thread * safe, but we have nothing else. * Since what should we do in this case... _sys_errlist * is a bsd extentions. */ const char *s = strerror(old_errno); # endif if(s) err_str_len = strnlen(s, buffer_remaining(*logg_buff)-2); else { s = "Unknown system error"; err_str_len = strlen(s) < (buffer_remaining(*logg_buff)-2) ? strlen(s) : buffer_remaining(*logg_buff)-2; } # endif if(s != buffer_start(*logg_buff)) my_memcpy(buffer_start(*logg_buff), s, err_str_len); logg_buff->pos += err_str_len; #else if(!strerror_r(old_errno, buffer_start(*logg_buff), buffer_remaining(*logg_buff))) // if(!strerror_s(buffer_start(*logg_buff), buffer_remaining(*logg_buff), old_errno)) logg_buff->pos += strnlen(buffer_start(*logg_buff), buffer_remaining(*logg_buff)); else { size_t err_l; const char *bs; if(EINVAL == errno) { err_l = str_size("Unknown errno value!"); bs = "Unknown errno value!"; } else if(ERANGE == errno) { err_l = str_size("errno msg to long for buffer!"); bs = "errno msg to long for buffer!"; } else { err_l = str_size("failure while retrieving errno msg!"); bs = "failure while retrieving errno msg!"; } err_l = (buffer_remaining(*logg_buff)-2) >= err_l ? err_l : (buffer_remaining(*logg_buff)-2); my_memcpy(buffer_start(*logg_buff), bs, err_l); logg_buff->pos += err_l; } #endif } *buffer_start(*logg_buff) = '\n'; logg_buff->pos++; *buffer_start(*logg_buff) = '\0'; } no_errno: /* output that stuff */ buffer_flip(*logg_buff); ret_val = do_logging(level, buffer_start(*logg_buff), buffer_remaining(*logg_buff)); logg_ret_buf(logg_buff); return ret_val; }
static void handle_tcp_reading(netio_type *netio, netio_handler_type *handler, netio_event_types_type event_types) { struct tcp_handler_data *data = (struct tcp_handler_data *) handler->user_data; ssize_t received; if (event_types & NETIO_EVENT_TIMEOUT) { /* Connection timed out. */ cleanup_tcp_handler(netio, handler); return; } if (data->nsd->tcp_query_count > 0 && data->query_count >= data->nsd->tcp_query_count) { /* No more queries allowed on this tcp connection. */ cleanup_tcp_handler(netio, handler); return; } assert(event_types & NETIO_EVENT_READ); if (data->bytes_transmitted == 0) { query_reset(data->query, TCP_MAX_MESSAGE_LEN, 1); } /* * Check if we received the leading packet length bytes yet. */ if (data->bytes_transmitted < sizeof(uint16_t)) { received = read(handler->fd, (char *) &data->query->tcplen + data->bytes_transmitted, sizeof(uint16_t) - data->bytes_transmitted); if (received == -1) { if (errno == EAGAIN || errno == EINTR) { /* * Read would block, wait until more * data is available. */ return; } else { #ifdef ECONNRESET if (verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; } } else if (received == 0) { /* EOF */ cleanup_tcp_handler(netio, handler); return; } data->bytes_transmitted += received; if (data->bytes_transmitted < sizeof(uint16_t)) { /* * Not done with the tcplen yet, wait for more * data to become available. */ return; } assert(data->bytes_transmitted == sizeof(uint16_t)); data->query->tcplen = ntohs(data->query->tcplen); /* * Minimum query size is: * * Size of the header (12) * + Root domain name (1) * + Query class (2) * + Query type (2) */ if (data->query->tcplen < QHEADERSZ + 1 + sizeof(uint16_t) + sizeof(uint16_t)) { VERBOSITY(2, (LOG_WARNING, "packet too small, dropping tcp connection")); cleanup_tcp_handler(netio, handler); return; } if (data->query->tcplen > data->query->maxlen) { VERBOSITY(2, (LOG_WARNING, "insufficient tcp buffer, dropping connection")); cleanup_tcp_handler(netio, handler); return; } buffer_set_limit(data->query->packet, data->query->tcplen); } assert(buffer_remaining(data->query->packet) > 0); /* Read the (remaining) query data. */ received = read(handler->fd, buffer_current(data->query->packet), buffer_remaining(data->query->packet)); if (received == -1) { if (errno == EAGAIN || errno == EINTR) { /* * Read would block, wait until more data is * available. */ return; } else { #ifdef ECONNRESET if (verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; } } else if (received == 0) { /* EOF */ cleanup_tcp_handler(netio, handler); return; } data->bytes_transmitted += received; buffer_skip(data->query->packet, received); if (buffer_remaining(data->query->packet) > 0) { /* * Message not yet complete, wait for more data to * become available. */ return; } assert(buffer_position(data->query->packet) == data->query->tcplen); /* Account... */ #ifdef BIND8_STATS # ifndef INET6 STATUP(data->nsd, ctcp); # else if (data->query->addr.ss_family == AF_INET) { STATUP(data->nsd, ctcp); } else if (data->query->addr.ss_family == AF_INET6) { STATUP(data->nsd, ctcp6); } # endif #endif /* BIND8_STATS */ /* We have a complete query, process it. */ /* tcp-query-count: handle query counter ++ */ data->query_count++; buffer_flip(data->query->packet); data->query_state = server_process_query(data->nsd, data->query); if (data->query_state == QUERY_DISCARDED) { /* Drop the packet and the entire connection... */ STATUP(data->nsd, dropped); #if defined(BIND8_STATS) && defined(USE_ZONE_STATS) if (data->query->zone) { ZTATUP(data->query->zone, dropped); } #endif cleanup_tcp_handler(netio, handler); return; } #ifdef BIND8_STATS if (RCODE(data->query->packet) == RCODE_OK && !AA(data->query->packet)) { STATUP(data->nsd, nona); ZTATUP(data->query->zone, nona); } # ifdef USE_ZONE_STATS # ifndef INET6 ZTATUP(data->query->zone, ctcp); # else if (data->query->addr.ss_family == AF_INET) { ZTATUP(data->query->zone, ctcp); } else if (data->query->addr.ss_family == AF_INET6) { ZTATUP(data->query->zone, ctcp6); } # endif # endif /* USE_ZONE_STATS */ #endif /* BIND8_STATS */ query_add_optional(data->query, data->nsd); /* Switch to the tcp write handler. */ buffer_flip(data->query->packet); data->query->tcplen = buffer_remaining(data->query->packet); data->bytes_transmitted = 0; handler->timeout->tv_sec = data->nsd->tcp_timeout; handler->timeout->tv_nsec = 0L; timespec_add(handler->timeout, netio_current_time(netio)); handler->event_types = NETIO_EVENT_WRITE | NETIO_EVENT_TIMEOUT; handler->event_handler = handle_tcp_writing; }
size_t argos_net_queue_room(const struct argos_net_conn *conn) { return buffer_remaining(conn->pktbuf); }
static int compress_and_xfer(struct argos_net_conn *conn, u_char force) { /* * if the packet-buf is empty, quit right off; this check is not necessary * for correctness, but its nice to check it early for efficiency and also * to avoid some spam in the logs (e.g. lots of 'compression timeout' * messages) since this case is quite common. */ if (buffer_len(conn->pktbuf) == 0) return 0; #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE size_t to_xfer = min(buffer_len(conn->pktbuf), buffer_remaining(conn->outbuf)); if (to_xfer == 0) return 0; #if ARGOS_NET_TRACE_IO struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ memcpy(buffer_tail(conn->outbuf), buffer_head(conn->pktbuf), to_xfer); #if ARGOS_NET_TRACE_IO struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } struct timeval elapsed; orion_time_subtract(&end, &start, &elapsed); float elapsed_msec = elapsed.tv_sec*1000 + (float)elapsed.tv_usec/1000; orion_log_debug("memcpy'ed %u bytes in %.2f ms (%.2f MB/s)", to_xfer, elapsed_msec, ((to_xfer/elapsed_msec)*1000)/(1024*1024)); #endif /* #if ARGOS_NET_TRACE_IO */ if (buffer_expand(conn->outbuf, to_xfer) < 0) KABOOM("buffer_expand"); if (buffer_discard(conn->pktbuf, to_xfer) < 0) KABOOM("buffer_discard"); #else /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE */ /* * In general, we want large blocks of packets to compress (because that * makes the compression more space-efficient). So if there isn't much data * in pktbuf, just return without doing anything. Note that we also need to * check to make sure there is enough room in the outbuf for us to put the * packets once they are compressed. */ size_t minlen; if (force) { minlen = 1; } else { /* * If this conn has a small pktbuf (such that even with totally full, * the COMPRESS_HARD_MIN won't be met), then we need to adjust to a * limit that is actually attainable; we use 75% of the buffer size. */ minlen = (3*buffer_size(conn->pktbuf))/4; /* usually, this is the value that we end up with for minlen: */ minlen = min(minlen, COMPRESS_HARD_MIN); /* * one more special case: if argos_net_shutdown() was called on this * connection then there is no minimum compression size - we just want * to drain the buffers no matter how much is in there */ if (conn->shutdown) minlen = 1; } /* quit if we don't have at least 'minlen' bytes of packet data to compress */ if (buffer_len(conn->pktbuf) < minlen) return 0; /* check the total space available in the connection outbuf */ size_t total_space = buffer_remaining(conn->outbuf); if (total_space < sizeof(struct argos_net_compress_msg)) return 0; /* not enough space available */ /* this is the total space available for the compressed data */ size_t space = total_space - sizeof(struct argos_net_compress_msg); /* don't exceed the maximum compression-block size */ space = min(space, ARGOS_NET_MAX_COMPRESS_LEN); /* * given the space available, calculate how much packet data we can safely * consume (considering worst-cast input:output size ratios for whatever * compression algorithm we are using). */ ssize_t ok_to_consume; #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO /* this is the inversion of the function given in the LZO faq file */ ok_to_consume = (16*(space - 64 - 3))/17; #elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ /* this is the inversion of the function given in the QuickLZ manual */ ok_to_consume = space - 400; #else #error "unknown value for ARGOS_NET_USE_COMPRESSION" #endif if (ok_to_consume <= 0) return 0; /* not enough space available */ /* number of bytes that will actually be compressed and transferred */ size_t readlen = min(ok_to_consume, buffer_len(conn->pktbuf)); assert(readlen > 0); /* don't exceed the maximum compression-block size */ readlen = min(readlen, COMPRESS_HARD_MAX); /* where the compressed data should be written */ u_char *write_ptr = buffer_tail(conn->outbuf) + sizeof(struct argos_net_compress_msg); struct argos_net_compress_msg *msg = (struct argos_net_compress_msg*)buffer_tail(conn->outbuf); msg->msgtype = htons(ARGOS_NET_COMPRESS_MSGTYPE); /* have to defer filling in msglen field */ msg->algorithm = ARGOS_NET_USE_COMPRESSION; msg->orig_len = htonl(readlen); #if ARGOS_NET_TRACE_IO /* measure the elapsed process time to try to detect cpu starvation */ struct itimerval itimer_start; bzero(&itimer_start, sizeof(itimer_start)); itimer_start.it_value.tv_sec = 100; /* arbitrary large value */ struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } if (setitimer(ITIMER_PROF, &itimer_start, NULL) != 0) { orion_log_crit_errno("setitimer"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO lzo_uint lzo_outlen; int rv = lzo1x_1_compress(buffer_head(conn->pktbuf), readlen, write_ptr, &lzo_outlen, conn->lzo_wrk_space); if (rv != LZO_E_OK) { /* according to LZO documentation "this should NEVER happen" */ orion_log_crit("LZO compression library internal error: %d", rv); return 0; } uint32_t outlen = lzo_outlen; #elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ uint32_t outlen = qlz_compress(buffer_head(conn->pktbuf), (char*)write_ptr, readlen, conn->qlz_scratch); #else #error "unknown value for ARGOS_NET_USE_COMPRESSION" #endif #if ARGOS_NET_TRACE_IO /* call this before gettimeofday */ struct itimerval itimer_end; if (getitimer(ITIMER_PROF, &itimer_end) != 0) { orion_log_crit_errno("getitimer"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } #if ARGOS_NET_TRACE_IO struct timeval real_elapsed; orion_time_subtract(&end, &start, &real_elapsed); float real_msec = real_elapsed.tv_sec*1000 + (float)real_elapsed.tv_usec/1000; struct timeval process_elapsed; orion_time_subtract(&itimer_start.it_value, &itimer_end.it_value, &process_elapsed); float process_msec = process_elapsed.tv_sec*1000 + (float)process_elapsed.tv_usec/1000; orion_log_debug("compressed %u bytes to %u (%.2f%%) in %.2f ms" " (%.2f MB/s); %.2f ms process time", readlen, outlen, (float)outlen*100/readlen, real_msec, ((readlen/real_msec)*1000)/(1024*1024), process_msec); #endif /* #if ARGOS_NET_TRACE_IO */ size_t total_len = sizeof(struct argos_net_compress_msg) + outlen; if (buffer_expand(conn->outbuf, total_len) < 0) KABOOM("buffer_expand"); if (buffer_discard(conn->pktbuf, readlen) < 0) KABOOM("buffer_discard"); /* write back into the msglen field now that we know the total length */ msg->msglen = htonl(sizeof(struct argos_net_compress_msg) + outlen); /* check for incompressible block (this is normal for small blocks) */ if (outlen > readlen) { if (readlen < 4096) orion_log_debug("incompressible block: inlen=%d, outlen=%d", readlen, outlen); else orion_log_warn("incompressible block: inlen=%d, outlen=%d", readlen, outlen); } #endif /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE */ /* cancel any currently schedule compression timeout event */ if (conn->compress_evt_reg != NULL) { if (async_cancel(conn->compress_evt_reg) != 0) orion_log_crit_errno("async_cancel"); conn->compress_evt_reg = NULL; } return 1; }
bool do_write(struct epoll_event *p_entry, some_fd epoll_fd) { g2_connection_t *w_entry = (g2_connection_t *)p_entry->data.ptr; bool ret_val = true, more_write; ssize_t result = 0; #ifdef DEBUG_DEVEL if(!w_entry->send) { logg_posd(LOGF_DEBUG, "%s Ip: %p#I\tFDNum: %i\n", "no send buffer!", &w_entry->remote_host, w_entry->com_socket); w_entry->flags.dismissed = true; ret_val = false; } #endif buffer_flip(*w_entry->send); do { set_s_errno(0); result = my_epoll_send(epoll_fd, w_entry->com_socket, w_entry->send->data, w_entry->send->limit, 0); } while(-1 == result && EINTR == s_errno); switch(result) { default: w_entry->send->pos += result; w_entry->flags.has_written = true; w_entry->last_send = local_time_now; if(buffer_remaining(*w_entry->send)) break; shortlock_lock(&w_entry->pts_lock); if(list_empty(&w_entry->packets_to_send)) { w_entry->poll_interrests &= ~((uint32_t)EPOLLOUT); more_write = true; } else more_write = false; if(more_write) { if(!(w_entry->poll_interrests & EPOLLONESHOT)) { struct epoll_event t_entry; t_entry.data.u64 = p_entry->data.u64; t_entry.events = w_entry->poll_interrests; shortlock_unlock(&w_entry->pts_lock); if(0 > my_epoll_ctl(epoll_fd, EPOLL_CTL_MOD, w_entry->com_socket, &t_entry)) { logg_serrno(LOGF_DEBUG, "changing sockets Epoll-interrests"); w_entry->flags.dismissed = true; ret_val = false; } } else { shortlock_unlock(&w_entry->pts_lock); if(w_entry->flags.dismissed) { logg_posd(LOGF_DEVEL_OLD, "%s\tIP: %p#I\tFDNum: %i\n", "Dismissed!", &w_entry->remote_host, w_entry->com_socket); ret_val = false; } } } else shortlock_unlock(&w_entry->pts_lock); break; case 0: if(buffer_remaining(*w_entry->send)) { if(EAGAIN != s_errno) { logg_posd(LOGF_DEVEL_OLD, "%s Ip: %p#I\tFDNum: %i\n", "Dismissed!", &w_entry->remote_host, w_entry->com_socket); w_entry->flags.dismissed = true; ret_val = false; } else logg_devel("not ready to write\n"); } else { shortlock_lock(&w_entry->pts_lock); w_entry->poll_interrests &= ~((uint32_t)EPOLLOUT); if(!(w_entry->poll_interrests & EPOLLONESHOT)) { struct epoll_event t_entry; t_entry.data.u64 = p_entry->data.u64; t_entry.events = w_entry->poll_interrests; shortlock_unlock(&w_entry->pts_lock); if(0 > my_epoll_ctl(epoll_fd, EPOLL_CTL_MOD, w_entry->com_socket, &t_entry)) { logg_serrno(LOGF_DEBUG, "changing sockets Epoll-interrests"); w_entry->flags.dismissed = true; ret_val = false; } } else { shortlock_unlock(&w_entry->pts_lock); if(w_entry->flags.dismissed) { logg_posd(LOGF_DEVEL_OLD, "%s ERRNO=%i Ip: %p#I\tFDNum: %i\n", "EOF reached!", s_errno, &w_entry->remote_host, w_entry->com_socket); ret_val = false; } } } break; case -1: if(!(EAGAIN == errno || EWOULDBLOCK == s_errno)) { logg_serrno(LOGF_DEBUG, "write"); ret_val = false; } break; } logg_develd_old("ret_val: %i\tpos: %u\tlim: %u\n", ret_val, w_entry->send->pos, w_entry->send->limit); buffer_compact(*w_entry->send); logg_develd_old("ret_val: %i\tpos: %u\tlim: %u\n", ret_val, w_entry->send->pos, w_entry->send->limit); return ret_val; }
/** * Process query. * */ query_state query_process(query_type* q, void* engine) { ldns_status status = LDNS_STATUS_OK; ldns_pkt* pkt = NULL; ldns_rr* rr = NULL; ldns_pkt_rcode rcode = LDNS_RCODE_NOERROR; ldns_pkt_opcode opcode = LDNS_PACKET_QUERY; ldns_rr_type qtype = LDNS_RR_TYPE_SOA; engine_type* e = (engine_type*) engine; ods_log_assert(e); ods_log_assert(q); ods_log_assert(q->buffer); if (!e || !q || !q->buffer) { ods_log_error("[%s] drop query: assertion error", query_str); return QUERY_DISCARDED; /* should not happen */ } if (buffer_limit(q->buffer) < BUFFER_PKT_HEADER_SIZE) { ods_log_debug("[%s] drop query: packet too small", query_str); return QUERY_DISCARDED; /* too small */ } if (buffer_pkt_qr(q->buffer)) { ods_log_debug("[%s] drop query: qr bit set", query_str); return QUERY_DISCARDED; /* not a query */ } /* parse packet */ status = ldns_wire2pkt(&pkt, buffer_current(q->buffer), buffer_remaining(q->buffer)); if (status != LDNS_STATUS_OK) { ods_log_debug("[%s] got bad packet: %s", query_str, ldns_get_errorstr_by_id(status)); return query_formerr(q); } rr = ldns_rr_list_rr(ldns_pkt_question(pkt), 0); lock_basic_lock(&e->zonelist->zl_lock); /* we can just lookup the zone, because we will only handle SOA queries, zone transfers, updates and notifies */ q->zone = zonelist_lookup_zone_by_dname(e->zonelist, ldns_rr_owner(rr), ldns_rr_get_class(rr)); /* don't answer for zones that are just added */ if (q->zone && q->zone->zl_status == ZONE_ZL_ADDED) { ods_log_warning("[%s] zone %s just added, don't answer for now", query_str, q->zone->name); q->zone = NULL; } lock_basic_unlock(&e->zonelist->zl_lock); if (!q->zone) { ods_log_debug("[%s] zone not found", query_str); return query_servfail(q); } /* see if it is tsig signed */ if (!query_find_tsig(q)) { return query_formerr(q); } /* else: valid tsig, or no tsig present */ ods_log_debug("[%s] tsig %s", query_str, tsig_status2str(q->tsig_rr->status)); rcode = query_process_tsig(q); if (rcode != LDNS_RCODE_NOERROR) { return query_error(q, rcode); } /* process edns */ rcode = query_process_edns(q); if (rcode != LDNS_RCODE_NOERROR) { /* We should not return FORMERR, but BADVERS (=16). * BADVERS is created with Ext. RCODE, followed by RCODE. * Ext. RCODE is set to 1, RCODE must be 0 (getting 0x10 = 16). * Thus RCODE = NOERROR = NSD_RC_OK. */ return query_error(q, LDNS_RCODE_NOERROR); } /* handle incoming request */ opcode = ldns_pkt_get_opcode(pkt); qtype = ldns_rr_get_type(rr); ldns_pkt_free(pkt); switch (opcode) { case LDNS_PACKET_NOTIFY: return query_process_notify(q, qtype, engine); case LDNS_PACKET_QUERY: return query_process_query(q, qtype, engine); case LDNS_PACKET_UPDATE: return query_process_update(q); default: break; } return query_notimpl(q); }
/** * NOTIFY. * */ static query_state query_process_notify(query_type* q, ldns_rr_type qtype, void* engine) { engine_type* e = (engine_type*) engine; dnsin_type* dnsin = NULL; uint16_t count = 0; uint16_t rrcount = 0; uint32_t serial = 0; size_t pos = 0; char address[128]; if (!e || !q || !q->zone) { return QUERY_DISCARDED; } ods_log_assert(e->dnshandler); ods_log_assert(q->zone->name); ods_log_debug("[%s] incoming notify for zone %s", query_str, q->zone->name); if (buffer_pkt_rcode(q->buffer) != LDNS_RCODE_NOERROR || buffer_pkt_qr(q->buffer) || !buffer_pkt_aa(q->buffer) || buffer_pkt_tc(q->buffer) || buffer_pkt_rd(q->buffer) || buffer_pkt_ra(q->buffer) || buffer_pkt_ad(q->buffer) || buffer_pkt_cd(q->buffer) || buffer_pkt_qdcount(q->buffer) != 1 || buffer_pkt_ancount(q->buffer) > 1 || qtype != LDNS_RR_TYPE_SOA) { return query_formerr(q); } if (!q->zone->adinbound || q->zone->adinbound->type != ADAPTER_DNS) { ods_log_error("[%s] zone %s is not configured to have input dns " "adapter", query_str, q->zone->name); return query_notauth(q); } ods_log_assert(q->zone->adinbound->config); dnsin = (dnsin_type*) q->zone->adinbound->config; if (!acl_find(dnsin->allow_notify, &q->addr, q->tsig_rr)) { if (addr2ip(q->addr, address, sizeof(address))) { ods_log_info("[%s] unauthorized notify for zone %s from client %s: " "no acl matches", query_str, q->zone->name, address); } else { ods_log_info("[%s] unauthorized notify for zone %s from unknown " "client: no acl matches", query_str, q->zone->name); } return query_notauth(q); } ods_log_assert(q->zone->xfrd); /* skip header and question section */ buffer_skip(q->buffer, BUFFER_PKT_HEADER_SIZE); count = buffer_pkt_qdcount(q->buffer); for (rrcount = 0; rrcount < count; rrcount++) { if (!buffer_skip_rr(q->buffer, 1)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad question section)", query_str, q->zone->name); return QUERY_DISCARDED; } } pos = buffer_position(q->buffer); /* examine answer section */ count = buffer_pkt_ancount(q->buffer); if (count) { if (!buffer_skip_dname(q->buffer) || !query_parse_soa(q->buffer, &serial)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad soa in answer section)", query_str, q->zone->name); return QUERY_DISCARDED; } lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = serial; q->zone->xfrd->serial_notify_acquired = time_now(); if (!util_serial_gt(q->zone->xfrd->serial_notify, q->zone->xfrd->serial_disk)) { ods_log_debug("[%s] ignore notify: already got zone %s serial " "%u on disk", query_str, q->zone->name, q->zone->xfrd->serial_notify); lock_basic_unlock(&q->zone->xfrd->serial_lock); goto send_notify_ok; } lock_basic_unlock(&q->zone->xfrd->serial_lock); } else { lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = 0; q->zone->xfrd->serial_notify_acquired = 0; lock_basic_unlock(&q->zone->xfrd->serial_lock); } /* forward notify to xfrd */ xfrd_set_timer_now(q->zone->xfrd); dnshandler_fwd_notify(e->dnshandler, buffer_begin(q->buffer), buffer_remaining(q->buffer)); send_notify_ok: /* send notify ok */ buffer_pkt_set_qr(q->buffer); buffer_pkt_set_aa(q->buffer); buffer_pkt_set_ancount(q->buffer, 0); buffer_clear(q->buffer); /* lim = pos, pos = 0; */ buffer_set_position(q->buffer, pos); buffer_set_limit(q->buffer, buffer_capacity(q->buffer)); q->reserved_space = edns_rr_reserved_space(q->edns_rr); q->reserved_space += tsig_rr_reserved_space(q->tsig_rr); return QUERY_PROCESSED; }