]> git.sesse.net Git - vlc/blob - src/misc/net.c
* skins2: Other attempt at supporting transparency on Windows (win2k, winxp).
[vlc] / src / misc / net.c
1 /*****************************************************************************
2  * net.c:
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <vlc/vlc.h>
29
30 #ifdef HAVE_ERRNO_H
31 #   include <errno.h>
32 #endif
33
34 #ifdef HAVE_FCNTL_H
35 #   include <fcntl.h>
36 #endif
37
38 #ifdef HAVE_SYS_TIME_H
39 #    include <sys/time.h>
40 #endif
41
42 #if defined( UNDER_CE )
43 #   include <winsock.h>
44 #elif defined( WIN32 )
45 #   include <winsock2.h>
46 #   include <ws2tcpip.h>
47 #   ifndef IN_MULTICAST
48 #       define IN_MULTICAST(a) IN_CLASSD(a)
49 #   endif
50 #else
51 #   include <sys/socket.h>
52 #endif
53
54 #ifdef HAVE_UNISTD_H
55 #   include <unistd.h>
56 #endif
57
58 #include "network.h"
59
60
61 /*****************************************************************************
62  * __net_OpenTCP:
63  *****************************************************************************
64  * Open a TCP connection and return a handle
65  *****************************************************************************/
66 int __net_OpenTCP( vlc_object_t *p_this, char *psz_host, int i_port )
67 {
68     vlc_value_t      val;
69     void            *private;
70
71     char            *psz_network = "";
72     network_socket_t sock;
73     module_t         *p_network;
74
75     /* Check if we have force ipv4 or ipv6 */
76     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
77     var_Get( p_this, "ipv4", &val );
78     if( val.b_bool )
79     {
80         psz_network = "ipv4";
81     }
82
83     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
84     var_Get( p_this, "ipv6", &val );
85     if( val.b_bool )
86     {
87         psz_network = "ipv6";
88     }
89
90     /* Prepare the network_socket_t structure */
91     sock.i_type = NETWORK_TCP;
92     sock.psz_bind_addr   = "";
93     sock.i_bind_port     = 0;
94     sock.psz_server_addr = psz_host;
95     sock.i_server_port   = i_port;
96     sock.i_ttl           = 0;
97
98     msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_host, i_port );
99     private = p_this->p_private;
100     p_this->p_private = (void*)&sock;
101     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
102     {
103         msg_Dbg( p_this, "net: connection to '%s:%d' failed",
104                  psz_host, i_port );
105         return -1;
106     }
107     module_Unneed( p_this, p_network );
108     p_this->p_private = private;
109
110     return sock.i_handle;
111 }
112
113 /*****************************************************************************
114  * __net_OpenUDP:
115  *****************************************************************************
116  * Open a UDP connection and return a handle
117  *****************************************************************************/
118 int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
119                    char *psz_server, int i_server )
120 {
121     vlc_value_t      val;
122     void            *private;
123
124     char            *psz_network = "";
125     network_socket_t sock;
126     module_t         *p_network;
127
128
129     /* Check if we have force ipv4 or ipv6 */
130     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
131     var_Get( p_this, "ipv4", &val );
132     if( val.b_bool )
133     {
134         psz_network = "ipv4";
135     }
136
137     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
138     var_Get( p_this, "ipv6", &val );
139     if( val.b_bool )
140     {
141         psz_network = "ipv6";
142     }
143     if( psz_server == NULL ) psz_server = "";
144     if( psz_bind   == NULL ) psz_bind   = "";
145
146     /* Prepare the network_socket_t structure */
147     sock.i_type = NETWORK_UDP;
148     sock.psz_bind_addr   = psz_bind;
149     sock.i_bind_port     = i_bind;
150     sock.psz_server_addr = psz_server;
151     sock.i_server_port   = i_server;
152     sock.i_ttl           = 0;
153
154     msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
155              psz_server, i_server, psz_bind, i_bind );
156     private = p_this->p_private;
157     p_this->p_private = (void*)&sock;
158     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
159     {
160         msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
161                  psz_server, i_server, psz_bind, i_bind );
162         return -1;
163     }
164     module_Unneed( p_this, p_network );
165     p_this->p_private = private;
166
167     return sock.i_handle;
168 }
169
170 /*****************************************************************************
171  * __net_Close:
172  *****************************************************************************
173  * Close a network handle
174  *****************************************************************************/
175 void net_Close( int fd )
176 {
177 #ifdef UNDER_CE
178     CloseHandle( (HANDLE)fd );
179 #elif defined( WIN32 )
180     closesocket( fd );
181 #else
182     close( fd );
183 #endif
184 }
185
186 /*****************************************************************************
187  * __net_Read:
188  *****************************************************************************
189  * Read from a network socket
190  * If b_rety is true, then we repeat until we have read the right amount of
191  * data
192  *****************************************************************************/
193 int __net_Read( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data,
194                 vlc_bool_t b_retry )
195 {
196     struct timeval  timeout;
197     fd_set          fds_r, fds_e;
198     int             i_recv;
199     int             i_total = 0;
200     int             i_ret;
201     vlc_bool_t      b_die = p_this->b_die;
202
203     while( i_data > 0 )
204     {
205         do
206         {
207             if( p_this->b_die != b_die )
208             {
209                 return 0;
210             }
211
212             /* Initialize file descriptor set */
213             FD_ZERO( &fds_r );
214             FD_SET( fd, &fds_r );
215             FD_ZERO( &fds_e );
216             FD_SET( fd, &fds_e );
217
218             /* We'll wait 0.5 second if nothing happens */
219             timeout.tv_sec = 0;
220             timeout.tv_usec = 500000;
221
222         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
223                  || ( i_ret < 0 && errno == EINTR ) );
224
225         if( i_ret < 0 )
226         {
227             msg_Err( p_this, "network select error (%s)", strerror(errno) );
228             return i_total > 0 ? i_total : -1;
229         }
230
231         if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) < 0 )
232         {
233 #ifdef WIN32
234             /* For udp only */
235             /* On win32 recv() will fail if the datagram doesn't fit inside
236              * the passed buffer, even though the buffer will be filled with
237              * the first part of the datagram. */
238             if( WSAGetLastError() == WSAEMSGSIZE )
239             {
240                 msg_Err( p_this, "recv() failed. "
241                          "Increase the mtu size (--mtu option)" );
242                 i_recv = i_data;
243             }
244             else
245                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
246 #else
247             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
248 #endif
249             return i_total > 0 ? i_total : -1;
250         }
251         else if( i_recv == 0 )
252         {
253             /* Connection closed */
254             b_retry = VLC_FALSE;
255         }
256
257         p_data += i_recv;
258         i_data -= i_recv;
259         i_total+= i_recv;
260         if( !b_retry )
261         {
262             break;
263         }
264     }
265     return i_total;
266 }
267
268 /* Write exact amount requested */
269 int __net_Write( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data )
270 {
271     struct timeval  timeout;
272     fd_set          fds_w, fds_e;
273     int             i_send;
274     int             i_total = 0;
275     int             i_ret;
276
277     vlc_bool_t      b_die = p_this->b_die;
278
279     while( i_data > 0 )
280     {
281         do
282         {
283             if( p_this->b_die != b_die )
284             {
285                 return 0;
286             }
287
288             /* Initialize file descriptor set */
289             FD_ZERO( &fds_w );
290             FD_SET( fd, &fds_w );
291             FD_ZERO( &fds_e );
292             FD_SET( fd, &fds_e );
293
294             /* We'll wait 0.5 second if nothing happens */
295             timeout.tv_sec = 0;
296             timeout.tv_usec = 500000;
297
298         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
299                  || ( i_ret < 0 && errno == EINTR ) );
300
301         if( i_ret < 0 )
302         {
303             msg_Err( p_this, "network select error (%s)", strerror(errno) );
304             return i_total > 0 ? i_total : -1;
305         }
306
307         if( ( i_send = send( fd, p_data, i_data, 0 ) ) < 0 )
308         {
309             /* XXX With udp for example, it will issue a message if the host
310              * isn't listening */
311             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
312             return i_total > 0 ? i_total : -1;
313         }
314
315         p_data += i_send;
316         i_data -= i_send;
317         i_total+= i_send;
318     }
319     return i_total;
320 }
321
322 char *__net_Gets( vlc_object_t *p_this, int fd )
323 {
324     char *psz_line = malloc( 1024 );
325     int  i_line = 0;
326     int  i_max = 1024;
327
328
329     for( ;; )
330     {
331         if( net_Read( p_this, fd, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
332         {
333             psz_line[i_line] = '\0';
334             break;
335         }
336         i_line++;
337
338         if( psz_line[i_line-1] == '\n' )
339         {
340             psz_line[i_line] = '\0';
341             break;
342         }
343
344         if( i_line >= i_max - 1 )
345         {
346             i_max += 1024;
347             psz_line = realloc( psz_line, i_max );
348         }
349     }
350
351     if( i_line <= 0 )
352     {
353         free( psz_line );
354         return NULL;
355     }
356
357     while( i_line >= 1 &&
358            ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
359     {
360         i_line--;
361         psz_line[i_line] = '\0';
362     }
363     return psz_line;
364 }
365
366 int net_Printf( vlc_object_t *p_this, int fd, char *psz_fmt, ... )
367 {
368     va_list args;
369     char    *psz;
370     int     i_size, i_ret;
371
372     va_start( args, psz_fmt );
373     vasprintf( &psz, psz_fmt, args );
374     va_end( args );
375
376     i_size = strlen( psz );
377
378     i_ret = __net_Write( p_this, fd, psz, i_size ) < i_size ? -1 : i_size;
379     free( psz );
380
381     return i_ret;
382 }