/* * 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); #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, char **error); #endif 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, 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 #if defined(linux) /* 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); } #endif /* 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; } #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) /* 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; } #endif #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)) { asprintf(error, "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 #if 0 /* 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; } #endif #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 #if 0 /* 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; } #endif /* 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); } #endif #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); } #endif 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 * sockaddr_in6 */ static int get_sockstorage_arg(struct expression *arg, struct sockaddr_storage *addr, int live_fd) { if (arg->type == EXPR_ELLIPSIS) { socklen_t len; len = (socklen_t)sizeof(struct sockaddr_storage); if (getpeername(live_fd, (struct sockaddr *)addr, &len)) { return STATUS_ERR; } } else if (arg->type == EXPR_SOCKET_ADDRESS_IPV4) { memcpy(addr, arg->value.socket_address_ipv4, sizeof(struct sockaddr_in)); } else if (arg->type == EXPR_SOCKET_ADDRESS_IPV6) { memcpy(addr, arg->value.socket_address_ipv6, sizeof(struct sockaddr_in6)); } else { return STATUS_ERR; } return STATUS_OK; } #endif #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 int check_u16_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 (script_val != value) { asprintf(error, "%s: expected: %hu actual: %hu", val_name, script_val, value); return STATUS_ERR; } } return STATUS_OK; } #endif 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) { if (expr->type != EXPR_ELLIPSIS) { u32 script_val; 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; } #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) { asprintf(error, "%s: expected: %zu actual: %zu", 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) 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); } /* Allocate and fill in an iovec described by the given expression. * Return STATUS_OK if the expression is a valid iovec. Otherwise * fill in the error with a human-readable error message and return * STATUS_ERR. */ static int iovec_new(struct expression *expression, struct iovec **iov_ptr, size_t *iov_len_ptr, char **error) { int status = STATUS_ERR; int i; struct expression_list *list; /* input expression from script */ size_t iov_len = 0; struct iovec *iov = NULL; /* live output */ if (check_type(expression, EXPR_LIST, error)) goto error_out; list = expression->value.list; iov_len = expression_list_length(list); iov = calloc(iov_len, sizeof(struct iovec)); for (i = 0; i < iov_len; ++i, list = list->next) { size_t len; struct iovec_expr *iov_expr; if (check_type(list->expression, EXPR_IOVEC, error)) goto error_out; iov_expr = list->expression->value.iovec; assert(iov_expr->iov_base->type == EXPR_ELLIPSIS || iov_expr->iov_base->type == EXPR_SCTP_ASSOC_CHANGE || iov_expr->iov_base->type == EXPR_SCTP_PADDR_CHANGE || iov_expr->iov_base->type == EXPR_SCTP_REMOTE_ERROR || iov_expr->iov_base->type == EXPR_SCTP_SEND_FAILED || iov_expr->iov_base->type == EXPR_SCTP_SHUTDOWN_EVENT || iov_expr->iov_base->type == EXPR_SCTP_ADAPTATION_EVENT || iov_expr->iov_base->type == EXPR_SCTP_PDAPI_EVENT || iov_expr->iov_base->type == EXPR_SCTP_AUTHKEY_EVENT || iov_expr->iov_base->type == EXPR_SCTP_SENDER_DRY_EVENT || iov_expr->iov_base->type == EXPR_SCTP_SEND_FAILED_EVENT || iov_expr->iov_base->type == EXPR_SCTP_TLV || iov_expr->iov_base->type == EXPR_SCTP_STREAM_RESET_EVENT || iov_expr->iov_base->type == EXPR_SCTP_ASSOC_RESET_EVENT || iov_expr->iov_base->type == EXPR_SCTP_STREAM_CHANGE_EVENT); assert(iov_expr->iov_len->type == EXPR_INTEGER); len = iov_expr->iov_len->value.num; iov[i].iov_len = len; iov[i].iov_base = calloc(len, 1); } status = STATUS_OK; error_out: *iov_ptr = iov; *iov_len_ptr = iov_len; return status; } /* Allocate and fill in an cmsghdr described by the given expression. * Return STATUS_OK if the expression is a valid cmsghdr. Otherwise * fill in the error with a human-readable error message and return * STATUS_ERR. */ #ifdef linux static int cmsg_new(struct expression *expression, void **cmsg_ptr, size_t *cmsg_len_ptr, bool send, char **error) #else static int cmsg_new(struct expression *expression, void **cmsg_ptr, socklen_t *cmsg_len_ptr, bool send, char **error) #endif { struct expression_list *list; int list_len = 0, i = 0; size_t cmsg_size = 0; struct cmsghdr *cmsg; if (check_type(expression, EXPR_LIST, error)) return STATUS_ERR; list = expression->value.list; list_len = expression_list_length(list); //calc size of cmsg in list if (list_len == 0){ cmsg_ptr = NULL; return STATUS_OK; } for (i = 0; i < list_len; i++) { struct expression *cmsg_expr; cmsg_expr = get_arg(list, i, error); switch (cmsg_expr->value.cmsghdr->cmsg_data->type) { #if defined(SCTP_INIT) case EXPR_SCTP_INITMSG: cmsg_size += CMSG_SPACE(sizeof(struct sctp_initmsg)); break; #endif #if defined(SCTP_SNDRCV) case EXPR_SCTP_SNDRCVINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)); break; #endif #if defined(SCTP_EXTRCV) case EXPR_SCTP_EXTRCVINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_extrcvinfo)); break; #endif #if defined(SCTP_SNDINFO) case EXPR_SCTP_SNDINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_sndinfo)); break; #endif #if defined(SCTP_RCVINFO) case EXPR_SCTP_RCVINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_rcvinfo)); break; #endif #if defined(SCTP_NXTINFO) case EXPR_SCTP_NXTINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_nxtinfo)); break; #endif #if defined(SCTP_PRINFO) case EXPR_SCTP_PRINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_prinfo)); break; #endif #if defined(SCTP_AUTHINFO) case EXPR_SCTP_AUTHINFO: cmsg_size += CMSG_SPACE(sizeof(struct sctp_authinfo)); break; #endif #if defined(SCTP_DSTADDRV4) case EXPR_SOCKET_ADDRESS_IPV4: cmsg_size += CMSG_SPACE(sizeof(struct in_addr)); break; #endif #if defined(SCTP_DSTADDRV6) case EXPR_SOCKET_ADDRESS_IPV6: cmsg_size += CMSG_SPACE(sizeof(struct in6_addr)); break; #endif default: asprintf(error,"cmsg %d type not valid", i); return STATUS_ERR; } } #ifndef linux *cmsg_len_ptr = (socklen_t)cmsg_size; #else *cmsg_len_ptr = cmsg_size; #endif cmsg = calloc(1, cmsg_size); *cmsg_ptr = (void *)cmsg; for (i = 0; i < list_len; i++) { struct expression *expr; struct cmsghdr_expr *cmsg_expr; expr = get_arg(list, i, error); if(check_type(expr, EXPR_CMSGHDR, error)) goto error_out; cmsg_expr = expr->value.cmsghdr; #ifdef linux if (get_size_t(cmsg_expr->cmsg_len, &cmsg->cmsg_len, error)) #else if (get_socklen_t(cmsg_expr->cmsg_len, &cmsg->cmsg_len, error)) #endif goto error_out; if (get_s32(cmsg_expr->cmsg_level, &cmsg->cmsg_level, error)) goto error_out; if (get_s32(cmsg_expr->cmsg_type, &cmsg->cmsg_type, error)) goto error_out; switch(cmsg_expr->cmsg_data->type) { #if defined(SCTP_INIT) case EXPR_SCTP_INITMSG: { struct sctp_initmsg init; if (parse_expression_to_sctp_initmsg(cmsg_expr->cmsg_data, &init, error)) { goto error_out; } memcpy(CMSG_DATA(cmsg), &init, sizeof(struct sctp_initmsg)); cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_initmsg))); break; } #endif #if defined(SCTP_SNDRCV) case EXPR_SCTP_SNDRCVINFO: { struct sctp_sndrcvinfo info; if (parse_expression_to_sctp_sndrcvinfo(cmsg_expr->cmsg_data, &info, send, error)) { goto error_out; } memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_sndrcvinfo)); cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))); break; } #endif #if defined(SCTP_EXTRCV) case EXPR_SCTP_EXTRCVINFO: { cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_extrcvinfo))); break; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) case EXPR_SCTP_SNDINFO: { struct sctp_sndinfo info; if (parse_expression_to_sctp_sndinfo(cmsg_expr->cmsg_data, &info, error)) { goto error_out; } memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_sndinfo)); cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo))); break; } #endif #if defined(SCTP_RCVINFO) case EXPR_SCTP_RCVINFO: cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_rcvinfo))); break; #endif #if defined(SCTP_NXTINFO) case EXPR_SCTP_NXTINFO: cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_nxtinfo))); break; #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) case EXPR_SCTP_PRINFO: { struct sctp_prinfo info; if (parse_expression_to_sctp_prinfo(cmsg_expr->cmsg_data, &info, error)) { goto error_out; } memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_prinfo)); cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo))); break; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) case EXPR_SCTP_AUTHINFO: { struct sctp_authinfo info; if (parse_expression_to_sctp_authinfo(cmsg_expr->cmsg_data, &info, error)) { goto error_out; } memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_authinfo)); cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo))); break; } #endif #if defined(SCTP_DSTADDRV4) case EXPR_SOCKET_ADDRESS_IPV4: memcpy(CMSG_DATA(cmsg), &cmsg_expr->cmsg_data->value.socket_address_ipv4->sin_addr, sizeof(struct in_addr)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr))); break; #endif #if defined(SCTP_DSTADDRV6) case EXPR_SOCKET_ADDRESS_IPV6: memcpy(CMSG_DATA(cmsg), &cmsg_expr->cmsg_data->value.socket_address_ipv6->sin6_addr, sizeof(struct in6_addr)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr))); break; #endif default: asprintf(error,"cmsg.cmsg_data %d type not valid", i); goto error_out; } } return STATUS_OK; error_out: free(*cmsg_ptr); *cmsg_ptr = NULL; *cmsg_len_ptr = 0; return STATUS_ERR; } static int check_cmsghdr(struct expression *expr_list, struct msghdr *msg, char **error) { struct expression_list *list; struct expression *cmsg_expr; struct cmsghdr *cmsg_ptr; int cnt = 0; int list_len = 0; assert(expr_list->type == EXPR_LIST); list = expr_list->value.list; list_len = expression_list_length(list); for (cmsg_ptr = CMSG_FIRSTHDR(msg); cmsg_ptr != NULL; cmsg_ptr = CMSG_NXTHDR(msg, cmsg_ptr)) { cmsg_expr = get_arg(list, cnt, error); if (cmsg_expr->type != EXPR_ELLIPSIS) { struct cmsghdr_expr *expr; expr = cmsg_expr->value.cmsghdr; if (check_s32_expr(expr->cmsg_type, cmsg_ptr->cmsg_type, "cmsghdr.cmsg_type", error)) return STATUS_ERR; #ifdef linux if (check_size_t_expr(expr->cmsg_len, cmsg_ptr->cmsg_len, "cmsghdr.cmsg_len", error)) #else if (check_socklen_t_expr(expr->cmsg_len, cmsg_ptr->cmsg_len, "cmsghdr.cmsg_len", error)) #endif return STATUS_ERR; if (check_s32_expr(expr->cmsg_level, cmsg_ptr->cmsg_level, "cmsghdr.cmsg_level", error)) return STATUS_ERR; if (expr->cmsg_data->type == EXPR_ELLIPSIS) { continue; } switch(cmsg_ptr->cmsg_type) { #ifdef SCTP_INIT case SCTP_INIT: if (check_sctp_initmsg(expr->cmsg_data->value.sctp_initmsg, (struct sctp_initmsg *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_SNDRCV case SCTP_SNDRCV: if (check_sctp_sndrcvinfo(expr->cmsg_data->value.sctp_sndrcvinfo, (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_EXTRCV case SCTP_EXTRCV: if (check_sctp_extrcvinfo(expr->cmsg_data->value.sctp_extrcvinfo, (struct sctp_extrcvinfo *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_SNDINFO case SCTP_SNDINFO: if (check_sctp_sndinfo(expr->cmsg_data->value.sctp_sndinfo, (struct sctp_sndinfo *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) case SCTP_RCVINFO: if (check_sctp_rcvinfo(expr->cmsg_data->value.sctp_rcvinfo, (struct sctp_rcvinfo *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) case SCTP_NXTINFO: if (check_sctp_nxtinfo(expr->cmsg_data->value.sctp_nxtinfo, (struct sctp_nxtinfo *) CMSG_DATA(cmsg_ptr), error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_PRINFO case SCTP_PRINFO: if (check_u16_expr(expr->cmsg_data->value.sctp_prinfo->pr_policy, ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_policy, "prinfo.pr_policy", error)) { return STATUS_ERR; } if (check_u32_expr(expr->cmsg_data->value.sctp_prinfo->pr_value, ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_value, "prinfo.pr_value", error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_AUTHINFO case SCTP_AUTHINFO: /* * Solaris 10.4 uses the wrong name: auth_keyid instead * of auth_keynumber as specified in * https://tools.ietf.org/html/rfc6458#section-5.3.8 */ if (check_u16_expr(expr->cmsg_data->value.sctp_authinfo->auth_keynumber, #if defined(__SunOS_5_11) ((struct sctp_authinfo *)CMSG_DATA(cmsg_ptr))->auth_keyid, #else ((struct sctp_authinfo *)CMSG_DATA(cmsg_ptr))->auth_keynumber, #endif "authinfo.auth_keynumber", error)) { return STATUS_ERR; } break; #endif #ifdef SCTP_DSTADDRV4 case SCTP_DSTADDRV4: if (expr->cmsg_data->type != EXPR_ELLIPSIS) { struct sockaddr_in *addr = expr->cmsg_data->value.socket_address_ipv4; struct in_addr *cmsg_addr = (struct in_addr *) CMSG_DATA(cmsg_ptr); if (addr->sin_addr.s_addr != cmsg_addr->s_addr) { asprintf(error, "cmsg_data for SCTP_DSTADDRV4: expected: %s actual: %s", inet_ntoa(addr->sin_addr), inet_ntoa(*cmsg_addr)); return STATUS_ERR; } } break; #endif #ifdef SCTP_DSTADDRV6 case SCTP_DSTADDRV6: if (expr->cmsg_data->type != EXPR_ELLIPSIS) { struct sockaddr_in6 *addr = expr->cmsg_data->value.socket_address_ipv6; struct in6_addr *cmsg_addr = (struct in6_addr *) CMSG_DATA(cmsg_ptr); if (memcmp(&addr->sin6_addr, cmsg_addr, sizeof(struct in6_addr))) { char expected_addr[INET6_ADDRSTRLEN]; char live_addr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr->sin6_addr, expected_addr, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, cmsg_addr, live_addr, INET6_ADDRSTRLEN); asprintf(error, "sockaddr_in6 from.sin6_addr. expected: %s actual %s", expected_addr, live_addr); return STATUS_ERR; } } break; #endif default: asprintf(error, "can't check cmsg type"); return STATUS_ERR; } } cnt++; } if (cnt != list_len) { asprintf(error, "Return cmsg count is unqual to expected list len. actual %u, expected %u", cnt, list_len); return STATUS_ERR; } return STATUS_OK; } /* Free all the space used by the given msghdr. */ static void msghdr_free(struct msghdr *msg, size_t iov_len) { if (msg == NULL) return; free(msg->msg_name); iovec_free(msg->msg_iov, iov_len); free(msg->msg_control); free(msg); } /* Allocate and fill in a msghdr described by the given expression. */ static int msghdr_new(struct expression *expression, struct msghdr **msg_ptr, size_t *iov_len_ptr, bool send, char **error) { int status = STATUS_ERR; s32 s32_val = 0; struct msghdr_expr *msg_expr; /* input expression from script */ socklen_t name_len = sizeof(struct sockaddr_storage); struct msghdr *msg = NULL; /* live output */ #ifdef linux size_t cmsg_len = 0; #else socklen_t cmsg_len = 0; #endif if (check_type(expression, EXPR_MSGHDR, error)) goto error_out; msg_expr = expression->value.msghdr; msg = calloc(1, sizeof(struct msghdr)); if (msg_expr->msg_name != NULL) { assert(msg_expr->msg_name->type == EXPR_ELLIPSIS); msg->msg_name = calloc(1, name_len); } if (msg_expr->msg_namelen != NULL) { assert(msg_expr->msg_namelen->type == EXPR_ELLIPSIS); msg->msg_namelen = name_len; } if (msg_expr->msg_iov != NULL) { if (iovec_new(msg_expr->msg_iov, &msg->msg_iov, iov_len_ptr, error)) goto error_out; } if (msg_expr->msg_iovlen != NULL) { if (get_s32(msg_expr->msg_iovlen, &s32_val, error)) goto error_out; msg->msg_iovlen = s32_val; } if (msg->msg_iovlen != *iov_len_ptr) { asprintf(error, "msg_iovlen %d does not match %d-element iovec array", (int)msg->msg_iovlen, (int)*iov_len_ptr); goto error_out; } if (msg_expr->msg_control != NULL) { if (cmsg_new(msg_expr->msg_control, &msg->msg_control, &cmsg_len, send, error)) goto error_out; } if (msg_expr->msg_controllen != NULL) { #ifdef linux if (get_size_t(msg_expr->msg_controllen, &msg->msg_controllen, error)) #else if (get_socklen_t(msg_expr->msg_controllen, &msg->msg_controllen, error)) #endif goto error_out; } if (msg->msg_controllen != cmsg_len) { asprintf(error, "msg_controllen %zu does not match %zu size of cmsghdr array", (size_t)msg->msg_controllen, (size_t)cmsg_len); goto error_out; } if (msg_expr->msg_flags != NULL) { if (get_s32(msg_expr->msg_flags, &s32_val, error)) goto error_out; msg->msg_flags = s32_val; } status = STATUS_OK; error_out: *msg_ptr = msg; return status; } /* Allocate and fill in a pollfds array described by the given * fds_expression. Return STATUS_OK if the expression is a valid * pollfd struct array. Otherwise fill in the error with a * human-readable error message and return STATUS_ERR. */ static int pollfds_new(struct state *state, struct expression *fds_expression, struct pollfd **fds_ptr, size_t *fds_len_ptr, char **error) { int status = STATUS_ERR; int i; struct expression_list *list; /* input expression from script */ size_t fds_len = 0; struct pollfd *fds = NULL; /* live output */ if (check_type(fds_expression, EXPR_LIST, error)) goto error_out; list = fds_expression->value.list; fds_len = expression_list_length(list); fds = calloc(fds_len, sizeof(struct pollfd)); for (i = 0; i < fds_len; ++i, list = list->next) { struct pollfd_expr *fds_expr; if (check_type(list->expression, EXPR_POLLFD, error)) goto error_out; fds_expr = list->expression->value.pollfd; if (check_type(fds_expr->fd, EXPR_INTEGER, error)) goto error_out; if (check_type(fds_expr->events, EXPR_INTEGER, error)) goto error_out; if (check_type(fds_expr->revents, EXPR_INTEGER, error)) goto error_out; if (to_live_fd(state, fds_expr->fd->value.num, &fds[i].fd, error)) goto error_out; fds[i].events = fds_expr->events->value.num; fds[i].revents = fds_expr->revents->value.num; } status = STATUS_OK; error_out: *fds_ptr = fds; *fds_len_ptr = fds_len; return status; } /* Check the results of a poll() system call: check that the output * revents fields in the fds array match those in the script. Return * STATUS_OK if they match. Otherwise fill in the error with a * human-readable error message and return STATUS_ERR. */ static int pollfds_check(struct expression *fds_expression, const struct pollfd *fds, size_t fds_len, char **error) { struct expression_list *list; /* input expression from script */ int i; assert(fds_expression->type == EXPR_LIST); list = fds_expression->value.list; for (i = 0; i < fds_len; ++i, list = list->next) { struct pollfd_expr *fds_expr; int expected_revents, actual_revents; assert(list->expression->type == EXPR_POLLFD); fds_expr = list->expression->value.pollfd; assert(fds_expr->fd->type == EXPR_INTEGER); assert(fds_expr->events->type == EXPR_INTEGER); assert(fds_expr->revents->type == EXPR_INTEGER); expected_revents = fds_expr->revents->value.num; actual_revents = fds[i].revents; if (actual_revents != expected_revents) { char *expected_revents_string = flags_to_string(poll_flags, expected_revents); char *actual_revents_string = flags_to_string(poll_flags, actual_revents); asprintf(error, "Expected revents of %s but got %s " "for pollfd %d", expected_revents_string, actual_revents_string, i); free(expected_revents_string); free(actual_revents_string); return STATUS_ERR; } } return STATUS_OK; } /* For blocking system calls, give up the global lock and wake the * main thread so it can continue test execution. Callers should call * this function immediately before calling a system call in order to * release the global lock immediately before a system call that the * script expects to block. */ static void begin_syscall(struct state *state, struct syscall_spec *syscall) { int err; if (is_blocking_syscall(syscall)) { assert(state->syscalls->state == SYSCALL_ENQUEUED); state->syscalls->state = SYSCALL_RUNNING; run_unlock(state); DEBUGP("syscall thread: begin_syscall signals dequeued\n"); if ((err = pthread_cond_signal(&state->syscalls->dequeued)) != 0) die_strerror("pthread_cond_signal", err); } } /* Verify that the system call returned the expected result code and * errno value. Returns STATUS_OK on success; on failure returns * STATUS_ERR and sets error message. Callers should call this function * immediately after returning from a system call in order to immediately * re-grab the global lock if this is a blocking call. */ enum result_check_t { CHECK_EXACT, /* check that result matches exactly */ CHECK_FD, /* check that result fd or matching error */ }; static int end_syscall(struct state *state, struct syscall_spec *syscall, enum result_check_t mode, int actual, char **error) { int actual_errno = errno; /* in case we clobber this later */ s32 expected = 0; /* For blocking calls, advance state and reacquire the global lock. */ if (is_blocking_syscall(syscall)) { s64 live_end_usecs = now_usecs(); DEBUGP("syscall thread: end_syscall grabs lock\n"); run_lock(state); state->syscalls->live_end_usecs = live_end_usecs; assert(state->syscalls->state == SYSCALL_RUNNING); state->syscalls->state = SYSCALL_DONE; } /* Compare actual vs expected return value */ if (get_s32(syscall->result, &expected, error)) return STATUS_ERR; if (mode == CHECK_FD && expected >= 0) { if (actual < 0) { asprintf(error, "Expected non-negative result but got %d with errno %d (%s)", actual, actual_errno, strerror(actual_errno)); return STATUS_ERR; } } else if (mode == CHECK_FD || mode == CHECK_EXACT) { if (actual != expected) { if (actual < 0) asprintf(error, "Expected result %d but got %d with errno %d (%s)", expected, actual, actual_errno, strerror(actual_errno)); else asprintf(error, "Expected result %d but got %d", expected, actual); return STATUS_ERR; } } else { assert(!"bad mode"); } /* Compare actual vs expected errno */ if (syscall->error != NULL) { s64 expected_errno = 0; if (symbol_to_int(syscall->error->errno_macro, &expected_errno, error)) return STATUS_ERR; if (actual_errno != expected_errno) { char *exp_error, *act_error; asprintf(&exp_error, "%s", strerror(expected_errno)); asprintf(&act_error, "%s", strerror(actual_errno)); asprintf(error, "Expected errno %d (%s) but got %d (%s)", (int)expected_errno, exp_error, actual_errno, act_error); free(exp_error); free(act_error); return STATUS_ERR; } } return STATUS_OK; } /* Return a pointer to the socket with the given script fd, or NULL. */ static struct socket *find_socket_by_script_fd( struct state *state, int script_fd) { struct socket *socket = NULL; for (socket = state->sockets; socket != NULL; socket = socket->next) if (!socket->is_closed && (socket->script.fd == script_fd)) { assert(socket->live.fd >= 0); assert(socket->script.fd >= 0); return socket; } return NULL; } /* Return a pointer to the socket with the given live fd, or NULL. */ static struct socket *find_socket_by_live_fd( struct state *state, int live_fd) { struct socket *socket = NULL; for (socket = state->sockets; socket != NULL; socket = socket->next) if (!socket->is_closed && (socket->live.fd == live_fd)) { assert(socket->live.fd >= 0); assert(socket->script.fd >= 0); return socket; } return NULL; } /* Find the live fd corresponding to the fd in a script. Returns * STATUS_OK on success; on failure returns STATUS_ERR and sets * error message. */ static int to_live_fd(struct state *state, int script_fd, int *live_fd, char **error) { struct socket *socket = find_socket_by_script_fd(state, script_fd); if (socket != NULL) { *live_fd = socket->live.fd; return STATUS_OK; } else { *live_fd = -1; asprintf(error, "unable to find socket with script fd %d", script_fd); return STATUS_ERR; } } /**************************************************************************** * Here we have the "backend" post-processing and pre-processing that * we perform after and/or before each of the system calls that * we support... */ /* The app called socket() in the script and we did a live reenactment * socket() call. Create a struct socket to track the new socket. * Returns STATUS_OK on success; on failure returns STATUS_ERR and * sets error message. */ static struct socket *insert_new_socket( struct state *state, int address_family, int protocol, int script_fd, int live_fd, char **error) { /* Validate fd values. */ if (script_fd < 0) { asprintf(error, "invalid socket fd %d in script", script_fd); return NULL; } if (live_fd < 0) { asprintf(error, "invalid live socket fd %d", live_fd); return NULL; } /* Look for sockets with conflicting fds. Should not happen if * the script is valid and this program is bug-free. */ if (find_socket_by_script_fd(state, script_fd)) { asprintf(error, "duplicate socket fd %d in script", script_fd); return NULL; } if (find_socket_by_live_fd(state, live_fd)) { asprintf(error, "duplicate live socket fd %d", live_fd); return NULL; } /* These fd values are kosher, so store them. */ struct socket *socket = socket_new(state); socket->state = SOCKET_NEW; socket->address_family = address_family; socket->protocol = protocol; socket->script.fd = script_fd; socket->live.fd = live_fd; socket->script.remote.ip.address_family = address_family; socket->script.local.ip.address_family = address_family; return socket; } static int run_syscall_socket(struct state *state, int address_family, int protocol, int script_fd, int live_fd, char **error) { struct socket *socket = insert_new_socket(state, address_family, protocol, script_fd, live_fd, error); if (socket == NULL) return STATUS_ERR; /* Any later packets in the test script will now be mapped here. */ state->socket_under_test = socket; DEBUGP("socket() creating new socket: script_fd: %d live_fd: %d\n", socket->script.fd, socket->live.fd); return STATUS_OK; } /* Handle a close() call for the given socket. * Returns STATUS_OK on success; on failure returns STATUS_ERR and * sets error message. */ static int run_syscall_close(struct state *state, int script_fd, int live_fd, char **error) { struct socket *socket = find_socket_by_script_fd(state, script_fd); if ((socket == NULL) || (socket->live.fd != live_fd)) goto error_out; socket->is_closed = true; return STATUS_OK; error_out: asprintf(error, "unable to find socket with script fd %d and live fd %d", script_fd, live_fd); return STATUS_ERR; } /* Fill in the live_addr and live_addrlen for a bind() call. * Returns STATUS_OK on success; on failure returns STATUS_ERR and * sets error message. */ static int run_syscall_bind(struct state *state, struct sockaddr *live_addr, socklen_t *live_addrlen, char **error) { DEBUGP("run_syscall_bind\n"); /* Fill in the live address we want to bind to */ ip_to_sockaddr(&state->config->live_bind_ip, state->config->live_bind_port, live_addr, live_addrlen); return STATUS_OK; } /* Handle a listen() call for the given socket. * Returns STATUS_OK on success; on failure returns STATUS_ERR and * sets error message. */ static int run_syscall_listen(struct state *state, int script_fd, int live_fd, char **error) { struct socket *socket = NULL; socket = find_socket_by_script_fd(state, script_fd); if (socket != NULL) { assert(socket->script.fd == script_fd); assert(socket->live.fd == live_fd); if (socket->state != SOCKET_NEW) { asprintf(error, "bad listen(); script fd %d in state %d", script_fd, socket->state); return STATUS_ERR; } socket->state = SOCKET_PASSIVE_LISTENING; return STATUS_OK; } else { asprintf(error, "unable to find socket with script fd %d", script_fd); return STATUS_ERR; } } /* Handle an accept() call creating a new socket with the given file * descriptors. * Returns STATUS_OK on success; on failure returns STATUS_ERR and * sets error message. */ static int run_syscall_accept(struct state *state, int script_accepted_fd, int live_accepted_fd, struct sockaddr *live_addr, int live_addrlen, char **error) { struct socket *socket = NULL; struct ip_address ip; u16 port = 0; DEBUGP("run_syscall_accept\n"); /* Parse the sockaddr into a nice multi-protocol ip_address struct. */ ip_from_sockaddr(live_addr, live_addrlen, &ip, &port); /* For ipv4-mapped-ipv6: if ip is IPv4-mapped IPv6, map it to IPv4. */ if (ip.address_family == AF_INET6) { struct ip_address ipv4; if (ipv6_map_to_ipv4(ip, &ipv4) == STATUS_OK) ip = ipv4; } for (socket = state->sockets; socket != NULL; socket = socket->next) { #if defined(DEBUG) if (debug_logging) { char remote_string[ADDR_STR_LEN]; DEBUGP("socket state=%d script addr: %s:%d\n", socket->state, ip_to_string(&socket->script.remote.ip, remote_string), socket->script.remote.port); } #endif /* DEBUG */ if ((socket->state == SOCKET_PASSIVE_SYNACK_SENT) || /* TFO */ #if defined(__FreeBSD__) /* * In FreeBSD the accept system call might return after the * reception of the SYN segment and before sending the * SYN-ACK segment to allow the user to provide data when * using TFO. */ (socket->state == SOCKET_PASSIVE_PACKET_RECEIVED) || #endif (socket->state == SOCKET_PASSIVE_SYNACK_ACKED) || (socket->state == SOCKET_PASSIVE_COOKIE_ECHO_RECEIVED)) { assert(is_equal_ip(&socket->live.remote.ip, &ip)); assert(is_equal_port(socket->live.remote.port, htons(port))); socket->script.fd = script_accepted_fd; socket->live.fd = live_accepted_fd; return STATUS_OK; } } if (!state->config->is_wire_client) { asprintf(error, "unable to find socket matching accept() call"); return STATUS_ERR; } /* If this is a wire client, then this process just * sees the system call action for this socket. Create a child * passive socket for this accept call, and fill in what we * know about the socket. Any further packets in the test * script will be directed to this child socket. */ socket = socket_new(state); state->socket_under_test = socket; assert(socket->state == SOCKET_INIT); socket->address_family = ip.address_family; socket->live.remote.ip = ip; socket->live.remote.port = port; socket->live.local.ip = state->config->live_local_ip; socket->live.local.port = htons(state->config->live_bind_port); socket->live.fd = live_accepted_fd; socket->script.fd = script_accepted_fd; #if defined(DEBUG) if (debug_logging) { char local_string[ADDR_STR_LEN]; char remote_string[ADDR_STR_LEN]; DEBUGP("live: local: %s.%d\n", ip_to_string(&socket->live.local.ip, local_string), ntohs(socket->live.local.port)); DEBUGP("live: remote: %s.%d\n", ip_to_string(&socket->live.remote.ip, remote_string), ntohs(socket->live.remote.port)); } #endif /* DEBUG */ return STATUS_OK; } /* Handle an connect() or sendto() call initiating a connect to a * remote address. Fill in the live_addr and live_addrlen for the live * connect(). Returns STATUS_OK on success; on failure returns * STATUS_ERR and sets error message. */ static int run_syscall_connect(struct state *state, int script_fd, bool must_be_new_socket, struct sockaddr *live_addr, socklen_t *live_addrlen, char **error) { struct socket *socket = NULL; DEBUGP("run_syscall_connect\n"); /* Fill in the live address we want to connect to */ if ((state->socket_under_test != NULL) && (state->socket_under_test->state == SOCKET_PASSIVE_PACKET_RECEIVED)) { ip_to_sockaddr(&state->socket_under_test->live.remote.ip, ntohs(state->socket_under_test->live.remote.port), live_addr, live_addrlen); } else { ip_to_sockaddr(&state->config->live_connect_ip, state->config->live_connect_port, live_addr, live_addrlen); } socket = find_socket_by_script_fd(state, script_fd); assert(socket != NULL); if (socket->state != SOCKET_NEW) { if (must_be_new_socket) { asprintf(error, "socket is not new"); return STATUS_ERR; } else { return STATUS_OK; } } socket->state = SOCKET_ACTIVE_CONNECTING; ip_reset(&socket->script.remote.ip); ip_reset(&socket->script.local.ip); socket->script.remote.port = 0; socket->script.local.port = 0; socket->live.remote.ip = state->config->live_remote_ip; socket->live.remote.port = htons(state->config->live_connect_port); DEBUGP("success: setting socket to state %d\n", socket->state); return STATUS_OK; } #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) static int run_syscall_sctp_peeloff(struct state *state, int script_copy_fd, int script_new_fd, int live_new_fd, char **error) { struct socket *copy_socket = NULL, *new_socket, *temp_socket; copy_socket = find_socket_by_script_fd(state, script_copy_fd); assert(copy_socket != NULL); if (copy_socket->state == SOCKET_NEW) { asprintf(error, "socket is not new"); return STATUS_ERR; } new_socket = find_socket_by_script_fd(state, script_new_fd); assert(new_socket == NULL); new_socket = socket_new(state); temp_socket = new_socket->next; memcpy(new_socket, copy_socket, sizeof(struct socket)); new_socket->next = temp_socket; new_socket->live.fd = live_new_fd; new_socket->script.fd = script_new_fd; DEBUGP("success: setting socket to state %d\n", new_socket->state); return STATUS_OK; } #endif /**************************************************************************** * Here we have the parsing and invocation of the system calls that * we support... */ static int syscall_socket(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int domain, type, protocol, live_fd, script_fd, result; if (check_arg_count(args, 3, error)) return STATUS_ERR; if (ellipsis_arg(args, 0, error)) return STATUS_ERR; if (s32_arg(args, 1, &type, error)) return STATUS_ERR; if (s32_arg(args, 2, &protocol, error)) return STATUS_ERR; domain = state->config->socket_domain; begin_syscall(state, syscall); result = socket(domain, type, protocol); if (end_syscall(state, syscall, CHECK_FD, result, error)) { if (result >= 0) { close(result); } return STATUS_ERR; } if (result >= 0) { const int off = 0; live_fd = result; /* If IPv4-mapped IPv6 addresses are used, disable IPV6_V6ONLY */ if (state->config->socket_domain == AF_INET6 && state->config->wire_protocol == AF_INET) { if (setsockopt(live_fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(int)) < 0) { die_perror("setsockopt IPV6_V6ONLY"); } } if (get_s32(syscall->result, &script_fd, error)) { close(live_fd); return STATUS_ERR; } if (run_syscall_socket(state, domain, protocol, script_fd, live_fd, error)) { close(live_fd); return STATUS_ERR; } } return STATUS_OK; } static int syscall_bind(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, result; struct sockaddr_storage live_addr; socklen_t live_addrlen; if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (ellipsis_arg(args, 2, error)) return STATUS_ERR; if (run_syscall_bind( state, (struct sockaddr *)&live_addr, &live_addrlen, error)) return STATUS_ERR; begin_syscall(state, syscall); result = bind(live_fd, (struct sockaddr *)&live_addr, live_addrlen); return end_syscall(state, syscall, CHECK_EXACT, result, error); } static int syscall_listen(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, backlog, result; if (check_arg_count(args, 2, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &backlog, error)) return STATUS_ERR; begin_syscall(state, syscall); result = listen(live_fd, backlog); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) return STATUS_ERR; if (run_syscall_listen(state, script_fd, live_fd, error)) return STATUS_ERR; return STATUS_OK; } static int syscall_accept(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, live_accepted_fd, script_accepted_fd, result; struct sockaddr_storage live_addr; socklen_t live_addrlen = sizeof(live_addr); if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (ellipsis_arg(args, 2, error)) return STATUS_ERR; begin_syscall(state, syscall); result = accept(live_fd, (struct sockaddr *)&live_addr, &live_addrlen); if (end_syscall(state, syscall, CHECK_FD, result, error)) { if (result >= 0) { close(result); } return STATUS_ERR; } if (result >= 0) { live_accepted_fd = result; if (get_s32(syscall->result, &script_accepted_fd, error)) { close(live_accepted_fd); return STATUS_ERR; } if (run_syscall_accept( state, script_accepted_fd, live_accepted_fd, (struct sockaddr *)&live_addr, live_addrlen, error)) { close(live_accepted_fd); return STATUS_ERR; } } return STATUS_OK; } static int syscall_connect(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, result; struct sockaddr_storage live_addr; socklen_t live_addrlen = sizeof(live_addr); if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (ellipsis_arg(args, 2, error)) return STATUS_ERR; if (run_syscall_connect( state, script_fd, true, (struct sockaddr *)&live_addr, &live_addrlen, error)) return STATUS_ERR; begin_syscall(state, syscall); result = connect(live_fd, (struct sockaddr *)&live_addr, live_addrlen); return end_syscall(state, syscall, CHECK_EXACT, result, error); } static int syscall_read(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, result; char *buf = NULL; if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; buf = malloc(count); assert(buf != NULL); begin_syscall(state, syscall); result = read(live_fd, buf, count); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_readv(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, iov_count, result; struct expression *iov_expression = NULL; struct iovec *iov = NULL; size_t iov_len = 0; int status = STATUS_ERR; if (check_arg_count(args, 3, error)) goto error_out; if (s32_arg(args, 0, &script_fd, error)) goto error_out; if (to_live_fd(state, script_fd, &live_fd, error)) goto error_out; iov_expression = get_arg(args, 1, error); if (iov_expression == NULL) goto error_out; if (iovec_new(iov_expression, &iov, &iov_len, error)) goto error_out; if (s32_arg(args, 2, &iov_count, error)) goto error_out; if (iov_count != iov_len) { asprintf(error, "iov_count %d does not match %d-element iovec array", iov_count, (int)iov_len); goto error_out; } begin_syscall(state, syscall); result = readv(live_fd, iov, iov_count); status = end_syscall(state, syscall, CHECK_EXACT, result, error); error_out: iovec_free(iov, iov_len); return status; } static int syscall_recv(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, flags, result; char *buf = NULL; if (check_arg_count(args, 4, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; buf = malloc(count); assert(buf != NULL); begin_syscall(state, syscall); result = recv(live_fd, buf, count, flags); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_recvfrom(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, flags, result; struct sockaddr_storage live_addr; socklen_t live_addrlen = sizeof(live_addr); char *buf = NULL; if (check_arg_count(args, 6, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; if (ellipsis_arg(args, 4, error)) return STATUS_ERR; if (ellipsis_arg(args, 5, error)) return STATUS_ERR; buf = malloc(count); assert(buf != NULL); begin_syscall(state, syscall); result = recvfrom(live_fd, buf, count, flags, (struct sockaddr *)&live_addr, &live_addrlen); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_recvmsg(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, flags, result; struct expression *msg_expression = NULL; struct msghdr *msg = NULL; size_t iov_len = 0; int expected_msg_flags = 0; int status = STATUS_ERR; if (check_arg_count(args, 3, error)) goto error_out; if (s32_arg(args, 0, &script_fd, error)) goto error_out; if (to_live_fd(state, script_fd, &live_fd, error)) goto error_out; msg_expression = get_arg(args, 1, error); if (msg_expression == NULL) goto error_out; if (msghdr_new(msg_expression, &msg, &iov_len, false, error)) goto error_out; if (s32_arg(args, 2, &flags, error)) goto error_out; expected_msg_flags = msg->msg_flags; begin_syscall(state, syscall); result = recvmsg(live_fd, msg, flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; if (msg->msg_flags != expected_msg_flags) { asprintf(error, "Expected msg_flags 0x%08X but got 0x%08X", expected_msg_flags, msg->msg_flags); goto error_out; } #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) if (msg->msg_flags & MSG_NOTIFICATION) { struct socket *socket = find_socket_by_script_fd(state, script_fd); if (check_sctp_notification(socket, msg->msg_iov, msg_expression->value.msghdr->msg_iov, error)) goto error_out; } #endif status = check_cmsghdr(msg_expression->value.msghdr->msg_control, msg, error); error_out: msghdr_free(msg, iov_len); return status; } static int syscall_write(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, result; char *buf = NULL; if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; buf = calloc(count, 1); assert(buf != NULL); begin_syscall(state, syscall); result = write(live_fd, buf, count); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_writev(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, iov_count, result; struct expression *iov_expression = NULL; struct iovec *iov = NULL; size_t iov_len = 0; int status = STATUS_ERR; if (check_arg_count(args, 3, error)) goto error_out; if (s32_arg(args, 0, &script_fd, error)) goto error_out; if (to_live_fd(state, script_fd, &live_fd, error)) goto error_out; iov_expression = get_arg(args, 1, error); if (iov_expression == NULL) goto error_out; if (iovec_new(iov_expression, &iov, &iov_len, error)) goto error_out; if (s32_arg(args, 2, &iov_count, error)) goto error_out; if (iov_count != iov_len) { asprintf(error, "iov_count %d does not match %d-element iovec array", iov_count, (int)iov_len); goto error_out; } begin_syscall(state, syscall); result = writev(live_fd, iov, iov_count); status = end_syscall(state, syscall, CHECK_EXACT, result, error); error_out: iovec_free(iov, iov_len); return status; } static int syscall_send(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, flags, result; char *buf = NULL; if (check_arg_count(args, 4, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; buf = calloc(count, 1); assert(buf != NULL); begin_syscall(state, syscall); result = send(live_fd, buf, count, flags); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_sendto(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, count, flags, result; struct sockaddr_storage live_addr; socklen_t live_addrlen = sizeof(live_addr); char *buf = NULL; if (check_arg_count(args, 6, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 2, &count, error)) return STATUS_ERR; if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; if (ellipsis_arg(args, 4, error)) return STATUS_ERR; if (ellipsis_arg(args, 5, error)) return STATUS_ERR; if (run_syscall_connect( state, script_fd, false, (struct sockaddr *)&live_addr, &live_addrlen, error)) return STATUS_ERR; buf = calloc(count, 1); assert(buf != NULL); begin_syscall(state, syscall); result = sendto(live_fd, buf, count, flags, (struct sockaddr *)&live_addr, live_addrlen); int status = end_syscall(state, syscall, CHECK_EXACT, result, error); free(buf); return status; } static int syscall_sendmsg(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, flags, result; struct expression *msg_expression = NULL; struct msghdr *msg = NULL; size_t iov_len = 0; int status = STATUS_ERR; if (check_arg_count(args, 3, error)) goto error_out; if (s32_arg(args, 0, &script_fd, error)) goto error_out; if (to_live_fd(state, script_fd, &live_fd, error)) goto error_out; msg_expression = get_arg(args, 1, error); if (msg_expression == NULL) goto error_out; if (msghdr_new(msg_expression, &msg, &iov_len, true, error)) goto error_out; if (s32_arg(args, 2, &flags, error)) goto error_out; if ((msg->msg_name != NULL) && run_syscall_connect(state, script_fd, false, msg->msg_name, &msg->msg_namelen, error)) goto error_out; if (msg->msg_flags != 0) { asprintf(error, "sendmsg ignores msg_flags field in msghdr"); goto error_out; } begin_syscall(state, syscall); result = sendmsg(live_fd, msg, flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; status = check_cmsghdr(msg_expression->value.msghdr->msg_control, msg, error); error_out: msghdr_free(msg, iov_len); return status; } static int syscall_fcntl(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, command, result; /* fcntl is an odd system call - it can take either 2 or 3 args. */ int actual_arg_count = get_arg_count(args); if ((actual_arg_count != 2) && (actual_arg_count != 3)) { asprintf(error, "fcntl expected 2-3 args but got %d", actual_arg_count); return STATUS_ERR; } if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &command, error)) return STATUS_ERR; if (actual_arg_count == 2) { begin_syscall(state, syscall); result = fcntl(live_fd, command); } else if (actual_arg_count == 3) { s32 arg; if (s32_arg(args, 2, &arg, error)) return STATUS_ERR; begin_syscall(state, syscall); result = fcntl(live_fd, command, arg); } else { assert(0); /* not reached */ } return end_syscall(state, syscall, CHECK_EXACT, result, error); } static int syscall_ioctl(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, command, result; /* ioctl is an odd system call - it can take either 2 or 3 args. */ int actual_arg_count = get_arg_count(args); if ((actual_arg_count != 2) && (actual_arg_count != 3)) { asprintf(error, "ioctl expected 2-3 args but got %d", actual_arg_count); return STATUS_ERR; } if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &command, error)) return STATUS_ERR; if (actual_arg_count == 2) { begin_syscall(state, syscall); result = ioctl(live_fd, command); return end_syscall(state, syscall, CHECK_EXACT, result, error); } else if (actual_arg_count == 3) { s32 script_optval, live_optval; if (s32_bracketed_arg(args, 2, &script_optval, error)) return STATUS_ERR; begin_syscall(state, syscall); result = ioctl(live_fd, command, &live_optval); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) return STATUS_ERR; if (live_optval != script_optval) { asprintf(error, "Bad ioctl optval: expected: %d actual: %d", (int)script_optval, (int)live_optval); return STATUS_ERR; } return STATUS_OK; } else { assert(0); /* not reached */ } return STATUS_ERR; } static int syscall_close(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, result, status; if (check_arg_count(args, 1, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; begin_syscall(state, syscall); result = close(live_fd); status = end_syscall(state, syscall, CHECK_EXACT, result, error); if (result == 0) { if (run_syscall_close(state, script_fd, live_fd, error)) status = STATUS_ERR; } return status; } static int syscall_shutdown(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd, how, result; if (check_arg_count(args, 2, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &how, error)) return STATUS_ERR; begin_syscall(state, syscall); result = shutdown(live_fd, how); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) return STATUS_ERR; return STATUS_OK; } static int check_linger(struct linger_expr *expr, struct linger *linger, char **error) { if (check_s32_expr(expr->l_onoff, linger->l_onoff, "linger.l_onoff", error)) return STATUS_ERR; if (check_s32_expr(expr->l_linger, linger->l_linger, "linger.l_linger", error)) return STATUS_ERR; return STATUS_OK; } #ifdef SCTP_RTOINFO static int check_sctp_rtoinfo(struct sctp_rtoinfo_expr *expr, struct sctp_rtoinfo *sctp_rtoinfo, char **error) { if (check_sctp_assoc_t_expr(expr->srto_assoc_id, sctp_rtoinfo->srto_assoc_id, "sctp_rtoinfo.srto_assoc_id", error)) return STATUS_ERR; if (check_u32_expr(expr->srto_initial, sctp_rtoinfo->srto_initial, "sctp_rtoinfo.srto_initial", error)) return STATUS_ERR; if (check_u32_expr(expr->srto_max, sctp_rtoinfo->srto_max, "sctp_rtoinfo.srto_max", error)) return STATUS_ERR; if (check_u32_expr(expr->srto_min, sctp_rtoinfo->srto_min, "sctp_rtoinfo.srto_min", error)) return STATUS_ERR; return STATUS_OK; } #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) { if (check_u16_expr(expr->sinit_num_ostreams, sctp_initmsg->sinit_num_ostreams, "sctp_initmsg.sinit_num_ostreams", error)) return STATUS_ERR; if (check_u16_expr(expr->sinit_max_instreams, sctp_initmsg->sinit_max_instreams, "sctp_initmsg.sinit_max_instreams", error)) return STATUS_ERR; if (check_u16_expr(expr->sinit_max_attempts, sctp_initmsg->sinit_max_attempts, "sctp_initmsg.sinit_max_attempts", error)) return STATUS_ERR; if (check_u16_expr(expr->sinit_max_init_timeo, sctp_initmsg->sinit_max_init_timeo, "sctp_initmsg.sinit_max_init_timeo", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(SCTP_AUTH_ACTIVE_KEY) || defined(SCTP_AUTH_DEACTIVATE_KEY) || defined(SCTP_AUTH_DELETE_KEY) static int check_sctp_authkeyid(struct sctp_authkeyid_expr *expr, struct sctp_authkeyid *sctp_authkeyid, char **error) { if (check_sctp_assoc_t_expr(expr->scact_assoc_id, sctp_authkeyid->scact_assoc_id, "sctp_authkeyid.scact_assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->scact_keynumber, sctp_authkeyid->scact_keynumber, "sctp_authkeyid.scact_keynumber", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_DELAYED_SACK static int check_sctp_sack_info(struct sctp_sack_info_expr *expr, struct sctp_sack_info *sctp_sack_info, char **error) { if (check_sctp_assoc_t_expr(expr->sack_assoc_id, sctp_sack_info->sack_assoc_id, "sctp_sack_info.sack_assoc_id", error)) return STATUS_ERR; if (check_u32_expr(expr->sack_delay, sctp_sack_info->sack_delay, "sctp_sack_info.sack_delay", error)) return STATUS_ERR; if (check_u32_expr(expr->sack_freq, sctp_sack_info->sack_freq, "sctp_sack_info.sack_freq", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(SCTP_GET_PEER_ADDR_INFO) || defined(SCTP_STATUS) static int check_sctp_paddrinfo(struct sctp_paddrinfo_expr *expr, struct sctp_paddrinfo *sctp_paddrinfo, char **error) { if (check_sctp_assoc_t_expr(expr->spinfo_assoc_id, sctp_paddrinfo->spinfo_assoc_id, "sctp_paddrinfo.spinfo_assoc_id", error)) return STATUS_ERR; if (check_s32_expr(expr->spinfo_state, sctp_paddrinfo->spinfo_state, "sctp_paddrinfo.spinfo_state", error)) return STATUS_ERR; if (check_u32_expr(expr->spinfo_cwnd, sctp_paddrinfo->spinfo_cwnd, "sctp_paddrinfo.spinfo_cwnd", error)) return STATUS_ERR; if (check_u32_expr(expr->spinfo_srtt, sctp_paddrinfo->spinfo_srtt, "sctp_paddrinfo.spinfo_srtt", error)) return STATUS_ERR; if (check_u32_expr(expr->spinfo_rto, sctp_paddrinfo->spinfo_rto, "sctp_paddrinfo.spinfo_rto", error)) return STATUS_ERR; if (check_u32_expr(expr->spinfo_mtu, sctp_paddrinfo->spinfo_mtu, "sctp_paddrinfo.spinfo_mtu", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_STATUS static int check_sctp_status(struct sctp_status_expr *expr, struct sctp_status *sctp_status, char **error) { if (check_sctp_assoc_t_expr(expr->sstat_assoc_id, sctp_status->sstat_assoc_id, "sctp_status.sstat_assoc_id", error)) return STATUS_ERR; if (check_s32_expr(expr->sstat_state, sctp_status->sstat_state, "sctp_status.sstat_state", error)) return STATUS_ERR; if (check_u32_expr(expr->sstat_rwnd, sctp_status->sstat_rwnd, "sctp_status.sstat_rwnd", error)) return STATUS_ERR; if (check_u16_expr(expr->sstat_unackdata, sctp_status->sstat_unackdata, "sctp_status.sstat_unackdata", error)) return STATUS_ERR; if (check_u16_expr(expr->sstat_penddata, sctp_status->sstat_penddata, "sctp_status.sstat_penddata", error)) return STATUS_ERR; if (check_u16_expr(expr->sstat_instrms, sctp_status->sstat_instrms, "sctp_status.sstat_instrms", error)) return STATUS_ERR; if (check_u16_expr(expr->sstat_outstrms, sctp_status->sstat_outstrms, "sctp_status.sstat_outstrms", error)) return STATUS_ERR; if (check_u32_expr(expr->sstat_fragmentation_point, sctp_status->sstat_fragmentation_point, "sctp_status.sstat_fragmentation_point", error)) return STATUS_ERR; if (expr->sstat_primary->type != EXPR_ELLIPSIS) { if (check_sctp_paddrinfo(expr->sstat_primary->value.sctp_paddrinfo, &sctp_status->sstat_primary, error)) { return STATUS_ERR; } } return STATUS_OK; } #endif #ifdef SCTP_PEER_ADDR_PARAMS static int check_sctp_paddrparams(struct sctp_paddrparams_expr *expr, struct sctp_paddrparams *sctp_paddrparams, char **error) { if (check_u32_expr(expr->spp_hbinterval, sctp_paddrparams->spp_hbinterval, "sctp_paddrparams.spp_hbinterval", error)) return STATUS_ERR; if (check_u16_expr(expr->spp_pathmaxrxt, sctp_paddrparams->spp_pathmaxrxt, "sctp_paddrparams.spp_pathmaxrxt", error)) return STATUS_ERR; if (check_u32_expr(expr->spp_pathmtu, sctp_paddrparams->spp_pathmtu, "sctp_paddrparams.spp_pathmtu", error)) return STATUS_ERR; if (check_u32_expr(expr->spp_flags, sctp_paddrparams->spp_flags, "sctp_paddrparams.spp_flags", error)) return STATUS_ERR; if (expr->spp_ipv6_flowlabel->type != EXPR_ELLIPSIS) { #ifdef linux asprintf(error, "linux doesn't support sctp_paddrparams.spp_ipv6_flowlabel"); return STATUS_ERR; #else if (check_u32_expr(expr->spp_ipv6_flowlabel, sctp_paddrparams->spp_ipv6_flowlabel, "sctp_paddrparams.spp_ipv6_flowlabel", error)) return STATUS_ERR; #endif } if (expr->spp_dscp->type != EXPR_ELLIPSIS) { #ifdef linux asprintf(error, "linux doesn't support sctp_paddrparams.spp_dscp"); return STATUS_ERR; #else #if defined(__SunOS_5_11) if (check_u8_expr(expr->spp_dscp, sctp_paddrparams->spp_ipv4_tos, "sctp_paddrparams.spp_dscp", error)) #else if (check_u8_expr(expr->spp_dscp, sctp_paddrparams->spp_dscp, "sctp_paddrparams.spp_dscp", error)) #endif return STATUS_ERR; #endif } return STATUS_OK; } #endif #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED) static int check_sctp_assoc_value(struct sctp_assoc_value_expr *expr, struct sctp_assoc_value *sctp_assoc_value, char **error) { if (check_sctp_assoc_t_expr(expr->assoc_id, sctp_assoc_value->assoc_id, "sctp_assoc_value.assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->assoc_value, sctp_assoc_value->assoc_value, "sctp_assoc_value.stream_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_SS_VALUE static int check_sctp_stream_value(struct sctp_stream_value_expr *expr, struct sctp_stream_value *sctp_stream_value, char **error) { if (check_u16_expr(expr->stream_id, sctp_stream_value->stream_id, "sctp_stream_value.stream_id", error)) return STATUS_ERR; if (check_u16_expr(expr->stream_value, sctp_stream_value->stream_value, "sctp_stream_value.stream_value", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_ASSOCINFO static int check_sctp_assocparams(struct sctp_assocparams_expr *expr, struct sctp_assocparams *sctp_assocparams, char **error) { if (check_sctp_assoc_t_expr(expr->sasoc_assoc_id, sctp_assocparams->sasoc_assoc_id, "sctp_assocparams.sasoc_assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->sasoc_asocmaxrxt, sctp_assocparams->sasoc_asocmaxrxt, "sctp_assocparams.sasoc_asocmaxrxt", error)) return STATUS_ERR; if (check_u16_expr(expr->sasoc_number_peer_destinations, sctp_assocparams->sasoc_number_peer_destinations, "sctp_assocparams.sasoc_number_peer_destinations", error)) return STATUS_ERR; if (check_u32_expr(expr->sasoc_peer_rwnd, sctp_assocparams->sasoc_peer_rwnd, "sctp_assocparams.sasoc_peer_rwnd", error)) return STATUS_ERR; if (check_u32_expr(expr->sasoc_local_rwnd, sctp_assocparams->sasoc_local_rwnd, "sctp_assocparams.sasoc_local_rwnd", error)) return STATUS_ERR; if (check_u32_expr(expr->sasoc_cookie_life, sctp_assocparams->sasoc_cookie_life, "sctp_assocparams.sasoc_cookie_life", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_EVENT static int check_sctp_event(struct sctp_event_expr *expr, struct sctp_event *sctp_event, char **error) { if (check_sctp_assoc_t_expr(expr->se_assoc_id, sctp_event->se_assoc_id, "sctp_event.se_assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->se_type, sctp_event->se_type, "sctp_event.se_type", error)) return STATUS_ERR; if (check_u8_expr(expr->se_on, sctp_event->se_on, "sctp_event.se_on", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_EVENTS static int check_sctp_event_subscribe(struct sctp_event_subscribe_expr *expr, struct sctp_event_subscribe *sctp_events, char **error) { if (check_u8_expr(expr->sctp_data_io_event, sctp_events->sctp_data_io_event, "sctp_event_subscribe.sctp_data_io_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_association_event, sctp_events->sctp_association_event, "sctp_event_subscribe.sctp_association_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_address_event, sctp_events->sctp_address_event, "sctp_event_subscribe.sctp_address_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_send_failure_event, sctp_events->sctp_send_failure_event, "sctp_event_subscribe.sctp_send_failure_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_peer_error_event, sctp_events->sctp_peer_error_event, "sctp_event_subscribe.sctp_peer_error_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_shutdown_event, sctp_events->sctp_shutdown_event, "sctp_event_subscribe.sctp_shutdown_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_partial_delivery_event, sctp_events->sctp_partial_delivery_event, "sctp_event_subscribe.sctp_partial_delivery_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_adaptation_layer_event, sctp_events->sctp_adaptation_layer_event, "sctp_event_subscribe.sctp_adaptation_layer_event", error)) return STATUS_ERR; #if !defined(__SunOS_5_11) if (check_u8_expr(expr->sctp_authentication_event, sctp_events->sctp_authentication_event, "sctp_event_subscribe.sctp_authentication_event", error)) return STATUS_ERR; if (check_u8_expr(expr->sctp_sender_dry_event, sctp_events->sctp_sender_dry_event, "sctp_event_subscribe.sctp_sender_dry_event", error)) return STATUS_ERR; #endif return STATUS_OK; } #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) { if (check_u16_expr(expr->snd_sid, sctp_sndinfo->snd_sid, "sctp_sndinfo.snd_sid", error)) return STATUS_ERR; if (check_u16_expr(expr->snd_flags, sctp_sndinfo->snd_flags, "sctp_sndinfo.snd_flags", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->snd_ppid, sctp_sndinfo->snd_ppid, "sctp_sndinfo.snd_ppid", error)) return STATUS_ERR; if (check_u32_expr(expr->snd_context, sctp_sndinfo->snd_context, "sctp_sndinfo.snd_context", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->snd_assoc_id, sctp_sndinfo->snd_assoc_id, "sctp_sndinfo.snd_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_DEFAULT_PRINFO static int check_sctp_default_prinfo(struct expression *expression, struct sctp_default_prinfo *info, char **error) { if (expression->type == EXPR_SCTP_DEFAULT_PRINFO) { struct sctp_default_prinfo_expr *expr = expression->value.sctp_default_prinfo; if (check_sctp_assoc_t_expr(expr->pr_assoc_id, info->pr_assoc_id, "sctp_default_prinfo.pr_assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->pr_policy, info->pr_policy, "sctp_default_prinfo.pr_policy", error)) return STATUS_ERR; if (check_u32_expr(expr->pr_value, info->pr_value, "sctp_default_prinfo.pr_value", error)) return STATUS_ERR; } else if (expression->type == EXPR_SCTP_PRINFO) { struct sctp_prinfo_expr *expr = expression->value.sctp_prinfo; if (check_u16_expr(expr->pr_policy, info->pr_policy, "sctp_default_prinfo.pr_policy", error)) return STATUS_ERR; if (check_u32_expr(expr->pr_value, info->pr_value, "sctp_default_prinfo.pr_value", error)) return STATUS_ERR; } else { return STATUS_ERR; } return STATUS_OK; } #endif #ifdef SCTP_PRIMARY_ADDR static int check_sctp_setprim(struct sctp_setprim_expr *expr, struct sctp_setprim *sctp_setprim, char **error) { if (check_sctp_assoc_t_expr(expr->ssp_assoc_id, sctp_setprim->ssp_assoc_id, "sctp_setprim.ssp_assoc_id", error)) return STATUS_ERR; if (check_sockaddr(expr->ssp_addr, (struct sockaddr *)&sctp_setprim->ssp_addr, error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_ADAPTATION_LAYER static int check_sctp_setadaptation(struct sctp_setadaptation_expr *expr, struct sctp_setadaptation *sctp_setadaptation, char **error) { if (check_u32_expr(expr->ssb_adaptation_ind, sctp_setadaptation->ssb_adaptation_ind, "sctp_setadptation.ssb_adaptation_ind", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) static int check_sctp_hamcalgo(struct sctp_hmacalgo_expr *expr, struct sctp_hmacalgo *sctp_hmacalgo, char **error) { if (check_u32_expr(expr->shmac_number_of_idents, sctp_hmacalgo->shmac_number_of_idents, "sctp_hmacalgo.shmac_number_of_idents", error)) return STATUS_ERR; if (check_type(expr->shmac_idents, EXPR_LIST, error)) { return STATUS_ERR; } if (check_u16array_expr(expr->shmac_idents, sctp_hmacalgo->shmac_idents, sctp_hmacalgo->shmac_number_of_idents, "sctp_hmacalgo.shmac_idents", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_GET_ASSOC_ID_LIST static int check_sctp_assoc_ids(struct sctp_assoc_ids_expr *expr, struct sctp_assoc_ids *sctp_assoc_ids, char **error) { int list_len = 0, i = 0; struct expression *assoc_id; struct expression_list *ids; if (check_u32_expr(expr->gaids_number_of_ids, sctp_assoc_ids->gaids_number_of_ids, "sctp_assoc_ids.gaids_number_of_ids", error)) { return STATUS_ERR; } ids = expr->gaids_assoc_id->value.list; list_len = expression_list_length(ids); if (list_len != sctp_assoc_ids->gaids_number_of_ids) { asprintf(error, "live gaids_number_if_ids unequal to length if expected gaids_assoc_id. actual %u, expected %d", sctp_assoc_ids->gaids_number_of_ids, list_len); return STATUS_ERR; } if (list_len == 1) { assoc_id = get_arg(ids, 0, error); if (assoc_id->type == EXPR_ELLIPSIS) { return STATUS_OK; } } for (i = 0; i < list_len; i++) { assoc_id = get_arg(ids, i, error); sctp_assoc_t script_id; if (get_sctp_assoc_t(assoc_id, &script_id, error)) { return STATUS_ERR; } if (script_id != sctp_assoc_ids->gaids_assoc_id[i]) { asprintf(error, "sctp_assoc_ids.gaids_assoc_id[%d]. expected %u, actual %u", i, script_id, sctp_assoc_ids->gaids_assoc_id[i]); return STATUS_ERR; } } return STATUS_OK; } #endif #if defined(SCTP_PEER_AUTH_CHUNKS) || defined(SCTP_LOCAL_AUTH_CHUNKS) static int check_sctp_authchunks(struct sctp_authchunks_expr *expr, struct sctp_authchunks *sctp_authchunks, char **error) { if (check_sctp_assoc_t_expr(expr->gauth_assoc_id, sctp_authchunks->gauth_assoc_id, "sctp_authchunks.gauth_assoc_id", error)) return STATUS_ERR; if (check_u32_expr(expr->gauth_number_of_chunks, sctp_authchunks->gauth_number_of_chunks, "sctp_authchunks.gauth_number_of_chunks", error)) return STATUS_ERR; if (check_u8array_expr(expr->gauth_chunks, sctp_authchunks->gauth_chunks, sctp_authchunks->gauth_number_of_chunks, "sctp_authchunks.gauth_chunks", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT static int check_sctp_udpencaps(struct sctp_udpencaps_expr *expr, struct sctp_udpencaps *sctp_udpencaps, char **error) { if (check_u16_htons_expr(expr->sue_port, sctp_udpencaps->sue_port, "sctp_udpencaps.sue_port", error)) return STATUS_ERR; return STATUS_OK; } #endif #ifdef SO_ACCEPTFILTER static int check_accept_filter_arg(struct accept_filter_arg_expr *expr, struct accept_filter_arg *accept_filter_arg, char **error) { if (expr->af_name->type != EXPR_ELLIPSIS) { if (strncmp(expr->af_name->value.string, accept_filter_arg->af_name, offsetof(struct accept_filter_arg, af_arg))) { asprintf(error, "accept_filter_arg.af_name: expected: %s, actual: %.*s\n", expr->af_name->value.string, (int)offsetof(struct accept_filter_arg, af_arg), accept_filter_arg->af_name); return STATUS_ERR; } } if (expr->af_arg != NULL && expr->af_arg->type != EXPR_ELLIPSIS) { if (strncmp(expr->af_arg->value.string, accept_filter_arg->af_arg, sizeof(struct accept_filter_arg) - offsetof(struct accept_filter_arg, af_arg))) { asprintf(error, "accept_filter_arg.af_arg: expected: %s, actual: %.*s\n", expr->af_arg->value.string, (int)(sizeof(struct accept_filter_arg) - offsetof(struct accept_filter_arg, af_arg)), accept_filter_arg->af_arg); return STATUS_ERR; } } return STATUS_OK; } #endif #ifdef TCP_FUNCTION_BLK static int check_tcp_function_set(struct tcp_function_set_expr *expr, struct tcp_function_set *tcp_function_set, char **error) { if (expr->function_set_name->type != EXPR_ELLIPSIS) { if (strncmp(expr->function_set_name->value.string, tcp_function_set->function_set_name, TCP_FUNCTION_NAME_LEN_MAX)) { asprintf(error, "tcp_function_set.function_set_name: expected: %s, actual: %.*s\n", expr->function_set_name->value.string, TCP_FUNCTION_NAME_LEN_MAX, tcp_function_set->function_set_name); return STATUS_ERR; } } if (check_u32_expr(expr->pcbcnt, tcp_function_set->pcbcnt, "tcp_function_set.pcbcnt", error)) return STATUS_ERR; return STATUS_OK; } #endif static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int script_fd, live_fd, level, optname, live_result, result = STATUS_OK; s32 script_optval, expected; socklen_t script_optlen, live_optlen; bool optlen_provided; void *live_optval; struct expression *val_expression, *len_expression; if (check_arg_count(args, 5, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &level, error)) return STATUS_ERR; if (s32_arg(args, 2, &optname, error)) return STATUS_ERR; len_expression = get_arg(args, 4, error); if (len_expression == NULL) return STATUS_ERR; if (len_expression->type == EXPR_ELLIPSIS) { optlen_provided = false; } else { if (socklen_t_bracketed_arg(args, 4, &script_optlen, error)) return STATUS_ERR; optlen_provided = true; } if (get_s32(syscall->result, &expected, error)) return STATUS_ERR; val_expression = get_arg(args, 3, error); if (val_expression == NULL) { return STATUS_ERR; } switch (val_expression->type) { case EXPR_LINGER: live_optval = malloc(sizeof(struct linger)); live_optlen = (socklen_t)sizeof(struct linger); break; #ifdef SCTP_RTOINFO case EXPR_SCTP_RTOINFO: live_optval = malloc(sizeof(struct sctp_rtoinfo)); live_optlen = (socklen_t)sizeof(struct sctp_rtoinfo); if (get_sctp_assoc_t(val_expression->value.sctp_rtoinfo->srto_assoc_id, &((struct sctp_rtoinfo*)live_optval)->srto_assoc_id, error)) { free(live_optval); return STATUS_ERR; } break; #endif #ifdef SCTP_ASSOCINFO case EXPR_SCTP_ASSOCPARAMS: live_optval = malloc(sizeof(struct sctp_assocparams)); live_optlen = (socklen_t)sizeof(struct sctp_assocparams); if (get_sctp_assoc_t(val_expression->value.sctp_assocparams->sasoc_assoc_id, &((struct sctp_assocparams*) live_optval)->sasoc_assoc_id, error)) { free(live_optval); return STATUS_ERR; } break; #endif #ifdef SCTP_INITMSG case EXPR_SCTP_INITMSG: live_optval = malloc(sizeof(struct sctp_initmsg)); live_optlen = (socklen_t)sizeof(struct sctp_initmsg); break; #endif #if defined(SCTP_AUTH_ACTIVE_KEY) || defined(SCTP_AUTH_DEACTIVATE_KEY) || defined(SCTP_AUTH_DELETE_KEY) case EXPR_SCTP_AUTHKEYID: live_optval = malloc(sizeof(struct sctp_authkeyid)); live_optlen = (socklen_t)sizeof(struct sctp_authkeyid); if (get_sctp_assoc_t(val_expression->value.sctp_authkeyid->scact_assoc_id, &((struct sctp_authkeyid*) live_optval)->scact_assoc_id, error)) { free(live_optval); return STATUS_ERR; } break; #endif #ifdef SCTP_DELAYED_SACK case EXPR_SCTP_SACKINFO: live_optval = malloc(sizeof(struct sctp_sack_info)); live_optlen = (socklen_t)sizeof(struct sctp_sack_info); if (get_sctp_assoc_t(val_expression->value.sctp_sack_info->sack_assoc_id, &((struct sctp_sack_info*) live_optval)->sack_assoc_id, error)) { free(live_optval); return STATUS_ERR; } break; #endif #ifdef SCTP_STATUS case EXPR_SCTP_STATUS: live_optval = malloc(sizeof(struct sctp_status)); live_optlen = (socklen_t)sizeof(struct sctp_status); if (get_sctp_assoc_t(val_expression->value.sctp_status->sstat_assoc_id, &((struct sctp_status*) live_optval)->sstat_assoc_id, error)) { free(live_optval); return STATUS_ERR; } break; #endif #ifdef SCTP_GET_PEER_ADDR_INFO case EXPR_SCTP_PADDRINFO: { struct sctp_paddrinfo_expr *expr_paddrinfo = val_expression->value.sctp_paddrinfo; struct sctp_paddrinfo *live_paddrinfo = malloc(sizeof(struct sctp_paddrinfo)); memset(live_paddrinfo, 0, sizeof(struct sctp_paddrinfo)); if (get_sctp_assoc_t(val_expression->value.sctp_paddrinfo->spinfo_assoc_id, &(live_paddrinfo->spinfo_assoc_id), error)) { free(live_paddrinfo); return STATUS_ERR; } if (get_sockstorage_arg(expr_paddrinfo->spinfo_address, &(live_paddrinfo->spinfo_address), live_fd)) { asprintf(error, "can't determine spinfo_address"); free(live_paddrinfo); return STATUS_ERR; } live_optval = live_paddrinfo; live_optlen = (socklen_t)sizeof(struct sctp_paddrinfo); break; } #endif #ifdef SCTP_PEER_ADDR_PARAMS case EXPR_SCTP_PEER_ADDR_PARAMS: { struct sctp_paddrparams_expr *expr_params = val_expression->value.sctp_paddrparams; struct sctp_paddrparams *live_params = malloc(sizeof(struct sctp_paddrparams)); memset(live_params, 0, sizeof(struct sctp_paddrparams)); if (get_sockstorage_arg(expr_params->spp_address, &live_params->spp_address, live_fd)) { asprintf(error, "can't determine spp_address"); free(live_params); return STATUS_ERR; } if (get_sctp_assoc_t(expr_params->spp_assoc_id, &live_params->spp_assoc_id, error)) { free(live_params); return STATUS_ERR; } live_optval = live_params; live_optlen = (socklen_t)sizeof(struct sctp_paddrparams); break; } #endif #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED) || defined(SCTP_ENABLE_STREAM_RESET) case EXPR_SCTP_ASSOC_VALUE: live_optval = malloc(sizeof(struct sctp_assoc_value)); if (get_sctp_assoc_t(val_expression->value.sctp_assoc_value->assoc_id, &((struct sctp_assoc_value *) live_optval)->assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_assoc_value); break; #endif #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) case EXPR_SCTP_HMACALGO: live_optval = malloc(sizeof(struct sctp_hmacalgo)); live_optlen = (socklen_t)sizeof(struct sctp_hmacalgo); break; #endif #ifdef SCTP_SS_VALUE case EXPR_SCTP_STREAM_VALUE: live_optval = malloc(sizeof(struct sctp_stream_value)); ((struct sctp_stream_value *) live_optval)->assoc_id = 0; if (get_u16(val_expression->value.sctp_stream_value->stream_id, &((struct sctp_stream_value *)live_optval)->stream_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_stream_value); break; #endif #ifdef SCTP_EVENT case EXPR_SCTP_EVENT: live_optval = malloc(sizeof(struct sctp_event)); if (get_sctp_assoc_t(val_expression->value.sctp_event->se_assoc_id, &((struct sctp_event *)live_optval)->se_assoc_id, error)) { free(live_optval); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_event->se_type, &((struct sctp_event *)live_optval)->se_type, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_event); break; #endif #ifdef SCTP_DEFAULT_SEND_PARAM case EXPR_SCTP_SNDRCVINFO: live_optval = malloc(sizeof(struct sctp_sndrcvinfo)); live_optlen = (socklen_t)sizeof(struct sctp_sndrcvinfo); break; #endif #ifdef SCTP_EVENTS case EXPR_SCTP_EVENT_SUBSCRIBE: live_optval = malloc(sizeof(struct sctp_event_subscribe)); live_optlen = (socklen_t)sizeof(struct sctp_event_subscribe); break; #endif #ifdef SCTP_DEFAULT_SNDINFO case EXPR_SCTP_SNDINFO: live_optval = malloc(sizeof(struct sctp_sndinfo)); if (get_sctp_assoc_t(val_expression->value.sctp_sndinfo->snd_assoc_id, &((struct sctp_sndinfo *)live_optval)->snd_assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_sndinfo); break; #endif #ifdef SCTP_DEFAULT_PRINFO case EXPR_SCTP_PRINFO: case EXPR_SCTP_DEFAULT_PRINFO: live_optval = malloc(sizeof(struct sctp_default_prinfo)); if (EXPR_SCTP_DEFAULT_PRINFO) { if (get_sctp_assoc_t(val_expression->value.sctp_default_prinfo->pr_assoc_id, &((struct sctp_default_prinfo *)live_optval)->pr_assoc_id, error)) { free(live_optval); return STATUS_ERR; } } else { ((struct sctp_default_prinfo *)live_optval)->pr_assoc_id = 0; } live_optlen = (socklen_t)sizeof(struct sctp_default_prinfo); break; #endif #ifdef SCTP_PRIMARY_ADDR case EXPR_SCTP_SETPRIM: live_optval = malloc(sizeof(struct sctp_setprim)); if (get_sctp_assoc_t(val_expression->value.sctp_setprim->ssp_assoc_id, &((struct sctp_setprim *)live_optval)->ssp_assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_setprim); break; #endif #ifdef SCTP_ADAPTATION_LAYER case EXPR_SCTP_SETADAPTATION: live_optval = malloc(sizeof(struct sctp_setadaptation)); live_optlen = (socklen_t)sizeof(struct sctp_setadaptation); break; #endif #ifdef SCTP_GET_ASSOC_ID_LIST case EXPR_SCTP_ASSOC_IDS: { int len = expression_list_length(val_expression->value.sctp_assoc_ids->gaids_assoc_id->value.list); live_optval = malloc(sizeof(struct sctp_assoc_ids) + (sizeof(sctp_assoc_t) * len)); live_optlen = (socklen_t)(sizeof(struct sctp_assoc_ids) + (sizeof(sctp_assoc_t) * len)); break; } #endif #if defined(SCTP_PEER_AUTH_CHUNKS) || defined(SCTP_LOCAL_AUTH_CHUNKS) case EXPR_SCTP_AUTHCHUNKS: { int len = expression_list_length(val_expression->value.sctp_authchunks->gauth_chunks->value.list); live_optval = malloc(sizeof(struct sctp_authchunks) + (sizeof(u8) * len)); if (get_sctp_assoc_t(val_expression->value.sctp_authchunks->gauth_assoc_id, &((struct sctp_authchunks *)live_optval)->gauth_assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)(sizeof(struct sctp_authchunks) + (sizeof(u8) * len)); break; } #endif #ifdef SCTP_RESET_STREAMS case EXPR_SCTP_RESET_STREAMS: live_optval = malloc(sizeof(struct sctp_reset_streams)); if (get_sctp_assoc_t(val_expression->value.sctp_reset_streams->srs_assoc_id, &((struct sctp_reset_streams *)live_optval)->srs_assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_reset_streams); break; #endif #ifdef SCTP_ADD_STREAMS case EXPR_SCTP_ADD_STREAMS: live_optval = malloc(sizeof(struct sctp_add_streams)); if (get_sctp_assoc_t(val_expression->value.sctp_add_streams->sas_assoc_id, &((struct sctp_add_streams *)live_optval)->sas_assoc_id, error)) { free(live_optval); return STATUS_ERR; } live_optlen = (socklen_t)sizeof(struct sctp_add_streams); break; #endif #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT case EXPR_SCTP_UDPENCAPS: { struct sctp_udpencaps_expr *expr_udpencaps = val_expression->value.sctp_udpencaps; struct sctp_udpencaps *live_udpencaps = malloc(sizeof(struct sctp_udpencaps)); memset(live_udpencaps, 0, sizeof(struct sctp_udpencaps)); if (get_sctp_assoc_t(expr_udpencaps->sue_assoc_id, &(live_udpencaps->sue_assoc_id), error)) { free(live_udpencaps); return STATUS_ERR; } if (get_sockstorage_arg(expr_udpencaps->sue_address, &(live_udpencaps->sue_address), live_fd)) { asprintf(error, "can't determine sue_address"); free(live_udpencaps); return STATUS_ERR; } live_optval = live_udpencaps; live_optlen = (socklen_t)sizeof(struct sctp_udpencaps); break; } #endif #ifdef SO_ACCEPTFILTER case EXPR_ACCEPT_FILTER_ARG: { struct accept_filter_arg *live_accept_filter_arg = malloc(sizeof(struct accept_filter_arg)); memset(live_accept_filter_arg, 0, sizeof(struct accept_filter_arg)); live_optval = live_accept_filter_arg; live_optlen = (socklen_t)sizeof(struct accept_filter_arg); break; } #endif #ifdef TCP_FUNCTION_BLK case EXPR_TCP_FUNCTION_SET: { struct tcp_function_set *live_tcp_function_set = malloc(sizeof(struct tcp_function_set)); memset(live_tcp_function_set, 0, sizeof(struct tcp_function_set)); live_optval = live_tcp_function_set; live_optlen = (socklen_t)sizeof(struct tcp_function_set); break; } #endif case EXPR_LIST: s32_bracketed_arg(args, 3, &script_optval, error); live_optval = malloc(sizeof(int)); live_optlen = (socklen_t)sizeof(int); break; default: asprintf(error, "unsupported value type: %s", expression_type_to_string(val_expression->type)); return STATUS_ERR; break; } begin_syscall(state, syscall); live_result = getsockopt(live_fd, level, optname, live_optval, &live_optlen); if (end_syscall(state, syscall, CHECK_EXACT, live_result, error)) { free(live_optval); return STATUS_ERR; } if (optlen_provided && live_optlen != script_optlen) { asprintf(error, "optlen: expected: %d actual: %d", (int)script_optlen, (int)live_optlen); free(live_optval); return STATUS_ERR; } if (live_result != 0) { free(live_optval); return STATUS_OK; } switch (val_expression->type) { case EXPR_LINGER: result = check_linger(val_expression->value.linger, live_optval, error); break; #ifdef SCTP_RTOINFO case EXPR_SCTP_RTOINFO: result = check_sctp_rtoinfo(val_expression->value.sctp_rtoinfo, live_optval, error); break; #endif #ifdef SCTP_ASSOCINFO case EXPR_SCTP_ASSOCPARAMS: result = check_sctp_assocparams(val_expression->value.sctp_assocparams, live_optval, error); break; #endif #ifdef SCTP_INITMSG case EXPR_SCTP_INITMSG: result = check_sctp_initmsg(val_expression->value.sctp_initmsg, live_optval, error); break; #endif #if defined(SCTP_AUTH_ACTIVE_KEY) || defined(SCTP_AUTH_DEACTIVATE_KEY) || defined(SCTP_AUTH_DELETE_KEY) case EXPR_SCTP_AUTHKEYID: result = check_sctp_authkeyid(val_expression->value.sctp_authkeyid, live_optval, error); break; #endif #ifdef SCTP_DELAYED_SACK case EXPR_SCTP_SACKINFO: result = check_sctp_sack_info(val_expression->value.sctp_sack_info, live_optval, error); break; #endif #ifdef SCTP_STATUS case EXPR_SCTP_STATUS: result = check_sctp_status(val_expression->value.sctp_status, live_optval, error); break; #endif #ifdef SCTP_GET_PEER_ADDR_INFO case EXPR_SCTP_PADDRINFO: result = check_sctp_paddrinfo(val_expression->value.sctp_paddrinfo, live_optval, error); break; #endif #ifdef SCTP_PEER_ADDR_PARAMS case EXPR_SCTP_PEER_ADDR_PARAMS: result = check_sctp_paddrparams(val_expression->value.sctp_paddrparams, live_optval, error); break; #endif #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED) || defined(SCTP_ENABLE_STREAM_RESET) case EXPR_SCTP_ASSOC_VALUE: result = check_sctp_assoc_value(val_expression->value.sctp_assoc_value, live_optval, error); break; #endif #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) case EXPR_SCTP_HMACALGO: result = check_sctp_hamcalgo(val_expression->value.sctp_hmacalgo, live_optval, error); break; #endif #ifdef SCTP_SS_VALUE case EXPR_SCTP_STREAM_VALUE: result = check_sctp_stream_value(val_expression->value.sctp_stream_value, live_optval, error); break; #endif #ifdef SCTP_EVENT case EXPR_SCTP_EVENT: result = check_sctp_event(val_expression->value.sctp_event, live_optval, error); break; #endif #ifdef SCTP_DEFAULT_SEND_PARAM case EXPR_SCTP_SNDRCVINFO: result = check_sctp_sndrcvinfo(val_expression->value.sctp_sndrcvinfo, live_optval, error); break; #endif #ifdef SCTP_EVENTS case EXPR_SCTP_EVENT_SUBSCRIBE: result = check_sctp_event_subscribe(val_expression->value.sctp_event_subscribe, live_optval, error); break; #endif #ifdef SCTP_DEFAULT_SNDINFO case EXPR_SCTP_SNDINFO: result = check_sctp_sndinfo(val_expression->value.sctp_sndinfo, live_optval, error); break; #endif #ifdef SCTP_DEFAULT_PRINFO case EXPR_SCTP_DEFAULT_PRINFO: result = check_sctp_default_prinfo(val_expression, live_optval, error); break; #endif #ifdef SCTP_PRIMARY_ADDR case EXPR_SCTP_SETPRIM: result = check_sctp_setprim(val_expression->value.sctp_setprim, live_optval, error); break; #endif #ifdef SCTP_ADAPTATION_LAYER case EXPR_SCTP_SETADAPTATION: result = check_sctp_setadaptation(val_expression->value.sctp_setadaptation, live_optval, error); break; #endif #ifdef SCTP_GET_ASSOC_ID_LIST case EXPR_SCTP_ASSOC_IDS: result = check_sctp_assoc_ids(val_expression->value.sctp_assoc_ids, live_optval, error); break; #endif #if defined(SCTP_PEER_AUTH_CHUNKS) || defined(SCTP_LOCAL_AUTH_CHUNKS) case EXPR_SCTP_AUTHCHUNKS: result = check_sctp_authchunks(val_expression->value.sctp_authchunks, live_optval, error); break; #endif #ifdef SCTP_RESET_STREAMS case EXPR_SCTP_RESET_STREAMS: // SCTP_RESET_STREAMS should not be a successfull option break; #endif #ifdef SCTP_ADD_STREAMS case EXPR_SCTP_ADD_STREAMS: // SCTP_ADD_STREAMS should not be a successfull option break; #endif #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT case EXPR_SCTP_UDPENCAPS: result = check_sctp_udpencaps(val_expression->value.sctp_udpencaps, live_optval, error); break; #endif #ifdef SO_ACCEPTFILTER case EXPR_ACCEPT_FILTER_ARG: result = check_accept_filter_arg(val_expression->value.accept_filter_arg, live_optval, error); break; #endif #ifdef TCP_FUNCTION_BLK case EXPR_TCP_FUNCTION_SET: result = check_tcp_function_set(val_expression->value.tcp_function_set, live_optval, error); break; #endif case EXPR_LIST: if (*(int*)live_optval != script_optval) { asprintf(error, "optval: expected: %d actual: %d", (int)script_optval, *(int*)live_optval); result = STATUS_ERR; } break; default: asprintf(error, "Cannot check value type: %s", expression_type_to_string(val_expression->type)); break; } free(live_optval); return result; } static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int script_fd, live_fd, level, optname, optval_s32, result; socklen_t optlen; bool optlen_provided; void *optval = NULL; struct expression *val_expression, *len_expression; struct linger linger; #ifdef SCTP_RTOINFO struct sctp_rtoinfo rtoinfo; #endif #ifdef SCTP_ASSOCINFO struct sctp_assocparams assocparams; #endif #ifdef SCTP_INITMSG struct sctp_initmsg initmsg; #endif #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED) || defined(SCTP_ENABLE_STREAM_RESET) struct sctp_assoc_value assoc_value; #endif #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) struct sctp_hmacalgo *hmacalgo = NULL; #endif #ifdef SCTP_AUTH_ACTIVE_KEY struct sctp_authkeyid authkeyid; #endif #ifdef SCTP_DELAYED_SACK struct sctp_sack_info sack_info; #endif #ifdef SCTP_STATUS struct sctp_status status; #endif #ifdef SCTP_GET_PEER_ADDR_INFO struct sctp_paddrinfo paddrinfo; #endif #if defined(SCTP_SS_VALUE) struct sctp_stream_value stream_value; #endif #ifdef SCTP_EVENT struct sctp_event event; #endif #ifdef SCTP_EVENTS struct sctp_event_subscribe event_subscribe; #endif #ifdef SCTP_DEFAULT_SEND_PARAM struct sctp_sndrcvinfo sndrcvinfo; #endif #ifdef SCTP_DEFAULT_SNDINFO struct sctp_sndinfo sndinfo; #endif #ifdef SCTP_DEFAULT_PRINFO struct sctp_default_prinfo default_prinfo; #endif #ifdef SCTP_PRIMARY_ADDR struct sctp_setprim setprim; #endif #ifdef SCTP_ADAPTATION_LAYER struct sctp_setadaptation setadaptation; #endif #ifdef SCTP_SET_PEER_PRIMARY_ADDR struct sctp_setpeerprim setpeerprim; #endif #ifdef SCTP_AUTH_CHUNK struct sctp_authchunk authchunk; #endif #ifdef SCTP_AUTH_KEY struct sctp_authkey *key = NULL; #endif #ifdef SCTP_PEER_ADDR_PARAMS struct sctp_paddrparams paddrparams; #ifdef linux u32 spp_ipv6_flowlabel; u8 spp_dscp; #endif #endif #ifdef SCTP_RESET_STREAMS struct sctp_reset_streams *reset_streams = NULL; #endif #ifdef SCTP_ADD_STREAMS struct sctp_add_streams add_streams; #endif #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT struct sctp_udpencaps udpencaps; #endif #ifdef SO_ACCEPTFILTER struct accept_filter_arg accept_filter_arg; #endif #ifdef TCP_FUNCTION_BLK struct tcp_function_set tcp_function_set; #endif if (check_arg_count(args, 5, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 1, &level, error)) return STATUS_ERR; if (s32_arg(args, 2, &optname, error)) return STATUS_ERR; len_expression = get_arg(args, 4, error); if (len_expression == NULL) return STATUS_ERR; if (len_expression->type == EXPR_ELLIPSIS) { optlen_provided = false; } else { if (get_socklen_t(len_expression, &optlen, error)) return STATUS_ERR; optlen_provided = true; } val_expression = get_arg(args, 3, error); if (val_expression == NULL) return STATUS_ERR; switch (val_expression->type) { case EXPR_LINGER: if (get_s32(val_expression->value.linger->l_onoff, &linger.l_onoff, error)) return STATUS_ERR; if (get_s32(val_expression->value.linger->l_linger, &linger.l_linger, error)) return STATUS_ERR; optval = &linger; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct linger); } break; case EXPR_STRING: optval = val_expression->value.string; if (!optlen_provided) { optlen = (socklen_t)strlen(val_expression->value.string); } break; case EXPR_LIST: if (s32_bracketed_arg(args, 3, &optval_s32, error)) return STATUS_ERR; optval = &optval_s32; if (!optlen_provided) { optlen = (socklen_t)sizeof(s32); } break; case EXPR_NULL: optval = NULL; if (!optlen_provided) { optlen = (socklen_t)0; } break; #ifdef SCTP_RTOINFO case EXPR_SCTP_RTOINFO: if (get_sctp_assoc_t(val_expression->value.sctp_rtoinfo->srto_assoc_id, &rtoinfo.srto_assoc_id, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_rtoinfo->srto_initial, &rtoinfo.srto_initial, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_rtoinfo->srto_max, &rtoinfo.srto_max, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_rtoinfo->srto_min, &rtoinfo.srto_min, error)) { return STATUS_ERR; } optval = &rtoinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_rtoinfo); } break; #endif #ifdef SCTP_ASSOCINFO case EXPR_SCTP_ASSOCPARAMS: if (get_sctp_assoc_t(val_expression->value.sctp_assocparams->sasoc_assoc_id, &assocparams.sasoc_assoc_id, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_assocparams->sasoc_asocmaxrxt, &assocparams.sasoc_asocmaxrxt, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_assocparams->sasoc_number_peer_destinations, &assocparams.sasoc_number_peer_destinations, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_assocparams->sasoc_peer_rwnd, &assocparams.sasoc_peer_rwnd, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_assocparams->sasoc_local_rwnd, &assocparams.sasoc_local_rwnd, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_assocparams->sasoc_cookie_life, &assocparams.sasoc_cookie_life, error)) { return STATUS_ERR; } optval = &assocparams; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_assocparams); } break; #endif #ifdef SCTP_INITMSG case EXPR_SCTP_INITMSG: if(parse_expression_to_sctp_initmsg(val_expression, &initmsg, error)) { return STATUS_ERR; } optval = &initmsg; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_initmsg); } break; #endif #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED) || defined(SCTP_ENABLE_STREAM_RESET) case EXPR_SCTP_ASSOC_VALUE: if (get_sctp_assoc_t(val_expression->value.sctp_assoc_value->assoc_id, &assoc_value.assoc_id, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_assoc_value->assoc_value, &assoc_value.assoc_value, error)) { return STATUS_ERR; } optval = &assoc_value; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_assoc_value); } break; #endif #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) case EXPR_SCTP_HMACALGO: { struct expression_list *list; size_t size; unsigned int len, i; if (check_type(val_expression->value.sctp_hmacalgo->shmac_idents, EXPR_LIST, error)) { return STATUS_ERR; } list = val_expression->value.sctp_hmacalgo->shmac_idents->value.list; len = expression_list_length(list); size = sizeof(struct sctp_hmacalgo) + len * sizeof(u16); hmacalgo = malloc(size); if (get_u32(val_expression->value.sctp_hmacalgo->shmac_number_of_idents, &hmacalgo->shmac_number_of_idents, error)) { free(hmacalgo); return STATUS_ERR; } for (i = 0; i < len; i++) { struct expression *expr; expr = get_arg(list, i, error); get_u16(expr, &(hmacalgo->shmac_idents[i]), error); } optval = &hmacalgo; if (!optlen_provided) { optlen = (socklen_t)size; } break; } #endif #ifdef SCTP_SS_VALUE case EXPR_SCTP_STREAM_VALUE: stream_value.assoc_id = 0; if (get_u16(val_expression->value.sctp_stream_value->stream_id, &stream_value.stream_id, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_stream_value->stream_value, &stream_value.stream_value, error)) { return STATUS_ERR; } optval = &stream_value; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_stream_value); } break; #endif #if defined(SCTP_AUTH_ACTIVE_KEY) || defined(SCTP_AUTH_DEACTIVATE_KEY) || defined(SCTP_AUTH_DELETE_KEY) case EXPR_SCTP_AUTHKEYID: if (get_sctp_assoc_t(val_expression->value.sctp_authkeyid->scact_assoc_id, &authkeyid.scact_assoc_id, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_authkeyid->scact_keynumber, &authkeyid.scact_keynumber, error)) { return STATUS_ERR; } optval = &authkeyid; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_authkeyid); } break; #endif #ifdef SCTP_DELAYED_SACK case EXPR_SCTP_SACKINFO: if (get_sctp_assoc_t(val_expression->value.sctp_sack_info->sack_assoc_id, &sack_info.sack_assoc_id, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_sack_info->sack_delay, &sack_info.sack_delay, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_sack_info->sack_freq, &sack_info.sack_freq, error)) { return STATUS_ERR; } optval = &sack_info; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_sack_info); } break; #endif #ifdef SCTP_STATUS case EXPR_SCTP_STATUS: if (get_sctp_assoc_t(val_expression->value.sctp_status->sstat_assoc_id, &status.sstat_assoc_id, error)) { return STATUS_ERR; } optval = &status; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_status); } break; #endif #ifdef SCTP_GET_PEER_ADDR_INFO case EXPR_SCTP_PADDRINFO: if (get_sctp_assoc_t(val_expression->value.sctp_paddrinfo->spinfo_assoc_id, &paddrinfo.spinfo_assoc_id, error)) { return STATUS_ERR; } if (get_sockstorage_arg(val_expression->value.sctp_paddrinfo->spinfo_address, &paddrinfo.spinfo_address, live_fd)) { asprintf(error, "can't determine spp_address"); return STATUS_ERR; } optval = &paddrinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_paddrinfo); } break; #endif #ifdef SCTP_EVENT case EXPR_SCTP_EVENT: if (get_sctp_assoc_t(val_expression->value.sctp_event->se_assoc_id, &event.se_assoc_id, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_event->se_type, &event.se_type, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event->se_on, &event.se_on, error)) { return STATUS_ERR; } optval = &event; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_event); } break; #endif #ifdef SCTP_EVENTS case EXPR_SCTP_EVENT_SUBSCRIBE: if (get_u8(val_expression->value.sctp_event_subscribe->sctp_data_io_event, &event_subscribe.sctp_data_io_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_association_event, &event_subscribe.sctp_association_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_address_event, &event_subscribe.sctp_address_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_send_failure_event, &event_subscribe.sctp_send_failure_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_peer_error_event, &event_subscribe.sctp_peer_error_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_shutdown_event, &event_subscribe.sctp_shutdown_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_partial_delivery_event, &event_subscribe.sctp_partial_delivery_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_adaptation_layer_event, &event_subscribe.sctp_adaptation_layer_event, error)) { return STATUS_ERR; } #if !defined(__SunOS_5_11) if (get_u8(val_expression->value.sctp_event_subscribe->sctp_authentication_event, &event_subscribe.sctp_authentication_event, error)) { return STATUS_ERR; } if (get_u8(val_expression->value.sctp_event_subscribe->sctp_sender_dry_event, &event_subscribe.sctp_sender_dry_event, error)) { return STATUS_ERR; } #endif optval = &event_subscribe; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_event_subscribe); } break; #endif #ifdef SCTP_DEFAULT_SEND_PARAM case EXPR_SCTP_SNDRCVINFO: if (parse_expression_to_sctp_sndrcvinfo(val_expression, &sndrcvinfo, true, error)) { return STATUS_ERR; } optval = &sndrcvinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_sndrcvinfo); } break; #endif #ifdef SCTP_DEFAULT_SNDINFO case EXPR_SCTP_SNDINFO: if (get_u16(val_expression->value.sctp_sndinfo->snd_sid, &sndinfo.snd_sid, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_sndinfo->snd_flags, &sndinfo.snd_flags, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_sndinfo->snd_ppid, &sndinfo.snd_ppid, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_sndinfo->snd_context, &sndinfo.snd_context, error)) { return STATUS_ERR; } if (get_sctp_assoc_t(val_expression->value.sctp_sndinfo->snd_assoc_id, &sndinfo.snd_assoc_id, error)) { return STATUS_ERR; } optval = &sndinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_sndinfo); } break; #endif #ifdef SCTP_DEFAULT_PRINFO case EXPR_SCTP_PRINFO: if (get_u16(val_expression->value.sctp_prinfo->pr_policy, &default_prinfo.pr_policy, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_prinfo->pr_value, &default_prinfo.pr_value, error)) { return STATUS_ERR; } default_prinfo.pr_assoc_id = 0; optval = &default_prinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_default_prinfo); } break; case EXPR_SCTP_DEFAULT_PRINFO: if (get_u16(val_expression->value.sctp_default_prinfo->pr_policy, &default_prinfo.pr_policy, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_default_prinfo->pr_value, &default_prinfo.pr_value, error)) { return STATUS_ERR; } if (get_sctp_assoc_t(val_expression->value.sctp_default_prinfo->pr_assoc_id, &default_prinfo.pr_assoc_id, error)) { return STATUS_ERR; } optval = &default_prinfo; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_default_prinfo); } break; #endif #ifdef SCTP_PRIMARY_ADDR case EXPR_SCTP_SETPRIM: if (get_sctp_assoc_t(val_expression->value.sctp_setprim->ssp_assoc_id, &setprim.ssp_assoc_id, error)) { return STATUS_ERR; } if (get_sockstorage_arg(val_expression->value.sctp_setprim->ssp_addr, &setprim.ssp_addr, live_fd)) { asprintf(error, "can't determine ssp_addr"); return STATUS_ERR; } optval = &setprim; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_setprim); } break; #endif #ifdef SCTP_ADAPTATION_LAYER case EXPR_SCTP_SETADAPTATION: if (get_u32(val_expression->value.sctp_setadaptation->ssb_adaptation_ind, &setadaptation.ssb_adaptation_ind, error)) { return STATUS_ERR; } optval = &setadaptation; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_setadaptation); } break; #endif #ifdef SCTP_SET_PEER_PRIMARY_ADDR case EXPR_SCTP_SETPEERPRIM: if (get_sctp_assoc_t(val_expression->value.sctp_setpeerprim->sspp_assoc_id, &setpeerprim.sspp_assoc_id, error)) { return STATUS_ERR; } if (get_sockstorage_arg(val_expression->value.sctp_setpeerprim->sspp_addr, &setpeerprim.sspp_addr, live_fd)) { asprintf(error, "can't determine sspp_addr"); return STATUS_ERR; } optval = &setpeerprim; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_setpeerprim); } break; #endif #ifdef SCTP_AUTH_CHUNK case EXPR_SCTP_AUTHCHUNK: if (get_u8(val_expression->value.sctp_authchunk->sauth_chunk, &authchunk.sauth_chunk, error)) { return STATUS_ERR; } optval = &authchunk; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_authchunk); } break; #endif #ifdef SCTP_AUTH_KEY case EXPR_SCTP_AUTHKEY: { struct expression *key_expr; struct expression_list *list; size_t size; unsigned int i, len; if (check_type(val_expression->value.sctp_authkey->sca_key, EXPR_LIST, error)) { return STATUS_ERR; } list = val_expression->value.sctp_authkey->sca_key->value.list; len = expression_list_length(list); size = sizeof(struct sctp_authkey) + len * sizeof(u8); key = malloc(size); if (get_sctp_assoc_t(val_expression->value.sctp_authkey->sca_assoc_id, &key->sca_assoc_id, error)) { free(key); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_authkey->sca_keynumber, &key->sca_keynumber, error)) { free(key); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_authkey->sca_keylength, &key->sca_keylength, error)) { free(key); return STATUS_ERR; } for (i = 0; i < len; i++) { key_expr = get_arg(list, i, error); if (get_u8(key_expr, &(key->sca_key[i]), error)) { free(key); return STATUS_ERR; } } optval = key; if (!optlen_provided) { optlen = (socklen_t)size; } break; } #endif #ifdef SCTP_PEER_ADDR_PARAMS case EXPR_SCTP_PEER_ADDR_PARAMS: if (get_sctp_assoc_t(val_expression->value.sctp_paddrparams->spp_assoc_id, &paddrparams.spp_assoc_id, error)) { return STATUS_ERR; } if (get_sockstorage_arg(val_expression->value.sctp_paddrparams->spp_address, &paddrparams.spp_address, live_fd)) { asprintf(error, "can't determine spp_address"); return STATUS_ERR; } if (get_u32(val_expression->value.sctp_paddrparams->spp_hbinterval, &paddrparams.spp_hbinterval, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_paddrparams->spp_pathmaxrxt, &paddrparams.spp_pathmaxrxt, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_paddrparams->spp_pathmtu, &paddrparams.spp_pathmtu, error)) { return STATUS_ERR; } if (get_u32(val_expression->value.sctp_paddrparams->spp_flags, &paddrparams.spp_flags, error)) { return STATUS_ERR; } #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) if (get_u32(val_expression->value.sctp_paddrparams->spp_ipv6_flowlabel, &paddrparams.spp_ipv6_flowlabel, error)) { return STATUS_ERR; } #if defined(__SunOS_5_11) if (get_u8(val_expression->value.sctp_paddrparams->spp_dscp, &paddrparams.spp_ipv4_tos, error)) { #else if (get_u8(val_expression->value.sctp_paddrparams->spp_dscp, &paddrparams.spp_dscp, error)) { #endif return STATUS_ERR; } #endif #ifdef linux if (get_u32(val_expression->value.sctp_paddrparams->spp_ipv6_flowlabel, &spp_ipv6_flowlabel, error)) { return STATUS_ERR; } else if (spp_ipv6_flowlabel != 0) { asprintf(error, "Linux doesn't support paddrparams.spp_ipv6_flowlabel"); return STATUS_ERR; } if (get_u8(val_expression->value.sctp_paddrparams->spp_dscp, &spp_dscp, error)) { return STATUS_ERR; } else if (spp_dscp != 0) { asprintf(error, "Linux doesn't support paddrparams.spp_dscp"); return STATUS_ERR; } paddrparams.spp_sackdelay = 0; #endif optval = &paddrparams; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_paddrparams); } break; #endif #ifdef SCTP_RESET_STREAMS case EXPR_SCTP_RESET_STREAMS: { struct expression_list *list; size_t size; unsigned int len, i; if (check_type(val_expression->value.sctp_reset_streams->srs_stream_list, EXPR_LIST, error)) { return STATUS_ERR; } list = val_expression->value.sctp_reset_streams->srs_stream_list->value.list; len = expression_list_length(list); size = sizeof(struct sctp_reset_streams) + len * sizeof(u16); reset_streams = malloc(size); if (get_sctp_assoc_t(val_expression->value.sctp_reset_streams->srs_assoc_id, &reset_streams->srs_assoc_id, error)) { free(reset_streams); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_reset_streams->srs_flags, &reset_streams->srs_flags, error)) { free(reset_streams); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_reset_streams->srs_number_streams, &reset_streams->srs_number_streams, error)) { free(reset_streams); return STATUS_ERR; } for (i = 0; i < len; i++) { struct expression *expr; expr = get_arg(list, i, error); if (expr == NULL) { free(reset_streams); return STATUS_ERR; } if (get_u16(expr, &(reset_streams->srs_stream_list[i]), error)) { free(reset_streams); return STATUS_ERR; } } optval = reset_streams; if (!optlen_provided) { optlen = (socklen_t)size; } break; } #endif #ifdef SCTP_ADD_STREAMS case EXPR_SCTP_ADD_STREAMS: if (get_sctp_assoc_t(val_expression->value.sctp_add_streams->sas_assoc_id, &add_streams.sas_assoc_id, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_add_streams->sas_instrms, &add_streams.sas_instrms, error)) { return STATUS_ERR; } if (get_u16(val_expression->value.sctp_add_streams->sas_outstrms, &add_streams.sas_outstrms, error)) { return STATUS_ERR; } optval = &add_streams; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_add_streams); } break; #endif #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT case EXPR_SCTP_UDPENCAPS: if (get_sctp_assoc_t(val_expression->value.sctp_udpencaps->sue_assoc_id, &udpencaps.sue_assoc_id, error)) { return STATUS_ERR; } if (get_sockstorage_arg(val_expression->value.sctp_udpencaps->sue_address, &udpencaps.sue_address, live_fd)) { asprintf(error, "can't determine sue_address"); return STATUS_ERR; } if (get_u16(val_expression->value.sctp_udpencaps->sue_port, &udpencaps.sue_port, error)) { return STATUS_ERR; } optval = &udpencaps; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct sctp_udpencaps); } break; #endif #ifdef SO_ACCEPTFILTER case EXPR_ACCEPT_FILTER_ARG: memset(&accept_filter_arg, 0, sizeof(struct accept_filter_arg)); if (check_type(val_expression->value.accept_filter_arg->af_name, EXPR_STRING, error)) { return STATUS_ERR; } strlcpy(accept_filter_arg.af_name, val_expression->value.accept_filter_arg->af_name->value.string, offsetof(struct accept_filter_arg, af_arg)); if (val_expression->value.accept_filter_arg->af_arg != NULL && val_expression->value.accept_filter_arg->af_arg->type != EXPR_ELLIPSIS) { if (check_type(val_expression->value.accept_filter_arg->af_arg, EXPR_STRING, error)) { return STATUS_ERR; } strlcpy(accept_filter_arg.af_arg, val_expression->value.accept_filter_arg->af_arg->value.string, sizeof(struct accept_filter_arg) - offsetof(struct accept_filter_arg, af_arg)); } optval = &accept_filter_arg; if (!optlen_provided) optlen = (socklen_t)sizeof(accept_filter_arg); break; #endif #ifdef TCP_FUNCTION_BLK case EXPR_TCP_FUNCTION_SET: memset(&tcp_function_set, 0, sizeof(struct tcp_function_set)); if (check_type(val_expression->value.tcp_function_set->function_set_name, EXPR_STRING, error)) { return STATUS_ERR; } strlcpy(tcp_function_set.function_set_name, val_expression->value.tcp_function_set->function_set_name->value.string, TCP_FUNCTION_NAME_LEN_MAX); if (get_u32(val_expression->value.tcp_function_set->pcbcnt, &tcp_function_set.pcbcnt, error)) { return STATUS_ERR; } optval = &tcp_function_set; if (!optlen_provided) { optlen = (socklen_t)sizeof(struct tcp_function_set); } break; #endif default: asprintf(error, "unsupported value type: %s", expression_type_to_string(val_expression->type)); return STATUS_ERR; break; } assert(val_expression->type == EXPR_NULL || optval != NULL); begin_syscall(state, syscall); result = setsockopt(live_fd, level, optname, optval, optlen); #if defined(SCTP_HMAC_IDENT) && !defined(__SunOS_5_11) free(hmacalgo); #endif #if defined(SCTP_AUTH_KEY) free(key); #endif #if defined(SCTP_RESET_STREAMS) free(reset_streams); #endif return end_syscall(state, syscall, CHECK_EXACT, result, error); } static int syscall_poll(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { struct expression *fds_expression = NULL; struct pollfd *fds = NULL; size_t fds_len; int nfds, timeout, result; int status = STATUS_ERR; if (check_arg_count(args, 3, error)) goto error_out; fds_expression = get_arg(args, 0, error); if (fds_expression == NULL) goto error_out; if (pollfds_new(state, fds_expression, &fds, &fds_len, error)) goto error_out; if (s32_arg(args, 1, &nfds, error)) goto error_out; if (s32_arg(args, 2, &timeout, error)) goto error_out; if (nfds != fds_len) { asprintf(error, "nfds %d does not match %d-element pollfd array", nfds, (int)fds_len); goto error_out; } begin_syscall(state, syscall); result = poll(fds, nfds, timeout); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; if (pollfds_check(fds_expression, fds, fds_len, error)) goto error_out; status = STATUS_OK; error_out: free(fds); return status; } static int syscall_open(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int script_fd, live_fd, result; struct expression *name_expression; char *name; int flags; if (check_arg_count(args, 2, error)) return STATUS_ERR; name_expression = get_arg(args, 0, error); if (check_type(name_expression, EXPR_STRING, error)) return STATUS_ERR; name = name_expression->value.string; if (s32_arg(args, 1, &flags, error)) return STATUS_ERR; begin_syscall(state, syscall); result = open(name, flags); if (end_syscall(state, syscall, CHECK_FD, result, error)) { if (result >= 0) { close(result); } return STATUS_ERR; } if (result >= 0) { live_fd = result; if (get_s32(syscall->result, &script_fd, error)) { close(live_fd); return STATUS_ERR; } if (!insert_new_socket(state, 0, 0, script_fd, live_fd, error)) { close(live_fd); return STATUS_ERR; } } return STATUS_OK; } #if defined(linux) static int syscall_sendfile(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_outfd, script_outfd; int live_infd, script_infd; int script_offset = 0; off_t live_offset; int count, result; int status = STATUS_ERR; if (check_arg_count(args, 4, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_outfd, error)) return STATUS_ERR; if (to_live_fd(state, script_outfd, &live_outfd, error)) return STATUS_ERR; if (s32_arg(args, 1, &script_infd, error)) return STATUS_ERR; if (to_live_fd(state, script_infd, &live_infd, error)) return STATUS_ERR; if (s32_bracketed_arg(args, 2, &script_offset, error)) return STATUS_ERR; if (s32_arg(args, 3, &count, error)) return STATUS_ERR; live_offset = script_offset; begin_syscall(state, syscall); result = sendfile(live_outfd, live_infd, &live_offset, count); status = end_syscall(state, syscall, CHECK_EXACT, result, error); return status; } #endif #if defined(__FreeBSD__) /* Free all the space used by the given sf_hdtr. */ static void sf_hdtr_free(struct sf_hdtr *sf_hdtr) { if (sf_hdtr == NULL) return; iovec_free(sf_hdtr->headers, sf_hdtr->hdr_cnt); iovec_free(sf_hdtr->trailers, sf_hdtr->trl_cnt); free(sf_hdtr); /* XXX */ } /* Allocate and fill in a sf_hdtr described by the given expression. */ static int sf_hdtr_new(struct expression *expression, struct sf_hdtr **sf_hdtr_ptr, char **error) { int status = STATUS_ERR; s32 s32_val = 0; struct sf_hdtr_expr *sf_hdtr_expr; /* input expression from script */ struct sf_hdtr *sf_hdtr = NULL; /* live output */ size_t iov_len; if (check_type(expression, EXPR_SF_HDTR, error)) goto error_out; sf_hdtr_expr = expression->value.sf_hdtr; sf_hdtr = calloc(1, sizeof(struct sf_hdtr)); if (sf_hdtr_expr->headers != NULL) { if (iovec_new(sf_hdtr_expr->headers, &sf_hdtr->headers, &iov_len, error)) goto error_out; } else { iov_len = 0; } if (sf_hdtr_expr->hdr_cnt != NULL) { if (get_s32(sf_hdtr_expr->hdr_cnt, &s32_val, error)) goto error_out; sf_hdtr->hdr_cnt = s32_val; } else { sf_hdtr->hdr_cnt = 0; } if (sf_hdtr->hdr_cnt != iov_len) { asprintf(error, "msg_iovlen %d does not match %d-element headers array", sf_hdtr->hdr_cnt, (int)iov_len); goto error_out; } if (sf_hdtr_expr->trailers != NULL) { if (iovec_new(sf_hdtr_expr->trailers, &sf_hdtr->trailers, &iov_len, error)) goto error_out; } else { iov_len = 0; } if (sf_hdtr_expr->trl_cnt != NULL) { if (get_s32(sf_hdtr_expr->trl_cnt, &s32_val, error)) goto error_out; sf_hdtr->trl_cnt = s32_val; } else { sf_hdtr->trl_cnt = 0; } if (sf_hdtr->trl_cnt != iov_len) { asprintf(error, "msg_iovlen %d does not match %d-element trailers array", sf_hdtr->trl_cnt, (int)iov_len); goto error_out; } status = STATUS_OK; error_out: *sf_hdtr_ptr = sf_hdtr; return status; } static int syscall_sendfile(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { int live_fd, script_fd; int live_s, script_s; off_t offset; size_t nbytes; off_t script_sbytes, live_sbytes; int flags; int result; int status = STATUS_ERR; struct expression *expression; struct sf_hdtr *sf_hdtr = NULL; if (check_arg_count(args, 7, error)) goto error_out; if (s32_arg(args, 0, &script_fd, error)) goto error_out; if (to_live_fd(state, script_fd, &live_fd, error)) goto error_out; if (s32_arg(args, 1, &script_s, error)) goto error_out; if (to_live_fd(state, script_s, &live_s, error)) goto error_out; if ((expression = get_arg(args, 2, error)) == NULL) goto error_out; if (get_off_t(expression, &offset, error)) goto error_out; if ((expression = get_arg(args, 3, error)) == NULL) goto error_out; if (get_size_t(expression, &nbytes, error)) goto error_out; if ((expression = get_arg(args, 4, error)) == NULL) goto error_out; if (sf_hdtr_new(expression, &sf_hdtr, error)) goto error_out; if (off_t_bracketed_arg(args, 5, &script_sbytes, error)) goto error_out; if (s32_arg(args, 6, &flags, error)) goto error_out; begin_syscall(state, syscall); result = sendfile(live_fd, live_s, offset, nbytes, sf_hdtr, &live_sbytes, flags); status = end_syscall(state, syscall, CHECK_EXACT, result, error); if ((status == STATUS_OK) && (script_sbytes != live_sbytes)) { asprintf(error, "Expected sbytes %lld but got %lld", (long long)script_sbytes, (long long)live_sbytes); status = STATUS_ERR; } error_out: sf_hdtr_free(sf_hdtr); return status; } #endif static int syscall_sctp_sendmsg(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int result, script_fd, live_fd; void *msg = NULL; struct sockaddr_storage to; struct sockaddr_storage *to_ptr = &to; socklen_t tolen = 0; size_t len; u32 ppid, flags, timetolive, context; u16 stream_no; struct expression *sockaddr_expr, *tolen_expr, *ppid_expr, *flags_expr, *ttl_expr; struct expression *len_expr, *stream_no_expr, *context_expr; if (check_arg_count(args, 10, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; len_expr = get_arg(args, 2, error); if (get_size_t(len_expr, &len, error)) return STATUS_ERR; sockaddr_expr = get_arg(args, 3, error); if (sockaddr_expr->type == EXPR_ELLIPSIS) { socklen_t socklen = (socklen_t)sizeof(struct sockaddr_storage); if (getpeername(live_fd, (struct sockaddr *)to_ptr, &socklen)) { return STATUS_ERR; } tolen = socklen; } else if (sockaddr_expr->type == EXPR_NULL) { to_ptr = NULL; tolen = 0; } else { if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV4) { memcpy(to_ptr, sockaddr_expr->value.socket_address_ipv4, sizeof(struct sockaddr_in)); tolen = sizeof(struct sockaddr_in); } else if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV6) { memcpy(to_ptr, sockaddr_expr->value.socket_address_ipv6, sizeof(struct sockaddr_in6)); tolen = sizeof(struct sockaddr_in6); } else { asprintf(error, "Bad input for receiver in sctp_sendmsg"); return STATUS_ERR; } } tolen_expr = get_arg(args, 4, error); if (tolen_expr->type != EXPR_ELLIPSIS) if (get_u32(tolen_expr, &tolen, error)) return STATUS_ERR; ppid_expr = get_arg(args, 5, error); if (get_u32(ppid_expr, &ppid, error)) return STATUS_ERR; flags_expr = get_arg(args, 6, error); if (get_u32(flags_expr, &flags, error)) return STATUS_ERR; stream_no_expr =get_arg(args, 7, error); if (get_u16(stream_no_expr, &stream_no, error)) return STATUS_ERR; ttl_expr = get_arg(args, 8, error); if (get_u32(ttl_expr, &timetolive, error)) return STATUS_ERR; context_expr = get_arg(args, 9, error); if (get_u32(context_expr, &context, error)) return STATUS_ERR; msg = calloc(len, 1); assert(msg != NULL); begin_syscall(state, syscall); result = sctp_sendmsg(live_fd, msg, len, (struct sockaddr *) to_ptr, tolen, ppid, flags, stream_no, timetolive, context); free(msg); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { return STATUS_ERR; } return STATUS_OK; #else asprintf(error, "sctp_sendmsg is not supported"); return STATUS_ERR; #endif } #if defined(__FreeBSD__) || defined(linux) || (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) { if (check_u16_expr(expr->sinfo_stream, sctp_sndrcvinfo->sinfo_stream, "sctp_sndrcvinfo.sinfo_stream", error)) return STATUS_ERR; if (check_u16_expr(expr->sinfo_ssn, sctp_sndrcvinfo->sinfo_ssn, "sctp_sndrcvinfo.sinfo_ssn", error)) return STATUS_ERR; if (check_u16_expr(expr->sinfo_flags, sctp_sndrcvinfo->sinfo_flags, "sctp_sndrcvinfo.sinfo_flags", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->sinfo_ppid, sctp_sndrcvinfo->sinfo_ppid, "sctp_sndrcvinfo.sinfo_ppid", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_context, sctp_sndrcvinfo->sinfo_context, "sctp_sndrcvinfo.sinfo_context", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_timetolive, sctp_sndrcvinfo->sinfo_timetolive, "sctp_sndrcvinfo.sinfo_timetolive", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_tsn, sctp_sndrcvinfo->sinfo_tsn, "sctp_sndrcvinfo.sinfo_tsn", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_cumtsn, sctp_sndrcvinfo->sinfo_cumtsn, "sctp_sndrcvinfo.sinfo_cumtsn", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sinfo_assoc_id, sctp_sndrcvinfo->sinfo_assoc_id, "sctp_sndrcvinfo.sinfo_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) static int check_sctp_extrcvinfo(struct sctp_extrcvinfo_expr *expr, struct sctp_extrcvinfo *sctp_extrcvinfo, char** error) { if (check_u16_expr(expr->sinfo_stream, sctp_extrcvinfo->sinfo_stream, "sctp_extrcvinfo.sinfo_stream", error)) return STATUS_ERR; if (check_u16_expr(expr->sinfo_ssn, sctp_extrcvinfo->sinfo_ssn, "sctp_extrcvinfo.sinfo_ssn", error)) return STATUS_ERR; if (check_u16_expr(expr->sinfo_flags, sctp_extrcvinfo->sinfo_flags, "sctp_extrcvinfo.sinfo_flags", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->sinfo_ppid, sctp_extrcvinfo->sinfo_ppid, "sctp_extrcvinfo.sinfo_ppid", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_context, sctp_extrcvinfo->sinfo_context, "sctp_extrcvinfo.sinfo_context", error)) return STATUS_ERR; #if __FreeBSD_version >= 1003000 if (check_u32_expr(expr->sinfo_pr_value, sctp_extrcvinfo->sinfo_pr_value, "sctp_extrcvinfo.sinfo_pr_value", error)) return STATUS_ERR; #else if (check_u32_expr(expr->sinfo_pr_value, sctp_extrcvinfo->sinfo_timetolive, "sctp_extrcvinfo.sinfo_pr_value", error)) return STATUS_ERR; #endif if (check_u32_expr(expr->sinfo_tsn, sctp_extrcvinfo->sinfo_tsn, "sctp_extrcvinfo.sinfo_tsn", error)) return STATUS_ERR; if (check_u32_expr(expr->sinfo_cumtsn, sctp_extrcvinfo->sinfo_cumtsn, "sctp_extrcvinfo.sinfo_cumtsn", error)) return STATUS_ERR; #if __FreeBSD_version >= 1003000 if (check_u16_expr(expr->serinfo_next_flags, sctp_extrcvinfo->serinfo_next_flags, "sctp_extrcvinfo.serinfo_next_flags", error)) return STATUS_ERR; if (check_u16_expr(expr->serinfo_next_stream, sctp_extrcvinfo->serinfo_next_stream, "sctp_extrcvinfo.serinfo_next_stream", error)) return STATUS_ERR; if (check_u32_expr(expr->serinfo_next_aid, sctp_extrcvinfo->serinfo_next_aid, "sctp_extrcvinfo.serinfo_next_aid", error)) return STATUS_ERR; if (check_u32_expr(expr->serinfo_next_length, sctp_extrcvinfo->serinfo_next_length, "sctp_extrcvinfo.serinfo_next_length", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->serinfo_next_ppid, sctp_extrcvinfo->serinfo_next_ppid, "sctp_extrcvinfo.serinfo_next_ppid", error)) return STATUS_ERR; #else if (check_u16_expr(expr->serinfo_next_flags, sctp_extrcvinfo->sreinfo_next_flags, "sctp_extrcvinfo.serinfo_next_flags", error)) return STATUS_ERR; if (check_u16_expr(expr->serinfo_next_stream, sctp_extrcvinfo->sreinfo_next_stream, "sctp_extrcvinfo.serinfo_next_stream", error)) return STATUS_ERR; if (check_u32_expr(expr->serinfo_next_aid, sctp_extrcvinfo->sreinfo_next_aid, "sctp_extrcvinfo.serinfo_next_aid", error)) return STATUS_ERR; if (check_u32_expr(expr->serinfo_next_length, sctp_extrcvinfo->sreinfo_next_length, "sctp_extrcvinfo.serinfo_next_length", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->serinfo_next_ppid, sctp_extrcvinfo->sreinfo_next_ppid, "sctp_extrcvinfo.serinfo_next_ppid", error)) return STATUS_ERR; #endif if (check_sctp_assoc_t_expr(expr->sinfo_assoc_id, sctp_extrcvinfo->sinfo_assoc_id, "sctp_extrcvinfo.sinfo_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int script_fd, live_fd, live_msg_flags = 0, result; void *msg; u32 len; struct sockaddr_storage live_from; socklen_t live_fromlen; struct sctp_sndrcvinfo live_sinfo; struct expression *len_expr, *script_sinfo_expr, *script_msg_flags_expr; struct expression *script_fromlen_expr, *script_from_expr; memset(&live_sinfo, 0, sizeof(struct sctp_sndrcvinfo)); if (check_arg_count(args, 7, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; len_expr = get_arg(args, 2, error); if (get_u32(len_expr, &len, error)) return STATUS_ERR; msg = calloc(len, 1); assert(msg != NULL); begin_syscall(state, syscall); result = sctp_recvmsg(live_fd, msg, len, (struct sockaddr*) &live_from, &live_fromlen, &live_sinfo, &live_msg_flags); free(msg); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { return STATUS_ERR; } script_from_expr = get_arg(args, 3, error); if (check_sockaddr(script_from_expr, (struct sockaddr *)&live_from, error)) { return STATUS_ERR; } script_fromlen_expr = get_arg(args, 4, error); if (check_socklen_t_expr(script_fromlen_expr, live_fromlen, "sctp_recvmsg fromlen", error)) { return STATUS_ERR; } script_sinfo_expr = get_arg(args, 5, error); if (script_sinfo_expr->type != EXPR_ELLIPSIS) { if (check_sctp_sndrcvinfo(script_sinfo_expr->value.sctp_sndrcvinfo, &live_sinfo, error)) { return STATUS_ERR; } } script_msg_flags_expr = get_arg(args, 6, error); if (check_s32_expr(script_msg_flags_expr, live_msg_flags, "sctp_recvmsg msg_flags", error)) { return STATUS_ERR; } return STATUS_OK; #else asprintf(error, "sctp_recvmsg is not supported"); return STATUS_ERR; #endif } #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) static int parse_expression_to_sctp_initmsg(struct expression *expr, struct sctp_initmsg *init, char **error) { if (expr->type == EXPR_SCTP_INITMSG) { struct sctp_initmsg_expr *init_expr = expr->value.sctp_initmsg; if (get_u16(init_expr->sinit_num_ostreams, &init->sinit_num_ostreams, error)) { return STATUS_ERR; } if (get_u16(init_expr->sinit_max_instreams, &init->sinit_max_instreams, error)) { return STATUS_ERR; } if (get_u16(init_expr->sinit_max_attempts, &init->sinit_max_attempts, error)) { return STATUS_ERR; } if (get_u16(init_expr->sinit_max_init_timeo, &init->sinit_max_init_timeo, error)) { return STATUS_ERR; } } else { return STATUS_ERR; } return STATUS_OK; } static int parse_expression_to_sctp_sndrcvinfo(struct expression *expr, struct sctp_sndrcvinfo *info, bool send, char **error) { if (expr->type == EXPR_SCTP_SNDRCVINFO) { struct sctp_sndrcvinfo_expr *sndrcvinfo_expr = expr->value.sctp_sndrcvinfo; if (sndrcvinfo_expr->sinfo_stream->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_stream must be specified"); return STATUS_ERR; } else { info->sinfo_stream = 0; } } else { if (get_u16(sndrcvinfo_expr->sinfo_stream, &info->sinfo_stream, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_ssn->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_ssn must be specified"); return STATUS_ERR; } else { info->sinfo_ssn = 0; } } else { if (get_u16(sndrcvinfo_expr->sinfo_ssn, &info->sinfo_ssn, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_flags->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_flags must be specified"); return STATUS_ERR; } else { info->sinfo_flags = 0; } } else { if (get_u16(sndrcvinfo_expr->sinfo_flags, &info->sinfo_flags, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_ppid->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_ppid must be specified"); return STATUS_ERR; } else { info->sinfo_ppid = 0; } } else { if (get_u32(sndrcvinfo_expr->sinfo_ppid, &info->sinfo_ppid, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_context->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_context must be specified"); return STATUS_ERR; } else { info->sinfo_context = 0; } } else { if (get_u32(sndrcvinfo_expr->sinfo_context, &info->sinfo_context, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_timetolive->type == EXPR_ELLIPSIS) { if (send) { asprintf(error, "sinfo_timetolive must be specified"); return STATUS_ERR; } else { info->sinfo_timetolive = 0; } } else { if (get_u32(sndrcvinfo_expr->sinfo_timetolive, &info->sinfo_timetolive, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_tsn->type == EXPR_ELLIPSIS) { info->sinfo_tsn = 0; } else { if (get_u32(sndrcvinfo_expr->sinfo_tsn, &info->sinfo_tsn, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_cumtsn->type == EXPR_ELLIPSIS) { info->sinfo_cumtsn = 0; } else { if (get_u32(sndrcvinfo_expr->sinfo_cumtsn, &info->sinfo_cumtsn, error)) { return STATUS_ERR; } } if (sndrcvinfo_expr->sinfo_assoc_id->type == EXPR_ELLIPSIS) { info->sinfo_assoc_id = 0; } else { if (get_sctp_assoc_t(sndrcvinfo_expr->sinfo_assoc_id, &info->sinfo_assoc_id, error)) { return STATUS_ERR; } } } else { return STATUS_ERR; } return STATUS_OK; } #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) { if (expr->type == EXPR_SCTP_SNDINFO) { struct sctp_sndinfo_expr *sndinfo_expr = expr->value.sctp_sndinfo; if (get_u16(sndinfo_expr->snd_sid, &info->snd_sid, error)) { return STATUS_ERR; } if (get_u16(sndinfo_expr->snd_flags, &info->snd_flags, error)) { return STATUS_ERR; } if (get_u32(sndinfo_expr->snd_ppid, &info->snd_ppid, error)) { return STATUS_ERR; } if (get_u32(sndinfo_expr->snd_context, &info->snd_context, error)) { return STATUS_ERR; } if (get_sctp_assoc_t(sndinfo_expr->snd_assoc_id, &info->snd_assoc_id, error)) { return STATUS_ERR; } } else { return STATUS_ERR; } return STATUS_OK; } static int parse_expression_to_sctp_authinfo(struct expression *expr, struct sctp_authinfo *info, char **error) { if (expr->type == EXPR_SCTP_AUTHINFO) { struct sctp_authinfo_expr *auth_expr = expr->value.sctp_authinfo; /* * Solaris 10.4 uses the wrong name: auth_keyid instead * of auth_keynumber as specified in * https://tools.ietf.org/html/rfc6458#section-5.3.8 */ #if defined(__SunOS_5_11) if (get_u16(auth_expr->auth_keynumber, &info->auth_keyid, error)) { #else if (get_u16(auth_expr->auth_keynumber, &info->auth_keynumber, error)) { #endif return STATUS_ERR; } } else { return STATUS_ERR; } return STATUS_OK; } static int parse_expression_to_sctp_prinfo(struct expression *expr, struct sctp_prinfo *info, char **error) { if (expr->type == EXPR_SCTP_PRINFO) { struct sctp_prinfo_expr *prinfo_expr = expr->value.sctp_prinfo; if (get_u16(prinfo_expr->pr_policy, &info->pr_policy, error)) { return STATUS_ERR; } if (get_u32(prinfo_expr->pr_value, &info->pr_value, error)) { return STATUS_ERR; } } else { return STATUS_ERR; } return STATUS_OK; } static int parse_expression_to_sctp_sendv_spa(struct expression *expr, struct sctp_sendv_spa *info, char **error) { if (expr->type == EXPR_SCTP_SENDV_SPA) { struct sctp_sendv_spa_expr *spa_expr = expr->value.sctp_sendv_spa; if (get_u32(spa_expr->sendv_flags, &info->sendv_flags, error)) { return STATUS_ERR; } if (spa_expr->sendv_sndinfo->type != EXPR_ELLIPSIS) { if (parse_expression_to_sctp_sndinfo(spa_expr->sendv_sndinfo, &info->sendv_sndinfo, error)) return STATUS_ERR; } if (spa_expr->sendv_sndinfo->type != EXPR_ELLIPSIS) { if (parse_expression_to_sctp_prinfo(spa_expr->sendv_prinfo, &info->sendv_prinfo, error)) return STATUS_ERR; } if (spa_expr->sendv_sndinfo->type != EXPR_ELLIPSIS) { if (parse_expression_to_sctp_authinfo(spa_expr->sendv_authinfo, &info->sendv_authinfo, error)) return STATUS_ERR; } } else { return STATUS_ERR; } return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) static int get_sockaddr_from_list(struct expression *expr, size_t *addr_size, struct sockaddr **addrs, char **error) { if (expr->type == EXPR_LIST) { struct expression_list *addrs_expr_list = (struct expression_list *)expr->value.list; struct expression *temp; int addrlen = expression_list_length(addrs_expr_list); int i = 0; size_t size = 0; char *addr_ptr; for (i = 0; i < addrlen; i++) { temp = get_arg(addrs_expr_list, i, error); if (temp->type == EXPR_SOCKET_ADDRESS_IPV4) { size += sizeof(struct sockaddr_in); } else if (temp->type == EXPR_SOCKET_ADDRESS_IPV6) { size += sizeof(struct sockaddr_in6); } else { *addrs = NULL; *addr_size = 0; return STATUS_ERR; } } *addr_size = size; *addrs = malloc(size); addr_ptr = (char *)*addrs; for (i = 0; i < addrlen; i++) { expr = get_arg(addrs_expr_list, i, error); if (expr->type == EXPR_SOCKET_ADDRESS_IPV4) { size = sizeof(struct sockaddr_in); memcpy(addr_ptr, expr->value.socket_address_ipv4, size); addr_ptr += size; } else if (expr->type == EXPR_SOCKET_ADDRESS_IPV6) { size = sizeof(struct sockaddr_in6); memcpy(addr_ptr, expr->value.socket_address_ipv6, size); addr_ptr += size; } else { *addr_size = 0; free(*addrs); return STATUS_ERR; } } return STATUS_OK; } else { addr_size = 0; *addrs = NULL; return STATUS_ERR; } } #endif static int syscall_sctp_send(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int script_fd, live_fd, flags, result; size_t len; void *msg; struct expression *len_expr, *info_expr; struct sctp_sndrcvinfo info; if (check_arg_count(args, 5, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; len_expr = get_arg(args, 2, error); if (get_size_t(len_expr, &len, error)) { return STATUS_ERR; } info_expr = get_arg(args, 3, error); if (check_type(info_expr, EXPR_SCTP_SNDRCVINFO, error)) { return STATUS_ERR; } if (parse_expression_to_sctp_sndrcvinfo(info_expr, &info, true, error)) { return STATUS_ERR; } if (s32_arg(args, 4, &flags, error)) { return STATUS_ERR; } msg = calloc(len, 1); assert(msg != NULL); begin_syscall(state, syscall); result = sctp_send(live_fd, msg, len, &info, flags); free(msg); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { return STATUS_ERR; } if (check_sctp_sndrcvinfo(info_expr->value.sctp_sndrcvinfo, &info, error)) { return STATUS_ERR; } return STATUS_OK; #else asprintf(error, "sctp_send is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_sendx(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) int script_fd, live_fd, flags, addrcnt, result; size_t len; void *msg = NULL; struct sockaddr *addrs = NULL; struct sctp_sndrcvinfo info; struct expression *info_expr, *len_expr, *addrs_expr; if (check_arg_count(args, 7, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (ellipsis_arg(args, 1, error)) return STATUS_ERR; len_expr = get_arg(args, 2, error); if (get_size_t(len_expr, &len, error)) { return STATUS_ERR; } addrs_expr = get_arg(args, 3, error); if (addrs_expr->type == EXPR_NULL) { addrs = NULL; } else if (addrs_expr->type == EXPR_SOCKET_ADDRESS_IPV4 || addrs_expr->type == EXPR_SOCKET_ADDRESS_IPV6 || addrs_expr->type == EXPR_ELLIPSIS) { addrs = malloc(sizeof(struct sockaddr_storage)); if (get_sockstorage_arg(addrs_expr, (struct sockaddr_storage *)addrs, live_fd)) { asprintf(error, "can't determine addrs"); goto error_out; } } else if (addrs_expr->type == EXPR_LIST) { size_t size; if (get_sockaddr_from_list(addrs_expr, &size, &addrs, error)) { goto error_out; } } else { goto error_out; } if (s32_arg(args, 4, &addrcnt, error)) goto error_out; info_expr = get_arg(args, 5, error); if (check_type(info_expr, EXPR_SCTP_SNDRCVINFO, error)) { goto error_out; } if (parse_expression_to_sctp_sndrcvinfo(info_expr, &info, true, error)) { goto error_out; } if (s32_arg(args, 6, &flags, error)) { goto error_out; } msg = calloc(len, 1); assert(msg != NULL); begin_syscall(state, syscall); result = sctp_sendx(live_fd, msg, len, addrs, addrcnt, &info, flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { goto error_out; } if (check_sctp_sndrcvinfo(info_expr->value.sctp_sndrcvinfo, &info, error)) { goto error_out; } free(msg); free(addrs); return STATUS_OK; error_out: free(msg); free(addrs); return STATUS_ERR; #else asprintf(error, "sctp_send is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_sendv(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) int script_fd, live_fd, iovcnt, addrcnt, result, flags; u32 infotype; size_t script_iovec_list_len = 0; socklen_t infolen; struct sockaddr *addrs = NULL; void *info; struct iovec *iov = NULL; struct expression *iovec_expr_list, *iovcnt_expr, *addrs_expr, *addrcnt_expr; struct expression *info_expr, *infolen_expr, *infotype_expr, *flags_expr; struct sctp_sndinfo sndinfo; struct sctp_prinfo prinfo; struct sctp_authinfo authinfo; struct sctp_sendv_spa spa; addrs = NULL; if (check_arg_count(args, 9, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; iovec_expr_list = get_arg(args, 1, error); if (iovec_expr_list == NULL) return STATUS_ERR; iovec_new(iovec_expr_list, &iov, &script_iovec_list_len, error); iovcnt_expr = get_arg(args, 2, error); if (iovcnt_expr == NULL) goto error_out; if (get_s32(iovcnt_expr, &iovcnt, error)) goto error_out; addrs_expr = get_arg(args, 3, error); if (addrs_expr == NULL) goto error_out; if (addrs_expr->type == EXPR_SOCKET_ADDRESS_IPV4 || addrs_expr->type == EXPR_SOCKET_ADDRESS_IPV6 || addrs_expr->type == EXPR_ELLIPSIS) { addrs = malloc(sizeof(struct sockaddr_storage)); if (get_sockstorage_arg(addrs_expr, (struct sockaddr_storage *)addrs, live_fd)) { asprintf(error, "can't determine addrs"); goto error_out; } } else if (addrs_expr->type == EXPR_LIST) { size_t size; if (get_sockaddr_from_list(addrs_expr, &size, &addrs, error)) { goto error_out; } } else if (addrs_expr->type != EXPR_NULL) { goto error_out; } addrcnt_expr = get_arg(args, 4, error); if (addrcnt_expr == NULL) goto error_out; if (get_s32(addrcnt_expr, &addrcnt, error)) goto error_out; info_expr = get_arg(args, 5, error); if (info_expr == NULL) goto error_out; if (info_expr->type == EXPR_SCTP_SNDINFO) { if (parse_expression_to_sctp_sndinfo(info_expr, &sndinfo, error)) goto error_out; info = &sndinfo; } else if (info_expr->type == EXPR_SCTP_PRINFO) { if (parse_expression_to_sctp_prinfo(info_expr, &prinfo, error)) goto error_out; info = &prinfo; } else if (info_expr->type == EXPR_SCTP_AUTHINFO) { if (parse_expression_to_sctp_authinfo(info_expr, &authinfo, error)) goto error_out; info = &authinfo; } else if (info_expr->type == EXPR_SCTP_SENDV_SPA) { if (parse_expression_to_sctp_sendv_spa(info_expr, &spa, error)) goto error_out; info = &spa; } else if (info_expr->type == EXPR_NULL) { info = NULL; } else { asprintf(error, "Bad input for info"); goto error_out; } infolen_expr = get_arg(args, 6, error); if (infolen_expr == NULL) goto error_out; if (get_u32(infolen_expr, &infolen, error)) goto error_out; infotype_expr = get_arg(args, 7, error); if (infotype_expr == NULL) goto error_out; if (get_u32(infotype_expr, &infotype, error)) goto error_out; flags_expr = get_arg(args, 8, error); if (flags_expr == NULL) goto error_out; if (get_s32(flags_expr, &flags, error)) goto error_out; begin_syscall(state, syscall); result = sctp_sendv(live_fd, iov, iovcnt, addrs, addrcnt, info, infolen, infotype, flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { free(addrs); iovec_free(iov, script_iovec_list_len); return STATUS_ERR; } free(addrs); iovec_free(iov, script_iovec_list_len); return STATUS_OK; error_out: if (iov != NULL) iovec_free(iov, script_iovec_list_len); free(addrs); return STATUS_ERR; #else asprintf(error, "sctp_sendv is not supported"); return STATUS_ERR; #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) { if (check_u16_expr(expr->rcv_sid, sctp_rcvinfo->rcv_sid, "sctp_rcvinfo.rcv_sid", error)) return STATUS_ERR; if (check_u16_expr(expr->rcv_ssn, sctp_rcvinfo->rcv_ssn, "sctp_rcvinfo.rcv_ssn", error)) return STATUS_ERR; if (check_u16_expr(expr->rcv_flags, sctp_rcvinfo->rcv_flags, "sctp_rcvinfo.rcv_flags", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->rcv_ppid, sctp_rcvinfo->rcv_ppid, "sctp_rcvinfo.rcv_ppid", error)) return STATUS_ERR; if (check_u32_expr(expr->rcv_tsn, sctp_rcvinfo->rcv_tsn, "sctp_rcvinfo.rcv_tsn", error)) return STATUS_ERR; if (check_u32_expr(expr->rcv_cumtsn, sctp_rcvinfo->rcv_cumtsn, "sctp_rcvinfo.rcv_cumtsn", error)) return STATUS_ERR; if (check_u32_expr(expr->rcv_context, sctp_rcvinfo->rcv_context, "sctp_rcvinfo.rcv_context", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->rcv_assoc_id, sctp_rcvinfo->rcv_assoc_id, "sctp_rcvinfo.rcv_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #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, char **error) { if (check_u16_expr(expr->nxt_sid, sctp_nxtinfo->nxt_sid, "sctp_nxtinfo.nxt_sid", error)) return STATUS_ERR; if (check_u16_expr(expr->nxt_flags, sctp_nxtinfo->nxt_flags, "sctp_nxtinfo.nxt_flags", error)) return STATUS_ERR; if (check_u32_hton_expr(expr->nxt_ppid, sctp_nxtinfo->nxt_ppid, "sctp_nxtinfo.nxt_ppid", error)) return STATUS_ERR; if (check_u32_expr(expr->nxt_length, sctp_nxtinfo->nxt_length, "sctp_nxtinfo.nxt_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->nxt_assoc_id, sctp_nxtinfo->nxt_assoc_id, "sctp_nxtinfo.nxt_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_assoc_change(struct sctp_assoc_change_expr *expr, struct sctp_assoc_change *sctp_event, char **error) { if (check_u16_expr(expr->sac_type, sctp_event->sac_type, "sctp_assoc_change.sac_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sac_flags, sctp_event->sac_flags, "sctp_assoc_change.sac_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sac_length, sctp_event->sac_length, "sctp_assoc_change.sac_length", error)) return STATUS_ERR; if (check_u16_expr(expr->sac_state, sctp_event->sac_state, "sctp_assoc_change.sac_state", error)) return STATUS_ERR; if (check_u16_expr(expr->sac_error, sctp_event->sac_error, "sctp_assoc_change.sac_error", error)) return STATUS_ERR; if (check_u16_expr(expr->sac_outbound_streams, sctp_event->sac_outbound_streams, "sctp_assoc_change.sac_outbound_streams", error)) return STATUS_ERR; if (check_u16_expr(expr->sac_inbound_streams, sctp_event->sac_inbound_streams, "sctp_assoc_change.sac_inbound_streams", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sac_assoc_id, sctp_event->sac_assoc_id, "sctp_assoc_change.sac_assoc_id", error)) return STATUS_ERR; #if defined(__SunOS_5_11) if (check_u8array_expr(expr->sac_info, (u8 *)(&sctp_event->sac_assoc_id + 1), sctp_event->sac_length - sizeof(struct sctp_assoc_change), "sctp_assoc_change.sac_info", error)) #else if (check_u8array_expr(expr->sac_info, sctp_event->sac_info, sctp_event->sac_length - sizeof(struct sctp_assoc_change), "sctp_assoc_change.sac_info", error)) #endif return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_paddr_change(struct sctp_paddr_change_expr *expr, struct sctp_paddr_change *sctp_event, char **error) { if (check_u16_expr(expr->spc_type, sctp_event->spc_type, "sctp_paddr_change.spc_type", error)) return STATUS_ERR; if (check_u16_expr(expr->spc_flags, sctp_event->spc_flags, "sctp_paddr_change.spc_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->spc_length, sctp_event->spc_length, "sctp_paddr_change.spc_length", error)) return STATUS_ERR; if (check_sockaddr(expr->spc_aaddr, (struct sockaddr *)&sctp_event->spc_aaddr, error)) return STATUS_ERR; if (check_u32_expr(expr->spc_state, sctp_event->spc_state, "sctp_paddr_change.spc_state", error)) return STATUS_ERR; if (check_u32_expr(expr->spc_error, sctp_event->spc_error, "sctp_paddr_change.spc_error", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->spc_assoc_id, sctp_event->spc_assoc_id, "sctp_paddr_change.spc_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_remote_error(struct sctp_remote_error_expr *expr, struct sctp_remote_error *sctp_event, char **error) { if (check_u16_expr(expr->sre_type, sctp_event->sre_type, "sctp_remote_error.sre_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sre_flags, sctp_event->sre_flags, "sctp_remote_error.sre_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sre_length, sctp_event->sre_length, "sctp_remote_error.sre_length", error)) return STATUS_ERR; if (check_u16_expr(expr->sre_error, sctp_event->sre_error, "sctp_remote_error.sre_error", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sre_assoc_id, sctp_event->sre_assoc_id, "sctp_remote_error.sre_assoc_id", error)) return STATUS_ERR; #if defined(__SunOS_5_11) if (check_u8array_expr(expr->sre_data, (u8 *)(&sctp_event->sre_assoc_id + 1), sctp_event->sre_length - sizeof(struct sctp_remote_error), "sctp_remote_error.sre_data", error)) #else if (check_u8array_expr(expr->sre_data, sctp_event->sre_data, sctp_event->sre_length - sizeof(struct sctp_remote_error), "sctp_remote_error.sre_data", error)) #endif return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_send_failed(struct sctp_send_failed_expr *expr, struct sctp_send_failed *sctp_event, char **error) { if (check_u16_expr(expr->ssf_type, sctp_event->ssf_type, "sctp_send_failed.ssf_type", error)) return STATUS_ERR; if (check_u16_expr(expr->ssf_flags, sctp_event->ssf_flags, "sctp_send_failed.ssf_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->ssf_length, sctp_event->ssf_length, "sctp_send_failed.ssf_length", error)) return STATUS_ERR; if (check_u32_expr(expr->ssf_error, sctp_event->ssf_error, "sctp_send_failed.ssf_error", error)) return STATUS_ERR; if (expr->ssf_info->type != EXPR_ELLIPSIS) { if (check_sctp_sndrcvinfo(expr->ssf_info->value.sctp_sndrcvinfo, &sctp_event->ssf_info, error)) return STATUS_ERR; } if (check_sctp_assoc_t_expr(expr->ssf_assoc_id, sctp_event->ssf_assoc_id, "sctp_send_failed.ssf_assoc_id", error)) return STATUS_ERR; #if defined(__SunOS_5_11) if (check_u8array_expr(expr->ssf_data, (u8 *)(&sctp_event->ssf_assoc_id + 1), sctp_event->ssf_length - sizeof(struct sctp_send_failed), "sctp_send_failed.ssf_data", error)) #else if (check_u8array_expr(expr->ssf_data, sctp_event->ssf_data, sctp_event->ssf_length - sizeof(struct sctp_send_failed), "sctp_send_failed.ssf_data", error)) #endif return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_shutdown_event(struct sctp_shutdown_event_expr *expr, struct sctp_shutdown_event *sctp_event, char **error) { if (check_u16_expr(expr->sse_type, sctp_event->sse_type, "sctp_shutdown_event.sse_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sse_flags, sctp_event->sse_flags, "sctp_shutdown_event.sse_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sse_length, sctp_event->sse_length, "sctp_shutdown_event.sse_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sse_assoc_id, sctp_event->sse_assoc_id, "sctp_shutdown_event.sse_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_adaptation_event(struct sctp_adaptation_event_expr *expr, struct sctp_adaptation_event *sctp_event, char **error) { if (check_u16_expr(expr->sai_type, sctp_event->sai_type, "sctp_adaptation_event.sai_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sai_flags, sctp_event->sai_flags, "sctp_adaptation_event.sai_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sai_length, sctp_event->sai_length, "sctp_adaptation_event.sai_length", error)) return STATUS_ERR; if (check_u32_expr(expr->sai_adaptation_ind, sctp_event->sai_adaptation_ind, "sctp_adaptation_event.sai_adaptation_ind", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sai_assoc_id, sctp_event->sai_assoc_id, "sctp_adaptation_event.sai_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_pdapi_event(struct sctp_pdapi_event_expr *expr, struct sctp_pdapi_event *sctp_event, char **error) { if (check_u16_expr(expr->pdapi_type, sctp_event->pdapi_type, "sctp_pdapi_event.pdapi_type", error)) return STATUS_ERR; if (check_u16_expr(expr->pdapi_flags, sctp_event->pdapi_flags, "sctp_pdapi_event.pdapi_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->pdapi_length, sctp_event->pdapi_length, "sctp_pdapi_event.pdapi_length", error)) return STATUS_ERR; if (check_u32_expr(expr->pdapi_indication, sctp_event->pdapi_indication, "sctp_pdapi_event.pdapi_indication", error)) return STATUS_ERR; #if defined(linux) if (expr->pdapi_stream->type != EXPR_ELLIPSIS) { asprintf(error, "Linux doesn't support sctp_pdapi_event.pdapi_stream"); return STATUS_ERR; } #else if (check_u32_expr(expr->pdapi_stream, sctp_event->pdapi_stream, "sctp_pdapi_event.pdapi_stream", error)) return STATUS_ERR; #endif #if defined(linux) if (expr->pdapi_seq->type != EXPR_ELLIPSIS) { asprintf(error, "Linux doesn't support sctp_pdapi_event.pdapi_seq"); return STATUS_ERR; } #else if (check_u32_expr(expr->pdapi_seq, sctp_event->pdapi_seq, "sctp_pdapi_event.pdapi_seq", error)) return STATUS_ERR; #endif if (check_sctp_assoc_t_expr(expr->pdapi_assoc_id, sctp_event->pdapi_assoc_id, "sctp_pdapi_event.pdapi_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) static int check_sctp_authkey_event(struct sctp_authkey_event_expr *expr, struct sctp_authkey_event *sctp_event, char **error) { if (check_u16_expr(expr->auth_type, sctp_event->auth_type, "sctp_authkey_event.auth_type", error)) return STATUS_ERR; if (check_u16_expr(expr->auth_flags, sctp_event->auth_flags, "sctp_authkey_event.auth_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->auth_length, sctp_event->auth_length, "sctp_authkey_event.auth_length", error)) return STATUS_ERR; if (check_u16_expr(expr->auth_keynumber, sctp_event->auth_keynumber, "sctp_authkey_event.auth_keynumber", error)) return STATUS_ERR; if (check_u32_expr(expr->auth_indication, sctp_event->auth_indication, "sctp_authkey_event.auth_indication", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->auth_assoc_id, sctp_event->auth_assoc_id, "sctp_authkey_event.auth_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_sender_dry_event(struct sctp_sender_dry_event_expr *expr, struct sctp_sender_dry_event *sctp_event, char **error) { if (check_u16_expr(expr->sender_dry_type, sctp_event->sender_dry_type, "sctp_sender_dry.sender_dry_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sender_dry_flags, sctp_event->sender_dry_flags, "sctp_sender_dry.sender_dry_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sender_dry_length, sctp_event->sender_dry_length, "sctp_sender_dry.sender_dry_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->sender_dry_assoc_id, sctp_event->sender_dry_assoc_id, "sctp_sender_dry.sender_dry_assoc_id", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_send_failed_event(struct sctp_send_failed_event_expr *expr, struct sctp_send_failed_event *sctp_event, char **error) { if (check_u16_expr(expr->ssfe_type, sctp_event->ssfe_type, "sctp_send_failed.ssfe_type", error)) return STATUS_ERR; if (check_u16_expr(expr->ssfe_flags, sctp_event->ssfe_flags, "sctp_send_failed.ssfe_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->ssfe_length, sctp_event->ssfe_length, "sctp_send_failed.ssfe_length", error)) return STATUS_ERR; if (check_u32_expr(expr->ssfe_error, sctp_event->ssfe_error, "sctp_send_failed.ssfe_error", error)) return STATUS_ERR; if (expr->ssfe_info->type != EXPR_ELLIPSIS) { if (check_sctp_sndinfo(expr->ssfe_info->value.sctp_sndinfo, &sctp_event->ssfe_info, error)) return STATUS_ERR; } if (check_sctp_assoc_t_expr(expr->ssfe_assoc_id, sctp_event->ssfe_assoc_id, "sctp_send_failed.ssfe_assoc_id", error)) return STATUS_ERR; if (check_u8array_expr(expr->ssfe_data, sctp_event->ssfe_data, sctp_event->ssfe_length - sizeof(struct sctp_send_failed_event), "sctp_send_failed_event.ssfe_data", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_tlv(struct sctp_tlv_expr *expr, struct sctp_tlv *sctp_tlv, char **error) { if (check_u16_expr(expr->sn_type, sctp_tlv->sn_type, "sctp_tlv.sn_type", error)) return STATUS_ERR; if (check_u16_expr(expr->sn_flags, sctp_tlv->sn_flags, "sctp_tlv.sn_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->sn_length, sctp_tlv->sn_length, "sctp_tlv.sn_length", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) static int check_sctp_stream_reset_event(struct sctp_stream_reset_event_expr *expr, struct sctp_stream_reset_event *sctp_stream_reset_event, char **error) { if (check_u16_expr(expr->strreset_type, sctp_stream_reset_event->strreset_type, "sctp_stream_reset_event.strreset_type", error)) return STATUS_ERR; if (check_u16_expr(expr->strreset_flags, sctp_stream_reset_event->strreset_flags, "sctp_stream_reset_event.strreset_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->strreset_length, sctp_stream_reset_event->strreset_length, "sctp_stream_reset_event.strreset_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->strreset_assoc_id, sctp_stream_reset_event->strreset_assoc_id, "sctp_stream_reset_event.strreset_assoc_id", error)) return STATUS_ERR; if (check_u16array_expr(expr->strreset_stream_list, sctp_stream_reset_event->strreset_stream_list, (sctp_stream_reset_event->strreset_length - sizeof(u16) - sizeof(u16) - sizeof(u32) - sizeof(sctp_assoc_t)) / sizeof(u16), "sctp_stream_reset_event.strreset_stream_list", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) static int check_sctp_assoc_reset_event(struct socket *socket, struct sctp_assoc_reset_event_expr *expr, struct sctp_assoc_reset_event *sctp_assoc_reset_event, char **error) { u32 local_diff = 0; u32 remote_diff = 0; assert(socket != NULL); local_diff = socket->live.local_initial_tsn - socket->script.local_initial_tsn; remote_diff = socket->live.remote_initial_tsn - socket->script.remote_initial_tsn; if (check_u16_expr(expr->assocreset_type, sctp_assoc_reset_event->assocreset_type, "sctp_assoc_reset_event.assocreset_type", error)) return STATUS_ERR; if (check_u16_expr(expr->assocreset_flags, sctp_assoc_reset_event->assocreset_flags, "sctp_assoc_reset_event.assocreset_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->assocreset_length, sctp_assoc_reset_event->assocreset_length, "sctp_assoc_reset_event.assocreset_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->assocreset_assoc_id, sctp_assoc_reset_event->assocreset_assoc_id, "sctp_assoc_reset_event.assocreset_assoc_id", error)) return STATUS_ERR; if (check_u32_expr(expr->assocreset_local_tsn, sctp_assoc_reset_event->assocreset_local_tsn - local_diff, "sctp_assoc_reset_event.assocreset_local_tsn", error)) return STATUS_ERR; if (check_u32_expr(expr->assocreset_remote_tsn, sctp_assoc_reset_event->assocreset_remote_tsn - remote_diff, "sctp_assoc_reset_event.assocreset_remote_tsn", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) static int check_sctp_stream_change_event(struct sctp_stream_change_event_expr *expr, struct sctp_stream_change_event *sctp_stream_change_event, char **error) { if (check_u16_expr(expr->strchange_type, sctp_stream_change_event->strchange_type, "sctp_stream_change_event.strchange_type", error)) return STATUS_ERR; if (check_u16_expr(expr->strchange_flags, sctp_stream_change_event->strchange_flags, "sctp_stream_change_event.strchange_flags", error)) return STATUS_ERR; if (check_u32_expr(expr->strchange_length, sctp_stream_change_event->strchange_length, "sctp_stream_change_event.strchange_length", error)) return STATUS_ERR; if (check_sctp_assoc_t_expr(expr->strchange_assoc_id, sctp_stream_change_event->strchange_assoc_id, "sctp_stream_change_event.strchange_assoc_id", error)) return STATUS_ERR; if (check_u16_expr(expr->strchange_instrms, sctp_stream_change_event->strchange_instrms, "sctp_stream_change_event.strchange_instrms", error)) return STATUS_ERR; if (check_u16_expr(expr->strchange_outstrms, sctp_stream_change_event->strchange_outstrms, "sctp_stream_change_event.strchange_outstrms", error)) return STATUS_ERR; return STATUS_OK; } #endif #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP) && defined(MSG_NOTIFICATION)) || defined(__SunOS_5_11) static int check_sctp_notification(struct socket *socket, struct iovec *iov, struct expression *iovec_expr, char **error) { struct expression_list *iovec_expr_list; struct expression *script_iov, *script_iov_base; if (iovec_expr == NULL) return STATUS_ERR; if (check_type(iovec_expr, EXPR_LIST, error)) return STATUS_ERR; iovec_expr_list = iovec_expr->value.list; int i=0; while (iovec_expr_list != NULL) { script_iov = iovec_expr_list->expression; if (check_type(script_iov, EXPR_IOVEC, error)) return STATUS_ERR; script_iov_base = script_iov->value.iovec->iov_base; switch (script_iov_base->type) { case EXPR_SCTP_ASSOC_CHANGE: if (check_sctp_assoc_change(script_iov_base->value.sctp_assoc_change, (struct sctp_assoc_change *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_PADDR_CHANGE: if (check_sctp_paddr_change(script_iov_base->value.sctp_paddr_change, (struct sctp_paddr_change *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_REMOTE_ERROR: if (check_sctp_remote_error(script_iov_base->value.sctp_remote_error, (struct sctp_remote_error *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_SEND_FAILED: if (check_sctp_send_failed(script_iov_base->value.sctp_send_failed, (struct sctp_send_failed *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_SHUTDOWN_EVENT: if (check_sctp_shutdown_event(script_iov_base->value.sctp_shutdown_event, (struct sctp_shutdown_event *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_ADAPTATION_EVENT: if (check_sctp_adaptation_event(script_iov_base->value.sctp_adaptation_event, (struct sctp_adaptation_event *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_PDAPI_EVENT: if (check_sctp_pdapi_event(script_iov_base->value.sctp_pdapi_event, (struct sctp_pdapi_event *) iov[i].iov_base, error)) return STATUS_ERR; break; case EXPR_SCTP_AUTHKEY_EVENT: #if defined(__SunOS_5_11) asprintf(error, "Solaris does not support SCTP_AUTHKEY_EVENT"); return STATUS_ERR; #else if (check_sctp_authkey_event(script_iov_base->value.sctp_authkey_event, (struct sctp_authkey_event *) iov[i].iov_base, error)) return STATUS_ERR; #endif break; case EXPR_SCTP_SENDER_DRY_EVENT: if (check_sctp_sender_dry_event(script_iov_base->value.sctp_sender_dry_event, (struct sctp_sender_dry_event *) iov[i].iov_base, error)) return STATUS_ERR; break; #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) case EXPR_SCTP_SEND_FAILED_EVENT: if (check_sctp_send_failed_event(script_iov_base->value.sctp_send_failed_event, (struct sctp_send_failed_event *) iov[i].iov_base, error)) return STATUS_ERR; break; #endif case EXPR_SCTP_TLV: if (check_sctp_tlv(script_iov_base->value.sctp_tlv, (struct sctp_tlv *) iov[i].iov_base, error)) return STATUS_ERR; break; #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) case EXPR_SCTP_STREAM_RESET_EVENT: if (check_sctp_stream_reset_event(script_iov_base->value.sctp_stream_reset_event, (struct sctp_stream_reset_event *) iov[i].iov_base, error)) return STATUS_ERR; break; #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) case EXPR_SCTP_ASSOC_RESET_EVENT: if (check_sctp_assoc_reset_event(socket, script_iov_base->value.sctp_assoc_reset_event, (struct sctp_assoc_reset_event *) iov[i].iov_base, error)) return STATUS_ERR; break; #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) case EXPR_SCTP_STREAM_CHANGE_EVENT: if (check_sctp_stream_change_event(script_iov_base->value.sctp_stream_change_event, (struct sctp_stream_change_event *) iov[i].iov_base, error)) return STATUS_ERR; break; #endif case EXPR_ELLIPSIS: break; default: asprintf(error, "Bad type for iov_base. Can't check type %s", expression_type_to_string(script_iov_base->type)); return STATUS_ERR; break; } i++; iovec_expr_list = iovec_expr_list->next; } return STATUS_OK; } #endif #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) static int check_sctp_recvv_rn(struct sctp_recvv_rn_expr *expr, struct sctp_recvv_rn *sctp_recvv_rn, char **error) { if (expr->recvv_rcvinfo->type != EXPR_ELLIPSIS) { if (check_type(expr->recvv_rcvinfo, EXPR_SCTP_RCVINFO, error)) return STATUS_ERR; if (check_sctp_rcvinfo(expr->recvv_rcvinfo->value.sctp_rcvinfo, &(sctp_recvv_rn->recvv_rcvinfo), error)) return STATUS_ERR; } if (expr->recvv_nxtinfo->type != EXPR_ELLIPSIS) { if (check_type(expr->recvv_nxtinfo, EXPR_SCTP_NXTINFO, error)) return STATUS_ERR; if (check_sctp_nxtinfo(expr->recvv_nxtinfo->value.sctp_nxtinfo, &(sctp_recvv_rn->recvv_nxtinfo), error)) return STATUS_ERR; } return STATUS_OK; } #endif static int syscall_sctp_recvv(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) || (defined(linux) && defined(HAVE_SCTP_SENDV)) int flags, iovlen, script_fd, live_fd, result; size_t script_iovec_list_len = 0; unsigned int infotype = 0; socklen_t infolen, fromlen; void *info; struct iovec *iov; struct sockaddr *from = NULL; struct expression *iovec_expr_list, *iovcnt_expr, *addr_expr, *fromlen_expr; struct expression *info_expr, *infotype_expr, *flags_expr; struct sctp_recvv_rn recvv_rn; struct sctp_rcvinfo rcvinfo; struct sctp_nxtinfo nxtinfo; if (check_arg_count(args, 9, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; iovec_expr_list = get_arg(args, 1, error); if (iovec_expr_list == NULL) return STATUS_ERR; iovec_new(iovec_expr_list, &iov, &script_iovec_list_len, error); iovcnt_expr = get_arg(args, 2, error); if (iovcnt_expr == NULL) goto error_out; if (get_s32(iovcnt_expr, &iovlen, error)) goto error_out; fromlen_expr = get_arg(args, 4, error); if (fromlen_expr == NULL) goto error_out; if (get_u32(fromlen_expr, &fromlen, error)) goto error_out; info_expr = get_arg(args, 5, error); if (info_expr == NULL) goto error_out; if (info_expr->type == EXPR_NULL) { info = NULL; } else if (info_expr->type == EXPR_SCTP_RCVINFO) { info = &rcvinfo; } else if (info_expr->type == EXPR_SCTP_NXTINFO) { info = &nxtinfo; } else if (info_expr->type == EXPR_SCTP_RECVV_RN) { info = &recvv_rn; } else { goto error_out; } if (u32_bracketed_arg(args, 6, &infolen, error)) { goto error_out; } infotype = 0; flags = 0; addr_expr = get_arg(args, 3, error); if (addr_expr == NULL) goto error_out; if (addr_expr->type != EXPR_NULL) { from = malloc(fromlen); } begin_syscall(state, syscall); result = sctp_recvv(live_fd, iov, iovlen, (struct sockaddr *)from, &fromlen, info, &infolen, &infotype, &flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; if (from != NULL) { if (check_sockaddr(addr_expr, from, error)) goto error_out; } infotype_expr = get_arg(args, 7, error); if (infotype_expr->type != EXPR_ELLIPSIS) { s32 script_infotype; if (s32_bracketed_arg(args, 7, &script_infotype, error)) goto error_out; if (infotype != script_infotype) { asprintf(error, "sctp_recvv infotype: expected: %u actual: %u", script_infotype, infotype); goto error_out; } } switch(infotype) { case SCTP_RECVV_NOINFO: if (infolen != 0) { asprintf(error, "infolen returned bad size for null. expected 0, actual %u", infolen); goto error_out; } break; case SCTP_RECVV_RCVINFO: if (infolen != sizeof(struct sctp_rcvinfo)) { asprintf(error, "infolen returned bad size for sctp_rcvinfo. expected %zu, actual %u", sizeof(struct sctp_rcvinfo), infolen); goto error_out; } if (info == NULL) { asprintf(error, "info is NULL for sctp_rcvinfo."); goto error_out; } if (check_sctp_rcvinfo(info_expr->value.sctp_rcvinfo, info, error)) goto error_out; break; case SCTP_RECVV_NXTINFO: if (infolen != sizeof(struct sctp_nxtinfo)) { asprintf(error, "infolen returned bad size for sctp_nxtinfo. expected %zu, actual %u", sizeof(struct sctp_nxtinfo), infolen); goto error_out; } if (info == NULL) { asprintf(error, "info is NULL for sctp_nxtinfo."); goto error_out; } if (check_sctp_nxtinfo(info_expr->value.sctp_nxtinfo, info, error)) goto error_out; break; case SCTP_RECVV_RN: if (infolen != sizeof(struct sctp_recvv_rn)) { asprintf(error, "infolen returned bad size for sctp_recvv_rn. expected %zu, actual %u", sizeof(struct sctp_recvv_rn), infolen); goto error_out; } if (info == NULL) { asprintf(error, "info is NULL for sctp_recvv_rn."); goto error_out; } if (check_sctp_recvv_rn(info_expr->value.sctp_recvv_rn, info, error)) goto error_out; break; default: goto error_out; break; } flags_expr = get_arg(args, 8, error); if (flags_expr->type != EXPR_ELLIPSIS) { s32 script_flags; if (s32_bracketed_arg(args, 8, &script_flags, error)) goto error_out; if (flags != script_flags) { asprintf(error, "sctp_recvv flags bad return value. expected %d, actual %d", script_flags, flags); goto error_out; #if defined(MSG_NOTIFICATION) } else if (flags & MSG_NOTIFICATION) { struct socket *socket = find_socket_by_script_fd(state, script_fd); if (check_sctp_notification(socket, iov, iovec_expr_list, error)) goto error_out; #endif } } free(from); iovec_free(iov, script_iovec_list_len); return STATUS_OK; error_out: free(from); iovec_free(iov, script_iovec_list_len); return STATUS_ERR; #else asprintf(error, "sctp_recvv is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_bindx(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, addrcnt, flags, result; struct sockaddr_storage addrs; struct expression *addr_list; socklen_t addrlen = sizeof(addrs); if (check_arg_count(args, 4, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; if (s32_arg(args, 2, &addrcnt, error)) return STATUS_ERR; if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; addr_list = get_arg(args, 1, error); if (check_type(addr_list, EXPR_LIST, error)) return STATUS_ERR; if (ellipsis_arg(addr_list->value.list, 0, error)) return STATUS_ERR; //TODO: Modify run_syscall_bind for multihoming if (run_syscall_bind( state, (struct sockaddr *)&addrs, &addrlen, error)) return STATUS_ERR; begin_syscall(state, syscall); result = sctp_bindx(live_fd, (struct sockaddr *)&addrs, addrcnt, flags); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) { return STATUS_ERR; } return STATUS_OK; #else asprintf(error, "sctp_bindx is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_connectx(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, addrcnt, result; struct sockaddr_storage live_addr; struct expression *addrs_expr, *assoc_expr; socklen_t live_addrlen = sizeof(live_addr); sctp_assoc_t live_associd; if (check_arg_count(args, 4, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; addrs_expr = get_arg(args, 1, error); if (check_type(addrs_expr, EXPR_LIST, error)) return STATUS_ERR; if (ellipsis_arg(addrs_expr->value.list, 0, error)) return STATUS_ERR; if (s32_arg(args, 2, &addrcnt, error)) return STATUS_ERR; //TODO: modify for Multihoming if (run_syscall_connect( state, script_fd, true, (struct sockaddr *)&live_addr, &live_addrlen, error)) return STATUS_ERR; begin_syscall(state, syscall); result = sctp_connectx(live_fd, (struct sockaddr *)&live_addr, addrcnt, &live_associd); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) return STATUS_ERR; assoc_expr = get_arg(args, 3, error); if (check_type(assoc_expr, EXPR_LIST, error)) return STATUS_ERR; if (check_arg_count(assoc_expr->value.list, 1, error)) return STATUS_ERR; assoc_expr = get_arg(assoc_expr->value.list, 0, error); if (check_sctp_assoc_t_expr(assoc_expr, live_associd, "sctp_connectx assoc_id", error)) return STATUS_ERR; return STATUS_OK; #else asprintf(error, "sctp_connectx is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_peeloff(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, result, script_new_fd; sctp_assoc_t assoc_id; struct expression *expr_assoc; if (check_arg_count(args, 2, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; expr_assoc = get_arg(args, 1, error); if (get_sctp_assoc_t(expr_assoc, &assoc_id, error)) return STATUS_ERR; //check connection Type and set assoc_id if one-to-many style socket begin_syscall(state, syscall); result = sctp_peeloff(live_fd, assoc_id); if (end_syscall(state, syscall, CHECK_FD, result, error)) return STATUS_ERR; if (get_s32(syscall->result, &script_new_fd, error)) return STATUS_ERR; if (run_syscall_sctp_peeloff(state, script_fd, script_new_fd, result, error)) { asprintf(error, "can't copy socket definition"); return STATUS_ERR; } return STATUS_OK; #else asprintf(error, "sctp_peeloff is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_getpaddrs(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, result; sctp_assoc_t assoc_id; struct expression *assoc_expr, *addrs_list_expr; /* * Solaris uses a signature for sctp_paddrs() different from what is specified * in https://tools.ietf.org/html/rfc6458#section-9.3. */ #if defined(__SunOS_5_11) void *live_addrs; #else struct sockaddr *live_addrs; #endif if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; assoc_expr = get_arg(args, 1, error); if (get_sctp_assoc_t(assoc_expr, &assoc_id, error)) return STATUS_ERR; begin_syscall(state, syscall); result = sctp_getpaddrs(live_fd, assoc_id, &live_addrs); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; addrs_list_expr = get_arg(args, 2, error); if (addrs_list_expr->type != EXPR_ELLIPSIS) { int list_length = 0, i = 0; #if defined(__SunOS_5_11) struct sockaddr *live_addr = (struct sockaddr *)live_addrs; #else struct sockaddr *live_addr = live_addrs; #endif if (check_type(addrs_list_expr, EXPR_LIST, error)) { goto error_out; } list_length = get_arg_count(addrs_list_expr->value.list); if (list_length != result) { asprintf(error, "Bad length of struct sockaddr array. expected: %d, actual %d", list_length, result); goto error_out; } for (i = 0; i < result; i++) { struct expression *script_addr_expr; script_addr_expr = get_arg(addrs_list_expr->value.list, i, error); if (check_sockaddr(script_addr_expr, live_addr, error)) { goto error_out; } if (live_addr->sa_family == AF_INET) { live_addr = (struct sockaddr *)((caddr_t)live_addr + sizeof(struct sockaddr_in)); } else if (live_addr->sa_family == AF_INET6) { live_addr = (struct sockaddr *)((caddr_t)live_addr + sizeof(struct sockaddr_in6)); } else { asprintf(error, "Bad Type of addrs[%d]", i); goto error_out; } } } return STATUS_OK; error_out: sctp_freepaddrs(live_addrs); return STATUS_ERR; #else asprintf(error, "sctp_getpaddrs is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_freepaddrs(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) struct expression *addrs_expr; if (check_arg_count(args, 1, error)) return STATUS_ERR; addrs_expr = get_arg(args, 0, error); if (check_type(addrs_expr, EXPR_LIST, error)) return STATUS_ERR; if (ellipsis_arg(addrs_expr->value.list, 0, error)) return STATUS_ERR; return STATUS_OK; #else asprintf(error, "sctp_freepaddrs is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_getladdrs(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, result; sctp_assoc_t assoc_id; struct expression *assoc_expr, *addrs_list_expr; /* * Solaris uses a signature for sctp_laddrs() different from what is specified * in https://tools.ietf.org/html/rfc6458#section-9.5. */ #if defined(__SunOS_5_11) void *live_addrs; #else struct sockaddr *live_addrs; #endif if (check_arg_count(args, 3, error)) return STATUS_ERR; if (s32_arg(args, 0, &script_fd, error)) return STATUS_ERR; if (to_live_fd(state, script_fd, &live_fd, error)) return STATUS_ERR; assoc_expr = get_arg(args, 1, error); if (get_sctp_assoc_t(assoc_expr, &assoc_id, error)) return STATUS_ERR; begin_syscall(state, syscall); result = sctp_getladdrs(live_fd, assoc_id, &live_addrs); if (end_syscall(state, syscall, CHECK_EXACT, result, error)) goto error_out; addrs_list_expr = get_arg(args, 2, error); if (addrs_list_expr->type != EXPR_ELLIPSIS) { int list_length = 0, i = 0; #if defined(__SunOS_5_11) struct sockaddr *live_addr = (struct sockaddr *)live_addrs; #else struct sockaddr *live_addr = live_addrs; #endif if (check_type(addrs_list_expr, EXPR_LIST, error)) { goto error_out; } list_length = get_arg_count(addrs_list_expr->value.list); if (list_length != result) { asprintf(error, "Bad length of struct sockaddr array. expected: %d, actual %d", list_length, result); goto error_out; } for (i = 0; i < result; i++) { struct expression *script_addr_expr; script_addr_expr = get_arg(addrs_list_expr->value.list, i, error); if (check_sockaddr(script_addr_expr, live_addr, error)) { goto error_out; } if (live_addr->sa_family == AF_INET) { live_addr = (struct sockaddr *)((caddr_t)live_addr + sizeof(struct sockaddr_in)); } else if (live_addr->sa_family == AF_INET6) { live_addr = (struct sockaddr *)((caddr_t)live_addr + sizeof(struct sockaddr_in6)); } else { asprintf(error, "Bad Type of addrs[%d]", i); goto error_out; } } } return STATUS_OK; error_out: sctp_freeladdrs(live_addrs); return STATUS_ERR; #else asprintf(error, "sctp_getladdrs is not supported"); return STATUS_ERR; #endif } static int syscall_sctp_freeladdrs(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) struct expression *addrs_expr; if (check_arg_count(args, 1, error)) return STATUS_ERR; addrs_expr = get_arg(args, 0, error); if (check_type(addrs_expr, EXPR_LIST, error)) return STATUS_ERR; if (ellipsis_arg(addrs_expr->value.list, 0, error)) return STATUS_ERR; return STATUS_OK; #else asprintf(error, "sctp_freeladdrs is not supported"); return STATUS_ERR; #endif } /* A dispatch table with all the system calls that we support... */ struct system_call_entry { const char *name; int (*function) (struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error); }; struct system_call_entry system_call_table[] = { {"socket", syscall_socket}, {"bind", syscall_bind}, {"listen", syscall_listen}, {"accept", syscall_accept}, {"connect", syscall_connect}, {"read", syscall_read}, {"readv", syscall_readv}, {"recv", syscall_recv}, {"recvfrom", syscall_recvfrom}, {"recvmsg", syscall_recvmsg}, {"write", syscall_write}, {"writev", syscall_writev}, {"send", syscall_send}, {"sendto", syscall_sendto}, {"sendmsg", syscall_sendmsg}, {"fcntl", syscall_fcntl}, {"ioctl", syscall_ioctl}, {"close", syscall_close}, {"shutdown", syscall_shutdown}, {"getsockopt", syscall_getsockopt}, {"setsockopt", syscall_setsockopt}, {"poll", syscall_poll}, {"open", syscall_open}, #if defined(linux) || defined(__FreeBSD__) {"sendfile", syscall_sendfile}, #endif {"sctp_bindx", syscall_sctp_bindx}, {"sctp_peeloff", syscall_sctp_peeloff}, {"sctp_getpaddrs", syscall_sctp_getpaddrs}, {"sctp_freepaddrs", syscall_sctp_freepaddrs}, {"sctp_getladdrs", syscall_sctp_getladdrs}, {"sctp_freeladdrs", syscall_sctp_freeladdrs}, {"sctp_sendmsg", syscall_sctp_sendmsg}, {"sctp_recvmsg", syscall_sctp_recvmsg}, {"sctp_connectx", syscall_sctp_connectx}, {"sctp_send", syscall_sctp_send}, {"sctp_sendx", syscall_sctp_sendx}, {"sctp_sendv", syscall_sctp_sendv}, {"sctp_recvv", syscall_sctp_recvv} }; /* Evaluate the system call arguments and invoke the system call. */ static void invoke_system_call( struct state *state, struct event *event, struct syscall_spec *syscall) { char *error = NULL, *script_path = NULL; const char *name = syscall->name; struct expression_list *args = NULL; int i = 0; int result = 0; DEBUGP("%d: invoke call: %s\n", event->line_number, syscall->name); /* Wait for the right time before firing off this event. */ wait_for_event(state); /* Find and invoke the handler for this system call. */ for (i = 0; i < ARRAY_SIZE(system_call_table); ++i) if (strcmp(name, system_call_table[i].name) == 0) break; if (i == ARRAY_SIZE(system_call_table)) { asprintf(&error, "Unknown system call: '%s'", name); goto error_out; } /* Evaluate script symbolic expressions to get live numeric args for * system calls. */ if (evaluate_expression_list(syscall->arguments, &args, &error)) goto error_out; /* Run the system call. */ result = system_call_table[i].function(state, syscall, args, &error); free_expression_list(args); if (result == STATUS_ERR) goto error_out; return; error_out: script_path = strdup(state->config->script_path); /* * To free resources, we need to free the state. But this can only * be done from the main thread, but this is the syscall thread. * * So not calling state_free(state, 1); */ state_free(state, 1); die("%s:%d: runtime error in %s call: %s\n", script_path, event->line_number, syscall->name, error); free(script_path); free(error); } /* Wait for the system call thread to go idle. To avoid mystifying * hangs when scripts specify overlapping time ranges for blocking * system calls, we limit the duration of our waiting to 1 second. */ static int await_idle_thread(struct state *state) { struct timespec end_time = { .tv_sec = 0, .tv_nsec = 0 }; const int MAX_WAIT_SECS = 1; while (state->syscalls->state != SYSCALL_IDLE) { /* On the first time through the loop, calculate end time. */ if (end_time.tv_sec == 0) { /* clock_gettime is available in MacOS 10.12 and higher. */ #if !defined(__APPLE__) || (defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) if (clock_gettime(CLOCK_REALTIME, &end_time) != 0) die_perror("clock_gettime"); #else struct timeval tv; if (gettimeofday(&tv, NULL) != 0) die_perror("gettimeofday"); TIMEVAL_TO_TIMESPEC(&tv, &end_time); #endif end_time.tv_sec += MAX_WAIT_SECS; } /* Wait for a signal or our timeout end_time to arrive. */ DEBUGP("main thread: awaiting idle syscall thread\n"); int err = pthread_cond_timedwait(&state->syscalls->idle, &state->mutex, &end_time); if (err == ETIMEDOUT) return STATUS_ERR; else if (err != 0) die_strerror("pthread_cond_timedwait", err); } return STATUS_OK; } #if !defined(__SunOS_5_11) static int yield(void) { #if defined(linux) return pthread_yield(); #elif defined(__FreeBSD__) || defined(__OpenBSD__) pthread_yield(); return 0; #elif defined(__NetBSD__) return sched_yield(); #elif defined(__APPLE__) pthread_yield_np(); return 0; #endif /* defined(__APPLE__) */ } #endif /* Enqueue the system call for the syscall thread and wake up the thread. */ static void enqueue_system_call( struct state *state, struct event *event, struct syscall_spec *syscall) { char *error = NULL, *script_path = NULL; int err; bool done = false; /* This MUST NOT be called from the system call thread. */ assert(!pthread_equal(pthread_self(), state->syscalls->thread)); /* Wait if there are back-to-back blocking system calls. */ if (await_idle_thread(state)) { asprintf(&error, "blocking system call while another blocking " "system call is already in progress"); goto error_out; } /* Enqueue the system call info and wake up the syscall thread. */ DEBUGP("main thread: signal enqueued\n"); state->syscalls->state = SYSCALL_ENQUEUED; if ((err = pthread_cond_signal(&state->syscalls->enqueued)) != 0) die_strerror("pthread_cond_signal", err); /* Wait for the syscall thread to dequeue and start the system call. */ while (state->syscalls->state == SYSCALL_ENQUEUED) { DEBUGP("main thread: waiting for dequeued signal; " "state: %d\n", state->syscalls->state); if ((err = pthread_cond_wait(&state->syscalls->dequeued, &state->mutex)) != 0) { die_strerror("pthread_cond_wait", err); } } /* Wait for the syscall thread to block or finish the call. */ while (!done) { #if defined(linux) pid_t thread_id; #elif defined(__FreeBSD__) int thread_id; #elif defined(__APPLE__) mach_port_t thread_id; #else int thread_id; /* FIXME */ #endif /* Unlock and yield so the system call thread can make * the system call in a timely fashion. */ DEBUGP("main thread: unlocking and yielding\n"); thread_id = state->syscalls->thread_id; run_unlock(state); #if defined(__SunOS_5_11) yield(); #else if (yield() != 0) die_perror("yield"); #endif DEBUGP("main thread: checking syscall thread state\n"); if (is_thread_sleeping(getpid(), thread_id)) done = true; /* Grab the lock again and see if the thread is idle. */ DEBUGP("main thread: locking and reading state\n"); run_lock(state); if (state->syscalls->state == SYSCALL_IDLE) done = true; } DEBUGP("main thread: continuing after syscall\n"); return; error_out: script_path = strdup(state->config->script_path); state_free(state, 1); die("%s:%d: runtime error in %s call: %s\n", script_path, event->line_number, syscall->name, error); free(script_path); free(error); } void run_system_call_event( struct state *state, struct event *event, struct syscall_spec *syscall) { DEBUGP("%d: system call: %s\n", event->line_number, syscall->name); if (is_blocking_syscall(syscall)) enqueue_system_call(state, event, syscall); else invoke_system_call(state, event, syscall); } /* The code executed by our system call thread, which executes * blocking system calls. */ static void *system_call_thread(void *arg) { struct state *state = (struct state *)arg; char *error = NULL; struct event *event = NULL; struct syscall_spec *syscall = NULL; int err; bool done = false; #if defined(__FreeBSD__) pthread_set_name_np(pthread_self(), "syscall thread"); #elif defined(__APPLE__) pthread_setname_np("syscall thread"); #endif DEBUGP("syscall thread: starting and locking\n"); run_lock(state); #if defined(linux) state->syscalls->thread_id = gettid(); if (state->syscalls->thread_id < 0) die_perror("gettid"); #elif defined(__FreeBSD__) state->syscalls->thread_id = pthread_getthreadid_np(); #elif defined(__APPLE__) state->syscalls->thread_id = pthread_mach_thread_np(pthread_self()); #else state->syscalls->thread_id = 0; /* FIXME */ #endif while (!done) { DEBUGP("syscall thread: in state %d\n", state->syscalls->state); switch (state->syscalls->state) { case SYSCALL_IDLE: DEBUGP("syscall thread: waiting\n"); if ((err = pthread_cond_wait(&state->syscalls->enqueued, &state->mutex))) { die_strerror("pthread_cond_wait", err); } break; case SYSCALL_RUNNING: case SYSCALL_DONE: assert(0); /* should not be reached */ break; case SYSCALL_ENQUEUED: DEBUGP("syscall thread: invoking syscall\n"); /* Remember the syscall event, since below we * release the global lock and the main thread * will move on to other, later events. */ event = state->event; syscall = event->event.syscall; assert(event->type == SYSCALL_EVENT); state->syscalls->event = event; state->syscalls->live_end_usecs = -1; /* Make the system call. Note that our callees * here will release the global lock before * making the actual system call and then * re-acquire it after the system call returns * and before returning to us. */ invoke_system_call(state, event, syscall); /* Check end time for the blocking system call. */ assert(state->syscalls->live_end_usecs >= 0); if (verify_time(state, event->time_type, syscall->end_usecs, 0, state->syscalls->live_end_usecs, "system call return", &error)) { die("%s:%d: %s\n", state->config->script_path, event->line_number, error); } /* Mark our thread idle and wake the main * thread if it's waiting for this call to * finish. */ assert(state->syscalls->state == SYSCALL_DONE); state->syscalls->state = SYSCALL_IDLE; state->syscalls->event = NULL; state->syscalls->live_end_usecs = -1; DEBUGP("syscall thread: now idle\n"); if ((err = pthread_cond_signal(&state->syscalls->idle)) != 0) die_strerror("pthread_cond_signal", err); break; case SYSCALL_EXITING: done = true; break; /* omitting default so compiler will catch missing cases */ } } DEBUGP("syscall thread: unlocking and exiting\n"); run_unlock(state); return NULL; } struct syscalls *syscalls_new(struct state *state) { struct syscalls *syscalls = calloc(1, sizeof(struct syscalls)); int err; syscalls->state = SYSCALL_IDLE; if ((err = pthread_create(&syscalls->thread, NULL, system_call_thread, state)) != 0) { die_strerror("pthread_create", err); } if (((err = pthread_cond_init(&syscalls->idle, NULL)) != 0) || ((err = pthread_cond_init(&syscalls->enqueued, NULL)) != 0) || ((err = pthread_cond_init(&syscalls->dequeued, NULL)) != 0)) { die_strerror("pthread_cond_init", err); } return syscalls; } void syscalls_free(struct state *state, struct syscalls *syscalls, int about_to_die) { int status, err; /* Wait a bit for the thread to go idle. */ status = await_idle_thread(state); if ((status == STATUS_ERR) && (about_to_die == 0)) { die("%s:%d: runtime error: exiting while " "a blocking system call is in progress\n", state->config->script_path, syscalls->event->line_number); } if (status == STATUS_ERR) { /* Canceling the thread. */ DEBUGP("main thread: canceling syscall thread \n"); pthread_cancel(syscalls->thread); } else { /* Send a request to terminate the thread. */ DEBUGP("main thread: signaling syscall thread to exit\n"); syscalls->state = SYSCALL_EXITING; if ((err = pthread_cond_signal(&syscalls->enqueued)) != 0) die_strerror("pthread_cond_signal", err); } /* Release the lock briefly and wait for syscall thread to finish. */ run_unlock(state); DEBUGP("main thread: unlocking, waiting for syscall thread exit\n"); void *thread_result = NULL; if ((err = pthread_join(syscalls->thread, &thread_result)) != 0) die_strerror("pthread_join", err); DEBUGP("main thread: joined syscall thread; relocking\n"); run_lock(state); if (((err = pthread_cond_destroy(&syscalls->idle)) != 0) || ((err = pthread_cond_destroy(&syscalls->enqueued)) != 0) || ((err = pthread_cond_destroy(&syscalls->dequeued)) != 0)) { die_strerror("pthread_cond_destroy", err); } memset(syscalls, 0, sizeof(*syscalls)); /* to help catch bugs */ free(syscalls); }