diff --git a/hdhomerun_control.c b/hdhomerun_control.c index d09d607..2c65656 100644 --- a/hdhomerun_control.c +++ b/hdhomerun_control.c @@ -98,7 +98,7 @@ void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs) static bool hdhomerun_control_connect_sock_discover(struct hdhomerun_control_sock_t *cs) { - struct hdhomerun_discover_t *ds = hdhomerun_discover_create(NULL); + struct hdhomerun_discover_t *ds = hdhomerun_discover_create(cs->dbg); if (!ds) { return false; } diff --git a/hdhomerun_device_selector.c b/hdhomerun_device_selector.c index 2a862ac..2749dca 100644 --- a/hdhomerun_device_selector.c +++ b/hdhomerun_device_selector.c @@ -23,6 +23,7 @@ struct hdhomerun_device_selector_t { struct hdhomerun_device_t **hd_list; size_t hd_count; + struct hdhomerun_discover_t *ds; struct hdhomerun_debug_t *dbg; }; @@ -53,6 +54,10 @@ void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, free(hds->hd_list); } + if (hds->ds) { + hdhomerun_discover_destroy(hds->ds); + } + free(hds); } @@ -123,18 +128,55 @@ struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomeru return NULL; } -static int hdhomerun_device_selector_load_from_str_discover(struct hdhomerun_device_selector_t *hds, uint32_t target_ip, uint32_t device_id) +static int hdhomerun_device_selector_load_from_str_discover(struct hdhomerun_device_selector_t *hds, uint32_t device_id, const struct sockaddr *device_addr) { - struct hdhomerun_discover_device_t result; - int discover_count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, device_id, &result, 1); - if (discover_count != 1) { + if (!hds->ds) { + hds->ds = hdhomerun_discover_create(hds->dbg); + if (!hds->ds) { + return 0; + } + } + + uint32_t device_types[1]; + device_types[0] = HDHOMERUN_DEVICE_TYPE_TUNER; + + if (device_id == 0) { + device_id = HDHOMERUN_DEVICE_ID_WILDCARD; + } + + int ret; + if (hdhomerun_sock_sockaddr_is_addr(device_addr)) { + if (device_id == HDHOMERUN_DEVICE_ID_WILDCARD) { + ret = hdhomerun_discover2_find_devices_targeted(hds->ds, device_addr, device_types, 1); + } else { + ret = hdhomerun_discover2_find_device_id_targeted(hds->ds, 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(hds->ds, flags, device_types, 1); + } else { + ret = hdhomerun_discover2_find_device_id_broadcast(hds->ds, flags, device_id); + } + } + if (ret <= 0) { + hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_str_discover: device not found\n"); return 0; } + struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(hds->ds); + struct hdhomerun_discover2_device_if_t *device_if = hdhomerun_discover2_iter_device_if_first(device); + + device_id = hdhomerun_discover2_device_get_device_id(device); + uint8_t tuner_count = hdhomerun_discover2_device_get_tuner_count(device); + + struct sockaddr_storage actual_ip_addr; + hdhomerun_discover2_device_if_get_ip_addr(device_if, &actual_ip_addr); + int count = 0; unsigned int tuner_index; - for (tuner_index = 0; tuner_index < result.tuner_count; tuner_index++) { - struct hdhomerun_device_t *hd = hdhomerun_device_create(result.device_id, result.ip_addr, tuner_index, hds->dbg); + for (tuner_index = 0; tuner_index < tuner_count; tuner_index++) { + struct hdhomerun_device_t *hd = hdhomerun_device_create_ex(device_id, (struct sockaddr *)&actual_ip_addr, tuner_index, hds->dbg); if (!hd) { continue; } @@ -146,98 +188,206 @@ static int hdhomerun_device_selector_load_from_str_discover(struct hdhomerun_dev return count; } +static bool hdhomerun_device_selector_load_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_selector_load_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 int hdhomerun_device_selector_load_from_str_tail(struct hdhomerun_device_selector_t *hds, const char *tail, uint32_t device_id, struct sockaddr_storage *device_addr) +{ + const char *ptr = tail; + if (*ptr == 0) { + return hdhomerun_device_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)device_addr); + } + + if (*ptr == ':') { + ptr++; + + char *end; + unsigned long port = strtoul(ptr + 1, &end, 10); + if (*end != 0) { + return 0; + } + if ((port < 1024) || (port > 65535)) { + return 0; + } + + 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); + + struct hdhomerun_device_t *hd = hdhomerun_device_create_multicast_ex((struct sockaddr *)device_addr, hds->dbg); + if (!hd) { + return 0; + } + + hdhomerun_device_selector_add_device(hds, hd); + return 1; + } + + 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); + + struct hdhomerun_device_t *hd = hdhomerun_device_create_multicast_ex((struct sockaddr *)device_addr, hds->dbg); + if (!hd) { + return 0; + } + + hdhomerun_device_selector_add_device(hds, hd); + return 1; + } + + return 0; + } + + if (*ptr == '-') { + ptr++; + + char *end; + unsigned int tuner_index = (unsigned int)strtoul(ptr, &end, 10); + if (*end != 0) { + return 0; + } + + struct hdhomerun_device_t *hd = hdhomerun_device_create_ex(device_id, (struct sockaddr *)device_addr, tuner_index, hds->dbg); + if (!hd) { + return 0; + } + + hdhomerun_device_selector_add_device(hds, hd); + return 1; + } + + return 0; +} + int hdhomerun_device_selector_load_from_str(struct hdhomerun_device_selector_t *hds, char *device_str) { - /* - * 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) { - struct hdhomerun_device_t *hd = hdhomerun_device_create_multicast(ip_addr, (uint16_t)port, hds->dbg); - if (!hd) { - return 0; - } - - hdhomerun_device_selector_add_device(hds, hd); - return 1; - } - - /* - * 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) { - struct hdhomerun_device_t *hd = hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, tuner, hds->dbg); - if (!hd) { - return 0; - } - - hdhomerun_device_selector_add_device(hds, hd); - return 1; - } - - /* - * IP address only - discover and add tuners. - */ - return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD); - } - - /* - * 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); - struct hdhomerun_device_t *hd = hdhomerun_device_create(device_id, 0, tuner, hds->dbg); - if (!hd) { - return 0; - } - - hdhomerun_device_selector_add_device(hds, hd); - return 1; - } - - /* - * Device ID only - discover and add tuners. - */ - return hdhomerun_device_selector_load_from_str_discover(hds, 0, device_id); - } - - /* - * 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 0; } - 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; + + char *ptr = str; + bool framed = (*ptr == '['); + if (framed) { + ptr++; + + char *end = strchr(ptr, ']'); + if (!end) { + return 0; + } + + *end++ = 0; + + if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) { + return hdhomerun_device_selector_load_from_str_tail(hds, end, device_id, &device_addr); + } - if (ip_addr == 0) { return 0; } - return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD); + char *dash = strchr(ptr, '-'); + if (dash) { + *dash = 0; + + if (hdhomerun_device_selector_load_from_str_parse_device_id(ptr, &device_id)) { + *dash = '-'; + return hdhomerun_device_selector_load_from_str_tail(hds, dash, device_id, &device_addr); + } + if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) { + *dash = '-'; + return hdhomerun_device_selector_load_from_str_tail(hds, dash, device_id, &device_addr); + } + + *dash = '-'; + if (hdhomerun_device_selector_load_from_str_parse_dns(ptr, &device_addr)) { + return hdhomerun_device_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)&device_addr); + } + + return 0; + } + + 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_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)&device_addr); + } + + return 0; + } + + *colon = 0; + + if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) { + *colon = ':'; + return hdhomerun_device_selector_load_from_str_tail(hds, colon, device_id, &device_addr); + } + + return 0; + } + + if (hdhomerun_device_selector_load_from_str_parse_device_id(ptr, &device_id)) { + return hdhomerun_device_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)&device_addr); + } + if (hdhomerun_sock_ip_str_to_sockaddr(ptr, &device_addr)) { + return hdhomerun_device_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)&device_addr); + } + if (hdhomerun_device_selector_load_from_str_parse_dns(ptr, &device_addr)) { + return hdhomerun_device_selector_load_from_str_discover(hds, device_id, (struct sockaddr *)&device_addr); + } + + return 0; } int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename) diff --git a/hdhomerun_discover.c b/hdhomerun_discover.c index 0ce17d4..e931fd7 100644 --- a/hdhomerun_discover.c +++ b/hdhomerun_discover.c @@ -130,7 +130,13 @@ static void hdhomerun_discover_sock_add_ipv6(void *arg, uint32_t ifindex, const detected_ip->sin6_port = htons(hdhomerun_discover_get_local_port(ds)); if (!hdhomerun_sock_bind_ex(dss->sock, (const struct sockaddr *)detected_ip, true)) { - hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to local ip (%d)\n", hdhomerun_sock_getlasterror()); + if (ds->dbg) { + int last_error = hdhomerun_sock_getlasterror(); + char detected_ip_str[64]; + hdhomerun_sock_sockaddr_to_ip_str(detected_ip_str, (const struct sockaddr *)detected_ip, true); + hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to local ip %s (%d)\n", detected_ip_str, last_error); + } + hdhomerun_discover_sock_free(dss); return; } @@ -183,7 +189,13 @@ static void hdhomerun_discover_sock_add_ipv4(void *arg, uint32_t ifindex, const detected_ip->sin_port = htons(hdhomerun_discover_get_local_port(ds)); if (!hdhomerun_sock_bind_ex(dss->sock, (const struct sockaddr *)detected_ip, true)) { - hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to local ip (%d)\n", hdhomerun_sock_getlasterror()); + if (ds->dbg) { + int last_error = hdhomerun_sock_getlasterror(); + char detected_ip_str[64]; + hdhomerun_sock_sockaddr_to_ip_str(detected_ip_str, (const struct sockaddr *)detected_ip, true); + hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to local ip %s (%d)\n", detected_ip_str, last_error); + } + hdhomerun_discover_sock_free(dss); return; } @@ -484,7 +496,7 @@ static inline bool hdhomerun_discover_is_ipv4_autoip(const struct sockaddr *ip_a const struct sockaddr_in *sock_addr_in = (const struct sockaddr_in *)ip_addr; uint8_t *addr_bytes = (uint8_t *)&sock_addr_in->sin_addr.s_addr; - return (addr_bytes[0] == 0xA9) && (addr_bytes[1] == 0xFE); + return (addr_bytes[0] == 169) && (addr_bytes[1] == 254); } static inline bool hdhomerun_discover_is_ipv6_localhost(const struct sockaddr *ip_addr) @@ -496,8 +508,8 @@ static inline bool hdhomerun_discover_is_ipv6_localhost(const struct sockaddr *i const struct sockaddr_in6 *sock_addr_in6 = (const struct sockaddr_in6 *)ip_addr; uint8_t *addr_bytes = (uint8_t *)sock_addr_in6->sin6_addr.s6_addr; - static uint8_t hdhomerun_discover_ipv6_localhost[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - return (memcmp(addr_bytes, hdhomerun_discover_ipv6_localhost, 16) == 0); + static uint8_t hdhomerun_discover_ipv6_localhost_bytes[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + return (memcmp(addr_bytes, hdhomerun_discover_ipv6_localhost_bytes, 16) == 0); } static inline bool hdhomerun_discover_is_ipv6_routable(const struct sockaddr *ip_addr) @@ -576,23 +588,38 @@ static void hdhomerun_discover_send_ipv4_targeted(struct hdhomerun_discover_t *d memcpy(&target_addr_in, target_addr, sizeof(target_addr_in)); target_addr_in.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT); - /* ipv4 autoip - send out every interface that has an autoip address */ - if (hdhomerun_discover_is_ipv4_autoip(target_addr)) { - struct hdhomerun_discover_sock_t *dss = default_dss->next; - while (dss) { - if (!hdhomerun_discover_is_ipv4_autoip((const struct sockaddr *)&dss->local_ip)) { - dss = dss->next; - continue; - } - - if (!hdhomerun_discover_send_request(ds, dss, (const struct sockaddr *)&target_addr_in, device_types, device_types_count, device_id)) { - dss = dss->next; - continue; - } + uint32_t target_ipv4 = ntohl(target_addr_in.sin_addr.s_addr); + bool local_subnet_send = false; + struct hdhomerun_discover_sock_t *dss = default_dss->next; + while (dss) { + if (dss->ipv4_subnet_mask == 0) { dss = dss->next; + continue; } + struct sockaddr_in *local_ip_in = (struct sockaddr_in *)&dss->local_ip; + uint32_t local_ipv4 = ntohl(local_ip_in->sin_addr.s_addr); + + if ((target_ipv4 & dss->ipv4_subnet_mask) != (local_ipv4 & dss->ipv4_subnet_mask)) { + dss = dss->next; + continue; + } + + if (!hdhomerun_discover_send_request(ds, dss, (const struct sockaddr *)&target_addr_in, device_types, device_types_count, device_id)) { + dss = dss->next; + continue; + } + + local_subnet_send = true; + dss = dss->next; + } + + if (local_subnet_send) { + return; + } + + if (hdhomerun_discover_is_ipv4_autoip(target_addr)) { return; } @@ -839,6 +866,60 @@ static void hdhomerun_discover_recv_internal_auth_bin(char **poutput, struct hdh *poutput = str; } +static void hdhomerun_discover_recv_fixup_tuner_count(struct hdhomerun_discover_t *ds, struct hdhomerun_discover2_device_t *device) +{ + if (!hdhomerun_discover2_device_is_type(device, HDHOMERUN_DEVICE_TYPE_TUNER)) { + return; + } + + if (device->tuner_count > 0) { + return; + } + + switch (device->device_id >> 20) { + case 0x102: + device->tuner_count = 1; + break; + + case 0x100: + case 0x101: + case 0x121: + device->tuner_count = 2; + break; + + default: + break; + } +} + +static void hdhomerun_discover_recv_fixup_base_url(struct hdhomerun_discover_t *ds, struct hdhomerun_discover2_device_t *device) +{ + if (!hdhomerun_discover2_device_is_type(device, HDHOMERUN_DEVICE_TYPE_TUNER)) { + return; + } + + struct hdhomerun_discover2_device_if_t *device_if = device->if_list; + if (device_if->base_url) { + return; + } + + if (device_if->ip_addr.ss_family != AF_INET) { + return; + } + + device_if->base_url = (char *)malloc(32); + if (!device_if->base_url) { + return; + } + + struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&device_if->ip_addr; + uint32_t ip = ntohl(sock_addr_in->sin_addr.s_addr); + + hdhomerun_sprintf(device_if->base_url, device_if->base_url + 32, "http://%u.%u.%u.%u:80", + (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF + ); +} + static bool hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_pkt_t *rx_pkt, struct hdhomerun_discover2_device_t *device) { uint16_t type; @@ -926,41 +1007,12 @@ static bool hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, st /* * Fixup for old firmware. */ - if (hdhomerun_discover2_device_is_type(device, HDHOMERUN_DEVICE_TYPE_TUNER)) { - if (device->tuner_count == 0) { - switch (device->device_id >> 20) { - case 0x102: - device->tuner_count = 1; - break; - - case 0x100: - case 0x101: - case 0x121: - device->tuner_count = 2; - break; - - default: - break; - } - } - - if (!device_if->base_url && (device_if->ip_addr.ss_family == AF_INET)) { - char *str = (char *)malloc(32); - if (!str) { - return false; - } - - struct sockaddr_in *sock_addr_in = (struct sockaddr_in *)&device_if->ip_addr; - uint32_t ip = ntohl(sock_addr_in->sin_addr.s_addr); - - hdhomerun_sprintf(str, str + 32, "http://%u.%u.%u.%u:80", - (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF - ); - - device_if->base_url = str; - } - } + hdhomerun_discover_recv_fixup_tuner_count(ds, device); + hdhomerun_discover_recv_fixup_base_url(ds, device); + /* + * Success + */ device_if->priority = hdhomerun_discover_compute_device_if_priority(device_if); return true; } @@ -1450,6 +1502,16 @@ bool hdhomerun_discover2_device_if_addr_is_ipv6_linklocal(struct hdhomerun_disco return hdhomerun_discover_is_ipv6_linklocal((const struct sockaddr *)&device_if->ip_addr); } +uint32_t hdhomerun_discover2_device_if_get_ipv6_linklocal_scope_id(struct hdhomerun_discover2_device_if_t *device_if) +{ + if (!hdhomerun_discover_is_ipv6_linklocal((const struct sockaddr *)&device_if->ip_addr)) { + return 0; + } + + const struct sockaddr_in6 *ip_addr_in6 = (const struct sockaddr_in6 *)&device_if->ip_addr; + return ip_addr_in6->sin6_scope_id; +} + void hdhomerun_discover2_device_if_get_ip_addr(struct hdhomerun_discover2_device_if_t *device_if, struct sockaddr_storage *ip_addr) { *ip_addr = device_if->ip_addr; diff --git a/hdhomerun_discover.h b/hdhomerun_discover.h index d6508d9..a01b83b 100644 --- a/hdhomerun_discover.h +++ b/hdhomerun_discover.h @@ -54,10 +54,10 @@ extern LIBHDHOMERUN_API void hdhomerun_discover_destroy(struct hdhomerun_discove * 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_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_device_id_broadcast(struct hdhomerun_discover_t *ds, uint32_t flags, uint32_t device_id); +extern LIBHDHOMERUN_API int hdhomerun_discover2_find_device_id_targeted(struct hdhomerun_discover_t *ds, const struct sockaddr *target_addr, uint32_t device_id); /* * Discover result access API. @@ -86,6 +86,7 @@ extern LIBHDHOMERUN_API const char *hdhomerun_discover2_device_get_device_auth(s 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 uint32_t hdhomerun_discover2_device_if_get_ipv6_linklocal_scope_id(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); diff --git a/hdhomerun_sock.h b/hdhomerun_sock.h index cea4be3..892a7cc 100644 --- a/hdhomerun_sock.h +++ b/hdhomerun_sock.h @@ -66,6 +66,7 @@ extern LIBHDHOMERUN_API void hdhomerun_sock_destroy(struct hdhomerun_sock_t *soc 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_ttl(struct hdhomerun_sock_t *sock, uint8_t ttl); 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); diff --git a/hdhomerun_sock_getifaddrs.c b/hdhomerun_sock_getifaddrs.c index f334712..72c5003 100644 --- a/hdhomerun_sock_getifaddrs.c +++ b/hdhomerun_sock_getifaddrs.c @@ -20,9 +20,17 @@ #include "hdhomerun.h" +#if defined(__APPLE__) +#import +#endif + #include #include + +#if !defined(TARGET_OS_IPHONE) #include +#endif + #include static uint8_t hdhomerun_local_ip_netmask_to_cidr(uint8_t *subnet_mask, size_t len) @@ -74,6 +82,11 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba continue; } + if (!hdhomerun_sock_sockaddr_is_addr(ifa->ifa_addr)) { + 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; @@ -93,6 +106,7 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba } if (ifa->ifa_addr->sa_family == AF_INET6) { +#if !defined(TARGET_OS_IPHONE) struct in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); @@ -109,7 +123,7 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba ifa = ifa->ifa_next; continue; } - +#endif uint32_t ifindex = if_nametoindex(ifa->ifa_name); struct sockaddr_in6 *netmask_in = (struct sockaddr_in6 *)ifa->ifa_netmask; diff --git a/hdhomerun_sock_netdevice.c b/hdhomerun_sock_netdevice.c index 4d9723a..c1a569d 100644 --- a/hdhomerun_sock_netdevice.c +++ b/hdhomerun_sock_netdevice.c @@ -89,9 +89,7 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba /* 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) { + if (!hdhomerun_sock_sockaddr_is_addr((const struct sockaddr *)&ip_addr_in)) { continue; } diff --git a/hdhomerun_sock_netlink.c b/hdhomerun_sock_netlink.c index c1a9da6..7fcab15 100644 --- a/hdhomerun_sock_netlink.c +++ b/hdhomerun_sock_netlink.c @@ -86,6 +86,11 @@ static void hdhomerun_local_ip_info2_newaddr(int af_sock, struct nlmsghdr *hdr, local_ip.sin_family = AF_INET; memcpy(&local_ip.sin_addr.s_addr, RTA_DATA(rta), 4); + if (!hdhomerun_sock_sockaddr_is_addr((const struct sockaddr *)&local_ip)) { + rta = RTA_NEXT(rta, ifa_payload_length); + continue; + } + callback(callback_arg, ifindex, (const struct sockaddr *)&local_ip, cidr); } @@ -96,6 +101,11 @@ static void hdhomerun_local_ip_info2_newaddr(int af_sock, struct nlmsghdr *hdr, local_ip.sin6_family = AF_INET6; memcpy(local_ip.sin6_addr.s6_addr, RTA_DATA(rta), 16); + if (!hdhomerun_sock_sockaddr_is_addr((const struct sockaddr *)&local_ip)) { + rta = RTA_NEXT(rta, ifa_payload_length); + continue; + } + if ((local_ip.sin6_addr.s6_addr[0] == 0xFE) && ((local_ip.sin6_addr.s6_addr[1] & 0xC0) == 0x80)) { local_ip.sin6_scope_id = ifindex; } diff --git a/hdhomerun_sock_posix.c b/hdhomerun_sock_posix.c index 76088a3..03fb557 100644 --- a/hdhomerun_sock_posix.c +++ b/hdhomerun_sock_posix.c @@ -26,6 +26,8 @@ struct hdhomerun_sock_t { int sock; + int af; + uint8_t ttl_set; }; static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int af, int protocol) @@ -35,6 +37,8 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int af, int proto return NULL; } + sock->af = af; + /* Create socket. */ sock->sock = socket(af, protocol, 0); if (sock->sock == -1) { @@ -113,6 +117,23 @@ 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_ttl(struct hdhomerun_sock_t *sock, uint8_t ttl) +{ + if (sock->ttl_set == ttl) { + return; + } + + int sock_opt = (int)(unsigned int)ttl; + if (sock->af == AF_INET) { + setsockopt(sock->sock, IPPROTO_IP, IP_TTL, (char *)&sock_opt, sizeof(sock_opt)); + } + if (sock->af == AF_INET6) { + setsockopt(sock->sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&sock_opt, sizeof(sock_opt)); + } + + sock->ttl_set = ttl; +} + void hdhomerun_sock_set_ipv4_onesbcast(struct hdhomerun_sock_t *sock, int v) { #if defined(IP_ONESBCAST) diff --git a/hdhomerun_sock_windows.c b/hdhomerun_sock_windows.c index e8b85b0..d5979e7 100644 --- a/hdhomerun_sock_windows.c +++ b/hdhomerun_sock_windows.c @@ -25,6 +25,8 @@ struct hdhomerun_sock_t { SOCKET sock; HANDLE event; long events_selected; + int af; + uint8_t ttl_set; }; bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callback, void *callback_arg) @@ -59,6 +61,11 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba IP_ADAPTER_ADDRESSES *adapter = adapter_addresses; while (adapter) { + if (adapter->OperStatus != 1) { + adapter = adapter->Next; + continue; + } + if ((adapter->IfType != MIB_IF_TYPE_ETHERNET) && (adapter->IfType != IF_TYPE_IEEE80211)) { adapter = adapter->Next; continue; @@ -73,12 +80,16 @@ bool hdhomerun_local_ip_info2(int af, hdhomerun_local_ip_info2_callback_t callba IP_ADAPTER_UNICAST_ADDRESS *adapter_address = adapter->FirstUnicastAddress; while (adapter_address) { - if (adapter_address->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) { + struct sockaddr *local_ip = adapter_address->Address.lpSockaddr; + if (!hdhomerun_sock_sockaddr_is_addr(local_ip)) { adapter_address = adapter_address->Next; continue; } - struct sockaddr *local_ip = adapter_address->Address.lpSockaddr; + if (adapter_address->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) { + adapter_address = adapter_address->Next; + continue; + } if ((local_ip->sa_family == AF_INET6) && (adapter_address->ValidLifetime != 0xFFFFFFFF)) { adapter_address = adapter_address->Next; @@ -105,6 +116,8 @@ static struct hdhomerun_sock_t *hdhomerun_sock_create_internal(int af, int proto return NULL; } + sock->af = af; + /* Create socket. */ sock->sock = socket(af, protocol, 0); if (sock->sock == INVALID_SOCKET) { @@ -189,6 +202,23 @@ 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_ttl(struct hdhomerun_sock_t *sock, uint8_t ttl) +{ + if (sock->ttl_set == ttl) { + return; + } + + int sock_opt = (int)(unsigned int)ttl; + if (sock->af == AF_INET) { + setsockopt(sock->sock, IPPROTO_IP, IP_TTL, (char *)&sock_opt, sizeof(sock_opt)); + } + if (sock->af == AF_INET6) { + setsockopt(sock->sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&sock_opt, sizeof(sock_opt)); + } + + sock->ttl_set = ttl; +} + void hdhomerun_sock_set_ipv4_onesbcast(struct hdhomerun_sock_t *sock, int v) { }