* ./Makefile.am: fixed rc compilation under mingw32/cygwin.
[vlc.git] / modules / access / udp.c
1 /*****************************************************************************
2  * udp.c: raw UDP access plug-in
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: udp.c,v 1.4 2002/11/12 13:57:12 sam Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/input.h>
36
37 #ifdef HAVE_UNISTD_H
38 #   include <unistd.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 )
40 #   include <io.h>
41 #endif
42
43 #ifdef WIN32
44 #   include <winsock2.h>
45 #   include <ws2tcpip.h>
46 #   ifndef IN_MULTICAST
47 #       define IN_MULTICAST(a) IN_CLASSD(a)
48 #   endif
49 #else
50 #   include <sys/socket.h>
51 #endif
52
53 #include "network.h"
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static int  Open       ( vlc_object_t * );
59 static void Close      ( vlc_object_t * );
60 static ssize_t Read    ( input_thread_t *, byte_t *, size_t );
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 vlc_module_begin();
66     set_description( _("raw UDP access module") );
67     set_capability( "access", 0 );
68     add_shortcut( "udp" );
69     add_shortcut( "udpstream" );
70     add_shortcut( "udp4" );
71     add_shortcut( "udp6" );
72     set_callbacks( Open, Close );
73 vlc_module_end();
74
75 /*****************************************************************************
76  * Open: open the socket
77  *****************************************************************************/
78 static int Open( vlc_object_t *p_this )
79 {
80     input_thread_t *    p_input = (input_thread_t *)p_this;
81     input_socket_t *    p_access_data;
82     module_t *          p_network;
83     char *              psz_network = "";
84     char *              psz_name = strdup(p_input->psz_name);
85     char *              psz_parser = psz_name;
86     char *              psz_server_addr = "";
87     char *              psz_server_port = "";
88     char *              psz_bind_addr = "";
89     char *              psz_bind_port = "";
90     int                 i_bind_port = 0, i_server_port = 0;
91     network_socket_t    socket_desc;
92
93     if( config_GetInt( p_input, "ipv4" ) )
94     {
95         psz_network = "ipv4";
96     }
97     if( config_GetInt( p_input, "ipv6" ) )
98     {
99         psz_network = "ipv6";
100     }
101
102     if( *p_input->psz_access )
103     {
104         /* Find out which shortcut was used */
105         if( !strncmp( p_input->psz_access, "udp6", 5 ) )
106         {
107             psz_network = "ipv6";
108         }
109         else if( !strncmp( p_input->psz_access, "udp4", 5 ) )
110         {
111             psz_network = "ipv4";
112         }
113     }
114
115     /* Parse psz_name syntax :
116      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
117
118     if( *psz_parser && *psz_parser != '@' )
119     {
120         /* Found server */
121         psz_server_addr = psz_parser;
122
123         while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
124         {
125             if( *psz_parser == '[' )
126             {
127                 /* IPv6 address */
128                 while( *psz_parser && *psz_parser != ']' )
129                 {
130                     psz_parser++;
131                 }
132             }
133             psz_parser++;
134         }
135
136         if( *psz_parser == ':' )
137         {
138             /* Found server port */
139             *psz_parser = '\0'; /* Terminate server name */
140             psz_parser++;
141             psz_server_port = psz_parser;
142
143             while( *psz_parser && *psz_parser != '@' )
144             {
145                 psz_parser++;
146             }
147         }
148     }
149
150     if( *psz_parser == '@' )
151     {
152         /* Found bind address or bind port */
153         *psz_parser = '\0'; /* Terminate server port or name if necessary */
154         psz_parser++;
155
156         if( *psz_parser && *psz_parser != ':' )
157         {
158             /* Found bind address */
159             psz_bind_addr = psz_parser;
160
161             while( *psz_parser && *psz_parser != ':' )
162             {
163                 if( *psz_parser == '[' )
164                 {
165                     /* IPv6 address */
166                     while( *psz_parser && *psz_parser != ']' )
167                     {
168                         psz_parser++;
169                     }
170                 }
171                 psz_parser++;
172             }
173         }
174
175         if( *psz_parser == ':' )
176         {
177             /* Found bind port */
178             *psz_parser = '\0'; /* Terminate bind address if necessary */
179             psz_parser++;
180
181             psz_bind_port = psz_parser;
182         }
183     }
184
185     /* Convert ports format */
186     if( *psz_server_port )
187     {
188         i_server_port = strtol( psz_server_port, &psz_parser, 10 );
189         if( *psz_parser )
190         {
191             msg_Err( p_input, "cannot parse server port near %s", psz_parser );
192             free(psz_name);
193             return( -1 );
194         }
195     }
196
197     if( *psz_bind_port )
198     {
199         i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
200         if( *psz_parser )
201         {
202             msg_Err( p_input, "cannot parse bind port near %s", psz_parser );
203             free(psz_name);
204             return( -1 );
205         }
206     }
207
208     p_input->pf_read = Read;
209     p_input->pf_set_program = input_SetProgram;
210     p_input->pf_set_area = NULL;
211     p_input->pf_seek = NULL;
212
213     vlc_mutex_lock( &p_input->stream.stream_lock );
214     p_input->stream.b_pace_control = 0;
215     p_input->stream.b_seekable = 0;
216     p_input->stream.p_selected_area->i_tell = 0;
217     p_input->stream.i_method = INPUT_METHOD_NETWORK;
218     vlc_mutex_unlock( &p_input->stream.stream_lock );
219
220     if( *psz_server_addr || i_server_port )
221     {
222         msg_Err( p_input, "this UDP syntax is deprecated; the server argument will be");
223         msg_Err( p_input, "ignored (%s:%d). If you wanted to enter a multicast address",
224                           psz_server_addr, i_server_port);
225         msg_Err( p_input, "or local port, type : %s:@%s:%d",
226                           *p_input->psz_access ? p_input->psz_access : "udp",
227                           psz_server_addr, i_server_port );
228
229         i_server_port = 0;
230         psz_server_addr = "";
231     }
232  
233     msg_Dbg( p_input, "opening server=%s:%d local=%s:%d",
234              psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );
235
236     /* Prepare the network_socket_t structure */
237     socket_desc.i_type = NETWORK_UDP;
238     socket_desc.psz_bind_addr = psz_bind_addr;
239     socket_desc.i_bind_port = i_bind_port;
240     socket_desc.psz_server_addr = psz_server_addr;
241     socket_desc.i_server_port = i_server_port;
242
243     /* Find an appropriate network module */
244     p_input->p_private = (void*) &socket_desc;
245     p_network = module_Need( p_input, "network", psz_network );
246     free(psz_name);
247     if( p_network == NULL )
248     {
249         return( -1 );
250     }
251     module_Unneed( p_input, p_network );
252     
253     p_access_data = malloc( sizeof(input_socket_t) );
254     p_input->p_access_data = (access_sys_t *)p_access_data;
255
256     if( p_access_data == NULL )
257     {
258         msg_Err( p_input, "out of memory" );
259         return( -1 );
260     }
261
262     p_access_data->i_handle = socket_desc.i_handle;
263     p_input->i_mtu = socket_desc.i_mtu;
264
265     return( 0 );
266 }
267
268 /*****************************************************************************
269  * Close: free unused data structures
270  *****************************************************************************/
271 static void Close( vlc_object_t *p_this )
272 {
273     input_thread_t *  p_input = (input_thread_t *)p_this;
274     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
275
276     msg_Info( p_input, "closing UDP target `%s'", p_input->psz_source );
277
278 #ifdef UNDER_CE
279     CloseHandle( (HANDLE)p_access_data->i_handle );
280 #elif defined( WIN32 )
281     closesocket( p_access_data->i_handle );
282 #else
283     close( p_access_data->i_handle );
284 #endif
285
286     free( p_access_data );
287 }
288
289 /*****************************************************************************
290  * Read: read on a file descriptor, checking b_die periodically
291  *****************************************************************************/
292 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
293 {
294 #ifdef UNDER_CE
295     return -1;
296
297 #else
298     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
299     struct timeval  timeout;
300     fd_set          fds;
301     int             i_ret;
302
303     /* Initialize file descriptor set */
304     FD_ZERO( &fds );
305     FD_SET( p_access_data->i_handle, &fds );
306
307     /* We'll wait 0.5 second if nothing happens */
308     timeout.tv_sec = 0;
309     timeout.tv_usec = 500000;
310
311     /* Find if some data is available */
312     i_ret = select( p_access_data->i_handle + 1, &fds,
313                     NULL, NULL, &timeout );
314
315     if( i_ret == -1 && errno != EINTR )
316     {
317         msg_Err( p_input, "network select error (%s)", strerror(errno) );
318     }
319     else if( i_ret > 0 )
320     {
321         ssize_t i_recv = recv( p_access_data->i_handle, p_buffer, i_len, 0 );
322
323         if( i_recv > 0 )
324         {
325             vlc_mutex_lock( &p_input->stream.stream_lock );
326             p_input->stream.p_selected_area->i_tell += i_recv;
327             vlc_mutex_unlock( &p_input->stream.stream_lock );
328         }
329
330         if( i_recv < 0 )
331         {
332             msg_Err( p_input, "recv failed (%s)", strerror(errno) );
333         }
334
335         return i_recv;
336     }
337
338     return 0;
339
340 #endif
341 }
342