X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fnetwork%2Fhttpd.c;h=f40f9cfcb6a806de9ae7ca77fa8bf0edf91c696d;hb=4a623b2496a2bfcf7967228c0d316a5184a7181d;hp=6545e3e1d71a9cce7f81797e0a468f5e2fa336d6;hpb=eedba76b51be5583dabed93222b570a8bc05cabe;p=vlc diff --git a/src/network/httpd.c b/src/network/httpd.c index 6545e3e1d7..f40f9cfcb6 100644 --- a/src/network/httpd.c +++ b/src/network/httpd.c @@ -2,6 +2,7 @@ * httpd.c ***************************************************************************** * Copyright (C) 2004-2006 the VideoLAN team + * Copyright © 2004-2007 Rémi Denis-Courmont * $Id$ * * Authors: Laurent Aimar @@ -35,6 +36,7 @@ #include #include #include +#include #include "../libvlc.h" #include @@ -48,6 +50,10 @@ # include #endif +#ifdef HAVE_POLL +# include +#endif + #if defined( UNDER_CE ) # include #elif defined( WIN32 ) @@ -87,11 +93,8 @@ struct httpd_host_t /* address/port and socket for listening at connections */ char *psz_hostname; int i_port; - int *fd; - - /* Statistics */ - counter_t *p_active_counter; - counter_t *p_total_counter; + int *fds; + unsigned nfd; vlc_mutex_t lock; @@ -159,8 +162,6 @@ struct httpd_client_t int i_ref; - struct sockaddr_storage sock; - int i_sock_size; int fd; int i_mode; @@ -187,63 +188,6 @@ struct httpd_client_t /***************************************************************************** * Various functions *****************************************************************************/ -/*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ -static void b64_decode( char *restrict dest, const char *restrict src ) -{ - int i_level; - int last = 0; - int b64[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ - 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ - 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ - -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ - 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ - }; - - for( i_level = 0; *src != '\0'; src++ ) - { - int c; - - c = b64[(unsigned int)*src]; - if( c == -1 ) - { - continue; - } - - switch( i_level ) - { - case 0: - i_level++; - break; - case 1: - *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 ); - i_level++; - break; - case 2: - *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); - i_level++; - break; - case 3: - *dest++ = ( ( last &0x03 ) << 6 ) | c; - i_level = 0; - } - last = c; - } - - *dest = '\0'; -} - static struct { const char *psz_ext; @@ -703,6 +647,7 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, { httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys; char *p_body; + (void)cl; if( answer == NULL || query == NULL ) { @@ -1051,8 +996,8 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, } /* to be sure to avoid multiple creation */ - var_Create( p_this->p_libvlc_global, "httpd_mutex", VLC_VAR_MUTEX ); - var_Get( p_this->p_libvlc_global, "httpd_mutex", &lockval ); + var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX ); + var_Get( p_this->p_libvlc, "httpd_mutex", &lockval ); vlc_mutex_lock( lockval.p_address ); if( !(httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE )) ) @@ -1131,12 +1076,13 @@ httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, vlc_mutex_init( httpd, &host->lock ); host->i_ref = 1; - host->fd = net_ListenTCP( p_this, psz_host, i_port ); - if( host->fd == NULL ) + host->fds = net_ListenTCP( p_this, psz_host, i_port ); + if( host->fds == NULL ) { msg_Err( p_this, "cannot create socket(s) for HTTP host" ); goto error; } + for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++); host->i_port = i_port; host->psz_hostname = psz_host; @@ -1174,7 +1120,7 @@ error: if( host != NULL ) { - net_ListenClose( host->fd ); + net_ListenClose( host->fds ); vlc_mutex_destroy( &host->lock ); vlc_object_destroy( host ); } @@ -1192,7 +1138,7 @@ void httpd_HostDelete( httpd_host_t *host ) vlc_value_t lockval; int i; - var_Get( httpd->p_libvlc_global, "httpd_mutex", &lockval ); + var_Get( httpd->p_libvlc, "httpd_mutex", &lockval ); vlc_mutex_lock( lockval.p_address ); host->i_ref--; @@ -1205,7 +1151,7 @@ void httpd_HostDelete( httpd_host_t *host ) } TAB_REMOVE( httpd->i_host, httpd->host, host ); - host->b_die = 1; + vlc_object_kill( host ); vlc_thread_join( host ); msg_Dbg( host, "HTTP host removed" ); @@ -1228,7 +1174,7 @@ void httpd_HostDelete( httpd_host_t *host ) if( host->p_tls != NULL) tls_ServerDelete( host->p_tls ); - net_ListenClose( host->fd ); + net_ListenClose( host->fds ); free( host->psz_hostname ); vlc_mutex_destroy( &host->lock ); @@ -1429,28 +1375,28 @@ void httpd_MsgAdd( httpd_message_t *msg, const char *name, const char *psz_value char *value = NULL; va_start( args, psz_value ); -#if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined(SYS_BEOS) - vasprintf( &value, psz_value, args ); -#else - { - int i_size = strlen( psz_value ) + 4096; /* FIXME stupid system */ - value = calloc( i_size, sizeof( char ) ); - vsnprintf( value, i_size, psz_value, args ); - value[i_size - 1] = 0; - } -#endif + if( vasprintf( &value, psz_value, args ) == -1 ) + value = NULL; va_end( args ); + if( value == NULL ) + return; + name = strdup( name ); + if( name == NULL ) + { + free( value ); + return; + } TAB_APPEND( msg->i_name, msg->name, (char*)name ); TAB_APPEND( msg->i_value, msg->value, value ); } -static void httpd_ClientInit( httpd_client_t *cl ) +static void httpd_ClientInit( httpd_client_t *cl, mtime_t now ) { cl->i_state = HTTPD_CLIENT_RECEIVING; - cl->i_activity_date = mdate(); + cl->i_activity_date = now; cl->i_activity_timeout = I64C(10000000); cl->i_buffer_size = HTTPD_CL_BUFSIZE; cl->i_buffer = 0; @@ -1502,9 +1448,7 @@ static void httpd_ClientClean( httpd_client_t *cl ) } } -static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock, - int i_sock_size, - tls_session_t *p_tls ) +static httpd_client_t *httpd_ClientNew( int fd, tls_session_t *p_tls, mtime_t now ) { httpd_client_t *cl = malloc( sizeof( httpd_client_t ) ); @@ -1512,12 +1456,10 @@ static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock, cl->i_ref = 0; cl->fd = fd; - memcpy( &cl->sock, sock, sizeof( cl->sock ) ); - cl->i_sock_size = i_sock_size; cl->url = NULL; cl->p_tls = p_tls; - httpd_ClientInit( cl ); + httpd_ClientInit( cl, now ); return cl; } @@ -1844,7 +1786,6 @@ static void httpd_ClientRecv( httpd_client_t *cl ) cl->i_state = HTTPD_CLIENT_DEAD; } } - cl->i_activity_date = mdate(); /* XXX: for QT I have to disable timeout. Try to find why */ if( cl->query.i_proto == HTTPD_PROTO_RTSP ) @@ -1927,7 +1868,6 @@ static void httpd_ClientSend( httpd_client_t *cl ) cl->i_buffer_size - cl->i_buffer ); if( i_len >= 0 ) { - cl->i_activity_date = mdate(); cl->i_buffer += i_len; if( cl->i_buffer >= cl->i_buffer_size ) @@ -2018,24 +1958,11 @@ static void httpd_ClientTlsHsOut( httpd_client_t *cl ) static void httpd_HostThread( httpd_host_t *host ) { tls_session_t *p_tls = NULL; - - host->p_total_counter = stats_CounterCreate( host, - VLC_VAR_INTEGER, STATS_COUNTER ); - host->p_active_counter = stats_CounterCreate( host, - VLC_VAR_INTEGER, STATS_COUNTER ); + counter_t *p_total_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); + counter_t *p_active_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); while( !host->b_die ) { - struct timeval timeout; - fd_set fds_read; - fd_set fds_write; - /* FIXME: (too) many int variables */ - int fd, i_fd; - int i_handle_max = -1; - int i_ret; - int i_client; - int b_low_delay = 0; - if( host->i_url <= 0 ) { /* 0.2s */ @@ -2043,50 +1970,55 @@ static void httpd_HostThread( httpd_host_t *host ) continue; } - /* built a set of handle to select */ - FD_ZERO( &fds_read ); - FD_ZERO( &fds_write ); - - for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ ) - { - FD_SET( fd, &fds_read ); - if( fd > i_handle_max ) - i_handle_max = fd; - } - /* prepare a new TLS session */ if( ( p_tls == NULL ) && ( host->p_tls != NULL ) ) p_tls = tls_ServerSessionPrepare( host->p_tls ); + struct pollfd ufd[host->nfd + host->i_client]; + unsigned nfd; + for( nfd = 0; nfd < host->nfd; nfd++ ) + { + ufd[nfd].fd = host->fds[nfd]; + ufd[nfd].events = POLLIN; + ufd[nfd].revents = 0; + } + /* add all socket that should be read/write and close dead connection */ vlc_mutex_lock( &host->lock ); - for( i_client = 0; i_client < host->i_client; i_client++ ) + mtime_t now = mdate(); + vlc_bool_t b_low_delay = VLC_FALSE; + + for(int i_client = 0; i_client < host->i_client; i_client++ ) { httpd_client_t *cl = host->client[i_client]; - if( cl->i_ref < 0 || ( cl->i_ref == 0 && ( cl->i_state == HTTPD_CLIENT_DEAD || ( cl->i_activity_timeout > 0 && - cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) ) + cl->i_activity_date+cl->i_activity_timeout < now) ) ) ) { httpd_ClientClean( cl ); - stats_UpdateInteger( host, host->p_active_counter, -1, NULL ); + stats_UpdateInteger( host, p_active_counter, -1, NULL ); TAB_REMOVE( host->i_client, host->client, cl ); free( cl ); i_client--; continue; } - else if( ( cl->i_state == HTTPD_CLIENT_RECEIVING ) + + struct pollfd *pufd = ufd + nfd; + assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0]))); + + pufd->fd = cl->fd; + pufd->events = pufd->revents = 0; + + if( ( cl->i_state == HTTPD_CLIENT_RECEIVING ) || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) ) { - FD_SET( cl->fd, &fds_read ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events = POLLIN; } else if( ( cl->i_state == HTTPD_CLIENT_SENDING ) || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) ) { - FD_SET( cl->fd, &fds_write ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events = POLLOUT; } else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) { @@ -2188,10 +2120,9 @@ static void httpd_HostThread( httpd_host_t *host ) { vlc_bool_t b_auth_failed = VLC_FALSE; vlc_bool_t b_hosts_failed = VLC_FALSE; - int i; /* Search the url and trigger callbacks */ - for( i = 0; i < host->i_url; i++ ) + for(int i = 0; i < host->i_url; i++ ) { httpd_url_t *url = host->url[i]; @@ -2203,54 +2134,51 @@ static void httpd_HostThread( httpd_host_t *host ) { char ip[NI_MAXNUMERICHOST]; - if( httpd_ClientIP( cl, ip ) != NULL ) + if( ( httpd_ClientIP( cl, ip ) == NULL ) + || ACL_Check( url->p_acl, ip ) ) { - if( ACL_Check( url->p_acl, ip ) ) - { - b_hosts_failed = VLC_TRUE; - break; - } - } - else b_hosts_failed = VLC_TRUE; + break; + } } if( answer && ( *url->psz_user || *url->psz_password ) ) { /* create the headers */ const char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */ - char *auth; - char *id; + char *user = NULL, *pass = NULL; - asprintf( &id, "%s:%s", url->psz_user, url->psz_password ); if( b64 != NULL - && !strncasecmp( b64, "BASIC", 5 ) ) + && !strncasecmp( b64, "BASIC", 5 ) ) { b64 += 5; while( *b64 == ' ' ) - { b64++; + + user = vlc_b64_decode( b64 ); + if (user != NULL) + { + pass = strchr (user, ':'); + if (pass != NULL) + *pass++ = '\0'; } - auth = malloc( strlen(b64) + 1 ); - b64_decode( auth, b64 ); - } - else - { - auth = strdup( "" ); } - if( strcmp( id, auth ) ) + if ((user == NULL) || (pass == NULL) + || strcmp (user, url->psz_user) + || strcmp (pass, url->psz_password)) { - httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user ); + httpd_MsgAdd( answer, + "WWW-Authenticate", + "Basic realm=\"%s\"", + url->psz_user ); /* We fail for all url */ b_auth_failed = VLC_TRUE; - free( id ); - free( auth ); + free( user ); break; } - free( id ); - free( auth ); + free( user ); } if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) ) @@ -2394,7 +2322,7 @@ static void httpd_HostThread( httpd_host_t *host ) &cl->answer, &cl->query ); if( cl->answer.i_type != HTTPD_MSG_NONE ) { - /* we have new data, so reenter send mode */ + /* we have new data, so re-enter send mode */ cl->i_buffer = 0; cl->p_buffer = cl->answer.p_body; cl->i_buffer_size = cl->answer.i_body; @@ -2413,117 +2341,45 @@ static void httpd_HostThread( httpd_host_t *host ) if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING ) { - FD_SET( cl->fd, &fds_read ); - i_handle_max = __MAX( i_handle_max, cl->fd ); + pufd->events |= POLLIN; } + + if (pufd->events != 0) + nfd++; } vlc_mutex_unlock( &host->lock ); /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */ - timeout.tv_sec = 0; - timeout.tv_usec = b_low_delay ? 20000 : 100000; - - i_ret = select( i_handle_max + 1, - &fds_read, &fds_write, NULL, &timeout ); - - if( (i_ret == -1) && (errno != EINTR) ) + switch( poll( ufd, nfd, b_low_delay ? 20 : 100) ) { - msg_Warn( host, "select error: %s", net_strerror( net_errno ) ); - msleep( 1000 ); - continue; - } - else if( i_ret <= 0 ) - { - continue; - } - - /* accept new connections */ - for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ ) - { - if( FD_ISSET( fd, &fds_read ) ) - { - socklen_t i_sock_size = sizeof( struct sockaddr_storage ); - struct sockaddr_storage sock; - - fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size ); - - if( fd >= 0 ) + case -1: + if (errno != EINTR) { - int i_state = 1; - - setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &i_state, sizeof (i_state)); - i_state = 0; - - /* set this new socket non-block */ -#if defined( WIN32 ) || defined( UNDER_CE ) - { - unsigned long i_dummy = 1; - ioctlsocket( fd, FIONBIO, &i_dummy ); - } -#else - fcntl( fd, F_SETFD, FD_CLOEXEC ); - { - int i_val = fcntl( fd, F_GETFL ); - fcntl( fd, F_SETFL, - O_NONBLOCK | ((i_val != -1) ? i_val : 0) ); - } - - if( fd >= FD_SETSIZE ) - { - net_Close( fd ); - fd = -1; - } - else -#endif - if( p_tls != NULL) - { - switch ( tls_ServerSessionHandshake( p_tls, fd ) ) - { - case -1: - msg_Err( host, "Rejecting TLS connection" ); - net_Close( fd ); - fd = -1; - p_tls = NULL; - break; - - case 1: /* missing input - most likely */ - i_state = HTTPD_CLIENT_TLS_HS_IN; - break; - - case 2: /* missing output */ - i_state = HTTPD_CLIENT_TLS_HS_OUT; - break; - } - } - - if( fd >= 0 ) - { - httpd_client_t *cl; - char ip[NI_MAXNUMERICHOST]; - stats_UpdateInteger( host, host->p_total_counter, - 1, NULL ); - stats_UpdateInteger( host, host->p_active_counter, - 1, NULL ); - cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls ); - httpd_ClientIP( cl, ip ); - msg_Dbg( host, "Connection from %s", ip ); - p_tls = NULL; - vlc_mutex_lock( &host->lock ); - TAB_APPEND( host->i_client, host->client, cl ); - vlc_mutex_unlock( &host->lock ); - - if( i_state != 0 ) - cl->i_state = i_state; // override state for TLS - } + /* This is most likely a bug */ + msg_Err( host, "polling error: %s", strerror (errno)); + msleep( 1000 ); } - } + case 0: + continue; } - /* now try all others socket */ + /* Handle client sockets */ vlc_mutex_lock( &host->lock ); - for( i_client = 0; i_client < host->i_client; i_client++ ) + now = mdate(); + for( int i_client = 0; i_client < host->i_client; i_client++ ) { httpd_client_t *cl = host->client[i_client]; + const struct pollfd *pufd = &ufd[host->nfd + i_client]; + + assert( pufd < &ufd[sizeof(ufd) / sizeof(ufd[0])] ); + + if( cl->fd != pufd->fd ) + continue; // we were not waiting for this client + if( pufd->revents == 0 ) + continue; // no event received + + cl->i_activity_date = now; + if( cl->i_state == HTTPD_CLIENT_RECEIVING ) { httpd_ClientRecv( cl ); @@ -2543,16 +2399,76 @@ static void httpd_HostThread( httpd_host_t *host ) if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING && - FD_ISSET( cl->fd, &fds_read ) ) + (pufd->revents & POLLIN) ) { cl->b_read_waiting = VLC_TRUE; } } vlc_mutex_unlock( &host->lock ); + + /* Handle server sockets (accept new connections) */ + for( nfd = 0; nfd < host->nfd; nfd++ ) + { + httpd_client_t *cl; + int i_state = -1; + + assert (ufd[nfd].fd == host->fds[nfd]); + + if( ufd[nfd].revents == 0 ) + continue; + + /* */ + int kludge[] = { ufd[nfd].fd, -1 }; + int fd = net_Accept( host, kludge, 0 ); + if( fd < 0 ) + continue; + + if( p_tls != NULL ) + { + switch( tls_ServerSessionHandshake( p_tls, fd ) ) + { + case -1: + msg_Err( host, "Rejecting TLS connection" ); + net_Close( fd ); + fd = -1; + p_tls = NULL; + break; + + case 1: /* missing input - most likely */ + i_state = HTTPD_CLIENT_TLS_HS_IN; + break; + + case 2: /* missing output */ + i_state = HTTPD_CLIENT_TLS_HS_OUT; + break; + } + + if( (p_tls == NULL) != (host->p_tls == NULL) ) + break; // wasted TLS session, cannot accept() anymore + } + + stats_UpdateInteger( host, p_total_counter, 1, NULL ); + stats_UpdateInteger( host, p_active_counter, 1, NULL ); + cl = httpd_ClientNew( fd, p_tls, now ); + p_tls = NULL; + vlc_mutex_lock( &host->lock ); + TAB_APPEND( host->i_client, host->client, cl ); + vlc_mutex_unlock( &host->lock ); + if( i_state != -1 ) + cl->i_state = i_state; // override state for TLS + + if (host->p_tls != NULL) + break; // cannot accept further without new TLS session + } + } if( p_tls != NULL ) tls_ServerSessionClose( p_tls ); + if( p_total_counter ) + stats_CounterClean( p_total_counter ); + if( p_active_counter ) + stats_CounterClean( p_active_counter ); } #else /* ENABLE_HTTPD */