521 lines
12 KiB
C++
Executable File
521 lines
12 KiB
C++
Executable File
/*
|
|
** JNetLib
|
|
** Copyright (C) 2000-2001 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"
|
|
|
|
|
|
JNL_Connection::JNL_Connection(JNL_AsyncDNS *dns, int sendbufsize, int recvbufsize)
|
|
{
|
|
m_errorstr="";
|
|
if (dns == JNL_CONNECTION_AUTODNS)
|
|
{
|
|
m_dns=new JNL_AsyncDNS();
|
|
m_dns_owned=1;
|
|
}
|
|
else
|
|
{
|
|
m_dns=dns;
|
|
m_dns_owned=0;
|
|
}
|
|
m_recv_buffer_len=recvbufsize;
|
|
m_send_buffer_len=sendbufsize;
|
|
m_recv_buffer=(char*)malloc(m_recv_buffer_len);
|
|
m_send_buffer=(char*)malloc(m_send_buffer_len);
|
|
m_socket=-1;
|
|
memset(m_recv_buffer,0,recvbufsize);
|
|
memset(m_send_buffer,0,sendbufsize);
|
|
m_remote_port=0;
|
|
m_state=STATE_NOCONNECTION;
|
|
m_recv_len=m_recv_pos=0;
|
|
m_send_len=m_send_pos=0;
|
|
m_host[0]=0;
|
|
m_saddr = new struct sockaddr_in;
|
|
memset(m_saddr,0,sizeof(m_saddr));
|
|
}
|
|
|
|
void JNL_Connection::connect(int s, struct sockaddr_in *loc)
|
|
{
|
|
close(1);
|
|
m_socket=s;
|
|
m_remote_port=0;
|
|
m_dns=NULL;
|
|
if (loc) *m_saddr=*loc;
|
|
else memset(m_saddr,0,sizeof(m_saddr));
|
|
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(char *hostname, int port)
|
|
{
|
|
close(1);
|
|
m_remote_port=(short)port;
|
|
m_socket=::socket(AF_INET,SOCK_STREAM,0);
|
|
if (m_socket==-1)
|
|
{
|
|
m_errorstr="creating socket";
|
|
m_state=STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
SET_SOCK_BLOCK(m_socket,0);
|
|
strncpy(m_host,hostname,sizeof(m_host)-1);
|
|
m_host[sizeof(m_host)-1]=0;
|
|
memset(m_saddr,0,sizeof(m_saddr));
|
|
if (!m_host[0])
|
|
{
|
|
m_errorstr="empty hostname";
|
|
m_state=STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
m_state=STATE_RESOLVING;
|
|
m_saddr->sin_family=AF_INET;
|
|
m_saddr->sin_port=htons((unsigned short)port);
|
|
m_saddr->sin_addr.s_addr=inet_addr(hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** 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 */
|
|
int JNL_Connection::socket_recv(char *buf, int len, int options)
|
|
{
|
|
return ::recv(m_socket,buf,len,options);
|
|
}
|
|
/*
|
|
** Joshua Teitelbaum 1/27/2006
|
|
** socket_send
|
|
** virtualization for ssl
|
|
*/
|
|
/* Virtual */
|
|
int JNL_Connection::socket_send(char *buf, int len, int options)
|
|
{
|
|
return ::send(m_socket,buf,len,options);
|
|
}
|
|
|
|
int JNL_Connection::socket_connect()
|
|
{
|
|
return ::connect(m_socket,(struct sockaddr *)m_saddr,16);
|
|
}
|
|
JNL_Connection::~JNL_Connection()
|
|
{
|
|
/*
|
|
** Joshua Teitelbaum 1/27/2006
|
|
** virtualization for ssl, calling socket_shtudown()
|
|
*/
|
|
socket_shutdown();
|
|
|
|
free(m_recv_buffer);
|
|
free(m_send_buffer);
|
|
if (m_dns_owned)
|
|
{
|
|
delete m_dns;
|
|
}
|
|
delete m_saddr;
|
|
}
|
|
|
|
void JNL_Connection::run(int max_send_bytes, int max_recv_bytes, int *bytes_sent, int *bytes_rcvd)
|
|
{
|
|
int bytes_allowed_to_send=(max_send_bytes<0)?m_send_buffer_len:max_send_bytes;
|
|
int bytes_allowed_to_recv=(max_recv_bytes<0)?m_recv_buffer_len:max_recv_bytes;
|
|
|
|
if (bytes_sent) *bytes_sent=0;
|
|
if (bytes_rcvd) *bytes_rcvd=0;
|
|
|
|
switch (m_state)
|
|
{
|
|
case STATE_RESOLVING:
|
|
if (m_saddr->sin_addr.s_addr == INADDR_NONE)
|
|
{
|
|
int a=m_dns?m_dns->resolve(m_host,(unsigned long int *)&m_saddr->sin_addr.s_addr):-1;
|
|
if (!a) { m_state=STATE_CONNECTING; }
|
|
else if (a == 1)
|
|
{
|
|
m_state=STATE_RESOLVING;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_errorstr="resolving hostname";
|
|
m_state=STATE_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
/*
|
|
** Joshua Teitelbaum 1/27/2006
|
|
** virtualization for ssl
|
|
*/
|
|
if(!socket_connect())
|
|
{
|
|
m_state=STATE_CONNECTED;
|
|
on_socket_connected();
|
|
}
|
|
else if (ERRNO!=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(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:
|
|
if (m_send_len>0 && bytes_allowed_to_send>0)
|
|
{
|
|
int len=m_send_buffer_len-m_send_pos;
|
|
if (len > m_send_len) len=m_send_len;
|
|
if (len > bytes_allowed_to_send) len=bytes_allowed_to_send;
|
|
if (len > 0)
|
|
{
|
|
int res=socket_send(m_send_buffer+m_send_pos,len,0);
|
|
if (res==-1 && ERRNO != EWOULDBLOCK)
|
|
{
|
|
// m_state=STATE_CLOSED;
|
|
// return;
|
|
}
|
|
if (res>0)
|
|
{
|
|
bytes_allowed_to_send-=res;
|
|
if (bytes_sent) *bytes_sent+=res;
|
|
m_send_pos+=res;
|
|
m_send_len-=res;
|
|
}
|
|
}
|
|
if (m_send_pos>=m_send_buffer_len)
|
|
{
|
|
m_send_pos=0;
|
|
if (m_send_len>0)
|
|
{
|
|
len=m_send_buffer_len-m_send_pos;
|
|
if (len > m_send_len) len=m_send_len;
|
|
if (len > bytes_allowed_to_send) len=bytes_allowed_to_send;
|
|
int res=socket_send(m_send_buffer+m_send_pos,len,0);
|
|
if (res==-1 && ERRNO != EWOULDBLOCK)
|
|
{
|
|
// m_state=STATE_CLOSED;
|
|
}
|
|
if (res>0)
|
|
{
|
|
bytes_allowed_to_send-=res;
|
|
if (bytes_sent) *bytes_sent+=res;
|
|
m_send_pos+=res;
|
|
m_send_len-=res;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_recv_len<m_recv_buffer_len)
|
|
{
|
|
int len=m_recv_buffer_len-m_recv_pos;
|
|
if (len > m_recv_buffer_len-m_recv_len) len=m_recv_buffer_len-m_recv_len;
|
|
if (len > bytes_allowed_to_recv) len=bytes_allowed_to_recv;
|
|
if (len>0)
|
|
{
|
|
/*
|
|
** Joshua Teitelbaum 1/27/2006
|
|
** virtualization for SSL
|
|
*/
|
|
int res = socket_recv(m_recv_buffer+m_recv_pos,len,0);
|
|
|
|
if (res == 0 || (res < 0 && ERRNO != EWOULDBLOCK))
|
|
{
|
|
m_state=STATE_CLOSED;
|
|
break;
|
|
}
|
|
if (res > 0)
|
|
{
|
|
bytes_allowed_to_recv-=res;
|
|
if (bytes_rcvd) *bytes_rcvd+=res;
|
|
m_recv_pos+=res;
|
|
m_recv_len+=res;
|
|
}
|
|
}
|
|
if (m_recv_pos >= m_recv_buffer_len)
|
|
{
|
|
m_recv_pos=0;
|
|
if (m_recv_len < m_recv_buffer_len)
|
|
{
|
|
len=m_recv_buffer_len-m_recv_len;
|
|
if (len > bytes_allowed_to_recv) len=bytes_allowed_to_recv;
|
|
if (len > 0)
|
|
{
|
|
int res=socket_recv(m_recv_buffer+m_recv_pos,len,0);
|
|
if (res == 0 || (res < 0 && ERRNO != EWOULDBLOCK))
|
|
{
|
|
m_state=STATE_CLOSED;
|
|
break;
|
|
}
|
|
if (res > 0)
|
|
{
|
|
bytes_allowed_to_recv-=res;
|
|
if (bytes_rcvd) *bytes_rcvd+=res;
|
|
m_recv_pos+=res;
|
|
m_recv_len+=res;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_state == STATE_CLOSING)
|
|
{
|
|
if (m_send_len < 1) 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;
|
|
memset(m_recv_buffer,0,m_recv_buffer_len);
|
|
memset(m_send_buffer,0,m_send_buffer_len);
|
|
m_remote_port=0;
|
|
m_recv_len=m_recv_pos=0;
|
|
m_send_len=m_send_pos=0;
|
|
m_host[0]=0;
|
|
memset(m_saddr,0,sizeof(m_saddr));
|
|
}
|
|
else
|
|
{
|
|
if (m_state == STATE_CONNECTED) m_state=STATE_CLOSING;
|
|
}
|
|
}
|
|
|
|
int JNL_Connection::send_bytes_in_queue(void)
|
|
{
|
|
return m_send_len;
|
|
}
|
|
|
|
int JNL_Connection::send_bytes_available(void)
|
|
{
|
|
return m_send_buffer_len-m_send_len;
|
|
}
|
|
|
|
int JNL_Connection::send(const void *_data, int length)
|
|
{
|
|
const char *data = static_cast<const char *>(_data);
|
|
if (length > send_bytes_available())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int write_pos=m_send_pos+m_send_len;
|
|
if (write_pos >= m_send_buffer_len)
|
|
{
|
|
write_pos-=m_send_buffer_len;
|
|
}
|
|
|
|
int len=m_send_buffer_len-write_pos;
|
|
if (len > length)
|
|
{
|
|
len=length;
|
|
}
|
|
|
|
memcpy(m_send_buffer+write_pos,data,len);
|
|
if (length > len)
|
|
{
|
|
memcpy(m_send_buffer,data+len,length-len);
|
|
}
|
|
m_send_len+=length;
|
|
return 0;
|
|
}
|
|
|
|
int JNL_Connection::send_string(const char *line)
|
|
{
|
|
return send(line,strlen(line));
|
|
}
|
|
|
|
int JNL_Connection::recv_bytes_available(void)
|
|
{
|
|
return m_recv_len;
|
|
}
|
|
|
|
int JNL_Connection::peek_bytes(void *_data, int maxlength)
|
|
{
|
|
char *data = static_cast<char *>(_data);
|
|
if (maxlength > m_recv_len)
|
|
{
|
|
maxlength=m_recv_len;
|
|
}
|
|
int read_pos=m_recv_pos-m_recv_len;
|
|
if (read_pos < 0)
|
|
{
|
|
read_pos += m_recv_buffer_len;
|
|
}
|
|
int len=m_recv_buffer_len-read_pos;
|
|
if (len > maxlength)
|
|
{
|
|
len=maxlength;
|
|
}
|
|
if (data != NULL) {
|
|
memcpy(data,m_recv_buffer+read_pos,len);
|
|
if (len < maxlength)
|
|
{
|
|
memcpy(data+len,m_recv_buffer,maxlength-len);
|
|
}
|
|
}
|
|
|
|
return maxlength;
|
|
}
|
|
|
|
int JNL_Connection::recv_bytes(void *_data, int maxlength)
|
|
{
|
|
char *data = static_cast<char *>(_data);
|
|
|
|
int ml=peek_bytes(data,maxlength);
|
|
m_recv_len-=ml;
|
|
return ml;
|
|
}
|
|
|
|
int JNL_Connection::getbfromrecv(int pos, int remove)
|
|
{
|
|
int read_pos=m_recv_pos-m_recv_len + pos;
|
|
if (pos < 0 || pos > m_recv_len) return -1;
|
|
if (read_pos < 0)
|
|
{
|
|
read_pos += m_recv_buffer_len;
|
|
}
|
|
if (read_pos >= m_recv_buffer_len)
|
|
{
|
|
read_pos-=m_recv_buffer_len;
|
|
}
|
|
if (remove) m_recv_len--;
|
|
return m_recv_buffer[read_pos];
|
|
}
|
|
|
|
int JNL_Connection::recv_lines_available(void)
|
|
{
|
|
int l=recv_bytes_available();
|
|
int lcount=0;
|
|
int lastch=0;
|
|
int pos;
|
|
for (pos=0; pos < l; pos ++)
|
|
{
|
|
int t=getbfromrecv(pos,0);
|
|
if (t == -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, int maxlength)
|
|
{
|
|
if (maxlength > m_recv_len) maxlength=m_recv_len;
|
|
while (maxlength--)
|
|
{
|
|
int t=getbfromrecv(0,1);
|
|
if (t == -1)
|
|
{
|
|
*line=0;
|
|
return 0;
|
|
}
|
|
if (t == '\r' || t == '\n')
|
|
{
|
|
int r=getbfromrecv(0,0);
|
|
if ((r == '\r' || r == '\n') && r != t) getbfromrecv(0,1);
|
|
*line=0;
|
|
return 0;
|
|
}
|
|
*line++=(char)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=16;
|
|
if (::getsockname(m_socket,(struct sockaddr *)&sin,&len)) return 0;
|
|
return (unsigned long) sin.sin_addr.s_addr;
|
|
}
|
|
|
|
unsigned long JNL_Connection::get_remote()
|
|
{
|
|
return m_saddr->sin_addr.s_addr;
|
|
}
|
|
|
|
short JNL_Connection::get_remote_port()
|
|
{
|
|
return m_remote_port;
|
|
}
|