diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/jnetlib | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/replicant/jnetlib')
34 files changed, 6304 insertions, 0 deletions
diff --git a/Src/replicant/jnetlib/VERSION b/Src/replicant/jnetlib/VERSION new file mode 100644 index 00000000..ea710abb --- /dev/null +++ b/Src/replicant/jnetlib/VERSION @@ -0,0 +1 @@ +1.2
\ No newline at end of file diff --git a/Src/replicant/jnetlib/asyncdns.cpp b/Src/replicant/jnetlib/asyncdns.cpp new file mode 100644 index 00000000..2a680216 --- /dev/null +++ b/Src/replicant/jnetlib/asyncdns.cpp @@ -0,0 +1,314 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: asyncdns.cpp - JNL portable asynchronous DNS implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "asyncdns.h" +#include <time.h> +#ifdef _WIN32 +#include <strsafe.h> +#endif + +enum +{ + MODE_RESOLVE=0, + MODE_REVERSE=1, +}; + + +struct cache_entry +{ + time_t last_used; // timestamp. + bool resolved; + int mode; // 1=reverse + unsigned short port; + char hostname[256]; + addrinfo *addr; + int sockettype; +}; + +JNL_AsyncDNS::JNL_AsyncDNS(int max_cache_entries) +{ + m_thread_kill=1; + m_thread=0; + m_cache_size=max_cache_entries; + m_cache=(cache_entry *)malloc(sizeof(cache_entry)*m_cache_size); + memset(m_cache, 0, sizeof(cache_entry)*m_cache_size); +} + +JNL_AsyncDNS::~JNL_AsyncDNS() +{ + m_thread_kill=1; + +#ifdef _WIN32 + if (m_thread) + { + WaitForSingleObject(m_thread,INFINITE); + CloseHandle(m_thread); + } +#else + if (m_thread) + { + void *p; + pthread_join(m_thread,&p); + } +#endif//!_WIN32 + // free all the addrinfo stuff + for (int x = 0; x < m_cache_size; x ++) + { + if (m_cache[x].addr) + freeaddrinfo(m_cache[x].addr); + } + + free(m_cache); +} + +int JNL_AsyncDNS::resolvenow(const char *hostname, unsigned short port, addrinfo **addr, int sockettype) +{ + addrinfo hints; + memset(&hints,0,sizeof(hints)); + hints.ai_family = PF_UNSPEC; + if (hostname) + hints.ai_flags = AI_NUMERICHOST; + else + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + hints.ai_socktype = sockettype; + + char portString[32] = {0}; + sprintf(portString, "%u", (unsigned int)port); + + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + return 0; + } + else + { + hints.ai_flags = 0; + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + return 0; + } + else + { + return -1; + } + } +} + +#ifdef _WIN32 +unsigned long WINAPI JNL_AsyncDNS::_threadfunc(LPVOID _d) +#else +unsigned int JNL_AsyncDNS::_threadfunc(void *_d) +#endif +{ + int nowinsock=JNL::open_socketlib(); + JNL_AsyncDNS *_this=(JNL_AsyncDNS*)_d; + int x; + for (x = 0; x < _this->m_cache_size && !_this->m_thread_kill; x ++) + { + if (_this->m_cache[x].last_used && !_this->m_cache[x].resolved) + { + if (!nowinsock) + { + if (_this->m_cache[x].mode==0) + { + addrinfo *res=0; + if (resolvenow(_this->m_cache[x].hostname, _this->m_cache[x].port, &res, _this->m_cache[x].sockettype) == 0) + { + _this->m_cache[x].addr=res; + } + else + { + _this->m_cache[x].addr=0;//INADDR_NONE; + } + } + else if (_this->m_cache[x].mode==1) + { + /* + hostent *ent; + // TODO: replace with getnameinfo for IPv6 + ent=gethostbyaddr((const char *)&_this->m_cache[x].addr,4,AF_INET); + if (ent) + lstrcpyn(_this->m_cache[x].hostname, ent->h_name, 256); + else + _this->m_cache[x].hostname[0]=0; + */ + } + _this->m_cache[x].resolved=true; + } + else + { + if (_this->m_cache[x].mode==0) + { + _this->m_cache[x].addr=0;//INADDR_NONE; + _this->m_cache[x].resolved=true; + } + else if (_this->m_cache[x].mode==1) + { + _this->m_cache[x].hostname[0]=0; + _this->m_cache[x].resolved=true; + } + } + } + } + if (!nowinsock) JNL::close_socketlib(); + _this->m_thread_kill=1; + + return 0; +} + +int JNL_AsyncDNS::resolve(const char *hostname, unsigned short port, addrinfo **addr, int sockettype) +{ + // return 0 on success, 1 on wait, -1 on unresolvable + int x; + + for (x = 0; x < m_cache_size; x ++) + { + if (!strcasecmp(m_cache[x].hostname,hostname) && port == m_cache[x].port && m_cache[x].mode==0 && m_cache[x].sockettype==sockettype) + { + m_cache[x].last_used=time(0); + if (m_cache[x].resolved) + { + if (m_cache[x].addr == 0)//INADDR_NONE) + { + return DNS_RESOLVE_UNRESOLVABLE; + } + *addr =m_cache[x].addr; + return DNS_RESOLVE_SUCCESS; + } + makesurethreadisrunning(); + return DNS_RESOLVE_WAIT; + } + } + // add to resolve list + int oi=-1; + for (x = 0; x < m_cache_size; x ++) + { + if (!m_cache[x].last_used) + { + oi=x; + break; + } + if ((oi==-1 || m_cache[x].last_used < m_cache[oi].last_used) && m_cache[x].resolved) + { + oi=x; + } + } + if (oi == -1) + { + return DNS_RESOLVE_UNRESOLVABLE; + } +#ifdef _WIN32 + StringCchCopyA(m_cache[oi].hostname, 256, hostname); +#elif defined(__APPLE__) + strlcpy(m_cache[oi].hostname, hostname, 255); +#else + strncpy(m_cache[oi].hostname, hostname, 255); + m_cache[oi].hostname[255]=0; +#endif + m_cache[oi].port=port; + m_cache[oi].mode=0; + m_cache[oi].addr=0;//INADDR_NONE; + m_cache[oi].resolved=false; + m_cache[oi].last_used=time(0); + m_cache[oi].sockettype=sockettype; + + makesurethreadisrunning(); + return DNS_RESOLVE_WAIT; +} + +/* +int JNL_AsyncDNS::reverse(unsigned long addr, char *hostname, size_t hostnameSize) +{ +// return 0 on success, 1 on wait, -1 on unresolvable +int x; +if (addr == INADDR_NONE) +{ +return DNS_REVERSE_UNRESOLVABLE; +} +#ifndef NO_DNS_SUPPORT +for (x = 0; x < m_cache_size; x ++) +{ +if (m_cache[x].addr==addr && m_cache[x].mode==1) +{ +m_cache[x].last_used=time(0); +if (m_cache[x].resolved) +{ +if (!m_cache[x].hostname[0]) +{ +return DNS_REVERSE_UNRESOLVABLE; +} +lstrcpyn(hostname,m_cache[x].hostname, hostnameSize); +return DNS_REVERSE_SUCCESS; +} +makesurethreadisrunning(); +return DNS_REVERSE_WAIT; +} +} +// add to resolve list +int oi=-1; +for (x = 0; x < m_cache_size; x ++) +{ +if (!m_cache[x].last_used) +{ +oi=x; +break; +} +if ((oi==-1 || m_cache[x].last_used < m_cache[oi].last_used) && m_cache[x].resolved) +{ +oi=x; +} +} +if (oi == -1) +{ +return DNS_REVERSE_UNRESOLVABLE; +} +m_cache[oi].addr=addr; +m_cache[oi].hostname[0]=0; +m_cache[oi].resolved=false; +m_cache[oi].mode=1; +m_cache[oi].last_used=time(0); + +makesurethreadisrunning(); +return DNS_REVERSE_WAIT; +#else +return DNS_REVERSE_UNRESOLVABLE; +#endif +} +*/ + +void JNL_AsyncDNS::makesurethreadisrunning(void) +{ + if (m_thread_kill) + { +#ifdef _WIN32 + if (m_thread) + { + WaitForSingleObject(m_thread,INFINITE); + CloseHandle(m_thread); + } + DWORD id; + m_thread_kill=0; + m_thread=CreateThread(NULL,0,_threadfunc,(LPVOID)this,0,&id); + if (!m_thread) + { +#else + if (m_thread) + { + void *p; + pthread_join(m_thread,&p); + } + m_thread_kill=0; + if (pthread_create(&m_thread,NULL,(void *(*) (void *))_threadfunc,(void*)this) != 0) + { +#endif + m_thread_kill=1; + } + } +} + diff --git a/Src/replicant/jnetlib/asyncdns.h b/Src/replicant/jnetlib/asyncdns.h new file mode 100644 index 00000000..b346eccd --- /dev/null +++ b/Src/replicant/jnetlib/asyncdns.h @@ -0,0 +1,66 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: asyncdns.h - JNL portable asynchronous DNS interface +** License: see jnetlib.h +** +** Usage: +** 1. Create JNL_AsyncDNS object, optionally with the number of cache entries. +** 2. call resolve() to resolve a hostname into an address. The return value of +** resolve is 0 on success (host successfully resolved), 1 on wait (meaning +** try calling resolve() with the same hostname in a few hundred milliseconds +** or so), or -1 on error (i.e. the host can't resolve). +** 3. call reverse() to do reverse dns (ala resolve()). +** 4. enjoy. +*/ + +#ifndef _ASYNCDNS_H_ +#define _ASYNCDNS_H_ + +#include "netinc.h" + +struct cache_entry; + +#define JNL_AUTODNS ((JNL_AsyncDNS *)-1) +enum +{ + DNS_RESOLVE_UNRESOLVABLE = -1, + DNS_RESOLVE_SUCCESS = 0, + DNS_RESOLVE_WAIT = 1, +}; + +enum +{ + DNS_REVERSE_UNRESOLVABLE = -1, + DNS_REVERSE_SUCCESS = 0, + DNS_REVERSE_WAIT = 1, +}; + +class JNL_AsyncDNS +{ +public: + JNL_AsyncDNS( int max_cache_entries = 64 ); + ~JNL_AsyncDNS(); + + int resolve( const char *hostname, unsigned short port, addrinfo **addr, int sockettype ); // return 0 on success, 1 on wait, -1 on unresolvable + static int resolvenow( const char *hostname, unsigned short port, addrinfo **addr, int sockettype ); // return 0 on success, -1 on unresolvable + //int reverse(unsigned long addr, char *hostname, size_t hostnameSize); // return 0 on success, 1 on wait, -1 on unresolvable. hostname must be at least 256 bytes. + +private: + cache_entry *m_cache; + int m_cache_size; + volatile int m_thread_kill; + +#ifdef _WIN32 + HANDLE m_thread; + static unsigned long WINAPI _threadfunc( LPVOID _d ); +#else + pthread_t m_thread; + static unsigned int _threadfunc( void *_d ); +#endif + + void makesurethreadisrunning( void ); +}; + +#endif //_ASYNCDNS_H_ 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(); + } +} diff --git a/Src/replicant/jnetlib/connection.h b/Src/replicant/jnetlib/connection.h new file mode 100644 index 00000000..9d32cab2 --- /dev/null +++ b/Src/replicant/jnetlib/connection.h @@ -0,0 +1,168 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: connection.h - JNL TCP connection interface +** License: see jnetlib.h +** +** Usage: +** 1. Create a JNL_Connection object, optionally specifying a JNL_AsyncDNS +** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto), +** and the send and receive buffer sizes. +** 2. Call connect() to have it connect to a host/port (the hostname will be +** resolved if possible). +** 3. call run() with the maximum send/recv amounts, and optionally parameters +** so you can tell how much has been send/received. You want to do this a lot, while: +** 4. check get_state() to check the state of the connection. The states are: +** JNL_Connection::STATE_ERROR +** - an error has occured on the connection. the connection has closed, +** and you can no longer write to the socket (there still might be +** data in the receive buffer - use recv_bytes_available()). +** JNL_Connection::STATE_NOCONNECTION +** - no connection has been made yet. call connect() already! :) +** JNL_Connection::STATE_RESOLVING +** - the connection is still waiting for a JNL_AsycnDNS to resolve the +** host. +** JNL_Connection::STATE_CONNECTING +** - the asynchronous call to connect() is still running. +** JNL_Connection::STATE_CONNECTED +** - the connection has connected, all is well. +** JNL_Connection::STATE_CLOSING +** - the connection is closing. This happens after a call to close, +** without the quick parameter set. This means that the connection +** will close once the data in the send buffer is sent (data could +** still be being received when it would be closed). After it is +** closed, the state will transition to: +** JNL_Connection::STATE_CLOSED +** - the connection has closed, generally without error. There still +** might be data in the receieve buffer, use recv_bytes_available(). +** 5. Use send() and send_string() to send data. You can use +** send_bytes_in_queue() to see how much has yet to go out, or +** send_bytes_available() to see how much you can write. If you use send() +** or send_string() and not enough room is available, both functions will +** return error ( < 0) +** 6. Use recv() and recv_line() to get data. If you want to see how much data +** there is, use recv_bytes_available() and recv_lines_available(). If you +** call recv() and not enough data is available, recv() will return how much +** data was actually read. See comments at the function defs. +** +** 7. To close, call close(1) for a quick close, or close() for a close that will +** make the socket close after sending all the data sent. +** +** 8. delete ye' ol' object. +*/ + +#ifndef _CONNECTION_H_ +#define _CONNECTION_H_ + +#include "netinc.h" +#include "asyncdns.h" +#include "../nu/RingBuffer.h" +#include "jnetlib_defines.h" +#include <stddef.h> +#include "nswasabi/ReferenceCounted.h" + +#if defined(_MSC_VER) && (_MSC_VER < 1200) +typedef int intptr_t; +#endif + + +#define PACKET_SIZE 16384 + +class JNL_Connection : private Filler, private Drainer, public ReferenceCountedBase<JNL_Connection> +{ +public: + typedef enum + { + STATE_ERROR = JNL_CONNECTION_STATE_ERROR, + STATE_NOCONNECTION = JNL_CONNECTION_STATE_NOCONNECTION, + STATE_RESOLVING = JNL_CONNECTION_STATE_RESOLVING, + STATE_CONNECTING = JNL_CONNECTION_STATE_CONNECTING, + STATE_CONNECTED = JNL_CONNECTION_STATE_CONNECTED, + STATE_CLOSING = JNL_CONNECTION_STATE_CLOSING, + STATE_CLOSED = JNL_CONNECTION_STATE_CLOSED, + STATE_RESOLVED = JNL_CONNECTION_STATE_RESOLVED, + } state; + + /* + ** Joshua Teitelbaum, 1/27/2006 adding virtual + */ + JNL_Connection(); + JNL_Connection(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize); + virtual ~JNL_Connection(); + + void open( JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = 8192, size_t recvbufsize = 8192 ); + void connect( const char *hostname, int port ); + virtual void connect( SOCKET sock, sockaddr *addr, socklen_t length /* of addr */ ); // used by the listen object, usually not needed by users. + + int set_recv_buffer_size(size_t new_buffer_size); + /* + ** Joshua Teitelbaum 2/2/2006 + ** Need to make this virtual to ensure SSL can init properly + */ + virtual void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL ); + + int get_state() { return m_state; } + char *get_errstr() { return m_errorstr; } + + void close( int quick = 0 ); + void flush_send( void ) { send_buffer.clear(); } + + size_t send_bytes_in_queue( void ); + size_t send_bytes_available( void ); + int send( const void *data, size_t length ); // returns -1 if not enough room + inline int send_bytes( const void *data, size_t length ) { return send( data, length ); } + int send_string( const char *line ); // returns -1 if not enough room + + size_t recv_bytes_available( void ); + size_t recv_bytes( void *data, size_t maxlength ); // returns actual bytes read + unsigned int recv_int( void ); + int recv_lines_available( void ); + int recv_line( char *line, size_t maxlength ); // returns 0 if the line was terminated with a \r or \n, 1 if not. + // (i.e. if you specify maxlength=10, and the line is 12 bytes long + // it will return 1. or if there is no \r or \n and that's all the data + // the connection has.) + size_t peek_bytes( void *data, size_t maxlength ); // returns bytes peeked + + unsigned long get_interface( void ); // this returns the interface the connection is on + unsigned long get_remote( void ); // remote host ip. + unsigned short get_remote_port( void ); // this returns the remote port of connection + + void set_dns( JNL_AsyncDNS *dns ); + void reuse(); + +protected: + SOCKET m_socket; + unsigned short m_remote_port; + + RingBuffer recv_buffer; + RingBuffer send_buffer; + + addrinfo *saddr; + sockaddr *address; + + char m_host[256]; + + JNL_AsyncDNS *m_dns; + bool m_dns_owned; + + state m_state; + char *m_errorstr; + + /* + ** Joshua Teitelbaum 1/27/2006 Adding new BSD socket analogues for SSL compatibility + */ + virtual void socket_shutdown(); + virtual ssize_t socket_recv( char *buf, size_t len, int options ); + virtual ssize_t socket_send( const char *buf, size_t len, int options ); + virtual int socket_connect(); + virtual void on_socket_connected(); + +private: + void init(); // constructor helper function + + // functions for RingBuffer + size_t Read( void *dest, size_t len ) override; + size_t Write( const void *dest, size_t len ) override; +}; +#endif // _Connection_H_ diff --git a/Src/replicant/jnetlib/headers.cpp b/Src/replicant/jnetlib/headers.cpp new file mode 100644 index 00000000..7e702cd2 --- /dev/null +++ b/Src/replicant/jnetlib/headers.cpp @@ -0,0 +1,119 @@ +#include "foundation/error.h" +#include "foundation/types.h" + +#include "headers.h" +#include "netinc.h" + +#include <stdlib.h> +#include <string.h> + +JNL_Headers::JNL_Headers() +{ + m_recvheaders = NULL; + m_recvheaders_size = 0; +} + +JNL_Headers::~JNL_Headers() +{ + if ( m_recvheaders ) + free( m_recvheaders ); +} + +void JNL_Headers::Reset() +{ + if ( m_recvheaders ) + free( m_recvheaders ); + + m_recvheaders = NULL; + m_recvheaders_size = 0; +} + +const char *JNL_Headers::GetAllHeaders() +{ + // double null terminated, null delimited list + if ( m_recvheaders ) + return m_recvheaders; + else + return "\0\0"; +} + +const char *JNL_Headers::GetHeader( const char *headername ) +{ + char *ret = NULL; + + if ( headername[ 0 ] == 0 || !m_recvheaders ) + return NULL; + + size_t headername_size = strlen( headername ); + char *buf = (char *)malloc( headername_size + 2 ); + strcpy( buf, headername ); + + if ( buf[ headername_size - 1 ] != ':' ) + { + buf[ headername_size++ ] = ':'; + buf[ headername_size ] = 0; + } + + char *p = m_recvheaders; + while ( p && *p ) + { + if ( !strncasecmp( buf, p, headername_size ) ) + { + ret = p + headername_size; + while ( ret && *ret && *ret == ' ' ) + ret++; + + break; + } + + p += strlen( p ) + 1; + } + + free( buf ); + + return ret; +} + +int JNL_Headers::Add( const char *buf ) +{ + if ( !m_recvheaders ) + { + m_recvheaders_size = strlen( buf ) + 1; + if ( m_recvheaders_size == 0 || m_recvheaders_size == (size_t)-1 ) // check for overflow + { + return NErr_OutOfMemory; + } + + m_recvheaders = (char *)malloc( m_recvheaders_size + 1 ); + if ( m_recvheaders ) + { + strcpy( m_recvheaders, buf ); // safe because we malloc'd specifically above + m_recvheaders[ m_recvheaders_size ] = 0; + } + else + { + return NErr_OutOfMemory; + } + } + else + { + size_t oldsize = m_recvheaders_size; + m_recvheaders_size += strlen( buf ) + 1; + if ( m_recvheaders_size + 1 < oldsize ) // check for overflow + { + return NErr_OutOfMemory; + } + + char *n = (char *)realloc( m_recvheaders, m_recvheaders_size + 1 ); + if ( !n ) + { + return NErr_OutOfMemory; + } + + strcpy( n + oldsize, buf ); // safe because we malloc specifially for the size + n[ m_recvheaders_size ] = 0; // double null terminate + m_recvheaders = n; + } + + return NErr_Success; +} diff --git a/Src/replicant/jnetlib/headers.h b/Src/replicant/jnetlib/headers.h new file mode 100644 index 00000000..9412ac74 --- /dev/null +++ b/Src/replicant/jnetlib/headers.h @@ -0,0 +1,20 @@ +#pragma once + + +// TODO: benski> change this to use a smarter data structure. +// this initial implementation is known to work, however +class JNL_Headers +{ +public: + JNL_Headers(); + ~JNL_Headers(); + + const char *GetAllHeaders(); + const char *GetHeader( const char *header_name ); + int Add( const char *buf ); + void Reset(); + +private: + char *m_recvheaders; + size_t m_recvheaders_size; +};
\ No newline at end of file diff --git a/Src/replicant/jnetlib/httpget.cpp b/Src/replicant/jnetlib/httpget.cpp new file mode 100644 index 00000000..d3a83452 --- /dev/null +++ b/Src/replicant/jnetlib/httpget.cpp @@ -0,0 +1,845 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: httpget.cpp - JNL HTTP GET implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "httpget.h" +#include "foundation/error.h" + +#ifdef USE_SSL +#include "sslconnection.h" +#endif + +#include <stdio.h> + +#define STRSAFE_NO_DEPRECATE + +#include "nu/strsafe.h" +#include "nu/AutoLock.h" + +char *JNL_HTTPGet::g_proxy = 0; + +static nu::LockGuard proxy_guard; + +char *JNL_HTTPGet::get_proxy() +{ + nu::AutoLock auto_lock( proxy_guard ); + + if ( g_proxy ) + return _strdup( g_proxy ); + else + return 0; +} + +void JNL_HTTPGet::set_proxy( const char *proxy ) +{ + nu::AutoLock auto_lock( proxy_guard ); + + free( g_proxy ); + + if ( proxy ) + g_proxy = _strdup( proxy ); + else + g_proxy = 0; +} + +JNL_HTTPGet::JNL_HTTPGet( size_t recvbufsize, size_t sendbufsize ) +{ + persistent = false; + accept_all_reply_codes = false; + zlibStream = 0; + allowCompression = false; + m_dns = JNL_AUTODNS; + m_con = NULL; + m_http_proxylpinfo = 0; + m_http_proxyhost = 0; + m_http_proxyport = 0; + m_sendbufsize = sendbufsize; + m_sendheaders = NULL; + + reinit(); + + m_recvbufsize = recvbufsize; + + char *p = get_proxy(); + if ( p ) + { + char *r = NULL; + do_parse_url( p, &m_http_proxyhost, &m_http_proxyport, &r, &m_http_proxylpinfo ); + + free( r ); + free( p ); + } +} + +JNL_HTTPGet::~JNL_HTTPGet() +{ + deinit(); + free( m_sendheaders ); + free( m_http_proxylpinfo ); + free( m_http_proxyhost ); +} + +void JNL_HTTPGet::reinit() +{ + m_errstr = 0; + m_recvheaders = NULL; + m_recvheaders_size = 0; + m_http_state = 0; + m_http_port = 0; + m_http_url = 0; + m_reply = 0; + m_http_host = m_http_lpinfo = m_http_request = NULL; +} + +void JNL_HTTPGet::deinit( bool full ) +{ + if ( !persistent || full || ( m_con && m_con->get_state() == JNL_Connection::STATE_ERROR ) ) + { + delete m_con; + m_con = NULL; + } + + free( m_recvheaders ); + free( m_http_url ); + free( m_http_host ); + free( m_http_lpinfo ); + free( m_http_request ); + free( m_errstr ); + free( m_reply ); + + if ( zlibStream ) + inflateEnd( zlibStream ); + + free( zlibStream ); + zlibStream = 0; + + reinit(); +} + +void JNL_HTTPGet::set_sendbufsize(size_t sendbufsize) +{ + m_sendbufsize = sendbufsize; +} + +int JNL_HTTPGet::set_recv_buffer_size( size_t new_buffer_size ) +{ + if ( m_con ) + { + int ret = m_con->set_recv_buffer_size( new_buffer_size ); + if ( ret == NErr_NoAction )// this will get returned if new_buffer_size is smaller than existing. + return NErr_Success; + else if ( ret != NErr_Success ) + return ret; + } + + m_recvbufsize = new_buffer_size; + return NErr_Success; +} + +void JNL_HTTPGet::addheader( const char *header ) +{ + if ( strstr( header, "\r" ) || strstr( header, "\n" ) ) + return; + + if ( !m_sendheaders ) + { + size_t len = strlen( header ) + 3; + m_sendheaders = (char *)malloc( len ); + if ( m_sendheaders ) + { + char *itr = m_sendheaders; + StringCchCopyExA( itr, len, header, &itr, &len, 0 ); + StringCchCatExA( itr, len, "\r\n", &itr, &len, 0 ); + } + } + else + { + size_t len = strlen( header ) + strlen( m_sendheaders ) + 1 + 2; + char *t = (char *)malloc( len ); + if ( t ) + { + char *newHeaders = t; + StringCchCopyExA( t, len, m_sendheaders, &t, &len, 0 ); + StringCchCatExA( t, len, header, &t, &len, 0 ); + StringCchCatExA( t, len, "\r\n", &t, &len, 0 ); + free( m_sendheaders ); + m_sendheaders = newHeaders; + } + } +} + +void JNL_HTTPGet::addheadervalue( const char *header, const char *value ) +{ + size_t additional = strlen( header ) + 2 + strlen( value ) + 2 + 1; + + if ( !m_sendheaders ) + { + m_sendheaders = (char *)malloc( additional ); + if ( m_sendheaders ) + { + char *p = m_sendheaders; + StringCchCopyExA( p, additional, header, &p, &additional, 0 ); + StringCchCatExA( p, additional, ": ", &p, &additional, 0 ); + StringCchCatExA( p, additional, value, &p, &additional, 0 ); + StringCchCatExA( p, additional, "\r\n", &p, &additional, 0 ); + } + } + else + { + size_t alloc_len = strlen( m_sendheaders ) + additional; + char *t = (char *)malloc( alloc_len ); + if ( t ) + { + char *p = t; + StringCchCopyExA( p, alloc_len, m_sendheaders, &p, &alloc_len, 0 ); + StringCchCatExA( p, alloc_len, header, &p, &alloc_len, 0 ); + StringCchCatExA( p, alloc_len, ": ", &p, &alloc_len, 0 ); + StringCchCatExA( p, alloc_len, value, &p, &alloc_len, 0 ); + StringCchCatExA( p, alloc_len, "\r\n", &p, &alloc_len, 0 ); + + free( m_sendheaders ); + + m_sendheaders = t; + } + } +} + +void JNL_HTTPGet::do_encode_mimestr( char *in, char *out ) +{ + char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int shift = 0; + int accum = 0; + + while ( in && *in ) + { + if ( *in ) + { + accum <<= 8; + shift += 8; + accum |= *in++; + } + + while ( shift >= 6 ) + { + shift -= 6; + *out++ = alphabet[ ( accum >> shift ) & 0x3F ]; + } + } + + if ( shift == 4 ) + { + *out++ = alphabet[ ( accum & 0xF ) << 2 ]; + *out++ = '='; + } + else if ( shift == 2 ) + { + *out++ = alphabet[ ( accum & 0x3 ) << 4 ]; + *out++ = '='; + *out++ = '='; + } + + *out++ = 0; +} + + +void JNL_HTTPGet::connect( const char *url, int ver, const char *requestmethod ) +{ + deinit( false ); + + m_http_url = _strdup( url ); + do_parse_url( m_http_url, &m_http_host, &m_http_port, &m_http_request, &m_http_lpinfo ); + + if ( !m_http_host || !m_http_host[ 0 ] || !m_http_port ) + { + m_http_state = -1; + seterrstr( "invalid URL" ); + + return; + } + + size_t sendbufferlen = 0; + + if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] ) + sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_request ) + 9 /* HTTP/1.0 */ + 2; + else + { + sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_url ) + 9 /* HTTP/1.0 */ + 2; + + if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] ) + sendbufferlen += 58 + strlen( m_http_proxylpinfo ) * 2; // being safe here + } + + sendbufferlen += 5 /* Host: */ + strlen( m_http_host ) + 2; + if ( m_http_port != 80 ) + sendbufferlen += 6; + + if ( m_http_lpinfo && m_http_lpinfo[ 0 ] ) + sendbufferlen += 46 + strlen( m_http_lpinfo ) * 2; // being safe here + + if ( m_sendheaders ) + sendbufferlen += strlen( m_sendheaders ); + + size_t strLen = sendbufferlen + 1024; + char *str = (char *)calloc( strLen, sizeof( char ) ); + char *connectString = str; + + if ( !str ) + { + seterrstr( "error allocating memory" ); + m_http_state = -1; + } + + if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] ) + { + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_request, ver % 10 ); + } + else + { + char *myp = NULL; + if ( strncasecmp( m_http_url, "uvox://", 7 ) == 0 ) + { + myp = m_http_url + 7; + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 ); + } + else if ( strncasecmp( m_http_url, "unsv://", 7 ) == 0 ) + { + myp = m_http_url + 7; + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 ); + } + else if ( strncasecmp( m_http_url, "uasf://", 7 ) == 0 ) + { + myp = m_http_url + 7; + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 ); + } + else + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_url, ver % 10 ); + } + + if ( m_http_port == 80 ) + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s\r\n", m_http_host ); + else + StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s:%d\r\n", m_http_host, m_http_port ); + + if ( m_http_lpinfo && m_http_lpinfo[ 0 ] ) + { + StringCchCatExA( str, strLen, "Authorization: Basic ", &str, &strLen, 0 ); + do_encode_mimestr( m_http_lpinfo, str ); + StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 ); + } + + if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] ) + { + StringCchCatExA( str, strLen, "Proxy-Authorization: Basic ", &str, &strLen, 0 ); + do_encode_mimestr( m_http_proxylpinfo, str ); + StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 ); + } + + if ( allowCompression ) + StringCchCatExA( str, strLen, "Accept-Encoding: gzip\r\n", &str, &strLen, 0 ); + + if ( m_sendheaders ) + StringCchCatExA( str, strLen, m_sendheaders, &str, &strLen, 0 ); + + StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 ); + + int a = (int)m_recvbufsize; + if ( a < 4096 ) + a = 4096; + + if ( !m_con ) + { + //m_con=new JNL_Connection(m_dns,strlen(str)+4,a); + /* + ** Joshua Teitelbaum delta 1/15/2006 + */ + +#ifdef USE_SSL + /* + ** Joshua Teitelbaum 1/27/2006 + ** Check for secure + */ + if ( !_strnicmp( m_http_url, "https:", strlen( "https:" ) ) ) + { + size_t send_buffer_size = strlen( connectString ) + 4; + if ( send_buffer_size < 8192 ) + send_buffer_size = 8192; + + send_buffer_size += m_sendbufsize; + + if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) ) + send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined + + m_con = new JNL_SSL_Connection( NULL, m_dns, send_buffer_size, a ); + } + else + { +#endif + + size_t send_buffer_size = strlen( connectString ) + 4 + m_sendbufsize; + if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) ) + send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined + + m_con = new JNL_Connection( m_dns, send_buffer_size, a ); + +#ifdef USE_SSL + } +#endif + + if ( m_con ) + { + if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] ) + m_con->connect( m_http_host, m_http_port ); + else + m_con->connect( m_http_proxyhost, m_http_proxyport ); + + m_con->send_string( connectString ); + } + else + { + m_http_state = -1; + seterrstr( "could not create connection object" ); + } + } + else + { + m_con->reuse(); + m_con->send_string( connectString ); + } + + free(connectString); +} + +void JNL_HTTPGet::do_parse_url( const char *url, char **host, unsigned short *port, char **req, char **lp ) +{ + char *l_port = 0; + + JNL::parse_url( url, &l_port, host, port, req, lp ); + + if ( !*port ) + { + if ( l_port ) + { + addrinfo *res; + + addrinfo hints; + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = 0; + hints.ai_socktype = SOCK_STREAM; + + if ( getaddrinfo( 0, l_port, &hints, &res ) == 0 ) + { + if ( res->ai_family == AF_INET ) + *port = htons( ( (sockaddr_in *)res->ai_addr )->sin_port ); + else if ( res->ai_family == AF_INET6 ) + *port = htons( ( (sockaddr_in6 *)res->ai_addr )->sin6_port ); + else // wtf? + *port = 80; + } + else + *port = 80; + } + else + *port = 80; + } + + if ( l_port ) + free( l_port ); + + if ( !*req ) + *req = _strdup( "/" ); +} + +const char *JNL_HTTPGet::getallheaders() +{ + // double null terminated, null delimited list + if (m_recvheaders) + return m_recvheaders; + else + return "\0\0"; +} + +const char *JNL_HTTPGet::getheader( const char *headername ) +{ + char *ret = NULL; + if ( headername[ 0 ] == 0 || !m_recvheaders ) + return NULL; + + size_t headername_size = strlen( headername ); + char *buf = (char *)malloc( headername_size + 2 ); + +#ifdef _WIN32 + StringCchCopyA( buf, headername_size + 2, headername ); +#elif defined(__APPLE__) + strlcpy( buf, headername, headername_size + 2 ); +#else + strncpy( buf, headername, headername_size + 1 ); + buf[ headername_size + 1 ] = 0; +#endif + + if ( buf[ headername_size - 1 ] != ':' ) + { + buf[ headername_size++ ] = ':'; + buf[ headername_size ] = 0; + } + + char *p = m_recvheaders; + while ( p && *p ) + { + if ( !strncasecmp( buf, p, headername_size ) ) + { + ret = p + headername_size; + while ( ret && *ret && *ret == ' ' ) + ret++; + + break; + } + + p += strlen( p ) + 1; + } + + free( buf ); + + return ret; +} + +int JNL_HTTPGet::run() +{ + int cnt = 0; + if ( m_http_state == -1 || !m_con ) + return HTTPGET_RUN_ERROR; // error + + +run_again: + m_con->run(); + + if ( m_con->get_state() == JNL_Connection::STATE_ERROR ) + { + seterrstr( m_con->get_errstr() ); + + return HTTPGET_RUN_ERROR; + } + + if ( m_con->get_state() == JNL_Connection::STATE_CLOSED ) + return HTTPGET_RUN_CONNECTION_CLOSED; + + if ( m_http_state == 0 ) // connected, waiting for reply + { + if ( m_con->recv_lines_available() > 0 ) + { + char buf[ 4096 ] = { 0 }; + m_con->recv_line( buf, 4096 ); + buf[ 4095 ] = 0; + + if ( m_reply && getreplycode() == 100 ) + { + free( m_reply ); + m_reply = 0; + goto run_again; + } + + m_reply = _strdup( buf ); + int code = getreplycode(); + if ( code >= 200 && code <= 206 ) + m_http_state = 2; // proceed to read headers normally + else if ( code == 301 || code == 302 || code == 303 || code == 307 ) + { + m_http_state = 1; // redirect city + } + else if ( code != 100 ) // in case of HTTP 100 Continue code, we'll keep looping + { + if ( accept_all_reply_codes ) + { + m_http_state = 2; // proceed to read headers normally + } + else + { + seterrstr( buf ); + m_http_state = -1; + + return HTTPGET_RUN_ERROR; + } + } + + cnt = 0; + } + else if ( !cnt++ ) + goto run_again; + } + + if ( m_http_state == 1 ) // redirect + { + char *loc = 0; + while ( m_con->recv_lines_available() > 0 ) + { + char buf[ 4096 ] = { 0 }; + m_con->recv_line( buf, 4096 ); + buf[ 4095 ] = 0; + + if ( !buf[ 0 ] ) + { + if ( !loc ) + { + m_http_state = -1; + return HTTPGET_RUN_ERROR; + } + else + break; + } + + if ( !strncasecmp( buf, "Location:", 9 ) ) + { + char *p = buf + 9; + while ( p && *p && *p == ' ' ) p++; + if ( p && *p ) + { + // TODO need to make this match the request type + loc = _strdup( p ); + } + } + } + + if ( loc ) + { + connect( loc ); + free( loc ); + + return HTTPGET_RUN_OK; + } + } + + /* ----- read headers ----- */ + if ( m_http_state == 2 ) + { + if ( !cnt++ && m_con->recv_lines_available() < 1 ) + goto run_again; + + while ( m_con->recv_lines_available() > 0 ) + { + char buf[ 8192 ] = { 0 }; + m_con->recv_line( buf, 8192 ); + buf[ 8191 ] = 0; + + if ( !buf[ 0 ] ) + { + const char *compression = getheader( "Content-Encoding" ); + if ( compression && !strcmp( compression, "gzip" ) ) + { + zlibStream = (z_stream *)malloc( sizeof( z_stream ) ); + zlibStream->next_in = Z_NULL; + zlibStream->avail_in = Z_NULL; + zlibStream->next_out = Z_NULL; + zlibStream->avail_out = Z_NULL; + zlibStream->zalloc = (alloc_func)0; + zlibStream->zfree = (free_func)0; + zlibStream->opaque = 0; + + int z_err = inflateInit2( zlibStream, 15 + 16 /* +16 for gzip */ ); + if ( z_err != Z_OK ) + { + free( zlibStream ); + zlibStream = 0; + } + } + else + { + if ( zlibStream ) + { + free( zlibStream ); + zlibStream = 0; + } + } + m_http_state = 3; + break; + } + if ( !m_recvheaders ) + { + m_recvheaders_size = strlen( buf ) + 1; + if ( m_recvheaders_size == 0 || m_recvheaders_size == (size_t)-1 ) // check for overflow + { + m_http_state = -1; + return HTTPGET_RUN_ERROR; + } + + m_recvheaders = (char *)malloc( m_recvheaders_size + 1 ); + if ( m_recvheaders ) + { + strcpy( m_recvheaders, buf ); // safe because we malloc'd specifically above + m_recvheaders[ m_recvheaders_size ] = 0; + } + else + { + m_http_state = -1; + return HTTPGET_RUN_ERROR; + } + } + else + { + size_t oldsize = m_recvheaders_size; + m_recvheaders_size += strlen( buf ) + 1; + if ( m_recvheaders_size + 1 < oldsize ) // check for overflow + { + m_http_state = -1; + return HTTPGET_RUN_ERROR; + } + + char *n = (char *)realloc( m_recvheaders, m_recvheaders_size + 1 ); + if ( !n ) + { + m_http_state = -1; + return HTTPGET_RUN_ERROR; + } + + strcpy( n + oldsize, buf ); // safe because we malloc specifially for the size + n[ m_recvheaders_size ] = 0; // double null terminate + m_recvheaders = n; + } + } + } + + return HTTPGET_RUN_OK; +} + +int JNL_HTTPGet::get_status() // returns 0 if connecting, 1 if reading headers, 2 if reading content, -1 if error. +{ + if ( m_http_state < 0 ) + return HTTPGET_STATUS_ERROR; + + if ( m_http_state < 2 ) + return HTTPGET_STATUS_CONNECTING; + + if ( m_http_state == 2 ) + return HTTPGET_STATUS_READING_HEADERS; + + if ( m_http_state == 3 ) + return HTTPGET_STATUS_READING_CONTENT; + + return HTTPGET_STATUS_ERROR; +} + +int JNL_HTTPGet::getreplycode() // returns 0 if none yet, otherwise returns http reply code. +{ + if ( !m_reply ) + return 0; + + char *p = m_reply; + + while ( p && *p && *p != ' ' ) + p++; // skip over HTTP/x.x + + if ( !p || !*p ) + return 0; + + return atoi( ++p ); +} + +size_t JNL_HTTPGet::bytes_available() +{ + if ( m_con && m_http_state == 3 ) + return m_con->recv_bytes_available(); + + return 0; +} + +size_t JNL_HTTPGet::get_bytes( char *buf, size_t len ) +{ + if ( m_con && m_http_state == 3 ) + { + if ( zlibStream ) + { + // TODO: benski> we need to pick a better buffer size + // either alloca() and use the passed in length + // or malloc a buffer based on the constructor-initted buffer size + char temp[ 8192 ] = { 0 }; + + int size = (int)m_con->peek_bytes( temp, 8192 ); + if ( size ) + { + zlibStream->next_in = reinterpret_cast<Bytef *>( temp ); + zlibStream->avail_in = (uInt)size; + zlibStream->next_out = reinterpret_cast<Bytef *>( buf ); + zlibStream->avail_out = (uInt)len; + + int zlib_err = inflate( zlibStream, Z_SYNC_FLUSH ); + + if ( zlib_err == Z_OK || zlib_err == Z_STREAM_END ) + { + m_con->recv_bytes( 0, size - zlibStream->avail_in ); // since we only peeked above + return len - zlibStream->avail_out; + } + else + return 0; // TODO: should we do something else here? + } + } + else + return m_con->recv_bytes( buf, len ); + } + + return 0; +} + +size_t JNL_HTTPGet::peek_bytes( char *buf, size_t len ) +{ + if ( m_con && m_http_state == 3 ) + { + if ( zlibStream ) + return 0; // TODO: benski> how are we going to do peek_bytes, since the inflater saves state? + else + return m_con->peek_bytes( buf, len ); + } + + return 0; +} + +uint64_t JNL_HTTPGet::content_length() +{ + const char *p = getheader( "content-length" ); + if ( p && *p ) + return strtoull( p, 0, 10 ); + else + { + // TODO need to check this further for reliability! + // Helps to handle responses without content-length + if ( m_recvheaders_size > 0 && bytes_available() > 0 ) + return bytes_available() - m_recvheaders_size; + } + return 0; +} + +void JNL_HTTPGet::seterrstr( const char *str ) +{ + if ( m_errstr ) + free( m_errstr ); + + m_errstr = _strdup( str ); +} + +void JNL_HTTPGet::AllowCompression() +{ + allowCompression = true; +} + +void JNL_HTTPGet::reset_headers() +{ + if ( m_sendheaders ) + { + free( m_sendheaders ); + m_sendheaders = 0; + } +} + +void JNL_HTTPGet::set_accept_all_reply_codes() +{ + accept_all_reply_codes = true; +} + +void JNL_HTTPGet::set_persistent() +{ + persistent = true; +} diff --git a/Src/replicant/jnetlib/httpget.h b/Src/replicant/jnetlib/httpget.h new file mode 100644 index 00000000..b29a17ce --- /dev/null +++ b/Src/replicant/jnetlib/httpget.h @@ -0,0 +1,139 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: httpget.h - JNL interface for doing HTTP GETs. +** License: see jnetlib.h +** +** Usage: +** 1. Create a JNL_HTTPGet object, optionally specifying a JNL_AsyncDNS +** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto), +** and the receive buffer size, and a string specifying proxy (or NULL +** for none). See note on proxy string below. +** 2. call addheader() to add whatever headers you want. It is recommended to +** add at least the following two: +** addheader("User-Agent:MyApp (Mozilla)"); +*/// addheader("Accept:*/*"); +/* ( the comment weirdness is there so I Can do the star-slash :) +** 3. Call connect() with the URL you wish to GET (see URL string note below) +** 4. Call run() once in a while, checking to see if it returns -1 +** (if it does return -1, call geterrorstr() to see what the error is). +** (if it returns 1, no big deal, the connection has closed). +** 5. While you're at it, you can call bytes_available() to see if any data +** from the http stream is available, or getheader() to see if any headers +** are available, or getreply() to see the HTTP reply, or getallheaders() +** to get a double null terminated, null delimited list of headers returned. +** 6. If you want to read from the stream, call get_bytes (which returns how much +** was actually read). +** 7. content_length() is a helper function that uses getheader() to check the +** content-length header. +** 8. Delete ye' ol' object when done. +** +** Proxy String: +** should be in the format of host:port, or user@host:port, or +** user:password@host:port. if port is not specified, 80 is assumed. +** URL String: +** should be in the format of http://user:pass@host:port/requestwhatever +** note that user, pass, port, and /requestwhatever are all optional :) +** note that also, http:// is really not important. if you do poo:// +** or even leave out the http:// altogether, it will still work. +*/ + +#ifndef _HTTPGET_H_ +#define _HTTPGET_H_ + +#include "connection.h" +#include "asyncdns.h" +#include "jnetlib_defines.h" +#ifdef _WIN32 +#include "minizip/unzip.h" +#else +#include <zlib.h> +#endif +#include "nswasabi/ReferenceCounted.h" + +class JNL_HTTPGet : public ReferenceCountedBase<JNL_HTTPGet> +{ +public: + JNL_HTTPGet(size_t recvbufsize=PACKET_SIZE, size_t sendbufsize=0); + ~JNL_HTTPGet(); + + void set_sendbufsize( size_t sendbufsize = PACKET_SIZE ); // call if you're going to POST or do any kind of bidirectional communications + int set_recv_buffer_size(size_t new_buffer_size); + void addheader(const char *header); + void addheadervalue(const char *header, const char *value); + + void connect(const char *url, int ver=0, const char *requestmethod="GET"); + + int run(); // returns: 0 if all is OK. -1 if error (call geterrorstr()). 1 if connection closed. + + int get_status(); // returns 0 if connecting, 1 if reading headers, + // 2 if reading content, -1 if error. + + const char *getallheaders(); // double null terminated, null delimited list + const char *getheader(const char *headername); + const char *getreply() { return m_reply; } + int getreplycode(); // returns 0 if none yet, otherwise returns http reply code. + + const char *geterrorstr() { return m_errstr;} + + size_t bytes_available(); + size_t get_bytes(char *buf, size_t len); + size_t peek_bytes(char *buf, size_t len); + + uint64_t content_length(); + + JNL_Connection *get_con() { return m_con; } + void AllowCompression(); + void reset_headers(); + + void set_dns(JNL_AsyncDNS *dns); + const char *get_url() { return m_http_url; } + void set_accept_all_reply_codes(); // call this if you want to retrieve content even though a 404 (etc) was returned + void set_persistent(); + static void set_proxy(const char *proxy); + +protected: + static char *get_proxy(); + void reinit(); + void deinit(bool full=true); + void seterrstr(const char *str); + + void do_parse_url(const char *url, char **host, unsigned short*port, char **req, char **lp); + void do_encode_mimestr(char *in, char *out); + + JNL_AsyncDNS *m_dns; + JNL_Connection *m_con; + size_t m_recvbufsize; + + int m_http_state; + + unsigned short m_http_port; + char *m_http_url; + char *m_http_host; + char *m_http_lpinfo; + char *m_http_request; + + char *m_http_proxylpinfo; + char *m_http_proxyhost; + unsigned short m_http_proxyport; + + char *m_sendheaders; + char *m_recvheaders; + size_t m_recvheaders_size; + char *m_reply; + + char *m_errstr; + bool allowCompression; + + size_t m_sendbufsize; + /* gzip stuff */ + z_stream *zlibStream; + + bool accept_all_reply_codes; + bool persistent; + + static char *g_proxy; +}; + +#endif // _HTTPGET_H_ diff --git a/Src/replicant/jnetlib/httpserv.cpp b/Src/replicant/jnetlib/httpserv.cpp new file mode 100644 index 00000000..4a129e9b --- /dev/null +++ b/Src/replicant/jnetlib/httpserv.cpp @@ -0,0 +1,234 @@ +/* +** JNetLib +** Copyright (C) 2001 Nullsoft, Inc. +** Author: Justin Frankel +** File: httpserv.cpp - JNL HTTP GET/POST serving implementation +** License: see jnetlib.h +** +** This class just manages the http reply/sending, not where the data +** comes from, etc. +*/ + +#include "netinc.h" +#include "util.h" + +#include "httpserv.h" + +/* + States for m_state: + -1 error (connection closed, etc) + 0 not read request yet. + 1 reading headers + 2 headers read, have not sent reply + 3 sent reply + 4 closed +*/ + +JNL_HTTPServ::JNL_HTTPServ(JNL_Connection *con) +{ + m_con=con; + m_state=0; + m_reply_headers=0; + m_reply_string=0; + m_recv_request=0; + m_errstr=0; + m_reply_ready=0; + m_method = 0; + http_ver = 0; + keep_alive = 0; +} + +JNL_HTTPServ::~JNL_HTTPServ() +{ + free(m_recv_request); + free(m_reply_string); + free(m_reply_headers); + free(m_errstr); + free(m_method); + m_con->Release(); +} + +static size_t strlen_whitespace(const char *str) +{ + size_t size=0; + while (str && *str && *str != ' ' && *str != '\r' && *str!='\n') + { + str++; + size++; + } + return size; +} + +int JNL_HTTPServ::run() +{ // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data. + int cnt=0; +run_again: + m_con->run(); + if (m_con->get_state()==JNL_Connection::STATE_ERROR) + { + seterrstr(m_con->get_errstr()); + return -1; + } + + if (m_con->get_state()==JNL_Connection::STATE_CLOSED) + return 4; + + if (m_state == 0) + { + if (m_con->recv_lines_available()>0) + { + char *buf=(char*)malloc(m_con->recv_bytes_available()-1); + m_con->recv_line(buf,m_con->recv_bytes_available()-1); + free(m_recv_request); + m_recv_request=(char*)malloc(strlen(buf)+2); + strcpy(m_recv_request,buf); + m_recv_request[strlen(m_recv_request)+1]=0; + free(buf); + buf=m_recv_request; + while (buf && *buf) buf++; + while (buf >= m_recv_request && *buf != ' ') buf--; + if (strncmp(buf+1,"HTTP",4))// || strncmp(m_recv_request,"GET ",3)) + { + seterrstr("malformed HTTP request"); + m_state=-1; + } + else + { + http_ver = atoi(buf+8); + + size_t method_len = strlen_whitespace(m_recv_request); + m_method = (char *)malloc(method_len + 1); + memcpy(m_method, m_recv_request, method_len); + m_method[method_len]=0; + + m_state=1; + cnt=0; + if (buf >= m_recv_request) buf[0]=buf[1]=0; + + buf=strstr(m_recv_request,"?"); + if (buf) + { + *buf++=0; // change &'s into 0s now. + char *t=buf; + int stat=1; + while (t && *t) + { + if (*t == '&' && !stat) { stat=1; *t=0; } + else stat=0; + t++; + } + } + } + } + else if (!cnt++) goto run_again; + } + if (m_state == 1) + { + if (!cnt++ && m_con->recv_lines_available()<1) goto run_again; + while (m_con->recv_lines_available()>0) + { + char buf[4096] = {0}; + m_con->recv_line(buf,4096); + if (!buf[0]) + { + m_state=2; + break; + } + recvheaders.Add(buf); + } + } + if (m_state == 2) + { + if (m_reply_ready) + { + // send reply + m_con->send_string((char*)(m_reply_string?m_reply_string:"HTTP/1.1 200 OK")); + m_con->send_string("\r\n"); + if (m_reply_headers) m_con->send_string(m_reply_headers); + m_con->send_string("\r\n"); + m_state=3; + } + } + if (m_state == 3) + { + // nothing. + } + + return m_state; +} + +const char *JNL_HTTPServ::get_request_file() +{ + // file portion of http request + if (!m_recv_request) return NULL; + char *t=m_recv_request; + while (t && *t && *t != ' ') t++; + if (!t || !*t) return NULL; + while (t && *t && *t == ' ') t++; + return t; +} + +const char *JNL_HTTPServ::get_request_parm(const char *parmname) // parameter portion (after ?) +{ + const char *t=m_recv_request; + while (t && *t) t++; + if (t) t++; + while (t && *t) + { + while (t && *t && *t == '&') t++; + if (!_strnicmp(t,parmname,strlen(parmname)) && t[strlen(parmname)] == '=') + { + return t+strlen(parmname)+1; + } + t+=strlen(t)+1; + } + return NULL; +} + +const char *JNL_HTTPServ::getheader(const char *headername) +{ + return recvheaders.GetHeader(headername); +} + +void JNL_HTTPServ::set_reply_string(const char *reply_string) // should be HTTP/1.1 OK or the like +{ + free(m_reply_string); + m_reply_string=(char*)malloc(strlen(reply_string)+1); + strcpy(m_reply_string,reply_string); +} + +void JNL_HTTPServ::add_reply_header(const char *header) // "Connection: close" for example +{ + // if they've specified a content-length, then we can keep alive an HTTP/1.1 connection + if (!keep_alive && http_ver == 1 && !_strnicmp(header, "Content-Length", 14)) + keep_alive = 1; + + if (m_reply_headers) + { + char *tmp=(char*)malloc(strlen(m_reply_headers)+strlen(header)+3); + strcpy(tmp,m_reply_headers); + strcat(tmp,header); + strcat(tmp,"\r\n"); + free(m_reply_headers); + m_reply_headers=tmp; + } + else + { + m_reply_headers=(char*)malloc(strlen(header)+3); + strcpy(m_reply_headers,header); + strcat(m_reply_headers,"\r\n"); + } +} + +void JNL_HTTPServ::reset() +{ + free(m_recv_request); m_recv_request = 0; + free(m_reply_string); m_reply_string = 0; + free(m_reply_headers); m_reply_headers = 0; + free(m_errstr); m_errstr = 0; + free(m_method); m_method =0; + m_reply_ready=0; + m_state = 0; + keep_alive = 0; +} + diff --git a/Src/replicant/jnetlib/httpserv.h b/Src/replicant/jnetlib/httpserv.h new file mode 100644 index 00000000..3b8e8cb5 --- /dev/null +++ b/Src/replicant/jnetlib/httpserv.h @@ -0,0 +1,71 @@ +/* +** JNetLib +** Copyright (C) 2001 Nullsoft, Inc. +** Author: Justin Frankel +** File: httpserv.h - JNL interface for doing HTTP GET/POST serving. +** License: see jnetlib.h +** This class just manages the http reply/sending, not where the data +** comes from, etc. +** for a mini-web server see webserver.h +*/ + +#ifndef _HTTPSERV_H_ +#define _HTTPSERV_H_ + +#include "connection.h" +#include "headers.h" +#include "nswasabi/ReferenceCounted.h" + +class JNL_HTTPServ : public ReferenceCountedBase<JNL_HTTPServ> +{ + public: + JNL_HTTPServ(JNL_Connection *con=NULL); + ~JNL_HTTPServ(); + + int run(); // returns: < 0 on error, 0 on request not read yet, 1 if reading headers, 2 if reply not sent, 3 if reply sent, sending data. 4 on connection closed. + + const char *geterrorstr() { return m_errstr;} + + // use these when state returned by run() is 2 + const char *get_request_file(); // file portion of http request + const char *get_request_parm(const char *parmname); // parameter portion (after ?) + const char *getallheaders() { return recvheaders.GetAllHeaders(); } // double null terminated, null delimited list + const char *getheader(const char *headername); + const char *get_method() { return m_method; }; + void set_reply_string(const char *reply_string); // should be HTTP/1.1 OK or the like + void add_reply_header(const char *header); // i.e. "content-size: 12345" + + void send_reply() { m_reply_ready=1; } // send reply, state will advance to 3. + + ////////// sending data /////////////// + int bytes_inqueue() { if (m_state == 3 || m_state == -1 || m_state ==4) return (int)m_con->send_bytes_in_queue(); else return 0; } + int bytes_cansend() { if (m_state == 3) return (int)m_con->send_bytes_available(); else return 0; } + void write_bytes(char *bytes, int length) { m_con->send(bytes,length); } + + void close(int quick) { m_con->close(quick); m_state=4; } + + JNL_Connection *get_con() { return m_con; } + + void reset(); // prepare for another request on the same connection (HTTP/1.1) + int get_http_version() { return http_ver; } + int get_keep_alive() { return keep_alive; } + + protected: + void seterrstr(const char *str) { if (m_errstr) free(m_errstr); m_errstr=_strdup(str); } + + int m_reply_ready; + int m_state; + int http_ver; + int keep_alive; + + char *m_errstr; + char *m_reply_headers; + char *m_reply_string; + JNL_Headers recvheaders; + char *m_recv_request; // either double-null terminated, or may contain parameters after first null. + char *m_method; + + JNL_Connection *m_con; +}; + +#endif // _HTTPSERV_H_ diff --git a/Src/replicant/jnetlib/httpuserv.cpp b/Src/replicant/jnetlib/httpuserv.cpp new file mode 100644 index 00000000..de14219a --- /dev/null +++ b/Src/replicant/jnetlib/httpuserv.cpp @@ -0,0 +1,216 @@ +/* +** JNetLib +** Copyright (C) 2012 Nullsoft, Inc. +** Author: Ben Allison +** File: httpuserv.cpp - JNL HTTPU (HTTP over UDP) serving implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "httpuserv.h" + +#include "foundation/error.h" + +/* +States for m_state: +-1 error (connection closed, etc) +0 not read request yet. +1 reading headers +2 headers read, have not sent reply +3 sent reply +4 closed +*/ + +JNL_HTTPUServ::JNL_HTTPUServ() +{ + m_reply_headers=0; + m_reply_string=0; + m_recv_request=0; + m_errstr=0; + m_reply_ready=0; + m_method = 0; + http_ver = 0; +} + +JNL_HTTPUServ::~JNL_HTTPUServ() +{ + free(m_recv_request); + free(m_reply_string); + free(m_reply_headers); + free(m_errstr); + free(m_method); +} + +static size_t strlen_whitespace(const char *str) +{ + size_t size=0; + while (str && *str && *str != ' ' && *str != '\r' && *str!='\n') + { + str++; + size++; + } + return size; +} + +int JNL_HTTPUServ::process(JNL_UDPConnection *m_con) +{ // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data. + + reset(); + if (m_con->get_state()==JNL_CONNECTION_STATE_ERROR) + { + seterrstr(m_con->get_errstr()); + return -1; + } + + if (m_con->get_state()==JNL_CONNECTION_STATE_CLOSED) + return 4; + + if (m_con->recv_lines_available()>0) + { + char *buf=(char*)malloc(m_con->recv_bytes_available()-1); + m_con->recv_line(buf,m_con->recv_bytes_available()-1); + free(m_recv_request); + m_recv_request=(char*)malloc(strlen(buf)+2); + strcpy(m_recv_request,buf); + m_recv_request[strlen(m_recv_request)+1]=0; + free(buf); + buf=m_recv_request; + while (buf && *buf) buf++; + while (buf >= m_recv_request && *buf != ' ') buf--; + if (strncmp(buf+1,"HTTP",4))// || strncmp(m_recv_request,"GET ",3)) + { + seterrstr("malformed HTTP request"); + } + else + { + http_ver = atoi(buf+8); + + size_t method_len = strlen_whitespace(m_recv_request); + m_method = (char *)malloc(method_len + 1); + if (m_method) + { + memcpy(m_method, m_recv_request, method_len); + m_method[method_len]=0; + } + else + { + seterrstr("malformed HTTP request"); + return -1; + } + + if (buf >= m_recv_request) buf[0]=buf[1]=0; + + buf=strstr(m_recv_request,"?"); + if (buf) + { + *buf++=0; // change &'s into 0s now. + char *t=buf; + int stat=1; + while (t && *t) + { + if (*t == '&' && !stat) { stat=1; *t=0; } + else stat=0; + t++; + } + } + } + } + else + { + seterrstr("malformed HTTP request"); + return -1; + } + + while (m_con->recv_lines_available()>0) + { + char buf[8192] = {0}; + m_con->recv_line(buf, 8192); + if (!buf[0]) + break; + + recvheaders.Add(buf); + } + + + return NErr_Success; +} + +void JNL_HTTPUServ::send_reply(JNL_UDPConnection *m_con) +{ + m_con->send_string((char*)(m_reply_string?m_reply_string:"HTTP/1.1 200 OK")); + m_con->send_string("\r\n"); + if (m_reply_headers) m_con->send_string(m_reply_headers); + m_con->send_string("\r\n"); + +} + +const char *JNL_HTTPUServ::get_request_uri() +{ + // file portion of http request + if (!m_recv_request) return NULL; + char *t=m_recv_request; + while (t && *t && *t != ' ') t++; + if (!t || !*t) return NULL; + while (t && *t && *t == ' ') t++; + return t; +} + +const char *JNL_HTTPUServ::get_request_parm(const char *parmname) // parameter portion (after ?) +{ + const char *t=m_recv_request; + while (*t) t++; + t++; + while (t && *t) + { + while (t && *t && *t == '&') t++; + if (!strncasecmp(t,parmname,strlen(parmname)) && t[strlen(parmname)] == '=') + { + return t+strlen(parmname)+1; + } + t+=strlen(t)+1; + } + return NULL; +} + +const char *JNL_HTTPUServ::getheader(const char *headername) +{ + return recvheaders.GetHeader(headername); +} + +void JNL_HTTPUServ::set_reply_string(const char *reply_string) // should be HTTP/1.1 OK or the like +{ + free(m_reply_string); + m_reply_string=(char*)malloc(strlen(reply_string)+1); + strcpy(m_reply_string,reply_string); +} + +void JNL_HTTPUServ::set_reply_header(const char *header) // "Connection: close" for example +{ + if (m_reply_headers) + { + char *tmp=(char*)malloc(strlen(m_reply_headers)+strlen(header)+3); + strcpy(tmp,m_reply_headers); + strcat(tmp,header); + strcat(tmp,"\r\n"); + free(m_reply_headers); + m_reply_headers=tmp; + } + else + { + m_reply_headers=(char*)malloc(strlen(header)+3); + strcpy(m_reply_headers,header); + strcat(m_reply_headers,"\r\n"); + } +} + +void JNL_HTTPUServ::reset() +{ + free(m_recv_request); m_recv_request = 0; + free(m_reply_string); m_reply_string = 0; + free(m_reply_headers); m_reply_headers = 0; + free(m_errstr); m_errstr = 0; + free(m_method); m_method =0; + recvheaders.Reset(); + m_reply_ready=0; +} diff --git a/Src/replicant/jnetlib/httpuserv.h b/Src/replicant/jnetlib/httpuserv.h new file mode 100644 index 00000000..d6015067 --- /dev/null +++ b/Src/replicant/jnetlib/httpuserv.h @@ -0,0 +1,55 @@ +/* +** JNetLib +** Copyright (C) 2012 Nullsoft, Inc. +** Author: Ben Allison +** File: httpuserv.h - JNL interface for doing HTTPU (HTTP over UDP) +** This is half-baked so far. Need to think things through a touch more +*/ + +#pragma once + +#include "udpconnection.h" +#include "headers.h" + +class JNL_HTTPUServ +{ +public: + JNL_HTTPUServ(); + ~JNL_HTTPUServ(); + + // pass this a connection that has just received a packet + int process( JNL_UDPConnection *m_con ); + + const char *geterrorstr() { return m_errstr; } + + // use these when state returned by run() is 2 + const char *get_request_uri(); // file portion of http request + const char *get_request_parm( const char *parmname ); // parameter portion (after ?) + const char *getallheaders() { return recvheaders.GetAllHeaders(); } // double null terminated, null delimited list + const char *getheader( const char *headername ); + const char *get_method() { return m_method; } + + void set_reply_string( const char *reply_string ); // should be HTTP/1.1 OK or the like + void set_reply_header( const char *header ); // i.e. "content-size: 12345" + + void send_reply( JNL_UDPConnection *m_con ); // sends a reply to the given UDP socket. it must have been setup beforehand with the appropriate peer + + void reset(); // prepare for another request + + int get_http_version() { return http_ver; } + +protected: + void seterrstr( const char *str ) { if ( m_errstr ) free( m_errstr ); m_errstr = _strdup( str ); } + + int m_reply_ready; + int http_ver; + + char *m_errstr; + char *m_reply_headers; + char *m_reply_string; + JNL_Headers recvheaders; + char *m_recv_request; // either double-null terminated, or may contain parameters after first null. + char *m_method; +}; + + diff --git a/Src/replicant/jnetlib/jnetlib-replicant.rc b/Src/replicant/jnetlib/jnetlib-replicant.rc new file mode 100644 index 00000000..fcff7711 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib-replicant.rc @@ -0,0 +1,76 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/replicant/jnetlib/jnetlib-replicant.vcproj b/Src/replicant/jnetlib/jnetlib-replicant.vcproj new file mode 100644 index 00000000..a514c3c0 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib-replicant.vcproj @@ -0,0 +1,491 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="jnetlib-replicant" + ProjectGUID="{AAE8BF3C-AD6B-405C-A216-BF62E2D49E95}" + RootNamespace="jnetlib" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..;../../openssl/include" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + DisableSpecificWarnings="4995;4996" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll" + LinkIncremental="2" + GenerateManifest="false" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb" + SubSystem="2" + ImportLibrary="$(IntDir)\$(TargetName).lib" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="1" + FavorSizeOrSpeed="2" + AdditionalIncludeDirectories="..;../../openssl/include" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL;WIN32_LEAN_AND_MEAN" + StringPooling="true" + RuntimeLibrary="2" + BufferSecurityCheck="false" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + DisableSpecificWarnings="4995;4996;4244" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll" + LinkIncremental="1" + GenerateManifest="false" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(IntDir)\$(TargetName).lib" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..;../../openssl/include" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + DisableSpecificWarnings="4995;4996" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll" + LinkIncremental="2" + GenerateManifest="false" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb" + SubSystem="2" + ImportLibrary="$(IntDir)\$(TargetName).lib" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="1" + FavorSizeOrSpeed="2" + AdditionalIncludeDirectories="..;../../openssl/include" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL;WIN32_LEAN_AND_MEAN" + StringPooling="true" + RuntimeLibrary="2" + BufferSecurityCheck="false" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + DisableSpecificWarnings="4995;4996;4244" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll" + LinkIncremental="1" + GenerateManifest="false" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(IntDir)\$(TargetName).lib" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + <ProjectReference + ReferencedProjectIdentifier="{EFC75A79-269F-44FC-BAC5-D7D4FD4EC92C}" + RelativePathToProject="..\replicant\nu\nu.vcproj" + /> + <ProjectReference + ReferencedProjectIdentifier="{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}" + RelativePathToProject="..\replicant\zlib\zlib.vcproj" + /> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\asyncdns.cpp" + > + </File> + <File + RelativePath=".\connection.cpp" + > + </File> + <File + RelativePath=".\headers.cpp" + > + </File> + <File + RelativePath=".\httpget.cpp" + > + </File> + <File + RelativePath=".\httpserv.cpp" + > + </File> + <File + RelativePath=".\httpuserv.cpp" + > + </File> + <File + RelativePath=".\jnetlib.cpp" + > + </File> + <File + RelativePath=".\listen.cpp" + > + </File> + <File + RelativePath=".\multicastlisten.cpp" + > + </File> + <File + RelativePath=".\sslconnection.cpp" + > + </File> + <File + RelativePath=".\udpconnection.cpp" + > + </File> + <File + RelativePath=".\util.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath=".\asyncdns.h" + > + </File> + <File + RelativePath=".\connection.h" + > + </File> + <File + RelativePath=".\headers.h" + > + </File> + <File + RelativePath=".\httpget.h" + > + </File> + <File + RelativePath=".\httpserv.h" + > + </File> + <File + RelativePath=".\httpuserv.h" + > + </File> + <File + RelativePath=".\jnetlib.h" + > + </File> + <File + RelativePath=".\listen.h" + > + </File> + <File + RelativePath=".\multicastlisten.h" + > + </File> + <File + RelativePath=".\netinc.h" + > + </File> + <File + RelativePath=".\precomp.h" + > + </File> + <File + RelativePath=".\sslconnection.h" + > + </File> + <File + RelativePath=".\udpconnection.h" + > + </File> + <File + RelativePath=".\util.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + > + <File + RelativePath=".\jnetlib-replicant.rc" + > + </File> + <File + RelativePath=".\resource.h" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/Src/replicant/jnetlib/jnetlib.cpp b/Src/replicant/jnetlib/jnetlib.cpp new file mode 100644 index 00000000..00fcfbdb --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib.cpp @@ -0,0 +1,630 @@ +#include "jnetlib.h" +#include "httpget.h" +#include "sslconnection.h" +#include "asyncdns.h" +#include "util.h" +#include "httpserv.h" +#include "httpuserv.h" +#include "listen.h" +#include "multicastlisten.h" + +#include "foundation/error.h" + +#include <new> + +int jnl_init() +{ + return JNL::open_socketlib(); +} + +void jnl_quit() +{ + JNL::close_socketlib(); +} + +/* --- Connection --- */ +jnl_connection_t jnl_connection_create(jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize) +{ + JNL_Connection *connection = new (std::nothrow) JNL_Connection((JNL_AsyncDNS *)dns, sendbufsize, recvbufsize); + return (jnl_connection_t)connection; +} + +jnl_connection_t jnl_sslconnection_create(jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize) +{ + JNL_SSL_Connection *connection = new (std::nothrow) JNL_SSL_Connection(NULL, (JNL_AsyncDNS *)dns, sendbufsize, recvbufsize); + return (jnl_connection_t)connection; +} + +void jnl_connection_run(jnl_connection_t _connection, size_t max_send_bytes, size_t max_receive_bytes, size_t *bytes_sent, size_t *bytes_received) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + connection->run(max_send_bytes, max_receive_bytes, bytes_sent, bytes_received); +} + +int jnl_connection_get_state(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->get_state(); +} + +size_t jnl_connection_send_bytes_available(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->send_bytes_available(); +} + +size_t jnl_connection_receive_bytes_available(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->recv_bytes_available(); +} + +int jnl_connection_send(jnl_connection_t _connection, const void *bytes, size_t size) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->send(bytes, size); +} + +JNL_API int jnl_connection_send_string(jnl_connection_t _connection, const char *str) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->send_string(str); +} + +size_t jnl_connection_send_bytes_in_queue(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->send_bytes_in_queue(); +} + +size_t jnl_connection_receive(jnl_connection_t _connection, void *bytes, size_t size) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->recv_bytes(bytes, size); +} + +size_t jnl_connection_peek(jnl_connection_t _connection, void *bytes, size_t size) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->peek_bytes(bytes, size); +} + +void jnl_connection_release(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + if (connection) + connection->Release(); +} + +int jnl_connection_receive_line(jnl_connection_t _connection, void *bytes, size_t size) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->recv_line((char *)bytes, size); +} + +size_t jnl_connection_receive_lines_available(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->recv_lines_available(); +} + +void jnl_connection_close(jnl_connection_t _connection, int fast) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + connection->close(fast); +} + +void jnl_connection_connect(jnl_connection_t _connection, const char *hostname, int port) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + connection->connect(hostname, port); +} + +const char *jnl_connection_get_error(jnl_connection_t _connection) +{ + JNL_Connection *connection = (JNL_Connection *)_connection; + return connection->get_errstr(); +} + +/* ---- UDP ----- */ +int jnl_udp_create_multicast_listener(jnl_udp_t *connection, const char *mcast_ip, unsigned short port) +{ + JNL_UDPConnection *udp = 0; + JNL::open_socketlib(); // TODO: cut + + int ret = CreateMulticastListener(&udp, mcast_ip, port); + if (ret != NErr_Success) + { + JNL::close_socketlib(); // TODO: cut + + return ret; + } + *connection = (jnl_udp_t)udp; + return NErr_Success; +} + +void jnl_udp_release(jnl_udp_t _connection) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + delete connection; // TODO: reference count + JNL::close_socketlib(); // TODO: cut +} + +void jnl_udp_run(jnl_udp_t _connection, size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + if (connection) + { + connection->run(max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd); + } +} + +size_t jnl_udp_recv_bytes(jnl_udp_t _connection, void *buf, size_t len) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + if (connection) + { + return connection->recv_bytes(buf, len); + } + else + return 0; +} + +int jnl_udp_send(jnl_udp_t _connection, const void *bytes, size_t size) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + return connection->send(bytes, size); +} + +void jnl_udp_set_peer(jnl_udp_t _connection, const char *hostname, unsigned short port) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + connection->setpeer(hostname, port); +} + +void jnl_udp_set_peer_address(jnl_udp_t _connection, sockaddr *addr, socklen_t length) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + connection->setpeer(addr, length); +} + +int jnl_udp_get_address(jnl_udp_t _connection, sockaddr **addr, socklen_t *length) +{ + JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection; + connection->get_last_recv_msg_addr(addr, length); + return NErr_Success; +} + +/* ---- HTTP ---- */ +jnl_http_t jnl_http_create(int recvbufsize, int sendbufsize) +{ + JNL_HTTPGet *http = new (std::nothrow) JNL_HTTPGet(recvbufsize, sendbufsize); + return (jnl_http_t)http; +} + +int jnl_http_set_recv_buffer_size(jnl_http_t _http, size_t new_size) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->set_recv_buffer_size(new_size); +} + +int jnl_http_run(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->run(); +} + +size_t jnl_http_get_bytes(jnl_http_t _http, void *buf, size_t len) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->get_bytes(static_cast<char *>(buf), len); +} + +size_t jnl_http_peek_bytes(jnl_http_t _http, void *buf, size_t len) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->peek_bytes(static_cast<char *>(buf), len); +} + +size_t jnl_http_bytes_available(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->bytes_available(); +} + +uint64_t jnl_http_content_length(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->content_length(); +} + +int jnl_http_get_status(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->get_status(); +} + +int jnl_http_getreplycode(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->getreplycode(); +} + +const char *jnl_http_getreply(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->getreply(); +} + +const char *jnl_http_getheader(jnl_http_t _http, const char *header) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->getheader(header); +} + +const char *jnl_http_get_all_headers(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->getallheaders(); +} + +void jnl_http_addheader(jnl_http_t _http, const char *header) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->addheader(header); +} + +void jnl_http_addheadervalue(jnl_http_t _http, const char *header, const char *value) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->addheadervalue(header, value); +} + +void jnl_http_connect(jnl_http_t _http, const char *url, int http_version, const char *method) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->connect(url, http_version, method); +} + +void jnl_http_release(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + + if (http) + http->Release(); +} + +jnl_http_t jnl_http_retain(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + if (http) + http->Retain(); + return _http; +} + +const char *jnl_http_get_url(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return http->get_url(); +} + +void jnl_http_reset_headers(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->reset_headers(); +} + +void jnl_http_set_persistent(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->set_persistent(); +} + +void jnl_http_allow_compression(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->AllowCompression(); +} + +void jnl_http_allow_accept_all_reply_codes(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + http->set_accept_all_reply_codes(); +} + +jnl_connection_t jnl_http_get_connection(jnl_http_t _http) +{ + JNL_HTTPGet *http = (JNL_HTTPGet *)_http; + return (jnl_connection_t)http->get_con(); +} + +void jnl_http_set_proxy(const char *proxy) +{ + JNL_HTTPGet::set_proxy(proxy); +} + +/* ------- HTTP Request Parser ------- */ +int jnl_http_request_create(jnl_http_request_t *_http, jnl_connection_t _connection) +{ + JNL_HTTPServ *http = new (std::nothrow) JNL_HTTPServ((JNL_Connection *)_connection);; + if (!http) + return NErr_OutOfMemory; + + *_http = (jnl_http_request_t) http; + return NErr_Success; +} + +void jnl_http_request_release(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + if (http) + http->Release(); +} + +int jnl_http_request_run(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->run(); +} + +int jnl_htt_request_get_keep_alive(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->get_keep_alive(); +} + +const char *jnl_http_request_get_header(jnl_http_request_t _http, const char *header) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->getheader(header); +} + +void jnl_http_request_reset(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + http->reset(); +} + +void jnl_http_request_addheader(jnl_http_request_t _http, const char *header) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + http->add_reply_header(header); +} + +void jnl_http_request_set_reply_string(jnl_http_request_t _http, const char *reply) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + http->set_reply_string(reply); +} + +void jnl_http_request_send_reply(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + http->send_reply(); +} + +const char *jnl_http_request_get_uri(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->get_request_file(); +} + +const char *jnl_http_request_get_parameter(jnl_http_request_t _http, const char *parameter) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->get_request_parm(parameter); +} + +jnl_connection_t jnl_http_request_get_connection(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + JNL_Connection *connection = http->get_con(); + if (connection) + { + connection->Retain(); + return (jnl_connection_t)connection; + } + else + return 0; +} + +const char *jnl_http_request_get_method(jnl_http_request_t _http) +{ + JNL_HTTPServ *http = (JNL_HTTPServ *)_http; + return http->get_method(); +} + +/* ------- HTTPU Request Parser ------- */ +int jnl_httpu_request_create(jnl_httpu_request_t *_httpu) +{ + JNL_HTTPUServ *httpu = new (std::nothrow) JNL_HTTPUServ; + if (!httpu) + return NErr_OutOfMemory; + + *_httpu = (jnl_httpu_request_t) httpu; + return NErr_Success; +} + +void jnl_httpu_request_release(jnl_httpu_request_t _httpu) +{ + JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu; + delete httpu; // TODO: reference count +} + +int jnl_httpu_request_process(jnl_httpu_request_t _httpu, jnl_udp_t _udp) +{ + JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu; + return httpu->process((JNL_UDPConnection *)_udp); +} + +const char *jnl_httpu_request_get_method(jnl_httpu_request_t _httpu) +{ + JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu; + return httpu->get_method(); +} + + +const char *jnl_httpu_request_get_uri(jnl_httpu_request_t _httpu) +{ + JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu; + return httpu->get_request_uri(); +} + +const char *jnl_httpu_request_get_header(jnl_httpu_request_t _httpu, const char *header) +{ + JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu; + return httpu->getheader(header); +} + +/* ----- DNS ----- */ +int jnl_dns_create(jnl_dns_t *out_dns) +{ + JNL_AsyncDNS *dns = new (std::nothrow)JNL_AsyncDNS; + if (!dns) + return NErr_OutOfMemory; + + *out_dns = (jnl_dns_t)dns; + return NErr_Success; +} + +void jnl_dns_release(jnl_dns_t _dns) +{ + JNL_AsyncDNS *dns = (JNL_AsyncDNS *)_dns; + delete dns; // TODO: reference counting +} + +int jnl_dns_resolve(jnl_dns_t _dns, const char *hostname, unsigned short port, addrinfo **addr, int sockettype) +{ + JNL_AsyncDNS *dns = (JNL_AsyncDNS *)_dns; + int ret = dns->resolve(hostname, port, addr, sockettype); + if (ret == 0) + return NErr_Success; + else if (ret == -1) + return NErr_TryAgain; + else + return NErr_Unknown; +} + +int jnl_dns_resolve_now(const char *hostname, unsigned short port, addrinfo **addr, int sockettype) +{ + int ret = JNL_AsyncDNS::resolvenow(hostname, port, addr, sockettype); + if (ret == 0) + return NErr_Success; + else + return NErr_Unknown; +} + + +void jnl_dns_freeaddrinfo(addrinfo *addr) +{ + freeaddrinfo(addr); +} + +void jnl_dns_gethostname(char *name, size_t cch) +{ + gethostname(name, (int)cch); +} + +#ifdef _WIN32 +PCSTR WSAAPI inet_ntop_xp(INT af, PVOID src, PSTR dst, size_t cnt) +{ + struct sockaddr_in srcaddr; + + memset(&srcaddr, 0, sizeof(struct sockaddr_in)); + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + + srcaddr.sin_family = af; + if (WSAAddressToStringA((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0) { + return NULL; + } + return dst; +} + +PCSTR WSAAPI inet_ntop_win32(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize) { + typedef PCSTR (WSAAPI * win32_inet_ntop)(INT, PVOID, PSTR, size_t); + static win32_inet_ntop pwin32_inet_ntop = NULL; + + if (!pwin32_inet_ntop){ + HMODULE hlib = LoadLibrary(L"WS2_32.DLL"); + pwin32_inet_ntop = (win32_inet_ntop)GetProcAddress(hlib, "inet_ntop"); + if (!pwin32_inet_ntop) { + pwin32_inet_ntop = inet_ntop_xp; + } + } + + return (*pwin32_inet_ntop)(Family, pAddr, pStringBuf, StringBufSize); +} + +#endif + +void jnl_dns_ntop(int af, const void *src, char *dst, socklen_t size) +{ +#ifdef _WIN32 + // TODO need to revist this at a later date + // [22:01:08] audiodsp: i will make a tweak for IPv6 compatability at some point + // [22:01:49] audiodsp: just change references from sockaddr_in to sockaddr_storage + // [22:02:24] audiodsp: keep it as is + // [22:02:32] audiodsp: we're only using it in IPv4 mode at the moment + // [22:02:40] audiodsp: i will fix when we need IPv6 server support + // [22:03:58] audiodsp: the memcpy is what makes it non-trivial + // [22:04:05] audiodsp: i have to switch on family and do different memcpy's accordingly + // [22:04:16] audiodsp: or change the method to require a length + // [22:04:19] audiodsp: which makes more sense + // [22:04:29] audiodsp: and just not pass it to the linux function + // [22:05:08] audiodsp: anyway not important. + inet_ntop_win32(af, (PVOID)src, dst, size); +#else + inet_ntop(af, src, dst, size); +#endif +} +/* Listen */ + +int jnl_listen_create(jnl_listen_t *_listen, unsigned short port) +{ + JNL_Listen *l = new (std::nothrow) JNL_Listen(); + if (!l) + return NErr_OutOfMemory; + int ret = l->Initialize(port); + if (ret != NErr_Success) + { + delete l; + return ret; + } + *_listen = (jnl_listen_t)l; + return NErr_Success; +} + +int jnl_listen_create_from_address(jnl_listen_t *_listen, struct addrinfo *addr, size_t index) +{ + JNL_Listen *l = new (std::nothrow) JNL_Listen(); + if (!l) + return NErr_OutOfMemory; + int ret = l->Initialize(addr, index); + if (ret != NErr_Success) + { + delete l; + return ret; + } + *_listen = (jnl_listen_t)l; + return NErr_Success; +} + +jnl_connection_t jnl_listen_get_connection(jnl_listen_t _listen) +{ + JNL_Listen *listen = (JNL_Listen *)_listen; + JNL_Connection *connection = listen->get_connect(); + return (jnl_connection_t)connection; +} + +unsigned short jnl_listen_get_port(jnl_listen_t _listen) +{ + JNL_Listen *listen = (JNL_Listen *)_listen; + return listen->get_port(); +} + +void jnl_listen_release(jnl_listen_t _listen) +{ + JNL_Listen *listen = (JNL_Listen *)_listen; + if (listen) + listen->Release(); +}
\ No newline at end of file diff --git a/Src/replicant/jnetlib/jnetlib.h b/Src/replicant/jnetlib/jnetlib.h new file mode 100644 index 00000000..2dcf08ca --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib.h @@ -0,0 +1,177 @@ +/* +** JNetLib +** Copyright (C) 2000-2006 Nullsoft, Inc. +** Author: Justin Frankel +** File: jnetlib.h - JNL main include file (not really necessary). +** +** For documentation, look at the following files: +** Generic network initialization: netinc.h +** DNS: asyncdns.h +** TCP connections: connection.h +** HTTP GET connections: httpget.h +** TCP listen: listen.h +** +** license: +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _JNETLIB_H_ +#define _JNETLIB_H_ + +#include "netinc.h" +#include "../foundation/types.h" +#include "jnetlib_defines.h" +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#ifdef JNETLIB_EXPORTS +#define JNL_API __declspec(dllexport) +#else +#define JNL_API __declspec(dllimport) +#endif +#elif defined(__ANDROID__) || defined(__APPLE__) +#define JNL_API __attribute__ ((visibility("default"))) +#elif defined(__linux__) +#if __GNUC__ >= 4 +#define JNL_API __attribute__ ((visibility ("default"))) +#else +#define JNL_API +#endif +#else +#error port me +#endif + +/* these are reference counted. so make sure to match init/quit calls. */ +JNL_API int jnl_init(); +JNL_API void jnl_quit(); + +/* ----- Connection ----- */ +JNL_API jnl_connection_t jnl_connection_create( jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize ); +JNL_API jnl_connection_t jnl_sslconnection_create( jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize ); +JNL_API void jnl_connection_run( jnl_connection_t connection, size_t max_send_bytes, size_t max_receive_bytes, size_t *bytes_sent, size_t *bytes_received ); +JNL_API int jnl_connection_get_state( jnl_connection_t connection ); +JNL_API size_t jnl_connection_send_bytes_available( jnl_connection_t connection ); +JNL_API size_t jnl_connection_receive_bytes_available( jnl_connection_t connection ); +JNL_API int jnl_connection_receive_line( jnl_connection_t _connection, void *bytes, size_t size ); +JNL_API int jnl_connection_send( jnl_connection_t connection, const void *bytes, size_t size ); +JNL_API int jnl_connection_send_string( jnl_connection_t connection, const char *str ); +JNL_API size_t jnl_connection_receive( jnl_connection_t connection, void *bytes, size_t size ); +JNL_API size_t jnl_connection_send_bytes_in_queue( jnl_connection_t connection ); +JNL_API void jnl_connection_release( jnl_connection_t connection ); +JNL_API size_t jnl_connection_receive_lines_available( jnl_connection_t connection ); +JNL_API void jnl_connection_close( jnl_connection_t _connection, int fast ); +JNL_API void jnl_connection_connect( jnl_connection_t connection, const char *hostname, int port ); +JNL_API const char *jnl_connection_get_error( jnl_connection_t connection ); +JNL_API size_t jnl_connection_peek( jnl_connection_t _connection, void *bytes, size_t size ); + +/* ----- UDP ----- */ +JNL_API int jnl_udp_create_multicast_listener( jnl_udp_t *connection, const char *mcast_ip, unsigned short port ); +JNL_API void jnl_udp_release( jnl_udp_t connection ); +JNL_API void jnl_udp_run( jnl_udp_t connection, size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd ); +JNL_API size_t jnl_udp_recv_bytes( jnl_udp_t connection, void *buf, size_t len ); +JNL_API int jnl_udp_send( jnl_udp_t connection, const void *bytes, size_t size ); +JNL_API void jnl_udp_set_peer( jnl_udp_t connection, const char *hostname, unsigned short port ); +JNL_API void jnl_udp_set_peer_address( jnl_udp_t connection, struct sockaddr *addr, socklen_t length ); +// gets the address of whomever sent the last message +JNL_API int jnl_udp_get_address( jnl_udp_t connection, struct sockaddr **addr, socklen_t *length ); + +/* ----- HTTP ----- */ + +/* creation/destruction */ +JNL_API jnl_http_t jnl_http_create( int recvbufsize, int sendbufsize ); +JNL_API int jnl_http_set_recv_buffer_size( jnl_http_t http, size_t new_size ); /* increases the receive buffer size */ +JNL_API jnl_http_t jnl_http_retain( jnl_http_t http ); +JNL_API void jnl_http_release( jnl_http_t http ); +JNL_API jnl_connection_t jnl_http_get_connection( jnl_http_t http ); +/* TODO: replace these with a jnl_http_configure(jnl_http_t http) function */ +JNL_API void jnl_http_set_persistent( jnl_http_t http ); +JNL_API void jnl_http_allow_compression( jnl_http_t http ); +JNL_API void jnl_http_allow_accept_all_reply_codes( jnl_http_t http ); + +/* run & status stuff */ +JNL_API void jnl_http_connect( jnl_http_t http, const char *url, int http_version, const char *method ); +JNL_API int jnl_http_run( jnl_http_t http ); +JNL_API int jnl_http_get_status( jnl_http_t http ); +JNL_API int jnl_http_getreplycode( jnl_http_t http ); +JNL_API const char *jnl_http_getreply( jnl_http_t http ); + +/* reading data */ +JNL_API size_t jnl_http_get_bytes( jnl_http_t http, void *buf, size_t len ); +JNL_API size_t jnl_http_peek_bytes( jnl_http_t http, void *buf, size_t len ); +JNL_API size_t jnl_http_bytes_available( jnl_http_t http ); +JNL_API uint64_t jnl_http_content_length( jnl_http_t http ); + +/* HTTP headers */ +JNL_API const char *jnl_http_getheader( jnl_http_t http, const char *header ); +JNL_API void jnl_http_addheader( jnl_http_t http, const char *header ); +JNL_API void jnl_http_addheadervalue( jnl_http_t http, const char *header, const char *value ); +JNL_API void jnl_http_reset_headers( jnl_http_t http ); +JNL_API const char *jnl_http_get_all_headers( jnl_http_t http ); + +/* other information */ +JNL_API const char *jnl_http_get_url( jnl_http_t http ); +JNL_API void jnl_http_set_proxy( const char *proxy ); + +/* ----- HTTP Request Parsing ----- */ +JNL_API int jnl_http_request_create( jnl_http_request_t *http, jnl_connection_t connection ); +JNL_API void jnl_http_request_release( jnl_http_request_t http ); +JNL_API int jnl_http_request_run( jnl_http_request_t http ); +JNL_API int jnl_htt_request_get_keep_alive( jnl_http_request_t http ); +JNL_API const char *jnl_http_request_get_header( jnl_http_request_t http, const char *header ); +JNL_API void jnl_http_request_reset( jnl_http_request_t http ); +JNL_API void jnl_http_request_addheader( jnl_http_request_t http, const char *header ); +JNL_API void jnl_http_request_set_reply_string( jnl_http_request_t http, const char *reply ); +JNL_API void jnl_http_request_send_reply( jnl_http_request_t http ); +JNL_API const char *jnl_http_request_get_uri( jnl_http_request_t http ); +JNL_API const char *jnl_http_request_get_parameter( jnl_http_request_t _http, const char *parameter ); +JNL_API jnl_connection_t jnl_http_request_get_connection( jnl_http_request_t http ); +JNL_API const char *jnl_http_request_get_method( jnl_http_request_t http ); + +/* ----- HTTPU Request Parsing ----- */ +JNL_API int jnl_httpu_request_create( jnl_httpu_request_t *httpu ); +JNL_API void jnl_httpu_request_release( jnl_httpu_request_t httpu ); +JNL_API int jnl_httpu_request_process( jnl_httpu_request_t httpu, jnl_udp_t udp ); +JNL_API const char *jnl_httpu_request_get_method( jnl_httpu_request_t httpu ); +JNL_API const char *jnl_httpu_request_get_uri( jnl_httpu_request_t httpu ); +JNL_API const char *jnl_httpu_request_get_header( jnl_httpu_request_t httpu, const char *header ); + +/* ----- DNS ------ */ +JNL_API int jnl_dns_create( jnl_dns_t *dns ); +JNL_API void jnl_dns_release( jnl_dns_t dns ); +JNL_API int jnl_dns_resolve( jnl_dns_t dns, const char *hostname, unsigned short port, struct addrinfo **addr, int sockettype ); +// when you call jnl_dns_resolve_now, you need to call jnl_dns_freeaddrinfo +JNL_API int jnl_dns_resolve_now( const char *hostname, unsigned short port, struct addrinfo **addr, int sockettype ); +JNL_API void jnl_dns_freeaddrinfo( struct addrinfo *addr ); +JNL_API void jnl_dns_gethostname( char *name, size_t cch ); +JNL_API void jnl_dns_ntop( int af, const void *src, char *dst, socklen_t size ); +/* listen */ +JNL_API int jnl_listen_create( jnl_listen_t *listen, unsigned short port ); +JNL_API int jnl_listen_create_from_address( jnl_listen_t *listen, struct addrinfo *addr, size_t index ); +JNL_API void jnl_listen_release( jnl_listen_t listen ); +JNL_API jnl_connection_t jnl_listen_get_connection( jnl_listen_t listen ); +JNL_API unsigned short jnl_listen_get_port( jnl_listen_t listen ); + + +#ifdef __cplusplus +} +#endif + +#endif//_JNETLIB_H_ diff --git a/Src/replicant/jnetlib/jnetlib.sln b/Src/replicant/jnetlib/jnetlib.sln new file mode 100644 index 00000000..a8e81332 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE} + EndGlobalSection +EndGlobal diff --git a/Src/replicant/jnetlib/jnetlib.vcxproj b/Src/replicant/jnetlib/jnetlib.vcxproj new file mode 100644 index 00000000..c0913b22 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib.vcxproj @@ -0,0 +1,281 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{E105A0A2-7391-47C5-86AC-718003524C3D}</ProjectGuid> + <RootNamespace>jnetlib</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <GenerateDebugInformation>false</GenerateDebugInformation> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <GenerateDebugInformation>false</GenerateDebugInformation> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="asyncdns.cpp" /> + <ClCompile Include="connection.cpp" /> + <ClCompile Include="Headers.cpp" /> + <ClCompile Include="httpget.cpp" /> + <ClCompile Include="httpserv.cpp" /> + <ClCompile Include="httpuserv.cpp" /> + <ClCompile Include="jnetlib.cpp" /> + <ClCompile Include="listen.cpp" /> + <ClCompile Include="multicastlisten.cpp" /> + <ClCompile Include="sslconnection.cpp" /> + <ClCompile Include="udpconnection.cpp" /> + <ClCompile Include="util.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="asyncdns.h" /> + <ClInclude Include="connection.h" /> + <ClInclude Include="Headers.h" /> + <ClInclude Include="httpget.h" /> + <ClInclude Include="httpserv.h" /> + <ClInclude Include="httpuserv.h" /> + <ClInclude Include="jnetlib.h" /> + <ClInclude Include="jnetlib_defines.h" /> + <ClInclude Include="listen.h" /> + <ClInclude Include="multicastlisten.h" /> + <ClInclude Include="netinc.h" /> + <ClInclude Include="sslconnection.h" /> + <ClInclude Include="udpconnection.h" /> + <ClInclude Include="util.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="jnetlib-replicant.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\nu\nu.vcxproj"> + <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/replicant/jnetlib/jnetlib.vcxproj.filters b/Src/replicant/jnetlib/jnetlib.vcxproj.filters new file mode 100644 index 00000000..09c9c057 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib.vcxproj.filters @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Headers.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="jnetlib.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="multicastlisten.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="connection.cpp"> + <Filter>Source Files\Connection</Filter> + </ClCompile> + <ClCompile Include="asyncdns.cpp"> + <Filter>Source Files\DNS</Filter> + </ClCompile> + <ClCompile Include="sslconnection.cpp"> + <Filter>Source Files\Connection</Filter> + </ClCompile> + <ClCompile Include="httpget.cpp"> + <Filter>Source Files\HTTP Receiver</Filter> + </ClCompile> + <ClCompile Include="httpserv.cpp"> + <Filter>Source Files\HTTP Server</Filter> + </ClCompile> + <ClCompile Include="udpconnection.cpp"> + <Filter>Source Files\UDP Connection</Filter> + </ClCompile> + <ClCompile Include="listen.cpp"> + <Filter>Source Files\Web Server</Filter> + </ClCompile> + <ClCompile Include="httpuserv.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="Headers.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="httpuserv.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="jnetlib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="jnetlib_defines.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="netinc.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="util.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="connection.h"> + <Filter>Header Files\Connection</Filter> + </ClInclude> + <ClInclude Include="asyncdns.h"> + <Filter>Header Files\DNS</Filter> + </ClInclude> + <ClInclude Include="sslconnection.h"> + <Filter>Header Files\Connection</Filter> + </ClInclude> + <ClInclude Include="httpget.h"> + <Filter>Header Files\HTTP Receiver</Filter> + </ClInclude> + <ClInclude Include="httpserv.h"> + <Filter>Header Files\HTTP Server</Filter> + </ClInclude> + <ClInclude Include="listen.h"> + <Filter>Header Files\Web Server</Filter> + </ClInclude> + <ClInclude Include="udpconnection.h"> + <Filter>Header Files\UDP Connection</Filter> + </ClInclude> + <ClInclude Include="multicastlisten.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{bcdc816d-5cef-49ce-bcae-e765d84d9d0d}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{22cf6f69-c499-4eb0-851c-e405c0343744}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{ebc9b8af-fa9f-4cb3-bd28-a6f2fa43f47a}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Connection"> + <UniqueIdentifier>{08aa3960-7673-4eee-a554-f75b30f6ca9d}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\DNS"> + <UniqueIdentifier>{2194f281-7614-4fce-ae5b-14ebe64c75df}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\HTTP Receiver"> + <UniqueIdentifier>{2a2b5cdf-93f4-4228-ab77-50138a1df782}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\HTTP Server"> + <UniqueIdentifier>{c3f9a1b8-ab75-4d41-b614-df702b4a9113}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\UDP Connection"> + <UniqueIdentifier>{080bb148-d8bc-41d5-b2a2-405b3068b15f}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Web Server"> + <UniqueIdentifier>{2208b2ff-bd0e-42a9-b60b-fb6c1be6bad8}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Connection"> + <UniqueIdentifier>{ab26ced4-713d-4b15-bea7-49219fe68955}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\DNS"> + <UniqueIdentifier>{336ee3a1-044b-42c0-9f39-f03f381c73ee}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\HTTP Receiver"> + <UniqueIdentifier>{b7051bc5-90cf-4673-8274-02c12e3da56c}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\HTTP Server"> + <UniqueIdentifier>{48e64e69-bc76-467b-8fd3-3430694896ee}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Web Server"> + <UniqueIdentifier>{ad8b2861-d8e2-4737-b293-124ad361f20c}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\UDP Connection"> + <UniqueIdentifier>{60bab4c5-d369-4284-b314-dd8c3c4dcd37}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="jnetlib-replicant.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/replicant/jnetlib/jnetlib_defines.h b/Src/replicant/jnetlib/jnetlib_defines.h new file mode 100644 index 00000000..76647604 --- /dev/null +++ b/Src/replicant/jnetlib/jnetlib_defines.h @@ -0,0 +1,66 @@ +#pragma once +#include "../foundation/types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* Connection */ +typedef struct jnl_connection_struct jnl_connection_struct; +typedef jnl_connection_struct *jnl_connection_t; + +enum +{ + JNL_CONNECTION_STATE_ERROR = 0, + JNL_CONNECTION_STATE_NOCONNECTION = 1, + JNL_CONNECTION_STATE_RESOLVING = 2, + JNL_CONNECTION_STATE_CONNECTING = 3, + JNL_CONNECTION_STATE_CONNECTED = 4, + JNL_CONNECTION_STATE_CLOSING = 5, + JNL_CONNECTION_STATE_CLOSED = 6, + JNL_CONNECTION_STATE_RESOLVED = 7, // happens after RESOLVING, but going here for compatability +}; + +/* UDP */ +typedef struct jnl_udp_struct jnl_udp_struct; +typedef jnl_udp_struct *jnl_udp_t; + +/* HTTP */ +typedef struct jnl_http_struct jnl_http_struct; +typedef jnl_http_struct *jnl_http_t; + +enum +{ + HTTPGET_STATUS_ERROR = -1, + JNL_HTTP_STATUS_ERROR = HTTPGET_STATUS_ERROR, + HTTPGET_STATUS_CONNECTING = 0, + JNL_HTTP_STATUS_CONNECTING = HTTPGET_STATUS_CONNECTING, + HTTPGET_STATUS_READING_HEADERS = 1, + JNL_HTTP_STATUS_READING_HEADERS = HTTPGET_STATUS_READING_HEADERS, + HTTPGET_STATUS_READING_CONTENT = 2, + JNL_HTTP_STATUS_READING_CONTENT = HTTPGET_STATUS_READING_CONTENT, +}; + +enum +{ + HTTPGET_RUN_ERROR = -1, + HTTPGET_RUN_OK = 0, + JNL_HTTP_RUN_OK = HTTPGET_RUN_OK, + HTTPGET_RUN_CONNECTION_CLOSED = 1, +}; + +/* DNS */ +typedef struct jnl_dns_struct jnl_dns_struct; +typedef jnl_dns_struct *jnl_dns_t; + +typedef struct jnl_httpu_request_struct jnl_httpu_request_struct; +typedef jnl_httpu_request_struct *jnl_httpu_request_t; + +typedef struct jnl_http_request_struct jnl_http_request_struct; +typedef jnl_http_request_struct *jnl_http_request_t; + +typedef struct jnl_listen_struct jnl_listen_struct; +typedef jnl_listen_struct *jnl_listen_t; + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/Src/replicant/jnetlib/listen.cpp b/Src/replicant/jnetlib/listen.cpp new file mode 100644 index 00000000..20d34d06 --- /dev/null +++ b/Src/replicant/jnetlib/listen.cpp @@ -0,0 +1,131 @@ +/* +** JNetLib +** Copyright (C) 2000-2013 Nullsoft, Inc. +** Author: Justin Frankel, Ben Allison +** File: listen.cpp - JNL TCP listen implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "listen.h" +#include "foundation/error.h" + +JNL_Listen::JNL_Listen() +{ + m_port=0; + m_socket=-1; +} + +int JNL_Listen::Initialize(unsigned short port, sockaddr *which_interface, int family) +{ + m_port = port; + + char portString[32] = {0}; + sprintf(portString, "%d", (int)port); + + addrinfo *res; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + hints.ai_addr = which_interface?which_interface:INADDR_ANY; + + if (getaddrinfo(NULL, portString, &hints, &res) == 0) + { + int ret = Initialize(res, 0); + freeaddrinfo(res); + return ret; + } + else + { + return NErr_Error; + } +} + +int JNL_Listen::Initialize(addrinfo *address, size_t index) +{ + addrinfo *res = address; + + while (index--) + { + res = res->ai_next; + if (!res) + return NErr_EndOfEnumeration; + } + + m_socket = ::socket(res->ai_family,res->ai_socktype, res->ai_protocol); + if (m_socket < 0) + { + freeaddrinfo(res); + return NErr_Error; + } + else + { + SET_SOCK_BLOCK(m_socket,0); +#ifndef _WIN32 + int bflag = 1; + setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &bflag, sizeof(bflag)); +#endif + if (::bind(m_socket, res->ai_addr, (int)res->ai_addrlen)) + { + closesocket(m_socket); + m_socket=-1; + return NErr_Error; + } + else if (::listen(m_socket,8)==-1) + { + closesocket(m_socket); + m_socket=-1; + return NErr_Error; + } + } + return NErr_Success; +} + +JNL_Listen::~JNL_Listen() +{ + if (m_socket>=0) + { + closesocket(m_socket); + } +} + +JNL_Connection *JNL_Listen::get_connect(size_t sendbufsize, size_t recvbufsize) +{ + if (m_socket < 0) + { + return NULL; + } + sockaddr_storage saddr; + socklen_t length = sizeof(saddr); + SOCKET s = accept(m_socket, (sockaddr *)&saddr, &length); + if (s != -1) + { + JNL_Connection *c=new JNL_Connection(NULL,sendbufsize, recvbufsize); + c->connect(s, (sockaddr *)&saddr, length); + return c; + } + + return NULL; +} + +socklen_t JNL_Listen::get_address(sockaddr* address, socklen_t *address_len) +{ + return getsockname(m_socket, address, address_len); +} + +unsigned short JNL_Listen::get_port() +{ + if (!m_port) + { + sockaddr_in address; + socklen_t namelen = sizeof(address); + if (getsockname(m_socket, (sockaddr *)&address, &namelen) == 0) + m_port = ntohs(address.sin_port); + } + + return m_port; +}
\ No newline at end of file diff --git a/Src/replicant/jnetlib/listen.h b/Src/replicant/jnetlib/listen.h new file mode 100644 index 00000000..dc8cfc18 --- /dev/null +++ b/Src/replicant/jnetlib/listen.h @@ -0,0 +1,44 @@ +/* +** JNetLib +** Copyright (C) 2000-2013 Nullsoft, Inc. +** Author: Justin Frankel, Ben Allison +** File: listen.h - JNL interface for opening a TCP listen +** License: see jnetlib.h +** +** Usage: +** 1. create a JNL_Listen object with the port and (optionally) the interface +** to listen on. +** 2. call get_connect() to get any new connections (optionally specifying what +** buffer sizes the connection should be created with) +** 3. check is_error() to see if an error has occured +** 4. call port() if you forget what port the listener is on. +** +*/ + +#ifndef _LISTEN_H_ +#define _LISTEN_H_ + +#include "connection.h" +#include "nswasabi/ReferenceCounted.h" + +class JNL_Listen : public ReferenceCountedBase<JNL_Listen> +{ +public: + JNL_Listen(); + ~JNL_Listen(); + + int Initialize( unsigned short port, sockaddr *which_interface = 0, int family = PF_UNSPEC ); + int Initialize( struct addrinfo *address, size_t index ); + + JNL_Connection *get_connect( size_t sendbufsize = 8192, size_t recvbufsize = 8192 ); + unsigned short get_port(); + int is_error( void ) { return ( m_socket < 0 ); } + + socklen_t get_address( sockaddr *address, socklen_t *address_len ); + +protected: + SOCKET m_socket; + unsigned short m_port; +}; + +#endif //_LISTEN_H_ diff --git a/Src/replicant/jnetlib/multicastlisten.cpp b/Src/replicant/jnetlib/multicastlisten.cpp new file mode 100644 index 00000000..21eea33c --- /dev/null +++ b/Src/replicant/jnetlib/multicastlisten.cpp @@ -0,0 +1,123 @@ +/* +** JNetLib +** Copyright (C) 2008 Nullsoft, Inc. +** Author: Ben Allison +** File: multicastlisten.cpp - JNL Multicast UDP listen implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "multicastlisten.h" +#include "foundation/error.h" +#include <new> + +int CreateMulticastListener(JNL_UDPConnection **connection, const char *mcast_ip, unsigned short port, size_t sendbufsize, size_t recvbufsize) +{ + char portString[32] = {0}; + + if (port) + sprintf(portString, "%d", (int)port); + + addrinfo *res=0; + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /* IPv4 only for now until we get IPv6 multicast registration working */ + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + hints.ai_protocol = IPPROTO_UDP; + + if (getaddrinfo(NULL, port?portString:0, &hints, &res) == 0) + { + SOCKET m_socket = ::socket(res->ai_family,res->ai_socktype, res->ai_protocol); + if (m_socket < 0) + { + freeaddrinfo(res); + return NErr_Error; + } + else + { + SET_SOCK_BLOCK(m_socket,0); + + int bflag = 1; + if (setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&bflag, sizeof(bflag))) + { + /*int err = ERRNO; + err=err; + printf("SO_REUSEADDR error: %d\n", err);*/ + } +#if defined(__FreeBSD__) || defined(__APPLE__) + bflag=1; // in case it magically got unset above + setsockopt(m_socket, SOL_SOCKET, SO_REUSEPORT, (const char *)&bflag, sizeof(bflag)); +#endif + if (::bind(m_socket, res->ai_addr, (int)res->ai_addrlen)) + { + closesocket(m_socket); + return NErr_Error; + } + else + { + // TODO: ipv6 with IPV6_ADD_MEMBERSHIP and ipv6_mreq + + sockaddr_in *ipv4 = (sockaddr_in *)res->ai_addr; + + /* join multicast group */ + ip_mreq ssdpMcastAddr; + memset(&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr)); + ssdpMcastAddr.imr_interface = ipv4->sin_addr; + ssdpMcastAddr.imr_multiaddr.s_addr = inet_addr(mcast_ip); + if (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&ssdpMcastAddr, sizeof(ssdpMcastAddr))) + { + closesocket(m_socket); + freeaddrinfo(res); + return NErr_Error; + } + + /* Set multicast interface. */ + in_addr addr; + memset(&addr, 0, sizeof(addr)); + addr = ipv4->sin_addr; + if (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&addr, sizeof(addr))) + { + /*int err = ERRNO; + err=err; + printf("IP_MULTICAST_IF error: %d\n", err);*/ + /* This is probably not a critical error, so let's continue. */ + } + + /* set TTL to 4 */ + uint8_t ttl=4; + if (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ttl, sizeof(ttl))) + { + /*int err = ERRNO; + err=err; + printf("IP_MULTICAST_TTL error: %d\n", err);*/ + /* This is probably not a critical error, so let's continue. */ + } + + int option = 1; + if (setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, (const char *)&option, sizeof(option)) != 0) + { + closesocket(m_socket); + freeaddrinfo(res); + return NErr_Error; + } + } + } + freeaddrinfo(res); + JNL_UDPConnection *c=new (std::nothrow) JNL_UDPConnection(); + if (!c) + { + closesocket(m_socket); + return NErr_OutOfMemory; + } + c->open((int)m_socket, NULL, sendbufsize, recvbufsize); + *connection = c; + return NErr_Success; + } + else + { + return NErr_Error; + } +} + diff --git a/Src/replicant/jnetlib/multicastlisten.h b/Src/replicant/jnetlib/multicastlisten.h new file mode 100644 index 00000000..eaa918a1 --- /dev/null +++ b/Src/replicant/jnetlib/multicastlisten.h @@ -0,0 +1,26 @@ +/* +** JNetLib +** Copyright (C) 2008 Nullsoft, Inc. +** Author: Ben Allison +** File: multicastlisten.h - JNL interface for opening a Multicast UDP listen +** License: see jnetlib.h +** +** Usage: +** 1. create a JNL_MulticastUDPListen object with the port and (optionally) the interface +** to listen on. +** 2. call get_connect() to get the associated UDP connection (optionally specifying what +** buffer sizes the connection should be created with). Unlike TCP listen, there is only one listen object. +** 3. check is_error() to see if an error has occured +** 4. call port() if you forget what port the listener is on. +** +*/ + +#ifndef _MULTICASTLISTEN_H_ +#define _MULTICASTLISTEN_H_ + +#include "udpconnection.h" + + +int CreateMulticastListener( JNL_UDPConnection **connection, const char *mcast_ip, unsigned short port, size_t sendbufsize = 8192, size_t recvbufsize = 8192 ); + +#endif //_LISTEN_H_ diff --git a/Src/replicant/jnetlib/netinc.h b/Src/replicant/jnetlib/netinc.h new file mode 100644 index 00000000..1952d3d7 --- /dev/null +++ b/Src/replicant/jnetlib/netinc.h @@ -0,0 +1,84 @@ +/* +** JNetLib +** Copyright (C) 2000-2006 Nullsoft, Inc. +** Author: Justin Frankel +** File: netinc.h - network includes and portability defines (used internally) +** License: see jnetlib.h +*/ + +#ifndef _NETINC_H_ +#define _NETINC_H_ + +#ifdef _WIN32 + +//#include <time.h> +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <winsock2.h> +#include <Ws2tcpip.h> +#include <wspiapi.h> +#include "../foundation/types.h" + +#define strtoull(x,y,z) _strtoui64(x,y,z) +#define strcasecmp(x,y) _stricmp(x,y) +#define strcasecmpn(x,y, count) _strnicmp(x,y,count) +#define strncasecmp(x,y, count) _strnicmp(x,y,count) +#define HTONS(val) ((((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | (((unsigned short) (val) & (unsigned short) 0xff00U) >> 8)) +#define ERRNO (WSAGetLastError()) +#define SET_SOCK_BLOCK(s,block) { unsigned long __i=block?0:1; ioctlsocket(s,FIONBIO,&__i); } +#define JNL_EWOULDBLOCK WSAEWOULDBLOCK +#define JNL_EINPROGRESS WSAEWOULDBLOCK + +#else +#define JNL_EWOULDBLOCK EWOULDBLOCK +#define JNL_EINPROGRESS EINPROGRESS +#ifndef THREAD_SAFE +#define THREAD_SAFE +#endif +#ifndef _REENTRANT +#define _REENTRANT +#endif +#include <pthread.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + + +#define ERRNO errno +#define closesocket(s) close(s) +#define SET_SOCK_BLOCK(s,block) { int __flags; if ((__flags = fcntl(s, F_GETFL, 0)) != -1) { if (!block) __flags |= O_NONBLOCK; else __flags &= ~O_NONBLOCK; fcntl(s, F_SETFL, __flags); } } + +#define _stricmp(x,y) strcasecmp(x,y) +#define _strnicmp(x,y,z) strncasecmp(x,y,z) +#define wsprintf sprintf +typedef int SOCKET; + +#endif // !_WIN32 + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#ifndef INADDR_ANY +#define INADDR_ANY 0 +#endif + +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif + +#endif //_NETINC_H_ diff --git a/Src/replicant/jnetlib/resource.h b/Src/replicant/jnetlib/resource.h new file mode 100644 index 00000000..b6cf148a --- /dev/null +++ b/Src/replicant/jnetlib/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by jnetlib-replicant.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/replicant/jnetlib/sslconnection.cpp b/Src/replicant/jnetlib/sslconnection.cpp new file mode 100644 index 00000000..5362c445 --- /dev/null +++ b/Src/replicant/jnetlib/sslconnection.cpp @@ -0,0 +1,329 @@ +//#ifdef USE_SSL +//#include "netinc.h" +//#include "util.h" +//#include "connection.h" +#include "sslconnection.h" + +SSL_CTX *sslContext = 0; + +#ifdef _DEBUG + +extern "C" void apps_ssl_info_callback (const SSL * s, int where, int ret) +{ + /* + ** DEBUG INFO HERE + */ +} +#endif + +/* +** Well, you should probably change this based on like... +** well, you're level of trust heh +** For now, this basically trusts all certs :) +** +*/ +#if 0 +extern "C" int verify_callback(int ok, X509_STORE_CTX * ctx) +{ + /* For certificate verification */ + int verify_depth = 0; + int verify_error = X509_V_OK; + + char buf[1024] = {0}; + X509 * err_cert = X509_STORE_CTX_get_current_cert(ctx); + int err = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + + if (!ok) + { + if (verify_depth >= depth) + { + ok = 1; + verify_error = X509_V_OK; + } + else + { + ok = 0; + verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG; + } + } + switch (ctx->error) + { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf)); + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + break; + + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + break; + } + return ok; +} +#endif + +JNL_SSL_Connection::JNL_SSL_Connection() : forceConnect(false), m_ssl(0), m_bsslinit(false), m_bcontextowned(true) +{ + m_bsslinit = true; +} + + +JNL_SSL_Connection::JNL_SSL_Connection(SSL* pssl, JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize) : JNL_Connection(dns, sendbufsize, recvbufsize), + forceConnect(false) +{ + m_ssl = pssl; + m_bsslinit = false; + + if (m_ssl) + { + m_bcontextowned = false; + } + else + { + m_bcontextowned = true; + } + + if (m_bcontextowned == false) + { + return ; + } + + m_bsslinit = true; + + + /* See the SSL states in our own callback */ +#ifdef _DEBUG + // SSL_CTX_set_info_callback(m_app_ctx, apps_ssl_info_callback); +#endif + + /* Set the certificate verification callback */ + //SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, verify_callback); + + /* Not sure what this does */ + //SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_CLIENT); + + return ; +} + +int JNL_SSL_Connection::socket_connect() +{ + int retval; + + if (m_bcontextowned == false) + { + /* + ** WTF? + */ + return -1; + } + if (m_ssl != NULL) + { + return -1; + } + + retval = JNL_Connection::socket_connect(); + + if (retval != 0) + { + if (ERRNO != JNL_EINPROGRESS) + { + return retval; // benski> if the underlying socket hasn't connected yet, then we can't start the SSL connection + /* + ** Joshua Teitelbaum 3/2/2006 + ** Fatal error here + */ + } + } + + // moved from InitSSL() as no need to create this unless + // we're actually going to use it which helps slow loads + if (!sslContext) + { + sslContext = SSL_CTX_new(SSLv23_client_method()); + if (sslContext) + { + SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL); + } + else + { + return -1; + } + } + + m_ssl = SSL_new(sslContext); + + if (m_ssl == NULL) + { + return -1; + } + + /* Tell that we are in connect mode */ + SSL_set_connect_state(m_ssl); + + /* Set socket descriptor with the socket we already have open */ + if(SSL_set_fd(m_ssl, m_socket) != 0) + { + return -1; + } + + return retval; +} + +void JNL_SSL_Connection::socket_shutdown() +{ + if (m_ssl) + SSL_shutdown(m_ssl); + JNL_Connection::socket_shutdown(); + + if (m_ssl) + { + SSL_free(m_ssl); + m_ssl = NULL; + } + return ; +} + +void JNL_SSL_Connection::run(size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd) +{ + if (!m_bsslinit) + { + int rval = SSL_accept(m_ssl); + if (rval == -1) + { + int e = SSL_get_error(m_ssl, rval); + + if (!((e == SSL_ERROR_WANT_READ) || (e == SSL_ERROR_WANT_WRITE))) + { + m_state = STATE_ERROR; + } + + return ; + } + else + { + m_bsslinit = true; + } + } + + /** + ** benski - march 2, 2006 + **if the underlying socket didn't connected yet, we need to try the SSL connection again + */ + if (forceConnect) + { + if(init_ssl_connection() == false) + { + return; + } + } + JNL_Connection::run(max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd); +} + +/* +** init_ssl_connection: +** Returns true, meaning can continue +** Else, cannot continue with underlying run +** side effects: +** sets forceConnect +*/ +bool JNL_SSL_Connection::init_ssl_connection() +{ + if(m_ssl == NULL) + { + /* + ** WTF? + ** cascade up. + */ + return true; + } + + int retval = SSL_connect(m_ssl); + + if (retval < 0) + { + int err = SSL_get_error(m_ssl, retval); + + switch (err) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_CONNECT: + forceConnect = true; + break; + // fall through + default: // TODO: benski> MOST other errors are OK, (especially "want read" and "want write", but we need to think through all the scenarios here + forceConnect=false; + } + } + else if(retval) + { + /* + ** success + */ + forceConnect = false; + } + + /* + ** If retval == 0 + ** socket is closed, or serious error occurred. + ** cascade up. + */ + + return forceConnect==false; +} + +void JNL_SSL_Connection::on_socket_connected(void) +{ + init_ssl_connection(); +} + +void JNL_SSL_Connection::connect(SOCKET s, sockaddr *addr, socklen_t length) +{ + /* + ** Joshua Teitelbaum 2/01/2006 + ** No need to close + ** This is the reason for divergence as well as setting + ** the connect state + */ + + 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; + SSL_set_fd(m_ssl, m_socket); + } + else + { + m_errorstr = "invalid socket passed to connect"; + m_state = STATE_ERROR; + } +} + +ssize_t JNL_SSL_Connection::socket_recv(char *buf, size_t len, int options) +{ + return SSL_read(m_ssl, buf, (int)len); +} + +ssize_t JNL_SSL_Connection::socket_send(const char *buf, size_t len, int options) +{ + return SSL_write(m_ssl, buf, (int)len); +} + +JNL_SSL_Connection::~JNL_SSL_Connection() +{ + if (m_ssl) + { + SSL_free(m_ssl); + m_ssl = NULL; + } +} +//#endif
\ No newline at end of file diff --git a/Src/replicant/jnetlib/sslconnection.h b/Src/replicant/jnetlib/sslconnection.h new file mode 100644 index 00000000..f6e54d05 --- /dev/null +++ b/Src/replicant/jnetlib/sslconnection.h @@ -0,0 +1,61 @@ +/* +** JNetLib +** Copyright (C) 2000-2006 CockOS, Inc. +** Author: Justin Frankel, Joshua Teitelbaum +** File: sslconnection.h - JNL SSL TCP connection interface +** License: see jnetlib.h +*/ + +#ifndef _JNETLIB_SSL_H_ +#define _JNETLIB_SSL_H_ + +#include "netinc.h" +#include "util.h" +#include "connection.h" + +/* +** AUTOLINK WITH THESE GUYS NOT IN PROJECT HEH HEH :) +** Build and go, for you :) +*/ + +#include <openssl/ssl.h> + +class JNL_SSL_Connection : public JNL_Connection +{ +protected: + SSL *m_ssl; + bool m_bcontextowned; + bool m_bsslinit; + +public: + JNL_SSL_Connection(); + JNL_SSL_Connection( SSL *pssl, JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize ); + virtual ~JNL_SSL_Connection(); + + virtual void connect( SOCKET sock, sockaddr *addr, socklen_t length /* of addr */ ); // used by the listen object, usually not needed by users. + virtual void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL ); + + /* + ** Joshua Teitelbaum 1/27/2006 Adding new BSD socket analogues for SSL compatibility + */ +protected: + virtual void socket_shutdown(); + virtual ssize_t socket_recv( char *buf, size_t len, int options ); + virtual ssize_t socket_send( const char *buf, size_t len, int options ); + virtual int socket_connect(); + virtual void on_socket_connected(); + /* + ** init_ssl_connection: + ** returns true if can continue onwards (could be error, in which case + ** we want the error to cascade through). + ** Else, false if connection was not made, and we have to call this + ** again. + */ + bool init_ssl_connection(); + + bool forceConnect; +}; + +extern SSL_CTX *sslContext; + +#endif diff --git a/Src/replicant/jnetlib/udpconnection.cpp b/Src/replicant/jnetlib/udpconnection.cpp new file mode 100644 index 00000000..fbb36eca --- /dev/null +++ b/Src/replicant/jnetlib/udpconnection.cpp @@ -0,0 +1,384 @@ +/* +** JNetLib +** Copyright (C) 2000-2001 Nullsoft, Inc. +** Author: Justin Frankel +** File: udpconnection.cpp - JNL UDP connection implementation +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "udpconnection.h" + +JNL_UDPConnection::JNL_UDPConnection(unsigned short incoming_port, JNL_AsyncDNS *dns, int sendbufsize, int recvbufsize) +{ + init(); + open(dns, sendbufsize, recvbufsize); + + m_socket=::socket(PF_INET,SOCK_DGRAM,0); + if (m_socket==-1) + { + m_errorstr="creating socket"; + m_state=STATE_ERROR; + } + SET_SOCK_BLOCK(m_socket,0); + sockaddr_in m_iaddr; + memset(&m_iaddr,0,sizeof(struct sockaddr_in)); + m_iaddr.sin_family=AF_INET; + m_iaddr.sin_port=htons(incoming_port); + m_iaddr.sin_addr.s_addr = htonl( INADDR_ANY ); + if(::bind(m_socket,(struct sockaddr *)&m_iaddr,sizeof(m_iaddr))==-1) + { + m_errorstr="binding socket"; + m_state=STATE_ERROR; + } + m_state=STATE_CONNECTED; +} + +JNL_UDPConnection::JNL_UDPConnection() +{ + init(); +} + +void JNL_UDPConnection::init() +{ + m_errorstr=""; + address=0; + address_len=0; + m_dns=0; + m_dns_owned=false; + m_socket=-1; + m_remote_port=0; + m_state=STATE_NOCONNECTION; + m_host=0; + saddr=0; + m_last_addr_len=0; + ttl=0; +} + +void JNL_UDPConnection::set_ttl(uint8_t new_ttl) +{ + ttl=new_ttl; + setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ttl, sizeof(ttl)); +} + +void JNL_UDPConnection::open(int incoming_socket, 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); + + m_socket=incoming_socket; + m_state=STATE_CONNECTED; + +} + +void JNL_UDPConnection::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_UDPConnection::setpeer(const char *hostname, int port) +{ + m_remote_port=(unsigned short)port; + m_host = _strdup(hostname); + + if (!m_host || !m_host[0]) + { + m_errorstr="empty hostname"; + m_state=STATE_ERROR; + } + else + { + m_state=STATE_RESOLVING; + } +} + +void JNL_UDPConnection::setpeer(sockaddr *addr, socklen_t length /* of addr */) +{ + //memcpy(&m_saddr, addr, sizeof(sockaddr)); + free(address); + address_len=length; + address=(sockaddr *)malloc(length); + memcpy(address, addr, length); +} + +JNL_UDPConnection::~JNL_UDPConnection() +{ + if (m_socket >= 0) + { + ::closesocket(m_socket); + m_socket=-1; + } + 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<JNL_AsyncDNS *>(m_dns); + } + free(m_host); +} + +void JNL_UDPConnection::run(size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd) +{ + size_t bytes_allowed_to_send=(max_send_bytes<0)?send_buffer.size():max_send_bytes; + size_t bytes_allowed_to_recv=(max_recv_bytes<0)?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_DGRAM); + if (!a) + { + address=saddr->ai_addr; + address_len=(socklen_t)saddr->ai_addrlen; + m_state=STATE_CONNECTED; + } + else if (a == 1) + { + m_state=STATE_RESOLVING; + break; + } + else + { + m_errorstr="resolving hostname"; + m_state=STATE_ERROR; + return; + } + } + break; + case STATE_CONNECTED: + case STATE_CLOSING: + if (!send_buffer.empty() && bytes_allowed_to_send>0) + { + size_t sent = send_buffer.drain(this, bytes_allowed_to_send); + if (bytes_sent) + *bytes_sent+=sent; + } + /* only read from socket when buffer is empty + * otherwise we risk data loss + * see "man 2 recvfrom" for details + */ + if (recv_buffer.empty() && bytes_allowed_to_recv) + { + /* + * use LockBuffer()/UnlockBuffer() because + * "wrap-around" reads can't be done + * we might read data from two separate packets + */ + size_t len = recv_buffer.avail(); + if (bytes_allowed_to_recv < len) + len = bytes_allowed_to_recv; + + recv_buffer.clear(); + void *buffer = recv_buffer.LockBuffer(); + m_last_addr_len = (int)sizeof(m_last_addr); + int res=::recvfrom(m_socket,(char *)buffer,(int)len,0,(sockaddr *)&m_last_addr,&m_last_addr_len); + if (res == 0 || (res < 0 && ERRNO != JNL_EWOULDBLOCK)) + { + recv_buffer.UnlockBuffer(0); + m_state=STATE_CLOSED; + break; + } + if (res > 0) + { + if (bytes_rcvd) + *bytes_rcvd+=res; + recv_buffer.UnlockBuffer(res); + } + else + recv_buffer.UnlockBuffer(0); + + } + if (m_state == STATE_CLOSING) + { + if (send_buffer.empty()) m_state = STATE_CLOSED; + } + break; + default: + break; + } +} + +/* RingBuffer client function */ +size_t JNL_UDPConnection::Read(void *dest, size_t len) +{ + if (!len) + return 0; + + m_last_addr_len = (int)sizeof(m_last_addr); + int res=::recvfrom(m_socket, (char *)dest, (int)len, 0, (sockaddr *)&m_last_addr,&m_last_addr_len); + 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_UDPConnection::Write(const void *dest, size_t len) +{ + if (!len) + return 0; + int res=::sendto(m_socket, (const char *)dest, (int)len, 0, address, address_len); + if (res==-1 && ERRNO != JNL_EWOULDBLOCK) + { + return 0; + // m_state=STATE_CLOSED; + } + if (res > 0) + return res; + else + return 0; +} + +void JNL_UDPConnection::close(int quick) +{ + if (quick || m_state == STATE_RESOLVING) + { + m_state=STATE_CLOSED; + if (m_socket >= 0) + { + ::closesocket(m_socket); + } + m_socket=-1; + recv_buffer.clear(); + send_buffer.clear(); + m_remote_port=0; + free(m_host); + m_host=0; + //memset(&m_saddr,0,sizeof(m_saddr)); + } + else + { + if (m_state == STATE_CONNECTED) m_state=STATE_CLOSING; + } +} + +size_t JNL_UDPConnection::send_bytes_in_queue(void) +{ + return send_buffer.size(); +} + +size_t JNL_UDPConnection::send_bytes_available(void) +{ + return send_buffer.avail(); +} + +int JNL_UDPConnection::send(const void *data, size_t length) +{ + if (length > send_bytes_available()) + { + return -1; + } + + send_buffer.write(data, length); + return 0; +} + +int JNL_UDPConnection::send_string(const char *line) +{ + return send(line,strlen(line)); +} + +size_t JNL_UDPConnection::recv_bytes_available(void) +{ + return recv_buffer.size(); +} + +size_t JNL_UDPConnection::peek_bytes(void *data, size_t maxlength) +{ + return recv_buffer.peek(data, maxlength); +} + +size_t JNL_UDPConnection::recv_bytes(void *data, size_t maxlength) +{ + return recv_buffer.read(data, maxlength); +} + +int JNL_UDPConnection::recv_lines_available(void) +{ + int l=(int)recv_bytes_available(); + int lcount=0; + int lastch=0; + int pos; + for (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_UDPConnection::recv_line(char *line, size_t maxlength) +{ + if (maxlength > recv_buffer.size()) maxlength=recv_buffer.size(); + 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; +} + +int JNL_UDPConnection::get_interface(sockaddr *sin, socklen_t *sin_length) +{ + if (m_socket==-1) return 1; + /*memset(sin,0,sizeof(sockaddr_storage)); + *sin_length =sizeof(sockaddr_storage);*/ + if (::getsockname(m_socket,(sockaddr *)sin,sin_length)) + return 1; + return 0; +} + diff --git a/Src/replicant/jnetlib/udpconnection.h b/Src/replicant/jnetlib/udpconnection.h new file mode 100644 index 00000000..c91d8422 --- /dev/null +++ b/Src/replicant/jnetlib/udpconnection.h @@ -0,0 +1,151 @@ +/* +** JNetLib +** Copyright (C) 2000-2001 Nullsoft, Inc. +** Author: Justin Frankel +** File: udpconnection.h - JNL UDP connection interface +** License: see jnetlib.h +** +** Usage: +** 1. Create a JNL_Connection object, optionally specifying a JNL_AsyncDNS +** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto), +** and the send and receive buffer sizes. +** 2. Call connect() to have it connect to a host/port (the hostname will be +** resolved if possible). +** 3. call run() with the maximum send/recv amounts, and optionally parameters +** so you can tell how much has been send/received. You want to do this a lot, while: +** 4. check get_state() to check the state of the connection. The states are: +** JNL_Connection::STATE_ERROR +** - an error has occured on the connection. the connection has closed, +** and you can no longer write to the socket (there still might be +** data in the receive buffer - use recv_bytes_available()). +** JNL_Connection::STATE_NOCONNECTION +** - no connection has been made yet. call connect() already! :) +** JNL_Connection::STATE_RESOLVING +** - the connection is still waiting for a JNL_AsycnDNS to resolve the +** host. +** JNL_Connection::STATE_CONNECTING +** - the asynchronous call to connect() is still running. +** JNL_Connection::STATE_CONNECTED +** - the connection has connected, all is well. +** JNL_Connection::STATE_CLOSING +** - the connection is closing. This happens after a call to close, +** without the quick parameter set. This means that the connection +** will close once the data in the send buffer is sent (data could +** still be being received when it would be closed). After it is +** closed, the state will transition to: +** JNL_Connection::STATE_CLOSED +** - the connection has closed, generally without error. There still +** might be data in the receieve buffer, use recv_bytes_available(). +** 5. Use send() and send_string() to send data. You can use +** send_bytes_in_queue() to see how much has yet to go out, or +** send_bytes_available() to see how much you can write. If you use send() +** or send_string() and not enough room is available, both functions will +** return error ( < 0) +** 6. Use recv() and recv_line() to get data. If you want to see how much data +** there is, use recv_bytes_available() and recv_lines_available(). If you +** call recv() and not enough data is available, recv() will return how much +** data was actually read. See comments at the function defs. +** +** 7. To close, call close(1) for a quick close, or close() for a close that will +** make the socket close after sending all the data sent. +** +** 8. delete ye' ol' object. +*/ + +#ifndef _UDPCONNECTION_H_ +#define _UDPCONNECTION_H_ + +#include "asyncdns.h" +#include "nu/RingBuffer.h" +#include "jnetlib_defines.h" + +#define JNL_DEFAULT_BUFFER_SIZE 8192 + +class JNL_UDPConnection : private Drainer, private Filler +{ +public: + typedef enum + { + STATE_ERROR = JNL_CONNECTION_STATE_ERROR, + STATE_NOCONNECTION = JNL_CONNECTION_STATE_NOCONNECTION, + STATE_RESOLVING = JNL_CONNECTION_STATE_RESOLVING, + STATE_CONNECTING = JNL_CONNECTION_STATE_CONNECTING, + STATE_CONNECTED = JNL_CONNECTION_STATE_CONNECTED, + STATE_CLOSING = JNL_CONNECTION_STATE_CLOSING, + STATE_CLOSED = JNL_CONNECTION_STATE_CLOSED, + STATE_RESOLVED = JNL_CONNECTION_STATE_RESOLVED, + } state; + + JNL_UDPConnection(); + JNL_UDPConnection( unsigned short port, JNL_AsyncDNS *dns, int sendbufsize = JNL_DEFAULT_BUFFER_SIZE, int recvbufsize = JNL_DEFAULT_BUFFER_SIZE ); + ~JNL_UDPConnection(); + + void open( JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = JNL_DEFAULT_BUFFER_SIZE, size_t recvbufsize = JNL_DEFAULT_BUFFER_SIZE ); + void open( int incoming_socket, JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = JNL_DEFAULT_BUFFER_SIZE, size_t recvbufsize = JNL_DEFAULT_BUFFER_SIZE ); + + void setpeer( const char *hostname, int port ); + void setpeer( sockaddr *addr, socklen_t length /* of addr */ ); + + void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL ); + + int get_state() { return m_state; } + const char *get_errstr() { return m_errorstr; } + + void close( int quick = 0 ); + void flush_send( void ) { send_buffer.clear(); } + + size_t send_bytes_in_queue( void ); + size_t send_bytes_available( void ); + int send( const void *data, size_t length ); // returns -1 if not enough room + int send_string( const char *line ); // returns -1 if not enough room + + + size_t recv_bytes_available( void ); + size_t recv_bytes( void *data, size_t maxlength ); // returns actual bytes read + unsigned int recv_int( void ); + int recv_lines_available( void ); + int recv_line( char *line, size_t maxlength ); // returns 0 if the line was terminated with a \r or \n, 1 if not. + // (i.e. if you specify maxlength=10, and the line is 12 bytes long + // it will return 1. or if there is no \r or \n and that's all the data + // the connection has.) + size_t peek_bytes( void *data, size_t maxlength ); // returns bytes peeked + + int get_interface( sockaddr *sin, socklen_t *sin_length ); // this returns the interface the connection is on + short get_remote_port( void ) { return m_remote_port; } // this returns the remote port of connection + + void get_last_recv_msg_addr( sockaddr **addr, socklen_t *len ) { *addr = (sockaddr *)&m_last_addr; *len = m_last_addr_len; } + + void set_ttl( uint8_t new_ttl ); + +protected: + uint8_t ttl; + SOCKET m_socket; + unsigned short m_remote_port; + RingBuffer recv_buffer; + RingBuffer send_buffer; + + sockaddr *address; + socklen_t address_len; + + sockaddr_storage m_last_addr; + socklen_t m_last_addr_len; + addrinfo *saddr; + + char *m_host; + + JNL_AsyncDNS *m_dns; + int m_dns_owned; + + state m_state; + const char *m_errorstr; + +private: + void init(); // constructor helper function + + // functions for RingBuffer + size_t Read( void *dest, size_t len ); + size_t Write( const void *dest, size_t len ); + +}; + +#endif // _UDPConnection_H_ diff --git a/Src/replicant/jnetlib/util.cpp b/Src/replicant/jnetlib/util.cpp new file mode 100644 index 00000000..3b2b5a11 --- /dev/null +++ b/Src/replicant/jnetlib/util.cpp @@ -0,0 +1,185 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: util.cpp - JNL implementation of basic network utilities +** License: see jnetlib.h +*/ + +#include "netinc.h" +#include "util.h" +#include "foundation/error.h" +#ifdef USE_SSL +#include "sslconnection.h" +#ifdef _WIN32 +#include <wincrypt.h> +#endif +#include <openssl/rand.h> + +#ifdef _WIN32 +static HCRYPTPROV GetKeySet() +{ + HCRYPTPROV hCryptProv; + LPCWSTR UserName = L"WinampKeyContainer"; // name of the key container + + if (CryptAcquireContext( + &hCryptProv, // handle to the CSP + UserName, // container name + NULL, // use the default provider + PROV_RSA_FULL, // provider type + 0)) // flag values + { + return hCryptProv; + } + else if (CryptAcquireContext( + &hCryptProv, + UserName, + NULL, + PROV_RSA_FULL, + CRYPT_NEWKEYSET)) + { + return hCryptProv; + } + else + return 0; +} +#endif + +static void InitSSL() +{ + SSL_load_error_strings(); + SSL_library_init(); +#ifdef _WIN32 + HCRYPTPROV hCryptProv = GetKeySet(); + if (hCryptProv) + { + BYTE pbData[8*sizeof(unsigned long)] = {0}; + if (CryptGenRandom(hCryptProv, 8*sizeof(unsigned long), pbData)) + { + RAND_seed(pbData, 16); + } + CryptReleaseContext(hCryptProv,0); + } +#endif +// sslContext = SSL_CTX_new(SSLv23_client_method()); +// SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL); + +// SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); +} +static int open_ssl_initted = 0; +#endif + +static int was_initted = 0; + +int JNL::open_socketlib() +{ +#ifdef _WIN32 + if (!was_initted) + { + WSADATA wsaData = {0}; + if (WSAStartup(MAKEWORD(1, 1), &wsaData)) + { + return NErr_Error; + } + } +#endif +#ifdef USE_SSL + if (!open_ssl_initted) + { + InitSSL(); + open_ssl_initted=1; + } +#endif + return NErr_Success; +} + +void JNL::close_socketlib() +{ +#ifdef _WIN32 + if (was_initted) + { + WSACleanup(); + } +#ifdef USE_SSL + // TODO need to do some reference counting to free this correctly + //SSL_CTX_free(sslContext); +#endif +#endif +} + +static char *jnl_strndup(const char *str, size_t n) +{ + char *o = (char *)calloc(n+1, sizeof(char)); + if (!o) + { + return 0; + } + strncpy(o, str, n); + o[n]=0; + return o; +} + +int JNL::parse_url(const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp) +{ + free(*prot); *prot=0; + free(*host); *host = 0; + free(*req); *req = 0; + free(*lp); *lp = 0; + *port = 0; + + const char *p; + const char *protocol = strstr(url, "://"); + if (protocol) + { + *prot = jnl_strndup(url, protocol-url); + p = protocol + 3; + } + else + { + p = url; + } + + while (p && *p && *p == '/') p++; // skip extra / + + size_t end = strcspn(p, "@/"); + + // check for username + if (p[end] == '@') + { + *lp = jnl_strndup(p, end); + p = p+end+1; + end = strcspn(p, "[:/"); + } + + if (p[0] == '[') // IPv6 style address + { + p++; + const char *ipv6_end = strchr(p, ']'); + if (!ipv6_end) + return NErr_Malformed; + + *host = jnl_strndup(p, ipv6_end-p); + p = ipv6_end+1; + } + else + { + end = strcspn(p, ":/"); + *host = jnl_strndup(p, end); + p += end; + } + + // is there a port number? + if (p[0] == ':') + { + char *new_end; + *port = (unsigned short)strtoul(p+1, &new_end, 10); + p = new_end; + } + + if (p[0]) + { + *req = _strdup(p); + } + + return NErr_Success; +} diff --git a/Src/replicant/jnetlib/util.h b/Src/replicant/jnetlib/util.h new file mode 100644 index 00000000..fa1cd005 --- /dev/null +++ b/Src/replicant/jnetlib/util.h @@ -0,0 +1,40 @@ +/* +** JNetLib +** Copyright (C) 2000-2007 Nullsoft, Inc. +** Author: Justin Frankel +** File: util.h - JNL interface for basic network utilities +** License: see jnetlib.h +** +** routines you may be interested in: +** JNL::open_socketlib(); +** opens the socket library. Call this once before using any network +** code. If you create a new thread, call this again. Only really an +** issue for Win32 support, but use it anyway for portability/ +** +** JNL::close_Socketlib(); +** closes the socketlib. Call this when you're done with the network, +** after all your JNetLib objects have been destroyed. +** +** unsigned long JNL::ipstr_to_addr(const char *cp); +** gives you the integer representation of a ip address in dotted +** decimal form. +** +** JNL::addr_to_ipstr(unsigned long addr, char *host, int maxhostlen); +** gives you the dotted decimal notation of an integer ip address. +** +*/ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +class JNL +{ +public: + static int open_socketlib(); + static void close_socketlib(); + static unsigned long ipstr_to_addr( const char *cp ); + static void addr_to_ipstr( unsigned long addr, char *host, int maxhostlen ); + static int parse_url( const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp ); +}; + +#endif //_UTIL_H_ diff --git a/Src/replicant/jnetlib/version.rc2 b/Src/replicant/jnetlib/version.rc2 new file mode 100644 index 00000000..a5a7ce27 --- /dev/null +++ b/Src/replicant/jnetlib/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION WINAMP_PRODUCTVER + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Support Library" + VALUE "FileVersion", STR_WINAMP_PRODUCTVER + VALUE "InternalName", "jnetlib.dll" + VALUE "LegalCopyright", "Copyright © 2012-2014 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "jnetlib.dll" + VALUE "ProductName", "Winamp Networking Service" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |