Merge pull request #4791 from jmarshallnz/playlist_settings
[vuplus_xbmc] / lib / libhts / net_winsock.c
1 /*
2  *  Networking under WINDOWS
3  *  Copyright (C) 2007-2008 Andreas Ă–man
4  *  Copyright (C) 2007-2008 Joakim Plate
5  *
6  *  This program is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  as published by the Free Software Foundation; either version 2
9  *  of the License, or (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <errno.h>
27
28 #include <winsock2.h>
29 #include <Ws2tcpip.h>
30 #include "msvc.h"
31 #include "net.h"
32
33
34 static int socket_errno()
35 {
36   int error = WSAGetLastError();
37   switch(error)
38   {
39     case WSAEINPROGRESS: return EINPROGRESS;
40     case WSAECONNRESET : return ECONNRESET;
41     case WSAETIMEDOUT  : return ETIMEDOUT;
42     case WSAEWOULDBLOCK: return EAGAIN;
43     default            : return error;
44   }
45 }
46
47 #ifndef MSG_WAITALL
48 #define MSG_WAITALL 0x8
49 #endif
50
51 static int recv_fixed (SOCKET s, char * buf, int len, int flags)
52 {
53   char* org = buf;
54   int   res = 1;
55
56   if((flags & MSG_WAITALL) == 0)
57     return recv(s, buf, len, flags);
58
59   flags &= ~MSG_WAITALL;
60   while(len > 0 && res > 0)
61   {
62     res = recv(s, buf, len, flags);
63     if(res < 0)
64       return res;
65
66     buf += res;
67     len -= res;
68   }
69   return buf - org;
70 }
71 #define recv(s, buf, len, flags) recv_fixed(s, buf, len, flags)
72
73 /**
74  *
75  */
76 socket_t
77 htsp_tcp_connect_addr(struct addrinfo* addr, char *errbuf, size_t errbufsize,
78             int timeout)
79 {
80   socket_t fd;
81   int r, err, val;
82   socklen_t errlen = sizeof(int);
83
84   fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
85   if(fd == -1) {
86     snprintf(errbuf, errbufsize, "Unable to create socket: %s",
87              strerror(socket_errno()));
88     return -1;
89   }
90
91   /**
92    * Switch to nonblocking
93    */
94   val = 1;
95   ioctlsocket(fd, FIONBIO, &val);
96
97   r = connect(fd, addr->ai_addr, addr->ai_addrlen);
98
99   if(r == -1) {
100     if(socket_errno() == EINPROGRESS ||
101        socket_errno() == EAGAIN) {
102       fd_set fd_write, fd_except;
103       struct timeval tv;
104
105       tv.tv_sec  =         timeout / 1000;
106       tv.tv_usec = 1000 * (timeout % 1000);
107
108       FD_ZERO(&fd_write);
109       FD_ZERO(&fd_except);
110
111       FD_SET(fd, &fd_write);
112       FD_SET(fd, &fd_except);
113
114       r = select((int)fd+1, NULL, &fd_write, &fd_except, &tv);
115
116       if(r == 0) {
117         /* Timeout */
118         snprintf(errbuf, errbufsize, "Connection attempt timed out");
119         closesocket(fd);
120         return -1;
121       }
122
123       if(r == -1) {
124         snprintf(errbuf, errbufsize, "select() error: %s", strerror(socket_errno()));
125         closesocket(fd);
126         return -1;
127       }
128
129       getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen);
130     } else {
131       err = socket_errno();
132     }
133   } else {
134     err = 0;
135   }
136
137   if(err != 0) {
138     snprintf(errbuf, errbufsize, "%s", strerror(err));
139     closesocket(fd);
140     return -1;
141   }
142
143   val = 0;
144   ioctlsocket(fd, FIONBIO, &val);
145
146   val = 1;
147   setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&val, sizeof(val));
148
149   return fd;
150 }
151
152
153 socket_t
154 htsp_tcp_connect(const char *hostname, int port, char *errbuf, size_t errbufsize,
155             int timeout)
156 {
157   struct   addrinfo hints;
158   struct   addrinfo *result, *addr;
159   char     service[33];
160   int      res;
161   socket_t fd = INVALID_SOCKET;
162
163   memset(&hints, 0, sizeof(hints));
164   hints.ai_family   = AF_UNSPEC;
165   hints.ai_socktype = SOCK_STREAM;
166   hints.ai_protocol = IPPROTO_TCP;
167   sprintf(service, "%d", port);
168
169   res = getaddrinfo(hostname, service, &hints, &result);
170   if(res) {
171     switch(res) {
172     case EAI_NONAME:
173       snprintf(errbuf, errbufsize, "The specified host is unknown");
174       break;
175
176     case EAI_FAIL:
177       snprintf(errbuf, errbufsize, "A nonrecoverable failure in name resolution occurred");
178       break;
179
180     case EAI_MEMORY:
181       snprintf(errbuf, errbufsize, "A memory allocation failure occurred");
182       break;
183
184     case EAI_AGAIN:
185       snprintf(errbuf, errbufsize, "A temporary error occurred on an authoritative name server");
186       break;
187
188     default:
189       snprintf(errbuf, errbufsize, "Unknown error %d", res);
190       break;
191     }
192     return -1;
193   }
194
195   for(addr = result; addr; addr = addr->ai_next) {
196     fd = htsp_tcp_connect_addr(addr, errbuf, errbufsize, timeout);
197     if(fd != INVALID_SOCKET)
198       break;
199   }
200
201   freeaddrinfo(result);
202   return fd;
203 }
204
205
206 /**
207  *
208  */
209 int
210 htsp_tcp_write_queue(socket_t fd, htsbuf_queue_t *q)
211 {
212   htsbuf_data_t *hd;
213   int l, r;
214
215   while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) {
216     TAILQ_REMOVE(&q->hq_q, hd, hd_link);
217
218     l = hd->hd_data_len - hd->hd_data_off;
219     r = send(fd, hd->hd_data + hd->hd_data_off, l, 0);
220     free(hd->hd_data);
221     free(hd);
222   }
223   q->hq_size = 0;
224   return 0;
225 }
226
227
228 /**
229  *
230  */
231 static int
232 tcp_fill_htsbuf_from_fd(socket_t fd, htsbuf_queue_t *hq)
233 {
234   htsbuf_data_t *hd = TAILQ_LAST(&hq->hq_q, htsbuf_data_queue);
235   int c;
236
237   if(hd != NULL) {
238     /* Fill out any previous buffer */
239     c = hd->hd_data_size - hd->hd_data_len;
240
241     if(c > 0) {
242
243       c = recv(fd, hd->hd_data + hd->hd_data_len, c, MSG_WAITALL);
244       if(c < 1)
245         return -1;
246
247       hd->hd_data_len += c;
248       hq->hq_size += c;
249       return 0;
250     }
251   }
252
253   hd = malloc(sizeof(htsbuf_data_t));
254
255   hd->hd_data_size = 1000;
256   hd->hd_data = malloc(hd->hd_data_size);
257
258   c = recv(fd, hd->hd_data, hd->hd_data_size, MSG_WAITALL);
259   if(c < 1) {
260     free(hd->hd_data);
261     free(hd);
262     return -1;
263   }
264   hd->hd_data_len = c;
265   hd->hd_data_off = 0;
266   TAILQ_INSERT_TAIL(&hq->hq_q, hd, hd_link);
267   hq->hq_size += c;
268   return 0;
269 }
270
271
272 /**
273  *
274  */
275 int
276 htsp_tcp_read_line(socket_t fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
277 {
278   int len;
279
280   while(1) {
281     len = htsbuf_find(spill, 0xa);
282
283     if(len == -1) {
284       if(tcp_fill_htsbuf_from_fd(fd, spill) < 0)
285         return -1;
286       continue;
287     }
288
289     if(len >= (int)bufsize - 1)
290       return -1;
291
292     htsbuf_read(spill, buf, len);
293     buf[len] = 0;
294     while(len > 0 && buf[len - 1] < 32)
295       buf[--len] = 0;
296     htsbuf_drop(spill, 1); /* Drop the \n */
297     return 0;
298   }
299 }
300
301
302 /**
303  *
304  */
305 int
306 htsp_tcp_read_data(socket_t fd, char *buf, const size_t bufsize, htsbuf_queue_t *spill)
307 {
308   int x, tot = htsbuf_read(spill, buf, bufsize);
309
310   if(tot == bufsize)
311     return 0;
312
313   x = recv(fd, buf + tot, bufsize - tot, MSG_WAITALL);
314   if(x != bufsize - tot)
315     return -1;
316
317   return 0;
318 }
319
320 /**
321  *
322  */
323 int
324 htsp_tcp_read(socket_t fd, void *buf, size_t len)
325 {
326   int x = recv(fd, buf, len, MSG_WAITALL);
327
328   if(x == -1)
329     return socket_errno();
330   if(x != len)
331     return ECONNRESET;
332   return 0;
333
334 }
335
336 /**
337  *
338  */
339 int
340 htsp_tcp_read_timeout(socket_t fd, char *buf, size_t len, int timeout)
341 {
342   int x, tot = 0, val, err;
343   fd_set fd_read;
344   struct timeval tv;
345
346   assert(timeout > 0);
347
348   while(tot != len) {
349
350     tv.tv_sec  =         timeout / 1000;
351     tv.tv_usec = 1000 * (timeout % 1000);
352
353     FD_ZERO(&fd_read);
354     FD_SET(fd, &fd_read);
355
356     x = select((int)fd+1, &fd_read, NULL, NULL, &tv);
357
358     if(x == 0)
359       return ETIMEDOUT;
360
361     val = 1;
362     ioctlsocket(fd, FIONBIO, &val);
363
364     x   = recv(fd, buf + tot, len - tot, 0);
365     err = socket_errno();
366
367     val = 0;
368     ioctlsocket(fd, FIONBIO, &val);
369
370     if(x == 0)
371       return ECONNRESET;
372     else if(x == -1)
373     {
374       if(err == EAGAIN)
375         continue;
376       return err;
377     }
378
379     tot += x;
380   }
381   return 0;
382 }
383
384 /**
385  *
386  */
387 void
388 htsp_tcp_close(socket_t fd)
389 {
390   closesocket(fd);
391 }