Blame testsuite/clknetsim/client_fuzz.c

Packit 9c3e7e
/*
Packit 9c3e7e
 * Copyright (C) 2015  Miroslav Lichvar <mlichvar@redhat.com>
Packit 9c3e7e
 * 
Packit 9c3e7e
 * This program is free software; you can redistribute it and/or modify
Packit 9c3e7e
 * it under the terms of the GNU General Public License as published by
Packit 9c3e7e
 * the Free Software Foundation; either version 2 of the License, or
Packit 9c3e7e
 * (at your option) any later version.
Packit 9c3e7e
 * 
Packit 9c3e7e
 * This program is distributed in the hope that it will be useful,
Packit 9c3e7e
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 9c3e7e
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 9c3e7e
 * GNU General Public License for more details.
Packit 9c3e7e
 * 
Packit 9c3e7e
 * You should have received a copy of the GNU General Public License
Packit 9c3e7e
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 9c3e7e
 */
Packit 9c3e7e
Packit 9c3e7e
/* This is a minimal replacement for the clknetsim server to allow fuzz
Packit 9c3e7e
   testing. There is no clock control or networking. When the time reaches
Packit 9c3e7e
   fuzz_start, a packet read from stdin is forwarded to the fuzz_port port of
Packit 9c3e7e
   the client, and the client is terminated. Packets sent by the client from
Packit 9c3e7e
   the port are written to stdout. */
Packit 9c3e7e
Packit 9c3e7e
enum {
Packit 9c3e7e
	FUZZ_MODE_DISABLED = 0,
Packit 9c3e7e
	FUZZ_MODE_ONESHOT = 1,
Packit 9c3e7e
	FUZZ_MODE_BURST = 2,
Packit 9c3e7e
	FUZZ_MODE_REPLY = 3,
Packit 9c3e7e
	FUZZ_MODE_NONE = 4,
Packit 9c3e7e
};
Packit 9c3e7e
Packit 9c3e7e
static int fuzz_mode;
Packit 9c3e7e
static int fuzz_port;
Packit 9c3e7e
static double fuzz_start;
Packit 9c3e7e
Packit 9c3e7e
static int fuzz_init(void) {
Packit 9c3e7e
	const char *env;
Packit 9c3e7e
Packit 9c3e7e
	env = getenv("CLKNETSIM_FUZZ_MODE");
Packit 9c3e7e
	if (!env)
Packit 9c3e7e
		return 0;
Packit 9c3e7e
Packit 9c3e7e
	fuzz_mode = atoi(env);
Packit 9c3e7e
Packit 9c3e7e
	if (fuzz_mode == FUZZ_MODE_DISABLED)
Packit 9c3e7e
		return 0;
Packit 9c3e7e
Packit 9c3e7e
	if (fuzz_mode < FUZZ_MODE_ONESHOT || fuzz_mode > FUZZ_MODE_NONE) {
Packit 9c3e7e
		fprintf(stderr, "clknetsim: unknown fuzz mode.\n");
Packit 9c3e7e
		exit(1);
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	env = getenv("CLKNETSIM_FUZZ_PORT");
Packit 9c3e7e
	if (!env) {
Packit 9c3e7e
		fprintf(stderr, "clknetsim: CLKNETSIM_FUZZ_PORT variable not set.\n");
Packit 9c3e7e
		exit(1);
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	fuzz_port = atoi(env);
Packit 9c3e7e
Packit 9c3e7e
	env = getenv("CLKNETSIM_FUZZ_START");
Packit 9c3e7e
	fuzz_start = env ? atof(env) : 0.1;
Packit 9c3e7e
Packit 9c3e7e
	return 1;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static int fuzz_read_packet(char *data, int maxlen) {
Packit 9c3e7e
	int len;
Packit 9c3e7e
	uint16_t slen;
Packit 9c3e7e
Packit 9c3e7e
	if (fuzz_mode > FUZZ_MODE_ONESHOT) {
Packit 9c3e7e
		if (fread(&slen, 1, sizeof (slen), stdin) != sizeof (slen))
Packit 9c3e7e
			return 0;
Packit 9c3e7e
		len = ntohs(slen);
Packit 9c3e7e
		if (len > maxlen)
Packit 9c3e7e
			len = maxlen;
Packit 9c3e7e
	} else {
Packit 9c3e7e
		len = maxlen;
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	return fread(data, 1, len, stdin);
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static void fuzz_write_packet(const char *data, int len) {
Packit 9c3e7e
	uint16_t slen;
Packit 9c3e7e
Packit 9c3e7e
	if (fuzz_mode > FUZZ_MODE_ONESHOT) {
Packit 9c3e7e
		slen = htons(len);
Packit 9c3e7e
		fwrite(&slen, 1, sizeof (slen), stdout);
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	fwrite(data, 1, len, stdout);
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
static void fuzz_process_reply(int request_id, const union Request_data *request, union Reply_data *reply, int replylen) {
Packit 9c3e7e
	static double network_time = 0.0;
Packit 9c3e7e
	static int received = 0;
Packit 9c3e7e
	static int sent = 0;
Packit 9c3e7e
	static int dst_port = 0;
Packit 9c3e7e
	static int packet_len = 0;
Packit 9c3e7e
	static char packet[MAX_PACKET_SIZE];
Packit 9c3e7e
Packit 9c3e7e
	if (reply)
Packit 9c3e7e
		memset(reply, 0, replylen);
Packit 9c3e7e
Packit 9c3e7e
	switch (request_id) {
Packit 9c3e7e
		case REQ_GETTIME:
Packit 9c3e7e
			reply->gettime.real_time = network_time;
Packit 9c3e7e
			reply->gettime.monotonic_time = network_time;
Packit 9c3e7e
			reply->gettime.network_time = network_time;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_SELECT:
Packit 9c3e7e
			if (fuzz_mode == FUZZ_MODE_NONE) {
Packit 9c3e7e
				reply->select.ret = REPLY_SELECT_TIMEOUT;
Packit 9c3e7e
				return;
Packit 9c3e7e
			}
Packit 9c3e7e
Packit 9c3e7e
			if (!packet_len && (!received || fuzz_mode != FUZZ_MODE_ONESHOT))
Packit 9c3e7e
				packet_len = fuzz_read_packet(packet, sizeof (packet));
Packit 9c3e7e
Packit 9c3e7e
			if (!packet_len) {
Packit 9c3e7e
				reply->select.ret = REPLY_SELECT_TERMINATE;
Packit 9c3e7e
			} else {
Packit 9c3e7e
				if (fuzz_mode == FUZZ_MODE_REPLY) {
Packit 9c3e7e
					if (sent > received) {
Packit 9c3e7e
						reply->select.ret = REPLY_SELECT_NORMAL;
Packit 9c3e7e
					} else {
Packit 9c3e7e
						network_time += request->select.timeout;
Packit 9c3e7e
						reply->select.ret = REPLY_SELECT_TIMEOUT;
Packit 9c3e7e
					}
Packit 9c3e7e
				} else {
Packit 9c3e7e
					if (network_time < fuzz_start && !sent) {
Packit 9c3e7e
						network_time += request->select.timeout;
Packit 9c3e7e
						if (network_time >= fuzz_start) {
Packit 9c3e7e
							network_time = fuzz_start;
Packit 9c3e7e
							reply->select.ret = REPLY_SELECT_NORMAL;
Packit 9c3e7e
						} else {
Packit 9c3e7e
							reply->select.ret = REPLY_SELECT_TIMEOUT;
Packit 9c3e7e
						}
Packit 9c3e7e
					} else {
Packit 9c3e7e
						reply->select.ret = REPLY_SELECT_NORMAL;
Packit 9c3e7e
					}
Packit 9c3e7e
				}
Packit 9c3e7e
			}
Packit 9c3e7e
Packit 9c3e7e
			reply->select.subnet = 0;
Packit 9c3e7e
			reply->select.dst_port = dst_port ? dst_port : fuzz_port;
Packit 9c3e7e
			reply->select.time.real_time = network_time;
Packit 9c3e7e
			reply->select.time.monotonic_time = network_time;
Packit 9c3e7e
			reply->select.time.network_time = network_time;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_SEND:
Packit 9c3e7e
			if (request->send.to != 1)
Packit 9c3e7e
				break;
Packit 9c3e7e
Packit 9c3e7e
			if (fuzz_mode == FUZZ_MODE_REPLY) {
Packit 9c3e7e
				if (request->send.dst_port != fuzz_port)
Packit 9c3e7e
					break;
Packit 9c3e7e
				dst_port = request->send.src_port;
Packit 9c3e7e
			} else if (request->send.src_port != fuzz_port)
Packit 9c3e7e
				break;
Packit 9c3e7e
Packit 9c3e7e
			fuzz_write_packet(request->send.data, request->send.len);
Packit 9c3e7e
			sent++;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_RECV:
Packit 9c3e7e
			network_time += 1e-1;
Packit 9c3e7e
			reply->recv.subnet = 0;
Packit 9c3e7e
			reply->recv.from = 1;
Packit 9c3e7e
			reply->recv.src_port = fuzz_port;
Packit 9c3e7e
			reply->recv.dst_port = dst_port ? dst_port : fuzz_port;
Packit 9c3e7e
			memcpy(reply->recv.data, packet, packet_len);
Packit 9c3e7e
			reply->recv.len = packet_len;
Packit 9c3e7e
			received++;
Packit 9c3e7e
			packet_len = 0;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_SETTIME:
Packit 9c3e7e
			network_time = request->settime.time;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_ADJTIME:
Packit 9c3e7e
		case REQ_GETREFSAMPLE:
Packit 9c3e7e
		case REQ_GETREFOFFSETS:
Packit 9c3e7e
		case REQ_DEREGISTER:
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_ADJTIMEX:
Packit 9c3e7e
			reply->adjtimex.timex.tick = 10000;
Packit 9c3e7e
			break;
Packit 9c3e7e
		case REQ_REGISTER:
Packit 9c3e7e
		default:
Packit 9c3e7e
			assert(0);
Packit 9c3e7e
	}
Packit 9c3e7e
}