Skip to content
Snippets Groups Projects
run_system_call.c 225 KiB
Newer Older
/*
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: ncardwell@google.com (Neal Cardwell)
 *
 * A module to execute a system call from a test script.
 */

#include "run_system_call.h"

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#if defined(__FreeBSD__)
#include <pthread_np.h>
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#if defined(__FreeBSD__) || defined(__NetBSD__)
#include <stddef.h>
#endif
#if defined(__FreeBSD__)
#include <kvm.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <libprocstat.h>
#endif
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/uio.h>
#if defined(linux)
#include <sys/sendfile.h>
#endif
#if defined(__APPLE__)
#include <pthread.h>
#include <mach/mach.h>
#endif
#include <time.h>
#include <unistd.h>
#include "assert.h"
#include "logging.h"
#include "run.h"
#include "script.h"

static int to_live_fd(struct state *state, int script_fd, int *live_fd,
		      char **error);
Michael Tüxen's avatar
Michael Tüxen committed
#if defined(linux)
struct sctp_tlv {
        u16 sn_type;
        u16 sn_flags;
        u32 sn_length;
};
#endif
#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
#if defined(MSG_NOTIFICATION)
static int check_sctp_notification(struct socket *socket, struct iovec *iov, struct expression *iovec_expr,
static int parse_expression_to_sctp_initmsg(struct expression *expr, struct sctp_initmsg *init,
				            char **error);
static int parse_expression_to_sctp_sndrcvinfo(struct expression *expr, struct sctp_sndrcvinfo *info,
					       bool send, char **error);
#endif
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV))
static int parse_expression_to_sctp_sndinfo(struct expression *expr, struct sctp_sndinfo *info,
				            char **error);
static int parse_expression_to_sctp_prinfo(struct expression *expr, struct sctp_prinfo *info,
				            char **error);
static int parse_expression_to_sctp_authinfo(struct expression *expr, struct sctp_authinfo *info,
				             char **error);
#endif
#if defined(SCTP_DEFAULT_SNDINFO) || defined(SCTP_SNDINFO)
static int check_sctp_sndinfo(struct sctp_sndinfo_expr *expr, struct sctp_sndinfo *sctp_sndinfo,
			      char **error);
#endif
#if defined(SCTP_INITMSG) || defined(SCTP_INIT)
static int check_sctp_initmsg(struct sctp_initmsg_expr *expr, struct sctp_initmsg *sctp_initmsg,
			      char **error);
#endif
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP))
static int check_sctp_extrcvinfo(struct sctp_extrcvinfo_expr *expr, struct sctp_extrcvinfo *sctp_info,
				 char **error);
#endif
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV))
static int check_sctp_rcvinfo(struct sctp_rcvinfo_expr *expr, struct sctp_rcvinfo *sctp_rcvinfo,
				 char** error);
#endif
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV))
static int check_sctp_nxtinfo(struct sctp_nxtinfo_expr *expr, struct sctp_nxtinfo *sctp_nxtinfo,
hoelscher's avatar
hoelscher committed
			      char **error);
#endif
#if defined(linux) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr,
				 struct sctp_sndrcvinfo *sctp_sndrcvinfo,
				 char** error);
#endif
/* Provide a wrapper for the Linux gettid() system call
 * (glibc only provides it in version 2.30 or higher).
 */
#if (__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 30))
static pid_t gettid(void)
{
	return syscall(__NR_gettid);
}

/* Read a whole file into the given buffer of the given length. */
static void read_whole_file(const char *path, char *buffer, int max_bytes)
{
	int fd = open(path, O_RDONLY);
	if (fd < 0)
		die_perror("open");

	int bytes = read(fd, buffer, max_bytes);
	if (bytes < 0)
		die_perror("read");
	else if (bytes == max_bytes)
		die("%s file too large to read\n", path);

	if (close(fd) < 0)
		die_perror("close");
}

