[misc] Add utility network functions, incl configurable addr binding
Also make misc.c/h a bit less messy.
This commit is contained in:
parent
3673aa7215
commit
7871d71a9c
|
@ -43,6 +43,11 @@ general {
|
|||
# Enable/disable IPv6
|
||||
ipv6 = yes
|
||||
|
||||
# Set this if you want the server to bind to a specific IP address. Can
|
||||
# be ipv6 or ipv4. Default (commented out or "::") is to listen on all
|
||||
# IP addresses.
|
||||
# bind_address = "::"
|
||||
|
||||
# Location of cache database
|
||||
# cache_path = "@localstatedir@/cache/@PACKAGE@/cache.db"
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ static cfg_opt_t sec_general[] =
|
|||
CFG_INT("websocket_port", 3688, CFGF_NONE),
|
||||
CFG_STR_LIST("trusted_networks", "{localhost,192.168,fd}", CFGF_NONE),
|
||||
CFG_BOOL("ipv6", cfg_true, CFGF_NONE),
|
||||
CFG_STR("bind_address", NULL, CFGF_NONE),
|
||||
CFG_STR("cache_path", STATEDIR "/cache/" PACKAGE "/cache.db", CFGF_NONE),
|
||||
CFG_INT("cache_daap_threshold", 1000, CFGF_NONE),
|
||||
CFG_BOOL("speaker_autoselect", cfg_false, CFGF_NONE),
|
||||
|
|
|
@ -1499,7 +1499,7 @@ httpd_admin_check_auth(struct evhttp_request *req)
|
|||
|
||||
evhttp_connection_get_peer(evcon, &addr, &port);
|
||||
|
||||
if (peer_address_is_trusted(addr))
|
||||
if (net_peer_address_is_trusted(addr))
|
||||
return true;
|
||||
|
||||
passwd = cfg_getstr(cfg_getsec(cfg, "general"), "admin_password");
|
||||
|
|
|
@ -717,7 +717,7 @@ daap_request_authorize(struct httpd_request *hreq)
|
|||
char *passwd;
|
||||
int ret;
|
||||
|
||||
if (peer_address_is_trusted(hreq->peer_address))
|
||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
||||
return 0;
|
||||
|
||||
// Regular DAAP clients like iTunes will login with /login, and we will reply
|
||||
|
@ -924,7 +924,7 @@ daap_reply_login(struct httpd_request *hreq)
|
|||
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32));
|
||||
|
||||
param = evhttp_find_header(hreq->query, "pairing-guid");
|
||||
if (param && !peer_address_is_trusted(hreq->peer_address))
|
||||
if (param && !net_peer_address_is_trusted(hreq->peer_address))
|
||||
{
|
||||
if (strlen(param) < 3)
|
||||
{
|
||||
|
|
|
@ -609,7 +609,7 @@ dacp_request_authorize(struct httpd_request *hreq)
|
|||
int32_t id;
|
||||
int ret;
|
||||
|
||||
if (peer_address_is_trusted(hreq->peer_address))
|
||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
||||
return 0;
|
||||
|
||||
param = evhttp_find_header(hreq->query, "session-id");
|
||||
|
|
|
@ -295,7 +295,7 @@ rsp_request_authorize(struct httpd_request *hreq)
|
|||
char *passwd;
|
||||
int ret;
|
||||
|
||||
if (peer_address_is_trusted(hreq->peer_address))
|
||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
||||
return 0;
|
||||
|
||||
passwd = cfg_getstr(cfg_getsec(cfg, "library"), "password");
|
||||
|
|
894
src/misc.c
894
src/misc.c
|
@ -37,6 +37,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef CLOCK_REALTIME
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
@ -44,6 +45,9 @@
|
|||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <unistr.h>
|
||||
#include <uniconv.h>
|
||||
|
||||
|
@ -107,12 +111,223 @@ static char *buildopts[] =
|
|||
NULL
|
||||
};
|
||||
|
||||
char **
|
||||
buildopts_get()
|
||||
|
||||
/* ------------------------ Network utility functions ----------------------- */
|
||||
|
||||
bool
|
||||
net_peer_address_is_trusted(const char *addr)
|
||||
{
|
||||
return buildopts;
|
||||
cfg_t *section;
|
||||
const char *network;
|
||||
int i;
|
||||
int n;
|
||||
|
||||
if (!addr)
|
||||
return false;
|
||||
|
||||
if (strncmp(addr, "::ffff:", strlen("::ffff:")) == 0)
|
||||
addr += strlen("::ffff:");
|
||||
|
||||
section = cfg_getsec(cfg, "general");
|
||||
|
||||
n = cfg_size(section, "trusted_networks");
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
network = cfg_getnstr(section, "trusted_networks", i);
|
||||
|
||||
if (!network || network[0] == '\0')
|
||||
return false;
|
||||
|
||||
if (strncmp(network, addr, strlen(network)) == 0)
|
||||
return true;
|
||||
|
||||
if ((strcmp(network, "localhost") == 0) && (strcmp(addr, "127.0.0.1") == 0 || strcmp(addr, "::1") == 0))
|
||||
return true;
|
||||
|
||||
if (strcmp(network, "any") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
net_address_get(char *addr, size_t addr_len, union net_sockaddr *naddr)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
memset(addr, 0, addr_len); // Just in case caller doesn't check for errors
|
||||
|
||||
if (naddr->sa.sa_family == AF_INET6)
|
||||
s = inet_ntop(AF_INET6, &naddr->sin6.sin6_addr, addr, addr_len);
|
||||
else
|
||||
s = inet_ntop(AF_INET, &naddr->sin.sin_addr, addr, addr_len);
|
||||
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
net_port_get(short unsigned *port, union net_sockaddr *naddr)
|
||||
{
|
||||
if (naddr->sa.sa_family == AF_INET6)
|
||||
*port = ntohs(naddr->sin6.sin6_port);
|
||||
else
|
||||
*port = ntohs(naddr->sin.sin_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
net_connect(const char *addr, unsigned short port, int type)
|
||||
{
|
||||
struct addrinfo hints = { 0 };
|
||||
struct addrinfo *servinfo;
|
||||
struct addrinfo *ptr;
|
||||
char strport[8];
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_MISC, "Connecting to %s, port %u\n", addr, port);
|
||||
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_family = (cfg_getbool(cfg_getsec(cfg, "general"), "ipv6")) ? AF_UNSPEC : AF_INET;
|
||||
|
||||
snprintf(strport, sizeof(strport), "%hu", port);
|
||||
ret = getaddrinfo(addr, strport, &hints, &servinfo);
|
||||
if (ret < 0)
|
||||
{
|
||||
gai_strerror(ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = servinfo; ptr; ptr = ptr->ai_next)
|
||||
{
|
||||
fd = socket(ptr->ai_family, ptr->ai_socktype | SOCK_CLOEXEC, ptr->ai_protocol);
|
||||
if (fd < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = connect(fd, ptr->ai_addr, ptr->ai_addrlen);
|
||||
if (ret < 0)
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Failed to connect to %s, port %u: %s\n", addr, port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// net_address_get(ipaddr, sizeof(ipaddr), (union net_sockaddr *)ptr->ai-addr);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// If *port is 0 then a random port will be assigned, and *port will be updated
|
||||
// with the port number
|
||||
int
|
||||
net_bind(short unsigned *port, int type, const char *log_service_name)
|
||||
{
|
||||
struct addrinfo hints = { 0 };
|
||||
struct addrinfo *servinfo;
|
||||
struct addrinfo *ptr;
|
||||
const char *cfgaddr;
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
char strport[8];
|
||||
int yes = 1;
|
||||
int no = 0;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
cfgaddr = cfg_getstr(cfg_getsec(cfg, "general"), "bind_address");
|
||||
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_family = (cfg_getbool(cfg_getsec(cfg, "general"), "ipv6")) ? AF_INET6 : AF_INET;
|
||||
hints.ai_flags = cfgaddr ? 0 : AI_PASSIVE;
|
||||
|
||||
snprintf(strport, sizeof(strport), "%hu", *port);
|
||||
ret = getaddrinfo(cfgaddr, strport, &hints, &servinfo);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Failure creating '%s' service, could not resolve '%s' (port %s): %s\n", log_service_name, cfgaddr ? cfgaddr : "(ANY)", strport, gai_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = servinfo, fd = -1; ptr != NULL; ptr = ptr->ai_next)
|
||||
{
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
fd = socket(ptr->ai_family, ptr->ai_socktype | SOCK_CLOEXEC, ptr->ai_protocol);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
// TODO libevent sets this, we do the same?
|
||||
ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes));
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
if (ptr->ai_family == AF_INET6)
|
||||
{
|
||||
// We want to be sure the service is dual stack
|
||||
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
|
||||
if (ret < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = bind(fd, ptr->ai_addr, ptr->ai_addrlen);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Could not create service '%s' with address %s, port %hu: %s\n", log_service_name, cfgaddr ? cfgaddr : "(ANY)", *port, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Get the port that was assigned
|
||||
ret = getsockname(fd, ptr->ai_addr, &ptr->ai_addrlen);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Could not find address of service '%s': %s\n", log_service_name, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
net_port_get(port, (union net_sockaddr *)ptr->ai_addr);
|
||||
net_address_get(addr, sizeof(addr), (union net_sockaddr *)ptr->ai_addr);
|
||||
|
||||
DPRINTF(E_DBG, L_MISC, "Service '%s' bound to %s, port %hu, socket %d\n", log_service_name, addr, *port, fd);
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------- Conversion/hashing/sanitizers -------------------- */
|
||||
|
||||
int
|
||||
safe_atoi32(const char *str, int32_t *val)
|
||||
{
|
||||
|
@ -441,259 +656,6 @@ safe_snreplace(char *s, size_t sz, const char *pattern, const char *replacement)
|
|||
}
|
||||
|
||||
|
||||
/* Key/value functions */
|
||||
struct keyval *
|
||||
keyval_alloc(void)
|
||||
{
|
||||
struct keyval *kv;
|
||||
|
||||
kv = calloc(1, sizeof(struct keyval));
|
||||
if (!kv)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for keyval alloc\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
int
|
||||
keyval_add_size(struct keyval *kv, const char *name, const char *value, size_t size)
|
||||
{
|
||||
struct onekeyval *okv;
|
||||
const char *val;
|
||||
|
||||
if (!kv)
|
||||
return -1;
|
||||
|
||||
/* Check for duplicate key names */
|
||||
val = keyval_get(kv, name);
|
||||
if (val)
|
||||
{
|
||||
/* Same value, fine */
|
||||
if (strcmp(val, value) == 0)
|
||||
return 0;
|
||||
else /* Different value, bad */
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv = (struct onekeyval *)malloc(sizeof(struct onekeyval));
|
||||
if (!okv)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv->name = strdup(name);
|
||||
if (!okv->name)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval name\n");
|
||||
|
||||
free(okv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv->value = (char *)malloc(size + 1);
|
||||
if (!okv->value)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval value\n");
|
||||
|
||||
free(okv->name);
|
||||
free(okv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(okv->value, value, size);
|
||||
okv->value[size] = '\0';
|
||||
|
||||
okv->next = NULL;
|
||||
|
||||
if (!kv->head)
|
||||
kv->head = okv;
|
||||
|
||||
if (kv->tail)
|
||||
kv->tail->next = okv;
|
||||
|
||||
kv->tail = okv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
keyval_add(struct keyval *kv, const char *name, const char *value)
|
||||
{
|
||||
return keyval_add_size(kv, name, value, strlen(value));
|
||||
}
|
||||
|
||||
void
|
||||
keyval_remove(struct keyval *kv, const char *name)
|
||||
{
|
||||
struct onekeyval *okv;
|
||||
struct onekeyval *pokv;
|
||||
|
||||
if (!kv)
|
||||
return;
|
||||
|
||||
for (pokv = NULL, okv = kv->head; okv; pokv = okv, okv = okv->next)
|
||||
{
|
||||
if (strcasecmp(okv->name, name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!okv)
|
||||
return;
|
||||
|
||||
if (okv == kv->head)
|
||||
kv->head = okv->next;
|
||||
|
||||
if (okv == kv->tail)
|
||||
kv->tail = pokv;
|
||||
|
||||
if (pokv)
|
||||
pokv->next = okv->next;
|
||||
|
||||
free(okv->name);
|
||||
free(okv->value);
|
||||
free(okv);
|
||||
}
|
||||
|
||||
const char *
|
||||
keyval_get(struct keyval *kv, const char *name)
|
||||
{
|
||||
struct onekeyval *okv;
|
||||
|
||||
if (!kv)
|
||||
return NULL;
|
||||
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
{
|
||||
if (strcasecmp(okv->name, name) == 0)
|
||||
return okv->value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
keyval_clear(struct keyval *kv)
|
||||
{
|
||||
struct onekeyval *hokv;
|
||||
struct onekeyval *okv;
|
||||
|
||||
if (!kv)
|
||||
return;
|
||||
|
||||
hokv = kv->head;
|
||||
|
||||
for (okv = hokv; hokv; okv = hokv)
|
||||
{
|
||||
hokv = okv->next;
|
||||
|
||||
free(okv->name);
|
||||
free(okv->value);
|
||||
free(okv);
|
||||
}
|
||||
|
||||
kv->head = NULL;
|
||||
kv->tail = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
keyval_sort(struct keyval *kv)
|
||||
{
|
||||
struct onekeyval *head;
|
||||
struct onekeyval *okv;
|
||||
struct onekeyval *sokv;
|
||||
|
||||
if (!kv || !kv->head)
|
||||
return;
|
||||
|
||||
head = kv->head;
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
{
|
||||
okv->sort = NULL;
|
||||
for (sokv = kv->head; sokv; sokv = sokv->next)
|
||||
{
|
||||
// We try to find a name which is greater than okv->name
|
||||
// but less than our current candidate (okv->sort->name)
|
||||
if ( (strcmp(sokv->name, okv->name) > 0) &&
|
||||
((okv->sort == NULL) || (strcmp(sokv->name, okv->sort->name) < 0)) )
|
||||
okv->sort = sokv;
|
||||
}
|
||||
|
||||
// Find smallest name, which will be the new head
|
||||
if (strcmp(okv->name, head->name) < 0)
|
||||
head = okv;
|
||||
}
|
||||
|
||||
while ((okv = kv->head))
|
||||
{
|
||||
kv->head = okv->next;
|
||||
okv->next = okv->sort;
|
||||
}
|
||||
|
||||
kv->head = head;
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
kv->tail = okv;
|
||||
|
||||
DPRINTF(E_DBG, L_MISC, "Keyval sorted. New head: %s. New tail: %s.\n", kv->head->name, kv->tail->name);
|
||||
}
|
||||
|
||||
|
||||
char **
|
||||
m_readfile(const char *path, int num_lines)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
char **lines;
|
||||
char *line;
|
||||
int i;
|
||||
|
||||
// Alloc array of char pointers
|
||||
lines = calloc(num_lines, sizeof(char *));
|
||||
if (!lines)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Could not open file '%s' for reading: %s\n", path, strerror(errno));
|
||||
free(lines);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_lines; i++)
|
||||
{
|
||||
line = fgets(buf, sizeof(buf), fp);
|
||||
if (!line)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "File '%s' has fewer lines than expected (found %d, expected %d)\n", path, i, num_lines);
|
||||
goto error;
|
||||
}
|
||||
|
||||
lines[i] = atrim(line);
|
||||
if (!lines[i] || (strlen(lines[i]) == 0))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Line %d in '%s' is invalid\n", i+1, path);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return lines;
|
||||
|
||||
error:
|
||||
for (i = 0; i < num_lines; i++)
|
||||
free(lines[i]);
|
||||
|
||||
free(lines);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
unicode_fixup_string(char *str, const char *fromcode)
|
||||
{
|
||||
|
@ -1015,124 +977,211 @@ murmur_hash64(const void *key, int len, uint32_t seed)
|
|||
# error Platform not supported
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UUID
|
||||
void
|
||||
uuid_make(char *str)
|
||||
|
||||
/* --------------------------- Key/value functions -------------------------- */
|
||||
|
||||
struct keyval *
|
||||
keyval_alloc(void)
|
||||
{
|
||||
uuid_t uu;
|
||||
struct keyval *kv;
|
||||
|
||||
uuid_generate_random(uu);
|
||||
uuid_unparse_upper(uu, str);
|
||||
}
|
||||
#else
|
||||
void
|
||||
uuid_make(char *str)
|
||||
{
|
||||
uint16_t uuid[8];
|
||||
time_t now;
|
||||
int i;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
srand((unsigned int)now);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(uuid); i++)
|
||||
kv = calloc(1, sizeof(struct keyval));
|
||||
if (!kv)
|
||||
{
|
||||
uuid[i] = (uint16_t)rand();
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for keyval alloc\n");
|
||||
|
||||
// time_hi_and_version, set version to 4 (=random)
|
||||
if (i == 3)
|
||||
uuid[i] = (uuid[i] & 0x0FFF) | 0x4000;
|
||||
// clock_seq, variant 1
|
||||
if (i == 4)
|
||||
uuid[i] = (uuid[i] & 0x3FFF) | 0x8000;
|
||||
|
||||
|
||||
if (i == 2 || i == 3 || i == 4 || i == 5)
|
||||
str += sprintf(str, "-");
|
||||
|
||||
str += sprintf(str, "%04" PRIX16, uuid[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
linear_regression(double *m, double *b, double *r2, const double *x, const double *y, int n)
|
||||
keyval_add_size(struct keyval *kv, const char *name, const char *value, size_t size)
|
||||
{
|
||||
double x_val;
|
||||
double sum_x = 0;
|
||||
double sum_x2 = 0;
|
||||
double sum_y = 0;
|
||||
double sum_y2 = 0;
|
||||
double sum_xy = 0;
|
||||
double denom;
|
||||
int i;
|
||||
struct onekeyval *okv;
|
||||
const char *val;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
x_val = x ? x[i] : (double)i;
|
||||
sum_x += x_val;
|
||||
sum_x2 += x_val * x_val;
|
||||
sum_y += y[i];
|
||||
sum_y2 += y[i] * y[i];
|
||||
sum_xy += x_val * y[i];
|
||||
}
|
||||
|
||||
denom = (n * sum_x2 - sum_x * sum_x);
|
||||
if (denom == 0)
|
||||
if (!kv)
|
||||
return -1;
|
||||
|
||||
*m = (n * sum_xy - sum_x * sum_y) / denom;
|
||||
*b = (sum_y * sum_x2 - sum_x * sum_xy) / denom;
|
||||
if (r2)
|
||||
*r2 = (sum_xy - (sum_x * sum_y)/n) * (sum_xy - (sum_x * sum_y)/n) / ((sum_x2 - (sum_x * sum_x)/n) * (sum_y2 - (sum_y * sum_y)/n));
|
||||
/* Check for duplicate key names */
|
||||
val = keyval_get(kv, name);
|
||||
if (val)
|
||||
{
|
||||
/* Same value, fine */
|
||||
if (strcmp(val, value) == 0)
|
||||
return 0;
|
||||
else /* Different value, bad */
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv = (struct onekeyval *)malloc(sizeof(struct onekeyval));
|
||||
if (!okv)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv->name = strdup(name);
|
||||
if (!okv->name)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval name\n");
|
||||
|
||||
free(okv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
okv->value = (char *)malloc(size + 1);
|
||||
if (!okv->value)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval value\n");
|
||||
|
||||
free(okv->name);
|
||||
free(okv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(okv->value, value, size);
|
||||
okv->value[size] = '\0';
|
||||
|
||||
okv->next = NULL;
|
||||
|
||||
if (!kv->head)
|
||||
kv->head = okv;
|
||||
|
||||
if (kv->tail)
|
||||
kv->tail->next = okv;
|
||||
|
||||
kv->tail = okv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
quality_is_equal(struct media_quality *a, struct media_quality *b)
|
||||
int
|
||||
keyval_add(struct keyval *kv, const char *name, const char *value)
|
||||
{
|
||||
return (a->sample_rate == b->sample_rate && a->bits_per_sample == b->bits_per_sample && a->channels == b->channels && a->bit_rate == b->bit_rate);
|
||||
return keyval_add_size(kv, name, value, strlen(value));
|
||||
}
|
||||
|
||||
bool
|
||||
peer_address_is_trusted(const char *addr)
|
||||
void
|
||||
keyval_remove(struct keyval *kv, const char *name)
|
||||
{
|
||||
cfg_t *section;
|
||||
const char *network;
|
||||
int i;
|
||||
int n;
|
||||
struct onekeyval *okv;
|
||||
struct onekeyval *pokv;
|
||||
|
||||
if (!addr)
|
||||
return false;
|
||||
if (!kv)
|
||||
return;
|
||||
|
||||
if (strncmp(addr, "::ffff:", strlen("::ffff:")) == 0)
|
||||
addr += strlen("::ffff:");
|
||||
|
||||
section = cfg_getsec(cfg, "general");
|
||||
|
||||
n = cfg_size(section, "trusted_networks");
|
||||
for (i = 0; i < n; i++)
|
||||
for (pokv = NULL, okv = kv->head; okv; pokv = okv, okv = okv->next)
|
||||
{
|
||||
network = cfg_getnstr(section, "trusted_networks", i);
|
||||
|
||||
if (!network || network[0] == '\0')
|
||||
return false;
|
||||
|
||||
if (strncmp(network, addr, strlen(network)) == 0)
|
||||
return true;
|
||||
|
||||
if ((strcmp(network, "localhost") == 0) && (strcmp(addr, "127.0.0.1") == 0 || strcmp(addr, "::1") == 0))
|
||||
return true;
|
||||
|
||||
if (strcmp(network, "any") == 0)
|
||||
return true;
|
||||
if (strcasecmp(okv->name, name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!okv)
|
||||
return;
|
||||
|
||||
if (okv == kv->head)
|
||||
kv->head = okv->next;
|
||||
|
||||
if (okv == kv->tail)
|
||||
kv->tail = pokv;
|
||||
|
||||
if (pokv)
|
||||
pokv->next = okv->next;
|
||||
|
||||
free(okv->name);
|
||||
free(okv->value);
|
||||
free(okv);
|
||||
}
|
||||
|
||||
const char *
|
||||
keyval_get(struct keyval *kv, const char *name)
|
||||
{
|
||||
struct onekeyval *okv;
|
||||
|
||||
if (!kv)
|
||||
return NULL;
|
||||
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
{
|
||||
if (strcasecmp(okv->name, name) == 0)
|
||||
return okv->value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
keyval_clear(struct keyval *kv)
|
||||
{
|
||||
struct onekeyval *hokv;
|
||||
struct onekeyval *okv;
|
||||
|
||||
if (!kv)
|
||||
return;
|
||||
|
||||
hokv = kv->head;
|
||||
|
||||
for (okv = hokv; hokv; okv = hokv)
|
||||
{
|
||||
hokv = okv->next;
|
||||
|
||||
free(okv->name);
|
||||
free(okv->value);
|
||||
free(okv);
|
||||
}
|
||||
|
||||
kv->head = NULL;
|
||||
kv->tail = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
keyval_sort(struct keyval *kv)
|
||||
{
|
||||
struct onekeyval *head;
|
||||
struct onekeyval *okv;
|
||||
struct onekeyval *sokv;
|
||||
|
||||
if (!kv || !kv->head)
|
||||
return;
|
||||
|
||||
head = kv->head;
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
{
|
||||
okv->sort = NULL;
|
||||
for (sokv = kv->head; sokv; sokv = sokv->next)
|
||||
{
|
||||
// We try to find a name which is greater than okv->name
|
||||
// but less than our current candidate (okv->sort->name)
|
||||
if ( (strcmp(sokv->name, okv->name) > 0) &&
|
||||
((okv->sort == NULL) || (strcmp(sokv->name, okv->sort->name) < 0)) )
|
||||
okv->sort = sokv;
|
||||
}
|
||||
|
||||
// Find smallest name, which will be the new head
|
||||
if (strcmp(okv->name, head->name) < 0)
|
||||
head = okv;
|
||||
}
|
||||
|
||||
while ((okv = kv->head))
|
||||
{
|
||||
kv->head = okv->next;
|
||||
okv->next = okv->sort;
|
||||
}
|
||||
|
||||
kv->head = head;
|
||||
for (okv = kv->head; okv; okv = okv->next)
|
||||
kv->tail = okv;
|
||||
|
||||
DPRINTF(E_DBG, L_MISC, "Keyval sorted. New head: %s. New tail: %s.\n", kv->head->name, kv->tail->name);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------- Ringbuffer ------------------------------- */
|
||||
|
||||
int
|
||||
ringbuffer_init(struct ringbuffer *buf, size_t size)
|
||||
{
|
||||
|
@ -1214,6 +1263,9 @@ ringbuffer_read(uint8_t **dst, size_t dstlen, struct ringbuffer *buf)
|
|||
return dstlen;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------- Clock utility functions ------------------------ */
|
||||
|
||||
int
|
||||
clock_gettime_with_res(clockid_t clock_id, struct timespec *tp, struct timespec *res)
|
||||
{
|
||||
|
@ -1411,6 +1463,24 @@ timer_getoverrun(timer_t timer_id)
|
|||
|
||||
#endif /* HAVE_MACH_CLOCK */
|
||||
|
||||
|
||||
/* ------------------------------- Media quality ---------------------------- */
|
||||
|
||||
bool
|
||||
quality_is_equal(struct media_quality *a, struct media_quality *b)
|
||||
{
|
||||
return (a->sample_rate == b->sample_rate && a->bits_per_sample == b->bits_per_sample && a->channels == b->channels && a->bit_rate == b->bit_rate);
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------- Misc utility functions ------------------------ */
|
||||
|
||||
char **
|
||||
buildopts_get()
|
||||
{
|
||||
return buildopts;
|
||||
}
|
||||
|
||||
int
|
||||
mutex_init(pthread_mutex_t *mutex)
|
||||
{
|
||||
|
@ -1425,6 +1495,136 @@ mutex_init(pthread_mutex_t *mutex)
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_UUID
|
||||
void
|
||||
uuid_make(char *str)
|
||||
{
|
||||
uuid_t uu;
|
||||
|
||||
uuid_generate_random(uu);
|
||||
uuid_unparse_upper(uu, str);
|
||||
}
|
||||
#else
|
||||
void
|
||||
uuid_make(char *str)
|
||||
{
|
||||
uint16_t uuid[8];
|
||||
time_t now;
|
||||
int i;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
srand((unsigned int)now);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(uuid); i++)
|
||||
{
|
||||
uuid[i] = (uint16_t)rand();
|
||||
|
||||
// time_hi_and_version, set version to 4 (=random)
|
||||
if (i == 3)
|
||||
uuid[i] = (uuid[i] & 0x0FFF) | 0x4000;
|
||||
// clock_seq, variant 1
|
||||
if (i == 4)
|
||||
uuid[i] = (uuid[i] & 0x3FFF) | 0x8000;
|
||||
|
||||
|
||||
if (i == 2 || i == 3 || i == 4 || i == 5)
|
||||
str += sprintf(str, "-");
|
||||
|
||||
str += sprintf(str, "%04" PRIX16, uuid[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
linear_regression(double *m, double *b, double *r2, const double *x, const double *y, int n)
|
||||
{
|
||||
double x_val;
|
||||
double sum_x = 0;
|
||||
double sum_x2 = 0;
|
||||
double sum_y = 0;
|
||||
double sum_y2 = 0;
|
||||
double sum_xy = 0;
|
||||
double denom;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
x_val = x ? x[i] : (double)i;
|
||||
sum_x += x_val;
|
||||
sum_x2 += x_val * x_val;
|
||||
sum_y += y[i];
|
||||
sum_y2 += y[i] * y[i];
|
||||
sum_xy += x_val * y[i];
|
||||
}
|
||||
|
||||
denom = (n * sum_x2 - sum_x * sum_x);
|
||||
if (denom == 0)
|
||||
return -1;
|
||||
|
||||
*m = (n * sum_xy - sum_x * sum_y) / denom;
|
||||
*b = (sum_y * sum_x2 - sum_x * sum_xy) / denom;
|
||||
if (r2)
|
||||
*r2 = (sum_xy - (sum_x * sum_y)/n) * (sum_xy - (sum_x * sum_y)/n) / ((sum_x2 - (sum_x * sum_x)/n) * (sum_y2 - (sum_y * sum_y)/n));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **
|
||||
m_readfile(const char *path, int num_lines)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *fp;
|
||||
char **lines;
|
||||
char *line;
|
||||
int i;
|
||||
|
||||
// Alloc array of char pointers
|
||||
lines = calloc(num_lines, sizeof(char *));
|
||||
if (!lines)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Could not open file '%s' for reading: %s\n", path, strerror(errno));
|
||||
free(lines);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_lines; i++)
|
||||
{
|
||||
line = fgets(buf, sizeof(buf), fp);
|
||||
if (!line)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "File '%s' has fewer lines than expected (found %d, expected %d)\n", path, i, num_lines);
|
||||
goto error;
|
||||
}
|
||||
|
||||
lines[i] = atrim(line);
|
||||
if (!lines[i] || (strlen(lines[i]) == 0))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Line %d in '%s' is invalid\n", i+1, path);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return lines;
|
||||
|
||||
error:
|
||||
for (i = 0; i < num_lines; i++)
|
||||
free(lines[i]);
|
||||
|
||||
free(lines);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------- Assertion ------------------------------- */
|
||||
|
||||
void
|
||||
log_fatal_err(int domain, const char *func, int line, int err)
|
||||
{
|
||||
|
|
194
src/misc.h
194
src/misc.h
|
@ -1,4 +1,3 @@
|
|||
|
||||
#ifndef __MISC_H__
|
||||
#define __MISC_H__
|
||||
|
||||
|
@ -7,11 +6,43 @@
|
|||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* Samples to bytes, bytes to samples */
|
||||
|
||||
/* ------------------------ Network utility functions ----------------------- */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
union net_sockaddr
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_storage ss;
|
||||
};
|
||||
|
||||
// Checks if the address is in a network that is configured as trusted
|
||||
bool
|
||||
net_peer_address_is_trusted(const char *addr);
|
||||
|
||||
int
|
||||
net_address_get(char *addr, size_t addr_len, union net_sockaddr *naddr);
|
||||
|
||||
int
|
||||
net_port_get(short unsigned *port, union net_sockaddr *naddr);
|
||||
|
||||
int
|
||||
net_connect(const char *addr, unsigned short port, int type);
|
||||
|
||||
int
|
||||
net_bind(short unsigned *port, int type, const char *log_service_name);
|
||||
|
||||
|
||||
/* ----------------------- Conversion/hashing/sanitizers -------------------- */
|
||||
|
||||
// Samples to bytes, bytes to samples
|
||||
#define STOB(s, bits, c) ((s) * (c) * (bits) / 8)
|
||||
#define BTOS(b, bits, c) ((b) / ((c) * (bits) / 8))
|
||||
|
||||
|
@ -30,41 +61,6 @@
|
|||
#define NTOSTR_HELPER(x) #x
|
||||
#define NTOSTR(x) NTOSTR_HELPER(x)
|
||||
|
||||
|
||||
// Remember to adjust quality_is_equal() if adding elements
|
||||
struct media_quality {
|
||||
int sample_rate;
|
||||
int bits_per_sample;
|
||||
int channels;
|
||||
int bit_rate;
|
||||
};
|
||||
|
||||
struct onekeyval {
|
||||
char *name;
|
||||
char *value;
|
||||
|
||||
struct onekeyval *next;
|
||||
struct onekeyval *sort;
|
||||
};
|
||||
|
||||
struct keyval {
|
||||
struct onekeyval *head;
|
||||
struct onekeyval *tail;
|
||||
};
|
||||
|
||||
struct ringbuffer {
|
||||
uint8_t *buffer;
|
||||
size_t size;
|
||||
size_t write_avail;
|
||||
size_t read_avail;
|
||||
size_t write_pos;
|
||||
size_t read_pos;
|
||||
};
|
||||
|
||||
|
||||
char **
|
||||
buildopts_get(void);
|
||||
|
||||
int
|
||||
safe_atoi32(const char *str, int32_t *val);
|
||||
|
||||
|
@ -95,32 +91,6 @@ safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...) __attribute__ ((for
|
|||
int
|
||||
safe_snreplace(char *s, size_t sz, const char *pattern, const char *replacement);
|
||||
|
||||
/* Key/value functions */
|
||||
struct keyval *
|
||||
keyval_alloc(void);
|
||||
|
||||
int
|
||||
keyval_add(struct keyval *kv, const char *name, const char *value);
|
||||
|
||||
int
|
||||
keyval_add_size(struct keyval *kv, const char *name, const char *value, size_t size);
|
||||
|
||||
void
|
||||
keyval_remove(struct keyval *kv, const char *name);
|
||||
|
||||
const char *
|
||||
keyval_get(struct keyval *kv, const char *name);
|
||||
|
||||
void
|
||||
keyval_clear(struct keyval *kv);
|
||||
|
||||
void
|
||||
keyval_sort(struct keyval *kv);
|
||||
|
||||
|
||||
char **
|
||||
m_readfile(const char *path, int num_lines);
|
||||
|
||||
char *
|
||||
unicode_fixup_string(char *str, const char *fromcode);
|
||||
|
||||
|
@ -150,18 +120,54 @@ b64_encode(const uint8_t *src, int srclen);
|
|||
uint64_t
|
||||
murmur_hash64(const void *key, int len, uint32_t seed);
|
||||
|
||||
void
|
||||
uuid_make(char *str);
|
||||
|
||||
/* --------------------------- Key/value functions -------------------------- */
|
||||
|
||||
struct onekeyval {
|
||||
char *name;
|
||||
char *value;
|
||||
|
||||
struct onekeyval *next;
|
||||
struct onekeyval *sort;
|
||||
};
|
||||
|
||||
struct keyval {
|
||||
struct onekeyval *head;
|
||||
struct onekeyval *tail;
|
||||
};
|
||||
|
||||
struct keyval *
|
||||
keyval_alloc(void);
|
||||
|
||||
int
|
||||
linear_regression(double *m, double *b, double *r, const double *x, const double *y, int n);
|
||||
keyval_add(struct keyval *kv, const char *name, const char *value);
|
||||
|
||||
bool
|
||||
quality_is_equal(struct media_quality *a, struct media_quality *b);
|
||||
int
|
||||
keyval_add_size(struct keyval *kv, const char *name, const char *value, size_t size);
|
||||
|
||||
// Checks if the address is in a network that is configured as trusted
|
||||
bool
|
||||
peer_address_is_trusted(const char *addr);
|
||||
void
|
||||
keyval_remove(struct keyval *kv, const char *name);
|
||||
|
||||
const char *
|
||||
keyval_get(struct keyval *kv, const char *name);
|
||||
|
||||
void
|
||||
keyval_clear(struct keyval *kv);
|
||||
|
||||
void
|
||||
keyval_sort(struct keyval *kv);
|
||||
|
||||
|
||||
/* ------------------------------- Ringbuffer ------------------------------- */
|
||||
|
||||
struct ringbuffer {
|
||||
uint8_t *buffer;
|
||||
size_t size;
|
||||
size_t write_avail;
|
||||
size_t read_avail;
|
||||
size_t write_pos;
|
||||
size_t read_pos;
|
||||
};
|
||||
|
||||
int
|
||||
ringbuffer_init(struct ringbuffer *buf, size_t size);
|
||||
|
@ -176,6 +182,10 @@ size_t
|
|||
ringbuffer_read(uint8_t **dst, size_t dstlen, struct ringbuffer *buf);
|
||||
|
||||
|
||||
/* ------------------------- Clock utility functions ------------------------ */
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifndef HAVE_CLOCK_GETTIME
|
||||
|
||||
#ifndef CLOCK_REALTIME
|
||||
|
@ -217,7 +227,7 @@ timer_getoverrun(timer_t timer_id);
|
|||
|
||||
#endif
|
||||
|
||||
/* Timer function for platforms without hi-res timers */
|
||||
// Timer function for platforms without hi-res timers
|
||||
int
|
||||
clock_gettime_with_res(clockid_t clock_id, struct timespec *tp, struct timespec *res);
|
||||
|
||||
|
@ -230,10 +240,44 @@ timespec_cmp(struct timespec time1, struct timespec time2);
|
|||
struct timespec
|
||||
timespec_reltoabs(struct timespec relative);
|
||||
|
||||
/* initialize mutex with error checking (not default on all platforms) */
|
||||
|
||||
/* ------------------------------- Media quality ---------------------------- */
|
||||
|
||||
// Remember to adjust quality_is_equal() if adding elements
|
||||
struct media_quality {
|
||||
int sample_rate;
|
||||
int bits_per_sample;
|
||||
int channels;
|
||||
int bit_rate;
|
||||
};
|
||||
|
||||
bool
|
||||
quality_is_equal(struct media_quality *a, struct media_quality *b);
|
||||
|
||||
|
||||
/* -------------------------- Misc utility functions ------------------------ */
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
char **
|
||||
buildopts_get(void);
|
||||
|
||||
// initialize mutex with error checking (not default on all platforms)
|
||||
int
|
||||
mutex_init(pthread_mutex_t *mutex);
|
||||
|
||||
void
|
||||
uuid_make(char *str);
|
||||
|
||||
int
|
||||
linear_regression(double *m, double *b, double *r, const double *x, const double *y, int n);
|
||||
|
||||
char **
|
||||
m_readfile(const char *path, int num_lines);
|
||||
|
||||
|
||||
/* -------------------------------- Assertion ------------------------------- */
|
||||
|
||||
/* Check that the function returns 0, logging a fatal error referencing
|
||||
returned error (type errno) if it fails, and aborts the process.
|
||||
Example: CHECK_ERR(L_MAIN, my_function()); */
|
||||
|
|
|
@ -4511,7 +4511,7 @@ mpd_accept_conn_cb(struct evconnlistener *listener,
|
|||
if (!client_ctx->authenticated)
|
||||
{
|
||||
sockaddr_to_string(address, addr_str, sizeof(addr_str));
|
||||
client_ctx->authenticated = peer_address_is_trusted(addr_str);
|
||||
client_ctx->authenticated = net_peer_address_is_trusted(addr_str);
|
||||
}
|
||||
|
||||
client_ctx->next = mpd_clients;
|
||||
|
|
Loading…
Reference in New Issue