Ejemplo n.º 1
0
static void start_one_job(const char *user, CronLine *line)
{
	const char *shell;
	struct passwd *pas;
	pid_t pid;

	pas = getpwnam(user);
	if (!pas) {
		bb_error_msg("can't get uid for %s", user);
		goto err;
	}

	/* Prepare things before vfork */
	shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL;
	set_env_vars(pas, shell);

	/* Fork as the user in question and run program */
	pid = vfork();
	if (pid == 0) {
		/* CHILD */
		/* initgroups, setgid, setuid, and chdir to home or CRON_DIR */
		change_user(pas);
		log5("child running %s", shell);
		/* crond 3.0pl1-100 puts tasks in separate process groups */
		bb_setpgrp();
		execl(shell, shell, "-c", line->cl_cmd, (char *) NULL);
		bb_error_msg_and_die("can't execute '%s' for user %s", shell, user);
	}
	if (pid < 0) {
		bb_perror_msg("vfork");
 err:
		pid = 0;
	}
	line->cl_pid = pid;
}
Ejemplo n.º 2
0
/*
 * Determine which jobs need to be run.  Under normal conditions, the
 * period is about a minute (one scan).  Worst case it will be one
 * hour (60 scans).
 */
static void flag_starting_jobs(time_t t1, time_t t2)
{
	time_t t;

	/* Find jobs > t1 and <= t2 */

	for (t = t1 - t1 % 60; t <= t2; t += 60) {
		struct tm *ptm;
		CronFile *file;
		CronLine *line;

		if (t <= t1)
			continue;

		ptm = localtime(&t);
		for (file = G.cron_files; file; file = file->cf_next) {
			log5("file %s:", file->cf_username);
			if (file->cf_deleted)
				continue;
			for (line = file->cf_lines; line; line = line->cl_next) {
				log5(" line %s", line->cl_cmd);
				if (line->cl_Mins[ptm->tm_min]
				 && line->cl_Hrs[ptm->tm_hour]
				 && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday])
				 && line->cl_Mons[ptm->tm_mon]
				) {
					log5(" job: %d %s",
							(int)line->cl_pid, line->cl_cmd);
					if (line->cl_pid > 0) {
						log8("user %s: process already running: %s",
							file->cf_username, line->cl_cmd);
					} else if (line->cl_pid == 0) {
						line->cl_pid = -1;
						file->cf_wants_starting = 1;
					}
				}
			}
		}
	}
}
Ejemplo n.º 3
0
// MyINFO example:
// $MyINFO $ALL <nick> <interest>$ $<speed\x01>$<e-mail>$<sharesize>$
// $MyINFO $ALL nick <++ V:0.668,M:P,H:39/0/0,S:1>$ $DSL\x01$$74894830123$
// at this point the tag has already been validated by the hub so we do only the simplest tests here
int cpiPython::SplitMyINFO(const char *msg, const char **nick, const char **desc, const char **tag, const char **speed, const char **mail, const char **size)
{
	const char *begin = "$MyINFO $ALL ";
	int dollars[5] = { -1, -1, -1, -1, -1 };
	int spacepos=0, validtag=0, tagstart=0, tagend=0;  // spacepos = position of space after nick
	int len = strlen(msg);
	int start = strlen(begin);
	if (len < 21 ) return 0;
	if (strncmp(msg, begin, start)) return 0;
	for (int pos = start, dollar = 0; pos < len && dollar < 5; pos++)
	{
		switch (msg[pos])
		{
			case '$': dollars[dollar] = pos; dollar++; break;
			case ' ': if (!spacepos and !dollar) spacepos = pos; break;
			case '<': if (!dollar) tagstart = pos; break;
			case '>': if (!dollar) tagend = pos; break;
		}
	}
	if (dollars[4] != len-1 || !spacepos) return 0;
	if (tagstart && tagend && msg[tagend+1] == '$') validtag = 1;

	string s = msg;
	string _nick  = s.substr( start, spacepos - start );
	string _desc  = (validtag) ? s.substr( spacepos + 1, tagstart - spacepos - 1 ) : s.substr( spacepos + 1, dollars[0] - spacepos - 1 );
	string _tag   = (validtag) ? s.substr( tagstart, dollars[0] - tagstart ) : "";
	string _speed = s.substr( dollars[1] + 1, dollars[2] - dollars[1] - 1 );
	string _mail  = s.substr( dollars[2] + 1, dollars[3] - dollars[2] - 1 );
	string _size  = s.substr( dollars[3] + 1, dollars[4] - dollars[3] - 1 );

	(*nick)  =  strdup(  _nick.c_str());
	(*desc)  =  strdup(  _desc.c_str());
	(*tag)   =  strdup(   _tag.c_str());
	(*speed) =  strdup( _speed.c_str());
	(*mail)  =  strdup(  _mail.c_str());
	(*size)  =  strdup(  _size.c_str());

	log5("PY: SplitMyINFO: [%s] \n    dollars:%d,%d,%d,%d,%d / tag start:%d,end:%d / space:%d\n    nick:%s/desc:%s/tag:%s/speed:%s/mail:%s/size:%s\n",
			s.c_str(), dollars[0], dollars[1], dollars[2], dollars[3], dollars[4], tagstart, tagend, spacepos,
			*nick, *desc, *tag, *speed, *mail, *size);

	return 1;
}
Ejemplo n.º 4
0
TEST(LoggerTest, LevelTest)
{
	std::ostringstream ss;
	Logger log1(ss, Logger::TRACE_);
	Logger log2(ss, Logger::VERBOSE_);
	Logger log3(ss, Logger::DEBUG_);
	Logger log4(ss, Logger::INFO_);
	Logger log5(ss, Logger::WARN_);
	Logger log6(ss, Logger::ERROR_);

	ASSERT_TRUE(log1.t());
	ASSERT_TRUE(log1.v());
	ASSERT_TRUE(log1.d());
	ASSERT_TRUE(log1.i());


	ASSERT_FALSE(log2.t());
	ASSERT_TRUE(log2.v());
	ASSERT_TRUE(log2.d());
	ASSERT_TRUE(log2.i());

	ASSERT_FALSE(log3.t());
	ASSERT_FALSE(log3.v());
	ASSERT_TRUE(log3.d());
	ASSERT_TRUE(log3.i());

	ASSERT_FALSE(log4.t());
	ASSERT_FALSE(log4.v());
	ASSERT_FALSE(log4.d());
	ASSERT_TRUE(log4.i());

	ASSERT_FALSE(log5.t());
	ASSERT_FALSE(log5.v());
	ASSERT_FALSE(log5.d());
	ASSERT_FALSE(log5.i());

	ASSERT_FALSE(log6.t());
	ASSERT_FALSE(log6.v());
	ASSERT_FALSE(log6.d());
	ASSERT_FALSE(log6.i());
}
Ejemplo n.º 5
0
int crond_main(int argc UNUSED_PARAM, char **argv)
{
	time_t t2;
	unsigned rescan;
	unsigned sleep_time;
	unsigned opts;

	INIT_G();

	/* "-b after -f is ignored", and so on for every pair a-b */
	opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
			/* -l and -d have numeric param */
			":l+" IF_FEATURE_CROND_D(":d+");
	opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
			&G.log_level, &G.log_filename, &G.crontab_dir_name
			IF_FEATURE_CROND_D(,&G.log_level));
	/* both -d N and -l N set the same variable: G.log_level */

	if (!(opts & OPT_f)) {
		/* close stdin, stdout, stderr.
		 * close unused descriptors - don't need them. */
		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
	}

	if (!(opts & OPT_d) && G.log_filename == NULL) {
		/* logging to syslog */
		openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
		logmode = LOGMODE_SYSLOG;
	}

	//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */

	reopen_logfile_to_stderr();
	xchdir(G.crontab_dir_name);
	log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
	rescan_crontab_dir();
	write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");

	/* Main loop */
	t2 = time(NULL);
	rescan = 60;
	sleep_time = 60;
	for (;;) {
		struct stat sbuf;
		time_t t1;
		long dt;

		/* Synchronize to 1 minute, minimum 1 second */
		t1 = t2;
		sleep(sleep_time - (time(NULL) % sleep_time));
		t2 = time(NULL);
		dt = (long)t2 - (long)t1;

		reopen_logfile_to_stderr();

		/*
		 * The file 'cron.update' is checked to determine new cron
		 * jobs.  The directory is rescanned once an hour to deal
		 * with any screwups.
		 *
		 * Check for time jump.  Disparities over an hour either way
		 * result in resynchronization.  A negative disparity
		 * less than an hour causes us to effectively sleep until we
		 * match the original time (i.e. no re-execution of jobs that
		 * have just been run).  A positive disparity less than
		 * an hour causes intermediate jobs to be run, but only once
		 * in the worst case.
		 *
		 * When running jobs, the inequality used is greater but not
		 * equal to t1, and less then or equal to t2.
		 */
		if (stat(G.crontab_dir_name, &sbuf) != 0)
			sbuf.st_mtime = 0; /* force update (once) if dir was deleted */
		if (G.crontab_dir_mtime != sbuf.st_mtime) {
			G.crontab_dir_mtime = sbuf.st_mtime;
			rescan = 1;
		}
		if (--rescan == 0) {
			rescan = 60;
			rescan_crontab_dir();
		}
		process_cron_update_file();
		log5("wakeup dt=%ld", dt);
		if (dt < -60 * 60 || dt > 60 * 60) {
			bb_error_msg("time disparity of %ld minutes detected", dt / 60);
			/* and we do not run any jobs in this case */
		} else if (dt > 0) {
			/* Usual case: time advances forward, as expected */
			flag_starting_jobs(t1, t2);
			start_jobs();
			sleep_time = 60;
			if (check_completions() > 0) {
				/* some jobs are still running */
				sleep_time = 10;
			}
		}
		/* else: time jumped back, do not run any jobs */
	} /* for (;;) */

	return 0; /* not reached */
}
Ejemplo n.º 6
0
static pid_t
fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
{
	struct passwd *pas;
	const char *shell, *prog;
	smallint sv_logmode;
	pid_t pid;

	/* prepare things before vfork */
	pas = getpwnam(user);
	if (!pas) {
		bb_error_msg("can't get uid for %s", user);
		goto err;
	}

	shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL;
	prog = run_sendmail ? SENDMAIL : shell;

	set_env_vars(pas, shell);

	sv_logmode = logmode;
	pid = vfork();
	if (pid == 0) {
		/* CHILD */
		/* initgroups, setgid, setuid, and chdir to home or CRON_DIR */
		change_user(pas);
		log5("child running %s", prog);
		if (mailFd >= 0) {
			xmove_fd(mailFd, run_sendmail ? 0 : 1);
			dup2(1, 2);
		}
		/* crond 3.0pl1-100 puts tasks in separate process groups */
		bb_setpgrp();
		if (!run_sendmail)
			execlp(prog, prog, "-c", line->cl_cmd, (char *) NULL);
		else
			execlp(prog, prog, SENDMAIL_ARGS, (char *) NULL);
		/*
		 * I want this error message on stderr too,
		 * even if other messages go only to syslog:
		 */
		logmode |= LOGMODE_STDIO;
		bb_error_msg_and_die("can't execute '%s' for user %s", prog, user);
	}
	logmode = sv_logmode;

	if (pid < 0) {
		bb_perror_msg("vfork");
 err:
		pid = 0;
	} /* else: PARENT, FORK SUCCESS */

	/*
	 * Close the mail file descriptor.. we can't just leave it open in
	 * a structure, closing it later, because we might run out of descriptors
	 */
	if (mailFd >= 0) {
		close(mailFd);
	}
	return pid;
}
Ejemplo n.º 7
0
static void load_crontab(const char *fileName)
{
	struct parser_t *parser;
	struct stat sbuf;
	int maxLines;
	char *tokens[6];
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
	char *mailTo = NULL;
#endif
	char *shell = NULL;

	delete_cronfile(fileName);

	if (!getpwnam(fileName)) {
		log7("ignoring file '%s' (no such user)", fileName);
		return;
	}

	parser = config_open(fileName);
	if (!parser)
		return;

	maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;

	if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) {
		CronFile *file = xzalloc(sizeof(CronFile));
		CronLine **pline;
		int n;

		file->cf_username = xstrdup(fileName);
		pline = &file->cf_lines;

		while (1) {
			CronLine *line;

			if (!--maxLines) {
				bb_error_msg("user %s: too many lines", fileName);
				break;
			}

			n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
			if (!n)
				break;

			log5("user:%s entry:%s", fileName, parser->data);

			/* check if line is setting MAILTO= */
			if (is_prefixed_with(tokens[0], "MAILTO=")) {
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
				free(mailTo);
				mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
#endif /* otherwise just ignore such lines */
				continue;
			}
			if (is_prefixed_with(tokens[0], "SHELL=")) {
				free(shell);
				shell = xstrdup(&tokens[0][6]);
				continue;
			}
//TODO: handle HOME= too? "man crontab" says:
//name = value
//
//where the spaces around the equal-sign (=) are optional, and any subsequent
//non-leading spaces in value will be part of the value assigned to name.
//The value string may be placed in quotes (single or double, but matching)
//to preserve leading or trailing blanks.
//
//Several environment variables are set up automatically by the cron(8) daemon.
//SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
//line of the crontab's owner. HOME and SHELL may be overridden by settings
//in the crontab; LOGNAME may not.

			/* check if a minimum of tokens is specified */
			if (n < 6)
				continue;
			*pline = line = xzalloc(sizeof(*line));
			/* parse date ranges */
			ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
			ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]);
			ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]);
			ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]);
			ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]);
			/*
			 * fix days and dow - if one is not "*" and the other
			 * is "*", the other is set to 0, and vise-versa
			 */
			FixDayDow(line);
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
			/* copy mailto (can be NULL) */
			line->cl_mailto = xstrdup(mailTo);
