X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fhttpd.c;h=b3e4d001be33b38018f4e027e1513460ca8feb3c;hb=e6a2e27bbb326744bbcfe0e464c32068dc41cbb4;hp=92f7ddd2c6feacc03886573e587a8f5b39ad402d;hpb=24a100c0362b3a73b3ae304f4a212981863e053c;p=vlc diff --git a/src/misc/httpd.c b/src/misc/httpd.c index 92f7ddd2c6..b3e4d001be 100644 --- a/src/misc/httpd.c +++ b/src/misc/httpd.c @@ -1,11 +1,11 @@ /***************************************************************************** * httpd.c ***************************************************************************** - * Copyright (C) 2004 VideoLAN + * Copyright (C) 2004-2005 the VideoLAN team * $Id$ * * Authors: Laurent Aimar - * Remi Denis-Courmont + * Rémi Denis-Courmont * * 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 @@ -25,15 +25,23 @@ #include #include +#ifdef ENABLE_HTTPD + #include "vlc_httpd.h" #include "network.h" +#include "vlc_tls.h" +#include "vlc_acl.h" #include #include + #ifdef HAVE_UNISTD_H # include #endif -#include + +#ifdef HAVE_FCNTL_H +# include +#endif #if defined( UNDER_CE ) # include @@ -43,16 +51,13 @@ #else # include /* hostent ... */ # include +/* FIXME: should not be needed */ # include # ifdef HAVE_ARPA_INET_H # include /* inet_ntoa(), inet_aton() */ # endif #endif -#if defined(WIN32) -static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}}; -#endif - #if 0 typedef struct httpd_t httpd_t; @@ -204,9 +209,9 @@ struct httpd_host_t int i_ref; /* address/port and socket for listening at connections */ - struct sockaddr_storage sock; - int i_sock_size; - int fd; + char *psz_hostname; + int i_port; + int *fd; vlc_mutex_t lock; @@ -219,6 +224,9 @@ struct httpd_host_t int i_client; httpd_client_t **client; + + /* TLS data */ + tls_server_t *p_tls; }; struct httpd_url_t @@ -227,9 +235,10 @@ struct httpd_url_t vlc_mutex_t lock; - char *psz_url; - char *psz_user; - char *psz_password; + char *psz_url; + char *psz_user; + char *psz_password; + vlc_acl_t *p_acl; struct { @@ -250,6 +259,9 @@ enum HTTPD_CLIENT_WAITING, HTTPD_CLIENT_DEAD, + + HTTPD_CLIENT_TLS_HS_IN, + HTTPD_CLIENT_TLS_HS_OUT }; /* mode */ enum @@ -284,6 +296,9 @@ struct httpd_client_t /* */ httpd_message_t query; /* client -> httpd */ httpd_message_t answer; /* httpd -> client */ + + /* TLS data */ + tls_session_t *p_tls; }; @@ -367,6 +382,7 @@ static struct { ".jpg", "image/jpeg" }, { ".jpeg", "image/jpeg" }, { ".png", "image/png" }, + { ".mpjpeg","multipart/x-mixed-replace; boundary=This Random String" }, /* media mime */ { ".avi", "video/avi" }, @@ -450,7 +466,7 @@ static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, if( query->i_type != HTTPD_MSG_HEAD ) { - char *psz_args = query->psz_args; + uint8_t *psz_args = query->psz_args; if( query->i_type == HTTPD_MSG_POST ) { /* Check that */ @@ -475,13 +491,14 @@ static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_file_t *httpd_FileNew( httpd_host_t *host, char *psz_url, char *psz_mime, char *psz_user, char *psz_password, - httpd_file_callback_t pf_fill, + const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill, httpd_file_sys_t *p_sys ) { httpd_file_t *file = malloc( sizeof( httpd_file_t ) ); if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user, - psz_password ) ) == NULL ) + psz_password, p_acl ) + ) == NULL ) { free( file ); return NULL; @@ -547,16 +564,22 @@ static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, answer->psz_status = strdup( "Moved Permanently" ); p = answer->p_body = malloc( 1000 + strlen( rdir->psz_dst ) ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "Redirection\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "

You should be redirected

