penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

fuzz/fuzz_proxy_v2_build.c

raw ยท 2912 bytes

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "../src/proxy_proto_v2.h"

static uint16_t read_u16(const uint8_t *p)
{
	return ((uint16_t)p[0] << 8) | (uint16_t)p[1];
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
	if (size < 1) {
		return 0;
	}

	struct sockaddr_storage src;
	struct sockaddr_storage dst;
	unsigned char buf[128];
	size_t out_len = 999999;

	memset(&src, 0, sizeof(src));
	memset(&dst, 0, sizeof(dst));
	memset(buf, 0xa5, sizeof(buf));

	int family = data[0] % 3;
	int sock_type = (size > 1 && (data[1] & 1)) ? SOCK_DGRAM : SOCK_STREAM;

	if (family == 0) {
		struct sockaddr_in *s = (struct sockaddr_in *)&src;
		struct sockaddr_in *d = (struct sockaddr_in *)&dst;

		s->sin_family = AF_INET;
		d->sin_family = AF_INET;

		if (size > 2) {
			memcpy(&s->sin_addr, data + 2, size - 2 > 4 ? 4 : size - 2);
		}
		if (size > 6) {
			memcpy(&d->sin_addr, data + 6, size - 6 > 4 ? 4 : size - 6);
		}
		if (size > 10) {
			memcpy(&s->sin_port, data + 10, size - 10 > 2 ? 2 : size - 10);
		}
		if (size > 12) {
			memcpy(&d->sin_port, data + 12, size - 12 > 2 ? 2 : size - 12);
		}

		size_t buf_len = size % sizeof(buf);

		int rc = proxy_v2_build(
			buf,
			buf_len,
			(const struct sockaddr *)s,
			sizeof(*s),
			(const struct sockaddr *)d,
			sizeof(*d),
			sock_type,
			&out_len
		);

		if (rc == 0) {
			if (out_len > buf_len) __builtin_trap();
			if (out_len != 28) __builtin_trap();
			if (memcmp(buf, PROXY_V2_SIG, 12) != 0) __builtin_trap();
			if (read_u16(buf + 14) != 12) __builtin_trap();
		}
	} else if (family == 1) {
		struct sockaddr_in6 *s = (struct sockaddr_in6 *)&src;
		struct sockaddr_in6 *d = (struct sockaddr_in6 *)&dst;

		s->sin6_family = AF_INET6;
		d->sin6_family = AF_INET6;

		if (size > 2) {
			memcpy(&s->sin6_addr, data + 2, size - 2 > 16 ? 16 : size - 2);
		}
		if (size > 18) {
			memcpy(&d->sin6_addr, data + 18, size - 18 > 16 ? 16 : size - 18);
		}
		if (size > 34) {
			memcpy(&s->sin6_port, data + 34, size - 34 > 2 ? 2 : size - 34);
		}
		if (size > 36) {
			memcpy(&d->sin6_port, data + 36, size - 36 > 2 ? 2 : size - 36);
		}

		size_t buf_len = size % sizeof(buf);

		int rc = proxy_v2_build(
			buf,
			buf_len,
			(const struct sockaddr *)s,
			sizeof(*s),
			(const struct sockaddr *)d,
			sizeof(*d),
			sock_type,
			&out_len
		);

		if (rc == 0) {
			if (out_len > buf_len) __builtin_trap();
			if (out_len != 52) __builtin_trap();
			if (memcmp(buf, PROXY_V2_SIG, 12) != 0) __builtin_trap();
			if (read_u16(buf + 14) != 36) __builtin_trap();
		}
	} else {
		struct sockaddr *s = (struct sockaddr *)&src;
		struct sockaddr *d = (struct sockaddr *)&dst;

		s->sa_family = AF_UNSPEC;
		d->sa_family = AF_UNSPEC;

		(void)proxy_v2_build(
			buf,
			size % sizeof(buf),
			s,
			sizeof(*s),
			d,
			sizeof(*d),
			sock_type,
			&out_len
		);
	}

	return 0;
}