void *p_sys;
struct virtual_socket_t sock;
- tls_session_t * (*pf_handshake) ( tls_session_t *, int );
+ int (*pf_handshake) ( tls_session_t *, int );
+ int (*pf_handshake2) ( tls_session_t * );
void (*pf_close) ( tls_session_t * );
};
VLC_EXPORT( void, tls_ClientDelete, ( tls_session_t * ) );
# define tls_SessionHandshake( a, b ) (((tls_session_t *)a)->pf_handshake (a, b))
+# define tls_SessionContinueHandshake( a ) (((tls_session_t *)a)->pf_handshake2 (a))
# define tls_SessionClose( a ) (((tls_session_t *)a)->pf_close (a))
/*****************************************************************************
- * tls_SessionHandshake:
+ * tls_Session(Continue)?Handshake:
*****************************************************************************
* Establishes TLS session with a peer through socket <fd>.
- * Returns NULL on error (do NOT call tls_SessionClose in case of error or
- * re-use the session structure).
+ * Returns -1 on error (you need not and must not call tls_SessionClose)
+ * 0 on succesful handshake completion, 1 if more would-be blocking recv is
+ * needed, 2 if more would-be blocking send is required.
*****************************************************************************/
-static tls_session_t *
-gnutls_SessionHandshake( tls_session_t *p_session, int fd )
+static int
+gnutls_SessionContinueHandshake( tls_session_t *p_session)
{
tls_session_sys_t *p_sys;
int val;
p_sys = (tls_session_sys_t *)(p_session->p_sys);
- gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
-
- do
- /* TODO: handle fatal error */
- val = gnutls_handshake( p_sys->session );
- while( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) );
+ /* TODO: handle fatal error */
+ val = gnutls_handshake( p_sys->session );
+ if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
+ return 1 + gnutls_record_get_direction( p_sys->session );
if( val < 0 )
{
gnutls_strerror( val ) );
free( p_sys );
free( p_session );
- return NULL;
+ return -1;
}
- return p_session;
+ return 0;
+}
+
+static int
+gnutls_SessionHandshake( tls_session_t *p_session, int fd )
+{
+ tls_session_sys_t *p_sys;
+
+ p_sys = (tls_session_sys_t *)(p_session->p_sys);
+
+ gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
+
+ return gnutls_SessionContinueHandshake( p_session );
}
p_session->sock.pf_send = gnutls_Send;
p_session->sock.pf_recv = gnutls_Recv;
p_session->pf_handshake = gnutls_SessionHandshake;
+ p_session->pf_handshake2 = gnutls_SessionContinueHandshake;
p_session->pf_close = gnutls_SessionClose;
return p_session;
HTTPD_CLIENT_WAITING,
HTTPD_CLIENT_DEAD,
+
+ HTTPD_CLIENT_TLS_HS_IN,
+ HTTPD_CLIENT_TLS_HS_OUT
};
/* mode */
enum
}
}
+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;
+ 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;
+ 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;
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 );
fd = accept( host->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 )
{
/* FIXME: that MUST be non-blocking */
if( p_tls != NULL)
{
- p_tls = tls_SessionHandshake( p_tls, fd );
- if ( p_tls == NULL )
+ switch ( tls_SessionHandshake( p_tls, fd ) )
{
- msg_Err( host, "Rejecting TLS connection" );
- net_Close( fd );
- fd = -1;
+ case -1:
+ msg_Err( host, "Rejecting TLS connection" );
+ net_Close( fd );
+ fd = -1;
+ 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;
}
}
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
+
// FIXME: it sucks to allocate memory for debug
ip = httpd_ClientIP( cl );
msg_Dbg( host, "new connection (%s)",
{
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 &&
p_session = __tls_ClientCreate( p_tls, psz_ca );
if( p_session != NULL )
{
- p_session = tls_SessionHandshake( p_session, fd );
- if( p_session != NULL )
+ if( tls_SessionHandshake( p_session, fd ) )
{
msg_Dbg( p_this, "TLS/SSL provider initialized" );
return p_session;