/* Return true iff the given thread is sleeping. */
static bool is_thread_sleeping(pid_t process_id, pid_t thread_id)
{
	/* Read the entire thread state file, using the buffer size ps uses. */
	char *proc_path = NULL;
	asprintf(&proc_path, "/proc/%d/task/%d/stat", process_id, thread_id);
	const int STATE_BUFFER_BYTES = 1023;
	char *state = calloc(STATE_BUFFER_BYTES, 1);
	read_whole_file(proc_path, state, STATE_BUFFER_BYTES - 1);
	state[STATE_BUFFER_BYTES - 1] = '\0';

	/* Parse the thread state from the third space-delimited field. */
	const int THREAD_STATE_INDEX = 3;
	const char *field = state;
	int i = 0;
	for (i = 0; i < THREAD_STATE_INDEX - 1; i++) {
		field = strchr(field, ' ');
		if (field == NULL)
			die("unable to parse %s\n", proc_path);
		++field;
	}
	bool is_sleeping = (field[0] == 'S');

	free(proc_path);
	free(state);

	return is_sleeping;
}
#elif defined(__FreeBSD__)
static bool is_thread_sleeping(pid_t process_id, int thread_id)
{
	struct procstat *procstat;
	struct kinfo_proc *kinfo_proc;
	unsigned int i, count;
	bool is_sleeping;

	procstat = procstat_open_sysctl();
	if (procstat == NULL) {
		die("procstat_open_sysctl() failed\n");
	}
	/* Get the information for all threads belonging to the given process.
	 * The number of entries (one for each thread) is returned in count.
	 */
	kinfo_proc = procstat_getprocs(procstat,
	                               KERN_PROC_PID | KERN_PROC_INC_THREAD,
	                               process_id, &count);
	if (kinfo_proc == NULL) {
		die("procstat_getprocs() failed\n");
	}
	/* Do a linear search to the entry for the requested thread.
	 * Since we have only two threads, that's OK performance-wise.
	 */
	for (i = 0; i < count; i++) {
		if (kinfo_proc[i].ki_tid == thread_id)
			break;
	}
	/* Die, if we can't find the entry for the requested thread. */
	if (i == count)
		die("unable to get state of thread %d\n", thread_id);
	is_sleeping = (kinfo_proc[i].ki_stat == SSLEEP);
	/* Cleanup the allocated data structures. */
	procstat_freeprocs(procstat, kinfo_proc);
	procstat_close(procstat);
	return is_sleeping;
}
Michael Tüxen's avatar
Michael Tüxen committed
#elif defined(__APPLE__)
static bool is_thread_sleeping(pid_t process_id, mach_port_t port)
	thread_info_data_t thinfo;
	thread_basic_info_t basic_info_th;
	mach_msg_type_number_t thread_info_count;
	kern_return_t kr;
	bool is_sleeping;

	thread_info_count = THREAD_INFO_MAX;
	kr = thread_info(port, THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
	if (kr != KERN_SUCCESS) {
		die("task_threads(): %s\n", mach_error_string(kr));
	}
	basic_info_th = (thread_basic_info_t)thinfo;
	switch (basic_info_th->run_state) {
	case TH_STATE_RUNNING:
		DEBUGP("run_state = TH_STATE_RUNNING\n");
		is_sleeping = false;
		break;
	case TH_STATE_STOPPED:
		DEBUGP("run_state = TH_STATE_STOPPED\n");
		is_sleeping = false;
		break;
	case TH_STATE_WAITING:
		DEBUGP("run_state = TH_STATE_WAITING\n");
		is_sleeping = true;
		break;
	case TH_STATE_UNINTERRUPTIBLE:
		DEBUGP("run_state = TH_STATE_UNINTERRUPTIBLE\n");
		is_sleeping = true;
		break;
	case TH_STATE_HALTED:
		DEBUGP("run_state = TH_STATE_HALTED\n");
		is_sleeping = false;
		break;
	default:
		die("Unknown thread state: %d\n", basic_info_th->run_state);
		is_sleeping = false;
		break;
	}
	return is_sleeping;
#else
static bool is_thread_sleeping(pid_t process_id, int thread_id)
{
	die("is_thread_sleeping not implemented on this platform\n");
	return true;
}
#endif

/* Returns number of expressions in the list. */
static int expression_list_length(struct expression_list *list)
{
	int count = 0;
	while (list != NULL) {
		list = list->next;
		++count;
	}
	return count;
}

static int get_arg_count(struct expression_list *args)
{
	return expression_list_length(args);
}
/* Verify that the expression list has the expected number of
 * expressions. Returns STATUS_OK on success; on failure returns
 * STATUS_ERR and sets error message.
 */
static int check_arg_count(struct expression_list *args, int expected,
			   char **error)
{
	assert(expected >= 0);
	int actual = get_arg_count(args);
	if (actual != expected) {
		asprintf(error, "Expected %d args but got %d", expected,
			 actual);
		return STATUS_ERR;
	}
	return STATUS_OK;
}

/* Returns the argument with the given index. Returns the argument on
 * success; on failure returns NULL and sets error message.
 */
static struct expression *get_arg(struct expression_list *args,
				   int index, char **error)
{
	assert(index >= 0);
	int current = 0;
	while ((args != NULL) && (current < index)) {
		args = args->next;
		++current;
	}
	if ((args != NULL) && (current == index)) {
		if (!args->expression)
			asprintf(error, "Unknown expression at index %d",
				 index);
		return args->expression;
	} else {
		asprintf(error, "Argument list too short");
		return NULL;
	}
}

/* Return STATUS_OK if the expression is of the expected
 * type. Otherwise fill in the error with a human-readable error
 * message about the mismatch and return STATUS_ERR.
 */
static int check_type(struct expression *expression,
		      enum expression_t expected_type,
		      char **error)
{
	if (expression->type == expected_type) {
		return STATUS_OK;
	} else {
		asprintf(error, "Bad type; actual: %s expected: %s",
			 expression_type_to_string(expression->type),
			 expression_type_to_string(expected_type));
		return STATUS_ERR;
	}
}

/* Sets the value from the expression argument, checking that it is a
 * valid socklen_t, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_socklen_t(struct expression *expression,
		         socklen_t *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if (expression->value.num < 0) {
		asprintf(error,
			 "Value out of range for socklen_t: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}

#if defined(linux) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
Michael Tüxen's avatar
Michael Tüxen committed
/* Sets the value from the expression argument, checking that it is a
 * valid size_t, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_size_t(struct expression *expression,
		      size_t *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if (expression->value.num < 0) {
		asprintf(error,
			 "Value out of range for size_t: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}
#if defined(__FreeBSD__)
/* Sets the value from the expression argument, checking that it is a
 * valid off_t, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_off_t(struct expression *expression,
		     off_t *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if (expression->value.num < 0) {
		asprintf(error,
			 "Value out of range for off_t: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}
#endif

#if defined(linux) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
/* Sets the value from the expression argument, checking that it is a
 * valid sctp_assoc_t, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_sctp_assoc_t(struct expression *expression,
			    sctp_assoc_t *value, char **error)
{
	if (expression->type == EXPR_ELLIPSIS) {
		*value = 0;
	} else {
		if (check_type(expression, EXPR_INTEGER, error))
			return STATUS_ERR;
		if (expression->value.num < 0) {
			asprintf(error,
				 "Value out of range for sctp_assoc_t: %lld",
				 expression->value.num);
			return STATUS_ERR;
		}
		*value = expression->value.num;
	}
	return STATUS_OK;
}
#endif

/* Sets the value from the expression argument, checking that it is a
 * valid u32, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_u32(struct expression *expression,
		   u32 *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > UINT32_MAX) ||
	    (expression->value.num < 0)) {
		asprintf(error,
			 "Value out of range for 32-bit unsigned integer: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}

/* Sets the value from the expression argument, checking that it is a
 * valid s32 or u32, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_s32(struct expression *expression,
		   s32 *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > UINT_MAX) ||
	    (expression->value.num < INT_MIN)) {
			 "Value out of range for 32-bit integer: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}

#if defined(SCTP_STATUS) || defined(SCTP_PEER_ADDR_PARAMS) || defined(SCTP_SS_VALUE)
/* Sets the value from the expression argument, checking that it is a
 * valid u16, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_u16(struct expression *expression,
		   u16 *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > UINT16_MAX) ||
	    (expression->value.num < 0)) {
		asprintf(error,
			 "Value out of range for 16-bit unsigned integer: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}
#endif
/* Sets the value from the expression argument, checking that it is a
 * valid s16, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_s16(struct expression *expression,
		   s16 *value, char **error)
{
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > INT16_MAX) ||
		(expression->value.num < INT16_MIN)) {
		asprintf(error,
			"Value out of range for 16-bit integer: %lld",
			expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
}
#if defined(SCTP_PEER_ADDR_PARAMS)
/* Sets the value from the expression argument, checking that it is a
 * valid u8, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_u8(struct expression *expression,
		  u8 *value, char **error)
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > UINT8_MAX) ||
		(expression->value.num < 0)) {
		asprintf(error,
			 "Value out of range for 8-bit unsigned integer: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
#endif
/* Sets the value from the expression argument, checking that it is a
 * valid s8, and matches the expected type. Returns STATUS_OK on
 * success; on failure returns STATUS_ERR and sets error message.
 */
static int get_s8(struct expression *expression,
		  s8 *value, char **error)
	if (check_type(expression, EXPR_INTEGER, error))
		return STATUS_ERR;
	if ((expression->value.num > INT8_MAX) ||
		(expression->value.num < INT8_MIN)) {
		asprintf(error,
			 "Value out of range for 8-bit integer: %lld",
			 expression->value.num);
		return STATUS_ERR;
	}
	*value = expression->value.num;
	return STATUS_OK;
/* Return the value of the argument with the given index, and verify
 * that it has the expected type.
 */
static int s32_arg(struct expression_list *args,
		   int index, s32 *value, char **error)
{
	struct expression *expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	return get_s32(expression, value, error);
}

/* Return the value of the argument with the given index, and verify
 * that it has the expected type: a list with a single integer.
 */
static int s32_bracketed_arg(struct expression_list *args,
			     int index, s32 *value, char **error)
{
	struct expression_list *list;
	struct expression *expression;

	expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	if (check_type(expression, EXPR_LIST, error))
		return STATUS_ERR;
	list = expression->value.list;
	if (expression_list_length(list) != 1) {
		asprintf(error,
			 "Expected [<integer>] but got multiple elements");
		return STATUS_ERR;
	}
	return get_s32(list->expression, value, error);
}

/* Return the value of the argument with the given index, and verify
 * that it has the expected type: a list with a single integer.
 */
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV))
static int u32_bracketed_arg(struct expression_list *args,
			     int index, u32 *value, char **error)
{
	struct expression_list *list;
	struct expression *expression;

	expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	if (check_type(expression, EXPR_LIST, error))
		return STATUS_ERR;
	list = expression->value.list;
	if (expression_list_length(list) != 1) {
		asprintf(error,
			 "Expected [<integer>] but got multiple elements");
		return STATUS_ERR;
	}
	return get_u32(list->expression, value, error);
}
#if defined(__FreeBSD__)
static int off_t_bracketed_arg(struct expression_list *args,
			       int index, off_t *value, char **error)
{
	struct expression_list *list;
	struct expression *expression;

	expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	if (check_type(expression, EXPR_LIST, error))
		return STATUS_ERR;
	list = expression->value.list;
	if (expression_list_length(list) != 1) {
		asprintf(error,
			 "Expected [<integer>] but got multiple elements");
		return STATUS_ERR;
	}
	return get_off_t(list->expression, value, error);
}
static int socklen_t_bracketed_arg(struct expression_list *args,
				   int index, socklen_t *value, char **error)
{
	struct expression_list *list;
	struct expression *expression;

	expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	if (check_type(expression, EXPR_LIST, error))
		return STATUS_ERR;
	list = expression->value.list;
	if (expression_list_length(list) != 1) {
		asprintf(error,
			 "Expected [<integer>] but got multiple elements");
		return STATUS_ERR;
	}
	return get_socklen_t(list->expression, value, error);
}

