Skip to content
Snippets Groups Projects
utils.c 73.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				/* Not enough room left for the escape sequence. */
    				break;
    			}
    
    
    			*p++ = '\\';
    			*p = escape_sequences_map[c - escape_sequences];
    		} else {
    			*p = *s;
    		}
    	}
    	*p = '\0';
    
    	return dest;
    }
    
    static char *escape_alloc(const char *s, size_t *size)
    {
    
    		return NULL;
    	}
    
    	/*
    	 * The result string needs to be twice the size of the given
    	 * string just in case every character in it needs to be escaped.
    	 */
    
    	*size = strlen(s) * 2 + 1;
    	return ast_malloc(*size);
    
    }
    
    char *ast_escape_alloc(const char *s, const char *to_escape)
    {
    	size_t size = 0;
    	char *dest = escape_alloc(s, &size);
    
    	return ast_escape(dest, s, size, to_escape);
    }
    
    char *ast_escape_c_alloc(const char *s)
    {
    	size_t size = 0;
    	char *dest = escape_alloc(s, &size);
    
    	return ast_escape_c(dest, s, size);
    }
    
    
    int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
    
    {
    	int result;
    
    	if (!buffer || !*buffer || !space || !*space)
    		return -1;
    
    	result = vsnprintf(*buffer, *space, fmt, ap);
    
    	if (result < 0)
    		return -1;
    	else if (result > *space)
    		result = *space;
    
    	*buffer += result;
    	*space -= result;
    	return 0;
    }
    
    
    int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
    {
    	va_list ap;
    	int result;
    
    	va_start(ap, fmt);
    	result = ast_build_string_va(buffer, space, fmt, ap);
    	va_end(ap);
    
    	return result;
    }
    
    
    int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str **regex_pattern)
    
    {
    	int regex_len = strlen(regex_string);
    	int ret = 3;
    
    	/* Chop off the leading / if there is one */
    	if ((regex_len >= 1) && (regex_string[0] == '/')) {
    
    		ast_str_set(regex_pattern, 0, "%s", regex_string + 1);
    
    		ret -= 2;
    	}
    
    	/* Chop off the ending / if there is one */
    	if ((regex_len > 1) && (regex_string[regex_len - 1] == '/')) {
    
    		ast_str_truncate(*regex_pattern, -1);
    
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    int ast_false(const char *s)
    {
    
    		return 0;
    
    	/* Determine if this is a false value */
    	if (!strcasecmp(s, "no") ||
    	    !strcasecmp(s, "false") ||
    	    !strcasecmp(s, "n") ||
    	    !strcasecmp(s, "f") ||
    	    !strcasecmp(s, "0") ||
    	    !strcasecmp(s, "off"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    
    		ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_sec += a.tv_usec / ONE_MILLION;
    
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    
    
    int ast_remaining_ms(struct timeval start, int max_ms)
    {
    	int ms;
    
    	if (max_ms < 0) {
    		ms = max_ms;
    	} else {
    		ms = max_ms - ast_tvdiff_ms(ast_tvnow(), start);
    		if (ms < 0) {
    			ms = 0;
    		}
    	}
    
    	return ms;
    }
    
    
    void ast_format_duration_hh_mm_ss(int duration, char *buf, size_t length)
    {
    	int durh, durm, durs;
    	durh = duration / 3600;
    	durm = (duration % 3600) / 60;
    	durs = duration % 60;
    	snprintf(buf, length, "%02d:%02d:%02d", durh, durm, durs);
    }
    
    
    AST_MUTEX_DEFINE_STATIC(randomlock);
    
    long int ast_random(void)
    {
    	long int res;
    
    	if (dev_urandom_fd >= 0) {
    		int read_res = read(dev_urandom_fd, &res, sizeof(res));
    
    	/* XXX - Thread safety really depends on the libc, not the OS.
    	 *
    	 * But... popular Linux libc's (uClibc, glibc, eglibc), all have a
    	 * somewhat thread safe random(3) (results are random, but not
    	 * reproducible). The libc's for other systems (BSD, et al.), not so
    	 * much.
    	 */
    
    	ast_mutex_lock(&randomlock);
    	res = random();
    	ast_mutex_unlock(&randomlock);
    
    void ast_replace_subargument_delimiter(char *s)
    {
    	for (; *s; s++) {
    		if (*s == '^') {
    			*s = ',';
    		}
    	}
    }
    
    
    char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
    {
    
    	char *dataPut = start;
    
    	int inEscape = 0;
    	int inQuotes = 0;
    
    	for (; *start; start++) {
    		if (inEscape) {
    			*dataPut++ = *start;       /* Always goes verbatim */
    			inEscape = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else {
    
    			if (*start == '\\') {
    				inEscape = 1;      /* Do not copy \ into the data */
    			} else if (*start == '\'') {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				inQuotes = 1 - inQuotes;   /* Do not copy ' into the data */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
    
    void ast_join_delim(char *s, size_t len, const char * const w[], unsigned int size, char delim)
    
    {
    	int x, ofs = 0;
    	const char *src;
    
    	/* Join words into a string */
    	if (!s)
    		return;
    
    	for (x = 0; ofs < len && x < size && w[x] ; x++) {
    
    			s[ofs++] = delim;
    
    		for (src = w[x]; *src && ofs < len; src++)
    			s[ofs++] = *src;
    	}
    	if (ofs == len)
    		ofs--;
    	s[ofs] = '\0';
    }
    
    char *ast_to_camel_case_delim(const char *s, const char *delim)
    {
    	char *res = ast_strdup(s);
    	char *front, *back, *buf = res;
    	int size;
    
    	front = strtok_r(buf, delim, &back);
    
    	while (front) {
    		size = strlen(front);
    		*front = toupper(*front);
    		ast_copy_string(buf, front, size + 1);
    		buf += size;
    		front = strtok_r(NULL, delim, &back);
    	}
    
    	return res;
    }
    
    
    /*! \brief
     * get values from config variables.
     */
    int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
    {
    	long double dtv = 0.0;
    	int scanned;
    
    	if (dst == NULL)
    		return -1;
    
    	*dst = _default;
    
    	if (ast_strlen_zero(src))
    		return -1;
    
    	/* only integer at the moment, but one day we could accept more formats */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(src, "%30Lf%n", &dtv, &scanned) > 0) {
    
    		dst->tv_sec = dtv;
    		dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
    		if (consumed)
    			*consumed = scanned;
    		return 0;
    	} else
    		return -1;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief
    
    int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
    
    
    	if (dst == NULL)
    		return -1;
    
    	*dst = _default;
    
    	if (ast_strlen_zero(src))
    		return -1;
    
    	/* only integer at the moment, but one day we could accept more formats */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(src, "%30ld%n", &t, &scanned) == 1) {
    
    void ast_enable_packet_fragmentation(int sock)
    {
    
    #if defined(HAVE_IP_MTU_DISCOVER)
    
    	int val = IP_PMTUDISC_DONT;
    
    	if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
    		ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
    
    #endif /* HAVE_IP_MTU_DISCOVER */
    
    
    int ast_mkdir(const char *path, int mode)
    {
    	char *ptr;
    	int len = strlen(path), count = 0, x, piececount = 0;
    	char *tmp = ast_strdupa(path);
    	char **pieces;
    
    	char *fullpath = ast_alloca(len + 1);
    
    	int res = 0;
    
    	for (ptr = tmp; *ptr; ptr++) {
    		if (*ptr == '/')
    			count++;
    	}
    
    	/* Count the components to the directory path */
    
    	pieces = ast_alloca(count * sizeof(*pieces));
    
    	for (ptr = tmp; *ptr; ptr++) {
    		if (*ptr == '/') {
    			*ptr = '\0';
    			pieces[piececount++] = ptr + 1;
    		}
    	}
    
    	*fullpath = '\0';
    	for (x = 0; x < piececount; x++) {
    		/* This looks funky, but the buffer is always ideally-sized, so it's fine. */
    		strcat(fullpath, "/");
    		strcat(fullpath, pieces[x]);
    		res = mkdir(fullpath, mode);
    		if (res && errno != EEXIST)
    			return errno;
    	}
    	return 0;
    }
    
    
    static int safe_mkdir(const char *base_path, char *path, int mode)
    {
    
    	RAII_VAR(char *, absolute_path, NULL, ast_std_free);
    
    
    	absolute_path = realpath(path, NULL);
    
    	if (absolute_path) {
    		/* Path exists, but is it in the right place? */
    		if (!ast_begins_with(absolute_path, base_path)) {
    			return EPERM;
    		}
    
    		/* It is in the right place! */
    		return 0;
    	} else {
    		/* Path doesn't exist. */
    
    		/* The slash terminating the subpath we're checking */
    		char *path_term = strchr(path, '/');
    		/* True indicates the parent path is within base_path */
    		int parent_is_safe = 0;
    		int res;
    
    		while (path_term) {
    
    			RAII_VAR(char *, absolute_subpath, NULL, ast_std_free);
    
    
    			/* Truncate the path one past the slash */
    			char c = *(path_term + 1);
    			*(path_term + 1) = '\0';
    			absolute_subpath = realpath(path, NULL);
    
    			if (absolute_subpath) {
    				/* Subpath exists, but is it safe? */
    				parent_is_safe = ast_begins_with(
    					absolute_subpath, base_path);
    			} else if (parent_is_safe) {
    				/* Subpath does not exist, but parent is safe
    				 * Create it */
    				res = mkdir(path, mode);
    				if (res != 0) {
    					ast_assert(errno != EEXIST);
    					return errno;
    				}
    			} else {
    				/* Subpath did not exist, parent was not safe
    				 * Fail! */
    				errno = EPERM;
    				return errno;
    			}
    			/* Restore the path */
    			*(path_term + 1) = c;
    			/* Move on to the next slash */
    			path_term = strchr(path_term + 1, '/');
    		}
    
    		/* Now to build the final path, but only if it's safe */
    		if (!parent_is_safe) {
    			errno = EPERM;
    			return errno;
    		}
    
    		res = mkdir(path, mode);
    		if (res != 0 && errno != EEXIST) {
    			return errno;
    		}
    
    		return 0;
    	}
    }
    
    int ast_safe_mkdir(const char *base_path, const char *path, int mode)
    {
    
    	RAII_VAR(char *, absolute_base_path, NULL, ast_std_free);
    
    	RAII_VAR(char *, p, NULL, ast_free);
    
    	if (base_path == NULL || path == NULL) {
    		errno = EFAULT;
    		return errno;
    	}
    
    	p = ast_strdup(path);
    	if (p == NULL) {
    		errno = ENOMEM;
    		return errno;
    	}
    
    	absolute_base_path = realpath(base_path, NULL);
    	if (absolute_base_path == NULL) {
    		return errno;
    	}
    
    	return safe_mkdir(absolute_base_path, p, mode);
    }
    
    
    static void utils_shutdown(void)
    {
    	close(dev_urandom_fd);
    	dev_urandom_fd = -1;
    #if defined(DEBUG_THREADS) && !defined(LOW_MEMORY)
    	ast_cli_unregister_multiple(utils_cli, ARRAY_LEN(utils_cli));
    #endif
    }
    
    
    int ast_utils_init(void)
    {
    	dev_urandom_fd = open("/dev/urandom", O_RDONLY);
    	base64_init();
    #ifdef DEBUG_THREADS
    
    #if !defined(LOW_MEMORY)
    
    	ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
    
    	ast_register_cleanup(utils_shutdown);
    
    
    /*!
     *\brief Parse digest authorization header.
     *\return Returns -1 if we have no auth or something wrong with digest.
     *\note	This function may be used for Digest request and responce header.
     * request arg is set to nonzero, if we parse Digest Request.
     * pedantic arg can be set to nonzero if we need to do addition Digest check.
     */
    int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic) {
    
    	struct ast_str *str = ast_str_create(16);
    
    
    	/* table of recognised keywords, and places where they should be copied */
    	const struct x {
    		const char *key;
    		const ast_string_field *field;
    	} *i, keys[] = {
    		{ "username=", &d->username },
    		{ "realm=", &d->realm },
    		{ "nonce=", &d->nonce },
    		{ "uri=", &d->uri },
    		{ "domain=", &d->domain },
    		{ "response=", &d->response },
    		{ "cnonce=", &d->cnonce },
    		{ "opaque=", &d->opaque },
    		/* Special cases that cannot be directly copied */
    		{ "algorithm=", NULL },
    		{ "qop=", NULL },
    		{ "nc=", NULL },
    		{ NULL, 0 },
    	};
    
    
    	if (ast_strlen_zero(digest) || !d || !str) {
    		ast_free(str);
    		return -1;
    	}
    
    	ast_str_set(&str, 0, "%s", digest);
    
    	c = ast_skip_blanks(ast_str_buffer(str));
    
    
    	if (strncasecmp(c, "Digest ", strlen("Digest "))) {
    
    		ast_log(LOG_WARNING, "Missing Digest.\n");
    		ast_free(str);
    		return -1;
    	}
    	c += strlen("Digest ");
    
    	/* lookup for keys/value pair */
    
    	while (c && *c && *(c = ast_skip_blanks(c))) {
    
    		for (i = keys; i->key != NULL; i++) {
    			char *src, *separator;
    			int unescape = 0;
    			if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
    				continue;
    
    			/* Found. Skip keyword, take text in quotes or up to the separator. */
    			c += strlen(i->key);
    			if (*c == '"') {
    				src = ++c;
    				separator = "\"";
    				unescape = 1;
    			} else {
    				src = c;
    				separator = ",";
    
    			strsep(&c, separator); /* clear separator and move ptr */
    			if (unescape) {
    				ast_unescape_c(src);
    			}
    			if (i->field) {
    				ast_string_field_ptr_set(d, i->field, src);
    			} else {
    
    Josh Soref's avatar
    Josh Soref committed
    				/* Special cases that require additional processing */
    
    				if (!strcasecmp(i->key, "algorithm=")) {
    					if (strcasecmp(src, "MD5")) {
    						ast_log(LOG_WARNING, "Digest algorithm: \"%s\" not supported.\n", src);
    						ast_free(str);
    						return -1;
    					}
    				} else if (!strcasecmp(i->key, "qop=") && !strcasecmp(src, "auth")) {
    					d->qop = 1;
    				} else if (!strcasecmp(i->key, "nc=")) {
    					unsigned long u;
    					if (sscanf(src, "%30lx", &u) != 1) {
    						ast_log(LOG_WARNING, "Incorrect Digest nc value: \"%s\".\n", src);
    						ast_free(str);
    						return -1;
    					}
    					ast_string_field_set(d, nc, src);
    				}
    
    			break;
    		}
    		if (i->key == NULL) { /* not found, try ',' */
    			strsep(&c, ",");
    
    		}
    	}
    	ast_free(str);
    
    	/* Digest checkout */
    	if (ast_strlen_zero(d->realm) || ast_strlen_zero(d->nonce)) {
    		/* "realm" and "nonce" MUST be always exist */
    		return -1;
    	}
    
    	if (!request) {
    		/* Additional check for Digest response */
    		if (ast_strlen_zero(d->username) || ast_strlen_zero(d->uri) || ast_strlen_zero(d->response)) {
    			return -1;
    		}
    
    		if (pedantic && d->qop && (ast_strlen_zero(d->cnonce) || ast_strlen_zero(d->nc))) {
    			return -1;
    		}
    	}
    
    	return 0;
    }
    
    
    int ast_get_tid(void)
    {
    	int ret = -1;
    #if defined (__linux) && defined(SYS_gettid)
    	ret = syscall(SYS_gettid); /* available since Linux 1.4.11 */
    #elif defined(__sun)
    	ret = pthread_self();
    #elif defined(__APPLE__)
    	ret = mach_thread_self();
    	mach_port_deallocate(mach_task_self(), ret);
    #elif defined(__FreeBSD__) && defined(HAVE_SYS_THR_H)
    	long lwpid;
    	thr_self(&lwpid); /* available since sys/thr.h creation 2003 */
    	ret = lwpid;
    
    #elif defined(__NetBSD__)
    	ret = _lwp_self();
    
    
    char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
    {
    	const char *envPATH = getenv("PATH");
    	char *tpath, *path;
    	struct stat unused;
    	if (!envPATH) {
    		return NULL;
    	}
    	tpath = ast_strdupa(envPATH);
    	while ((path = strsep(&tpath, ":"))) {
    		snprintf(fullpath, fullpath_size, "%s/%s", path, binary);
    		if (!stat(fullpath, &unused)) {
    			return fullpath;
    		}
    	}
    	return NULL;
    }
    
    
    int ast_check_ipv6(void)
    {
    	int udp6_socket = socket(AF_INET6, SOCK_DGRAM, 0);
    
    	if (udp6_socket < 0) {
    		return 0;
    	}
    
    	close(udp6_socket);
    	return 1;
    }
    
    
    void DO_CRASH_NORETURN ast_do_crash(void)
    
    {
    #if defined(DO_CRASH)
    	abort();
    	/*
    	 * Just in case abort() doesn't work or something else super
    	 * silly, and for Qwell's amusement.
    	 */
    	*((int *) 0) = 0;
    #endif	/* defined(DO_CRASH) */
    }
    
    
    void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
    
    {
    	/*
    	 * Attempt to put it into the logger, but hope that at least
    	 * someone saw the message on stderr ...
    	 */
    	fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
    		condition_str, condition, line, function, file);
    
    	ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
    		condition_str, condition);
    
    	/*
    	 * Give the logger a chance to get the message out, just in case
    	 * we abort(), or Asterisk crashes due to whatever problem just
    	 * happened after we exit ast_assert().
    	 */
    	usleep(1);
    	ast_do_crash();
    }
    
    
    char *ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
    {
    	int x;
    	char *os = s;
    	if (maxlen < 18) {
    		if (s && (maxlen > 0)) {
    			*s = '\0';
    		}
    	} else {
    		for (x = 0; x < 5; x++) {
    
    			sprintf(s, "%02hhx:", eid->eid[x]);
    
    		sprintf(s, "%02hhx", eid->eid[5]);
    
    #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__Darwin__)
    
    #include <ifaddrs.h>
    #include <net/if_dl.h>
    
    
    void ast_set_default_eid(struct ast_eid *eid)
    {
    
    	struct ifaddrs *ifap, *ifaphead;
    	int rtnerr;
    	const struct sockaddr_dl *sdl;
    	int alen;
    	caddr_t ap;
    
    	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
    	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    
    	rtnerr = getifaddrs(&ifaphead);
    	if (rtnerr) {
    		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    			"You will have to set it manually.\n");
    
    	if (!ifaphead) {
    		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    			"You will have to set it manually.\n");
    		return;
    	}
    
    	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
    		if (ifap->ifa_addr->sa_family != AF_LINK) {
    			continue;
    
    		sdl = (const struct sockaddr_dl *) ifap->ifa_addr;
    		ap = ((caddr_t) ((sdl)->sdl_data + (sdl)->sdl_nlen));
    		alen = sdl->sdl_alen;
    		if (alen != 6 || !(memcmp(ap, &empty_mac, 6) && memcmp(ap, &full_mac, 6))) {
    			continue;
    
    		memcpy(eid, ap, sizeof(*eid));
    		ast_debug(1, "Seeding global EID '%s'\n",
    				ast_eid_to_str(eid_str, sizeof(eid_str), eid));
    		freeifaddrs(ifaphead);
    		return;
    	}
    
    	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    		"You will have to set it manually.\n");
    	freeifaddrs(ifaphead);
    
    	return;
    }
    
    #elif defined(SOLARIS)
    #include <sys/sockio.h>
    #include <net/if_arp.h>
    
    void ast_set_default_eid(struct ast_eid *eid)
    {
    	int s;
    	int x;
    	struct lifreq *ifr = NULL;
    	struct lifnum ifn;
    	struct lifconf ifc;
    	struct arpreq ar;
    	struct sockaddr_in *sa, *sa2;
    	char *buf = NULL;
    	char eid_str[20];
    	int bufsz;
    	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
    	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    
    	s = socket(AF_INET, SOCK_STREAM, 0);
    	if (s <= 0) {
    		ast_log(LOG_WARNING, "Unable to open a socket for seeding global EID. "
    			" You will have to set it manually.\n");
    		return;
    	}
    
    	/* Get a count of interfaces on the machine */
    	ifn.lifn_family = AF_UNSPEC;
    	ifn.lifn_flags = 0;
    	ifn.lifn_count = 0;
    	if (ioctl(s, SIOCGLIFNUM, &ifn) < 0) {
    		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    			" You will have to set it manually.\n");
    
    
    	bufsz = ifn.lifn_count * sizeof(struct lifreq);
    	if (!(buf = ast_malloc(bufsz))) {
    		ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
    			"You will have to set it manually.\n");
    		close(s);
    		return;
    	}
    	memset(buf, 0, bufsz);
    
    	/* Get a list of interfaces on the machine */
    	ifc.lifc_len = bufsz;
    	ifc.lifc_buf = buf;
    	ifc.lifc_family = AF_UNSPEC;
    	ifc.lifc_flags = 0;
    	if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
    		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    			"You will have to set it manually.\n");
    		ast_free(buf);
    		close(s);
    		return;
    	}
    
    	for (ifr = (struct lifreq *)buf, x = 0; x < ifn.lifn_count; ifr++, x++) {
    		unsigned char *p;
    
    		sa = (struct sockaddr_in *)&(ifr->lifr_addr);
    		sa2 = (struct sockaddr_in *)&(ar.arp_pa);
    		*sa2 = *sa;
    
    		if(ioctl(s, SIOCGARP, &ar) >= 0) {
    			p = (unsigned char *)&(ar.arp_ha.sa_data);
    			if (!(memcmp(p, &empty_mac, 6) && memcmp(p, &full_mac, 6))) {
    				continue;
    			}
    
    			memcpy(eid, p, sizeof(*eid));
    			ast_debug(1, "Seeding global EID '%s'\n",
    				ast_eid_to_str(eid_str, sizeof(eid_str), eid));
    			ast_free(buf);
    			close(s);
    			return;
    		}
    	}
    
    	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    		"You will have to set it manually.\n");
    	ast_free(buf);
    
    void ast_set_default_eid(struct ast_eid *eid)
    {
    	int s;
    	int i;
    	struct ifreq *ifr;
    	struct ifreq *ifrp;
    	struct ifconf ifc;
    	char *buf = NULL;
    
    	int bufsz, num_interfaces;
    	unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
    	unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    
    	s = socket(AF_INET, SOCK_STREAM, 0);
    
    		ast_log(LOG_WARNING, "Unable to open socket for seeding global EID. "
    			"You will have to set it manually.\n");
    		return;
    	}
    
    	ifc.ifc_len = 0;
    	ifc.ifc_buf = NULL;
    	if (ioctl(s, SIOCGIFCONF, &ifc) || ifc.ifc_len <= 0) {
    		ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    			"You will have to set it manually.\n");
    		close(s);
    		return;
    	}
    	bufsz = ifc.ifc_len;
    
    	if (!(buf = ast_malloc(bufsz))) {
    		ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
    			"You will have to set it manually.\n");
    		close(s);
    		return;
    	}
    
    	ifc.ifc_buf = buf;
    	if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
    		ast_log(LOG_WARNING, "Unable to retrieve ethernet interfaces for seeding global EID. "
    			"You will have to set it manually.\n");
    		ast_free(buf);
    		close(s);
    		return;
    	}
    
    	ifrp = ifc.ifc_req;
    	num_interfaces = ifc.ifc_len / sizeof(*ifr);
    
    	for (i = 0; i < num_interfaces; i++) {
    		ifr = &ifrp[i];
    		if (!ioctl(s, SIOCGIFHWADDR, ifr)) {
    			unsigned char *hwaddr = (unsigned char *) ifr->ifr_hwaddr.sa_data;
    
    			if (!(memcmp(hwaddr, &empty_mac, 6) && memcmp(hwaddr, &full_mac, 6))) {
    				continue;
    
    
    			memcpy(eid, hwaddr, sizeof(*eid));
    			ast_debug(1, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n",
    				ast_eid_to_str(eid_str, sizeof(eid_str), eid), ifr->ifr_name);
    			ast_free(buf);
    			close(s);
    			return;
    
    
    	ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
    		"You will have to set it manually.\n");
    	ast_free(buf);
    	close(s);
    
    	return;
    
    
    int ast_str_to_eid(struct ast_eid *eid, const char *s)
    {
    	unsigned int eid_int[6];
    	int x;
    
    	if (sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", &eid_int[0], &eid_int[1], &eid_int[2],
    		 &eid_int[3], &eid_int[4], &eid_int[5]) != 6) {
    			return -1;
    	}
    
    	for (x = 0; x < 6; x++) {
    		eid->eid[x] = eid_int[x];
    	}
    
    	return 0;
    }
    
    int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
    {
    	return memcmp(eid1, eid2, sizeof(*eid1));
    }
    
    int ast_eid_is_empty(const struct ast_eid *eid)
    {
    	struct ast_eid empty_eid;
    
    	memset(&empty_eid, 0, sizeof(empty_eid));
    	return memcmp(eid, &empty_eid, sizeof(empty_eid)) ? 0 : 1;