char *pid_check_and_create() { struct stat st; char* pidfile = pid_filename(); pid_t pid = pid_read(pidfile); /* Check PID for existence and liveness. */ if (pid > 0 && pid_running(pid)) { log_server_error("Server PID found, already running.\n"); free(pidfile); return NULL; } else if (stat(pidfile, &st) == 0) { log_server_warning("Removing stale PID file '%s'.\n", pidfile); pid_remove(pidfile); } /* Create a PID file. */ int ret = pid_write(pidfile); if (ret != KNOT_EOK) { log_server_error("Couldn't create a PID file '%s'.\n", pidfile); free(pidfile); return NULL; } return pidfile; }
int proc_update_privileges(int uid, int gid) { #ifdef HAVE_SETGROUPS /* Drop supplementary groups. */ if (uid != getuid() || gid != getgid()) { if (setgroups(0, NULL) < 0) { log_server_warning("Failed to drop supplementary groups" " for uid '%d' (%s).\n", getuid(), strerror(errno)); } } #endif /* Watch uid/gid. */ if (gid != getgid()) { log_server_info("Changing group id to '%d'.\n", gid); if (setregid(gid, gid) < 0) { log_server_error("Failed to change gid to '%d'.\n", gid); } } if (uid != getuid()) { log_server_info("Changing user id to '%d'.\n", uid); if (setreuid(uid, uid) < 0) { log_server_error("Failed to change uid to '%d'.\n", uid); } } /* Check storage writeability. */ int ret = KNOT_EOK; char *lfile = strcdup(conf()->storage, "/knot.lock"); assert(lfile != NULL); FILE* fp = fopen(lfile, "w"); if (fp == NULL) { log_server_warning("Storage directory '%s' is not writeable.\n", conf()->storage); ret = KNOT_EACCES; } else { fclose(fp); unlink(lfile); } free(lfile); return ret; }
journal_t* journal_open(const char *fn, size_t fslimit, int mode, uint16_t bflags) { /*! \todo Memory mapping may be faster than stdio? (issue #964) */ if (fn == NULL) { return NULL; } /* Check for lazy mode. */ if (mode & JOURNAL_LAZY) { dbg_journal("journal: opening journal %s lazily\n", fn); journal_t *j = malloc(sizeof(journal_t)); if (j != NULL) { memset(j, 0, sizeof(journal_t)); j->fd = -1; j->path = strdup(fn); j->fslimit = fslimit; j->bflags = bflags; j->refs = 1; } return j; } /* Open journal file for r/w (returns error if not exists). */ int fd = open(fn, O_RDWR); if (fd < 0) { if (errno == ENOENT) { if(journal_create(fn, JOURNAL_NCOUNT) == KNOT_EOK) { return journal_open(fn, fslimit, mode, bflags); } } return NULL; } /* File lock. */ struct flock fl; memset(&fl, 0, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_pid = getpid(); /* Attempt to lock. */ dbg_journal_verb("journal: locking journal %s\n", fn); int ret = fcntl(fd, F_SETLK, &fl); /* Lock. */ if (ret < 0) { struct flock efl; memcpy(&efl, &fl, sizeof(struct flock)); fcntl(fd, F_GETLK, &efl); log_server_warning("Journal file '%s' is locked by process " "PID=%d, waiting for process to " "release lock.\n", fn, efl.l_pid); ret = fcntl(fd, F_SETLKW, &fl); } fl.l_type = F_UNLCK; dbg_journal("journal: locked journal %s (returned %d)\n", fn, ret); /* Read magic bytes. */ dbg_journal("journal: reading magic bytes\n"); const char magic_req[MAGIC_LENGTH] = JOURNAL_MAGIC; char magic[MAGIC_LENGTH]; if (!sfread(magic, MAGIC_LENGTH, fd)) { dbg_journal_detail("journal: cannot read magic bytes\n"); fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } if (memcmp(magic, magic_req, MAGIC_LENGTH) != 0) { log_server_warning("Journal file '%s' version is too old, " "it will be flushed.\n", fn); fcntl(fd, F_SETLK, &fl); close(fd); if (journal_create(fn, JOURNAL_NCOUNT) == KNOT_EOK) { return journal_open(fn, fslimit, mode, bflags); } return NULL; } crc_t crc = 0; if (!sfread(&crc, sizeof(crc_t), fd)) { dbg_journal_detail("journal: cannot read CRC\n"); fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } /* Recalculate CRC. */ char buf[4096]; ssize_t rb = 0; crc_t crc_calc = crc_init(); while((rb = read(fd, buf, sizeof(buf))) > 0) { crc_calc = crc_update(crc_calc, (const unsigned char *)buf, rb); } /* Compare */ if (crc == crc_calc) { /* Rewind. */ if (lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) { fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } } else { log_server_warning("Journal file '%s' CRC error, " "it will be flushed.\n", fn); fcntl(fd, F_SETLK, &fl); close(fd); if (journal_create(fn, JOURNAL_NCOUNT) == KNOT_EOK) { return journal_open(fn, fslimit, mode, bflags); } return NULL; } /* Read maximum number of entries. */ uint16_t max_nodes = 512; if (!sfread(&max_nodes, sizeof(uint16_t), fd)) { dbg_journal_detail("journal: cannot read max_nodes\n"); fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } /* Check max_nodes, but this is riddiculous. */ if (max_nodes == 0) { dbg_journal_detail("journal: max_nodes is invalid\n"); fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } /* Allocate journal structure. */ const size_t node_len = sizeof(journal_node_t); journal_t *j = malloc(sizeof(journal_t) + max_nodes * node_len); if (j == NULL) { dbg_journal_detail("journal: cannot allocate journal\n"); fcntl(fd, F_SETLK, &fl); close(fd); return NULL; } memset(j, 0, sizeof(journal_t) + max_nodes * node_len); j->qhead = j->qtail = 0; j->fd = fd; j->max_nodes = max_nodes; j->bflags = bflags; j->refs = 1; /* Load node queue state. */ if (!sfread(&j->qhead, sizeof(uint16_t), fd)) { dbg_journal_detail("journal: cannot read qhead\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Load queue tail. */ if (!sfread(&j->qtail, sizeof(uint16_t), fd)) { dbg_journal_detail("journal: cannot read qtail\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Check head + tail */ if (j->qtail > max_nodes || j->qhead > max_nodes) { dbg_journal_detail("journal: queue pointers corrupted\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Load empty segment descriptor. */ if (!sfread(&j->free, node_len, fd)) { dbg_journal_detail("journal: cannot read free segment ptr\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Read journal descriptors table. */ if (!sfread(&j->nodes, max_nodes * node_len, fd)) { dbg_journal_detail("journal: cannot read node table\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Get journal file size. */ struct stat st; if (stat(fn, &st) < 0) { dbg_journal_detail("journal: cannot get journal fsize\n"); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } /* Set file size. */ j->fsize = st.st_size; if (fslimit == 0) { j->fslimit = FSLIMIT_INF; } else { j->fslimit = (size_t)fslimit; } dbg_journal("journal: opened journal size=%u, queue=<%u, %u>, fd=%d\n", max_nodes, j->qhead, j->qtail, j->fd); /* Check node queue. */ unsigned qtail_free = (jnode_flags(j, j->qtail) <= JOURNAL_FREE); unsigned qhead_free = j->max_nodes - 1; /* Left of qhead must be free.*/ if (j->qhead > 0) { qhead_free = (j->qhead - 1); } qhead_free = (jnode_flags(j, qhead_free) <= JOURNAL_FREE); if ((j->qhead != j->qtail) && (!qtail_free || !qhead_free)) { log_server_warning("Recovering journal '%s' metadata " "after crash.\n", fn); ret = journal_recover(j); if (ret != KNOT_EOK) { log_server_error("Journal file '%s' is unrecoverable, " "metadata corrupted - %s\n", fn, knot_strerror(ret)); fcntl(fd, F_SETLK, &fl); close(fd); free(j); return NULL; } } /* Save file lock. */ fl.l_type = F_WRLCK; memcpy(&j->fl, &fl, sizeof(struct flock)); return j; }
/*! \brief Open journal file for r/w (returns error if not exists). */ static int journal_open_file(journal_t *j) { assert(j != NULL); int ret = KNOT_EOK; j->fd = open(j->path, O_RDWR); dbg_journal_verb("journal: open_file '%s'\n", j->path); if (j->fd < 0) { if (errno != ENOENT) { return knot_map_errno(errno); } /* Create new journal file and open if not exists. */ ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } /* File lock. */ memset(&j->fl, 0, sizeof(struct flock)); j->fl.l_type = F_WRLCK; j->fl.l_whence = SEEK_SET; j->fl.l_start = 0; j->fl.l_len = 0; j->fl.l_pid = getpid(); /* Attempt to lock. */ dbg_journal_verb("journal: locking journal %s\n", j->path); ret = fcntl(j->fd, F_SETLK, &j->fl); /* Lock. */ if (ret < 0) { struct flock efl = {0}; memcpy(&efl, &j->fl, sizeof(struct flock)); (void) fcntl(j->fd, F_GETLK, &efl); log_server_warning("Journal file '%s' is locked by process " "PID=%d, waiting for process to " "release lock.\n", j->path, efl.l_pid); ret = fcntl(j->fd, F_SETLKW, &j->fl); } UNUSED(ret); dbg_journal("journal: locked journal %s (returned %d)\n", j->path, ret); /* Read magic bytes. */ dbg_journal("journal: reading magic bytes\n"); const char magic_req[MAGIC_LENGTH] = JOURNAL_MAGIC; char magic[MAGIC_LENGTH]; if (!sfread(magic, MAGIC_LENGTH, j->fd)) { dbg_journal_verb("journal: cannot read magic bytes\n"); goto open_file_error; } if (memcmp(magic, magic_req, MAGIC_LENGTH) != 0) { log_server_warning("Journal file '%s' version is too old, " "it will be purged.\n", j->path); close(j->fd); j->fd = -1; ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } crc_t crc = 0; if (!sfread(&crc, sizeof(crc_t), j->fd)) { dbg_journal_verb("journal: cannot read CRC\n"); goto open_file_error; } /* Recalculate CRC. */ char buf[4096]; ssize_t rb = 0; crc_t crc_calc = crc_init(); while((rb = read(j->fd, buf, sizeof(buf))) > 0) { crc_calc = crc_update(crc_calc, (const unsigned char *)buf, rb); } /* Compare */ if (crc == crc_calc) { /* Rewind. */ if (lseek(j->fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) { goto open_file_error; } } else { log_server_warning("Journal file '%s' CRC error, " "it will be purged.\n", j->path); close(j->fd); j->fd = -1; ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } /* Get journal file size. */ struct stat st; if (stat(j->path, &st) < 0) { dbg_journal_verb("journal: cannot get journal fsize\n"); goto open_file_error; } /* Set file size. */ j->fsize = st.st_size; /* Read maximum number of entries. */ if (!sfread(&j->max_nodes, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read max_nodes\n"); goto open_file_error; } /* Check max_nodes, but this is riddiculous. */ if (j->max_nodes == 0) { dbg_journal_verb("journal: invalid max_nodes\n"); goto open_file_error; } /* Allocate nodes. */ const size_t node_len = sizeof(journal_node_t); j->nodes = malloc(j->max_nodes * node_len); if (j->nodes == NULL) { dbg_journal_verb("journal: can't allocate nodes\n"); goto open_file_error; } else { memset(j->nodes, 0, j->max_nodes * node_len); } /* Load node queue state. */ j->qhead = j->qtail = 0; if (!sfread(&j->qhead, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read qhead\n"); goto open_file_error; } /* Load queue tail. */ if (!sfread(&j->qtail, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read qtail\n"); goto open_file_error; } /* Check head + tail */ if (j->qtail >= j->max_nodes || j->qhead >= j->max_nodes) { dbg_journal_verb("journal: queue pointers corrupted\n"); goto open_file_error; } /* Load empty segment descriptor. */ if (!sfread(&j->free, node_len, j->fd)) { dbg_journal_verb("journal: cannot read free segment ptr\n"); goto open_file_error; } /* Read journal descriptors table. */ if (!sfread(j->nodes, j->max_nodes * node_len, j->fd)) { dbg_journal_verb("journal: cannot read node table\n"); goto open_file_error; } dbg_journal("journal: opened journal size=%u, queue=<%u, %u>, fd=%d\n", j->max_nodes, j->qhead, j->qtail, j->fd); /* Check node queue. */ unsigned qtail_free = (jnode_flags(j, j->qtail) <= JOURNAL_FREE); unsigned qhead_free = j->max_nodes - 1; /* Left of qhead must be free.*/ if (j->qhead > 0) { qhead_free = (j->qhead - 1); } qhead_free = (jnode_flags(j, qhead_free) <= JOURNAL_FREE); if ((j->qhead != j->qtail) && (!qtail_free || !qhead_free)) { log_server_warning("Recovering journal '%s' metadata " "after crash.\n", j->path); ret = journal_recover(j); if (ret != KNOT_EOK) { log_server_error("Journal file '%s' is unrecoverable, " "metadata corrupted - %s\n", j->path, knot_strerror(ret)); goto open_file_error; } } /* Save file lock and return. */ return KNOT_EOK; /* Unlock and close file and return error. */ open_file_error: free(j->nodes); j->nodes = NULL; close(j->fd); j->fd = -1; return KNOT_ERROR; }
int proc_update_privileges(int uid, int gid) { #ifdef HAVE_SETGROUPS /* Drop supplementary groups. */ if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) { if (setgroups(0, NULL) < 0) { log_server_warning("Failed to drop supplementary groups" " for uid '%d' (%s).\n", getuid(), strerror(errno)); } # ifdef HAVE_INITGROUPS struct passwd *pw; if ((pw = getpwuid(uid)) == NULL) { log_server_warning("Failed to get passwd entry" " for uid '%d' (%s).\n", uid, strerror(errno)); } else { if (initgroups(pw->pw_name, gid) < 0) { log_server_warning("Failed to set supplementary groups" " for uid '%d' (%s).\n", uid, strerror(errno)); } } # endif /* HAVE_INITGROUPS */ } #endif /* HAVE_SETGROUPS */ /* Watch uid/gid. */ if ((gid_t)gid != getgid()) { log_server_info("Changing group id to '%d'.\n", gid); if (setregid(gid, gid) < 0) { log_server_error("Failed to change gid to '%d'.\n", gid); } } if ((uid_t)uid != getuid()) { log_server_info("Changing user id to '%d'.\n", uid); if (setreuid(uid, uid) < 0) { log_server_error("Failed to change uid to '%d'.\n", uid); } } /* Check storage writeability. */ int ret = KNOT_EOK; const bool sorted = false; hattrie_iter_t *z_iter = hattrie_iter_begin(conf()->zones, sorted); if (z_iter == NULL) { return KNOT_ERROR; } for (; !hattrie_iter_finished(z_iter); hattrie_iter_next(z_iter)) { conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); char *lfile = strcdup(zone->storage, "/knot.lock"); assert(lfile != NULL); FILE* fp = fopen(lfile, "w"); if (fp == NULL) { log_server_warning("Storage directory '%s' is not " "writeable.\n", zone->storage); ret = KNOT_EACCES; } else { fclose(fp); unlink(lfile); } free(lfile); if (ret != KNOT_EOK) { break; } } hattrie_iter_free(z_iter); return ret; }
int notify_process_request(knot_nameserver_t *ns, knot_packet_t *notify, sockaddr_t *from, uint8_t *buffer, size_t *size) { /*! \todo Most of this function is identical to xfrin_transfer_needed() * - it will be fine to merge the code somehow. */ if (notify == NULL || ns == NULL || buffer == NULL || size == NULL || from == NULL) { dbg_notify("notify: invalid parameters for %s()\n", "notify_process_request"); return KNOT_EINVAL; } int ret = KNOT_EOK; dbg_notify("notify: parsing rest of the packet\n"); if (notify->parsed < notify->size) { if (knot_packet_parse_rest(notify, 0) != KNOT_EOK) { dbg_notify("notify: failed to parse NOTIFY query\n"); knot_ns_error_response_from_query(ns, notify, KNOT_RCODE_FORMERR, buffer, size); return KNOT_EOK; } } // check if it makes sense - if the QTYPE is SOA if (knot_packet_qtype(notify) != KNOT_RRTYPE_SOA) { // send back FORMERR knot_ns_error_response_from_query(ns, notify, KNOT_RCODE_FORMERR, buffer, size); return KNOT_EOK; } // create NOTIFY response dbg_notify("notify: creating response\n"); ret = notify_create_response(notify, buffer, size); if (ret != KNOT_EOK) { dbg_notify("notify: failed to create NOTIFY response\n"); knot_ns_error_response_from_query(ns, notify, KNOT_RCODE_SERVFAIL, buffer, size); return KNOT_EOK; } /* Process notification. */ ret = KNOT_ENOZONE; unsigned serial = 0; const knot_dname_t *qname = knot_packet_qname(notify); rcu_read_lock(); /* z */ const knot_zone_t *z = knot_zonedb_find_zone_for_name(ns->zone_db, qname); if (z != NULL) { ret = notify_check_and_schedule(ns, z, from); const knot_rrset_t *soa_rr = NULL; soa_rr = knot_packet_answer_rrset(notify, 0); if (soa_rr && knot_rrset_type(soa_rr) == KNOT_RRTYPE_SOA) { serial = knot_rrset_rdata_soa_serial(soa_rr); } } rcu_read_unlock(); int rcode = KNOT_RCODE_NOERROR; switch (ret) { case KNOT_ENOZONE: rcode = KNOT_RCODE_NOTAUTH; break; case KNOT_EACCES: rcode = KNOT_RCODE_REFUSED; break; default: break; } /* Format resulting log message. */ char *qstr = knot_dname_to_str(qname); char *fromstr = xfr_remote_str(from, NULL); if (rcode != KNOT_RCODE_NOERROR) { knot_ns_error_response_from_query(ns, notify, KNOT_RCODE_REFUSED, buffer, size); log_server_warning(NOTIFY_MSG "%s\n", qstr, fromstr, knot_strerror(ret)); ret = KNOT_EOK; /* Send response. */ } else { log_server_info(NOTIFY_MSG NOTIFY_XMSG "\n", qstr, fromstr, serial); } free(qstr); free(fromstr); return ret; }