/* Return STATUS_OK iff the argument with the given index is an
 * ellipsis (...).
 */
static int ellipsis_arg(struct expression_list *args, int index, char **error)
{
	struct expression *expression = get_arg(args, index, error);
	if (expression == NULL)
		return STATUS_ERR;
	if (check_type(expression, EXPR_ELLIPSIS, error))
		return STATUS_ERR;
	return STATUS_OK;
}

#if defined(SCTP_GET_PEER_ADDR_INFO) || defined(SCTP_PEER_ADDR_PARAMS) || defined(SCTP_SET_PEER_PRIMARY_ADDR)
/* Return STATUS_OK if the argument in from type sockaddr_in or
hoelscher's avatar
hoelscher committed
 * sockaddr_in6
 */
Michael Tüxen's avatar
Michael Tüxen committed
static int get_sockstorage_arg(struct expression *arg, struct sockaddr_storage *addr, int live_fd)
hoelscher's avatar
hoelscher committed
{
	if (arg->type == EXPR_ELLIPSIS) {
Michael Tüxen's avatar
Michael Tüxen committed
		socklen_t len;
Michael Tüxen's avatar
Michael Tüxen committed
		len = (socklen_t)sizeof(struct sockaddr_storage);
		if (getpeername(live_fd, (struct sockaddr *)addr, &len)) {
hoelscher's avatar
hoelscher committed
			return STATUS_ERR;
		}
	} else if (arg->type == EXPR_SOCKET_ADDRESS_IPV4) {
Michael Tüxen's avatar
Michael Tüxen committed
		memcpy(addr, arg->value.socket_address_ipv4, sizeof(struct sockaddr_in));
hoelscher's avatar
hoelscher committed
	} else if (arg->type == EXPR_SOCKET_ADDRESS_IPV6) {
Michael Tüxen's avatar
Michael Tüxen committed
		memcpy(addr, arg->value.socket_address_ipv6, sizeof(struct sockaddr_in6));
hoelscher's avatar
hoelscher committed
	} else {
		return STATUS_ERR;
	}
	return STATUS_OK;
}
#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
static int check_sockaddr(struct expression *sockaddr_expr, struct sockaddr *live_addr, char **error) {

	if (sockaddr_expr->type != EXPR_ELLIPSIS) {
		struct sockaddr *script_addr;
		if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV4) {
			script_addr = (struct sockaddr*)sockaddr_expr->value.socket_address_ipv4;
		} else if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV6) {
			script_addr = (struct sockaddr*)sockaddr_expr->value.socket_address_ipv6;
		} else {
			asprintf(error, "Bad type for sockaddr");
			return STATUS_ERR;
		}
		if (script_addr->sa_family != live_addr->sa_family) {
			asprintf(error, "sockaddr sa_family expected: %d actual: %d",
				 script_addr->sa_family, live_addr->sa_family);
			return STATUS_ERR;
		}
		switch(script_addr->sa_family) {
		case AF_INET:
			{
				struct sockaddr_in *script_sockaddr = (struct sockaddr_in*)script_addr;
				struct sockaddr_in *live_sockaddr = (struct sockaddr_in*)live_addr;
				if (live_sockaddr->sin_port != script_sockaddr->sin_port) {
					asprintf(error, "sockaddr_in from.sinport. expected: %d actual %d",
						ntohs(script_sockaddr->sin_port), ntohs(live_sockaddr->sin_port));
					return STATUS_ERR;
				}
				if (live_sockaddr->sin_addr.s_addr != script_sockaddr->sin_addr.s_addr) {
					int len = strnlen(inet_ntoa(script_sockaddr->sin_addr), 16);
					char *expected_addr = malloc(sizeof(char) * len);
					memcpy(expected_addr, inet_ntoa(script_sockaddr->sin_addr), len);
					asprintf(error, "sockaddr_in from.sin_addr. expected: %s actual %s",
						expected_addr, inet_ntoa(live_sockaddr->sin_addr));
					free(expected_addr);
					return STATUS_ERR;
				}
			}
			break;
		case AF_INET6:
			{
				struct sockaddr_in6 *script_sockaddr = (struct sockaddr_in6*)script_addr;
				struct sockaddr_in6 *live_sockaddr = (struct sockaddr_in6*)live_addr;
				if (live_sockaddr->sin6_port != script_sockaddr->sin6_port) {
					asprintf(error, "sockaddr_in6 from.sinport. expected: %d actual %d",
						ntohs(script_sockaddr->sin6_port), ntohs(live_sockaddr->sin6_port));
					return STATUS_ERR;
				}
				if (live_sockaddr->sin6_addr.s6_addr != script_sockaddr->sin6_addr.s6_addr) {
					char expected_addr[INET6_ADDRSTRLEN];
					char live_addr[INET6_ADDRSTRLEN];
					inet_ntop(AF_INET6, &script_sockaddr->sin6_addr, expected_addr, INET6_ADDRSTRLEN);
					inet_ntop(AF_INET6, &live_sockaddr->sin6_addr, live_addr, INET6_ADDRSTRLEN);
					asprintf(error, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
						 expected_addr, live_addr);
					return STATUS_ERR;
				}
			}
			break;
		}
	}
	return STATUS_OK;
}
#endif
#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
int check_u8_expr(struct expression *expr, u8 value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		u8 script_val;
		if (get_u8(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %hhu actual: %hhu", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}
#endif

#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
int check_u16_htons_expr(struct expression *expr, u16 value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		u16 script_val;

		if (get_u16(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (ntohs(script_val) != ntohs(value)) {
			asprintf(error, "%s: expected: %hu actual: %hu",
				 val_name, ntohs(script_val), ntohs(value));
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}
#endif

hoelscher's avatar
hoelscher committed
int check_u16_expr(struct expression *expr, u16 value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		u16 script_val;
hoelscher's avatar
hoelscher committed
		if (get_u16(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %hu actual: %hu", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}
#endif
hoelscher's avatar
hoelscher committed

int check_s32_expr(struct expression *expr, s16 value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		s32 script_val;
		if (get_s32(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %d actual: %d", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}

int check_u32_hton_expr(struct expression *expr, u32 value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		u32 script_val;

		if (get_u32(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (htonl(value) != htonl(script_val)) {
			asprintf(error, "%s: expected: %u actual: %u", val_name,
				 ntohl(script_val), ntohl(value));
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}

int check_u32_expr(struct expression *expr, u32 value, char *val_name, char **error) {
hoelscher's avatar
hoelscher committed
	if (expr->type != EXPR_ELLIPSIS) {
		u32 script_val;
hoelscher's avatar
hoelscher committed
		if (get_u32(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %u actual: %u", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}

#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
int check_sctp_assoc_t_expr(struct expression *expr, sctp_assoc_t value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		sctp_assoc_t script_val;

		if (get_sctp_assoc_t(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %u actual: %u", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}
#endif

int check_socklen_t_expr(struct expression *expr, socklen_t value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		socklen_t script_val;

		if (get_socklen_t(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
			asprintf(error, "%s: expected: %u actual: %u", val_name, script_val, value);
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}

Michael Tüxen's avatar
Michael Tüxen committed
#ifdef linux
int check_size_t_expr(struct expression *expr, size_t value, char *val_name, char **error) {
	if (expr->type != EXPR_ELLIPSIS) {
		size_t script_val;

		if (get_size_t(expr, &script_val, error)) {
			return STATUS_ERR;
		}
		if (script_val != value) {
Michael Tüxen's avatar
Michael Tüxen committed
			asprintf(error, "%s: expected: %zu actual: %zu", val_name, script_val, value);
Michael Tüxen's avatar
Michael Tüxen committed
			return STATUS_ERR;
		}
	}
	return STATUS_OK;
}
#endif

#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
static int check_u8array_expr(struct expression *expr_list, u8 *data, size_t data_len, char *val_name, char **error) {
	if ( expr_list->type != EXPR_ELLIPSIS) {
		struct expression *expr = NULL;
		unsigned int i;

		switch(expr_list->type) {
		case EXPR_LIST:
			if (data_len != expression_list_length(expr_list->value.list)) {
				asprintf(error, "%s length: expected: %u actual %zu",
					 val_name, expression_list_length(expr_list->value.list), data_len);
				return STATUS_ERR;
			}
			for (i = 0; i < data_len; i++) {
				expr = get_arg(expr_list->value.list, i, error);
				if (expr->type != EXPR_ELLIPSIS) {
					u8 script_val;

					if (get_u8(expr, &script_val, error)) {
						return STATUS_ERR;
					}
					if (script_val != data[i]) {
						asprintf(error, "%s[%d]: expected: %hhu actual: %hhu",
							val_name, i, script_val, data[i]);
						return STATUS_ERR;
					}
				}
			}
			break;
		case EXPR_NULL:
			if (data != NULL)
				return STATUS_ERR;
			break;
		default: asprintf(error, "Bad expressiontype for %s", val_name);
			return STATUS_ERR;
			break;
		}
	}
	return STATUS_OK;
}
#endif

#if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11)
static int check_u16array_expr(struct expression *expr_list, u16 *data, int data_len, char *val_name, char **error) {
	if ( expr_list->type != EXPR_ELLIPSIS) {
		struct expression *expr = NULL;
		unsigned int i;

		switch(expr_list->type) {
		case EXPR_LIST:
			if (data_len != expression_list_length(expr_list->value.list)) {
				asprintf(error, "%s length: expected: %u actual %d",
					 val_name, expression_list_length(expr_list->value.list), data_len);
				return STATUS_ERR;
			}
			for (i = 0; i < data_len; i++) {
				expr = get_arg(expr_list->value.list, i, error);
				if (expr->type != EXPR_ELLIPSIS) {
					u16 script_val;

					if (get_u16(expr, &script_val, error)) {
						return STATUS_ERR;
					}
					if (script_val != data[i]) {
						asprintf(error, "%s[%d]: expected: %hu actual: %hu",
							val_name, i, script_val, data[i]);
						return STATUS_ERR;
				}
			}
			break;
		case EXPR_NULL:
			if (data != NULL)
				return STATUS_ERR;
			break;
		default: asprintf(error, "Bad expressiontype for %s", val_name);
			return STATUS_ERR;
			break;
		}
	}
	return STATUS_OK;
}
#endif

/* Free all the space used by the given iovec. */
static void iovec_free(struct iovec *iov, size_t iov_len)
{
	int i;

	if (iov == NULL)
		return;

	for (i = 0; i < iov_len; ++i)
		free(iov[i].iov_base);
	free(iov);