\n", rdir->psz_dst ); - p += sprintf( p, "
\n" ); - p += sprintf( p, "VideoLAN\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); + p += sprintf( (char *)p, + "\n" + "\n" + "\n" + "\n" + "Redirection\n" + "\n" + "\n" + "

You should be " + "redirected

\n" + "
\n" + "

VideoLAN\n

" + "
\n" + "\n" + "\n", rdir->psz_dst ); answer->i_body = p - answer->p_body; /* XXX check if it's ok or we need to set an absolute url */ @@ -572,7 +595,7 @@ httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, char *psz_url_dst, { httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) ); - if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL ) ) ) + if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) ) { free( rdir ); return NULL; @@ -747,12 +770,14 @@ static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, httpd_stream_t *httpd_StreamNew( httpd_host_t *host, char *psz_url, char *psz_mime, - char *psz_user, char *psz_password ) + char *psz_user, char *psz_password, + const vlc_acl_t *p_acl ) { httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) ); if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user, - psz_password ) ) == NULL ) + psz_password, p_acl ) + ) == NULL ) { free( stream ); return NULL; @@ -854,114 +879,35 @@ void httpd_StreamDelete( httpd_stream_t *stream ) /***************************************************************************** * Low level *****************************************************************************/ -#define LISTEN_BACKLOG 100 - -#if defined(HAVE_GETNAMEINFO) && !defined(HAVE_GETADDRINFO) -/* - * For now, VLC's configure script does not check for getaddrinfo(), - * but it should be present if getnameinfo() is (the opposite is untrue, with - * Debian potato as an example) - */ -# define HAVE_GETADDRINFO 1 -#endif - static void httpd_HostThread( httpd_host_t * ); -static int GetAddrPort( const struct sockaddr_storage *p_ss ); -#ifndef HAVE_GETADDRINFO -struct httpd_addrinfo +/* create a new host */ +httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host, + int i_port ) { - int ai_family; - int ai_socktype; - int ai_protocol; - /*int ai_flags;*/ - struct sockaddr *ai_addr; - int ai_addrlen; - struct httpd_addrinfo *ai_next; -}; -# define addrinfo httpd_addrinfo - -static int BuildAddr( struct sockaddr_in * p_socket, - const char * psz_address, int i_port ); -#endif - + return httpd_TLSHostNew( p_this, psz_host, i_port, NULL, NULL, NULL, NULL + ); +} -/* create a new host */ -httpd_host_t *httpd_HostNew( vlc_object_t *p_this, char *psz_host, int i_port ) +httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, + int i_port, + const char *psz_cert, const char *psz_key, + const char *psz_ca, const char *psz_crl ) { httpd_t *httpd; - httpd_host_t *host = NULL; - vlc_value_t lockval; - int fd = -1; - struct addrinfo *res, *ptr; - - /* resolv */ -#ifdef HAVE_GETADDRINFO - { - vlc_value_t val; - char psz_port[6]; - struct addrinfo hints; - -#if 0 - memset( &hints, 0, sizeof( hints ) ); - /* Check if we have force ipv4 or ipv6 */ - var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_this, "ipv4", &val ); - if( val.b_bool ) - hints.ai_family = PF_INET; -#else - /* - * For now, keep IPv4 by default. That said, it should be safe to use - * IPv6 by default *on the server side*, as, apart from NetBSD, most - * systems accept IPv4 clients on IPv6 listening sockets. - * - * - */ - hints.ai_family = PF_INET; - - var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_this, "ipv6", &val ); - if( val.b_bool ) - hints.ai_family = 0; // try IPv6, then IPv4 -#endif - - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - if (*psz_host == '\0') - psz_host = NULL; - - snprintf( psz_port, sizeof( psz_port ), "%d", i_port ); - psz_port[sizeof( psz_port ) - 1] = '\0'; - - if( getaddrinfo( psz_host, psz_port, &hints, &res ) ) - { - msg_Err( p_this, "cannot resolve %s:%d", psz_host, i_port ); - return NULL; - } - } -#else - struct sockaddr_in sock; - struct httpd_addrinfo info; - - info.ai_family = PF_INET; - info.ai_socktype = SOCK_STREAM; - info.ai_protocol = 0; - info.ai_addr = (struct sockaddr *)&sock; - info.ai_addrlen = sizeof( sock ); - info.ai_next = NULL; - - res = &info; + httpd_host_t *host; + tls_server_t *p_tls; + char *psz_host; + vlc_value_t lockval; + int i; - if( BuildAddr( &sock, psz_host, i_port ) ) + psz_host = strdup( psz_hostname ); + if( psz_host == NULL ) { - msg_Err( p_this, "cannot build address for %s:%d", psz_host, i_port ); + msg_Err( p_this, "memory error" ); return NULL; } -# define freeaddrinfo( r ) (void)0; -#endif - /* to be sure to avoid multiple creation */ var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX ); var_Get( p_this->p_libvlc, "httpd_mutex", &lockval ); @@ -973,7 +919,7 @@ httpd_host_t *httpd_HostNew( vlc_object_t *p_this, char *psz_host, int i_port ) if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL ) { vlc_mutex_unlock( lockval.p_address ); - freeaddrinfo( res ); + free( psz_host ); return NULL; } @@ -984,153 +930,74 @@ httpd_host_t *httpd_HostNew( vlc_object_t *p_this, char *psz_host, int i_port ) vlc_object_attach( httpd, p_this->p_vlc ); } - for( ptr = res; (ptr != NULL) && (fd == -1); ptr = ptr->ai_next ) + /* verify if it already exist */ + for( i = httpd->i_host - 1; i >= 0; i-- ) { - int i; + host = httpd->host[i]; - if( ((unsigned)ptr->ai_addrlen) > sizeof( struct sockaddr_storage ) ) - { - msg_Dbg( p_this, "socket address too big" ); + /* cannot mix TLS and non-TLS hosts */ + if( ( ( httpd->host[i]->p_tls != NULL ) != ( psz_cert != NULL ) ) + || ( host->i_port != i_port ) + || strcmp( host->psz_hostname, psz_hostname ) ) continue; - } - - /* verify if it already exist */ - for( i = 0; i < httpd->i_host; i++ ) - { - if (GetAddrPort (&httpd->host[i]->sock) != i_port) - continue; - -#ifdef AF_INET6 - if( httpd->host[i]->sock.ss_family == AF_INET6 ) - { - const struct sockaddr_in6 *p_hsock, *p_sock; - - p_hsock = (const struct sockaddr_in6 *)&httpd->host[i]->sock; - p_sock = (const struct sockaddr_in6 *)res->ai_addr; - - if( memcmp( &p_hsock->sin6_addr, &in6addr_any, - sizeof( struct in6_addr ) ) && - ( p_sock->sin6_family != AF_INET6 || - memcmp( &p_hsock->sin6_addr, &p_sock->sin6_addr, - sizeof( struct in6_addr ) ) ) ) - continue; /* does not match */ - } - else if( res->ai_family == PF_INET6 ) - continue; - else -#endif - if( httpd->host[i]->sock.ss_family == AF_INET ) - { - const struct sockaddr_in *p_hsock, *p_sock; - - p_hsock = (const struct sockaddr_in *)&httpd->host[i]->sock; - p_sock = (const struct sockaddr_in *)res->ai_addr; - - if( p_hsock->sin_addr.s_addr != INADDR_ANY && - ( p_sock->sin_family != AF_INET || - p_hsock->sin_addr.s_addr != p_sock->sin_addr.s_addr ) ) - continue; /* does not match */ - } - else if( res->ai_family == PF_INET ) - continue; - else - { - msg_Dbg( p_this, "host with unknown address family" ); - continue; - } - - freeaddrinfo( res ); - - /* yep found */ - host = httpd->host[i]; - host->i_ref++; - vlc_mutex_unlock( lockval.p_address ); + /* yep found */ + host->i_ref++; - msg_Dbg( p_this, "host already registered" ); - return host; - } + vlc_mutex_unlock( lockval.p_address ); + return host; + } - /* create the listening socket */ - fd = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); - if( fd == -1 ) - continue; + host = NULL; - /* reuse socket */ + /* determine TLS configuration */ + if ( psz_cert != NULL ) + { + p_tls = tls_ServerCreate( p_this, psz_cert, psz_key ); + if ( p_tls == NULL ) { - int dummy = 1; - if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, - (void *)&dummy, sizeof( dummy ) ) < 0 ) - { - msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" ); - } + msg_Err( p_this, "TLS initialization error" ); + goto error; } - /* bind it */ - if( bind( fd, res->ai_addr, res->ai_addrlen ) ) + if ( ( psz_ca != NULL) && tls_ServerAddCA( p_tls, psz_ca ) ) { - msg_Err( p_this, "cannot bind socket" ); - goto socket_error; + msg_Err( p_this, "TLS CA error" ); + goto error; } - /* set to non-blocking */ -#if defined( WIN32 ) || defined( UNDER_CE ) - { - unsigned long i_dummy = 1; - if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 ) - { - msg_Err( p_this, "cannot set socket to non-blocking mode" ); - goto socket_error; - } - } -#else - { - unsigned int i_flags; - if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 ) - { - msg_Err( p_this, "cannot F_GETFL socket" ); - goto socket_error; - } - if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 ) - { - msg_Err( p_this, "cannot F_SETFL O_NONBLOCK" ); - goto socket_error; - } - } -#endif - /* listen */ - if( listen( fd, LISTEN_BACKLOG ) < 0 ) + + if ( ( psz_crl != NULL) && tls_ServerAddCRL( p_tls, psz_crl ) ) { - msg_Err( p_this, "cannot listen socket" ); - close( fd ); - fd = -1; + msg_Err( p_this, "TLS CRL error" ); + goto error; } - - continue; - -socket_error: - close( fd ); - fd = -1; } - - freeaddrinfo( res ); - - if( fd == -1 ) - goto error; + else + p_tls = NULL; /* create the new host */ host = vlc_object_create( p_this, sizeof( httpd_host_t ) ); host->httpd = httpd; vlc_mutex_init( httpd, &host->lock ); host->i_ref = 1; - host->fd = fd; - memcpy( &host->sock, res->ai_addr, res->ai_addrlen ); - host->i_sock_size = res->ai_addrlen; + host->fd = net_ListenTCP( p_this, psz_host, i_port ); + if( host->fd == NULL ) + { + msg_Err( p_this, "cannot create socket(s) for HTTP host" ); + goto error; + } + + host->i_port = i_port; + host->psz_hostname = psz_host; + host->i_url = 0; host->url = NULL; host->i_client = 0; host->client = NULL; - + + host->p_tls = p_tls; + /* create the thread */ if( vlc_thread_create( host, "httpd host thread", httpd_HostThread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) ) @@ -1146,19 +1013,25 @@ socket_error: return host; error: + free( psz_host ); + if( httpd->i_host <= 0 ) + { + vlc_object_release( httpd ); + vlc_object_detach( httpd ); + vlc_object_destroy( httpd ); + } vlc_mutex_unlock( lockval.p_address ); - if( fd != -1 ) - net_Close( fd ); - if( host != NULL ) { + net_ListenClose( host->fd ); vlc_mutex_destroy( &host->lock ); vlc_object_destroy( host ); } - /* TODO destroy no more used httpd TODO */ - vlc_object_release( httpd ); + if( p_tls != NULL ) + tls_ServerDelete( p_tls ); + return NULL; } @@ -1174,8 +1047,6 @@ void httpd_HostDelete( httpd_host_t *host ) var_Get( httpd->p_libvlc, "httpd_mutex", &lockval ); vlc_mutex_lock( lockval.p_address ); - vlc_object_release( httpd ); - host->i_ref--; if( host->i_ref > 0 ) { @@ -1208,13 +1079,19 @@ void httpd_HostDelete( httpd_host_t *host ) /* TODO */ } - net_Close( host->fd ); + if( host->p_tls != NULL) + tls_ServerDelete( host->p_tls ); + + net_ListenClose( host->fd ); + free( host->psz_hostname ); + vlc_mutex_destroy( &host->lock ); vlc_object_destroy( host ); if( httpd->i_host <= 0 ) { msg_Info( httpd, "httpd doesn't reference any host, deleting" ); + vlc_object_release( httpd ); vlc_object_detach( httpd ); vlc_object_destroy( httpd ); } @@ -1224,7 +1101,7 @@ void httpd_HostDelete( httpd_host_t *host ) /* register a new url */ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password, - vlc_bool_t b_check ) + const vlc_acl_t *p_acl, vlc_bool_t b_check ) { httpd_url_t *url; int i; @@ -1251,6 +1128,7 @@ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, char *psz_url, url->psz_url = strdup( psz_url ); url->psz_user = strdup( psz_user ? psz_user : "" ); url->psz_password = strdup( psz_password ? psz_password : "" ); + url->p_acl = ACL_Duplicate( host, p_acl ); for( i = 0; i < HTTPD_MSG_MAX; i++ ) { url->catch[i].cb = NULL; @@ -1264,17 +1142,19 @@ static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, char *psz_url, } httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url, - char *psz_user, char *psz_password ) + char *psz_user, char *psz_password, + const vlc_acl_t *p_acl ) { return httpd_UrlNewPrivate( host, psz_url, psz_user, - psz_password, VLC_FALSE ); + psz_password, p_acl, VLC_FALSE ); } httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url, - char *psz_user, char *psz_password ) + char *psz_user, char *psz_password, + const vlc_acl_t *p_acl ) { return httpd_UrlNewPrivate( host, psz_url, psz_user, - psz_password, VLC_TRUE ); + psz_password, p_acl, VLC_TRUE ); } /* register callback on a url */ @@ -1303,6 +1183,7 @@ void httpd_UrlDelete( httpd_url_t *url ) free( url->psz_url ); free( url->psz_user ); free( url->psz_password ); + ACL_Destroy( url->p_acl ); for( i = 0; i < host->i_client; i++ ) { @@ -1424,7 +1305,7 @@ static void httpd_ClientInit( httpd_client_t *cl ) { cl->i_state = HTTPD_CLIENT_RECEIVING; cl->i_activity_date = mdate(); - cl->i_activity_timeout = 50000000; + cl->i_activity_timeout = I64C(10000000); cl->i_buffer_size = 10000; cl->i_buffer = 0; cl->p_buffer = malloc( cl->i_buffer_size ); @@ -1447,59 +1328,35 @@ void httpd_ClientModeBidir( httpd_client_t *cl ) char* httpd_ClientIP( httpd_client_t *cl ) { -#ifdef HAVE_GETNAMEINFO - char sz_ip[INET6_ADDRSTRLEN + 2]; int i; + char sz_ip[NI_MAXNUMERICHOST + 2]; - if( (cl->sock.ss_family == AF_INET6) && - IN6_IS_ADDR_V4MAPPED( &((const struct sockaddr_in6 *) - &cl->sock)->sin6_addr) ) - { - /* If client is using IPv4 but server is using IPv6 */ - struct sockaddr_in a; - - memset( &a, 0, sizeof( a ) ); - a.sin_family = AF_INET; - a.sin_port = ((const struct sockaddr_in6 *)&cl->sock)->sin6_port; - a.sin_addr.s_addr = ((const uint32_t *)&((const struct sockaddr_in6 *) - &cl->sock)->sin6_addr)[3]; - i = getnameinfo( (const struct sockaddr *)&a, sizeof( a ), - &sz_ip[1], INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST ); - } - else - i = getnameinfo( (const struct sockaddr *)&cl->sock, cl->i_sock_size, - &sz_ip[1], INET6_ADDRSTRLEN, NULL, 0, - NI_NUMERICHOST ); + i = vlc_getnameinfo( (const struct sockaddr *)&cl->sock, cl->i_sock_size, + sz_ip+1, NI_MAXNUMERICHOST, NULL, NI_NUMERICHOST ); if( i != 0 ) - /* FIXME: msg_Err */ return NULL; - - fprintf( stderr, "ClientIP = %s\n", &sz_ip[1]); - - if( strchr( &sz_ip[1], ':' ) != NULL ) + + /* semi-colon in address => must add bracket for HTTP */ + if( strchr( sz_ip + 1, ':' ) != NULL ) { - *sz_ip = '['; + sz_ip[0] = '['; i = strlen( sz_ip ); sz_ip[i++] = ']'; sz_ip[i] = '\0'; - - fprintf( stderr, "ClientIP (with []) = %s\n", sz_ip); - return strdup( sz_ip ); + + return strdup(sz_ip); } - return strdup( &sz_ip[1] ); - -#else - /* FIXME not thread safe */ - return strdup( inet_ntoa( ((const struct sockaddr_in *)&cl->sock)->sin_addr ) ); -#endif + return strdup(sz_ip + 1); } static void httpd_ClientClean( httpd_client_t *cl ) { - if( cl->fd > 0 ) + if( cl->fd >= 0 ) { + if( cl->p_tls != NULL ) + tls_ServerSessionClose( cl->p_tls ); net_Close( cl->fd ); cl->fd = -1; } @@ -1515,29 +1372,47 @@ static void httpd_ClientClean( httpd_client_t *cl ) } static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock, - int i_sock_size ) + int i_sock_size, + tls_session_t *p_tls ) { httpd_client_t *cl = malloc( sizeof( httpd_client_t ) ); - /* 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_SETFL, O_NONBLOCK ); -#endif 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 ); return cl; } + +static int httpd_NetRecv( httpd_client_t *cl, uint8_t *p, int i_len ) +{ + tls_session_t *p_tls; + + p_tls = cl->p_tls; + if( p_tls != NULL) + return tls_Recv( p_tls, p, i_len ); + + return recv( cl->fd, p, i_len, 0 ); +} + + +static int httpd_NetSend( httpd_client_t *cl, const uint8_t *p, int i_len ) +{ + tls_session_t *p_tls; + + p_tls = cl->p_tls; + if( p_tls != NULL) + return tls_Send( p_tls, p, i_len ); + + return send( cl->fd, p, i_len, 0 ); +} + + static void httpd_ClientRecv( httpd_client_t *cl ) { int i_len; @@ -1545,8 +1420,8 @@ static void httpd_ClientRecv( httpd_client_t *cl ) if( cl->query.i_proto == HTTPD_PROTO_NONE ) { /* enought to see if it's rtp over rtsp or RTSP/HTTP */ - i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 4 - cl->i_buffer, 0 ); - + i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer], + 4 - cl->i_buffer ); if( i_len > 0 ) { cl->i_buffer += i_len; @@ -1554,7 +1429,7 @@ static void httpd_ClientRecv( httpd_client_t *cl ) if( cl->i_buffer >= 4 ) { - fprintf( stderr, "peek=%4.4s\n", cl->p_buffer ); + /*fprintf( stderr, "peek=%4.4s\n", cl->p_buffer );*/ /* detect type */ if( cl->p_buffer[0] == '$' ) { @@ -1567,19 +1442,19 @@ static void httpd_ClientRecv( httpd_client_t *cl ) cl->i_buffer = 0; } - else if( !strncmp( cl->p_buffer, "HTTP", 4 ) ) + else if( !memcmp( cl->p_buffer, "HTTP", 4 ) ) { cl->query.i_proto = HTTPD_PROTO_HTTP; cl->query.i_type = HTTPD_MSG_ANSWER; } - else if( !strncmp( cl->p_buffer, "RTSP", 4 ) ) + else if( !memcmp( cl->p_buffer, "RTSP", 4 ) ) { cl->query.i_proto = HTTPD_PROTO_RTSP; cl->query.i_type = HTTPD_MSG_ANSWER; } - else if( !strncmp( cl->p_buffer, "GET", 3 ) || - !strncmp( cl->p_buffer, "HEAD", 4 ) || - !strncmp( cl->p_buffer, "POST", 4 ) ) + else if( !memcmp( cl->p_buffer, "GET", 3 ) || + !memcmp( cl->p_buffer, "HEAD", 4 ) || + !memcmp( cl->p_buffer, "POST", 4 ) ) { cl->query.i_proto = HTTPD_PROTO_HTTP; cl->query.i_type = HTTPD_MSG_NONE; @@ -1594,8 +1469,8 @@ static void httpd_ClientRecv( httpd_client_t *cl ) else if( cl->query.i_body > 0 ) { /* we are reading the body of a request or a channel */ - i_len = recv( cl->fd, &cl->query.p_body[cl->i_buffer], - cl->query.i_body - cl->i_buffer, 0 ); + i_len = httpd_NetRecv( cl, &cl->query.p_body[cl->i_buffer], + cl->query.i_body - cl->i_buffer ); if( i_len > 0 ) { cl->i_buffer += i_len; @@ -1610,7 +1485,7 @@ static void httpd_ClientRecv( httpd_client_t *cl ) /* we are reading a header -> char by char */ for( ;; ) { - i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 1, 0 ); + i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 ); if( i_len <= 0 ) { break; @@ -1622,8 +1497,8 @@ static void httpd_ClientRecv( httpd_client_t *cl ) cl->i_buffer_size += 1024; cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size ); } - if( ( cl->i_buffer >= 2 && !strncmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )|| - ( cl->i_buffer >= 4 && !strncmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) ) + if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )|| + ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) ) { char *p; @@ -1632,8 +1507,12 @@ static void httpd_ClientRecv( httpd_client_t *cl ) if( cl->query.i_type == HTTPD_MSG_ANSWER ) { + /* FIXME: + * assume strlen( "HTTP/1.x" ) = 8 + */ cl->query.i_status = - strtol( &cl->p_buffer[strlen( "HTTP/1.x" )], &p, 0 ); + strtol( (char *)&cl->p_buffer[8], + &p, 0 ); while( *p == ' ' ) { p++; @@ -1668,14 +1547,14 @@ static void httpd_ClientRecv( httpd_client_t *cl ) p = NULL; cl->query.i_type = HTTPD_MSG_NONE; - fprintf( stderr, "received new request=%s\n", cl->p_buffer); + /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/ for( i = 0; msg_type[i].name != NULL; i++ ) { - if( !strncmp( cl->p_buffer, msg_type[i].name, + if( !strncmp( (char *)cl->p_buffer, msg_type[i].name, strlen( msg_type[i].name ) ) ) { - p = &cl->p_buffer[strlen(msg_type[i].name) + 1 ]; + p = (char *)&cl->p_buffer[strlen((char *)msg_type[i].name) + 1 ]; cl->query.i_type = msg_type[i].i_type; if( cl->query.i_proto != msg_type[i].i_proto ) { @@ -1688,11 +1567,11 @@ static void httpd_ClientRecv( httpd_client_t *cl ) } if( p == NULL ) { - if( strstr( cl->p_buffer, "HTTP/1." ) ) + if( strstr( (char *)cl->p_buffer, "HTTP/1." ) ) { cl->query.i_proto = HTTPD_PROTO_HTTP; } - else if( strstr( cl->p_buffer, "RTSP/1." ) ) + else if( strstr( (char *)cl->p_buffer, "RTSP/1." ) ) { cl->query.i_proto = HTTPD_PROTO_RTSP; } @@ -1722,7 +1601,7 @@ static void httpd_ClientRecv( httpd_client_t *cl ) if( ( p3 = strchr( cl->query.psz_url, '?' ) ) ) { *p3++ = '\0'; - cl->query.psz_args = strdup( p3 ); + cl->query.psz_args = (uint8_t *)strdup( p3 ); } if( p2 ) { @@ -1835,8 +1714,12 @@ static void httpd_ClientRecv( httpd_client_t *cl ) } 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 ) + cl->i_activity_timeout = 0; + /* Debugging only */ - if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) + /*if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) { int i; @@ -1859,9 +1742,10 @@ static void httpd_ClientRecv( httpd_client_t *cl ) fprintf( stderr, " - option name='%s' value='%s'\n", cl->query.name[i], cl->query.value[i] ); } - } + }*/ } + static void httpd_ClientSend( httpd_client_t *cl ) { int i; @@ -1887,7 +1771,7 @@ static void httpd_ClientSend( httpd_client_t *cl ) free( cl->p_buffer ); cl->p_buffer = malloc( i_size ); } - p = cl->p_buffer; + p = (char *)cl->p_buffer; p += sprintf( p, "%s/1.%d %d %s\r\n", cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP", @@ -1903,12 +1787,12 @@ static void httpd_ClientSend( httpd_client_t *cl ) cl->i_buffer = 0; cl->i_buffer_size = (uint8_t*)p - cl->p_buffer; - fprintf( stderr, "sending answer\n" ); - fprintf( stderr, "%s", cl->p_buffer ); + /*fprintf( stderr, "sending answer\n" ); + fprintf( stderr, "%s", cl->p_buffer );*/ } - i_len = send( cl->fd, &cl->p_buffer[cl->i_buffer], - cl->i_buffer_size - cl->i_buffer, 0 ); + i_len = httpd_NetSend( cl, &cl->p_buffer[cl->i_buffer], + cl->i_buffer_size - cl->i_buffer ); if( i_len > 0 ) { cl->i_activity_date = mdate(); @@ -1962,13 +1846,54 @@ static void httpd_ClientSend( httpd_client_t *cl ) } } +static void httpd_ClientTlsHsIn( httpd_client_t *cl ) +{ + switch( tls_SessionContinueHandshake( cl->p_tls ) ) + { + case 0: + cl->i_state = HTTPD_CLIENT_RECEIVING; + break; + + case -1: + cl->i_state = HTTPD_CLIENT_DEAD; + cl->p_tls = NULL; + break; + + case 2: + cl->i_state = HTTPD_CLIENT_TLS_HS_OUT; + } +} + +static void httpd_ClientTlsHsOut( httpd_client_t *cl ) +{ + switch( tls_SessionContinueHandshake( cl->p_tls ) ) + { + case 0: + cl->i_state = HTTPD_CLIENT_RECEIVING; + break; + + case -1: + cl->i_state = HTTPD_CLIENT_DEAD; + cl->p_tls = NULL; + break; + + case 1: + cl->i_state = HTTPD_CLIENT_TLS_HS_IN; + break; + } +} + static void httpd_HostThread( httpd_host_t *host ) { + tls_session_t *p_tls = NULL; + 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 = 0; int i_ret; int i_client; @@ -1985,8 +1910,18 @@ static void httpd_HostThread( httpd_host_t *host ) FD_ZERO( &fds_read ); FD_ZERO( &fds_write ); - FD_SET( host->fd, &fds_read ); - i_handle_max = host->fd; + i_handle_max = -1; + + 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 ); /* add all socket that should be read/write and close dead connection */ vlc_mutex_lock( &host->lock ); @@ -1996,28 +1931,23 @@ static void httpd_HostThread( httpd_host_t *host ) if( cl->i_ref < 0 || ( cl->i_ref == 0 && ( cl->i_state == HTTPD_CLIENT_DEAD || - cl->i_activity_date + cl->i_activity_timeout < mdate() ) ) ) + ( cl->i_activity_timeout > 0 && + cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) ) { - char *ip; - - // FIXME: it sucks to allocate memory on the stack for debug - ip = httpd_ClientIP( cl ); - msg_Dbg( host, "connection closed(%s)", - (ip != NULL) ? ip : "unknown" ); - free( ip ); - httpd_ClientClean( cl ); TAB_REMOVE( host->i_client, host->client, cl ); free( cl ); i_client--; continue; } - else if( cl->i_state == HTTPD_CLIENT_RECEIVING ) + else 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 ); } - else if( cl->i_state == HTTPD_CLIENT_SENDING ) + 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 ); @@ -2107,16 +2037,20 @@ static void httpd_HostThread( httpd_host_t *host ) p = answer->p_body = malloc( 1000 ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "Error 501\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "

501 Unimplemented

\n" ); - p += sprintf( p, "
\n" ); - p += sprintf( p, "VideoLAN\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); + p += sprintf( (char *)p, + "" + "\n" + "\n" + "\n" + "Error 501\n" + "\n" + "\n" + "

501 Unimplemented

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n" ); answer->i_body = p - answer->p_body; httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); @@ -2128,6 +2062,7 @@ static void httpd_HostThread( httpd_host_t *host ) else { vlc_bool_t b_auth_failed = VLC_FALSE; + vlc_bool_t b_hosts_failed = VLC_FALSE; int i; /* Search the url and trigger callbacks */ @@ -2139,6 +2074,21 @@ static void httpd_HostThread( httpd_host_t *host ) { if( url->catch[i_msg].cb ) { + if( answer && ( url->p_acl != NULL ) ) + { + char *ip = httpd_ClientIP( cl ); + if( ip != NULL ) + { + if( ACL_Check( url->p_acl, ip ) ) + { + b_hosts_failed = VLC_TRUE; + free( ip ); + break; + } + free( ip ); + } + } + if( answer && ( *url->psz_user || *url->psz_password ) ) { /* create the headers */ @@ -2197,21 +2147,46 @@ static void httpd_HostThread( httpd_host_t *host ) answer->i_version= 0; p = answer->p_body = malloc( 1000 + strlen(query->psz_url) ); - if( b_auth_failed ) + if( b_hosts_failed ) + { + answer->i_status = 403; + answer->psz_status = strdup( "Forbidden" ); + + /* FIXME: lots of code duplication */ + p += sprintf( (char *)p, + "" + "\n" + "\n" + "\n" + "Error 403\n" + "\n" + "\n" + "

403 Forbidden (%s)

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n", query->psz_url ); + } + else if( b_auth_failed ) { answer->i_status = 401; answer->psz_status = strdup( "Authorization Required" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "Error 401\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "

401 Authorization Required (%s)

\n", query->psz_url ); - p += sprintf( p, "
\n" ); - p += sprintf( p, "VideoLAN\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); + p += sprintf( (char *)p, + "" + "\n" + "\n" + "\n" + "Error 401\n" + "\n" + "\n" + "

401 Authorization Required (%s)

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n", query->psz_url ); } else { @@ -2219,16 +2194,20 @@ static void httpd_HostThread( httpd_host_t *host ) answer->i_status = 404; answer->psz_status = strdup( "Not found" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "Error 404\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "

404 Ressource not found(%s)

\n", query->psz_url ); - p += sprintf( p, "
\n" ); - p += sprintf( p, "VideoLAN\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); + p += sprintf( (char *)p, + "" + "\n" + "\n" + "\n" + "Error 404\n" + "\n" + "\n" + "

404 Resource not found(%s)

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n", query->psz_url ); } answer->i_body = p - answer->p_body; @@ -2338,7 +2317,11 @@ static void httpd_HostThread( httpd_host_t *host ) if( i_ret == -1 && errno != EINTR ) { - msg_Warn( host, "cannot select sockets" ); +#if defined(WIN32) || defined(UNDER_CE) + msg_Warn( host, "cannot select sockets (%d)", WSAGetLastError( ) ); +#else + msg_Warn( host, "cannot select sockets : %s", strerror( errno ) ); +#endif msleep( 1000 ); continue; } @@ -2348,30 +2331,66 @@ static void httpd_HostThread( httpd_host_t *host ) } /* accept new connections */ - if( FD_ISSET( host->fd, &fds_read ) ) + for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ ) { - int i_sock_size = sizeof( struct sockaddr_storage ); - struct sockaddr_storage sock; - int fd; - - fd = accept( host->fd, (struct sockaddr *)&sock, &i_sock_size ); - if( fd > 0 ) + if( FD_ISSET( fd, &fds_read ) ) { - char *ip; - - httpd_client_t *cl = httpd_ClientNew( fd, &sock, i_sock_size ); - - vlc_mutex_lock( &host->lock ); - TAB_APPEND( host->i_client, host->client, cl ); - vlc_mutex_unlock( &host->lock ); - - // FIXME: it sucks to allocate heap memory for a debug message - ip = httpd_ClientIP( cl ); - msg_Dbg( host, "new connection (%s)", - ip != NULL ? ip : "unknown" ); - free( ip ); + 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 ) + { + int 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_SETFL, O_NONBLOCK ); + #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; + + cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls ); + 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 + } + } } } + /* now try all others socket */ vlc_mutex_lock( &host->lock ); for( i_client = 0; i_client < host->i_client; i_client++ ) @@ -2385,6 +2404,14 @@ static void httpd_HostThread( httpd_host_t *host ) { httpd_ClientSend( cl ); } + else if( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) + { + httpd_ClientTlsHsIn( cl ); + } + else if( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) + { + httpd_ClientTlsHsOut( cl ); + } if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING && @@ -2395,69 +2422,66 @@ static void httpd_HostThread( httpd_host_t *host ) } vlc_mutex_unlock( &host->lock ); } -} -#ifndef HAVE_GETADDRINFO -static int BuildAddr( struct sockaddr_in * p_socket, - const char * psz_address, int i_port ) -{ - /* Reset struct */ - memset( p_socket, 0, sizeof( struct sockaddr_in ) ); - p_socket->sin_family = AF_INET; /* family */ - p_socket->sin_port = htons( (uint16_t)i_port ); - if( !*psz_address ) - { - p_socket->sin_addr.s_addr = INADDR_ANY; - } - else - { - struct hostent * p_hostent; - - /* Try to convert address directly from in_addr - this will work if - * psz_address is dotted decimal. */ -#ifdef HAVE_ARPA_INET_H - if( !inet_aton( psz_address, &p_socket->sin_addr ) ) -#else - p_socket->sin_addr.s_addr = inet_addr( psz_address ); + if( p_tls != NULL ) + tls_ServerSessionClose( p_tls ); +} -/* if( p_socket->sin_addr.s_addr == INADDR_NONE )*/ - if( p_socket->sin_addr.s_addr == INADDR_BROADCAST ) -#endif - { - /* We have a fqdn, try to find its address */ - if ( (p_hostent = gethostbyname( psz_address )) == NULL ) - { - return( -1 ); - } +#else /* ENABLE_HTTPD */ - /* Copy the first address of the host in the socket address */ - memcpy( &((struct sockaddr_in *)p_socket)->sin_addr, p_hostent->h_addr_list[0], - p_hostent->h_length ); - } - } - return( 0 ); +/* We just define an empty wrapper */ +httpd_host_t *httpd_TLSHostNew( vlc_object_t *a, char *b, int c, + tls_server_t *d ) +{ + msg_Err( a, "HTTP daemon support is disabled" ); + return 0; } -#endif - -static int GetAddrPort( const struct sockaddr_storage *p_ss ) +httpd_host_t *httpd_HostNew( vlc_object_t *a, char *b, int c ) { - int i_port = 0; - - switch (p_ss->ss_family) - { -#ifdef AF_INET6 - case AF_INET6: - i_port = ((const struct sockaddr_in6 *)p_ss)->sin6_port; - break; -#endif - - case AF_INET: - i_port = ((const struct sockaddr_in *)p_ss)->sin_port; - break; - - default: - return -1; - } - - return ntohs( i_port ); + msg_Err( a, "HTTP daemon support is disabled" ); + return 0; +} +void httpd_HostDelete( httpd_host_t *a ) +{ +} +httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url, + char *psz_user, char *psz_password, + const vlc_acl_t *p_acl ) +{ + return NULL; +} +httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url, + char *psz_user, char *psz_password, + const vlc_acl_t *p_acl ) +{ + return NULL; } +int httpd_UrlCatch( httpd_url_t *a, int b, httpd_callback_t c, + httpd_callback_sys_t *d ){ return 0; } +void httpd_UrlDelete( httpd_url_t *a ){} + +char *httpd_ClientIP( httpd_client_t *a ){ return 0; } +void httpd_ClientModeStream( httpd_client_t *a ){} +void httpd_ClientModeBidir( httpd_client_t *a ){} + +void httpd_FileDelete( httpd_file_t *a ){} +httpd_file_t *httpd_FileNew( httpd_host_t *a, char *b, char *c, char *d, + char *e, httpd_file_callback_t f, + httpd_file_sys_t *g ){ return 0; } + +void httpd_RedirectDelete( httpd_redirect_t *a ){} +httpd_redirect_t *httpd_RedirectNew( httpd_host_t *a, + char *b, char *c ){ return 0; } + +void httpd_StreamDelete( httpd_stream_t *a ){} +int httpd_StreamHeader( httpd_stream_t *a, uint8_t *b, int c ){ return 0; } +int httpd_StreamSend ( httpd_stream_t *a, uint8_t *b, int c ){ return 0; } +httpd_stream_t *httpd_StreamNew( httpd_host_t *a, char *b, char *c, + char *d, char *e ){ return 0; } + +void httpd_MsgInit ( httpd_message_t *a ){} +void httpd_MsgAdd ( httpd_message_t *a, char *b, char *c, ... ){} +char *httpd_MsgGet ( httpd_message_t *a, char *b ){ return 0; } +void httpd_MsgClean( httpd_message_t *a ){} + +#endif /* ENABLE_HTTPD */