winamp/Src/Components/wac_network/wac_network_connection.cpp
2024-09-24 14:54:57 +02:00

562 lines
12 KiB
C++

/*
** JNetLib
** Copyright (C) 2000-2007 Nullsoft, Inc.
** Author: Justin Frankel
** File: connection.cpp - JNL TCP connection implementation
** License: see jnetlib.h
*/
#include "netinc.h"
#include "util.h"
#include "wac_network_connection.h"
#include "wac_network_dns.h"
#include "foundation\error.h"
#include "../nu/strsafe.h"
#pragma intrinsic(memcpy, memset)
WAC_Network_Connection::WAC_Network_Connection()
{
init();
}
WAC_Network_Connection::WAC_Network_Connection( api_dns *dns, size_t sendbufsize, size_t recvbufsize )
{
init();
open( dns, sendbufsize, recvbufsize );
}
void WAC_Network_Connection::init()
{
m_errorstr = 0;
address = 0;
m_dns = NULL;
m_dns_owned = false;
m_socket = -1;
m_remote_port = 0;
m_state = STATE_NOCONNECTION;
m_host[ 0 ] = 0;
saddr = 0;
}
WAC_Network_Connection::~WAC_Network_Connection()
{
/*
** Joshua Teitelbaum 1/27/2006
** virtualization for ssl, calling socket_shtudown()
*/
socket_shutdown();
if ( !saddr ) // free it if it was passed to us (by JNL_Listen, presumably)
free( address ); // TODO: change this if we ever do round-robin DNS connecting or in any way change how we handle 'address'
if ( m_dns_owned )
delete static_cast<wa::Components::WAC_Network_AsyncDNS *>( m_dns );
}
void WAC_Network_Connection::set_dns( api_dns *dns )
{
if ( m_dns_owned )
delete static_cast<wa::Components::WAC_Network_AsyncDNS *>( m_dns );
m_dns = dns;
m_dns_owned = false;
}
void WAC_Network_Connection::open( api_dns *dns, size_t sendbufsize, size_t recvbufsize )
{
if ( dns != API_DNS_AUTODNS && dns )
{
m_dns = dns;
m_dns_owned = false;
}
else if ( !m_dns )
{
m_dns = new wa::Components::WAC_Network_AsyncDNS;
m_dns_owned = true;
}
recv_buffer.reserve( recvbufsize );
send_buffer.reserve( sendbufsize );
}
void WAC_Network_Connection::connect( SOCKET s, sockaddr *addr, socklen_t length )
{
close( 1 );
m_socket = s;
address = (sockaddr *)malloc( length );
memcpy( address, addr, length );
m_remote_port = 0;
if ( m_socket != -1 )
{
SET_SOCK_BLOCK( m_socket, 0 );
m_state = STATE_CONNECTED;
}
else
{
m_errorstr = _strdup( "invalid socket passed to connect" );
m_state = STATE_ERROR;
}
}
void WAC_Network_Connection::connect( const char *hostname, int port )
{
close( 1 );
m_remote_port = (unsigned short)port;
#ifdef _WIN32
lstrcpynA( m_host, hostname, sizeof( m_host ) );
#elif defined(__APPLE__)
strlcpy( m_host, hostname, sizeof( m_host ) );
#else
strncpy( m_host, hostname, sizeof( m_host ) - 1 );
m_host[ sizeof( m_host ) - 1 ] = 0;
#endif
//memset(&m_saddr,0,sizeof(m_saddr));
if ( !m_host[ 0 ] )
{
m_errorstr = _strdup( "empty hostname" );
m_state = STATE_ERROR;
}
else
{
m_state = STATE_RESOLVING;
}
}
/*
** Joshua Teitelbaum 1/27/2006
** socket_shutdown
** virtualization for ssl
*/
/* Virtual */
void WAC_Network_Connection::socket_shutdown()
{
if ( m_socket >= 0 )
{
::shutdown( m_socket, SHUT_RDWR );
::closesocket( m_socket );
m_socket = -1;
}
}
/*
** Joshua Teitelbaum 1/27/2006
** socket_recv
** virtualization for ssl
*/
/* Virtual */
ssize_t WAC_Network_Connection::socket_recv( char *buf, size_t len, int options )
{
return ::recv( m_socket, buf, (int)len, options );
}
/*
** Joshua Teitelbaum 1/27/2006
** socket_send
** virtualization for ssl
*/
/* Virtual */
ssize_t WAC_Network_Connection::socket_send( const char *buf, size_t len, int options )
{
return ::send( m_socket, buf, (int)len, options );
}
int WAC_Network_Connection::socket_connect()
{
return ::connect( m_socket, saddr->ai_addr, (int)saddr->ai_addrlen );
}
void WAC_Network_Connection::run( size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd )
{
socklen_t socket_buffer_size = 0;
socklen_t socket_buffer_size_len = sizeof( socket_buffer_size );
socklen_t send_buffer_size;
socklen_t recv_buffer_size;
size_t bytes_allowed_to_send = ( max_send_bytes == (size_t)-1 ) ? send_buffer.size() : max_send_bytes;
size_t bytes_allowed_to_recv = ( max_recv_bytes == (size_t)-1 ) ? recv_buffer.avail() : max_recv_bytes;
if ( bytes_sent )
*bytes_sent = 0;
if ( bytes_rcvd )
*bytes_rcvd = 0;
switch ( m_state )
{
case STATE_RESOLVING:
if ( saddr == 0 )
{
int a = m_dns->resolve( m_host, m_remote_port, &saddr, SOCK_STREAM );
if ( !a )
{
m_state = STATE_RESOLVED;
}
else if ( a == 1 )
{
m_state = STATE_RESOLVING;
break;
}
else
{
m_errorstr = _strdup( "resolving hostname" );
m_state = STATE_ERROR;
return;
}
}
// fall through
case STATE_RESOLVED:
m_socket = ::socket( saddr->ai_family, saddr->ai_socktype, saddr->ai_protocol );
if ( m_socket == -1 )
{
m_errorstr = _strdup( "creating socket" );
m_state = STATE_ERROR;
}
else
{
SET_SOCK_BLOCK( m_socket, 0 );
}
socket_buffer_size = 0;
socket_buffer_size_len = sizeof( socket_buffer_size );
getsockopt( m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&socket_buffer_size, &socket_buffer_size_len );
send_buffer_size = (int)( send_buffer.avail() + send_buffer.size() );
if ( send_buffer_size > 65536 )
send_buffer_size = 65536;
if ( socket_buffer_size < send_buffer_size )
setsockopt( m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&send_buffer_size, sizeof( send_buffer_size ) );
getsockopt( m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&socket_buffer_size, &socket_buffer_size_len );
getsockopt( m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&socket_buffer_size, &socket_buffer_size_len );
recv_buffer_size = (int)recv_buffer.avail();
if ( recv_buffer_size > 65536 )
recv_buffer_size = 65536;
if ( socket_buffer_size < recv_buffer_size )
setsockopt( m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buffer_size, sizeof( recv_buffer_size ) );
getsockopt( m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&socket_buffer_size, &socket_buffer_size_len );
/*
** Joshua Teitelbaum 1/27/2006
** virtualization for ssl
*/
if ( !socket_connect() )
{
address = saddr->ai_addr;
m_state = STATE_CONNECTED;
on_socket_connected();
}
else if ( ERRNO != EINPROGRESS )
{
m_errorstr = _strdup( "Connecting to host" );
m_state = STATE_ERROR;
}
else
{
m_state = STATE_CONNECTING;
}
break;
case STATE_CONNECTING:
{
fd_set f[ 3 ];
FD_ZERO( &f[ 0 ] );
FD_ZERO( &f[ 1 ] );
FD_ZERO( &f[ 2 ] );
FD_SET( m_socket, &f[ 0 ] );
FD_SET( m_socket, &f[ 1 ] );
FD_SET( m_socket, &f[ 2 ] );
struct timeval tv;
memset( &tv, 0, sizeof( tv ) );
if ( select( (int)m_socket + 1, &f[ 0 ], &f[ 1 ], &f[ 2 ], &tv ) == -1 )
{
m_errorstr = _strdup( "Connecting to host (calling select())" );
m_state = STATE_ERROR;
}
else if ( FD_ISSET( m_socket, &f[ 1 ] ) )
{
m_state = STATE_CONNECTED;
on_socket_connected();
}
else if ( FD_ISSET( m_socket, &f[ 2 ] ) )
{
m_errorstr = _strdup( "Connecting to host" );
m_state = STATE_ERROR;
}
}
break;
case STATE_CONNECTED:
case STATE_CLOSING:
/* --- send --- */
{
size_t sent = send_buffer.drain( this, bytes_allowed_to_send );
if ( bytes_sent )
*bytes_sent += sent;
if ( m_state == STATE_CLOSED )
break;
/* --- receive --- */
size_t received = recv_buffer.fill( this, bytes_allowed_to_recv );
if ( bytes_rcvd )
*bytes_rcvd += received;
}
if ( m_state == STATE_CLOSING )
{
if ( send_buffer.empty() ) m_state = STATE_CLOSED;
}
break;
default:
break;
}
}
void WAC_Network_Connection::on_socket_connected( void )
{
return;
}
void WAC_Network_Connection::close( int quick )
{
if ( quick || m_state == STATE_RESOLVING || m_state == STATE_CONNECTING )
{
m_state = STATE_CLOSED;
/*
** Joshua Teitelbaum 1/27/2006
** virualization for ssl
*/
socket_shutdown();
m_socket = -1;
recv_buffer.clear();
send_buffer.clear();
m_remote_port = 0;
m_host[ 0 ] = 0;
//memset(&m_saddr,0,sizeof(m_saddr));
}
else
{
if ( m_state == STATE_CONNECTED )
m_state = STATE_CLOSING;
}
}
size_t WAC_Network_Connection::send_bytes_in_queue( void )
{
return send_buffer.size();
}
size_t WAC_Network_Connection::send_bytes_available( void )
{
return send_buffer.avail();
}
int WAC_Network_Connection::send( const void *data, size_t length )
{
if ( length > send_bytes_available() )
return -1;
send_buffer.write( data, length );
return 0;
}
int WAC_Network_Connection::send_string( const char *line )
{
return send( line, strlen( line ) );
}
size_t WAC_Network_Connection::recv_bytes_available( void )
{
return recv_buffer.size();
}
size_t WAC_Network_Connection::peek_bytes( void *data, size_t maxlength )
{
if ( data )
return recv_buffer.peek( data, maxlength );
else
return min( maxlength, recv_bytes_available() );
}
size_t WAC_Network_Connection::recv_bytes( void *data, size_t maxlength )
{
if ( data )
return recv_buffer.read( data, maxlength );
else
return recv_buffer.advance( maxlength );
}
int WAC_Network_Connection::recv_lines_available( void )
{
int l = (int)recv_bytes_available();
int lcount = 0;
int lastch = 0;
for ( int pos = 0; pos < l; pos++ )
{
char t;
if ( recv_buffer.at( pos, &t, 1 ) != 1 )
return lcount;
if ( ( t == '\r' || t == '\n' ) && ( ( lastch != '\r' && lastch != '\n' ) || lastch == t ) )
lcount++;
lastch = t;
}
return lcount;
}
int WAC_Network_Connection::recv_line( char *line, size_t maxlength )
{
while ( maxlength-- )
{
char t;
if ( recv_buffer.read( &t, 1 ) == 0 )
{
*line = 0;
return 0;
}
if ( t == '\r' || t == '\n' )
{
char r;
if ( recv_buffer.peek( &r, 1 ) != 0 )
{
if ( ( r == '\r' || r == '\n' ) && r != t )
recv_buffer.advance( 1 );
}
*line = 0;
return 0;
}
*line++ = t;
}
return 1;
}
unsigned long WAC_Network_Connection::get_interface( void )
{
if ( m_socket == -1 )
return 0;
struct sockaddr_in sin;
memset( &sin, 0, sizeof( sin ) );
socklen_t len = sizeof( sin );
if ( ::getsockname( m_socket, (struct sockaddr *)&sin, &len ) )
return 0;
return (unsigned long)sin.sin_addr.s_addr;
}
unsigned long WAC_Network_Connection::get_remote()
{
// TODO: IPv6
if ( address )
{
sockaddr_in *ipv4 = (sockaddr_in *)address;
return ipv4->sin_addr.s_addr;
}
return 0;
}
unsigned short WAC_Network_Connection::get_remote_port()
{
return m_remote_port;
}
/* RingBuffer client function */
size_t WAC_Network_Connection::Read( void *dest, size_t len )
{
if ( !len )
return 0;
int res = (int)socket_recv( (char *)dest, len, 0 );
if ( res == 0 || ( res < 0 && ERRNO != EWOULDBLOCK ) )
{
m_state = STATE_CLOSED;
return 0;
}
if ( res > 0 )
return res;
else
return 0;
}
/* RingBuffer client function */
size_t WAC_Network_Connection::Write( const void *dest, size_t len )
{
if ( !len )
return 0;
int res = (int)socket_send( (const char *)dest, len, 0 );
if ( res == -1 && ERRNO != EWOULDBLOCK )
{
return 0;
// m_state=STATE_CLOSED;
}
if ( res > 0 )
return res;
else
return 0;
}
int WAC_Network_Connection::set_recv_buffer_size( size_t new_buffer_size )
{
return recv_buffer.expand( new_buffer_size );
}
void WAC_Network_Connection::reuse()
{
if ( m_state == STATE_CLOSED )
{
m_state = STATE_CONNECTED;
recv_buffer.clear();
}
}
#define CBCLASS WAC_Network_Connection
START_DISPATCH;
VCB( API_CONNECTION_OPEN, open )
case API_CONNECTION_CONNECT: connect( *(char **)( params[ 0 ] ), *(int *)( params[ 1 ] ) ); return 1;
VCB( API_CONNECTION_RUN, run )
CB( API_CONNECTION_GETSTATE, get_state )
CB( API_CONNECTION_GETERROR, get_errstr )
VCB( API_CONNECTION_CLOSE, close )
VCB( API_CONNECTION_FLUSHSEND, flush_send )
CB( API_CONNECTION_GETSENDBYTESINQUEUE, send_bytes_in_queue )
CB( API_CONNECTION_GETSENDBYTESAVAILABLE, send_bytes_available )
CB( API_CONNECTION_SEND, send )
CB( API_CONNECTION_SENDBYTES, send_bytes )
CB( API_CONNECTION_SENDSTRING, send_string )
CB( API_CONNECTION_GETRECEIVEBYTESAVAILABLE, recv_bytes_available )
CB( API_CONNECTION_RECEIVEBYTES, recv_bytes )
CB( API_CONNECTION_GETRECEIVELINESAVAILABLE, recv_lines_available )
CB( API_CONNECTION_RECEIVELINE, recv_line )
CB( API_CONNECTION_PEEKBYTES, peek_bytes )
CB( API_CONNECTION_GETINTERFACE, get_interface )
CB( API_CONNECTION_GETREMOTEADDRESS, get_remote )
CB( API_CONNECTION_GETREMOTEPORT, get_remote_port )
END_DISPATCH;
#undef CBCLASS