aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/jnetlib/connection.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/jnetlib/connection.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/replicant/jnetlib/connection.cpp')
-rw-r--r--Src/replicant/jnetlib/connection.cpp533
1 files changed, 533 insertions, 0 deletions
diff --git a/Src/replicant/jnetlib/connection.cpp b/Src/replicant/jnetlib/connection.cpp
new file mode 100644
index 00000000..4aacdd47
--- /dev/null
+++ b/Src/replicant/jnetlib/connection.cpp
@@ -0,0 +1,533 @@
+/*
+** 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();
+ }
+}