00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "hdhomerun.h"
00034
00035 #if defined(__CYGWIN__) || defined(__WINDOWS__)
00036 #include <windows.h>
00037 #include <iphlpapi.h>
00038 #define USE_IPHLPAPI 1
00039 #else
00040 #include <net/if.h>
00041 #include <sys/ioctl.h>
00042 #ifndef _SIZEOF_ADDR_IFREQ
00043 #define _SIZEOF_ADDR_IFREQ(x) sizeof(x)
00044 #endif
00045 #endif
00046
00047 #define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16
00048
00049 struct hdhomerun_discover_sock_t {
00050 int sock;
00051 uint32_t local_ip;
00052 uint32_t subnet_mask;
00053 };
00054
00055 struct hdhomerun_discover_t {
00056 struct hdhomerun_discover_sock_t socks[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT];
00057 unsigned int sock_count;
00058 struct hdhomerun_pkt_t tx_pkt;
00059 struct hdhomerun_pkt_t rx_pkt;
00060 };
00061
00062 static bool_t hdhomerun_discover_sock_create(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask)
00063 {
00064 if (ds->sock_count >= HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) {
00065 return FALSE;
00066 }
00067
00068
00069 int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
00070 if (sock == -1) {
00071 return FALSE;
00072 }
00073
00074
00075 setsocktimeout(sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
00076 setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
00077
00078
00079 int sock_opt = 1;
00080 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
00081
00082
00083 struct sockaddr_in sock_addr;
00084 memset(&sock_addr, 0, sizeof(sock_addr));
00085 sock_addr.sin_family = AF_INET;
00086 sock_addr.sin_addr.s_addr = htonl(local_ip);
00087 sock_addr.sin_port = htons(0);
00088 if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
00089 close(sock);
00090 return FALSE;
00091 }
00092
00093
00094 struct hdhomerun_discover_sock_t *dss = &ds->socks[ds->sock_count++];
00095 dss->sock = sock;
00096 dss->local_ip = local_ip;
00097 dss->subnet_mask = subnet_mask;
00098
00099 return TRUE;
00100 }
00101
00102 #if defined(USE_IPHLPAPI)
00103 static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
00104 {
00105 PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
00106 ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
00107
00108 DWORD Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
00109 if (Ret != NO_ERROR) {
00110 free(pAdapterInfo);
00111 if (Ret != ERROR_BUFFER_OVERFLOW) {
00112 return;
00113 }
00114 pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
00115 Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
00116 if (Ret != NO_ERROR) {
00117 free(pAdapterInfo);
00118 return;
00119 }
00120 }
00121
00122 PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
00123 while (pAdapter) {
00124 IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList;
00125 while (pIPAddr) {
00126 uint32_t local_ip = ntohl(inet_addr(pIPAddr->IpAddress.String));
00127 uint32_t mask = ntohl(inet_addr(pIPAddr->IpMask.String));
00128
00129 if (local_ip == 0) {
00130 pIPAddr = pIPAddr->Next;
00131 continue;
00132 }
00133
00134 hdhomerun_discover_sock_create(ds, local_ip, mask);
00135 pIPAddr = pIPAddr->Next;
00136 }
00137
00138 pAdapter = pAdapter->Next;
00139 }
00140
00141 free(pAdapterInfo);
00142 }
00143
00144 #else
00145
00146 static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
00147 {
00148 int fd = socket(AF_INET, SOCK_DGRAM, 0);
00149 if (fd == -1) {
00150 return;
00151 }
00152
00153 struct ifconf ifc;
00154 uint8_t buf[8192];
00155 ifc.ifc_len = sizeof(buf);
00156 ifc.ifc_buf = (char *)buf;
00157
00158 memset(buf, 0, sizeof(buf));
00159
00160 if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
00161 close(fd);
00162 return;
00163 }
00164
00165 uint8_t *ptr = (uint8_t *)ifc.ifc_req;
00166 uint8_t *end = (uint8_t *)&ifc.ifc_buf[ifc.ifc_len];
00167
00168 while (ptr <= end) {
00169 struct ifreq *ifr = (struct ifreq *)ptr;
00170 ptr += _SIZEOF_ADDR_IFREQ(*ifr);
00171
00172 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
00173 continue;
00174 }
00175 struct sockaddr_in *addr_in = (struct sockaddr_in *)&(ifr->ifr_addr);
00176 uint32_t local_ip = ntohl(addr_in->sin_addr.s_addr);
00177 if (local_ip == 0) {
00178 continue;
00179 }
00180
00181 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
00182 continue;
00183 }
00184 struct sockaddr_in *mask_in = (struct sockaddr_in *)&(ifr->ifr_addr);
00185 uint32_t mask = ntohl(mask_in->sin_addr.s_addr);
00186
00187 hdhomerun_discover_sock_create(ds, local_ip, mask);
00188 }
00189 }
00190 #endif
00191
00192 static struct hdhomerun_discover_t *hdhomerun_discover_create(void)
00193 {
00194 struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
00195 if (!ds) {
00196 return NULL;
00197 }
00198
00199
00200 if (!hdhomerun_discover_sock_create(ds, 0, 0)) {
00201 free(ds);
00202 return NULL;
00203 }
00204
00205
00206 hdhomerun_discover_sock_detect(ds);
00207
00208
00209 return ds;
00210 }
00211
00212 static void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds)
00213 {
00214 unsigned int i;
00215 for (i = 0; i < ds->sock_count; i++) {
00216 struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
00217 close(dss->sock);
00218 }
00219
00220 free(ds);
00221 }
00222
00223 static bool_t hdhomerun_discover_send_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
00224 {
00225 struct hdhomerun_pkt_t *tx_pkt = &ds->tx_pkt;
00226 hdhomerun_pkt_reset(tx_pkt);
00227
00228 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_TYPE);
00229 hdhomerun_pkt_write_var_length(tx_pkt, 4);
00230 hdhomerun_pkt_write_u32(tx_pkt, device_type);
00231 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_ID);
00232 hdhomerun_pkt_write_var_length(tx_pkt, 4);
00233 hdhomerun_pkt_write_u32(tx_pkt, device_id);
00234 hdhomerun_pkt_seal_frame(tx_pkt, HDHOMERUN_TYPE_DISCOVER_REQ);
00235
00236 struct sockaddr_in sock_addr;
00237 memset(&sock_addr, 0, sizeof(sock_addr));
00238 sock_addr.sin_family = AF_INET;
00239 sock_addr.sin_addr.s_addr = htonl(target_ip);
00240 sock_addr.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT);
00241
00242 int length = (int)(tx_pkt->end - tx_pkt->start);
00243 if (sendto(dss->sock, (char *)tx_pkt->start, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != length) {
00244 return FALSE;
00245 }
00246
00247 return TRUE;
00248 }
00249
00250 static bool_t hdhomerun_discover_send_wildcard_ip(struct hdhomerun_discover_t *ds, uint32_t device_type, uint32_t device_id)
00251 {
00252 bool_t result = FALSE;
00253
00254
00255
00256
00257
00258 unsigned int i;
00259 for (i = 1; i < ds->sock_count; i++) {
00260 struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
00261 uint32_t target_ip = dss->local_ip | ~dss->subnet_mask;
00262 result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
00263 }
00264
00265
00266
00267
00268 if (!result) {
00269 struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
00270 result = hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id);
00271 }
00272
00273 return result;
00274 }
00275
00276 static bool_t hdhomerun_discover_send_target_ip(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
00277 {
00278 bool_t result = FALSE;
00279
00280
00281
00282
00283
00284 unsigned int i;
00285 for (i = 1; i < ds->sock_count; i++) {
00286 struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
00287 if ((target_ip & dss->subnet_mask) != (dss->local_ip & dss->subnet_mask)) {
00288 continue;
00289 }
00290
00291 result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
00292 }
00293
00294
00295
00296
00297 if (!result) {
00298 struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
00299 result = hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
00300 }
00301
00302 return result;
00303 }
00304
00305 static bool_t hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
00306 {
00307 if (target_ip != 0) {
00308 return hdhomerun_discover_send_target_ip(ds, target_ip, device_type, device_id);
00309 }
00310
00311 return hdhomerun_discover_send_wildcard_ip(ds, device_type, device_id);
00312 }
00313
00314 static int hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result)
00315 {
00316 struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt;
00317 hdhomerun_pkt_reset(rx_pkt);
00318
00319 struct sockaddr_in sock_addr;
00320 memset(&sock_addr, 0, sizeof(sock_addr));
00321 socklen_t sockaddr_size = sizeof(sock_addr);
00322
00323 int rx_length = recvfrom(dss->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
00324 if (rx_length <= 0) {
00325
00326 return 0;
00327 }
00328 rx_pkt->end += rx_length;
00329
00330 uint16_t type;
00331 if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) {
00332 return 0;
00333 }
00334 if (type != HDHOMERUN_TYPE_DISCOVER_RPY) {
00335 return 0;
00336 }
00337
00338 result->ip_addr = ntohl(sock_addr.sin_addr.s_addr);
00339 result->device_type = 0;
00340 result->device_id = 0;
00341
00342 while (1) {
00343 uint8_t tag;
00344 size_t len;
00345 uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
00346 if (!next) {
00347 break;
00348 }
00349
00350 switch (tag) {
00351 case HDHOMERUN_TAG_DEVICE_TYPE:
00352 if (len != 4) {
00353 break;
00354 }
00355 result->device_type = hdhomerun_pkt_read_u32(rx_pkt);
00356 break;
00357
00358 case HDHOMERUN_TAG_DEVICE_ID:
00359 if (len != 4) {
00360 break;
00361 }
00362 result->device_id = hdhomerun_pkt_read_u32(rx_pkt);
00363 break;
00364
00365 default:
00366 break;
00367 }
00368
00369 rx_pkt->pos = next;
00370 }
00371
00372 return 1;
00373 }
00374
00375 static int hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result)
00376 {
00377 struct timeval t;
00378 t.tv_sec = 0;
00379 t.tv_usec = 250000;
00380
00381 fd_set readfds;
00382 FD_ZERO(&readfds);
00383 int max_sock = -1;
00384
00385 unsigned int i;
00386 for (i = 0; i < ds->sock_count; i++) {
00387 struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
00388 FD_SET(dss->sock, &readfds);
00389 if (dss->sock > max_sock) {
00390 max_sock = dss->sock;
00391 }
00392 }
00393
00394 if (select(max_sock+1, &readfds, NULL, NULL, &t) < 0) {
00395 return -1;
00396 }
00397
00398 for (i = 0; i < ds->sock_count; i++) {
00399 struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
00400 if (!FD_ISSET(dss->sock, &readfds)) {
00401 continue;
00402 }
00403
00404 if (hdhomerun_discover_recv_internal(ds, dss, result) <= 0) {
00405 continue;
00406 }
00407
00408 return 1;
00409 }
00410
00411 return 0;
00412 }
00413
00414 static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, uint32_t ip_addr)
00415 {
00416 int index;
00417 for (index = 0; index < count; index++) {
00418 struct hdhomerun_discover_device_t *result = &result_list[index];
00419 if (result->ip_addr == ip_addr) {
00420 return result;
00421 }
00422 }
00423
00424 return NULL;
00425 }
00426
00427 static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
00428 {
00429 int count = 0;
00430 int attempt;
00431 for (attempt = 0; attempt < 4; attempt++) {
00432 if (!hdhomerun_discover_send(ds, target_ip, device_type, device_id)) {
00433 return -1;
00434 }
00435
00436 uint64_t timeout = getcurrenttime() + 250;
00437 while (getcurrenttime() < timeout) {
00438 struct hdhomerun_discover_device_t *result = &result_list[count];
00439
00440 int ret = hdhomerun_discover_recv(ds, result);
00441 if (ret < 0) {
00442 return -1;
00443 }
00444 if (ret == 0) {
00445 continue;
00446 }
00447
00448
00449 if (device_type != HDHOMERUN_DEVICE_TYPE_WILDCARD) {
00450 if (device_type != result->device_type) {
00451 continue;
00452 }
00453 }
00454 if (device_id != HDHOMERUN_DEVICE_ID_WILDCARD) {
00455 if (device_id != result->device_id) {
00456 continue;
00457 }
00458 }
00459
00460
00461 if (hdhomerun_discover_find_in_list(result_list, count, result->ip_addr)) {
00462 continue;
00463 }
00464
00465
00466 count++;
00467 if (count >= max_count) {
00468 return count;
00469 }
00470 }
00471 }
00472
00473 return count;
00474 }
00475
00476 int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
00477 {
00478 struct hdhomerun_discover_t *ds = hdhomerun_discover_create();
00479 if (!ds) {
00480 return -1;
00481 }
00482
00483 int ret = hdhomerun_discover_find_devices_internal(ds, target_ip, device_type, device_id, result_list, max_count);
00484
00485 hdhomerun_discover_destroy(ds);
00486 return ret;
00487 }
00488
00489 bool_t hdhomerun_discover_validate_device_id(uint32_t device_id)
00490 {
00491 static uint32_t lookup_table[16] = {0xA, 0x5, 0xF, 0x6, 0x7, 0xC, 0x1, 0xB, 0x9, 0x2, 0x8, 0xD, 0x4, 0x3, 0xE, 0x0};
00492
00493 uint32_t checksum = 0;
00494
00495 checksum ^= lookup_table[(device_id >> 28) & 0x0F];
00496 checksum ^= (device_id >> 24) & 0x0F;
00497 checksum ^= lookup_table[(device_id >> 20) & 0x0F];
00498 checksum ^= (device_id >> 16) & 0x0F;
00499 checksum ^= lookup_table[(device_id >> 12) & 0x0F];
00500 checksum ^= (device_id >> 8) & 0x0F;
00501 checksum ^= lookup_table[(device_id >> 4) & 0x0F];
00502 checksum ^= (device_id >> 0) & 0x0F;
00503
00504 return (checksum == 0);
00505 }
00506