#endif
			line->cl_shell = xstrdup(shell);
			/* copy command */
			line->cl_cmd = xstrdup(tokens[5]);
			pline = &line->cl_next;
//bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
		}
		*pline = NULL;

		file->cf_next = G.cron_files;
		G.cron_files = file;
	}
	config_close(parser);
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
	free(mailTo);
#endif
	free(shell);
}
Ejemplo n.º 8
0
static void load_crontab(const char *fileName)
{
	struct parser_t *parser;
	struct stat sbuf;
	int maxLines;
	char *tokens[6];
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
	char *mailTo = NULL;
#endif
	char *shell = NULL;

	delete_cronfile(fileName);

	if (!getpwnam(fileName)) {
		log7("ignoring file '%s' (no such user)", fileName);
		return;
	}

	parser = config_open(fileName);
	if (!parser)
		return;

	maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;

	if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) {
		CronFile *file = xzalloc(sizeof(CronFile));
		CronLine **pline;
		int n;

		file->cf_username = xstrdup(fileName);
		pline = &file->cf_lines;

		while (1) {
			CronLine *line;

			if (!--maxLines) {
				bb_error_msg("user %s: too many lines", fileName);
				break;
			}

			n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
			if (!n)
				break;

			log5("user:%s entry:%s", fileName, parser->data);

			/* check if line is setting MAILTO= */
			if (is_prefixed_with(tokens[0], "MAILTO=")) {
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
				free(mailTo);
				mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
#endif /* otherwise just ignore such lines */
				continue;
			}
			if (is_prefixed_with(tokens[0], "SHELL=")) {
				free(shell);
				shell = xstrdup(&tokens[0][6]);
				continue;
			}
//TODO: handle HOME= too? "man crontab" says:
//name = value
//
//where the spaces around the equal-sign (=) are optional, and any subsequent
//non-leading spaces in value will be part of the value assigned to name.
//The value string may be placed in quotes (single or double, but matching)
//to preserve leading or trailing blanks.
//
//Several environment variables are set up automatically by the cron(8) daemon.
//SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
//line of the crontab's owner. HOME and SHELL may be overridden by settings
//in the crontab; LOGNAME may not.

#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
			if (tokens[0][0] == '@') {
				/*
				 * "@daily /a/script/to/run PARAM1 PARAM2..."
				 */
				typedef struct SpecialEntry {
					const char *name;
					const char tokens[8];
				} SpecialEntry;
				static const SpecialEntry SpecAry[] = {
					/*              hour  day   month weekday */
					{ "yearly",     "0\0" "1\0" "1\0" "*" },
					{ "annually",   "0\0" "1\0" "1\0" "*" },
					{ "monthly",    "0\0" "1\0" "*\0" "*" },
					{ "weekly",     "0\0" "*\0" "*\0" "0" },
					{ "daily",      "0\0" "*\0" "*\0" "*" },
					{ "midnight",   "0\0" "*\0" "*\0" "*" },
					{ "hourly",     "*\0" "*\0" "*\0" "*" },
					{ "reboot",     ""                    },
				};
				const SpecialEntry *e = SpecAry;

				if (n < 2)
					continue;
				for (;;) {
					if (strcmp(e->name, tokens[0] + 1) == 0) {
						/*
						 * tokens[1] is only the first word of command,
						 * can'r use it.
						 * find the entire command in unmodified string:
						 */
						tokens[5] = skip_whitespace(
							skip_non_whitespace(
							skip_whitespace(parser->data)));
						if (e->tokens[0]) {
							char *et = (char*)e->tokens;
							/* minute is "0" for all specials */
							tokens[0] = (char*)"0";
							tokens[1] = et;
							tokens[2] = et + 2;
							tokens[3] = et + 4;
							tokens[4] = et + 6;
						}
						goto got_it;
					}
					if (!e->tokens[0])
						break;
					e++;
				}
				continue; /* bad line (unrecognized '@foo') */
			}
#endif
			/* check if a minimum of tokens is specified */
			if (n < 6)
				continue;
 IF_FEATURE_CROND_SPECIAL_TIMES(
  got_it:
 )
			*pline = line = xzalloc(sizeof(*line));
#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
			if (tokens[0][0] == '@') { /* "@reboot" line */
				file->cf_wants_starting = 1;
				line->cl_pid = START_ME_REBOOT; /* wants to start */
				/* line->cl_Mins/Hrs/etc stay zero: never match any time */
			} else
#endif
			{
				/* parse date ranges */
				ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
				ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]);
				ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]);
				ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]);
				ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]);
				/*
				 * fix days and dow - if one is not "*" and the other
				 * is "*", the other is set to 0, and vise-versa
				 */
				FixDayDow(line);
			}
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
			/* copy mailto (can be NULL) */
			line->cl_mailto = xstrdup(mailTo);
