Example #1
0
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;
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
/*! \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;
}
Example #5
0
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;
}
Example #6
0
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;
}