static int unix_server_start(server_generic_t *server) { int ret; struct sockaddr_un *sa = (struct sockaddr_un *) server->sa; server->sock = socket(AF_UNIX, SOCK_STREAM, 0); if ( server->sock < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error creating UNIX socket: %s", strerror(errno)); ret = is_unix_socket_already_used(server->sock, sa, server->slen); if ( ret < 0 ) { close(server->sock); return ret; } ret = generic_server(server->sock, server->sa, server->slen); if ( ret < 0 ) { close(server->sock); return ret; } /* * Everyone should be able to access the filesystem object * representing our socket. */ ret = chmod(sa->sun_path, S_IRWXU|S_IRWXG|S_IRWXO); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "could not set permission on UNIX socket: %s", strerror(errno)); return 0; }
static int get_profile_analyzerid(prelude_client_profile_t *cp) { int ret; FILE *fd; char *ptr, filename[256], buf[256]; prelude_client_profile_get_profile_dirname(cp, filename, sizeof(filename)); if ( access(filename, R_OK|X_OK) < 0 ) { if ( errno == ENOENT ) return prelude_error_verbose(PRELUDE_ERROR_PROFILE, "profile '%s' does not exist", cp->name); else if ( errno == EACCES ) return prelude_error_verbose(PRELUDE_ERROR_PROFILE, "could not open profile '%s': insufficient permission", cp->name); } prelude_client_profile_get_analyzerid_filename(cp, filename, sizeof(filename)); fd = fopen(filename, "r"); if ( ! fd ) return prelude_error_verbose(PRELUDE_ERROR_PROFILE, "could not open '%s' for reading", filename); ptr = fgets(buf, sizeof(buf), fd); fclose(fd); if ( ! ptr ) return prelude_error_verbose(PRELUDE_ERROR_PROFILE, "could not read analyzerID from '%s'", filename); ret = sscanf(buf, "%" PRELUDE_PRIu64, &cp->analyzerid); if ( ret != 1 ) return prelude_error_verbose(PRELUDE_ERROR_PROFILE, "'%s' is not a valid analyzerID", buf); return 0; }
/* * If the UNIX socket already exist, check if it is in use. * if it is not, delete it. * * FIXME: Using connect for this is dirty. * * return 1 if the socket is already in use. * return 0 if the socket is unused. * retuir -1 on error. */ static int is_unix_socket_already_used(int sock, struct sockaddr_un *sa, int addrlen) { int ret; struct stat st; /* * Minor check for operating system where bind() will incorrectly follow symlink. * Note that there is a race condition between the stat() and the bind call. */ ret = stat(sa->sun_path, &st); if ( ret < 0 ) return 0; if ( ! S_ISSOCK(st.st_mode) ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "'%s' exist and is not an UNIX socket", sa->sun_path); ret = connect(sock, (struct sockaddr *) sa, addrlen); if ( ret == 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "UNIX socket '%s' already in use", sa->sun_path); /* * The unix socket exist on the file system, * but no one use it... Delete it. */ ret = unlink(sa->sun_path); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "could not delete unused UNIX socket: %s", strerror(errno)); return 0; }
static int inet_server_start(server_generic_t *server, struct sockaddr *addr, socklen_t addrlen) { int ret, on = 1; server->sock = socket(server->sa->sa_family, SOCK_STREAM, IPPROTO_TCP); if ( server->sock < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error creating socket: %s", strerror(errno)); #ifdef IPV6_V6ONLY /* * There is a problem on Linux system where getaddrinfo() return address in * the wrong sort order (IPv4 first, IPv6 next). * * As a result we first bind IPv4 addresses, but then we get an error for * dual-stacked addresses, when the IPv6 addresses come second. When an * address is dual-stacked, we thus end-up listening only to the IPv4 * instance. * * The error happen on dual-stack Linux system, because mapping the IPv6 * address will actually attempt to bind both the IPv4 and IPv6 address. * * In order to prevent this problem, we set the IPV6_V6ONLY option so that * only the IPv6 address is bound. */ if ( server->sa->sa_family == AF_INET6 ) { ret = setsockopt(server->sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &on, sizeof(int)); if ( ret < 0 ) { ret = prelude_error_verbose(PRELUDE_ERROR_GENERIC, "could not set IPV6_V6ONLY: %s.\n", strerror(errno)); goto err; } } #endif ret = setsockopt(server->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); if ( ret < 0 ) { ret = prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error setting SO_REUSEADDR: %s", strerror(errno)); goto err; } ret = generic_server(server->sock, addr, addrlen); if ( ret < 0 ) goto err; return 0; err: close(server->sock); return ret; }
static int generic_server(int sock, struct sockaddr *addr, size_t alen) { int ret; ret = bind(sock, addr, alen); if ( ret < 0 ) return prelude_error_verbose(prelude_error_code_from_errno(errno), "could not bind socket: %s", strerror(errno)); ret = listen(sock, 10); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "listen error: %s", strerror(errno)); return 0; }
/* * put client socket in non blocking mode and * create a prelude_io object for IO abstraction. * * Tell server-logic to handle event on the newly accepted client. */ static int setup_client_socket(server_generic_t *server, server_generic_client_t *cdata, int client) { int ret; #ifdef HAVE_TCP_WRAPPERS if ( server->sa->sa_family != AF_UNIX ) { ret = tcpd_auth(cdata, client); if ( ret < 0 ) return -1; } #endif /* * set client socket non blocking. */ #if ! ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) ret = fcntl(client, F_SETFL, O_NONBLOCK); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "could not set non blocking mode for client: %s", strerror(errno)); fcntl(client, F_SETFD, fcntl(client, F_GETFD) | FD_CLOEXEC); #endif ret = prelude_io_new(&cdata->fd); if ( ret < 0 ) return ret; prelude_io_set_sys_io(cdata->fd, client); cdata->msg = NULL; cdata->state = 0; return 0; }
static int parse_time_hmsu(struct tm *tm, uint32_t *usec, char **buf) { int fraction; char *eptr = NULL; *buf = strptime(*buf, "%H:%M:%S", tm); if ( ! *buf ) goto fmterror; if ( **buf == '.' || **buf == ',' ) { (*buf)++; fraction = strtoul(*buf, &eptr, 10); if ( eptr == *buf ) goto fmterror; fraction = digit2usec(fraction, eptr - *buf); if ( fraction < 0 ) return fraction; *buf = eptr; *usec = fraction; } return 0; fmterror: return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error parsing time field, format should be: HH:MM:SS(.fraction)"); }
/* * Only called if the memory dump of the file was modified, * will write the new content to filename 'filename'. */ static int sync_and_free_file_content(config_t *cfg) { FILE *fd; unsigned int i; size_t ret, len; fd = fopen(cfg->filename, "w"); if ( ! fd ) return prelude_error_verbose(prelude_error_code_from_errno(errno), "could not open '%s' for writing: %s", cfg->filename, strerror(errno)); for ( i = 0; i < cfg->elements; i++ ) { len = strlen(cfg->content[i]); ret = fwrite(cfg->content[i], 1, len, fd); if ( ret != len && ferror(fd) ) prelude_log(PRELUDE_LOG_ERR, "error writing content to '%s': %s", cfg->filename, strerror(errno)); if ( i + 1 != cfg->elements ) { ret = fwrite("\n", 1, 1, fd); if ( ret != 1 && ferror(fd) ) prelude_log(PRELUDE_LOG_ERR, "error writing content to '%s': %s", cfg->filename, strerror(errno)); } free(cfg->content[i]); } fclose(fd); free(cfg->content); return 0; }
/** * prelude_read_multiline: * @fd: File descriptor to read input from. * @line: Pointer to a line counter. * @buf: Pointer to a buffer where the line should be stored. * @size: Size of the @buf buffer. * * This function handles line reading separated by the '\' character. * * Returns: 0 on success, -1 if an error occured. */ int prelude_read_multiline(FILE *fd, unsigned int *line, char *buf, size_t size) { size_t i, j, len; prelude_bool_t eol, has_data = FALSE, miss_eol=FALSE; while ( size > 1 ) { if ( ! fgets(buf, size, fd) ) return (has_data) ? 0 : prelude_error(PRELUDE_ERROR_EOF); len = strlen(buf); if ( ! len ) continue; eol = FALSE; for ( i = len - 1; isspace((int) buf[i]); i-- ) { if ( buf[i] == '\n' || buf[i] == '\r' ) { buf[i] = 0; if ( ! eol ) { eol = TRUE; (*line)++; } } if ( i == 0 ) break; } if ( miss_eol && eol && i == 0 ) continue; /* * We don't want to handle multilines in case this is a comment. */ for ( j = 0; buf[j] != '\0' && isspace((int) buf[j]); j++ ); if ( buf[j] == '#' ) continue; /* * Multiline found, continue reading. */ if ( buf[i] != '\\' ) { if ( eol ) return 0; if ( len == size - 1 ) break; has_data = TRUE; } if ( ! eol ) miss_eol = TRUE; buf += i; size -= i; } return prelude_error_verbose(PRELUDE_ERROR_EINVAL, "buffer is too small to store input line"); }
/* * Loads filename into memory, we use append_line() for that. */ static int load_file_in_memory(config_t *cfg) { int ret; FILE *fd; size_t len; prelude_string_t *out; char line[1024], *ptr, *tmp; ret = prelude_string_new(&out); if ( ret < 0 ) return ret; fd = fopen(cfg->filename, "r"); if ( ! fd ) { prelude_string_destroy(out); return prelude_error_verbose(prelude_error_code_from_errno(errno), "could not open '%s' for reading: %s", cfg->filename, strerror(errno)); } do { len = 0; ptr = fgets(line, sizeof(line), fd); if ( ptr ) { len = strlen(line); if ( line[len - 1] == '\n' ) line[len - 1] = 0; ret = prelude_string_cat(out, line); if ( ret < 0 ) goto err; if ( line[len - 1] != 0 ) continue; } ret = prelude_string_get_string_released(out, &tmp); if ( ret < 0 ) goto err; if ( ! tmp ) tmp = strdup(""); ret = op_append_line(cfg, tmp); if ( ret < 0 ) { free(tmp); goto err; } prelude_string_clear(out); } while ( ptr ); err: prelude_string_destroy(out); fclose(fd); return 0; }
/** * idmef_time_set_from_string: * @time: Pointer to an #idmef_time_t object. * @buf: Pointer to a string describing a time in an IDMEF conforming format. * * Fills @time object with information retrieved from the user provided * @buf, containing a string describing a time in a format conforming * to the IDMEF definition (v. 0.10, section 3.2.6). * * Additionally, the provided time might be separated with white spaces, * instead of the IDMEF defined 'T' character. * * If there is no UTC offset specified, we assume that the provided * time is local, and compute the GMT offset by ourselve. * * Returns: 0 on success, a negative value if an error occured. */ int idmef_time_set_from_string(idmef_time_t *time, const char *buf) { int ret; char *ptr; struct tm tm; prelude_bool_t miss_gmt = FALSE; prelude_return_val_if_fail(time, prelude_error(PRELUDE_ERROR_ASSERTION)); prelude_return_val_if_fail(buf, prelude_error(PRELUDE_ERROR_ASSERTION)); memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; ptr = parse_time_ymd(&tm, buf); if ( ! ptr ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error parsing date field, format should be: YY-MM-DD"); time->usec = 0; time->gmt_offset = 0; if ( *ptr ) { ret = parse_time_hmsu(&tm, &time->usec, &ptr); if ( ret < 0 ) return ret; miss_gmt = TRUE; if ( *ptr ) { ret = parse_time_gmt(&tm, &time->gmt_offset, ptr); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error parsing GMT offset field (Z)?(+|-)?HH:MM"); miss_gmt = FALSE; } } if ( miss_gmt ) { long gmtoff; prelude_get_gmt_offset_from_tm(&tm, &gmtoff); time->gmt_offset = (int32_t) gmtoff; } time->sec = miss_gmt ? mktime(&tm) : prelude_timegm(&tm); return 0; }
static int digit2usec(uint32_t n, int digit_count) { int i; const size_t max_digit = 6; /* 999999 */ if ( digit_count > max_digit ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "Invalid number of digits for time fraction"); for ( i = 0; i < (max_digit - digit_count); i++ ) n *= 10; return n; }
static int inet_server_start(server_generic_t *server, struct sockaddr *addr, socklen_t addrlen) { int ret, on = 1; server->sock = socket(server->sa->sa_family, SOCK_STREAM, IPPROTO_TCP); if ( server->sock < 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error creating socket: %s", strerror(errno)); ret = setsockopt(server->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); if ( ret < 0 ) { ret = prelude_error_verbose(PRELUDE_ERROR_GENERIC, "error setting SO_REUSEADDR: %s", strerror(errno)); goto err; } ret = generic_server(server->sock, addr, addrlen); if ( ret < 0 ) goto err; return 0; err: close(server->sock); return ret; }
static int do_getaddrinfo(struct addrinfo **ai, const char *addr, unsigned int port) { int ret; struct addrinfo hints; char service[sizeof("00000")]; memset(&hints, 0, sizeof(hints)); snprintf(service, sizeof(service), "%u", port); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ret = getaddrinfo(addr, service, &hints, ai); if ( ret != 0 ) ret = prelude_error_verbose(PRELUDE_ERROR_GENERIC, "could not resolve '%s': %s", addr, (ret == EAI_SYSTEM) ? strerror(errno) : gai_strerror(ret)); return ret; }
/** * prelude_parse_version: * @version: A version string. * @out: Where to store the parsed version * * Parse version to an integer, and return it in @out. * Accepted format are: * * major.minor.micro.patchlevel * the following special level string are supported : alpha, beta, rc * * For example: 1.1.1rc1 * * Returns: The 0 on success, a negative value in case of error. */ int prelude_parse_version(const char *version, unsigned int *out) { int ret; char levels[6] = { 0 }; int major = 0, minor = 0, micro = 0, level = 0, patch = 0; ret = sscanf(version, "%d.%d.%d%5[^0-9]%d", &major, &minor, µ, levels, &patch); if ( ret <= 0 ) return prelude_error_verbose(PRELUDE_ERROR_GENERIC, "version formatting error with '%s'", version); if ( *levels == 0 || *levels == '.' ) level = LIBPRELUDE_RELEASE_LEVEL_FINAL; else { level = levelstr_to_int(levels); if ( level < 0 ) return level; } *out = (major << 24) | (minor << 16) | (micro << 8) | (level << 4) | (patch << 0); return 0; }
static int lockfile_get_exclusive(const char *lockfile) { int fd; #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) int ret; struct flock lock; #endif fd = open(lockfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if ( fd < 0 ) return prelude_error_from_errno(errno); #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); lock.l_type = F_WRLCK; /* write lock */ lock.l_start = 0; /* from offset 0 */ lock.l_whence = SEEK_SET; /* at the beginning of the file */ lock.l_len = 0; /* until EOF */ ret = fcntl(fd, F_SETLK, &lock); if ( ret < 0 ) { if ( errno == EACCES || errno == EAGAIN ) return prelude_error_verbose(PRELUDE_ERROR_DAEMONIZE_LOCK_HELD, "'%s' lock is held by another process", slockfile); close(fd); return prelude_error_from_errno(errno); } #endif /* * lock is now held until program exits. */ return fd; }
int _prelude_load_file(const char *filename, unsigned char **fdata, size_t *outsize) { int ret, fd; struct stat st; unsigned char *dataptr; fd = open(filename, O_RDONLY); if ( fd < 0 ) return prelude_error_from_errno(errno); ret = fstat(fd, &st); if ( ret < 0 ) { close(fd); return prelude_error_from_errno(errno); } if ( st.st_size == 0 ) { close(fd); return prelude_error_verbose(prelude_error_code_from_errno(EINVAL), "could not load '%s': empty file", filename); } *outsize = st.st_size; #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) dataptr = *fdata = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if ( dataptr == MAP_FAILED ) { close(fd); return prelude_error_from_errno(errno); } #else dataptr = *fdata = malloc(st.st_size); if ( ! dataptr ) { close(fd); return prelude_error_from_errno(errno); } _setmode(fd, O_BINARY); do { ssize_t len; len = read(fd, dataptr, st.st_size); if ( len < 0 ) { if ( errno == EINTR ) continue; close(fd); free(*fdata); return prelude_error_from_errno(errno); } dataptr += len; st.st_size -= len; } while ( st.st_size > 0 ); #endif close(fd); return 0; }
/** * prelude_init: * @argc: Address of the argc parameter of your main() function. * @argv: Address of the argv parameter of your main() function. * * Call this function before using any other Prelude functions in your applications. * It will initialize everything needed to operate the library and parses some standard * command line options. @argc and @argv are adjusted accordingly so your own code will * never see those standard arguments. * * Returns: 0 on success, a negative value if an error occured. */ int prelude_init(int *argc, char **argv) { int ret; const char *env; if ( libprelude_refcount++ > 0 ) return 0; env = getenv("LIBPRELUDE_DEBUG"); if ( env ) prelude_log_set_debug_level(atoi(env)); env = getenv("LIBPRELUDE_TLS_DEBUG"); if ( env ) { gnutls_global_set_log_level(atoi(env)); gnutls_global_set_log_function(tls_log_func); } env = getenv("LIBPRELUDE_LOGFILE"); if ( env ) prelude_log_set_logfile(env); env = getenv("LIBPRELUDE_PREFIX"); if ( env ) _prelude_prefix = strdup(env); env = getenv("LIBPRELUDE_ABORT"); if ( env ) { if ( *env ) _prelude_log_set_abort_level_from_string(env); else _prelude_log_set_abort_level(PRELUDE_LOG_CRIT); } prelude_thread_init(NULL); if ( ! getcwd(_prelude_init_cwd, sizeof(_prelude_init_cwd)) ) _prelude_init_cwd[0] = 0; ret = _prelude_timer_init(); if ( ret < 0 ) return ret; ret = glthread_atfork(prelude_fork_prepare, prelude_fork_parent, prelude_fork_child); if ( ret != 0 ) return prelude_error_from_errno(ret); slice_arguments(argc, argv); ret = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_prelude); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_TLS, "gcrypt initialization failed: %s", gcry_strerror(ret)); ret = gnutls_global_init(); if ( ret < 0 ) return prelude_error_verbose(PRELUDE_ERROR_TLS, "TLS initialization failed: %s", gnutls_strerror(ret)); return 0; }