#endif
			line->cl_shell = xstrdup(shell);
			/* copy command */
			line->cl_cmd = xstrdup(tokens[5]);
			pline = &line->cl_next;
//bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
		}
		*pline = NULL;

		file->cf_next = G.cron_files;
		G.cron_files = file;
	}
Ejemplo n.º 9
0
/**
 * This is the main message reading loop.  Messages are read, validated,
 * decrypted if necessary, then passed to the appropriate routine for handling.
 */
void mainloop(void)
{
    struct uftp_h *header;
    struct group_list_t *group;
    unsigned char *buf, *decrypted, *message;
    char rxname[INET6_ADDRSTRLEN];
    unsigned int decryptlen, meslen;
    int packetlen, rval, i, ecn;
    uint8_t version, *func, tos;
    uint16_t txseq;
    union sockaddr_u src;
    struct timeval *tv, rxtime;
    double new_grtt;

    log2(0, 0, 0, "%s", VERSIONSTR);
    for (i = 0; i < key_count; i++) {
        if (privkey_type[i] == KEYBLOB_RSA) {
            log2(0, 0, 0, "Loaded %d bit RSA key with fingerprint %s",
                  RSA_keylen(privkey[i].rsa) * 8,
                  print_key_fingerprint(privkey[i], KEYBLOB_RSA));
        } else {
            log2(0, 0, 0, "Loaded ECDSA key with curve %s and fingerprint %s",
                  curve_name(get_EC_curve(privkey[i].ec)),
                  print_key_fingerprint(privkey[i], KEYBLOB_EC));
        }
    }

    buf = safe_calloc(MAXMTU, 1);
    decrypted = safe_calloc(MAXMTU, 1);
    header = (struct uftp_h *)buf;

    while (1) {
        tv = getrecenttimeout();
        if (tv) {
            log5(0, 0, 0, "read timeout: %d.%06d", tv->tv_sec, tv->tv_usec);
        }
        if (read_packet(listener, &src, buf, &packetlen,
                        MAXMTU, tv, &tos) <= 0) {
            continue;
        }
        gettimeofday(&rxtime, NULL);

        if ((rval = getnameinfo((struct sockaddr *)&src, family_len(src),
                rxname, sizeof(rxname), NULL, 0, NI_NUMERICHOST)) != 0) {
            log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
        }

        if (header->version == UFTP_VER_NUM) {
            version = header->version;
            group = find_group(ntohl(header->group_id), header->group_inst);
        } else {
            log1(0, 0, 0, "Invalid message from %s: not uftp packet "
                          "or invalid version", rxname);
            continue;
        }
        if (packetlen < sizeof(struct uftp_h) + 4) {
            log1(0, 0, 0, "Invalid packet size from %s: %d", rxname, packetlen);
            continue;
        }

        txseq = htons(header->seq);
        // A KEY_INFO or ABORT could come from a proxy, so don't check the seq
        // TODO: need to account for these in the loss history
        if ((group != NULL) && (header->func != KEYINFO) &&
                (header->func != ABORT)) {
            if ((int16_t)(group->max_txseq - txseq) > MAXMISORDER) {
                glog3(group, "seq out of range, dropping");
                continue;
            }
            if (group->cc_type != CC_NONE) {
                ecn = ((tos & 0x3) == 3);
                update_loss_history(group, txseq, packetlen, ecn);
            } else if ((int16_t)(txseq - group->max_txseq) > 0) {
                group->max_txseq = txseq;
            }
        }

        if ((header->func == ENCRYPTED) && (group != NULL) &&
                (group->keytype != KEY_NONE)) {
            if (group->phase == PHASE_REGISTERED) {
                glog1(group, "Got encrypted packet from %s "
                             "but keys not established", rxname);
            }

            if (!validate_and_decrypt(buf, packetlen, &decrypted, &decryptlen,
                    group->keytype, group->groupkey, group->groupsalt,
                    group->ivlen, group->hashtype, group->grouphmackey,
                    group->hmaclen, group->sigtype, group->keyextype,
                    group->server_pubkey, group->server_pubkeylen)) {
                glog1(group, "Rejecting message from %s: "
                             "decrypt/validate failed", rxname);
                continue;
            }
            func = (uint8_t *)decrypted;
            message = decrypted;
            meslen = decryptlen;
        } else {
            if ((group != NULL) && (group->keytype != KEY_NONE) &&
                    ((header->func == FILEINFO) || (header->func == FILESEG) ||
                     (header->func == DONE) || (header->func == DONE_CONF) ||
                     ((header->func == ABORT) &&
                         (group->phase != PHASE_REGISTERED)))) {
                glog1(group, "Rejecting %s message from %s: not encrypted",
                             func_name(header->func), rxname);
                continue;
            }
            func = (uint8_t *)&header->func;
            message = buf + sizeof(struct uftp_h);
            meslen = packetlen - sizeof(struct uftp_h);
        }

        if (group != NULL) {
            new_grtt = unquantize_grtt(header->grtt);
            if (fabs(new_grtt - group->grtt) > 0.001) {
                group->grtt = new_grtt;
                set_timeout(group, 1);
            }
            group->gsize = unquantize_gsize(header->gsize);
            glog5(group, "grtt: %.3f", group->grtt);
        }

        if (header->func == PROXY_KEY) {
            handle_proxy_key(&src, message, meslen);
            continue;
        }
        if (header->func == HB_RESP) {
            handle_hb_response(listener, &src, message, meslen, hb_hosts,
                               hbhost_count, privkey[0], privkey_type[0], uid);
            continue;
        }
        if (header->func == ANNOUNCE) {
            // Ignore any ANNOUNCE for a group we're already handling
            if (group == NULL) {
                handle_announce(&src, buf, packetlen, rxtime);
            } else if (group->phase == PHASE_MIDGROUP) {
                // Make sure we don't time out while waiting for other
                // clients to register with the server.
                set_timeout(group, 0);
            }
        } else {
            if (group == NULL) {
                // group / file ID not in list
                continue;
            }
            if (group->version != version) {
                glog1(group, "Version mismatch");
                continue;
            }
            if (group->src_id != header->src_id) {
                glog1(group, "Source ID mismatch");
                continue;
            }
            if (*func == ABORT) {
                handle_abort(group, message, meslen);
                continue;
            }
            switch (group->phase) {
            case PHASE_REGISTERED:
                if (group->keytype != KEY_NONE) {
                    if (*func == KEYINFO) {
                        handle_keyinfo(group, message, meslen, header->src_id);
                    } else {
                        glog1(group, "Expected KEYINFO, got %s",
                                     func_name(*func));
                    }
                } else if (group->keytype == KEY_NONE) {
                    if (*func == REG_CONF) {
                        handle_regconf(group, message, meslen);
                    } else if (*func == FILEINFO) {
                        handle_fileinfo(group, message, meslen, rxtime);
                    } else {
                        glog1(group, "Expected REG_CONF, got %s",
                                     func_name(*func));
                    }
                }
                break;
            case PHASE_MIDGROUP:
                if (*func == FILEINFO) {
                    handle_fileinfo(group, message, meslen, rxtime);
                } else if (*func == KEYINFO) {
                    handle_keyinfo(group, message, meslen, header->src_id);
                } else if (*func == DONE) {
                    handle_done(group, message, meslen);
                } else {
                    // Other clients may be still getting earlier files or
                    // setting up, so silently ignore anything unexpected
                    // and reset the timeout.
                    set_timeout(group, 0);
                }
                break;
            case PHASE_RECEIVING:
                if (*func == FILEINFO) {
                    handle_fileinfo(group, message, meslen, rxtime);
                } else if (*func == FILESEG) {
                    handle_fileseg(group, message, meslen, txseq);
                } else if (*func == DONE) {
                    handle_done(group, message, meslen);
                } else if (*func == CONG_CTRL) {
                    handle_cong_ctrl(group, message, meslen, rxtime);
                }
                break;
            case PHASE_COMPLETE:
                if (*func == DONE_CONF) {
                    handle_done_conf(group, message, meslen);
                }
                break;
            }
        }
    }
}