input_network.c 8.82 KB
Newer Older
1
2
/*****************************************************************************
 * network.c: functions to read from the network
Michel Kaempf's avatar
Michel Kaempf committed
3
 * Manages a socket.
4
5
6
 *****************************************************************************
 * Copyright (C) 1999, 2000 VideoLAN
 *
7
8
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *          Benot Steiner <benny@via.ecp.fr>
9
10
11
12
13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 * 
15
16
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
19
 *
20
21
22
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
24

25
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
26
 * Preamble
27
 *****************************************************************************/
28
29
#include "defs.h"

30
31
32
#include <sys/types.h>                        /* on BSD, uio.h needs types.h */
#include <sys/uio.h>                                            /* "input.h" */
#include <string.h>                                            /* strerror() */
33
#include <unistd.h>                                               /* close() */
34
35
36
#include <errno.h>                                                  /* errno */
#include <sys/time.h>                                   /* "input_network.h" */

37
#if defined(SYS_BSD) || defined(SYS_BEOS)
38
39
40
41
#include <sys/socket.h>                                   /* struct sockaddr */
#endif

#include <netdb.h>     /* servent, getservbyname(), hostent, gethostbyname() */
42
#include <netinet/in.h>                     /* sockaddr_in, htons(), htonl() */
Michel Kaempf's avatar
Michel Kaempf committed
43
44

#include "config.h"
45
46
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
47
48
49
50
51
52
53
54
#include "mtime.h"
#include "netutils.h"

#include "input.h"
#include "input_network.h"
#include "input_vlan.h"

#include "intf_msg.h"
55
#include "plugins.h"
56
#include "main.h"
Michel Kaempf's avatar
Michel Kaempf committed
57

58
/*****************************************************************************
59
 * input_NetworkOpen: initialize a network stream
60
 *****************************************************************************/
61
int input_NetworkOpen( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
62
{
63
    int                     i_socket_option;
Michel Kaempf's avatar
Michel Kaempf committed
64
    struct sockaddr_in      sa_in;
65
    char                    psz_hostname[INPUT_MAX_SOURCE_LENGTH];
Michel Kaempf's avatar
Michel Kaempf committed
66

67
    /* First and foremost, in the VLAN method, join the desired VLAN. */
Michel Kaempf's avatar
Michel Kaempf committed
68
69
    if( p_input->i_method == INPUT_METHOD_TS_VLAN_BCAST )
    {
70
        if( input_VlanJoin( p_input->i_vlan ) )
Michel Kaempf's avatar
Michel Kaempf committed
71
        {
72
73
74
            intf_ErrMsg("error: can't join vlan %d\n", p_input->i_vlan);
            return( 1 );
        }
Michel Kaempf's avatar
Michel Kaempf committed
75
76
    }

77
    /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
Michel Kaempf's avatar
Michel Kaempf committed
78
79
80
     * protocol */
    if( (p_input->i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == (-1) )
    {
81
82
        intf_ErrMsg("error: can't create socket (%s)\n", strerror(errno));
        return( 1 );
Michel Kaempf's avatar
Michel Kaempf committed
83
84
    }

85
86
    /*
     * Set up the options of the socket
87
     */
Michel Kaempf's avatar
Michel Kaempf committed
88

89
    /* Set SO_REUSEADDR option which allows to re-bind() a busy port */
Michel Kaempf's avatar
Michel Kaempf committed
90
91
92
93
94
95
96
    i_socket_option = 1;
    if( setsockopt( p_input->i_handle,
                    SOL_SOCKET,
                    SO_REUSEADDR,
                    &i_socket_option,
                    sizeof( i_socket_option ) ) == (-1) )
    {
97
        intf_ErrMsg("error: can't configure socket (SO_REUSEADDR: %s)\n", strerror(errno));
Michel Kaempf's avatar
Michel Kaempf committed
98
        close( p_input->i_handle );
99
        return( 1 );
Michel Kaempf's avatar
Michel Kaempf committed
100
101
    }

102
#ifndef SYS_BEOS
Michel Kaempf's avatar
Michel Kaempf committed
103
104
105
106
107
108
109
110
111
    /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
     * packet loss caused by scheduling problems */
    i_socket_option = 524288;
    if( setsockopt( p_input->i_handle,
                    SOL_SOCKET,
                    SO_RCVBUF,
                    &i_socket_option,
                    sizeof( i_socket_option ) ) == (-1) )
    {
112
        intf_ErrMsg("error: can't configure socket (SO_RCVBUF: %s)\n", strerror(errno));
Michel Kaempf's avatar
Michel Kaempf committed
113
        close( p_input->i_handle );
114
        return( 1 );
Michel Kaempf's avatar
Michel Kaempf committed
115
    }
116
#endif /* SYS_BEOS */
117
118

    /*
119
120
     * Bind the socket
     */
Michel Kaempf's avatar
Michel Kaempf committed
121

122
123
    /* Use default port if not specified */
    if( p_input->i_port == 0 )
Michel Kaempf's avatar
Michel Kaempf committed
124
    {
125
        p_input->i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
Michel Kaempf's avatar
Michel Kaempf committed
126
127
    }

128
129
130
131
132
133
    /* See if the user requested broadcast method */
    if( main_GetIntVariable( INPUT_BROADCAST_VAR, INPUT_BROADCAST_DEFAULT ) )
    {
        p_input->i_method = INPUT_METHOD_TS_BCAST;
    }

Michel Kaempf's avatar
Michel Kaempf committed
134
135
136
    /* Find the address. */
    switch( p_input->i_method )
    {
137
138
139
140
141
142
    case INPUT_METHOD_TS_BCAST:
    case INPUT_METHOD_TS_VLAN_BCAST:
        /* In that case, we have to bind with the broadcast address.
         * broadcast addresses are very hard to find and depends on
         * implementation, so we thought using a #define would be much
         * simpler. */
Michel Kaempf's avatar
Michel Kaempf committed
143
#ifdef INPUT_BCAST_ADDR
144
145
146
147
148
        if( BuildInetAddr( &sa_in, INPUT_BCAST_ADDR, p_input->i_port ) == (-1) )
        {
            close( p_input->i_handle );
            return( 1 );
        }
Michel Kaempf's avatar
Michel Kaempf committed
149
#else
150
151
152
153
        /* We bind with any address. Security problem ! */
        if( BuildInetAddr( &sa_in, NULL, p_input->i_port ) == (-1) )
        {
            close( p_input->i_handle );
154
            return( -1 );
155
        }
Michel Kaempf's avatar
Michel Kaempf committed
156
#endif
157
        break;
Michel Kaempf's avatar
Michel Kaempf committed
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    case INPUT_METHOD_TS_UCAST:
        /* Unicast: bind with the local address. */
        if( gethostname( psz_hostname, sizeof( psz_hostname ) ) == (-1) )
        {
            intf_ErrMsg("error: can't get hostname (%s)\n", strerror(errno));
            close( p_input->i_handle );
            return( 1 );
        }
        if( BuildInetAddr( &sa_in, psz_hostname, p_input->i_port ) == (-1) )
        {
            close( p_input->i_handle );
            return( 1 );
        }
        break;

    case INPUT_METHOD_TS_MCAST:
        /* Multicast: bind with 239.0.0.1. */
        if( BuildInetAddr( &sa_in, "239.0.0.1", p_input->i_port ) == (-1) )
        {
            close( p_input->i_handle );
            return( 1 );
        }
        break;
Michel Kaempf's avatar
Michel Kaempf committed
182
183
184
    }

    /* Effectively bind the socket. */
185
    if( bind( p_input->i_handle, (struct sockaddr *) &sa_in, sizeof( sa_in ) ) < 0 )
Michel Kaempf's avatar
Michel Kaempf committed
186
    {
187
        intf_ErrMsg("error: can't bind socket (%s)\n", strerror(errno));
Michel Kaempf's avatar
Michel Kaempf committed
188
        close( p_input->i_handle );
189
        return( 1 );
Michel Kaempf's avatar
Michel Kaempf committed
190
191
    }

192
    /*
193
194
     * Connect the socket to the remote server
     */
Michel Kaempf's avatar
Michel Kaempf committed
195

196
    /* Use default host if not specified */
197
    if( p_input->p_source == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
198
    {
199
        p_input->p_source = main_GetPszVariable( INPUT_SERVER_VAR, INPUT_SERVER_DEFAULT );
Michel Kaempf's avatar
Michel Kaempf committed
200
    }
201

202
    if( BuildInetAddr( &sa_in, p_input->p_source, htons(0) ) == (-1) )
Michel Kaempf's avatar
Michel Kaempf committed
203
    {
204
        close( p_input->i_handle );
Michel Kaempf's avatar
Michel Kaempf committed
205
206
207
        return( -1 );
    }

208
    /* Connect the socket. */
209
    if( connect( p_input->i_handle, (struct sockaddr *) &sa_in,
Michel Kaempf's avatar
Michel Kaempf committed
210
211
                 sizeof( sa_in ) ) == (-1) )
    {
212
        intf_ErrMsg("error: can't connect socket\n" );
Michel Kaempf's avatar
Michel Kaempf committed
213
        close( p_input->i_handle );
214
        return( 1 );
Michel Kaempf's avatar
Michel Kaempf committed
215
216
217
218
    }
    return( 0 );
}

219
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
220
 * input_NetworkRead: read a stream from the network
221
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
222
223
224
225
226
 * Wait for data during up to 1 second and then abort if none is arrived. The
 * number of bytes read is returned or -1 if an error occurs (so 0 is returned
 * after a timeout)
 * We don't have to make any test on presentation times, since we suppose
 * the network server sends us data when we need it.
227
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
228
229
230
231
232
233
234
235
236
237
int input_NetworkRead( input_thread_t *p_input, const struct iovec *p_vector,
                       size_t i_count )
{
    fd_set rfds;
    struct timeval tv;
    int i_rc;

    /* Watch the given fd to see when it has input */
    FD_ZERO(&rfds);
    FD_SET(p_input->i_handle, &rfds);
238

Michel Kaempf's avatar
Michel Kaempf committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
    /* Wait up to 1 second */
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    i_rc = select(p_input->i_handle+1, &rfds, NULL, NULL, &tv);

    if( i_rc > 0 )
    {
        /* Data were received before timeout */
        i_rc = readv( p_input->i_handle, p_vector, i_count );
    }

    return( i_rc );
}

254
/*****************************************************************************
255
 * input_NetworkClose: close a network stream
256
 *****************************************************************************/
257
void input_NetworkClose( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
258
259
260
261
{
    /* Close local socket. */
    if( p_input->i_handle )
    {
262
        close( p_input->i_handle );
Michel Kaempf's avatar
Michel Kaempf committed
263
264
    }

265
    /* Leave vlan if required */
Michel Kaempf's avatar
Michel Kaempf committed
266
    if( p_input->i_method == INPUT_METHOD_TS_VLAN_BCAST )
267
    {
268
        input_VlanLeave( p_input->i_vlan );
269
    }
Michel Kaempf's avatar
Michel Kaempf committed
270
}
271