|
MythTV
0.26-pre
|
00001 /* 00002 * hdhomerun_sock_posix.c 00003 * 00004 * Copyright © 2010 Silicondust USA Inc. <www.silicondust.com>. 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 3 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 00018 * 00019 * As a special exception to the GNU Lesser General Public License, 00020 * you may link, statically or dynamically, an application with a 00021 * publicly distributed version of the Library to produce an 00022 * executable file containing portions of the Library, and 00023 * distribute that executable file under terms of your choice, 00024 * without any of the additional requirements listed in clause 4 of 00025 * the GNU Lesser General Public License. 00026 * 00027 * By "a publicly distributed version of the Library", we mean 00028 * either the unmodified Library as distributed by Silicondust, or a 00029 * modified version of the Library that is distributed under the 00030 * conditions defined in the GNU Lesser General Public License. 00031 */ 00032 00033 /* 00034 * Implementation notes: 00035 * 00036 * API specifies timeout for each operation (or zero for non-blocking). 00037 * 00038 * It is not possible to rely on the OS socket timeout as this will fail to 00039 * detect the command-response situation where data is sent successfully and 00040 * the other end chooses not to send a response (other than the TCP ack). 00041 * 00042 * The select() cannot be used with high socket numbers (typically max 1024) 00043 * so the code works as follows: 00044 * - Use non-blocking sockets to allow operation without select. 00045 * - Use select where safe (low socket numbers). 00046 * - Poll with short sleep when select cannot be used safely. 00047 */ 00048 00049 #include "hdhomerun.h" 00050 00051 #include <net/if.h> 00052 #include <sys/ioctl.h> 00053 #ifndef SIOCGIFCONF 00054 #include <sys/sockio.h> 00055 #endif 00056 #ifndef _SIZEOF_ADDR_IFREQ 00057 #define _SIZEOF_ADDR_IFREQ(x) sizeof(x) 00058 #endif 00059 00060 int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) 00061 { 00062 int sock = socket(AF_INET, SOCK_DGRAM, 0); 00063 if (sock == HDHOMERUN_SOCK_INVALID) { 00064 return -1; 00065 } 00066 00067 struct ifconf ifc; 00068 size_t ifreq_buffer_size = 1024; 00069 00070 while (1) { 00071 ifc.ifc_len = ifreq_buffer_size; 00072 ifc.ifc_buf = (char *)malloc(ifreq_buffer_size); 00073 if (!ifc.ifc_buf) { 00074 close(sock); 00075 return -1; 00076 } 00077 00078 memset(ifc.ifc_buf, 0, ifreq_buffer_size); 00079 00080 if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) { 00081 free(ifc.ifc_buf); 00082 close(sock); 00083 return -1; 00084 } 00085 00086 if (ifc.ifc_len < ifreq_buffer_size) { 00087 break; 00088 } 00089 00090 free(ifc.ifc_buf); 00091 ifreq_buffer_size += 1024; 00092 } 00093 00094 char *ptr = ifc.ifc_buf; 00095 char *end = ifc.ifc_buf + ifc.ifc_len; 00096 00097 int count = 0; 00098 while (ptr <= end) { 00099 struct ifreq *ifr = (struct ifreq *)ptr; 00100 ptr += _SIZEOF_ADDR_IFREQ(*ifr); 00101 00102 if (ioctl(sock, SIOCGIFADDR, ifr) != 0) { 00103 continue; 00104 } 00105 00106 struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&(ifr->ifr_addr); 00107 uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr); 00108 if (ip_addr == 0) { 00109 continue; 00110 } 00111 00112 if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) { 00113 continue; 00114 } 00115 00116 struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&(ifr->ifr_addr); 00117 uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr); 00118 00119 struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count++]; 00120 ip_info->ip_addr = ip_addr; 00121 ip_info->subnet_mask = subnet_mask; 00122 00123 if (count >= max_count) { 00124 break; 00125 } 00126 } 00127 00128 free(ifc.ifc_buf); 00129 close(sock); 00130 return count; 00131 } 00132 00133 hdhomerun_sock_t hdhomerun_sock_create_udp(void) 00134 { 00135 /* Create socket. */ 00136 hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_DGRAM, 0); 00137 if (sock == -1) { 00138 return HDHOMERUN_SOCK_INVALID; 00139 } 00140 00141 /* Set non-blocking */ 00142 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { 00143 close(sock); 00144 return HDHOMERUN_SOCK_INVALID; 00145 } 00146 00147 /* Allow broadcast. */ 00148 int sock_opt = 1; 00149 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt)); 00150 00151 /* Success. */ 00152 return sock; 00153 } 00154 00155 hdhomerun_sock_t hdhomerun_sock_create_tcp(void) 00156 { 00157 /* Create socket. */ 00158 hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_STREAM, 0); 00159 if (sock == -1) { 00160 return HDHOMERUN_SOCK_INVALID; 00161 } 00162 00163 /* Set non-blocking */ 00164 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { 00165 close(sock); 00166 return HDHOMERUN_SOCK_INVALID; 00167 } 00168 00169 /* Success. */ 00170 return sock; 00171 } 00172 00173 void hdhomerun_sock_destroy(hdhomerun_sock_t sock) 00174 { 00175 close(sock); 00176 } 00177 00178 int hdhomerun_sock_getlasterror(void) 00179 { 00180 return errno; 00181 } 00182 00183 uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock) 00184 { 00185 struct sockaddr_in sock_addr; 00186 socklen_t sockaddr_size = sizeof(sock_addr); 00187 00188 if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { 00189 return 0; 00190 } 00191 00192 return ntohl(sock_addr.sin_addr.s_addr); 00193 } 00194 00195 uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock) 00196 { 00197 struct sockaddr_in sock_addr; 00198 socklen_t sockaddr_size = sizeof(sock_addr); 00199 00200 if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { 00201 return 0; 00202 } 00203 00204 return ntohs(sock_addr.sin_port); 00205 } 00206 00207 uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock) 00208 { 00209 struct sockaddr_in sock_addr; 00210 socklen_t sockaddr_size = sizeof(sock_addr); 00211 00212 if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) { 00213 return 0; 00214 } 00215 00216 return ntohl(sock_addr.sin_addr.s_addr); 00217 } 00218 00219 uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name) 00220 { 00221 struct addrinfo hints; 00222 memset(&hints, 0, sizeof(hints)); 00223 hints.ai_family = AF_INET; 00224 hints.ai_socktype = SOCK_STREAM; 00225 hints.ai_protocol = IPPROTO_TCP; 00226 00227 struct addrinfo *sock_info; 00228 if (getaddrinfo(name, "", &hints, &sock_info) != 0) { 00229 return 0; 00230 } 00231 00232 struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr; 00233 uint32_t addr = ntohl(sock_addr->sin_addr.s_addr); 00234 00235 freeaddrinfo(sock_info); 00236 return addr; 00237 } 00238 00239 bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port, bool_t allow_reuse) 00240 { 00241 int sock_opt = allow_reuse; 00242 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt)); 00243 00244 struct sockaddr_in sock_addr; 00245 memset(&sock_addr, 0, sizeof(sock_addr)); 00246 sock_addr.sin_family = AF_INET; 00247 sock_addr.sin_addr.s_addr = htonl(local_addr); 00248 sock_addr.sin_port = htons(local_port); 00249 00250 if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) { 00251 return FALSE; 00252 } 00253 00254 return TRUE; 00255 } 00256 00257 static bool_t hdhomerun_sock_wait_for_read_event(hdhomerun_sock_t sock, uint64_t stop_time) 00258 { 00259 uint64_t current_time = getcurrenttime(); 00260 if (current_time >= stop_time) { 00261 return FALSE; 00262 } 00263 00264 if (sock < FD_SETSIZE) { 00265 uint64_t timeout = stop_time - current_time; 00266 struct timeval t; 00267 t.tv_sec = timeout / 1000; 00268 t.tv_usec = (timeout % 1000) * 1000; 00269 00270 fd_set readfds; 00271 FD_ZERO(&readfds); 00272 FD_SET(sock, &readfds); 00273 00274 fd_set errorfds; 00275 FD_ZERO(&errorfds); 00276 FD_SET(sock, &errorfds); 00277 00278 if (select(sock + 1, &readfds, NULL, &errorfds, &t) <= 0) { 00279 return FALSE; 00280 } 00281 if (!FD_ISSET(sock, &readfds)) { 00282 return FALSE; 00283 } 00284 } else { 00285 uint64_t delay = stop_time - current_time; 00286 if (delay > 5) { 00287 delay = 5; 00288 } 00289 00290 msleep_approx(delay); 00291 } 00292 00293 return TRUE; 00294 } 00295 00296 static bool_t hdhomerun_sock_wait_for_write_event(hdhomerun_sock_t sock, uint64_t stop_time) 00297 { 00298 uint64_t current_time = getcurrenttime(); 00299 if (current_time >= stop_time) { 00300 return FALSE; 00301 } 00302 00303 if (sock < FD_SETSIZE) { 00304 uint64_t timeout = stop_time - current_time; 00305 struct timeval t; 00306 t.tv_sec = timeout / 1000; 00307 t.tv_usec = (timeout % 1000) * 1000; 00308 00309 fd_set writefds; 00310 FD_ZERO(&writefds); 00311 FD_SET(sock, &writefds); 00312 00313 fd_set errorfds; 00314 FD_ZERO(&errorfds); 00315 FD_SET(sock, &errorfds); 00316 00317 if (select(sock + 1, NULL, &writefds, &errorfds, &t) <= 0) { 00318 return FALSE; 00319 } 00320 if (!FD_ISSET(sock, &writefds)) { 00321 return FALSE; 00322 } 00323 } else { 00324 uint64_t delay = stop_time - current_time; 00325 if (delay > 5) { 00326 delay = 5; 00327 } 00328 00329 msleep_approx(delay); 00330 } 00331 00332 return TRUE; 00333 } 00334 00335 bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout) 00336 { 00337 struct sockaddr_in sock_addr; 00338 memset(&sock_addr, 0, sizeof(sock_addr)); 00339 sock_addr.sin_family = AF_INET; 00340 sock_addr.sin_addr.s_addr = htonl(remote_addr); 00341 sock_addr.sin_port = htons(remote_port); 00342 00343 if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) == 0) { 00344 return TRUE; 00345 } 00346 00347 uint64_t stop_time = getcurrenttime() + timeout; 00348 00349 /* 00350 * getpeername() is used to detect if connect succeeded. Bug - cygwin 00351 * will return getpeername success even if the connect process hasn't 00352 * completed. This first call to select is used to work around the 00353 * problem (at least for low numbered sockets where select is used). 00354 */ 00355 if (!hdhomerun_sock_wait_for_write_event(sock, stop_time)) { 00356 return FALSE; 00357 } 00358 00359 while (1) { 00360 struct sockaddr_in sock_addr; 00361 socklen_t sockaddr_size = sizeof(sock_addr); 00362 if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) == 0) { 00363 return TRUE; 00364 } 00365 00366 if (errno != ENOTCONN) { 00367 return FALSE; 00368 } 00369 00370 if (!hdhomerun_sock_wait_for_write_event(sock, stop_time)) { 00371 return FALSE; 00372 } 00373 } 00374 } 00375 00376 bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout) 00377 { 00378 uint64_t stop_time = getcurrenttime() + timeout; 00379 const uint8_t *ptr = (const uint8_t *)data; 00380 00381 while (1) { 00382 int ret = send(sock, ptr, length, 0); 00383 if (ret >= (int)length) { 00384 return TRUE; 00385 } 00386 00387 if (ret > 0) { 00388 ptr += ret; 00389 length -= ret; 00390 } 00391 00392 if (errno == EINPROGRESS) { 00393 errno = EWOULDBLOCK; 00394 } 00395 if (errno != EWOULDBLOCK) { 00396 return FALSE; 00397 } 00398 00399 if (!hdhomerun_sock_wait_for_write_event(sock, stop_time)) { 00400 return FALSE; 00401 } 00402 } 00403 } 00404 00405 bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout) 00406 { 00407 uint64_t stop_time = getcurrenttime() + timeout; 00408 const uint8_t *ptr = (const uint8_t *)data; 00409 00410 while (1) { 00411 struct sockaddr_in sock_addr; 00412 memset(&sock_addr, 0, sizeof(sock_addr)); 00413 sock_addr.sin_family = AF_INET; 00414 sock_addr.sin_addr.s_addr = htonl(remote_addr); 00415 sock_addr.sin_port = htons(remote_port); 00416 00417 int ret = sendto(sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); 00418 if (ret >= (int)length) { 00419 return TRUE; 00420 } 00421 00422 if (ret > 0) { 00423 ptr += ret; 00424 length -= ret; 00425 } 00426 00427 if (errno == EINPROGRESS) { 00428 errno = EWOULDBLOCK; 00429 } 00430 if (errno != EWOULDBLOCK) { 00431 return FALSE; 00432 } 00433 00434 if (!hdhomerun_sock_wait_for_write_event(sock, stop_time)) { 00435 return FALSE; 00436 } 00437 } 00438 } 00439 00440 bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout) 00441 { 00442 uint64_t stop_time = getcurrenttime() + timeout; 00443 00444 while (1) { 00445 int ret = recv(sock, data, *length, 0); 00446 if (ret > 0) { 00447 *length = ret; 00448 return TRUE; 00449 } 00450 00451 if (errno == EINPROGRESS) { 00452 errno = EWOULDBLOCK; 00453 } 00454 if (errno != EWOULDBLOCK) { 00455 return FALSE; 00456 } 00457 00458 if (!hdhomerun_sock_wait_for_read_event(sock, stop_time)) { 00459 return FALSE; 00460 } 00461 } 00462 } 00463 00464 bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout) 00465 { 00466 uint64_t stop_time = getcurrenttime() + timeout; 00467 00468 while (1) { 00469 struct sockaddr_in sock_addr; 00470 memset(&sock_addr, 0, sizeof(sock_addr)); 00471 socklen_t sockaddr_size = sizeof(sock_addr); 00472 00473 int ret = recvfrom(sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size); 00474 if (ret > 0) { 00475 *remote_addr = ntohl(sock_addr.sin_addr.s_addr); 00476 *remote_port = ntohs(sock_addr.sin_port); 00477 *length = ret; 00478 return TRUE; 00479 } 00480 00481 if (errno == EINPROGRESS) { 00482 errno = EWOULDBLOCK; 00483 } 00484 if (errno != EWOULDBLOCK) { 00485 return FALSE; 00486 } 00487 00488 if (!hdhomerun_sock_wait_for_read_event(sock, stop_time)) { 00489 return FALSE; 00490 } 00491 } 00492 }
1.7.6.1