MythTV  0.26-pre
hdhomerun_sock_windows.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_sock_windows.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  * Windows supports select() however native WSA events are used to:
00043  * - avoid problems with socket numbers above 1024.
00044  * - wait without allowing other events handlers to run (important for use
00045  *   with win7 WMC).
00046  */
00047 
00048 #include "hdhomerun.h"
00049 #include <windows.h>
00050 #include <iphlpapi.h>
00051 
00052 int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
00053 {
00054         PIP_ADAPTER_INFO AdapterInfo;
00055         ULONG AdapterInfoLength = sizeof(IP_ADAPTER_INFO) * 16;
00056 
00057         while (1) {
00058                 AdapterInfo = (IP_ADAPTER_INFO *)malloc(AdapterInfoLength);
00059                 if (!AdapterInfo) {
00060                         return -1;
00061                 }
00062 
00063                 ULONG LengthNeeded = AdapterInfoLength;
00064                 DWORD Ret = GetAdaptersInfo(AdapterInfo, &LengthNeeded);
00065                 if (Ret == NO_ERROR) {
00066                         break;
00067                 }
00068 
00069                 free(AdapterInfo);
00070 
00071                 if (Ret != ERROR_BUFFER_OVERFLOW) {
00072                         return -1;
00073                 }
00074                 if (AdapterInfoLength >= LengthNeeded) {
00075                         return -1;
00076                 }
00077 
00078                 AdapterInfoLength = LengthNeeded;
00079         }
00080 
00081         int count = 0;
00082         PIP_ADAPTER_INFO Adapter = AdapterInfo;
00083         while (Adapter) {
00084                 IP_ADDR_STRING *IPAddr = &Adapter->IpAddressList;
00085                 while (IPAddr) {
00086                         uint32_t ip_addr = ntohl(inet_addr(IPAddr->IpAddress.String));
00087                         uint32_t subnet_mask = ntohl(inet_addr(IPAddr->IpMask.String));
00088 
00089                         if (ip_addr == 0) {
00090                                 IPAddr = IPAddr->Next;
00091                                 continue;
00092                         }
00093 
00094                         struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count++];
00095                         ip_info->ip_addr = ip_addr;
00096                         ip_info->subnet_mask = subnet_mask;
00097 
00098                         if (count >= max_count) {
00099                                 break;
00100                         }
00101 
00102                         IPAddr = IPAddr->Next;
00103                 }
00104 
00105                 if (count >= max_count) {
00106                         break;
00107                 }
00108 
00109                 Adapter = Adapter->Next;
00110         }
00111 
00112         free(AdapterInfo);
00113         return count;
00114 }
00115 
00116 hdhomerun_sock_t hdhomerun_sock_create_udp(void)
00117 {
00118         /* Create socket. */
00119         hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_DGRAM, 0);
00120         if (sock == -1) {
00121                 return HDHOMERUN_SOCK_INVALID;
00122         }
00123 
00124         /* Set non-blocking */
00125         unsigned long mode = 1;
00126         if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
00127                 closesocket(sock);
00128                 return HDHOMERUN_SOCK_INVALID;
00129         }
00130 
00131         /* Allow broadcast. */
00132         int sock_opt = 1;
00133         setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
00134 
00135         /* Success. */
00136         return sock;
00137 }
00138 
00139 hdhomerun_sock_t hdhomerun_sock_create_tcp(void)
00140 {
00141         /* Create socket. */
00142         hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_STREAM, 0);
00143         if (sock == -1) {
00144                 return HDHOMERUN_SOCK_INVALID;
00145         }
00146 
00147         /* Set non-blocking */
00148         unsigned long mode = 1;
00149         if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
00150                 closesocket(sock);
00151                 return HDHOMERUN_SOCK_INVALID;
00152         }
00153 
00154         /* Success. */
00155         return sock;
00156 }
00157 
00158 void hdhomerun_sock_destroy(hdhomerun_sock_t sock)
00159 {
00160         closesocket(sock);
00161 }
00162 
00163 int hdhomerun_sock_getlasterror(void)
00164 {
00165         return WSAGetLastError();
00166 }
00167 
00168 uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock)
00169 {
00170         struct sockaddr_in sock_addr;
00171         int sockaddr_size = sizeof(sock_addr);
00172 
00173         if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
00174                 return 0;
00175         }
00176 
00177         return ntohl(sock_addr.sin_addr.s_addr);
00178 }
00179 
00180 uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock)
00181 {
00182         struct sockaddr_in sock_addr;
00183         int sockaddr_size = sizeof(sock_addr);
00184 
00185         if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
00186                 return 0;
00187         }
00188 
00189         return ntohs(sock_addr.sin_port);
00190 }
00191 
00192 uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock)
00193 {
00194         struct sockaddr_in sock_addr;
00195         int sockaddr_size = sizeof(sock_addr);
00196 
00197         if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
00198                 return 0;
00199         }
00200 
00201         return ntohl(sock_addr.sin_addr.s_addr);
00202 }
00203 
00204 uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name)
00205 {
00206         struct addrinfo hints;
00207         memset(&hints, 0, sizeof(hints));
00208         hints.ai_family = AF_INET;
00209         hints.ai_socktype = SOCK_STREAM;
00210         hints.ai_protocol = IPPROTO_TCP;
00211 
00212         struct addrinfo *sock_info;
00213         if (getaddrinfo(name, "", &hints, &sock_info) != 0) {
00214                 return 0;
00215         }
00216 
00217         struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
00218         uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);
00219 
00220         freeaddrinfo(sock_info);
00221         return addr;
00222 }
00223 
00224 bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port, bool_t allow_reuse)
00225 {
00226         int sock_opt = allow_reuse;
00227         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
00228 
00229         struct sockaddr_in sock_addr;
00230         memset(&sock_addr, 0, sizeof(sock_addr));
00231         sock_addr.sin_family = AF_INET;
00232         sock_addr.sin_addr.s_addr = htonl(local_addr);
00233         sock_addr.sin_port = htons(local_port);
00234 
00235         if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
00236                 return FALSE;
00237         }
00238 
00239         return TRUE;
00240 }
00241 
00242 bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
00243 {
00244         WSAEVENT wsa_event = WSACreateEvent();
00245         if (wsa_event == WSA_INVALID_EVENT) {
00246                 return FALSE;
00247         }
00248 
00249         if (WSAEventSelect(sock, wsa_event, FD_CONNECT) == SOCKET_ERROR) {
00250                 WSACloseEvent(wsa_event);
00251                 return FALSE;
00252         }
00253 
00254         /* Connect (non-blocking). */
00255         struct sockaddr_in sock_addr;
00256         memset(&sock_addr, 0, sizeof(sock_addr));
00257         sock_addr.sin_family = AF_INET;
00258         sock_addr.sin_addr.s_addr = htonl(remote_addr);
00259         sock_addr.sin_port = htons(remote_port);
00260 
00261         if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
00262                 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00263                         WSACloseEvent(wsa_event);
00264                         return FALSE;
00265                 }
00266         }
00267 
00268         /* Wait for connect to complete (both success and failure will signal). */
00269         DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)timeout, FALSE);
00270         WSACloseEvent(wsa_event);
00271 
00272         if (ret != WAIT_OBJECT_0) {
00273                 return FALSE;
00274         }
00275 
00276         /* Detect success/failure. */
00277         int sockaddr_size = sizeof(sock_addr);
00278         if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
00279                 return FALSE;
00280         }
00281 
00282         return TRUE;
00283 }
00284 
00285 static bool_t hdhomerun_sock_wait_for_event(hdhomerun_sock_t sock, long event_type, uint64_t stop_time)
00286 {
00287         uint64_t current_time = getcurrenttime();
00288         if (current_time >= stop_time) {
00289                 return FALSE;
00290         }
00291 
00292         WSAEVENT wsa_event = WSACreateEvent();
00293         if (wsa_event == WSA_INVALID_EVENT) {
00294                 return FALSE;
00295         }
00296 
00297         if (WSAEventSelect(sock, wsa_event, event_type) == SOCKET_ERROR) {
00298                 WSACloseEvent(wsa_event);
00299                 return FALSE;
00300         }
00301 
00302         DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)(stop_time - current_time), FALSE);
00303         WSACloseEvent(wsa_event);
00304 
00305         if (ret != WAIT_OBJECT_0) {
00306                 return FALSE;
00307         }
00308 
00309         return TRUE;
00310 }
00311 
00312 bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout)
00313 {
00314         uint64_t stop_time = getcurrenttime() + timeout;
00315         const uint8_t *ptr = (uint8_t *)data;
00316 
00317         while (1) {
00318                 int ret = send(sock, (char *)ptr, (int)length, 0);
00319                 if (ret >= (int)length) {
00320                         return TRUE;
00321                 }
00322 
00323                 if (ret > 0) {
00324                         ptr += ret;
00325                         length -= ret;
00326                 }
00327 
00328                 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00329                         return FALSE;
00330                 }
00331 
00332                 if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
00333                         return FALSE;
00334                 }
00335         }
00336 }
00337 
00338 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)
00339 {
00340         uint64_t stop_time = getcurrenttime() + timeout;
00341         const uint8_t *ptr = (uint8_t *)data;
00342 
00343         while (1) {
00344                 struct sockaddr_in sock_addr;
00345                 memset(&sock_addr, 0, sizeof(sock_addr));
00346                 sock_addr.sin_family = AF_INET;
00347                 sock_addr.sin_addr.s_addr = htonl(remote_addr);
00348                 sock_addr.sin_port = htons(remote_port);
00349 
00350                 int ret = sendto(sock, (char *)ptr, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
00351                 if (ret >= (int)length) {
00352                         return TRUE;
00353                 }
00354 
00355                 if (ret > 0) {
00356                         ptr += ret;
00357                         length -= ret;
00358                 }
00359 
00360                 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00361                         return FALSE;
00362                 }
00363 
00364                 if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
00365                         return FALSE;
00366                 }
00367         }
00368 }
00369 
00370 bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout)
00371 {
00372         uint64_t stop_time = getcurrenttime() + timeout;
00373 
00374         while (1) {
00375                 int ret = recv(sock, (char *)data, (int)(*length), 0);
00376                 if (ret > 0) {
00377                         *length = ret;
00378                         return TRUE;
00379                 }
00380 
00381                 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00382                         return FALSE;
00383                 }
00384 
00385                 if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
00386                         return FALSE;
00387                 }
00388         }
00389 }
00390 
00391 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)
00392 {
00393         uint64_t stop_time = getcurrenttime() + timeout;
00394 
00395         while (1) {
00396                 struct sockaddr_in sock_addr;
00397                 memset(&sock_addr, 0, sizeof(sock_addr));
00398                 int sockaddr_size = sizeof(sock_addr);
00399 
00400                 int ret = recvfrom(sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
00401                 if (ret > 0) {
00402                         *remote_addr = ntohl(sock_addr.sin_addr.s_addr);
00403                         *remote_port = ntohs(sock_addr.sin_port);
00404                         *length = ret;
00405                         return TRUE;
00406                 }
00407 
00408                 if (WSAGetLastError() != WSAEWOULDBLOCK) {
00409                         return FALSE;
00410                 }
00411 
00412                 if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
00413                         return FALSE;
00414                 }
00415         }
00416 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends