Archived
1
0
This repository has been archived on 2024-10-17. You can view files and clone it, but cannot push or open issues or pull requests.
winamp/Src/replicant/jnetlib/connection.cpp
2024-09-24 14:54:57 +02:00

534 lines
10 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 "connection.h"
#include "asyncdns.h"
#include "foundation\error.h"
#ifndef min
#define min(X,Y) ((X) < (Y) ? (X) : (Y))
#endif
JNL_Connection::JNL_Connection()
{
init();
}
JNL_Connection::JNL_Connection(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
{
init();
open(dns, sendbufsize, recvbufsize);
}
void JNL_Connection::init()
{
m_errorstr="";
address=0;
m_dns=0;
m_dns_owned=false;
m_socket=-1;
m_remote_port=0;
m_state=STATE_NOCONNECTION;
m_host[0]=0;
saddr=0;
}
JNL_Connection::~JNL_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 m_dns;
}
void JNL_Connection::set_dns(JNL_AsyncDNS *dns)
{
if (m_dns_owned)
delete static_cast<JNL_AsyncDNS *>(m_dns);
m_dns=dns;
m_dns_owned=false;
}
void JNL_Connection::open(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
{
if (dns != JNL_AUTODNS && dns)
{
m_dns=dns;
m_dns_owned=false;
}
else if (!m_dns)
{
m_dns=new JNL_AsyncDNS;
m_dns_owned=true;
}
recv_buffer.reserve(recvbufsize);
send_buffer.reserve(sendbufsize);
}
void JNL_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="invalid socket passed to connect";
m_state=STATE_ERROR;
}
}
void JNL_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="empty hostname";
m_state=STATE_ERROR;
}
else
{
m_state=STATE_RESOLVING;
}
}
/*
** Joshua Teitelbaum 1/27/2006
** socket_shutdown
** virtualization for ssl
*/
/* Virtual */
void JNL_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 JNL_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 JNL_Connection::socket_send(const char *buf, size_t len, int options)
{
return ::send(m_socket,buf,(int)len,options);
}
int JNL_Connection::socket_connect()
{
return ::connect(m_socket, saddr->ai_addr, (int)saddr->ai_addrlen);
}
void JNL_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="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="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!=JNL_EINPROGRESS)
{
m_errorstr="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="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="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 JNL_Connection::on_socket_connected(void)
{
return;
}
void JNL_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 JNL_Connection::send_bytes_in_queue(void)
{
return send_buffer.size();
}
size_t JNL_Connection::send_bytes_available(void)
{
return send_buffer.avail();
}
int JNL_Connection::send(const void *data, size_t length)
{
if (length > send_bytes_available())
return -1;
send_buffer.write(data, length);
return 0;
}
int JNL_Connection::send_string(const char *line)
{
return send(line,strlen(line));
}
size_t JNL_Connection::recv_bytes_available(void)
{
return recv_buffer.size();
}
size_t JNL_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 JNL_Connection::recv_bytes(void *data, size_t maxlength)
{
if (data)
return recv_buffer.read(data, maxlength);
else
return recv_buffer.advance(maxlength);
}
int JNL_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 JNL_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 JNL_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 JNL_Connection::get_remote()
{
// TODO: IPv6
if (address)
{
sockaddr_in *ipv4 = (sockaddr_in *)address;
return ipv4->sin_addr.s_addr;
}
return 0;
}
unsigned short JNL_Connection::get_remote_port()
{
return m_remote_port;
}
/* RingBuffer client function */
size_t JNL_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 != JNL_EWOULDBLOCK))
{
m_state=STATE_CLOSED;
return 0;
}
if (res > 0)
return res;
else
return 0;
}
/* RingBuffer client function */
size_t JNL_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 != JNL_EWOULDBLOCK)
{
return 0;
// m_state=STATE_CLOSED;
}
if (res > 0)
return res;
else
return 0;
}
int JNL_Connection::set_recv_buffer_size(size_t new_buffer_size)
{
return recv_buffer.expand(new_buffer_size);
}
void JNL_Connection::reuse()
{
if (m_state == STATE_CLOSED)
{
m_state = STATE_CONNECTED;
recv_buffer.clear();
}
}