MythTV  0.26-pre
hdhomerun_sock_posix.c
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends