static void* scheduler_queue_dnskey_create_thread(void* data_) { scheduler_dnskey_create *data = (scheduler_dnskey_create*)data_; ya_result return_value; log_info("dnssec: key create (%{dnsname} %hd %hhd %hd)", data->zone->origin, data->flags, data->algorithm, data->size); char origin[MAX_DOMAIN_LENGTH]; dnsname_to_cstr(origin, data->zone->origin); dnssec_key* key; if(ISOK(return_value = dnssec_key_createnew(DNSKEY_ALGORITHM_RSASHA1_NSEC3, data->size, data->flags, origin, &key))) { if(ISOK(return_value = dnssec_key_store_private(key))) { if(ISOK(return_value = dnssec_key_store_dnskey(key))) { log_info("dnssec: key created (%{dnsname} %hd %hhd %hd)", data->zone->origin, data->flags, data->algorithm, data->size); } else { log_err("dnssec: key store public (%{dnsname} %hd %hhd %hd): %r", data->zone->origin, data->flags, data->algorithm, data->size, return_value); } } else { log_err("dnssec: key store private (%{dnsname} %hd %hhd %hd): %r", data->zone->origin, data->flags, data->algorithm, data->size, return_value); } } else { log_err("dnssec: key create failed (%{dnsname} %hd %hhd %hd): %r", data->zone->origin, data->flags, data->algorithm, data->size, return_value); } if(ISOK(return_value)) { data->key = key; } else { dnssec_key_free(key); data->key = NULL; } scheduler_schedule_task(scheduler_queue_dnskey_create_callback, data); /* WARNING: From this point forward, 'data' cannot be used anymore */ /* * The key is still in the keystore. * */ return NULL; }
static asynStatus executeCommand(Port* pport,asynUser* pasynUser) { int i; asynStatus sts; asynPrint(pasynUser,ASYN_TRACE_FLOW,"drvLove::executeCommand\n"); pasynUser->timeout = K_COMTMO; for( i = 0; i < 3; ++i ) { epicsThreadSleep( K_TUNE ); sts = sendCommand(pport,pasynUser,i); if( ISOK(sts) ) asynPrint(pasynUser,ASYN_TRACEIO_FILTER,"drvLove::executeCommand write \"%s\"\n",pport->outMsg); else { if( sts == asynTimeout ) { asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::executeCommand write timeout, retrying\n"); continue; } asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::executeCommand write failure - Sent \"%s\" \n",pport->outMsg); return( sts ); } sts = recvReply(pport,pasynUser,pport->inpMsg,sizeof(pport->inpMsg)); if( ISOK(sts) ) asynPrint(pasynUser,ASYN_TRACEIO_FILTER,"drvLove::executeCommand read \"%s\"\n",pport->inpMsg); else { if( sts == asynTimeout ) { asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::executeCommand read timeout, retrying\n"); continue; } asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::executeCommand read failure - Sent \"%s\" Rcvd \"%s\" \n",pport->outMsg,pport->inpMsg); return( sts ); } return( asynSuccess ); } asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::executeCommand retries exceeded\n"); return( asynError ); }
ya_result nsec3_load_add_rrsig(nsec3_load_context *context, const u8 *digest_label, s32 ttl, const u8 *rdata, u16 rdata_size) { u8 digest_len = BASE32HEX_DECODED_LEN(digest_label[0]); u8 digest[digest_len + 1]; digest[0] = digest_len; if(ISOK(base32hex_decode((const char*)&digest_label[1], digest_label[0], &digest[1]))) { zdb_packed_ttlrdata *rrsig; ZDB_RECORD_ZALLOC(rrsig,ttl,rdata_size,rdata); if(context->last_inserted_nsec3 != NULL) { nsec3_context_record* nsec3_r = (nsec3_context_record*)context->last_inserted_nsec3; if(memcmp(nsec3_r->digest_then_rdata, digest, 1 + digest[0]) == 0) { rrsig->next = nsec3_r->rrsig; nsec3_r->rrsig = rrsig; return SUCCESS; } } // the records are not nicely ordered : need to postpone the insertion of this record. ptr_node *node = ptr_set_avl_insert(&context->postponed_rrsig, digest); if(node->value == NULL) { u8 *key; ZALLOC_ARRAY_OR_DIE(u8*, key, digest_len + 1, N3LKEYDG_TAG); memcpy(key, digest, digest_len + 1); node->key = key; }
static nsec3_context_record* nsec3_load_context_record_new(const u8 *digest, s32 ttl, const u8 *rdata, u16 rdata_size) { u8 digest_len = BASE32HEX_DECODED_LEN(digest[0]); nsec3_context_record* record; ZALLOC_ARRAY_OR_DIE(nsec3_context_record*, record, sizeof(nsec3_context_record) + 1 + digest_len + rdata_size, N3LCTXRR_TAG); record->rrsig = NULL; record->ttl = ttl; record->rdata_size = rdata_size; //memcpy(&record->digest_then_rdata[0], digest, digest_len); record->digest_then_rdata[0] = digest_len; if(ISOK(base32hex_decode((const char*)&digest[1], digest[0], &record->digest_then_rdata[1]))) { memcpy(&record->digest_then_rdata[digest_len + 1], rdata, rdata_size); return record; } else { nsec3_load_context_record_delete(record); return NULL; } }
nsec3_zone_item* nsec3_get_nsec3_by_name(zdb_zone* zone, const u8 *nsec3_label, const u8* nsec3_rdata) { nsec3_zone* n3 = zone->nsec.nsec3; while(n3 != NULL) { if(nsec3_zone_rdata_compare(n3->rdata, nsec3_rdata) == 0) { u8 digest[256]; #ifndef NDEBUG memset(digest, 0xd1, sizeof(digest)); #endif ya_result digest_len = base32hex_decode((char*)&nsec3_label[1], nsec3_label[0], &digest[1]); if(ISOK(digest_len)) { digest[0] = digest_len; nsec3_zone_item* item = nsec3_avl_find(&n3->items, digest); return item; } break; } n3 = n3->next; } return NULL; }
ya_result logger_channel_file_open(const char *fullpath, uid_t uid, gid_t gid, u16 mode, bool forceflush,logger_channel* chan) { ya_result return_code; file_data* sd; MALLOC_OR_DIE(file_data*, sd, sizeof (file_data), 0x4d5254534e414843); /* CHANSTRM */ if(ISOK(return_code = logger_channel_file_append(fullpath, uid, gid, mode, sd))) { sd->file_name = strdup(fullpath); sd->uid = uid; sd->gid = gid; sd->mode = mode; sd->force_flush = forceflush; chan->data = sd; chan->vtbl = &stream_vtbl; } else { free(sd); } return return_code; }
void nsec3_remove_rrsig(zdb_zone* zone, zdb_packed_ttlrdata* nsec3param) { nsec3_zone* n3 = zone->nsec.nsec3; while(n3 != NULL) { if(nsec3_zone_rdata_compare(n3->rdata, nsec3param->rdata_start) == 0) { u8 digest[256]; ya_result digest_len = base32hex_decode((char*)&nsec3param->rdata_start[1], (u32)nsec3param->rdata_start[0], digest); if(ISOK(digest_len)) { digest[0] = digest_len; nsec3_zone_item* item = nsec3_avl_find(&n3->items, digest); if(item != NULL) { nsec3_zone_item_rrsig_delete_all(item); } } break; } n3 = n3->next; } }
static void* xfr_query_axfr_load_thread(void *data) { axfr_query_axfr_load_param *aqalp = (axfr_query_axfr_load_param*) data; ya_result return_value; /* * Now that the access has been cut, this could be done in background, with the loading */ /* * Load the zone again * (other thread, with no zone visible until the very end) */ zdb_zone *newzone = NULL; log_info("slave: zone %{dnsname} transferred", aqalp->origin); /** * Behaviour change : loading an zone file (axfr file) into memory will now drop (invalidate) the zone before * loading it. Else loading a zone like .com would lead to potentially twice the amount of memory made readily * available to yadifa when most of it will remain untouched. * * so: * * invalidate the zone * drop the zone * load the zone */ if(ISOK(return_value = zdb_zone_load((zdb*)aqalp->db, &aqalp->zr, &newzone, g_config->xfr_path, aqalp->origin, ZDB_ZONE_DESTROY_JOURNAL|ZDB_ZONE_IS_SLAVE))) { zassert(newzone != NULL); log_info("slave: zone %{dnsname} loaded", aqalp->origin); aqalp->new_zone = newzone; } else { log_err("slave: zone %{dnsname} failed to load: %r", aqalp->origin, return_value); if(return_value == DNSSEC_ERROR_NSEC3_INVALIDZONESTATE) { /** @todo don't try for a while */ } } zone_reader_close(&aqalp->zr); /* * At this point, since we are a slave, we need to start the alarm again. */ scheduler_schedule_task(xfr_query_final_callback, aqalp); return NULL; }
static void xfr_query_mark_zone_loaded(zdb* db, const u8 *origin, u32 refreshed_time, u32 retried_time) { zone_data *zone = zone_getbydnsname(origin); bool refresh = TRUE; if(zone != NULL) { zone_setloading(zone, FALSE); if(refreshed_time != 0) { zone->refresh.refreshed_time = refreshed_time; } if(retried_time != 0) { zone->refresh.retried_time = retried_time; } zdb_zone *dbzone = zdb_zone_find_from_dnsname(db, origin, CLASS_IN); if(dbzone != NULL) { soa_rdata soa; zdb_zone_lock(dbzone, ZDB_ZONE_MUTEX_XFR); if(ISOK(zdb_zone_getsoa(dbzone, &soa))) { u32 now = time(NULL); if(zone->refresh.refreshed_time >= now + soa.expire) { log_info("slave: zone %{dnsname} has expired", origin); dbzone->apex->flags |= ZDB_RR_LABEL_INVALID_ZONE; refresh = FALSE; } } zdb_zone_unlock(dbzone, ZDB_ZONE_MUTEX_XFR); } log_info("slave: zone %{dnsname} load operation done", origin); } else { log_err("slave: expected to find zone '%{dnsname}' in the database", origin); } if(refresh) { database_zone_refresh_maintenance(g_config->database, origin); } }
static ya_result counter_write(output_stream* stream, const u8* counter_, u32 len) { counter_output_stream_data* data = (counter_output_stream_data*)stream->data; data->write_count += len; if(ISOK(data->result)) { data->result = output_stream_write(data->filtered, counter_, len); if(ISOK(data->result)) { data->writed_count += data->result; } } return data->result; }
static ya_result counter_flush(output_stream* stream) { counter_output_stream_data* data = (counter_output_stream_data*)stream->data; if(ISOK(data->result)) { data->result = output_stream_flush(data->filtered); } return data->result; }
static asynStatus setDefaultEos(Port* plov) { asynStatus sts; char inpEos = '\006'; char outEos = '\003'; Serport* pser = plov->pserport; sts = pser->pasynOctet->setInputEos(pser->pasynOctetPvt,pser->pasynUser,&inpEos,1); if( ISOK(sts) ) printf("drvLove::setDefaultEos Input EOS set to \\0%d\n",inpEos); else printf("drvLove::setDefaultEos Input EOS set failed to \\0%d\n",inpEos); sts = pser->pasynOctet->setOutputEos(pser->pasynOctetPvt,pser->pasynUser,&outEos,1); if( ISOK(sts) ) printf("drvLove::setDefaultEos Output EOS set to \\0%d\n",outEos); else printf("drvLove::setDefaultEos Output EOS set failed to \\0%d\n",outEos); return( sts ); }
ya_result tcp_io_stream_connect_ex(const char *server, u16 port, io_stream *ios, struct sockaddr *bind_from) { input_stream istream; output_stream ostream; ya_result return_code; if(ISOK(return_code = tcp_input_output_stream_connect_ex(server, port, &istream, &ostream, bind_from, 0))) { io_stream_link(ios, &istream, &ostream); } return return_code; }
ya_result tcp_input_output_stream_connect_host_address(const host_address *ha, input_stream *istream_, output_stream *ostream_, u8 to_sec) { socketaddress sa; ya_result return_code; if(ISOK(return_code = host_address2sockaddr(&sa, ha))) { return_code = tcp_input_output_stream_connect_sockaddr(&sa.sa, istream_, ostream_, NULL, to_sec); } return return_code; }
void nsec3_add_nsec3_by_name(zdb_zone* zone, const u8 *nsec3_label, const u8* nsec3_rdata, u16 nsec3_rdata_size) { nsec3_zone* n3 = zone->nsec.nsec3; while(n3 != NULL) { if(nsec3_zone_rdata_compare(n3->rdata, nsec3_rdata) == 0) { u8 digest[256]; #ifndef NDEBUG memset(digest, 0xd1, sizeof(digest)); #endif ya_result digest_len = base32hex_decode((char*)&nsec3_label[1], nsec3_label[0], &digest[1]); if(ISOK(digest_len)) { digest[0] = digest_len; nsec3_zone_item* item = nsec3_avl_find(&n3->items, digest); if(item == NULL) { nsec3_zone_item *self = nsec3_avl_insert(&n3->items, (u8*)digest); self->flags = nsec3_rdata[1]; /* self->rc = 0; self->sc = 0; self->type_bit_maps = NULL; self->type_bit_maps_size = 0; */ nsec3_zone_item_update_bitmap(self, nsec3_rdata, nsec3_rdata_size); } else { /* exists already */ assert(FALSE); } } break; } n3 = n3->next; } }
static int tcl_cpucount(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { ya_result count = sys_get_cpu_count(); if(ISOK(count)) { fprintf(stdout, "Detected cpu count : %i\n", count); } else { fprintf(stdout, "Assumed cpu count : %i\n", DEFAULT_ASSUMED_CPU_COUNT); } fflush(stdout); return TCL_OK; }
ya_result tcp_input_output_stream_connect_ex(const char *server, u16 port, input_stream *istream_, output_stream *ostream_, struct sockaddr *bind_from, u8 to_sec) { ya_result return_code; socketaddress sa; /* * If the client interface is specified, then use its family. * Else use the unspecified familly to let the algorithm choose the first available one. */ int family = (bind_from != NULL) ? bind_from->sa_family : AF_UNSPEC; if(ISOK(return_code = gethostaddr(server, port, &sa.sa, family))) { return_code = tcp_input_output_stream_connect_sockaddr(&sa.sa, istream_, ostream_, bind_from, to_sec); } return return_code; }
static asynStatus sendCommand(void* ppvt,asynUser* pasynUser,int retry) { unsigned char cs; asynStatus sts; int addr; size_t len,bytesXfer; Port* plov = (Port*)ppvt; Serport* pser = plov->pserport; asynPrint(pasynUser,ASYN_TRACE_FLOW,"drvLove::sendCommand - retries(%d)\n",retry); if( retry == 0 ) { sts = pasynManager->getAddr(pasynUser,&addr); if( ISNOTOK(sts) ) return( sts ); sprintf(plov->tmpMsg,"%02X%s",addr,plov->outMsg); calcChecksum(strlen(plov->tmpMsg),plov->tmpMsg,&cs); sprintf(plov->outMsg,"\002L%s%2X",plov->tmpMsg,cs); } len = strlen(plov->outMsg); sts = pser->pasynOctet->write(pser->pasynOctetPvt,pser->pasynUser,plov->outMsg,len,&bytesXfer); if( ISOK(sts) ) asynPrint(pasynUser,ASYN_TRACEIO_FILTER,"drvLove::sendCommand - retries(%d),data \"%s\" \"%s\"\n",retry,plov->outMsg); else { if( sts == asynTimeout ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::sendCommand - retries(%d) asynTimeout\n",retry); else if( sts == asynOverflow ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::sendCommand - retries(%d) asynOverflow\n",retry); else if( sts == asynError ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::sendCommand - retries(%d) asynError\n",retry); else asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::sendCommand - retries(%d) failed - unknown Asyn error\n",retry); } return( sts ); }
/// register the logging configuration /// note that for this to work, logger_handle_create("handle-name",logger_handle_for_handle_name_ptr_ptr) /// must be called before the config_read is done ya_result config_register_logger(const char *null_or_channels_name, const char *null_or_loggers_name, s32 priority) { //null_or_channels_name = "channels"; //null_or_loggers_name = "loggers"; (void)null_or_channels_name; (void)null_or_loggers_name; if(priority < 0) { priority = 0; } ya_result return_code; if(ISOK(return_code = config_register(&config_section_handles_descriptor, priority + 0))) { return_code = config_register(&config_section_loggers_descriptor, priority + 1); } return return_code; }
static asynStatus readUInt32(void* ppvt,asynUser* pasynUser,epicsUInt32* value,epicsUInt32 mask) { asynStatus sts; Port* pport = (Port*)ppvt; Inst* pinst = (Inst*)pasynUser->drvUser; asynPrint(pasynUser,ASYN_TRACE_FLOW,"drvLove::readUInt32\n"); if( pinst->pcmd->read ) sprintf(pport->outMsg,"%s",pinst->pcmd->read); else { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,"%s error %s",pport->name,pport->pasynUser->errorMessage); return( asynError ); } lockPort(pport,pasynUser); sts = executeCommand(pport,pasynUser); unlockPort(pport,pasynUser); if( ISNOTOK(sts) ) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,"%s error %s",pport->name,pport->pasynUser->errorMessage); return( sts ); } sts = pinst->read(pinst,(epicsInt32*)value); if( ISNOTOK(sts) ) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,"%s error %s",pport->name,pport->pasynUser->errorMessage); return( sts ); } if( ISOK(sts) ) asynPrint(pasynUser,ASYN_TRACEIO_FILTER,"drvLove::readUInt32 readback from %s is 0x%X,mask=0x%X\n",pport->name,*value,mask); return( asynSuccess ); }
static ya_result journal_ix_reopen(journal *jh) { journal_ix *jix = (journal_ix*)jh; ya_result ret = SUCCESS; journal_ix_writelock(jix); if(jix->fd < 0) { if(ISOK(ret = open_ex(jix->journal_name, O_RDWR))) { jix->fd = ret; } else { ret = ERRNO_ERROR; if(ret == MAKE_ERRNO_ERROR(ENOENT)) { ret = ZDB_ERROR_ICMTL_NOTFOUND; } } } journal_ix_writeunlock(jix); return ret; }
static asynStatus recvReply(void* ppvt,asynUser* pasynUser,char* data,size_t maxchars) { int eom; asynStatus sts; size_t bytesXfer; Port* plov = (Port*)ppvt; Serport* pser = plov->pserport; asynPrint(pasynUser,ASYN_TRACE_FLOW,"drvLove::recvReplay\n"); sts = pser->pasynOctet->read(pser->pasynOctetPvt,pser->pasynUser,data,maxchars,&bytesXfer,&eom); if( ISOK(sts) ) { if( eom != ASYN_EOM_EOS ) { asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::recvReply eos failure\n"); return( asynError ); } sts = evalMessage(&bytesXfer,plov->inpMsg,pasynUser,data); asynPrint(pasynUser,ASYN_TRACEIO_FILTER,"drvLove::recvReply %d \"%s\"\n",bytesXfer,data); } else { if( sts == asynTimeout ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::recvReply asynTimeout\n"); else if( sts == asynOverflow ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::recvReply asynOverflow\n"); else if( sts == asynError ) asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::recvReply asynError\n"); else asynPrint(pasynUser,ASYN_TRACE_ERROR,"drvLove::recvReply failed - unknown Asyn error\n"); } return( sts ); }
ya_result config_register_main(s32 priority) { MALLOC_OR_DIE(config_data*, g_config, sizeof(config_data), YGCONFIG_TAG); ZEROMEMORY(g_config, sizeof(config_data)); g_config->gid = getgid(); g_config->uid = getuid(); const char *section_name = "main"; ya_result return_code = config_register_struct(section_name, config_main_desc, g_config, priority); if(ISOK(return_code)) { // hook a new finaliser before the standard one config_section_descriptor_s *section_desc = config_section_get_descriptor(section_name); config_section_descriptor_vtbl_s *vtbl = (config_section_descriptor_vtbl_s*)section_desc->vtbl; vtbl->postprocess = config_main_section_postprocess; } return return_code; }
static ya_result config_section_zone_stop(struct config_section_descriptor_s *csd) { #if CONFIG_SETTINGS_DEBUG formatln("config: section: zone: stop"); #endif // NOP zone_desc_s *zone_desc = (zone_desc_s*)csd->base; ya_result return_code; // ensure the descriptor is valid if(ISOK(return_code = zone_complete_settings(zone_desc))) { zone_setdefaults(zone_desc); if(logger_is_running()) { log_debug("config: %{dnsname}: zone section parsed", zone_desc->origin); } // load the descriptor (most likely offline) if(config_section_zone_filter(zone_desc, config_section_zone_filter_params) == 1) { if(logger_is_running()) { log_debug("config: %{dnsname}: sending zone to service", zone_desc->origin); } database_zone_desc_load(zone_desc); } else { zone_desc_s *current_zone_desc = zone_acquirebydnsname(zone_desc->origin); if(current_zone_desc != NULL) { if(logger_is_running()) { log_debug("config: %{dnsname}: clearing original zone drop status", zone_desc->origin); } zone_lock(current_zone_desc, ZONE_LOCK_REPLACE_DESC); zone_clear_status(current_zone_desc, ZONE_STATUS_DROP_AFTER_RELOAD); zone_unlock(current_zone_desc, ZONE_LOCK_REPLACE_DESC); zone_release(current_zone_desc); } zone_release(zone_desc); } } else { zone_release(zone_desc); } csd->base = NULL; return return_code; }
static ya_result buffer_input_stream_read(input_stream* stream, u8* buffer, u32 len) { buffer_input_stream_data* data = (buffer_input_stream_data*)stream->data; u8* src = data->buffer; ya_result ret; u32 remaining = data->buffer_size - data->buffer_offset; if(len <= remaining) { MEMCOPY(buffer, &src[data->buffer_offset], len); data->buffer_offset += len; /* There are still some bytes available */ return len; } /* len >= remaining : copy what remains in the buffer */ MEMCOPY(buffer, &src[data->buffer_offset], remaining); len -= remaining; buffer += remaining; /* NOTE: at this point the internal buffer is empty */ if(len >= data->buffer_maxsize) { /* It would be pointless to buffer a read bigger than the buffer */ data->buffer_offset = data->buffer_size; /* mark the buffer as "empty" */ if(ISOK(ret = input_stream_read(&data->filtered, buffer, len))) { return remaining + ret; /* the chunk we've read from the buffer + the chunk we've read from the stream */ } else // 'remaining' bytes may have been copied already, if so, return that before the error { return (remaining > 0)?remaining:ret; } } #ifdef DEBUG memset(data->buffer, 0xee, data->buffer_maxsize); #endif // What remains to read is smaller than the buffer max size: // read a full buffer if((ret = input_stream_read(&data->filtered, data->buffer, data->buffer_maxsize)) <= 0) { data->buffer_size = 0; data->buffer_offset = 0; // 'remaining' bytes may have been copied already, if so, return that before the error return (remaining > 0) ? remaining : ERROR /* eof */; } if(len > ret) { len = ret; } MEMCOPY(buffer, data->buffer, len); /* starts at offset 0 */ data->buffer_size = ret; data->buffer_offset = len; return remaining + len; }
void nsec3_remove_nsec3_by_name(zdb_zone* zone, const u8 *nsec3_label, const u8* nsec3_rdata) { nsec3_zone* n3 = zone->nsec.nsec3; while(n3 != NULL) { if(nsec3_zone_rdata_compare(n3->rdata, nsec3_rdata) == 0) { u8 digest[256]; #ifndef NDEBUG memset(digest, 0xd1, sizeof(digest)); #endif ya_result digest_len = base32hex_decode((char*)&nsec3_label[1], nsec3_label[0], &digest[1]); if(ISOK(digest_len)) { digest[0] = digest_len; nsec3_zone_item* item = nsec3_avl_find(&n3->items, digest); if(item != NULL) { int depth; log_debug("nsec3_remove_nsec3_by_name: destroying %{digest32h}", item->digest); /* GOT IT : AN NSEC3 RECORD IS REMOVED BY IXFR BUT THE LABEL HAS NOT BEEN CHANGED I PRESUME IT IS BECAUSE THE AXFR CHAIN IS CHANGED I NEED A REPLACE FUNCTION, I NEED TO SORT THE IXFR NSEC(3) OPERATIONS */ if(item->sc > 0) { nsec3_zone_item* prev = nsec3_avl_node_mod_prev(item); if(prev != NULL) { nsec3_move_all_star(item, prev); } else { nsec3_remove_all_star(item); } } nsec3_remove_all_owners(item); zassert(item->rc == 0 && item->sc == 0); ZFREE_ARRAY(item->type_bit_maps, item->type_bit_maps_size); item->type_bit_maps = NULL; item->type_bit_maps_size = 0; nsec3_zone_item_rrsig_delete_all(item); nsec3_avl_delete(&n3->items, item->digest); } } break; } n3 = n3->next; } }
static ya_result journal_ix_get_ixfr_stream_at_serial(journal *jh, u32 serial_from, input_stream *out_input_stream, dns_resource_record *last_soa_rr) { journal_ix *jix = (journal_ix*)jh; ya_result return_value = SUCCESS; journal_ix_readlock(jix); /* * check that serial_from in in the journal range * set the file descriptor to the position * create a stream that'll stop at the current end of the stream */ if(serial_lt(serial_from, jix->first_serial) || serial_ge(serial_from, jix->last_serial) || ((jix->first_serial == 0) && (jix->last_serial == 0))) { /* out of known range */ journal_ix_readunlock(jix); if(serial_from == jix->last_serial) { return SUCCESS; } else { return ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } } /* * On success, dup() returns a new file descriptor that has the following in common with the original: * * _ Same open file (or pipe) * _ Same file pointer (both file descriptors share one file pointer) <= THIS is a problem * _ Same access mode (read, write, or read/write) * * So this is wrong: * * cloned_fd = dup(jix->fd); */ int cloned_fd; while((cloned_fd = open_ex(jix->journal_name, O_RDONLY)) < 0) { int err = errno; if(err == EINTR) { continue; } return_value = MAKE_ERRNO_ERROR(err); #ifdef DEBUG log_debug("journal: ix: unable to clone the file descriptor: %r", return_value); #endif journal_ix_readunlock(jix); return return_value; } /* * given that I use a clone of the fd and * given that only appends are done in the file and * given that the limit of the file has already been processed (should be at this point) * * THEN * * there is no point keeping the lock for reading (on unix systems) */ struct stat journal_stat; s64 last_page_offset = jix->last_page_offset; if(fstat(cloned_fd, &journal_stat) < 0) { return_value = ERRNO_ERROR; log_err("journal: ix: unable to get journal file status", return_value); close_ex(cloned_fd); return return_value; } s64 file_size = journal_stat.st_size; #if DEBUG_JOURNAL log_debug("journal: ix: the last page starts at position %lld", last_page_offset); #endif journal_ix_readunlock(jix); jix = NULL; input_stream fis; fd_input_stream_attach(&fis, cloned_fd); if(last_soa_rr != NULL) { /* seek and store the last SOA print*/ last_soa_rr->tctr.qtype = 0; // clear type if(lseek(cloned_fd, last_page_offset, SEEK_SET) >= 0) { /* deleted SOA */ if((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { /* DEL records */ last_soa_rr->tctr.qtype = 0; // clear type /* scan until added SOA found */ while((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { break; } } } } // if the SOA has not been found, it's an error (EOF has been reached is covered by this) if(ISOK(return_value)) { if(last_soa_rr->tctr.qtype != TYPE_SOA) { return_value = ZDB_JOURNAL_SOA_RECORD_EXPECTED; } } } else { return_value = ERRNO_ERROR; } if(FAIL(return_value)) { input_stream_close(&fis); return return_value; } } /* * this format has no indexing so we scan for a page that STARTS with a DELETE of the SOA with serial = serial_from */ if(lseek(cloned_fd, 0, SEEK_SET) != 0) /* the resulting offset MUST be zero */ { return_value = ERRNO_ERROR; if(ISOK(return_value)) { return_value = ERROR; } input_stream_close(&fis); return return_value; } input_stream bis; dns_resource_record rr; dns_resource_record_init(&rr); buffer_input_stream_init(&fis, &bis, 512); s64 offset = 0; /* skip until the right serial is found */ u32 soa_count = 0; #ifdef DEBUG_JOURNAL u32 rr_count = 0; #endif for(;;) { if( (return_value = dns_resource_record_read(&rr, &bis)) <= 0 ) // FAIL or nothing to { return_value = ZDB_JOURNAL_ERROR_READING_JOURNAL; /* is the journal file broken ? */ break; } #ifdef DEBUG_JOURNAL rr_count++; #endif u32 record_size = return_value; if(rr.tctr.qtype == TYPE_SOA) { // ((0+1)&1) != 0 => Y N Y N if((++soa_count & 1) != 0) // 1 2 3 4 { u8 *p = rr.rdata; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; u32 serial = ntohl(GET_U32_AT(*p)); if(serial_ge(serial, serial_from)) { if(serial == serial_from) { /* setup the serial to be from 'offset' up to the current length of the stream */ return_value = SUCCESS; } else { /* the serial does not exist in the range */ return_value = ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } break; } } } offset += record_size; } #if DEBUG_JOURNAL log_debug("journal: ix: serial %08x (%d) is at offset %lld. %d records parsed", serial_from, serial_from, offset, rr_count); #endif dns_resource_record_clear(&rr); /* * detach the file descriptor from the file stream in the buffer stream * I do it like this because the streams are not needed anymore but the * file descriptor still is (if no error occurred) */ fd_input_stream_detach(buffer_input_stream_get_filtered(&bis)); input_stream_close(&bis); if(ISOK(return_value)) { // offset is the start of the page we are looking for if(lseek(cloned_fd, offset, SEEK_SET) >= 0) { fd_input_stream_attach(&fis, cloned_fd); limited_input_stream_init(&fis, out_input_stream, file_size - offset); } else { return_value = ERRNO_ERROR; close_ex(cloned_fd); } } else { close_ex(cloned_fd); } return return_value; }
static ya_result journal_ix_append_ixfr_stream(journal *jh, input_stream *ixfr_wire_is) { journal_ix *jix = (journal_ix*)jh; journal_ix_writelock(jix); /* * Move at the end of the file * Check that the wire starts with the last soa/serial * Append the wire * update the last serial */ // read the record ya_result return_value; dns_resource_record rr; dns_resource_record_init(&rr); if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) { /* FAIL or EOF */ dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } /* * The first record is an SOA and our starting point (to be deleted) */ #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: DEL %{dnsname} %{typerdatadesc}", rr.name, &rdatadesc); #endif if(rr.tctr.qtype != TYPE_SOA) { u16 rtype = rr.tctr.qtype; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: expected SOA record but got %{dnstype} instead", &rtype); return ZDB_JOURNAL_SOA_RECORD_EXPECTED; } /* * check the journal file exists/is defined * do it now if not * proceed */ if(((jix->first_serial == 0) && (jix->last_serial == 0)) || (jix->fd == -1)) { /* the file does not exists yet */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &jix->first_serial))) { dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } int fd = open_create_ex(jix->journal_name, O_RDWR|O_CREAT, 0644); if(fd < 0) { return_value = ERRNO_ERROR; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to open journal file '%s': %r", jix->journal_name, return_value); return return_value; } log_info("journal: ix: journal file created '%s'", jix->journal_name); jix->fd = fd; } if(FAIL(return_value = journal_ix_ensure_opened(jix))) { return return_value; } u64 valid_offset = lseek(jix->fd, 0, SEEK_END); u64 current_offset = valid_offset; u32 valid_serial = jix->last_serial; u32 potential_serial = valid_serial; s64 valid_page_offset = jix->last_page_offset; s64 potential_page_offset = current_offset; #ifdef DEBUG log_debug("journal: ix: ready to append to journal after serial %08x (%d) at offset %lld", valid_serial, valid_serial, valid_offset); #endif u8 mode = 0; /* 0: del, 1: add */ output_stream fos; output_stream bos; fd_output_stream_attach(&fos, jix->fd); buffer_output_stream_init(&bos, &fos, 512); for(;;) { /* write the first */ if(FAIL(return_value = dns_resource_record_write(&rr, &bos))) { /* this is VERY bad */ log_err("journal: ix: error writing a record to the journal: %r", return_value); break; } /* update the current offset */ current_offset += return_value; if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) /* no bytes read OR error, there is no macro for this */ { /* error or end of stream */ if(return_value == 0) /* end of stream */ { if(mode != 0) /* on add mode so everything should be fine */ { valid_offset = current_offset; valid_serial = potential_serial; valid_page_offset = potential_page_offset; } else /* but on delete mode instead of add mode */ { log_err("journal: ix: ixfr stream unexpected eof"); return_value = UNEXPECTED_EOF; /* we have an error */ } } break; } if(rr.tctr.qtype == TYPE_SOA) { mode ^= 1; #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); #endif if(mode == 0) { /* * new SOA to delete * * it's a new "page" (delete -> add) * * the offset before we write this record is the highest valid one in the file * so the error correcting truncation will be made at that offset */ valid_offset = current_offset; /* * the serial number that has been added with the previous page */ valid_serial = potential_serial; /* * the offset of the previous page */ valid_page_offset = potential_page_offset; /* * the new page starts here : update */ potential_page_offset = current_offset; } else { /* * new SOA add * * this is the second half of the page, we know what serial it is about */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &potential_serial))) { break; } } } #ifdef DEBUG else { rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); } #endif } if(FAIL(return_value)) { /* * The journal is only valid up to valid_offset with serial ... */ log_err("journal: ix: rewinding journal up to last valid point (%lld)", valid_offset); ftruncate(jix->fd, valid_offset); } #ifdef DEBUG log_debug("journal: ix: page offset got from %d to %d", jix->last_page_offset, valid_page_offset); log_debug("journal: ix: serial got from %d to %d", jix->last_serial, valid_serial); #endif jix->last_page_offset = valid_page_offset; jix->last_serial = valid_serial; /* * rename the file */ if(ISOK(return_value)) { char new_name[PATH_MAX]; memcpy(new_name, jix->journal_name, jix->journal_name_len); snformat(&new_name[jix->journal_name_len - FIRST_FROM_END], 8 + 1 + 8 + 1 + IX_EXT_STRLEN + 1, "%08x-%08x." IX_EXT , jix->first_serial, jix->last_serial); if(rename(jix->journal_name, new_name) >= 0) { memcpy(jix->journal_name, new_name, jix->journal_name_len); } } /* */ #ifdef DEBUG log_debug("journal: ix: fd=%i from=%08x to=%08x soa@%lld file=%s", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET"); #endif output_stream_flush(&bos); fd_output_stream_detach(buffer_output_stream_get_filtered(&bos)); output_stream_close(&bos); dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); if(ISOK(return_value)) { #ifdef DEBUG log_debug("journal: ix: page added (fd=%i from=%08x to=%08x soa@%lld file=%s): %r", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET", return_value); #endif return TYPE_IXFR; /* that's what the caller expects to handle the new journal pages */ } else { log_err("journal: ix: failed to add page"); return return_value; } }
static ya_result config_main_verify_and_update_file(const char *base_path, char **dirp) { if(dirp == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } char *p = *dirp; if(p == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } char *e = p + strlen(p) - 1; while((*e != '/') && (e > p)) { e--; } if((*e == '/') || (e == p)) { char filename[PATH_MAX]; if(*e == '/') { *e = '\0'; e++; strncpy(filename, e, sizeof(filename)); *e = '\0'; } else { strncpy(filename, e, sizeof(filename)); *e = '\0'; } ya_result ret; if(ISOK(ret = config_main_verify_and_update_directory(base_path, dirp))) { chroot_unmanage_path(dirp); int pathlen = strlen(*dirp); memmove(&filename[pathlen + 1], filename, strlen(filename) + 1); memcpy(filename, *dirp, pathlen); filename[pathlen] = '/'; free(*dirp); *dirp = strdup(filename); chroot_manage_path(dirp, filename, FALSE); return SUCCESS; } else { return ret; } } return SUCCESS; }
static ya_result xfr_query_callback(void* data) { xfr_query_schedule_param *xqsp = (xfr_query_schedule_param*)data; ya_result return_value; ya_result scheduler_status; u32 refreshed_time = 0; u32 retried_time = time(NULL); /* * Zone refresh will be enabled (again) here. * * xfr_query_mark_zone_loaded * */ if(ISOK(xqsp->return_value)) { /* success but type == 0 => transfer done */ if(xqsp->type != 0) { log_info("slave: %{dnstype}: proceeding with transfer of %{dnsname}", &xqsp->type, xqsp->origin); } } else { log_err("slave: %{dnstype}: transfer of %{dnsname} failed: %r", &xqsp->type, xqsp->origin, xqsp->return_value); /* * Here (?) put the invalid zone placeholder if it is not there yet */ } switch(xqsp->type) { case TYPE_AXFR: { /* * Load the axfr */ if(ISOK(xqsp->return_value)) { axfr_query_axfr_load_param *aqalp; MALLOC_OR_DIE(axfr_query_axfr_load_param*, aqalp, sizeof(axfr_query_axfr_load_param), GENERIC_TAG); log_info("slave: opening AXFR for %{dnsname} %d", xqsp->origin, xqsp->loaded_serial); if(ISOK(return_value = zone_axfr_reader_open_with_serial(g_config->xfr_path, xqsp->origin, xqsp->loaded_serial, &aqalp->zr))) { /* * The system is ready to load an AXFR * A zone is already in place (by design), there may be an old zone that is now irrelevant. * At this point ... * * If the serial are not the same * MT the old zone must be destroyed (if it exist) * then * MT The new zone has to be created (loaded from the AXFR file) * Else * ST The new zone is actually the old zone, and the old zone is non-existent * EndIf * * then * * ST The new zone and the placeholder zone have to be swapped, then the placeholder has to be destroyed * */ zdb_zone *zone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN); if(zone != NULL) { zdb_zone_truncate_invalidate(zone); } /** * schedule the axfr load */ aqalp->old_zone = NULL; /* old zone */ aqalp->new_zone = NULL; aqalp->db = xqsp->db; aqalp->origin = dnsname_dup(xqsp->origin); scheduler_status = SCHEDULER_TASK_PROGRESS; thread_pool_schedule_job(xfr_query_axfr_load_thread, aqalp, NULL, "axfr load"); break; } } /** * @todo If the "old_zone" exists, then ... (destroy ? swap back ? expired ?) * */ /* * An issue occurred (AXFR transfer, load) */ log_err("slave: unable to load the axfr (retry set in %d seconds)", g_config->axfr_retry_delay); alarm_event_node *event = alarm_event_alloc(); event->epoch = time(NULL) + g_config->axfr_retry_delay; if(g_config->axfr_retry_jitter > 0) { random_ctx rndctx = thread_pool_get_random_ctx(); u32 jitter = random_next(rndctx) % g_config->axfr_retry_jitter; event->epoch += jitter; } event->function = scheduler_axfr_query_alarm; event->args = xqsp; event->key = ALARM_KEY_ZONE_AXFR_QUERY; event->flags = ALARM_DUP_NOP; event->text = "scheduler_axfr_query_alarm"; zdb *db = (zdb*)xqsp->db; alarm_set(db->alarm_handle, event); /* DO NOT FREE xqsp, SO DO NOT BREAK : return now */ return SCHEDULER_TASK_FINISHED; } case TYPE_IXFR: { /** * Load the ixfr (single thread, zone locked) */ scheduler_status = SCHEDULER_TASK_FINISHED; if(ISOK(xqsp->return_value)) { zdb_zone *dbzone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN); if(dbzone == NULL) { log_err("slave: zone %{dnsname} journal has vanished", xqsp->origin); break; } log_info("slave: opening journal for '%{dnsname}'", xqsp->origin); zdb_zone_lock(dbzone, ZDB_ZONE_MUTEX_XFR); if(ISOK(return_value = zdb_icmtl_replay(dbzone, g_config->xfr_path, xqsp->serial_start_offset, xqsp->loaded_serial, ZDB_ICMTL_REPLAY_SERIAL_OFFSET|ZDB_ICMTL_REPLAY_SERIAL_LIMIT))) { log_info("slave: replayed %d records changes", return_value); dbzone->apex->flags &= ~ZDB_RR_LABEL_INVALID_ZONE; refreshed_time = time(NULL); } else { log_err("slave: replay error: %r this is bad: invalidate the zone and ask for an AXFR", return_value); /** @todo INVALIDATE AND AXFR */ } /** @todo invalitate if retried_time > expired_time */ zdb_zone_unlock(dbzone, ZDB_ZONE_MUTEX_XFR); } log_info("slave: journal transfer done"); xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time); scheduler_status = SCHEDULER_TASK_FINISHED; break; } default: { refreshed_time = time(NULL); log_info("slave: transfer done"); xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time); scheduler_status = SCHEDULER_TASK_FINISHED; break; } } /* switch return_value */