IPv6 support

This commit is contained in:
nickkelsey 2022-10-10 20:31:24 -07:00
parent 7d4e2d34ed
commit 0ea5574fa5
19 changed files with 3527 additions and 1098 deletions

View file

@ -1,4 +1,31 @@
ifneq ($(OS),Windows_NT)
OS := $(shell uname -s)
endif
CC := $(CROSS_COMPILE)gcc
STRIP := $(CROSS_COMPILE)strip
CFLAGS += -O2 -Wall -Wextra -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith -Wno-unused-parameter
LDFLAGS += -lpthread
SHARED = -shared -Wl,-soname,libhdhomerun$(LIBEXT)
IF_DETECT := getifaddrs
BINEXT :=
LIBEXT := .so
ifeq ($(OS),Windows_NT)
IF_DETECT := netdevice
BINEXT := .exe
LIBEXT := .dll
LDFLAGS += -liphlpapi
endif
ifeq ($(OS),Linux)
IF_DETECT := netlink
LDFLAGS += -lrt
endif
LIBSRCS += hdhomerun_channels.c
LIBSRCS += hdhomerun_channelscan.c
LIBSRCS += hdhomerun_control.c
@ -8,35 +35,39 @@ LIBSRCS += hdhomerun_device_selector.c
LIBSRCS += hdhomerun_discover.c
LIBSRCS += hdhomerun_os_posix.c
LIBSRCS += hdhomerun_pkt.c
LIBSRCS += hdhomerun_sock.c
LIBSRCS += hdhomerun_sock_posix.c
LIBSRCS += hdhomerun_sock_$(IF_DETECT).c
LIBSRCS += hdhomerun_video.c
CC := $(CROSS_COMPILE)gcc
STRIP := $(CROSS_COMPILE)strip
CFLAGS += -O2 -Wall -Wextra -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith -Wno-unused-parameter
LDFLAGS += -lpthread
SHARED = -shared -Wl,-soname,libhdhomerun$(LIBEXT)
ifeq ($(OS),Windows_NT)
BINEXT := .exe
LIBEXT := .dll
LDFLAGS += -liphlpapi
else
OS := $(shell uname -s)
LIBEXT := .so
ifeq ($(OS),Linux)
LDFLAGS += -lrt
endif
ifeq ($(OS),SunOS)
LDFLAGS += -lsocket
endif
ifeq ($(OS),Darwin)
CFLAGS += -arch x86_64
LIBEXT := .dylib
SHARED := -dynamiclib -install_name libhdhomerun$(LIBEXT)
endif
endif
TARGET_X64 := -target x86_64-apple-macos10.11
TARGET_ARM64 := -target arm64-apple-macos11
all : hdhomerun_config libhdhomerun.dylib
hdhomerun_config_x64 : hdhomerun_config.c $(LIBSRCS)
$(CC) $(TARGET_X64) $(CFLAGS) $+ $(LDFLAGS) -o $@
$(STRIP) $@
hdhomerun_config_arm64 : hdhomerun_config.c $(LIBSRCS)
$(CC) $(TARGET_ARM64) $(CFLAGS) $+ $(LDFLAGS) -o $@
$(STRIP) $@
hdhomerun_config : hdhomerun_config_x64 hdhomerun_config_arm64
lipo -create -output hdhomerun_config hdhomerun_config_x64 hdhomerun_config_arm64
libhdhomerun_x64.dylib : $(LIBSRCS)
$(CC) $(TARGET_X64) $(CFLAGS) -DDLL_EXPORT -fPIC -dynamiclib $+ $(LDFLAGS) -o $@
libhdhomerun_arm64.dylib : $(LIBSRCS)
$(CC) $(TARGET_ARM64) $(CFLAGS) -DDLL_EXPORT -fPIC -dynamiclib $+ $(LDFLAGS) -o $@
libhdhomerun.dylib : libhdhomerun_x64.dylib libhdhomerun_arm64.dylib
lipo -create -output libhdhomerun.dylib libhdhomerun_x64.dylib libhdhomerun_arm64.dylib
else
all : hdhomerun_config$(BINEXT) libhdhomerun$(LIBEXT)
@ -45,7 +76,9 @@ hdhomerun_config$(BINEXT) : hdhomerun_config.c $(LIBSRCS)
$(STRIP) $@
libhdhomerun$(LIBEXT) : $(LIBSRCS)
$(CC) $(CFLAGS) -fPIC -DDLL_EXPORT $(SHARED) $+ $(LDFLAGS) -o $@
$(CC) $(CFLAGS) -DDLL_EXPORT -fPIC $(SHARED) $+ $(LDFLAGS) -o $@
endif
clean :
-rm -f hdhomerun_config$(BINEXT)

View file

@ -1,4 +1,4 @@
Copyright © 2005-2017 Silicondust USA Inc. <www.silicondust.com>.
Copyright © 2005-2022 Silicondust USA Inc. <www.silicondust.com>.
This library implements the libhdhomerun protocol for use with Silicondust HDHomeRun TV tuners.

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_config.c
*
* Copyright © 2006-2017 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,7 +27,7 @@ struct hdhomerun_device_t *hd;
static int help(void)
{
printf("Usage:\n");
printf("\t%s discover\n", appname);
printf("\t%s discover [-4] [-6] [--dedupe] [<ip>]\n", appname);
printf("\t%s <id> get help\n", appname);
printf("\t%s <id> get <item>\n", appname);
printf("\t%s <id> set <item> <value>\n", appname);
@ -69,59 +69,102 @@ static bool contains(const char *arg, const char *cmpstr)
return false;
}
static uint32_t parse_ip_addr(const char *str)
static int discover_print(int argc, char *argv[])
{
unsigned int a[4];
if (sscanf(str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) != 4) {
return 0;
}
const char *target_ip_str = NULL;
uint32_t flags = 0;
bool dedupe = false;
return (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
}
static int discover_print(char *target_ip_str)
{
uint32_t target_ip = 0;
if (target_ip_str) {
target_ip = parse_ip_addr(target_ip_str);
if (target_ip == 0) {
fprintf(stderr, "invalid ip address: %s\n", target_ip_str);
return -1;
}
}
struct hdhomerun_discover_device_t result_list[64];
int result_count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, 64);
if (result_count < 0) {
fprintf(stderr, "error sending discover request\n");
return -1;
}
struct hdhomerun_discover_device_t *result = result_list;
struct hdhomerun_discover_device_t *result_end = result_list + result_count;
int valid_count = 0;
while (result < result_end) {
if (result->device_id == 0) {
result++;
while (argc--) {
char *param = *argv++;
if (param[0] != '-') {
target_ip_str = param;
continue;
}
printf("hdhomerun device %08X found at %u.%u.%u.%u\n",
(unsigned int)result->device_id,
(unsigned int)(result->ip_addr >> 24) & 0x0FF, (unsigned int)(result->ip_addr >> 16) & 0x0FF,
(unsigned int)(result->ip_addr >> 8) & 0x0FF, (unsigned int)(result->ip_addr >> 0) & 0x0FF
);
valid_count++;
result++;
if (contains(param, "-4")) {
flags |= HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL;
continue;
}
if (contains(param, "-6")) {
flags |= HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL | HDHOMERUN_DISCOVER_FLAGS_IPV6_LINKLOCAL;
continue;
}
if (valid_count == 0) {
if (contains(param, "dedupe")) {
dedupe = true;
continue;
}
}
if (flags == 0) {
flags = HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL | HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL | HDHOMERUN_DISCOVER_FLAGS_IPV6_LINKLOCAL;
}
struct hdhomerun_discover_t *ds = hdhomerun_discover_create(NULL);
if (!ds) {
fprintf(stderr, "resource error\n");
return -1;
}
/* Most applications will specify a list of specific types rather than wildcard */
uint32_t device_types[1];
device_types[0] = HDHOMERUN_DEVICE_TYPE_WILDCARD;
int ret;
if (target_ip_str) {
struct sockaddr_storage target_addr;
if (!hdhomerun_sock_ip_str_to_sockaddr(target_ip_str , &target_addr)) {
fprintf(stderr, "invalid ip address: %s\n", target_ip_str);
hdhomerun_discover_destroy(ds);
return -1;
}
ret = hdhomerun_discover2_find_devices_targeted(ds, (struct sockaddr *)&target_addr, device_types, 1);
} else {
ret = hdhomerun_discover2_find_devices_broadcast(ds, flags, device_types, 1);
}
if (ret < 0) {
fprintf(stderr, "error sending discover request\n");
hdhomerun_discover_destroy(ds);
return -1;
}
if (ret == 0) {
printf("no devices found\n");
hdhomerun_discover_destroy(ds);
return 0;
}
return valid_count;
struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
while (device) {
uint32_t device_id = hdhomerun_discover2_device_get_device_id(device);
if (device_id == 0) {
device = hdhomerun_discover2_iter_device_next(device);
continue;
}
struct hdhomerun_discover2_device_if_t *device_if = hdhomerun_discover2_iter_device_if_first(device);
while (device_if) {
struct sockaddr_storage ip_addr;
hdhomerun_discover2_device_if_get_ip_addr(device_if, &ip_addr);
char ip_str[64];
hdhomerun_sock_sockaddr_to_ip_str(ip_str, (struct sockaddr *)&ip_addr, true);
printf("hdhomerun device %08X found at %s\n", device_id, ip_str);
if (dedupe) {
break;
}
device_if = hdhomerun_discover2_iter_device_if_next(device_if);
}
device = hdhomerun_discover2_iter_device_next(device);
}
hdhomerun_discover_destroy(ds);
return 1;
}
static int cmd_get(const char *item)
@ -632,7 +675,7 @@ static int main_internal(int argc, char *argv[])
/* Initialize network socket support. */
WORD wVersionRequested = MAKEWORD(2, 0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
(void)WSAStartup(wVersionRequested, &wsaData);
#endif
extract_appname(argv[0]);
@ -648,11 +691,7 @@ static int main_internal(int argc, char *argv[])
return help();
}
if (contains(id_str, "discover")) {
if (argc < 1) {
return discover_print(NULL);
} else {
return discover_print(argv[0]);
}
return discover_print(argc, argv);
}
/* Device object. */

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_control.c
*
* Copyright © 2006-2016 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,9 +27,9 @@
struct hdhomerun_control_sock_t {
uint32_t desired_device_id;
uint32_t desired_device_ip;
uint32_t actual_device_id;
uint32_t actual_device_ip;
struct sockaddr_storage desired_device_addr;
struct sockaddr_storage actual_device_addr;
struct hdhomerun_sock_t *sock;
struct hdhomerun_debug_t *dbg;
struct hdhomerun_pkt_t tx_pkt;
@ -47,16 +47,36 @@ static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
}
void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip)
{
struct sockaddr_in device_addr;
memset(&device_addr, 0, sizeof(device_addr));
device_addr.sin_family = AF_INET;
device_addr.sin_addr.s_addr = htonl(device_ip);
hdhomerun_control_set_device_ex(cs, device_id, (const struct sockaddr *)&device_addr);
}
void hdhomerun_control_set_device_ex(struct hdhomerun_control_sock_t *cs, uint32_t device_id, const struct sockaddr *device_addr)
{
hdhomerun_control_close_sock(cs);
cs->desired_device_id = device_id;
cs->desired_device_ip = device_ip;
cs->actual_device_id = 0;
cs->actual_device_ip = 0;
hdhomerun_sock_sockaddr_copy(&cs->desired_device_addr, device_addr);
memset(&cs->actual_device_addr, 0, sizeof(cs->actual_device_addr));
}
struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg)
{
struct sockaddr_in device_addr;
memset(&device_addr, 0, sizeof(device_addr));
device_addr.sin_family = AF_INET;
device_addr.sin_addr.s_addr = htonl(device_ip);
return hdhomerun_control_create_ex(device_id, (const struct sockaddr *)&device_addr, dbg);
}
struct hdhomerun_control_sock_t *hdhomerun_control_create_ex(uint32_t device_id, const struct sockaddr *device_addr, struct hdhomerun_debug_t *dbg)
{
struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)calloc(1, sizeof(struct hdhomerun_control_sock_t));
if (!cs) {
@ -65,7 +85,7 @@ struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, ui
}
cs->dbg = dbg;
hdhomerun_control_set_device(cs, device_id, device_ip);
hdhomerun_control_set_device_ex(cs, device_id, device_addr);
return cs;
}
@ -76,39 +96,82 @@ void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs)
free(cs);
}
static bool hdhomerun_control_connect_sock_discover(struct hdhomerun_control_sock_t *cs)
{
struct hdhomerun_discover_t *ds = hdhomerun_discover_create(NULL);
if (!ds) {
return false;
}
uint32_t device_types[1];
device_types[0] = HDHOMERUN_DEVICE_TYPE_WILDCARD;
uint32_t device_id = cs->desired_device_id;
if (device_id == 0) {
device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
}
int ret;
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&cs->desired_device_addr)) {
if (device_id == HDHOMERUN_DEVICE_ID_WILDCARD) {
ret = hdhomerun_discover2_find_devices_targeted(ds, (struct sockaddr *)&cs->desired_device_addr, device_types, 1);
} else {
ret = hdhomerun_discover2_find_device_id_targeted(ds, (struct sockaddr *)&cs->desired_device_addr, device_id);
}
} else {
uint32_t flags = HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL;
if (device_id == HDHOMERUN_DEVICE_ID_WILDCARD) {
ret = hdhomerun_discover2_find_devices_broadcast(ds, flags, device_types, 1);
} else {
ret = hdhomerun_discover2_find_device_id_broadcast(ds, flags, device_id);
}
}
if (ret <= 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: device not found\n");
hdhomerun_discover_destroy(ds);
return false;
}
struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
cs->actual_device_id = hdhomerun_discover2_device_get_device_id(device);
struct hdhomerun_discover2_device_if_t *device_if = hdhomerun_discover2_iter_device_if_first(device);
hdhomerun_discover2_device_if_get_ip_addr(device_if, &cs->actual_device_addr);
hdhomerun_discover_destroy(ds);
return true;
}
static bool hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
{
if (cs->sock) {
return true;
}
if ((cs->desired_device_id == 0) && (cs->desired_device_ip == 0)) {
if ((cs->desired_device_id == 0) && !hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&cs->desired_device_addr)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: no device specified\n");
return false;
}
if (hdhomerun_discover_is_ip_multicast(cs->desired_device_ip)) {
if (hdhomerun_discover_is_ip_multicast_ex((struct sockaddr *)&cs->desired_device_addr)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: cannot use multicast ip address for device operations\n");
return false;
}
/* Find device. */
struct hdhomerun_discover_device_t result;
if (hdhomerun_discover_find_devices_custom_v2(cs->desired_device_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, cs->desired_device_id, &result, 1) <= 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: device not found\n");
if (!hdhomerun_control_connect_sock_discover(cs)) {
return false;
}
cs->actual_device_ip = result.ip_addr;
cs->actual_device_id = result.device_id;
/* Create socket. */
cs->sock = hdhomerun_sock_create_tcp();
cs->sock = hdhomerun_sock_create_tcp_ex(cs->actual_device_addr.ss_family);
if (!cs->sock) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", hdhomerun_sock_getlasterror());
return false;
}
/* Initiate connection. */
if (!hdhomerun_sock_connect(cs->sock, cs->actual_device_ip, HDHOMERUN_CONTROL_TCP_PORT, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) {
hdhomerun_sock_sockaddr_set_port((struct sockaddr *)&cs->actual_device_addr, HDHOMERUN_CONTROL_TCP_PORT);
if (!hdhomerun_sock_connect_ex(cs->sock, (struct sockaddr *)&cs->actual_device_addr, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", hdhomerun_sock_getlasterror());
hdhomerun_control_close_sock(cs);
return false;
@ -135,7 +198,24 @@ uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs)
return 0;
}
return cs->actual_device_ip;
if (cs->actual_device_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *actual_device_addr_in = (struct sockaddr_in *)&cs->actual_device_addr;
return ntohl(actual_device_addr_in->sin_addr.s_addr);
}
bool hdhomerun_control_get_device_addr(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result)
{
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_ip: connect failed\n");
memset(result, 0, sizeof(struct sockaddr_storage));
return false;
}
*result = cs->actual_device_addr;
return hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)result);
}
uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs)
@ -145,23 +225,47 @@ uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock
uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs)
{
return cs->desired_device_ip;
if (cs->desired_device_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *desired_device_addr_in = (struct sockaddr_in *)&cs->desired_device_addr;
return ntohl(desired_device_addr_in->sin_addr.s_addr);
}
bool hdhomerun_control_get_device_addr_requested(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result)
{
*result = cs->desired_device_addr;
return hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)result);
}
uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs)
{
struct sockaddr_storage local_addr;
if (!hdhomerun_control_get_local_addr_ex(cs, &local_addr)) {
return 0;
}
if (local_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *local_addr_in = (struct sockaddr_in *)&local_addr;
return ntohl(local_addr_in->sin_addr.s_addr);
}
bool hdhomerun_control_get_local_addr_ex(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result)
{
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: connect failed\n");
return 0;
return false;
}
uint32_t addr = hdhomerun_sock_getsockname_addr(cs->sock);
if (addr == 0) {
if (!hdhomerun_sock_getsockname_addr_ex(cs->sock, result)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
return 0;
return false;
}
return addr;
return true;
}
static bool hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_control.h
*
* Copyright © 2006-2015 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -38,6 +38,7 @@ struct hdhomerun_control_sock_t;
* When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
*/
extern LIBHDHOMERUN_API struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_control_sock_t *hdhomerun_control_create_ex(uint32_t device_id, const struct sockaddr *device_addr, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs);
/*
@ -47,10 +48,13 @@ extern LIBHDHOMERUN_API void hdhomerun_control_destroy(struct hdhomerun_control_
*/
extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs);
extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs);
extern LIBHDHOMERUN_API bool hdhomerun_control_get_device_addr(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs);
extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs);
extern LIBHDHOMERUN_API bool hdhomerun_control_get_device_addr_requested(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip);
extern LIBHDHOMERUN_API void hdhomerun_control_set_device_ex(struct hdhomerun_control_sock_t *cs, uint32_t device_id, const struct sockaddr *device_addr);
/*
* Get the local machine IP address used when communicating with the device.
@ -60,6 +64,7 @@ extern LIBHDHOMERUN_API void hdhomerun_control_set_device(struct hdhomerun_contr
* Returns 32-bit IP address with native endianness, or 0 on error.
*/
extern LIBHDHOMERUN_API uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs);
extern LIBHDHOMERUN_API bool hdhomerun_control_get_local_addr_ex(struct hdhomerun_control_sock_t *cs, struct sockaddr_storage *result);
/*
* Low-level communication.

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_device.c
*
* Copyright © 2006-2015 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -25,8 +25,7 @@ struct hdhomerun_device_t {
struct hdhomerun_video_sock_t *vs;
struct hdhomerun_debug_t *dbg;
struct hdhomerun_channelscan_t *scan;
uint32_t multicast_ip;
uint16_t multicast_port;
struct sockaddr_storage multicast_addr;
uint32_t device_id;
unsigned int tuner;
uint32_t lockkey;
@ -36,13 +35,23 @@ struct hdhomerun_device_t {
int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
{
if ((device_id == 0) && (device_ip == 0)) {
struct sockaddr_in device_addr;
memset(&device_addr, 0, sizeof(device_addr));
device_addr.sin_family = AF_INET;
device_addr.sin_addr.s_addr = htonl(device_ip);
return hdhomerun_device_set_device_ex(hd, device_id, (const struct sockaddr *)&device_addr);
}
int hdhomerun_device_set_device_ex(struct hdhomerun_device_t *hd, uint32_t device_id, const struct sockaddr *device_addr)
{
if ((device_id == 0) && !hdhomerun_sock_sockaddr_is_addr(device_addr)) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: device not specified\n");
return -1;
}
if (hdhomerun_discover_is_ip_multicast(device_ip)) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: invalid address %08X\n", (unsigned int)device_ip);
if (hdhomerun_discover_is_ip_multicast_ex(device_addr)) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: invalid address\n");
return -1;
}
@ -54,14 +63,13 @@ int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_i
}
}
hdhomerun_control_set_device(hd->cs, device_id, device_ip);
hdhomerun_control_set_device_ex(hd->cs, device_id, device_addr);
if ((device_id == 0) || (device_id == HDHOMERUN_DEVICE_ID_WILDCARD)) {
device_id = hdhomerun_control_get_device_id(hd->cs);
}
hd->multicast_ip = 0;
hd->multicast_port = 0;
memset(&hd->multicast_addr, 0, sizeof(hd->multicast_addr));
hd->device_id = device_id;
hd->tuner = 0;
hd->lockkey = 0;
@ -74,11 +82,23 @@ int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_i
int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip, uint16_t multicast_port)
{
if (!hdhomerun_discover_is_ip_multicast(multicast_ip)) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device_multicast: invalid address %08X\n", (unsigned int)multicast_ip);
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
multicast_addr.sin_port = htons(multicast_port);
return hdhomerun_device_set_multicast_ex(hd, (const struct sockaddr *)&multicast_addr);
}
int hdhomerun_device_set_multicast_ex(struct hdhomerun_device_t *hd, const struct sockaddr *multicast_addr)
{
if (!hdhomerun_discover_is_ip_multicast_ex(multicast_addr)) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device_multicast: invalid address\n");
return -1;
}
uint16_t multicast_port = hdhomerun_sock_sockaddr_get_port(multicast_addr);
if (multicast_port == 0) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device_multicast: invalid port %u\n", (unsigned int)multicast_port);
return -1;
@ -89,14 +109,12 @@ int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multi
hd->cs = NULL;
}
hd->multicast_ip = multicast_ip;
hd->multicast_port = multicast_port;
hdhomerun_sock_sockaddr_copy(&hd->multicast_addr, multicast_addr);
hd->device_id = 0;
hd->tuner = 0;
hd->lockkey = 0;
unsigned int ip = multicast_ip;
hdhomerun_sprintf(hd->name, hd->name + sizeof(hd->name), "%u.%u.%u.%u:%u", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF, (unsigned int)multicast_port);
hdhomerun_sprintf(hd->name, hd->name + sizeof(hd->name), "multicast:%u", (unsigned int)multicast_port);
hdhomerun_sprintf(hd->model, hd->model + sizeof(hd->model), "multicast");
return 1;
@ -104,7 +122,7 @@ int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multi
int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
if (tuner != 0) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner: tuner cannot be specified in multicast mode\n");
return -1;
@ -148,16 +166,30 @@ static struct hdhomerun_device_t *hdhomerun_device_create_internal(struct hdhome
struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg)
{
struct sockaddr_in device_addr;
memset(&device_addr, 0, sizeof(device_addr));
device_addr.sin_family = AF_INET;
device_addr.sin_addr.s_addr = htonl(device_ip);
return hdhomerun_device_create_ex(device_id, (const struct sockaddr *)&device_addr, tuner, dbg);
}
struct hdhomerun_device_t *hdhomerun_device_create_ex(uint32_t device_id, const struct sockaddr *device_addr, unsigned int tuner, struct hdhomerun_debug_t *dbg)
{
if ((device_id != 0) && !hdhomerun_discover_validate_device_id(device_id)) {
return NULL;
}
struct hdhomerun_device_t *hd = hdhomerun_device_create_internal(dbg);
if (!hd) {
return NULL;
}
if ((device_id == 0) && (device_ip == 0) && (tuner == 0)) {
if ((device_id == 0) && !hdhomerun_sock_sockaddr_is_addr(device_addr) && (tuner == 0)) {
return hd;
}
if (hdhomerun_device_set_device(hd, device_id, device_ip) <= 0) {
if (hdhomerun_device_set_device_ex(hd, device_id, device_addr) <= 0) {
free(hd);
return NULL;
}
@ -170,13 +202,24 @@ struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t
}
struct hdhomerun_device_t *hdhomerun_device_create_multicast(uint32_t multicast_ip, uint16_t multicast_port, struct hdhomerun_debug_t *dbg)
{
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
multicast_addr.sin_port = htons(multicast_port);
return hdhomerun_device_create_multicast_ex((const struct sockaddr *)&multicast_addr, dbg);
}
struct hdhomerun_device_t *hdhomerun_device_create_multicast_ex(const struct sockaddr *multicast_addr, struct hdhomerun_debug_t *dbg)
{
struct hdhomerun_device_t *hd = hdhomerun_device_create_internal(dbg);
if (!hd) {
return NULL;
}
if (hdhomerun_device_set_multicast(hd, multicast_ip, multicast_port) <= 0) {
if (hdhomerun_device_set_multicast_ex(hd, multicast_addr) <= 0) {
free(hd);
return NULL;
}
@ -201,80 +244,186 @@ void hdhomerun_device_destroy(struct hdhomerun_device_t *hd)
free(hd);
}
static bool hdhomerun_device_create_from_str_parse_device_id(const char *name, uint32_t *pdevice_id)
{
char *end;
uint32_t device_id = (uint32_t)strtoul(name, &end, 16);
if (end != name + 8) {
return false;
}
if (*end != 0) {
return false;
}
*pdevice_id = device_id;
return true;
}
static bool hdhomerun_device_create_from_str_parse_dns(const char *name, struct sockaddr_storage *device_addr)
{
const char *ptr = name;
if (*ptr == 0) {
return false;
}
while (1) {
char c = *ptr++;
if (c == 0) {
break;
}
if ((c >= '0') && (c <= '9')) {
continue;
}
if ((c >= 'a') && (c <= 'z')) {
continue;
}
if ((c >= 'A') && (c <= 'Z')) {
continue;
}
if ((c == '.') || (c == '-')) {
continue;
}
return false;
}
return hdhomerun_sock_getaddrinfo_addr_ex(AF_INET, name, device_addr);
}
static struct hdhomerun_device_t *hdhomerun_device_create_from_str_tail(const char *tail, uint32_t device_id, struct sockaddr_storage *device_addr, struct hdhomerun_debug_t *dbg)
{
const char *ptr = tail;
if (*ptr == 0) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)device_addr, 0, dbg);
}
if (*ptr == ':') {
ptr++;
char *end;
unsigned long port = strtoul(ptr + 1, &end, 10);
if (*end != 0) {
return NULL;
}
if ((port < 1024) || (port > 65535)) {
return NULL;
}
if (device_addr->ss_family == AF_INET) {
struct sockaddr_in *device_addr_in = (struct sockaddr_in *)device_addr;
device_addr_in->sin_port = htons((uint16_t)port);
return hdhomerun_device_create_multicast_ex((struct sockaddr *)device_addr, dbg);
}
if (device_addr->ss_family == AF_INET6) {
struct sockaddr_in6 *device_addr_in = (struct sockaddr_in6 *)device_addr;
device_addr_in->sin6_port = htons((uint16_t)port);
return hdhomerun_device_create_multicast_ex((struct sockaddr *)device_addr, dbg);
}
return NULL;
}
if (*ptr == '-') {
ptr++;
char *end;
unsigned int tuner_index = (unsigned int)strtoul(ptr, &end, 10);
if (*end != 0) {
return NULL;
}
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)device_addr, tuner_index, dbg);
}
return NULL;
}
struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str, struct hdhomerun_debug_t *dbg)
{
/*
* IP address based device_str.
*/
unsigned int a[4];
if (sscanf(device_str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) == 4) {
uint32_t ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
/*
* Multicast IP address.
*/
unsigned int port;
if (sscanf(device_str, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &port) == 5) {
return hdhomerun_device_create_multicast(ip_addr, (uint16_t)port, dbg);
}
/*
* IP address + tuner number.
*/
unsigned int tuner;
if (sscanf(device_str, "%u.%u.%u.%u-%u", &a[0], &a[1], &a[2], &a[3], &tuner) == 5) {
return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, tuner, dbg);
}
/*
* IP address only.
*/
return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, 0, dbg);
}
/*
* Device ID based device_str.
*/
char *end;
uint32_t device_id = (uint32_t)strtoul(device_str, &end, 16);
if ((end == device_str + 8) && hdhomerun_discover_validate_device_id(device_id)) {
/*
* IP address + tuner number.
*/
if (*end == '-') {
unsigned int tuner = (unsigned int)strtoul(end + 1, NULL, 10);
return hdhomerun_device_create(device_id, 0, tuner, dbg);
}
/*
* Device ID only.
*/
return hdhomerun_device_create(device_id, 0, 0, dbg);
}
/*
* DNS based device_str.
*/
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *sock_info;
if (getaddrinfo(device_str, "65001", &hints, &sock_info) != 0) {
char str[64];
if (!hdhomerun_sprintf(str, str + sizeof(str), "%s", device_str)) {
return NULL;
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t ip_addr = (uint32_t)ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
struct sockaddr_storage device_addr;
device_addr.ss_family = 0;
if (ip_addr == 0) {
char *ptr = str;
bool framed = (*ptr == '[');
if (framed) {
ptr++;
char *end = strchr(ptr, ']');
if (!end) {
return NULL;
}
return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, 0, dbg);
*end++ = 0;
if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) {
return hdhomerun_device_create_from_str_tail(end, device_id, &device_addr, dbg);
}
return NULL;
}
char *dash = strchr(ptr, '-');
if (dash) {
*dash = 0;
if (hdhomerun_device_create_from_str_parse_device_id(ptr, &device_id)) {
*dash = '-';
return hdhomerun_device_create_from_str_tail(dash, device_id, &device_addr, dbg);
}
if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) {
*dash = '-';
return hdhomerun_device_create_from_str_tail(dash, device_id, &device_addr, dbg);
}
*dash = '-';
if (hdhomerun_device_create_from_str_parse_dns(ptr, &device_addr)) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)&device_addr, 0, dbg);
}
return NULL;
}
char *colon = strchr(ptr, ':');
if (colon) {
char *second_colon = strchr(colon, ':');
if (second_colon) {
if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)&device_addr, 0, dbg);
}
return NULL;
}
*colon = 0;
if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) {
*colon = ':';
return hdhomerun_device_create_from_str_tail(colon, device_id, &device_addr, dbg);
}
return NULL;
}
if (hdhomerun_device_create_from_str_parse_device_id(ptr, &device_id)) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)&device_addr, 0, dbg);
}
if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)&device_addr, 0, dbg);
}
if (hdhomerun_device_create_from_str_parse_dns(ptr, &device_addr)) {
return hdhomerun_device_create_ex(device_id, (struct sockaddr *)&device_addr, 0, dbg);
}
return NULL;
}
const char *hdhomerun_device_get_name(struct hdhomerun_device_t *hd)
@ -289,38 +438,73 @@ uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd)
uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd)
{
if (hd->multicast_ip != 0) {
return hd->multicast_ip;
struct sockaddr_storage device_addr;
if (!hdhomerun_device_get_device_addr(hd, &device_addr)) {
return 0;
}
if (hd->cs) {
return hdhomerun_control_get_device_ip(hd->cs);
if (device_addr.ss_family != AF_INET) {
return 0;
}
return 0;
struct sockaddr_in *device_addr_in = (struct sockaddr_in *)&device_addr;
return ntohl(device_addr_in->sin_addr.s_addr);
}
bool hdhomerun_device_get_device_addr(struct hdhomerun_device_t *hd, struct sockaddr_storage *result)
{
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
*result = hd->multicast_addr;
return true;
}
if (!hd->cs) {
memset(result, 0, sizeof(struct sockaddr_storage));
return false;
}
return hdhomerun_control_get_device_addr(hd->cs, result);
}
uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
return 0;
}
if (hd->cs) {
return hdhomerun_control_get_device_id_requested(hd->cs);
}
if (!hd->cs) {
return 0;
}
return hdhomerun_control_get_device_id_requested(hd->cs);
}
uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd)
{
if (hd->multicast_ip != 0) {
return hd->multicast_ip;
struct sockaddr_storage device_addr;
if (!hdhomerun_device_get_device_addr_requested(hd, &device_addr)) {
return 0;
}
if (hd->cs) {
return hdhomerun_control_get_device_ip_requested(hd->cs);
if (device_addr.ss_family != AF_INET) {
return 0;
}
return 0;
struct sockaddr_in *device_addr_in = (struct sockaddr_in *)&device_addr;
return ntohl(device_addr_in->sin_addr.s_addr);
}
bool hdhomerun_device_get_device_addr_requested(struct hdhomerun_device_t *hd, struct sockaddr_storage *result)
{
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
*result = hd->multicast_addr;
return true;
}
if (!hd->cs) {
memset(result, 0, sizeof(struct sockaddr_storage));
return false;
}
return hdhomerun_control_get_device_addr_requested(hd->cs, result);
}
unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd)
@ -339,9 +523,24 @@ struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_
return hd->vs;
}
bool allow_port_reuse = (hd->multicast_port != 0);
bool allow_port_reuse = false;
struct sockaddr_storage listen_addr;
memset(&listen_addr, 0, sizeof(listen_addr));
hd->vs = hdhomerun_video_create(hd->multicast_port, allow_port_reuse, VIDEO_DATA_BUFFER_SIZE_1S * 2, hd->dbg);
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
listen_addr.ss_family = hd->multicast_addr.ss_family;
hdhomerun_sock_sockaddr_set_port((struct sockaddr *)&listen_addr, hdhomerun_sock_sockaddr_get_port((struct sockaddr *)&hd->multicast_addr));
allow_port_reuse = true;
}
struct sockaddr_storage device_addr;
if (!hdhomerun_control_get_device_addr(hd->cs, &device_addr)) {
return NULL;
}
listen_addr.ss_family = device_addr.ss_family;
hd->vs = hdhomerun_video_create_ex((struct sockaddr *)&listen_addr, allow_port_reuse, VIDEO_DATA_BUFFER_SIZE_1S * 2, hd->dbg);
if (!hd->vs) {
hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_video_sock: failed to create video object\n");
return NULL;
@ -352,11 +551,26 @@ struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_
uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd)
{
if (hd->cs) {
return hdhomerun_control_get_local_addr(hd->cs);
struct sockaddr_storage local_addr;
if (!hdhomerun_device_get_local_machine_addr_ex(hd, &local_addr)) {
return 0;
}
if (local_addr.ss_family != AF_INET) {
return 0;
}
return 0;
struct sockaddr_in *local_addr_in = (struct sockaddr_in *)&local_addr;
return ntohl(local_addr_in->sin_addr.s_addr);
}
bool hdhomerun_device_get_local_machine_addr_ex(struct hdhomerun_device_t *hd, struct sockaddr_storage *result)
{
if (!hd->cs) {
memset(result, 0, sizeof(struct sockaddr_storage));
return false;
}
return hdhomerun_control_get_local_addr_ex(hd->cs, result);
}
static uint32_t hdhomerun_device_get_status_parse(const char *status_str, const char *tag)
@ -367,7 +581,7 @@ static uint32_t hdhomerun_device_get_status_parse(const char *status_str, const
}
unsigned int value = 0;
sscanf(ptr + strlen(tag), "%u", &value);
(void)sscanf(ptr + strlen(tag), "%u", &value);
return (uint32_t)value;
}
@ -466,12 +680,12 @@ int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **psta
if (status) {
char *channel = strstr(status_str, "ch=");
if (channel) {
sscanf(channel + 3, "%31s", status->channel);
(void)sscanf(channel + 3, "%31s", status->channel);
}
char *lock = strstr(status_str, "lock=");
if (lock) {
sscanf(lock + 5, "%31s", status->lock_str);
(void)sscanf(lock + 5, "%31s", status->lock_str);
}
status->signal_strength = (unsigned int)hdhomerun_device_get_status_parse(status_str, "ss=");
@ -516,12 +730,12 @@ int hdhomerun_device_get_oob_status(struct hdhomerun_device_t *hd, char **pstatu
if (status) {
char *channel = strstr(status_str, "ch=");
if (channel) {
sscanf(channel + 3, "%31s", status->channel);
(void)sscanf(channel + 3, "%31s", status->channel);
}
char *lock = strstr(status_str, "lock=");
if (lock) {
sscanf(lock + 5, "%31s", status->lock_str);
(void)sscanf(lock + 5, "%31s", status->lock_str);
}
status->signal_strength = (unsigned int)hdhomerun_device_get_status_parse(status_str, "ss=");
@ -558,27 +772,27 @@ int hdhomerun_device_get_tuner_vstatus(struct hdhomerun_device_t *hd, char **pvs
if (vstatus) {
char *vch = strstr(vstatus_str, "vch=");
if (vch) {
sscanf(vch + 4, "%31s", vstatus->vchannel);
(void)sscanf(vch + 4, "%31s", vstatus->vchannel);
}
char *name = strstr(vstatus_str, "name=");
if (name) {
sscanf(name + 5, "%31s", vstatus->name);
(void)sscanf(name + 5, "%31s", vstatus->name);
}
char *auth = strstr(vstatus_str, "auth=");
if (auth) {
sscanf(auth + 5, "%31s", vstatus->auth);
(void)sscanf(auth + 5, "%31s", vstatus->auth);
}
char *cci = strstr(vstatus_str, "cci=");
if (cci) {
sscanf(cci + 4, "%31s", vstatus->cci);
(void)sscanf(cci + 4, "%31s", vstatus->cci);
}
char *cgms = strstr(vstatus_str, "cgms=");
if (cgms) {
sscanf(cgms + 5, "%31s", vstatus->cgms);
(void)sscanf(cgms + 5, "%31s", vstatus->cgms);
}
if (strncmp(vstatus->auth, "not-subscribed", 14) == 0) {
@ -1049,7 +1263,7 @@ int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, co
int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char **perror)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
return 1;
}
if (!hd->cs) {
@ -1077,7 +1291,7 @@ int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char *
int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
return 1;
}
if (!hd->cs) {
@ -1099,7 +1313,7 @@ int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd)
int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
return 1;
}
if (!hd->cs) {
@ -1117,7 +1331,7 @@ int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd)
void hdhomerun_device_tuner_lockkey_use_value(struct hdhomerun_device_t *hd, uint32_t lockkey)
{
if (hd->multicast_ip != 0) {
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
return;
}
@ -1163,8 +1377,10 @@ int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd)
hdhomerun_video_set_keepalive(hd->vs, 0, 0, 0);
/* Set target. */
if (hd->multicast_ip != 0) {
int ret = hdhomerun_video_join_multicast_group(hd->vs, hd->multicast_ip, 0);
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
struct sockaddr local_ip;
memset(&local_ip, 0, sizeof(local_ip));
int ret = hdhomerun_video_join_multicast_group_ex(hd->vs, (struct sockaddr *)&hd->multicast_addr, &local_ip);
if (ret <= 0) {
return ret;
}
@ -1216,8 +1432,10 @@ void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd)
return;
}
if (hd->multicast_ip != 0) {
hdhomerun_video_leave_multicast_group(hd->vs, hd->multicast_ip, 0);
if (hdhomerun_sock_sockaddr_is_addr((struct sockaddr *)&hd->multicast_addr)) {
struct sockaddr local_ip;
memset(&local_ip, 0, sizeof(local_ip));
hdhomerun_video_leave_multicast_group_ex(hd->vs, (struct sockaddr *)&hd->multicast_addr, &local_ip);
} else {
hdhomerun_device_set_tuner_target(hd, "none");
}

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_device.h
*
* Copyright © 2006-2015 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -64,7 +64,9 @@ extern "C" {
* /tuner<tuner index>
*/
extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_ex(uint32_t device_id, const struct sockaddr *device_addr, unsigned int tuner, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_multicast(uint32_t multicast_ip, uint16_t multicast_port, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_multicast_ex(const struct sockaddr *multicast_addr, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API void hdhomerun_device_destroy(struct hdhomerun_device_t *hd);
@ -74,12 +76,16 @@ extern LIBHDHOMERUN_API void hdhomerun_device_destroy(struct hdhomerun_device_t
extern LIBHDHOMERUN_API const char *hdhomerun_device_get_name(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API bool hdhomerun_device_get_device_addr(struct hdhomerun_device_t *hd, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API bool hdhomerun_device_get_device_addr_requested(struct hdhomerun_device_t *hd, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
extern LIBHDHOMERUN_API int hdhomerun_device_set_device_ex(struct hdhomerun_device_t *hd, uint32_t device_id, const struct sockaddr *device_addr);
extern LIBHDHOMERUN_API int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip, uint16_t multicast_port);
extern LIBHDHOMERUN_API int hdhomerun_device_set_multicast_ex(struct hdhomerun_device_t *hd, const struct sockaddr *multicast_addr);
extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str);
@ -91,6 +97,7 @@ extern LIBHDHOMERUN_API int hdhomerun_device_set_tuner_from_str(struct hdhomerun
* Returns 32-bit IP address with native endianness, or 0 on error.
*/
extern LIBHDHOMERUN_API uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd);
extern LIBHDHOMERUN_API bool hdhomerun_device_get_local_machine_addr_ex(struct hdhomerun_device_t *hd, struct sockaddr_storage *result);
/*
* Get operations.

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_discover.h
*
* Copyright © 2006-2019 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -21,6 +21,111 @@
extern "C" {
#endif
#define HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL (1 << 0)
#define HDHOMERUN_DISCOVER_FLAGS_IPV4_LOCALHOST (1 << 1)
#define HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL (1 << 2)
#define HDHOMERUN_DISCOVER_FLAGS_IPV6_LINKLOCAL (1 << 3)
#define HDHOMERUN_DISCOVER_FLAGS_IPV6_LOCALHOST (1 << 4)
struct hdhomerun_discover2_device_t;
struct hdhomerun_discover2_device_if_t;
/*
* Discover object.
*
* May be maintained and reused across the lifespan of the app or may be created and destroyed for each discover.
* If the app polls discover the same discover instance should be reused to avoid burning through local IP ports.
*/
extern LIBHDHOMERUN_API struct hdhomerun_discover_t *hdhomerun_discover_create(struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds);
/*
* Discover API:
*
* Use hdhomerun_discover2_find_devices_broadcast() to find all devices on the local subnet.
* flags: IPv4 only use HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL
* flags: IPv4 and IPv6 use HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL | HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL
* flags: Linklocal IPv6 requires special handling (scope id) - only include HDHOMERUN_DISCOVER_FLAGS_IPV6_LINKLOCAL if the app tracks scope id needed for linklocal IPv6.
* device_types: do not use HDHOMERUN_DEVICE_TYPE_WILDCARD, instead provide an array of types the app wants to discover, for example:
* uint32_t device_types[2];
* device_types[0] = HDHOMERUN_DEVICE_TYPE_TUNER;
* device_types[1] = HDHOMERUN_DEVICE_TYPE_STORAGE;
* Returns 1 when one or more devices are found.
* Results 0 when no devices are found.
* Returns -1 on error.
*/
extern LIBHDHOMERUN_API int hdhomerun_discover2_find_devices_targeted(struct hdhomerun_discover_t *ds, const struct sockaddr *target_addr, const uint32_t device_types[], size_t device_types_count);
extern LIBHDHOMERUN_API int hdhomerun_discover2_find_devices_broadcast(struct hdhomerun_discover_t *ds, uint32_t flags, uint32_t const device_types[], size_t device_types_count);
extern LIBHDHOMERUN_API int hdhomerun_discover2_find_device_id_targeted(struct hdhomerun_discover_t *ds, const struct sockaddr *target_addr, uint32_t device_id);
extern LIBHDHOMERUN_API int hdhomerun_discover2_find_device_id_broadcast(struct hdhomerun_discover_t *ds, uint32_t flags, uint32_t device_id);
/*
* Discover result access API.
*
* Use hdhomerun_discover2_iter_device_first() and hdhomerun_discover2_iter_device_next() to iterate through discover results.
* Use hdhomerun_discover2_device_xxx() APIs to query properties of the device.
*
* Use hdhomerun_discover2_iter_device_if_first() to select the first (best) set of device URLs.
* Use hdhomerun_discover2_device_if_xxx() to query specific device URLs.
*
* In most cases hdhomerun_discover2_iter_device_if_next() will not be used as the app should use the first set of URLs.
*
* Results are available until a new discover is run on the same discover object or until the discover object is destroyed. Strings shoud be copied.
*/
extern LIBHDHOMERUN_API struct hdhomerun_discover2_device_t *hdhomerun_discover2_iter_device_first(struct hdhomerun_discover_t *ds);
extern LIBHDHOMERUN_API struct hdhomerun_discover2_device_t *hdhomerun_discover2_iter_device_next(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API struct hdhomerun_discover2_device_if_t *hdhomerun_discover2_iter_device_if_first(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API struct hdhomerun_discover2_device_if_t *hdhomerun_discover2_iter_device_if_next(struct hdhomerun_discover2_device_if_t *device_if);
extern LIBHDHOMERUN_API bool hdhomerun_discover2_device_is_legacy(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API bool hdhomerun_discover2_device_is_type(struct hdhomerun_discover2_device_t *device, uint32_t device_type);
extern LIBHDHOMERUN_API uint32_t hdhomerun_discover2_device_get_device_id(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_get_storage_id(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API uint8_t hdhomerun_discover2_device_get_tuner_count(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_get_device_auth(struct hdhomerun_discover2_device_t *device);
extern LIBHDHOMERUN_API bool hdhomerun_discover2_device_if_addr_is_ipv4(struct hdhomerun_discover2_device_if_t *device_if);
extern LIBHDHOMERUN_API bool hdhomerun_discover2_device_if_addr_is_ipv6_linklocal(struct hdhomerun_discover2_device_if_t *device_if);
extern LIBHDHOMERUN_API void hdhomerun_discover2_device_if_get_ip_addr(struct hdhomerun_discover2_device_if_t *device_if, struct sockaddr_storage *ip_addr);
extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_if_get_base_url(struct hdhomerun_discover2_device_if_t *device_if);
extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_if_get_lineup_url(struct hdhomerun_discover2_device_if_t *device_if);
extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_if_get_storage_url(struct hdhomerun_discover2_device_if_t *device_if);
/*
* Verify that the device ID given is valid.
*
* The device ID contains a self-check sequence that detects common user input errors including
* single-digit errors and two digit transposition errors.
*
* Returns true if valid.
* Returns false if not valid.
*/
extern LIBHDHOMERUN_API bool hdhomerun_discover_validate_device_id(uint32_t device_id);
/*
* Detect if an IP address is multicast.
*
* Returns true if multicast.
* Returns false if zero, unicast, expermental, or broadcast.
*/
extern LIBHDHOMERUN_API bool hdhomerun_discover_is_ip_multicast(uint32_t ip_addr);
extern LIBHDHOMERUN_API bool hdhomerun_discover_is_ip_multicast_ex(const struct sockaddr *ip_addr);
/*
* Legacy API - not for new applications.
*
* The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars.
* Multiple attempts are made to find devices.
* Execution time is typically 400ms unless max_count is reached.
*
* Set target_ip to zero to auto-detect the IP address.
* Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices.
* Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids.
*
* Returns the number of devices found.
* Retruns -1 on error.
*/
struct hdhomerun_discover_device_t {
uint32_t ip_addr;
uint32_t device_type;
@ -45,50 +150,12 @@ struct hdhomerun_discover_device_v3_t {
char storage_url[128];
};
/*
* Find devices.
*
* The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars.
* Multiple attempts are made to find devices.
* Execution time is typically 400ms unless max_count is reached.
*
* Set target_ip to zero to auto-detect the IP address.
* Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices.
* Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids.
*
* Returns the number of devices found.
* Retruns -1 on error.
*/
extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_custom_v2(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count);
extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_custom_v3(uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count);
/*
* Optional: persistent discover instance available for discover polling use.
*/
extern LIBHDHOMERUN_API struct hdhomerun_discover_t *hdhomerun_discover_create(struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds);
extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_v2(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_t result_list[], int max_count);
extern LIBHDHOMERUN_API int hdhomerun_discover_find_devices_v3(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type_match, uint32_t device_id_match, struct hdhomerun_discover_device_v3_t result_list[], int max_count);
/*
* Verify that the device ID given is valid.
*
* The device ID contains a self-check sequence that detects common user input errors including
* single-digit errors and two digit transposition errors.
*
* Returns true if valid.
* Returns false if not valid.
*/
extern LIBHDHOMERUN_API bool hdhomerun_discover_validate_device_id(uint32_t device_id);
/*
* Detect if an IP address is multicast.
*
* Returns true if multicast.
* Returns false if zero, unicast, expermental, or broadcast.
*/
extern LIBHDHOMERUN_API bool hdhomerun_discover_is_ip_multicast(uint32_t ip_addr);
#ifdef __cplusplus
}
#endif

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_pkt.h
*
* Copyright © 2006-2015 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -133,6 +133,7 @@ extern "C" {
#define HDHOMERUN_TAG_BASE_URL 0x2A
#define HDHOMERUN_TAG_DEVICE_AUTH_STR 0x2B
#define HDHOMERUN_TAG_STORAGE_ID 0x2C
#define HDHOMERUN_TAG_MULTI_TYPE 0x2D
#define HDHOMERUN_DEVICE_TYPE_WILDCARD 0xFFFFFFFF
#define HDHOMERUN_DEVICE_TYPE_TUNER 0x00000001

380
hdhomerun_sock.c Normal file
View file

@ -0,0 +1,380 @@
/*
* hdhomerun_sock.c
*
* Copyright © 2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#if defined(_WIN32)
#include <netioapi.h>
#else
#include <net/if.h>
#endif
#if defined(_WINRT)
static inline uint32_t if_nametoindex(const char *ifname) { return 0; }
#endif
bool hdhomerun_sock_sockaddr_is_addr(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET6) {
static uint8_t hdhomerun_sock_ipv6_zero_bytes[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)addr;
return (memcmp(addr_in6->sin6_addr.s6_addr, hdhomerun_sock_ipv6_zero_bytes, 16) != 0);
}
if (addr->sa_family == AF_INET) {
const struct sockaddr_in *addr_in = (const struct sockaddr_in *)addr;
return (addr_in->sin_addr.s_addr != 0);
}
return false;
}
uint16_t hdhomerun_sock_sockaddr_get_port(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)addr;
return ntohs(addr_in6->sin6_port);
}
if (addr->sa_family == AF_INET) {
const struct sockaddr_in *addr_in = (const struct sockaddr_in *)addr;
return ntohs(addr_in->sin_port);
}
return 0;
}
void hdhomerun_sock_sockaddr_set_port(struct sockaddr *addr, uint16_t port)
{
if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
addr_in6->sin6_port = htons(port);
return;
}
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
addr_in->sin_port = htons(port);
return;
}
}
void hdhomerun_sock_sockaddr_copy(struct sockaddr_storage *result, const struct sockaddr *addr)
{
memset(result, 0, sizeof(struct sockaddr_storage));
if (addr->sa_family == AF_INET6) {
memcpy(result, addr, sizeof(struct sockaddr_in6));
}
if (addr->sa_family == AF_INET) {
memcpy(result, addr, sizeof(struct sockaddr_in));
}
}
void hdhomerun_sock_sockaddr_to_ip_str(char ip_str[64], const struct sockaddr *ip_addr, bool include_ipv6_scope_id)
{
size_t ip_str_size = 64;
if (ip_addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *ip_addr_in6 = (const struct sockaddr_in6 *)ip_addr;
inet_ntop(AF_INET6, ip_addr_in6->sin6_addr.s6_addr, ip_str, ip_str_size);
if (include_ipv6_scope_id && (ip_addr_in6->sin6_scope_id != 0)) {
hdhomerun_sprintf(strchr(ip_str, 0), ip_str + ip_str_size, "%%%u", ip_addr_in6->sin6_scope_id);
}
return;
}
if (ip_addr->sa_family == AF_INET) {
const struct sockaddr_in *ip_addr_in = (const struct sockaddr_in *)ip_addr;
inet_ntop(AF_INET, &ip_addr_in->sin_addr.s_addr, ip_str, ip_str_size);
return;
}
ip_str[0] = 0;
}
bool hdhomerun_sock_ip_str_to_sockaddr(const char *ip_str, struct sockaddr_storage *result)
{
memset(result, 0, sizeof(struct sockaddr_storage));
struct sockaddr_in *result_in = (struct sockaddr_in *)result;
if (inet_pton(AF_INET, ip_str, &result_in->sin_addr) == 1) {
result_in->sin_family = AF_INET;
return true;
}
bool framed = (*ip_str == '[');
bool ifname_present = (strchr(ip_str, '%') != NULL);
if (!framed && !ifname_present) {
struct sockaddr_in6 *result_in6 = (struct sockaddr_in6 *)result;
if (inet_pton(AF_INET6, ip_str, &result_in6->sin6_addr) == 1) {
result_in6->sin6_family = AF_INET6;
return true;
}
return false;
}
if (framed) {
ip_str++;
}
char ip_str_tmp[64];
if (!hdhomerun_sprintf(ip_str_tmp, ip_str_tmp + sizeof(ip_str_tmp), "%s", ip_str)) {
return false;
}
if (framed) {
char *end = strchr(ip_str_tmp, ']');
if (!end) {
return false;
}
*end++ = 0;
if (*end) {
return false;
}
}
char *ifname = NULL;
if (ifname_present) {
ifname = strchr(ip_str_tmp, '%');
*ifname++ = 0;
}
struct sockaddr_in6 *result_in6 = (struct sockaddr_in6 *)result;
if (inet_pton(AF_INET6, ip_str_tmp, &result_in6->sin6_addr) != 1) {
return false;
}
result_in6->sin6_family = AF_INET6;
if (!ifname_present) {
return true;
}
char *end;
result_in6->sin6_scope_id = (uint32_t)strtoul(ifname, &end, 10);
if ((result_in6->sin6_scope_id > 0) && (*end == 0)) {
return true;
}
result_in6->sin6_scope_id = if_nametoindex(ifname);
return (result_in6->sin6_scope_id > 0);
}
struct hdhomerun_local_ip_info_state_t {
struct hdhomerun_local_ip_info_t *ip_info;
int max_count;
int count;
};
static void hdhomerun_local_ip_info_callback(void *arg, uint32_t ifindex, const struct sockaddr *local_ip, uint8_t cidr)
{
struct hdhomerun_local_ip_info_state_t *state = (struct hdhomerun_local_ip_info_state_t *)arg;
if (state->count >= state->max_count) {
state->count++;
return;
}
const struct sockaddr_in *local_ip_in = (const struct sockaddr_in *)local_ip;
state->ip_info->ip_addr = ntohl(local_ip_in->sin_addr.s_addr);
state->ip_info->subnet_mask = 0xFFFFFFFF << (32 - cidr);
state->ip_info++;
state->count++;
}
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
struct hdhomerun_local_ip_info_state_t state;
state.ip_info = ip_info_list;
state.max_count = max_count;
state.count = 0;
if (!hdhomerun_local_ip_info2(AF_INET, hdhomerun_local_ip_info_callback, &state)) {
return -1;
}
return state.count;
}
struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void)
{
return hdhomerun_sock_create_udp_ex(AF_INET);
}
struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void)
{
return hdhomerun_sock_create_tcp_ex(AF_INET);
}
uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock)
{
struct sockaddr_storage sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
if (!hdhomerun_sock_getsockname_addr_ex(sock, &sock_addr)) {
return 0;
}
if (sock_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
return ntohl(sock_addr_in->sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock)
{
struct sockaddr_storage sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
if (!hdhomerun_sock_getpeername_addr_ex(sock, &sock_addr)) {
return 0;
}
if (sock_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
return ntohl(sock_addr_in->sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name)
{
struct sockaddr_storage sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
if (!hdhomerun_sock_getaddrinfo_addr_ex(AF_INET, name, &sock_addr)) {
return 0;
}
if (sock_addr.ss_family != AF_INET) {
return 0;
}
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
return ntohl(sock_addr_in->sin_addr.s_addr);
}
bool hdhomerun_sock_getaddrinfo_addr_ex(int af, const char *name, struct sockaddr_storage *result)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *sock_info;
if (getaddrinfo(name, NULL, &hints, &sock_info) != 0) {
return false;
}
if ((size_t)sock_info->ai_addrlen > sizeof(struct sockaddr_storage)) {
return false;
}
memcpy(result, sock_info->ai_addr, sock_info->ai_addrlen);
freeaddrinfo(sock_info);
return true;
}
bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
{
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(local_ip);
return hdhomerun_sock_join_multicast_group_ex(sock, (const struct sockaddr *)&multicast_addr, (const struct sockaddr *)&local_addr);
}
bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
{
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(local_ip);
return hdhomerun_sock_leave_multicast_group_ex(sock, (const struct sockaddr *)&multicast_addr, (const struct sockaddr *)&local_addr);
}
bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse)
{
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(local_addr);
sock_addr.sin_port = htons(local_port);
return hdhomerun_sock_bind_ex(sock, (const struct sockaddr *)&sock_addr, allow_reuse);
}
bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
{
struct sockaddr_in sock_addr_in;
memset(&sock_addr_in, 0, sizeof(sock_addr_in));
sock_addr_in.sin_family = AF_INET;
sock_addr_in.sin_addr.s_addr = htonl(remote_addr);
sock_addr_in.sin_port = htons(remote_port);
return hdhomerun_sock_connect_ex(sock, (const struct sockaddr *)&sock_addr_in, timeout);
}
bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
{
struct sockaddr_in sock_addr_in;
memset(&sock_addr_in, 0, sizeof(sock_addr_in));
sock_addr_in.sin_family = AF_INET;
sock_addr_in.sin_addr.s_addr = htonl(remote_addr);
sock_addr_in.sin_port = htons(remote_port);
return hdhomerun_sock_sendto_ex(sock, (const struct sockaddr *)&sock_addr_in, data, length, timeout);
}
bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
{
struct sockaddr_storage sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
if (!hdhomerun_sock_recvfrom_ex(sock, &sock_addr, data, length, timeout)) {
return false;
}
if (sock_addr.ss_family != AF_INET) {
return false;
}
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
*remote_addr = ntohl(sock_addr_in->sin_addr.s_addr);
*remote_port = ntohs(sock_addr_in->sin_port);
return true;
}

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_sock.h
*
* Copyright © 2010-2016 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2010-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -21,42 +21,81 @@
extern "C" {
#endif
/*
* Windows:
* hdhomerun_sock.c
* hdhomerun_sock_windows.c
*
* Linux/Android:
* hdhomerun_sock.c
* hdhomerun_sock_netlink.c
* hdhomerun_sock_posix.c
*
* Mac/BSD:
* hdhomerun_sock.c
* hdhomerun_sock_getifaddrs.c
* hdhomerun_sock_posix.c
*/
extern LIBHDHOMERUN_API bool hdhomerun_sock_sockaddr_is_addr(const struct sockaddr *addr);
extern LIBHDHOMERUN_API uint16_t hdhomerun_sock_sockaddr_get_port(const struct sockaddr *addr);
extern LIBHDHOMERUN_API void hdhomerun_sock_sockaddr_set_port(struct sockaddr *addr, uint16_t port);
extern LIBHDHOMERUN_API void hdhomerun_sock_sockaddr_copy(struct sockaddr_storage *result, const struct sockaddr *addr);
extern LIBHDHOMERUN_API void hdhomerun_sock_sockaddr_to_ip_str(char ip_str[64], const struct sockaddr *ip_addr, bool include_ipv6_scope_id);
extern LIBHDHOMERUN_API bool hdhomerun_sock_ip_str_to_sockaddr(const char *ip_str, struct sockaddr_storage *result);
struct hdhomerun_local_ip_info_t {
uint32_t ip_addr;
uint32_t subnet_mask;
};
typedef void (*hdhomerun_local_ip_info2_callback_t)(void *arg, uint32_t ifindex, const struct sockaddr *local_ip, uint8_t cidr);
extern LIBHDHOMERUN_API int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count);
extern LIBHDHOMERUN_API void hdhomerun_local_ip_info_set_str(const char *ip_info_str); /* WinRT only */
extern LIBHDHOMERUN_API bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg);
struct hdhomerun_sock_t;
extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void);
extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void);
extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_udp_ex(int af);
extern LIBHDHOMERUN_API struct hdhomerun_sock_t *hdhomerun_sock_create_tcp_ex(int af);
extern LIBHDHOMERUN_API void hdhomerun_sock_stop(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API void hdhomerun_sock_set_send_buffer_size(struct hdhomerun_sock_t *sock, size_t size);
extern LIBHDHOMERUN_API void hdhomerun_sock_set_recv_buffer_size(struct hdhomerun_sock_t *sock, size_t size);
extern LIBHDHOMERUN_API void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API void hdhomerun_sock_set_ipv4_onesbcast(struct hdhomerun_sock_t *sock, int v);
extern LIBHDHOMERUN_API void hdhomerun_sock_set_ipv6_multicast_ifindex(struct hdhomerun_sock_t *sock, uint32_t ifindex);
extern LIBHDHOMERUN_API int hdhomerun_sock_getlasterror(void);
extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API bool hdhomerun_sock_getsockname_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock);
extern LIBHDHOMERUN_API bool hdhomerun_sock_getpeername_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name);
extern LIBHDHOMERUN_API bool hdhomerun_sock_getaddrinfo_addr_ex(int af, const char *name, struct sockaddr_storage *result);
extern LIBHDHOMERUN_API bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip);
extern LIBHDHOMERUN_API bool hdhomerun_sock_join_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr);
extern LIBHDHOMERUN_API bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip);
extern LIBHDHOMERUN_API bool hdhomerun_sock_leave_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr);
extern LIBHDHOMERUN_API bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse);
extern LIBHDHOMERUN_API bool hdhomerun_sock_bind_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *local_addr, bool allow_reuse);
extern LIBHDHOMERUN_API bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_connect_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t length, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_sendto_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, const void *data, size_t length, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *length, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout);
extern LIBHDHOMERUN_API bool hdhomerun_sock_recvfrom_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *remote_addr, void *data, size_t *length, uint64_t timeout);
#ifdef __cplusplus
}

130
hdhomerun_sock_getifaddrs.c Normal file
View file

@ -0,0 +1,130 @@
/*
* hdhomerun_sock_getifaddrs.c
*
* Copyright © 2010-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet6/in6_var.h>
#include <ifaddrs.h>
static uint8_t hdhomerun_local_ip_netmask_to_cidr(uint8_t *subnet_mask, size_t len)
{
uint8_t result = 0;
uint8_t *ptr = subnet_mask;
uint8_t *end = subnet_mask + len;
while (ptr < end) {
uint8_t c = *ptr++;
if (c == 0xFF) {
result += 8;
continue;
}
while (c & 0x80) {
result++;
c <<= 1;
}
break;
}
return result;
}
bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg)
{
int af6_sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (af6_sock == -1) {
return -1;
}
struct ifaddrs *ifaddrs;
if (getifaddrs(&ifaddrs) != 0) {
close(af6_sock);
return -1;
}
struct ifaddrs *ifa = ifaddrs;
while (ifa) {
if (ifa->ifa_addr == NULL) {
ifa = ifa->ifa_next;
continue;
}
if ((af != AF_UNSPEC) && (ifa->ifa_addr->sa_family != af)) {
ifa = ifa->ifa_next;
continue;
}
unsigned int flags = ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING | IFF_MULTICAST);
if (flags != (IFF_UP | IFF_RUNNING | IFF_MULTICAST)) {
ifa = ifa->ifa_next;
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) {
uint32_t ifindex = if_nametoindex(ifa->ifa_name);
struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask;
uint8_t cidr = hdhomerun_local_ip_netmask_to_cidr((uint8_t *)&netmask_in->sin_addr.s_addr, 4);
callback(callback_arg, ifindex, ifa->ifa_addr, cidr);
ifa = ifa->ifa_next;
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET6) {
struct in6_ifreq ifr6;
memset(&ifr6, 0, sizeof(ifr6));
struct sockaddr_in6 *addr_in = (struct sockaddr_in6 *)ifa->ifa_addr;
strcpy(ifr6.ifr_name, ifa->ifa_name);
ifr6.ifr_addr = *addr_in;
if (ioctl(af6_sock, SIOCGIFALIFETIME_IN6, &ifr6) < 0) {
ifa = ifa->ifa_next;
continue;
}
if (ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime != 0xFFFFFFFF) {
ifa = ifa->ifa_next;
continue;
}
uint32_t ifindex = if_nametoindex(ifa->ifa_name);
struct sockaddr_in6 *netmask_in = (struct sockaddr_in6 *)ifa->ifa_netmask;
uint8_t cidr = hdhomerun_local_ip_netmask_to_cidr(netmask_in->sin6_addr.s6_addr, 16);
callback(callback_arg, ifindex, ifa->ifa_addr, cidr);
ifa = ifa->ifa_next;
continue;
}
ifa = ifa->ifa_next;
}
close(af6_sock);
freeifaddrs(ifaddrs);
return true;
}

130
hdhomerun_sock_netdevice.c Normal file
View file

@ -0,0 +1,130 @@
/*
* hdhomerun_sock_getifaddrs.c
*
* Copyright © 2010-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#include <sys/ioctl.h>
#include <net/if.h>
static uint8_t hdhomerun_local_ip_netmask_to_cidr(uint8_t *subnet_mask, size_t len)
{
uint8_t result = 0;
uint8_t *ptr = subnet_mask;
uint8_t *end = subnet_mask + len;
while (ptr < end) {
uint8_t c = *ptr++;
if (c == 0xFF) {
result += 8;
continue;
}
while (c & 0x80) {
result++;
c <<= 1;
}
break;
}
return result;
}
bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg)
{
if (af != AF_INET) {
return false;
}
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) {
return false;
}
int ifreq_buffer_size = 128 * sizeof(struct ifreq);
char *ifreq_buffer = (char *)calloc(ifreq_buffer_size, 1);
if (!ifreq_buffer) {
close(sock);
return false;
}
struct ifconf ifc;
ifc.ifc_len = ifreq_buffer_size;
ifc.ifc_buf = ifreq_buffer;
if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) {
free(ifreq_buffer);
close(sock);
return false;
}
if (ifc.ifc_len > ifreq_buffer_size) {
ifc.ifc_len = ifreq_buffer_size;
}
char *ptr = ifc.ifc_buf;
char *end = ifc.ifc_buf + ifc.ifc_len;
while (ptr + sizeof(struct ifreq) <= end) {
struct ifreq *ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
/* Local IP address. */
struct sockaddr_in ip_addr_in;
memcpy(&ip_addr_in, &ifr->ifr_addr, sizeof(ip_addr_in));
uint32_t ip_addr = ntohl(ip_addr_in.sin_addr.s_addr);
if (ip_addr == 0) {
continue;
}
/* Flags. */
if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) {
continue;
}
unsigned int flags = ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING | IFF_MULTICAST);
if (flags != (IFF_UP | IFF_RUNNING | IFF_MULTICAST)) {
continue;
}
/* Subnet mask. */
if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
continue;
}
struct sockaddr_in *netmask_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint8_t cidr = hdhomerun_local_ip_netmask_to_cidr((uint8_t *)&netmask_in->sin_addr.s_addr, 4);
/* ifindex. */
if (ioctl(sock, SIOCGIFINDEX, ifr) != 0) {
continue;
}
uint32_t ifindex = ifr->ifr_ifindex;
/* Result. */
callback(callback_arg, ifindex, (const struct sockaddr *)&ip_addr_in, cidr);
}
free(ifreq_buffer);
close(sock);
return true;
}

192
hdhomerun_sock_netlink.c Normal file
View file

@ -0,0 +1,192 @@
/*
* hdhomerun_sock_netlink.c
*
* Copyright © 2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define HDHOMERUN_SOCK_NETLINK_BUFFER_SIZE 32768
struct nlmsghdr_ifaddrmsg {
struct nlmsghdr nlh;
struct ifaddrmsg msg;
};
static void hdhomerun_local_ip_info2_newaddr(int af_sock, struct nlmsghdr *hdr, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg)
{
struct ifaddrmsg *addrmsg = (struct ifaddrmsg *)NLMSG_DATA(hdr);
if ((addrmsg->ifa_family != AF_INET) && (addrmsg->ifa_family != AF_INET6)) {
return;
}
if ((addrmsg->ifa_family == AF_INET6) && (addrmsg->ifa_flags & IFA_F_TEMPORARY)) {
return; /* skip temporary IPv6 addresses */
}
/* ifindex */
uint32_t ifindex = addrmsg->ifa_index;
/* interface flags */
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
if (!if_indextoname(ifindex, ifr.ifr_name)) {
return;
}
if (ioctl(af_sock, SIOCGIFFLAGS, &ifr) < 0) {
return;
}
uint32_t flags = ifr.ifr_flags;
flags &= (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING | IFF_MULTICAST);
if (flags != (IFF_UP | IFF_RUNNING | IFF_MULTICAST)) {
return;
}
/* addresses */
size_t ifa_payload_length = IFA_PAYLOAD(hdr);
struct rtattr *rta = IFA_RTA(addrmsg);
while (1) {
if (!RTA_OK(rta, ifa_payload_length)) {
break;
}
if (rta->rta_type != IFA_ADDRESS) {
rta = RTA_NEXT(rta, ifa_payload_length);
continue;
}
uint8_t cidr = (uint8_t)addrmsg->ifa_prefixlen;
if (addrmsg->ifa_family == AF_INET) {
struct sockaddr_in local_ip;
memset(&local_ip, 0, sizeof(local_ip));
local_ip.sin_family = AF_INET;
memcpy(&local_ip.sin_addr.s_addr, RTA_DATA(rta), 4);
callback(callback_arg, ifindex, (const struct sockaddr *)&local_ip, cidr);
}
if (addrmsg->ifa_family == AF_INET6) {
struct sockaddr_in6 local_ip;
memset(&local_ip, 0, sizeof(local_ip));
local_ip.sin6_family = AF_INET6;
memcpy(local_ip.sin6_addr.s6_addr, RTA_DATA(rta), 16);
if ((local_ip.sin6_addr.s6_addr[0] == 0xFE) && ((local_ip.sin6_addr.s6_addr[1] & 0xC0) == 0x80)) {
local_ip.sin6_scope_id = ifindex;
}
callback(callback_arg, ifindex, (const struct sockaddr *)&local_ip, cidr);
}
rta = RTA_NEXT(rta, ifa_payload_length);
}
}
bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg)
{
uint8_t *nl_buffer = (uint8_t *)malloc(HDHOMERUN_SOCK_NETLINK_BUFFER_SIZE);
if (!nl_buffer) {
return false;
}
int nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
int af_sock = socket(AF_INET, SOCK_DGRAM, 0);
if ((nl_sock == -1) || (af_sock == -1)) {
close(af_sock);
close(nl_sock);
free(nl_buffer);
return false;
}
struct nlmsghdr_ifaddrmsg req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req)));
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
req.msg.ifa_family = af;
if (send(nl_sock, &req, req.nlh.nlmsg_len, 0) != (ssize_t)req.nlh.nlmsg_len) {
close(af_sock);
close(nl_sock);
free(nl_buffer);
return false;
}
bool again = true;
while (1) {
struct pollfd poll_fds[1];
poll_fds[0].fd = nl_sock;
poll_fds[0].events = POLLIN;
poll_fds[0].revents = 0;
int ret = poll(poll_fds, 1, 25);
if (ret <= 0) {
break;
}
if ((poll_fds[0].revents & POLLIN) == 0) {
break;
}
int length = (int)recv(nl_sock, nl_buffer, HDHOMERUN_SOCK_NETLINK_BUFFER_SIZE, 0);
if (length <= 0) {
break;
}
struct nlmsghdr *hdr = (struct nlmsghdr *)nl_buffer;
while (1) {
if (!NLMSG_OK(hdr, length)) {
break;
}
if (hdr->nlmsg_type == NLMSG_DONE) {
again = false;
break;
}
if (hdr->nlmsg_type == NLMSG_ERROR) {
again = false;
break;
}
if (hdr->nlmsg_type == RTM_NEWADDR) {
hdhomerun_local_ip_info2_newaddr(af_sock, hdr, callback, callback_arg);
}
hdr = NLMSG_NEXT(hdr, length);
}
if (!again) {
break;
}
}
close(af_sock);
close(nl_sock);
free(nl_buffer);
return true;
}

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_sock_posix.c
*
* Copyright © 2010-2019 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2010-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -20,14 +20,6 @@
#include "hdhomerun.h"
#if defined(LIBHDHOMERUN_USE_SIOCGIFCONF)
#include <sys/ioctl.h>
#else
#include <ifaddrs.h>
#endif
#include <net/if.h>
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
@ -36,136 +28,7 @@ struct hdhomerun_sock_t {
int sock;
};
#if defined(LIBHDHOMERUN_USE_SIOCGIFCONF)
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) {
return -1;
}
int ifreq_buffer_size = 128 * sizeof(struct ifreq);
char *ifreq_buffer = (char *)calloc(ifreq_buffer_size, 1);
if (!ifreq_buffer) {
close(sock);
return -1;
}
struct ifconf ifc;
ifc.ifc_len = ifreq_buffer_size;
ifc.ifc_buf = ifreq_buffer;
if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) {
free(ifreq_buffer);
close(sock);
return -1;
}
if (ifc.ifc_len > ifreq_buffer_size) {
ifc.ifc_len = ifreq_buffer_size;
}
struct hdhomerun_local_ip_info_t *ip_info = ip_info_list;
int count = 0;
char *ptr = ifc.ifc_buf;
char *end = ifc.ifc_buf + ifc.ifc_len;
while (ptr + sizeof(struct ifreq) <= end) {
struct ifreq *ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
/* Local IP address. */
struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr);
if (ip_addr == 0) {
continue;
}
/* Flags. */
if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) {
continue;
}
unsigned int flags = ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING);
if (flags != (IFF_UP | IFF_RUNNING)) {
continue;
}
/* Subnet mask. */
if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
continue;
}
struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr);
/* Result. */
if (count < max_count) {
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
ip_info++;
}
count++;
}
free(ifreq_buffer);
close(sock);
return count;
}
#else
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
struct ifaddrs *ifaddrs;
if (getifaddrs(&ifaddrs) != 0) {
return -1;
}
struct hdhomerun_local_ip_info_t *ip_info = ip_info_list;
struct ifaddrs *ifa = ifaddrs;
int count = 0;
while (ifa) {
if (ifa->ifa_addr == NULL) {
ifa = ifa->ifa_next;
continue;
}
if (ifa->ifa_addr->sa_family != AF_INET) {
ifa = ifa->ifa_next;
continue;
}
unsigned int flags = ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING);
if (flags != (IFF_UP | IFF_RUNNING)) {
ifa = ifa->ifa_next;
continue;
}
struct sockaddr_in *addr_in = (struct sockaddr_in *)ifa->ifa_addr;
uint32_t ip_addr = ntohl(addr_in->sin_addr.s_addr);
struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask;
uint32_t subnet_mask = ntohl(netmask_in->sin_addr.s_addr);
ifa = ifa->ifa_next;
if (count < max_count) {
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
ip_info++;
}
count++;
}
freeifaddrs(ifaddrs);
return count;
}
#endif
static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int af, int protocol)
{
struct hdhomerun_sock_t *sock = (struct hdhomerun_sock_t *)calloc(1, sizeof(struct hdhomerun_sock_t));
if (!sock) {
@ -173,7 +36,7 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
}
/* Create socket. */
sock->sock = socket(AF_INET, protocol, 0);
sock->sock = socket(af, protocol, 0);
if (sock->sock == -1) {
free(sock);
return NULL;
@ -191,13 +54,19 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
setsockopt(sock->sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&set, sizeof(set));
#endif
/* Set ipv6 */
if (af == AF_INET6) {
int sock_opt_ipv6only = 1;
setsockopt(sock->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&sock_opt_ipv6only, sizeof(sock_opt_ipv6only));
}
/* Success. */
return sock;
}
struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void)
struct hdhomerun_sock_t *hdhomerun_sock_create_udp_ex(int af)
{
struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(SOCK_DGRAM);
struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(af, SOCK_DGRAM);
if (!sock) {
return NULL;
}
@ -210,9 +79,9 @@ struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void)
return sock;
}
struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void)
struct hdhomerun_sock_t *hdhomerun_sock_create_tcp_ex(int af)
{
return hdhomerun_sock_create_internal(SOCK_STREAM);
return hdhomerun_sock_create_internal(af, SOCK_STREAM);
}
void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock)
@ -244,73 +113,80 @@ void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock)
setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
}
void hdhomerun_sock_set_ipv4_onesbcast(struct hdhomerun_sock_t *sock, int v)
{
#if defined(IP_ONESBCAST)
setsockopt(sock->sock, IPPROTO_IP, IP_ONESBCAST, (char *)&v, sizeof(v));
#endif
}
void hdhomerun_sock_set_ipv6_multicast_ifindex(struct hdhomerun_sock_t *sock, uint32_t ifindex)
{
setsockopt(sock->sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&ifindex, sizeof(ifindex));
}
int hdhomerun_sock_getlasterror(void)
{
return errno;
}
uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock)
bool hdhomerun_sock_getsockname_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
return (getsockname(sock->sock, (struct sockaddr *)result, &sockaddr_size) == 0);
}
uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock)
{
struct sockaddr_in sock_addr;
struct sockaddr_storage sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohs(sock_addr.sin_port);
if (sock_addr.ss_family == AF_INET) {
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
return ntohs(sock_addr_in->sin_port);
}
if (sock_addr.ss_family == AF_INET6) {
struct sockaddr_in6 *sock_addr_in = (struct sockaddr_in6 *)&sock_addr;
return ntohs(sock_addr_in->sin6_port);
}
uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getpeername(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name)
bool hdhomerun_sock_getpeername_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *sock_info;
if (getaddrinfo(name, NULL, &hints, &sock_info) != 0) {
return 0;
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
return (getpeername(sock->sock, (struct sockaddr *)result, &sockaddr_size) == 0);
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
return addr;
}
bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
bool hdhomerun_sock_join_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (multicast_addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *multicast_addr_in = (const struct sockaddr_in6 *)multicast_addr;
struct ipv6_mreq imr;
memset(&imr, 0, sizeof(imr));
memcpy(imr.ipv6mr_multiaddr.s6_addr, multicast_addr_in->sin6_addr.s6_addr, 16);
imr.ipv6mr_interface = multicast_addr_in->sin6_scope_id;
if (setsockopt(sock->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
}
return true;
}
if (multicast_addr->sa_family == AF_INET) {
const struct sockaddr_in *multicast_addr_in = (const struct sockaddr_in *)multicast_addr;
const struct sockaddr_in *local_addr_in = (const struct sockaddr_in *)local_addr;
struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(multicast_ip);
imr.imr_interface.s_addr = htonl(local_ip);
imr.imr_multiaddr.s_addr = multicast_addr_in->sin_addr.s_addr;
imr.imr_interface.s_addr = (local_addr->sa_family == AF_INET) ? local_addr_in->sin_addr.s_addr : 0;
if (setsockopt(sock->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
@ -319,12 +195,34 @@ bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t
return true;
}
bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
return false;
}
bool hdhomerun_sock_leave_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (multicast_addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *multicast_addr_in = (const struct sockaddr_in6 *)multicast_addr;
struct ipv6_mreq imr;
memset(&imr, 0, sizeof(imr));
memcpy(imr.ipv6mr_multiaddr.s6_addr, multicast_addr_in->sin6_addr.s6_addr, 16);
imr.ipv6mr_interface = multicast_addr_in->sin6_scope_id;
if (setsockopt(sock->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
}
return true;
}
if (multicast_addr->sa_family == AF_INET) {
const struct sockaddr_in *multicast_addr_in = (const struct sockaddr_in *)multicast_addr;
const struct sockaddr_in *local_addr_in = (const struct sockaddr_in *)local_addr;
struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(multicast_ip);
imr.imr_interface.s_addr = htonl(local_ip);
imr.imr_multiaddr.s_addr = multicast_addr_in->sin_addr.s_addr;
imr.imr_interface.s_addr = (local_addr->sa_family == AF_INET) ? local_addr_in->sin_addr.s_addr : 0;
if (setsockopt(sock->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
@ -333,33 +231,48 @@ bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_
return true;
}
bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse)
return false;
}
bool hdhomerun_sock_bind_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *local_addr, bool allow_reuse)
{
socklen_t local_addr_size;
switch (local_addr->sa_family) {
case AF_INET6:
local_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
local_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
int sock_opt = allow_reuse;
setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(local_addr);
sock_addr.sin_port = htons(local_port);
if (bind(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (bind(sock->sock, (const struct sockaddr *)local_addr, local_addr_size) != 0) {
return false;
}
return true;
}
bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
bool hdhomerun_sock_connect_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, uint64_t timeout)
{
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);
socklen_t remote_addr_size;
switch (remote_addr->sa_family) {
case AF_INET6:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
if (connect(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (connect(sock->sock, remote_addr, remote_addr_size) != 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return false;
}
@ -437,16 +350,22 @@ bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t
}
}
bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
bool hdhomerun_sock_sendto_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, const void *data, size_t length, uint64_t timeout)
{
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);
socklen_t remote_addr_size;
switch (remote_addr->sa_family) {
case AF_INET6:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
const uint8_t *ptr = (const uint8_t *)data;
ssize_t ret = sendto(sock->sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
ssize_t ret = sendto(sock->sock, ptr, length, 0, remote_addr, remote_addr_size);
if (ret >= (ssize_t)length) {
return true;
}
@ -476,7 +395,7 @@ bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr,
return false;
}
ret = sendto(sock->sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
ret = sendto(sock->sock, ptr, length, 0, remote_addr, remote_addr_size);
if (ret >= (ssize_t)length) {
return true;
}
@ -536,16 +455,11 @@ bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *leng
return false;
}
bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
bool hdhomerun_sock_recvfrom_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *remote_addr, void *data, size_t *length, uint64_t timeout)
{
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
socklen_t sockaddr_size = sizeof(sock_addr);
ssize_t ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
ssize_t ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)remote_addr, &sockaddr_size);
if (ret > 0) {
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = (size_t)ret;
return true;
}
@ -570,10 +484,8 @@ bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_add
return false;
}
ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
ret = recvfrom(sock->sock, data, *length, 0, (struct sockaddr *)remote_addr, &sockaddr_size);
if (ret > 0) {
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = (size_t)ret;
return true;
}

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_sock_windows.c
*
* Copyright © 2010-2016 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2010-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,123 +27,78 @@ struct hdhomerun_sock_t {
long events_selected;
};
#if defined(_WINRT)
static char *hdhomerun_local_ip_info_str = NULL;
/*
* String format: ip address '/' subnet mask bits <space> ...
* Example: "192.168.0.100/24 169.254.0.100/16"
*/
void hdhomerun_local_ip_info_set_str(const char *ip_info_str)
bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg)
{
if (hdhomerun_local_ip_info_str) {
free(hdhomerun_local_ip_info_str);
}
hdhomerun_local_ip_info_str = strdup(ip_info_str);
}
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
const char *ptr = hdhomerun_local_ip_info_str;
if (!ptr) {
return 0;
}
struct hdhomerun_local_ip_info_t *ip_info = ip_info_list;
int count = 0;
while (count < max_count) {
unsigned int a[4];
unsigned int mask_bitcount;
if (sscanf(ptr, "%u.%u.%u.%u/%u", &a[0], &a[1], &a[2], &a[3], &mask_bitcount) != 5) {
break;
}
ip_info->ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
ip_info->subnet_mask = 0xFFFFFFFF << (32 - mask_bitcount);
ip_info++;
count++;
ptr = strchr(ptr, ' ');
if (!ptr) {
break;
}
ptr++;
}
return count;
}
#endif
#if !defined(_WINRT)
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
PIP_ADAPTER_INFO AdapterInfo;
ULONG AdapterInfoLength = sizeof(IP_ADAPTER_INFO) * 16;
IP_ADAPTER_ADDRESSES *adapter_addresses;
ULONG adapter_addresses_length = sizeof(IP_ADAPTER_ADDRESSES) * 16;
while (1) {
AdapterInfo = (IP_ADAPTER_INFO *)malloc(AdapterInfoLength);
if (!AdapterInfo) {
return -1;
adapter_addresses = (IP_ADAPTER_ADDRESSES *)malloc(adapter_addresses_length);
if (!adapter_addresses) {
return false;
}
ULONG LengthNeeded = AdapterInfoLength;
DWORD Ret = GetAdaptersInfo(AdapterInfo, &LengthNeeded);
if (Ret == NO_ERROR) {
ULONG length_needed = adapter_addresses_length;
DWORD ret = GetAdaptersAddresses(af, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, adapter_addresses, &length_needed);
if (ret == NO_ERROR) {
break;
}
free(AdapterInfo);
free(adapter_addresses);
if (Ret != ERROR_BUFFER_OVERFLOW) {
return -1;
if (ret != ERROR_BUFFER_OVERFLOW) {
return false;
}
if (AdapterInfoLength >= LengthNeeded) {
return -1;
if (adapter_addresses_length >= length_needed) {
return false;
}
AdapterInfoLength = LengthNeeded;
adapter_addresses_length = length_needed;
}
int count = 0;
PIP_ADAPTER_INFO Adapter = AdapterInfo;
while (Adapter) {
IP_ADDR_STRING *IPAddr = &Adapter->IpAddressList;
while (IPAddr) {
uint32_t ip_addr = ntohl(inet_addr(IPAddr->IpAddress.String));
uint32_t subnet_mask = ntohl(inet_addr(IPAddr->IpMask.String));
IP_ADAPTER_ADDRESSES *adapter = adapter_addresses;
if (ip_addr == 0) {
IPAddr = IPAddr->Next;
while (adapter) {
if ((adapter->IfType != MIB_IF_TYPE_ETHERNET) && (adapter->IfType != IF_TYPE_IEEE80211)) {
adapter = adapter->Next;
continue;
}
if (count < max_count) {
struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count];
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
if (adapter->PhysicalAddressLength != 6) {
adapter = adapter->Next;
continue;
}
count++;
IPAddr = IPAddr->Next;
uint32_t ifindex = adapter->IfIndex;
IP_ADAPTER_UNICAST_ADDRESS *adapter_address = adapter->FirstUnicastAddress;
while (adapter_address) {
if (adapter_address->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) {
adapter_address = adapter_address->Next;
continue;
}
if (count >= max_count) {
break;
struct sockaddr *local_ip = adapter_address->Address.lpSockaddr;
if ((local_ip->sa_family == AF_INET6) && (adapter_address->ValidLifetime != 0xFFFFFFFF)) {
adapter_address = adapter_address->Next;
continue; /* skip temporary addresses */
}
Adapter = Adapter->Next;
uint8_t cidr = adapter_address->OnLinkPrefixLength;
callback(callback_arg, ifindex, local_ip, cidr);
adapter_address = adapter_address->Next;
}
free(AdapterInfo);
return count;
adapter = adapter->Next;
}
#endif
static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
free(adapter_addresses);
return true;
}
static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int af, int protocol)
{
struct hdhomerun_sock_t *sock = (struct hdhomerun_sock_t *)calloc(1, sizeof(struct hdhomerun_sock_t));
if (!sock) {
@ -151,7 +106,7 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
}
/* Create socket. */
sock->sock = socket(AF_INET, protocol, 0);
sock->sock = socket(af, protocol, 0);
if (sock->sock == INVALID_SOCKET) {
free(sock);
return NULL;
@ -164,6 +119,12 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
return NULL;
}
/* Set ipv6 */
if (af == AF_INET6) {
int sock_opt_ipv6only = 1;
setsockopt(sock->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&sock_opt_ipv6only, sizeof(sock_opt_ipv6only));
}
/* Event */
sock->event = CreateEvent(NULL, false, false, NULL);
if (!sock->event) {
@ -175,9 +136,9 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int protocol)
return sock;
}
struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void)
struct hdhomerun_sock_t *hdhomerun_sock_create_udp_ex(int af)
{
struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(SOCK_DGRAM);
struct hdhomerun_sock_t *sock = hdhomerun_sock_create_internal(af, SOCK_DGRAM);
if (!sock) {
return NULL;
}
@ -190,9 +151,9 @@ struct hdhomerun_sock_t *hdhomerun_sock_create_udp(void)
return sock;
}
struct hdhomerun_sock_t *hdhomerun_sock_create_tcp(void)
struct hdhomerun_sock_t *hdhomerun_sock_create_tcp_ex(int af)
{
return hdhomerun_sock_create_internal(SOCK_STREAM);
return hdhomerun_sock_create_internal(af, SOCK_STREAM);
}
void hdhomerun_sock_destroy(struct hdhomerun_sock_t *sock)
@ -228,73 +189,77 @@ void hdhomerun_sock_set_allow_reuse(struct hdhomerun_sock_t *sock)
setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
}
void hdhomerun_sock_set_ipv4_onesbcast(struct hdhomerun_sock_t *sock, int v)
{
}
void hdhomerun_sock_set_ipv6_multicast_ifindex(struct hdhomerun_sock_t *sock, uint32_t ifindex)
{
setsockopt(sock->sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&ifindex, sizeof(ifindex));
}
int hdhomerun_sock_getlasterror(void)
{
return WSAGetLastError();
}
uint32_t hdhomerun_sock_getsockname_addr(struct hdhomerun_sock_t *sock)
bool hdhomerun_sock_getsockname_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
return (getsockname(sock->sock, (struct sockaddr *)result, &sockaddr_size) == 0);
}
uint16_t hdhomerun_sock_getsockname_port(struct hdhomerun_sock_t *sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
struct sockaddr_storage sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohs(sock_addr.sin_port);
if (sock_addr.ss_family == AF_INET) {
struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&sock_addr;
return ntohs(sock_addr_in->sin_port);
}
if (sock_addr.ss_family == AF_INET6) {
struct sockaddr_in6 *sock_addr_in = (struct sockaddr_in6 *)&sock_addr;
return ntohs(sock_addr_in->sin6_port);
}
uint32_t hdhomerun_sock_getpeername_addr(struct hdhomerun_sock_t *sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
if (getpeername(sock->sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getaddrinfo_addr(struct hdhomerun_sock_t *sock, const char *name)
bool hdhomerun_sock_getpeername_addr_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *result)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *sock_info;
if (getaddrinfo(name, NULL, &hints, &sock_info) != 0) {
return 0;
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
return (getpeername(sock->sock, (struct sockaddr *)result, &sockaddr_size) == 0);
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
return addr;
}
bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
bool hdhomerun_sock_join_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (multicast_addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *multicast_addr_in = (const struct sockaddr_in6 *)multicast_addr;
struct ipv6_mreq imr;
memset(&imr, 0, sizeof(imr));
memcpy(imr.ipv6mr_multiaddr.s6_addr, multicast_addr_in->sin6_addr.s6_addr, 16);
imr.ipv6mr_interface = multicast_addr_in->sin6_scope_id;
if (setsockopt(sock->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
}
return true;
}
if (multicast_addr->sa_family == AF_INET) {
const struct sockaddr_in *multicast_addr_in = (const struct sockaddr_in *)multicast_addr;
const struct sockaddr_in *local_addr_in = (const struct sockaddr_in *)local_addr;
struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(multicast_ip);
imr.imr_interface.s_addr = htonl(local_ip);
imr.imr_multiaddr.s_addr = multicast_addr_in->sin_addr.s_addr;
imr.imr_interface.s_addr = (local_addr->sa_family == AF_INET) ? local_addr_in->sin_addr.s_addr : 0;
if (setsockopt(sock->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
@ -303,12 +268,34 @@ bool hdhomerun_sock_join_multicast_group(struct hdhomerun_sock_t *sock, uint32_t
return true;
}
bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_t multicast_ip, uint32_t local_ip)
return false;
}
bool hdhomerun_sock_leave_multicast_group_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (multicast_addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *multicast_addr_in = (const struct sockaddr_in6 *)multicast_addr;
struct ipv6_mreq imr;
memset(&imr, 0, sizeof(imr));
memcpy(imr.ipv6mr_multiaddr.s6_addr, multicast_addr_in->sin6_addr.s6_addr, 16);
imr.ipv6mr_interface = multicast_addr_in->sin6_scope_id;
if (setsockopt(sock->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
}
return true;
}
if (multicast_addr->sa_family == AF_INET) {
const struct sockaddr_in *multicast_addr_in = (const struct sockaddr_in *)multicast_addr;
const struct sockaddr_in *local_addr_in = (const struct sockaddr_in *)local_addr;
struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(multicast_ip);
imr.imr_interface.s_addr = htonl(local_ip);
imr.imr_multiaddr.s_addr = multicast_addr_in->sin_addr.s_addr;
imr.imr_interface.s_addr = (local_addr->sa_family == AF_INET) ? local_addr_in->sin_addr.s_addr : 0;
if (setsockopt(sock->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return false;
@ -317,18 +304,27 @@ bool hdhomerun_sock_leave_multicast_group(struct hdhomerun_sock_t *sock, uint32_
return true;
}
bool hdhomerun_sock_bind(struct hdhomerun_sock_t *sock, uint32_t local_addr, uint16_t local_port, bool allow_reuse)
return false;
}
bool hdhomerun_sock_bind_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *local_addr, bool allow_reuse)
{
socklen_t local_addr_size;
switch (local_addr->sa_family) {
case AF_INET6:
local_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
local_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
int sock_opt = allow_reuse;
setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(local_addr);
sock_addr.sin_port = htons(local_port);
if (bind(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (bind(sock->sock, (const struct sockaddr *)local_addr, local_addr_size) != 0) {
return false;
}
@ -348,19 +344,25 @@ static bool hdhomerun_sock_event_select(struct hdhomerun_sock_t *sock, long even
return true;
}
bool hdhomerun_sock_connect(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
bool hdhomerun_sock_connect_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, uint64_t timeout)
{
socklen_t remote_addr_size;
switch (remote_addr->sa_family) {
case AF_INET6:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
if (!hdhomerun_sock_event_select(sock, FD_WRITE | FD_CLOSE)) {
return false;
}
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);
if (connect(sock->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (connect(sock->sock, remote_addr, remote_addr_size) != 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return false;
}
@ -420,19 +422,25 @@ bool hdhomerun_sock_send(struct hdhomerun_sock_t *sock, const void *data, size_t
}
}
bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
bool hdhomerun_sock_sendto_ex(struct hdhomerun_sock_t *sock, const struct sockaddr *remote_addr, const void *data, size_t length, uint64_t timeout)
{
socklen_t remote_addr_size;
switch (remote_addr->sa_family) {
case AF_INET6:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in6);
break;
case AF_INET:
remote_addr_size = (socklen_t)sizeof(struct sockaddr_in);
break;
default:
return false;
}
if (!hdhomerun_sock_event_select(sock, FD_WRITE | FD_CLOSE)) {
return false;
}
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);
int ret = sendto(sock->sock, (char *)data, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
int ret = sendto(sock->sock, (char *)data, (int)length, 0, remote_addr, remote_addr_size);
if (ret >= (int)length) {
return true;
}
@ -448,7 +456,7 @@ bool hdhomerun_sock_sendto(struct hdhomerun_sock_t *sock, uint32_t remote_addr,
return false;
}
ret = sendto(sock->sock, (char *)data, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
ret = sendto(sock->sock, (char *)data, (int)length, 0, remote_addr, remote_addr_size);
if (ret >= (int)length) {
return true;
}
@ -488,20 +496,15 @@ bool hdhomerun_sock_recv(struct hdhomerun_sock_t *sock, void *data, size_t *leng
return false;
}
bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
bool hdhomerun_sock_recvfrom_ex(struct hdhomerun_sock_t *sock, struct sockaddr_storage *remote_addr, void *data, size_t *length, uint64_t timeout)
{
if (!hdhomerun_sock_event_select(sock, FD_READ | FD_CLOSE)) {
return false;
}
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
int sockaddr_size = sizeof(sock_addr);
int ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
socklen_t sockaddr_size = sizeof(struct sockaddr_storage);
int ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)remote_addr, &sockaddr_size);
if (ret > 0) {
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = ret;
return true;
}
@ -517,10 +520,8 @@ bool hdhomerun_sock_recvfrom(struct hdhomerun_sock_t *sock, uint32_t *remote_add
return false;
}
ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
ret = recvfrom(sock->sock, (char *)data, (int)(*length), 0, (struct sockaddr *)remote_addr, &sockaddr_size);
if (ret > 0) {
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = ret;
return true;
}

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_video.c
*
* Copyright © 2006-2016 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -26,8 +26,7 @@ struct hdhomerun_video_sock_t {
struct hdhomerun_sock_t *sock;
uint32_t keepalive_lockkey;
uint32_t keepalive_addr;
uint16_t keepalive_port;
struct sockaddr_storage keepalive_addr;
volatile bool keepalive_start;
volatile size_t head;
@ -52,6 +51,16 @@ struct hdhomerun_video_sock_t {
static void hdhomerun_video_thread_execute(void *arg);
struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg)
{
struct sockaddr_in listen_addr_in;
memset(&listen_addr_in, 0, sizeof(listen_addr_in));
listen_addr_in.sin_family = AF_INET;
listen_addr_in.sin_port = htons(listen_port);
return hdhomerun_video_create_ex((const struct sockaddr *)&listen_addr_in, allow_port_reuse, buffer_size, dbg);
}
struct hdhomerun_video_sock_t *hdhomerun_video_create_ex(const struct sockaddr *listen_addr, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg)
{
/* Create object. */
struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)calloc(1, sizeof(struct hdhomerun_video_sock_t));
@ -82,7 +91,7 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool
}
/* Create socket. */
vs->sock = hdhomerun_sock_create_udp();
vs->sock = hdhomerun_sock_create_udp_ex(listen_addr->sa_family);
if (!vs->sock) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n");
goto error;
@ -92,8 +101,8 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool
hdhomerun_sock_set_recv_buffer_size(vs->sock, 1024 * 1024);
/* Bind socket. */
if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port, allow_port_reuse)) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port);
if (!hdhomerun_sock_bind_ex(vs->sock, listen_addr, allow_port_reuse)) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket\n");
goto error;
}
@ -134,14 +143,36 @@ void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
}
void hdhomerun_video_set_keepalive(struct hdhomerun_video_sock_t *vs, uint32_t remote_addr, uint16_t remote_port, uint32_t lockkey)
{
if ((remote_addr == 0) || (remote_port == 0)) {
hdhomerun_video_set_keepalive_ex(vs, NULL, lockkey);
return;
}
struct sockaddr_in remote_addr_in;
memset(&remote_addr_in, 0, sizeof(remote_addr_in));
remote_addr_in.sin_family = AF_INET;
remote_addr_in.sin_addr.s_addr = htonl(remote_addr);
remote_addr_in.sin_port = htons(remote_port);
hdhomerun_video_set_keepalive_ex(vs, (struct sockaddr *)&remote_addr_in, lockkey);
}
void hdhomerun_video_set_keepalive_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *remote_addr, uint32_t lockkey)
{
thread_mutex_lock(&vs->lock);
vs->keepalive_addr = remote_addr;
vs->keepalive_port = remote_port;
memset(&vs->keepalive_addr, 0, sizeof(vs->keepalive_addr));
if (remote_addr && (remote_addr->sa_family == AF_INET6)) {
memcpy(&vs->keepalive_addr, remote_addr, sizeof(struct sockaddr_in6));
}
if (remote_addr && (remote_addr->sa_family == AF_INET)) {
memcpy(&vs->keepalive_addr, remote_addr, sizeof(struct sockaddr_in));
}
vs->keepalive_lockkey = lockkey;
if ((remote_addr != 0) && (remote_port != 0)) {
if (vs->keepalive_addr.ss_family) {
vs->keepalive_start = true;
}
@ -166,7 +197,22 @@ uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
{
if (!hdhomerun_sock_join_multicast_group(vs->sock, multicast_ip, local_ip)) {
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(local_ip);
return hdhomerun_video_join_multicast_group_ex(vs, (struct sockaddr *)&multicast_addr, (struct sockaddr *)&local_addr);
}
int hdhomerun_video_join_multicast_group_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (!hdhomerun_sock_join_multicast_group_ex(vs->sock, multicast_addr, local_addr)) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
return -1;
}
@ -176,7 +222,22 @@ int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint
void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
{
if (!hdhomerun_sock_leave_multicast_group(vs->sock, multicast_ip, local_ip)) {
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(multicast_ip);
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(local_ip);
hdhomerun_video_leave_multicast_group_ex(vs, (struct sockaddr *)&multicast_addr, (struct sockaddr *)&local_addr);
}
void hdhomerun_video_leave_multicast_group_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr)
{
if (!hdhomerun_sock_leave_multicast_group_ex(vs->sock, multicast_addr, local_addr)) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
}
}
@ -251,19 +312,19 @@ static void hdhomerun_video_thread_send_keepalive(struct hdhomerun_video_sock_t
{
thread_mutex_lock(&vs->lock);
uint32_t keepalive_lockkey = vs->keepalive_lockkey;
uint32_t keepalive_addr = vs->keepalive_addr;
uint16_t keepalive_port = vs->keepalive_port;
struct sockaddr_storage keepalive_addr;
keepalive_addr = vs->keepalive_addr;
vs->keepalive_start = false;
thread_mutex_unlock(&vs->lock);
if ((keepalive_addr == 0) || (keepalive_port == 0)) {
if (keepalive_addr.ss_family == 0) {
return;
}
struct hdhomerun_pkt_t pkt;
hdhomerun_pkt_reset(&pkt);
hdhomerun_pkt_write_u32(&pkt, keepalive_lockkey);
hdhomerun_sock_sendto(vs->sock, keepalive_addr, keepalive_port, pkt.start, pkt.end - pkt.start, 25);
hdhomerun_sock_sendto_ex(vs->sock, (struct sockaddr *)&keepalive_addr, pkt.start, pkt.end - pkt.start, 25);
}
static void hdhomerun_video_thread_execute(void *arg)

View file

@ -1,7 +1,7 @@
/*
* hdhomerun_video.h
*
* Copyright © 2006-2016 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2022 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -49,12 +49,14 @@ struct hdhomerun_video_stats_t {
* When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
*/
extern LIBHDHOMERUN_API struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API struct hdhomerun_video_sock_t *hdhomerun_video_create_ex(const struct sockaddr *listen_addr, bool allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg);
extern LIBHDHOMERUN_API void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
/*
* Configure to send a keepalive packet every second.
*/
extern LIBHDHOMERUN_API void hdhomerun_video_set_keepalive(struct hdhomerun_video_sock_t *vs, uint32_t remote_addr, uint16_t remote_port, uint32_t lockkey);
extern LIBHDHOMERUN_API void hdhomerun_video_set_keepalive_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *remote_addr, uint32_t lockkey);
/*
* Get the port the socket is listening on.
@ -67,7 +69,9 @@ extern LIBHDHOMERUN_API uint16_t hdhomerun_video_get_local_port(struct hdhomerun
* Join/leave multicast group.
*/
extern LIBHDHOMERUN_API int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip);
extern LIBHDHOMERUN_API int hdhomerun_video_join_multicast_group_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr);
extern LIBHDHOMERUN_API void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip);
extern LIBHDHOMERUN_API void hdhomerun_video_leave_multicast_group_ex(struct hdhomerun_video_sock_t *vs, const struct sockaddr *multicast_addr, const struct sockaddr *local_addr);
/*
* Read data from buffer.