PageRenderTime 41ms CodeModel.GetById 15ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/platform/posix/os-socket.h

https://github.com/spike008t/libcec
C++ Header | 341 lines | 259 code | 44 blank | 38 comment | 74 complexity | 072ac99da1ab4c3ed1b6c035c5b74728 MD5 | raw file
  1#pragma once
  2/*
  3 * This file is part of the libCEC(R) library.
  4 *
  5 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited.  All rights reserved.
  6 * libCEC(R) is an original work, containing original code.
  7 *
  8 * libCEC(R) is a trademark of Pulse-Eight Limited.
  9 *
 10 * This program is dual-licensed; you can redistribute it and/or modify
 11 * it under the terms of the GNU General Public License as published by
 12 * the Free Software Foundation; either version 2 of the License, or
 13 * (at your option) any later version.
 14 *
 15 * This program is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18 * GNU General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU General Public License
 21 * along with this program; if not, write to the Free Software
 22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 23 *
 24 *
 25 * Alternatively, you can license this library under a commercial license,
 26 * please contact Pulse-Eight Licensing for more information.
 27 *
 28 * For more information contact:
 29 * Pulse-Eight Licensing       <license@pulse-eight.com>
 30 *     http://www.pulse-eight.com/
 31 *     http://www.pulse-eight.net/
 32 */
 33
 34
 35#include "lib/platform/os.h"
 36#include "lib/platform/util/timeutils.h"
 37#include <stdio.h>
 38#include <fcntl.h>
 39#include <sys/ioctl.h>
 40#include <sys/socket.h>
 41#include <netinet/in.h>
 42#include <netinet/tcp.h>
 43#include <arpa/inet.h>
 44#include <netdb.h>
 45#include <poll.h>
 46
 47/* Needed on Mac OS/X */
 48#ifndef SOL_TCP
 49#define SOL_TCP IPPROTO_TCP
 50#endif
 51
 52namespace PLATFORM
 53{
 54  // Standard sockets
 55  //@{
 56  inline void SocketClose(socket_t socket)
 57  {
 58    if (socket != INVALID_SOCKET_VALUE)
 59      close(socket);
 60  }
 61
 62  inline void SocketSetBlocking(socket_t socket, bool bSetTo)
 63  {
 64    if (socket != INVALID_SOCKET_VALUE)
 65    {
 66      if (bSetTo)
 67        fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK);
 68      else
 69        fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
 70    }
 71  }
 72
 73  inline ssize_t SocketWrite(socket_t socket, int *iError, void* data, size_t len)
 74  {
 75    fd_set port;
 76
 77    if (socket == INVALID_SOCKET_VALUE)
 78    {
 79      *iError = EINVAL;
 80      return -EINVAL;
 81    }
 82
 83    ssize_t iBytesWritten(0);
 84    struct timeval *tv(NULL);
 85
 86    while (iBytesWritten < (ssize_t)len)
 87    {
 88      FD_ZERO(&port);
 89      FD_SET(socket, &port);
 90      ssize_t returnv = (ssize_t)select(socket + 1, NULL, &port, NULL, tv);
 91      if (returnv < 0)
 92      {
 93        *iError = errno;
 94        return -errno;
 95      }
 96      else if (returnv == 0)
 97      {
 98        *iError = ETIMEDOUT;
 99        return -ETIMEDOUT;
100      }
101
102      returnv = write(socket, (char*)data + iBytesWritten, len - iBytesWritten);
103      if (returnv == -1)
104      {
105        *iError = errno;
106        return -errno;
107      }
108      iBytesWritten += returnv;
109    }
110
111    return iBytesWritten;
112  }
113
114  inline ssize_t SocketRead(socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
115  {
116    fd_set port;
117    struct timeval timeout, *tv;
118    int64_t iNow(0), iTarget(0);
119    ssize_t iBytesRead(0);
120    *iError = 0;
121
122    if (socket == INVALID_SOCKET_VALUE)
123    {
124      *iError = EINVAL;
125      return -EINVAL;
126    }
127
128    if (iTimeoutMs > 0)
129    {
130      iNow    = GetTimeMs();
131      iTarget = iNow + (int64_t) iTimeoutMs;
132    }
133
134    while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
135    {
136      if (iTimeoutMs == 0)
137      {
138        tv = NULL;
139      }
140      else
141      {
142        timeout.tv_sec  = ((long int)iTarget - (long int)iNow) / (long int)1000.;
143        timeout.tv_usec = ((long int)iTarget - (long int)iNow) % (long int)1000.;
144        tv = &timeout;
145      }
146
147      FD_ZERO(&port);
148      FD_SET(socket, &port);
149      ssize_t returnv = (ssize_t)select(socket + 1, &port, NULL, NULL, tv);
150
151      if (returnv == -1)
152      {
153        *iError = errno;
154        return -errno;
155      }
156      else if (returnv == 0)
157      {
158        break; //nothing to read
159      }
160
161      returnv = read(socket, (char*)data + iBytesRead, len - iBytesRead);
162      if (returnv == -1)
163      {
164        *iError = errno;
165        return -errno;
166      }
167
168      iBytesRead += returnv;
169
170      if (iTimeoutMs > 0)
171        iNow = GetTimeMs();
172    }
173
174    return iBytesRead;
175  }
176
177  inline int SocketIoctl(socket_t socket, int *iError, int request, void* data)
178  {
179    if (socket == INVALID_SOCKET_VALUE)
180    {
181      *iError = EINVAL;
182      return -1;
183    }
184
185    int iReturn = ioctl(socket, request, data);
186    if (iReturn < 0)
187      *iError = errno;
188    return iReturn;
189  }
190  //@}
191
192  // TCP
193  //@{
194  inline void TcpSocketClose(tcp_socket_t socket)
195  {
196    SocketClose(socket);
197  }
198
199  inline void TcpSocketShutdown(tcp_socket_t socket)
200  {
201    if (socket != INVALID_SOCKET_VALUE)
202      shutdown(socket, SHUT_RDWR);
203  }
204
205  inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
206  {
207    if (socket == INVALID_SOCKET_VALUE)
208    {
209      *iError = EINVAL;
210      return -1;
211    }
212
213    ssize_t iReturn = send(socket, data, len, 0);
214    if (iReturn < (ssize_t)len)
215      *iError = errno;
216    return iReturn;
217  }
218
219  inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
220  {
221    int64_t iNow(0), iTarget(0);
222    ssize_t iBytesRead(0);
223    *iError = 0;
224
225    if (socket == INVALID_SOCKET_VALUE)
226    {
227      *iError = EINVAL;
228      return -EINVAL;
229    }
230
231    if (iTimeoutMs > 0)
232    {
233      iNow    = GetTimeMs();
234      iTarget = iNow + (int64_t) iTimeoutMs;
235    }
236
237    struct pollfd fds;
238    fds.fd = socket;
239    fds.events = POLLIN;
240    fds.revents = 0;
241
242    while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
243    {
244      if (iTimeoutMs > 0)
245      {
246        int iPollResult = poll(&fds, 1, (int)(iTarget - iNow));
247        if (iPollResult == 0)
248        {
249          *iError = ETIMEDOUT;
250          return -ETIMEDOUT;
251        }
252      }
253
254      ssize_t iReadResult = (iTimeoutMs > 0) ?
255          recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_DONTWAIT) :
256          recv(socket, data, len, MSG_WAITALL);
257      if (iReadResult < 0)
258      {
259        if (errno == EAGAIN && iTimeoutMs > 0)
260          continue;
261        *iError = errno;
262        return -errno;
263      }
264      else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
265      {
266        *iError = ECONNRESET;
267        return -ECONNRESET;
268      }
269
270      iBytesRead += iReadResult;
271
272      if (iTimeoutMs > 0)
273        iNow = GetTimeMs();
274    }
275
276    if (iBytesRead < (ssize_t)len)
277      *iError = ETIMEDOUT;
278    return iBytesRead;
279  }
280
281  inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
282  {
283    struct   addrinfo hints;
284    char     service[33];
285    memset(&hints, 0, sizeof(hints));
286    hints.ai_family   = AF_UNSPEC;
287    hints.ai_socktype = SOCK_STREAM;
288    hints.ai_protocol = IPPROTO_TCP;
289    sprintf(service, "%d", iPort);
290
291    *iError = getaddrinfo(strHost, service, &hints, info);
292    return !(*iError);
293  }
294
295  inline int TcpGetSocketError(tcp_socket_t socket)
296  {
297    int iReturn(0);
298    socklen_t optLen = sizeof(socket_t);
299    getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&iReturn, &optLen);
300    return iReturn;
301  }
302
303  inline bool TcpSetNoDelay(tcp_socket_t socket)
304  {
305    int iSetTo(1);
306    setsockopt(socket, SOL_TCP, TCP_NODELAY, &iSetTo, sizeof(iSetTo));
307    return true;
308  }
309
310  inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
311  {
312    *iError = 0;
313    int iConnectResult = connect(socket, addr->ai_addr, addr->ai_addrlen);
314    if (iConnectResult == -1)
315    {
316      if (errno == EINPROGRESS)
317      {
318        struct pollfd pfd;
319        pfd.fd = socket;
320        pfd.events = POLLOUT;
321        pfd.revents = 0;
322
323        int iPollResult = poll(&pfd, 1, (int)iTimeout);
324        if (iPollResult == 0)
325          *iError = ETIMEDOUT;
326        else if (iPollResult == -1)
327          *iError = errno;
328
329        socklen_t errlen = sizeof(int);
330        getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)iError, &errlen);
331      }
332      else
333      {
334        *iError = errno;
335      }
336    }
337
338    return *iError == 0;
339  }
340  //@}
341}