From fa4f4a8a50c4bab3a3e247fb7186a7f9a00dfc11 Mon Sep 17 00:00:00 2001 From: Othmar Gsenger Date: Mon, 3 Dec 2007 10:51:16 +0000 Subject: Added syncsocket --- Sockets/Ajp13Socket.cpp | 404 ++++++ Sockets/Ajp13Socket.h | 69 + Sockets/AjpBaseSocket.cpp | 258 ++++ Sockets/AjpBaseSocket.h | 83 ++ Sockets/Base64.cpp | 272 ++++ Sockets/Base64.h | 77 ++ Sockets/Debug.cpp | 79 ++ Sockets/Debug.h | 75 ++ Sockets/DevCpp/Sockets.dev | 1129 +++++++++++++++++ Sockets/DevCpp/Test.dev | 59 + Sockets/Event.cpp | 85 ++ Sockets/Event.h | 76 ++ Sockets/EventHandler.cpp | 223 ++++ Sockets/EventHandler.h | 85 ++ Sockets/EventTime.cpp | 105 ++ Sockets/EventTime.h | 77 ++ Sockets/Exception.cpp | 48 + Sockets/Exception.h | 58 + Sockets/File.cpp | 169 +++ Sockets/File.h | 84 ++ Sockets/HTTPSocket.cpp | 413 ++++++ Sockets/HTTPSocket.h | 152 +++ Sockets/HttpBaseSocket.cpp | 204 +++ Sockets/HttpBaseSocket.h | 76 ++ Sockets/HttpClientSocket.cpp | 277 ++++ Sockets/HttpClientSocket.h | 121 ++ Sockets/HttpDebugSocket.cpp | 125 ++ Sockets/HttpDebugSocket.h | 70 + Sockets/HttpGetSocket.cpp | 114 ++ Sockets/HttpGetSocket.h | 66 + Sockets/HttpPostSocket.cpp | 294 +++++ Sockets/HttpPostSocket.h | 94 ++ Sockets/HttpPutSocket.cpp | 125 ++ Sockets/HttpPutSocket.h | 83 ++ Sockets/HttpRequest.cpp | 358 ++++++ Sockets/HttpRequest.h | 125 ++ Sockets/HttpResponse.cpp | 201 +++ Sockets/HttpResponse.h | 82 ++ Sockets/HttpTransaction.cpp | 244 ++++ Sockets/HttpTransaction.h | 94 ++ Sockets/HttpdCookies.cpp | 270 ++++ Sockets/HttpdCookies.h | 89 ++ Sockets/HttpdForm.cpp | 616 +++++++++ Sockets/HttpdForm.h | 118 ++ Sockets/HttpdSocket.cpp | 528 ++++++++ Sockets/HttpdSocket.h | 106 ++ Sockets/IBase.h | 44 + Sockets/IEventHandler.h | 75 ++ Sockets/IEventOwner.cpp | 79 ++ Sockets/IEventOwner.h | 77 ++ Sockets/IFile.h | 71 ++ Sockets/IHttpServer.h | 56 + Sockets/ISocketHandler.h | 233 ++++ Sockets/Ipv4Address.cpp | 214 ++++ Sockets/Ipv4Address.h | 98 ++ Sockets/Ipv6Address.cpp | 270 ++++ Sockets/Ipv6Address.h | 107 ++ Sockets/ListenSocket.h | 420 ++++++ Sockets/Lock.cpp | 55 + Sockets/Lock.h | 59 + Sockets/Makefile | 154 +++ Sockets/Makefile.Defines.linux-x86-32 | 28 + Sockets/Makefile.Defines.macosx | 28 + Sockets/Makefile.Defines.solaris8 | 29 + Sockets/Makefile.Defines.solaris9-sparc-64 | 29 + Sockets/Makefile.Defines.win32-cygwin | 26 + Sockets/Makefile.solaris9-sparc-64 | 70 + Sockets/Makefile.version | 49 + Sockets/MemFile.cpp | 288 +++++ Sockets/MemFile.h | 100 ++ Sockets/Mutex.cpp | 81 ++ Sockets/Mutex.h | 68 + Sockets/OSX.zip | Bin 0 -> 16669 bytes Sockets/Parse.cpp | 321 +++++ Sockets/Parse.h | 101 ++ Sockets/Project.net/Sockets/Sockets.sln | 30 + Sockets/Project.net/Sockets/Sockets.vcproj | 452 +++++++ Sockets/Project.net/Test/Test.vcproj | 140 ++ Sockets/Project/Sockets.dsp | 526 ++++++++ Sockets/Project/Sockets.dsw | 68 + Sockets/Project/Test.dsp | 102 ++ Sockets/Project/echoserver.dsp | 100 ++ Sockets/Project/stressclient.dsp | 100 ++ Sockets/README.macosx | 3 + Sockets/RandomNumber.cpp | 116 ++ Sockets/RandomNumber.h | 202 +++ Sockets/ResolvServer.cpp | 97 ++ Sockets/ResolvServer.h | 73 ++ Sockets/ResolvSocket.cpp | 436 +++++++ Sockets/ResolvSocket.h | 106 ++ Sockets/SSLInitializer.cpp | 144 +++ Sockets/SSLInitializer.h | 76 ++ Sockets/SctpSocket.cpp | 496 ++++++++ Sockets/SctpSocket.h | 110 ++ Sockets/Semaphore.cpp | 76 ++ Sockets/Semaphore.h | 67 + Sockets/SmtpdSocket.cpp | 234 ++++ Sockets/SmtpdSocket.h | 143 +++ Sockets/Socket.cpp | 1896 ++++++++++++++++++++++++++++ Sockets/Socket.h | 738 +++++++++++ Sockets/SocketAddress.h | 95 ++ Sockets/SocketHandler.cpp | 1422 +++++++++++++++++++++ Sockets/SocketHandler.h | 266 ++++ Sockets/Sockets-config.cpp | 121 ++ Sockets/StdLog.h | 74 ++ Sockets/StdoutLog.cpp | 96 ++ Sockets/StdoutLog.h | 57 + Sockets/StreamSocket.cpp | 169 +++ Sockets/StreamSocket.h | 127 ++ Sockets/TcpSocket.cpp | 1729 +++++++++++++++++++++++++ Sockets/TcpSocket.h | 355 ++++++ Sockets/Thread.cpp | 166 +++ Sockets/Thread.h | 100 ++ Sockets/UdpSocket.cpp | 843 +++++++++++++ Sockets/UdpSocket.h | 215 ++++ Sockets/Utility.cpp | 953 ++++++++++++++ Sockets/Utility.h | 153 +++ Sockets/ajp13.h | 91 ++ Sockets/gpl.txt | 340 +++++ Sockets/mkdot.sh | 13 + Sockets/socket_include.cpp | 90 ++ Sockets/socket_include.h | 288 +++++ Sockets/sockets-config.h | 97 ++ Sockets/tests/Makefile | 73 ++ Sockets/tests/base64.cpp | 34 + Sockets/tests/copy.cpp | 264 ++++ Sockets/tests/crlf.cpp | 139 ++ Sockets/tests/echoserver.cpp | 237 ++++ Sockets/tests/events.cpp | 212 ++++ Sockets/tests/http.cpp | 65 + Sockets/tests/http_post.cpp | 107 ++ Sockets/tests/httpd.cpp | 109 ++ Sockets/tests/https.cpp | 113 ++ Sockets/tests/resolve.cpp | 156 +++ Sockets/tests/resume.cpp | 111 ++ Sockets/tests/retry.cpp | 67 + Sockets/tests/semtest.cpp | 96 ++ Sockets/tests/sloppy_http.cpp | 55 + Sockets/tests/sockets_test.cpp | 347 +++++ Sockets/tests/stressclient.cpp | 516 ++++++++ Sockets/tests/x.cpp | 298 +++++ 141 files changed, 29180 insertions(+) create mode 100644 Sockets/Ajp13Socket.cpp create mode 100644 Sockets/Ajp13Socket.h create mode 100644 Sockets/AjpBaseSocket.cpp create mode 100644 Sockets/AjpBaseSocket.h create mode 100644 Sockets/Base64.cpp create mode 100644 Sockets/Base64.h create mode 100644 Sockets/Debug.cpp create mode 100644 Sockets/Debug.h create mode 100644 Sockets/DevCpp/Sockets.dev create mode 100644 Sockets/DevCpp/Test.dev create mode 100644 Sockets/Event.cpp create mode 100644 Sockets/Event.h create mode 100644 Sockets/EventHandler.cpp create mode 100644 Sockets/EventHandler.h create mode 100644 Sockets/EventTime.cpp create mode 100644 Sockets/EventTime.h create mode 100644 Sockets/Exception.cpp create mode 100644 Sockets/Exception.h create mode 100644 Sockets/File.cpp create mode 100644 Sockets/File.h create mode 100644 Sockets/HTTPSocket.cpp create mode 100644 Sockets/HTTPSocket.h create mode 100644 Sockets/HttpBaseSocket.cpp create mode 100644 Sockets/HttpBaseSocket.h create mode 100644 Sockets/HttpClientSocket.cpp create mode 100644 Sockets/HttpClientSocket.h create mode 100644 Sockets/HttpDebugSocket.cpp create mode 100644 Sockets/HttpDebugSocket.h create mode 100644 Sockets/HttpGetSocket.cpp create mode 100644 Sockets/HttpGetSocket.h create mode 100644 Sockets/HttpPostSocket.cpp create mode 100644 Sockets/HttpPostSocket.h create mode 100644 Sockets/HttpPutSocket.cpp create mode 100644 Sockets/HttpPutSocket.h create mode 100644 Sockets/HttpRequest.cpp create mode 100644 Sockets/HttpRequest.h create mode 100644 Sockets/HttpResponse.cpp create mode 100644 Sockets/HttpResponse.h create mode 100644 Sockets/HttpTransaction.cpp create mode 100644 Sockets/HttpTransaction.h create mode 100644 Sockets/HttpdCookies.cpp create mode 100644 Sockets/HttpdCookies.h create mode 100644 Sockets/HttpdForm.cpp create mode 100644 Sockets/HttpdForm.h create mode 100644 Sockets/HttpdSocket.cpp create mode 100644 Sockets/HttpdSocket.h create mode 100644 Sockets/IBase.h create mode 100644 Sockets/IEventHandler.h create mode 100644 Sockets/IEventOwner.cpp create mode 100644 Sockets/IEventOwner.h create mode 100644 Sockets/IFile.h create mode 100644 Sockets/IHttpServer.h create mode 100644 Sockets/ISocketHandler.h create mode 100644 Sockets/Ipv4Address.cpp create mode 100644 Sockets/Ipv4Address.h create mode 100644 Sockets/Ipv6Address.cpp create mode 100644 Sockets/Ipv6Address.h create mode 100644 Sockets/ListenSocket.h create mode 100644 Sockets/Lock.cpp create mode 100644 Sockets/Lock.h create mode 100644 Sockets/Makefile create mode 100644 Sockets/Makefile.Defines.linux-x86-32 create mode 100644 Sockets/Makefile.Defines.macosx create mode 100644 Sockets/Makefile.Defines.solaris8 create mode 100644 Sockets/Makefile.Defines.solaris9-sparc-64 create mode 100644 Sockets/Makefile.Defines.win32-cygwin create mode 100644 Sockets/Makefile.solaris9-sparc-64 create mode 100644 Sockets/Makefile.version create mode 100644 Sockets/MemFile.cpp create mode 100644 Sockets/MemFile.h create mode 100644 Sockets/Mutex.cpp create mode 100644 Sockets/Mutex.h create mode 100755 Sockets/OSX.zip create mode 100644 Sockets/Parse.cpp create mode 100644 Sockets/Parse.h create mode 100644 Sockets/Project.net/Sockets/Sockets.sln create mode 100644 Sockets/Project.net/Sockets/Sockets.vcproj create mode 100644 Sockets/Project.net/Test/Test.vcproj create mode 100644 Sockets/Project/Sockets.dsp create mode 100644 Sockets/Project/Sockets.dsw create mode 100644 Sockets/Project/Test.dsp create mode 100644 Sockets/Project/echoserver.dsp create mode 100644 Sockets/Project/stressclient.dsp create mode 100644 Sockets/README.macosx create mode 100644 Sockets/RandomNumber.cpp create mode 100644 Sockets/RandomNumber.h create mode 100644 Sockets/ResolvServer.cpp create mode 100644 Sockets/ResolvServer.h create mode 100644 Sockets/ResolvSocket.cpp create mode 100644 Sockets/ResolvSocket.h create mode 100644 Sockets/SSLInitializer.cpp create mode 100644 Sockets/SSLInitializer.h create mode 100644 Sockets/SctpSocket.cpp create mode 100644 Sockets/SctpSocket.h create mode 100644 Sockets/Semaphore.cpp create mode 100644 Sockets/Semaphore.h create mode 100644 Sockets/SmtpdSocket.cpp create mode 100644 Sockets/SmtpdSocket.h create mode 100644 Sockets/Socket.cpp create mode 100644 Sockets/Socket.h create mode 100644 Sockets/SocketAddress.h create mode 100644 Sockets/SocketHandler.cpp create mode 100644 Sockets/SocketHandler.h create mode 100644 Sockets/Sockets-config.cpp create mode 100644 Sockets/StdLog.h create mode 100644 Sockets/StdoutLog.cpp create mode 100644 Sockets/StdoutLog.h create mode 100644 Sockets/StreamSocket.cpp create mode 100644 Sockets/StreamSocket.h create mode 100644 Sockets/TcpSocket.cpp create mode 100644 Sockets/TcpSocket.h create mode 100644 Sockets/Thread.cpp create mode 100644 Sockets/Thread.h create mode 100644 Sockets/UdpSocket.cpp create mode 100644 Sockets/UdpSocket.h create mode 100644 Sockets/Utility.cpp create mode 100644 Sockets/Utility.h create mode 100644 Sockets/ajp13.h create mode 100644 Sockets/gpl.txt create mode 100755 Sockets/mkdot.sh create mode 100644 Sockets/socket_include.cpp create mode 100644 Sockets/socket_include.h create mode 100644 Sockets/sockets-config.h create mode 100644 Sockets/tests/Makefile create mode 100644 Sockets/tests/base64.cpp create mode 100644 Sockets/tests/copy.cpp create mode 100644 Sockets/tests/crlf.cpp create mode 100644 Sockets/tests/echoserver.cpp create mode 100644 Sockets/tests/events.cpp create mode 100644 Sockets/tests/http.cpp create mode 100644 Sockets/tests/http_post.cpp create mode 100644 Sockets/tests/httpd.cpp create mode 100644 Sockets/tests/https.cpp create mode 100644 Sockets/tests/resolve.cpp create mode 100644 Sockets/tests/resume.cpp create mode 100644 Sockets/tests/retry.cpp create mode 100644 Sockets/tests/semtest.cpp create mode 100644 Sockets/tests/sloppy_http.cpp create mode 100644 Sockets/tests/sockets_test.cpp create mode 100644 Sockets/tests/stressclient.cpp create mode 100644 Sockets/tests/x.cpp (limited to 'Sockets') diff --git a/Sockets/Ajp13Socket.cpp b/Sockets/Ajp13Socket.cpp new file mode 100644 index 0000000..57755f0 --- /dev/null +++ b/Sockets/Ajp13Socket.cpp @@ -0,0 +1,404 @@ +/** + ** \file Ajp13Socket.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "Ajp13Socket.h" +#include "ajp13.h" +#include "HttpRequest.h" +#include "HttpResponse.h" +#include "IFile.h" +#include "Utility.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +// -------------------------------------------------------------------------------------- +Ajp13Socket::Ajp13Socket(ISocketHandler& h) : AjpBaseSocket(h) +, m_body_size_left(0) +, m_res_file(NULL) +{ +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::OnHeader( short id, short len ) +{ + if (id != 0x1234) + { + fprintf(stderr, "ABORT: bad packet id: %x\n", id); + SetCloseAndDelete(); + } + else + { + DEB(fprintf(stderr, "Packet size: %d bytes\n", len);) + } +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::ReceiveBody(const char *buf, size_t sz) +{ + if (sz - 2 > m_body_size_left) + { + fprintf(stderr, "More body data received than expected\n"); + SetCloseAndDelete(); + return; + } + + m_req.Write( buf + 2, sz - 2 ); + m_body_size_left -= sz - 2; + + // request more body data + if (m_body_size_left) + { + int ptr = 4; + char msg[100]; + msg[0] = 'A'; + msg[1] = 'B'; + +// reply codes +// 0x3 Send Body Chunk +// 0x4 Send Headers +// 0x5 End Response +// 0x6 Get Body Chunk <------ +// 0x9 CPong Reply + + put_byte(msg, ptr, 0x06); // GET_BODY_CHUNK; + put_integer(msg, ptr, 1000); // request 1000 bytes + + short len = htons( ptr - 4 ); + memcpy( msg + 2, &len, 2 ); + + SendBuf( msg, ptr ); + return; + } + + // Close + m_req.CloseBody(); + + // no more body data left to read - execute + Execute(); + +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::ReceiveForwardRequest( const char *buf, size_t sz ) +{ + // + int ptr = 0; + + get_byte(buf, ptr); // skip first byte: prefix_code + unsigned char method = get_byte(buf, ptr); + std::string protocol = get_string(buf, ptr); + std::string req_uri = get_string(buf, ptr); + std::string remote_addr = get_string(buf, ptr); + std::string remote_host = get_string(buf, ptr); + std::string server_name = get_string(buf, ptr); + short server_port = get_integer(buf, ptr); + bool is_ssl = get_boolean(buf, ptr); + + std::string method_str = Utility::l2string( method ); + std::map::const_iterator it = Init.Method.find( method ); + if (it != Init.Method.end()) + { + method_str = it -> second; + } + m_req.SetHttpMethod( method_str ); + m_req.SetHttpVersion( protocol ); + m_req.SetUri( req_uri ); + m_req.SetRemoteAddr( remote_addr ); + m_req.SetRemoteHost( remote_host ); + m_req.SetServerName( server_name ); + m_req.SetServerPort( server_port ); + m_req.SetIsSsl( is_ssl ); + + // Get Headers + short num_headers = get_integer(buf, ptr); + for (int i = 0; i < num_headers; i++) + { + std::string key; + switch ( (unsigned char)buf[ptr]) // 0xa0 + { + case 0xa0: + { + unsigned short x = (unsigned short)get_integer(buf, ptr); + std::map::const_iterator it; + if ( (it = Init.Header.find(x)) != Init.Header.end()) + { + key = it -> second; + } + else + { + fprintf(stderr, "Unknown header key value: %x\n", x); + SetCloseAndDelete(); + } + } + break; + + default: // string + key = get_string(buf, ptr); + } + if (Utility::ToLower(key) == "cookie" || Utility::ToLower(key) == "cookie2") + m_req.AddCookie(get_string(buf, ptr)); + else + m_req.SetHeader(key, get_string(buf, ptr)); + } // for + + // size left to read from web server + m_body_size_left = m_req.ContentLength(); + + // Get Attributes + while ( (unsigned char)buf[ptr] != 0xff) + { + std::string key; + unsigned char code = buf[ptr++]; + switch ( code) + { + case 10: // req_attribute, attribute name follow + key = get_string(buf, ptr); + break; + default: + { + std::map::const_iterator it = Init.Attribute.find( code ); + if (it != Init.Attribute.end()) + { + key = it -> second; + } + else + { + fprintf(stderr, "Unknown attribute key: 0x%02x\n", buf[ptr]); + SetCloseAndDelete(); + } + } + } + m_req.SetAttribute(key, get_string(buf, ptr)); + } // while + + // execute at once if no body data + if (!m_body_size_left) + { + Execute(); + } + else + { + // open temporary file for body data + m_req.InitBody( m_body_size_left ); + } +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::ReceiveShutdown( const char *buf, size_t sz ) +{ +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::ReceivePing( const char *buf, size_t sz ) +{ +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::ReceiveCPing( const char *buf, size_t sz ) +{ +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::Execute() +{ + // parse form data / query_string and cookie header if available + m_req.ParseBody(); + + // prepare page + OnExec( m_req ); + +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::Respond(const HttpResponse& res) +{ + char msg[8192]; + msg[0] = 'A'; + msg[1] = 'B'; + +// reply codes +// 0x3 Send Body Chunk +// 0x4 Send Headers +// 0x5 End Response +// 0x6 Get Body Chunk +// 0x9 CPong Reply + + // check content length + if (!res.ContentLength() && res.GetFile().size()) + { +// res.SetContentLength( res.GetFile().size() ); + } + + // Send Headers + { + int ptr = 4; + put_byte(msg, ptr, 0x04); // send headers + put_integer(msg, ptr, res.HttpStatusCode() ); + put_string(msg, ptr, res.HttpStatusMsg() ); + put_integer(msg, ptr, (short)res.Headers().size() ); + for (std::map::const_iterator it = res.Headers().begin(); it != res.Headers().end(); ++it) + { + std::map::const_iterator it2 = Init.ResponseHeader.find( it -> first ); + if (it2 != Init.ResponseHeader.end()) + { + put_integer(msg, ptr, it2 -> second); + } + else + { + put_string(msg, ptr, it -> first); + } + put_string(msg, ptr, it -> second); + } + std::list vec = res.CookieNames(); + { + for (std::list::iterator it = vec.begin(); it != vec.end(); it++) + { + std::map::const_iterator it2 = Init.ResponseHeader.find( "set-cookie" ); + if (it2 != Init.ResponseHeader.end()) + { + put_integer(msg, ptr, it2 -> second); + } + else + { + put_string(msg, ptr, "set-cookie"); + } + put_string(msg, ptr, res.Cookie(*it) ); + } + } + + short len = htons( ptr - 4 ); + memcpy( msg + 2, &len, 2 ); + + SendBuf( msg, ptr ); + } + m_res_file = &res.GetFile(); + // Send Body Chunk + OnTransferLimit(); +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::OnTransferLimit() +{ + char msg[8192]; + msg[0] = 'A'; + msg[1] = 'B'; + + // Send Body Chunk + size_t n = m_res_file -> fread(msg + 7, 1, 8100); + while (n > 0) + { + int ptr = 4; + put_byte(msg, ptr, 0x03); // send body chunk + put_integer(msg, ptr, (short)n); + ptr += (int)n; + + short len = htons( ptr - 4 ); + memcpy( msg + 2, &len, 2 ); + + SendBuf( msg, ptr ); + if (GetOutputLength() > 1) + { + SetTransferLimit( 1 ); + break; + } + + // + n = m_res_file -> fread(msg + 7, 1, 8100); + } + if (!GetOutputLength()) // all body data sent and no data in output buffer - send end response + { + // End Response + int ptr = 4; + put_byte(msg, ptr, 0x05); // end response + put_boolean(msg, ptr, false); // reuse + /* + don't reuse + - but with m_req.Reset() and res.Reset() it should be possible + - also reset any AjpBaseSocket/Ajp13Socket specific states + */ + + short len = htons( ptr - 4 ); + memcpy( msg + 2, &len, 2 ); + + SendBuf( msg, ptr ); + } +} + + +// -------------------------------------------------------------------------------------- +void Ajp13Socket::OnPacket( const char *buf, size_t sz ) +{ + DEB(fprintf(stderr, "OnPacket: %d bytes, code 0x%02x %02x %02x %02x\n", sz, *buf, buf[1], buf[2], buf[3]);) + + // check body size left to read, if non-zero packet is body data + if (m_body_size_left) // must be a body packet + { + ReceiveBody(buf, sz); + return; + } + switch (*buf) + { + case 0x2: // Forward Request + ReceiveForwardRequest(buf, sz); + break; + case 0x7: // Shutdown + ReceiveShutdown(buf, sz); + break; + case 0x8: // Ping + ReceivePing(buf, sz); + break; + case 0xa: // CPing + ReceiveCPing(buf, sz); + break; + default: + fprintf(stderr, "Unknown packet type: 0x%02x\n", *buf); + SetCloseAndDelete(); + } + +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/Sockets/Ajp13Socket.h b/Sockets/Ajp13Socket.h new file mode 100644 index 0000000..3da5be7 --- /dev/null +++ b/Sockets/Ajp13Socket.h @@ -0,0 +1,69 @@ +/** + ** \file Ajp13Socket.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ajp13Socket_H +#define _SOCKETS_Ajp13Socket_H + +#include "AjpBaseSocket.h" +#include "HttpRequest.h" +#include "IHttpServer.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpResponse; + +class Ajp13Socket : public AjpBaseSocket, public IHttpServer +{ +public: + Ajp13Socket(ISocketHandler& h); + + void OnHeader( short id, short len ); + void OnPacket( const char *buf, size_t sz ); + + // implements IHttpServer::Respond + void Respond(const HttpResponse& res); + + void OnTransferLimit(); + +private: + void ReceiveBody( const char *buf, size_t sz ); + void ReceiveForwardRequest( const char *buf, size_t sz ); + void ReceiveShutdown( const char *buf, size_t sz ); + void ReceivePing( const char *buf, size_t sz ); + void ReceiveCPing( const char *buf, size_t sz ); + void Execute(); + // + size_t m_body_size_left; + HttpRequest m_req; + const IFile *m_res_file; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_Ajp13Socket_H + diff --git a/Sockets/AjpBaseSocket.cpp b/Sockets/AjpBaseSocket.cpp new file mode 100644 index 0000000..3b54dd0 --- /dev/null +++ b/Sockets/AjpBaseSocket.cpp @@ -0,0 +1,258 @@ +/** + ** \file AjpBaseSocket.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "AjpBaseSocket.h" +#include "ajp13.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +// --------------------------------------------------------------------------- +AjpBaseSocket::Initializer AjpBaseSocket::Init; + + +// --------------------------------------------------------------------------- +AjpBaseSocket::Initializer::Initializer() +{ + + Header[HTTP_REQUEST_ACCEPT] = "accept"; + Header[HTTP_REQUEST_ACCEPT_CHARSET] = "accept-charset"; + Header[HTTP_REQUEST_ACCEPT_ENCODING] = "accept-encoding"; + Header[HTTP_REQUEST_ACCEPT_LANGUAGE] = "accept-language"; + Header[HTTP_REQUEST_AUTHORIZATION] = "authorization"; + Header[HTTP_REQUEST_CONNECTION] = "connection"; + Header[HTTP_REQUEST_CONTENT_TYPE] = "content-type"; + Header[HTTP_REQUEST_CONTENT_LENGTH] = "content-length"; + Header[HTTP_REQUEST_COOKIE] = "cookie"; + Header[HTTP_REQUEST_COOKIE2] = "cookie2"; + Header[HTTP_REQUEST_HOST] = "host"; + Header[HTTP_REQUEST_PRAGMA] = "pragma"; + Header[HTTP_REQUEST_REFERER] = "referer"; + Header[HTTP_REQUEST_USER_AGENT] = "user-agent"; + + Method[HTTP_METHOD_OPTIONS] = "OPTIONS"; + Method[HTTP_METHOD_GET] = "GET"; + Method[HTTP_METHOD_HEAD] = "HEAD"; + Method[HTTP_METHOD_POST] = "POST"; + Method[HTTP_METHOD_PUT] = "PUT"; + Method[HTTP_METHOD_DELETE] = "DELETE"; + Method[HTTP_METHOD_TRACE] = "TRACE"; + Method[HTTP_METHOD_PROPFIND] = "PROPFIND"; + Method[HTTP_METHOD_PROPPATCH] = "PROPPATCH"; + Method[HTTP_METHOD_MKCOL] = "MKCOL"; + Method[HTTP_METHOD_COPY] = "COPY"; + Method[HTTP_METHOD_MOVE] = "MOVE"; + Method[HTTP_METHOD_LOCK] = "LOCK"; + Method[HTTP_METHOD_UNLOCK] = "UNLOCK"; + Method[HTTP_METHOD_ACL] = "ACL"; + Method[HTTP_METHOD_REPORT] = "REPORT"; + Method[HTTP_METHOD_VERSION_CONTROL] = "VERSION_CONTROL"; // with a dash "VERSION-CONTROL" + Method[HTTP_METHOD_CHECKIN] = "CHECKIN"; + Method[HTTP_METHOD_CHECKOUT] = "CHECKOUT"; + Method[HTTP_METHOD_UNCHECKOUT] = "UNCHECKOUT"; + Method[HTTP_METHOD_SEARCH] = "SEARCH"; + Method[HTTP_METHOD_MKWORKSPACE] = "MKWORKSPACE"; + Method[HTTP_METHOD_UPDATE] = "UPDATE"; + Method[HTTP_METHOD_LABEL] = "LABEL"; + Method[HTTP_METHOD_MERGE] = "MERGE"; + Method[HTTP_METHOD_BASELINE_CONTROL] = "BASELINE_CONTROL"; + Method[HTTP_METHOD_MKACTIVITY] = "MKACTIVITY"; + + Attribute[ATTR_CONTEXT] = "context"; + Attribute[ATTR_SERVLET_PATH] = "servlet_path"; + Attribute[ATTR_REMOTE_USER] = "remote_user"; + Attribute[ATTR_AUTH_TYPE] = "auth_type"; + Attribute[ATTR_QUERY_STRING] = "query_string"; + Attribute[ATTR_ROUTE] = "route"; + Attribute[ATTR_SSL_CERT] = "ssl_cert"; + Attribute[ATTR_SSL_CIPHER] = "ssl_cipher"; + Attribute[ATTR_SSL_SESSION] = "ssl_session"; + Attribute[ATTR_SSL_KEY_SIZE] = "ssl_key_size"; + Attribute[ATTR_SECRET] = "secret"; + Attribute[ATTR_STORED_METHOD] = "stored_method"; + + ResponseHeader["content-type"] = 0xa001; + ResponseHeader["content-language"] = 0xa002; + ResponseHeader["content-length"] = 0xa003; + ResponseHeader["date"] = 0xa004; + ResponseHeader["last-modified"] = 0xa005; + ResponseHeader["location"] = 0xa006; + ResponseHeader["set-cookie"] = 0xa007; + ResponseHeader["set-cookie2"] = 0xa008; + ResponseHeader["servlet-engine"] = 0xa009; + ResponseHeader["status"] = 0xa00a; + ResponseHeader["www-authenticate"] = 0xa00b; +} + + +// --------------------------------------------------------------------------- +AjpBaseSocket::AjpBaseSocket(ISocketHandler& h) : TcpSocket(h) +, m_state(0) +, m_length(4) +, m_ptr(0) +{ +} + + +// --------------------------------------------------------------------------- +void AjpBaseSocket::OnRawData(const char *buf, size_t sz) +{ +DEB(fprintf(stderr, "OnRawData: %d bytes\n", sz);) + size_t ptr = 0; + while (true) + { + size_t left = sz - ptr; +DEB(fprintf(stderr, " left: %d bytes\n", left); +fprintf(stderr, " state: %d\n", m_state);) + switch (m_state) + { + case 0: + { + size_t missing = m_length - m_ptr; + short len = (short)(missing < left ? missing : left); + memcpy(m_message + m_ptr, buf + ptr, len); + m_ptr += len; + ptr += len; + if (m_ptr < m_length) + { + return; // read more + } + int p = 0; + short id = get_integer(m_message, p); + short length = get_integer(m_message, p); + OnHeader(id, length); + m_state = 1; + m_length = length; + m_ptr = 0; // bytes in m_message + } + break; + case 1: + { + size_t missing = m_length - m_ptr; + short len = (short)(missing < left ? missing : left); + memcpy(m_message + m_ptr, buf + ptr, len); + m_ptr += len; + ptr += len; + if (m_ptr < m_length) + { + return; // read more + } + OnPacket(m_message, m_ptr); + m_state = 0; + m_length = 4; + m_ptr = 0; + } + break; + } + } +} + + +// --------------------------------------------------------------------------- +unsigned char AjpBaseSocket::get_byte(const char *buf, int& ptr) +{ + return (unsigned char)buf[ptr++]; +} + + +// --------------------------------------------------------------------------- +bool AjpBaseSocket::get_boolean(const char *buf, int& ptr) +{ + return ( (unsigned char)buf[ptr++] & 1) == 1 ? true : false; +} + + +// --------------------------------------------------------------------------- +short AjpBaseSocket::get_integer(const char *buf, int& ptr) +{ + short n; + memcpy(&n, buf + ptr, 2); + ptr += 2; + return ntohs(n); +} + + +// --------------------------------------------------------------------------- +std::string AjpBaseSocket::get_string(const char *buf, int& ptr) +{ + short len = get_integer(buf, ptr); + if (len != -1) + { + std::string tmp = buf + ptr; + ptr += len; + ptr++; // skip trailing 0x0 + tmp.resize(len); + return tmp; + } + return ""; +} + + +// --------------------------------------------------------------------------- +void AjpBaseSocket::put_byte(char *buf, int& ptr, unsigned char zz) +{ + buf[ptr++] = zz; +} + + +// --------------------------------------------------------------------------- +void AjpBaseSocket::put_boolean(char *buf, int& ptr, bool zz) +{ + buf[ptr++] = zz ? 1 : 0; +} + + +// --------------------------------------------------------------------------- +void AjpBaseSocket::put_integer(char *buf, int& ptr, short zz) +{ + short tmp = htons(zz); + memcpy(buf + ptr, &tmp, 2); + ptr += 2; +} + + +// --------------------------------------------------------------------------- +void AjpBaseSocket::put_string(char *buf, int& ptr, const std::string& zz) +{ + put_integer(buf, ptr, (short)zz.size() ); + memcpy(buf + ptr, zz.c_str(), zz.size()); + ptr += (int)zz.size(); + put_byte(buf, ptr, 0); +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/Sockets/AjpBaseSocket.h b/Sockets/AjpBaseSocket.h new file mode 100644 index 0000000..222df70 --- /dev/null +++ b/Sockets/AjpBaseSocket.h @@ -0,0 +1,83 @@ +/** + ** \file AjpBaseSocket.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_AjpBaseSocket_H +#define _SOCKETS_AjpBaseSocket_H + +#include "TcpSocket.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class AjpBaseSocket : public TcpSocket +{ + class Initializer + { + public: + Initializer(); + virtual ~Initializer() {} + + std::map Method; + std::map Header; + std::map Attribute; + + std::map ResponseHeader; + + }; + +public: + AjpBaseSocket(ISocketHandler& h); + + void OnRawData(const char *buf, size_t sz); + + virtual void OnHeader( short id, short len ) = 0; + virtual void OnPacket( const char *buf, size_t sz ) = 0; + +protected: + unsigned char get_byte(const char *buf, int& ptr); + bool get_boolean(const char *buf, int& ptr); + short get_integer(const char *buf, int& ptr); + std::string get_string(const char *buf, int& ptr); + + void put_byte(char *buf, int& ptr, unsigned char zz); + void put_boolean(char *buf, int& ptr, bool zz); + void put_integer(char *buf, int& ptr, short zz); + void put_string(char *buf, int& ptr, const std::string& zz); + + static Initializer Init; + +private: + int m_state; + int m_length; + int m_ptr; + char m_message[8192]; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_AjpBaseSocket_H + diff --git a/Sockets/Base64.cpp b/Sockets/Base64.cpp new file mode 100644 index 0000000..4619756 --- /dev/null +++ b/Sockets/Base64.cpp @@ -0,0 +1,272 @@ +/** \file Base64.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Base64.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +const char *Base64::bstr = + "ABCDEFGHIJKLMNOPQ" + "RSTUVWXYZabcdefgh" + "ijklmnopqrstuvwxy" + "z0123456789+/"; + +const char Base64::rstr[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0}; + + +Base64::Base64() +{ +} + + +void Base64::encode(FILE *fil, std::string& output, bool add_crlf) +{ + size_t remain; + size_t i = 0; + size_t o = 0; + char input[4]; + + output = ""; + remain = fread(input,1,3,fil); + while (remain > 0) + { + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + // + remain = fread(input,1,3,fil); + } +} + + +void Base64::encode(const std::string& str_in, std::string& str_out, bool add_crlf) +{ + encode(str_in.c_str(), str_in.size(), str_out, add_crlf); +} + + +void Base64::encode(const char* input,size_t l,std::string& output, bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + + +void Base64::encode(const unsigned char* input,size_t l,std::string& output,bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + + +void Base64::decode(const std::string& input,std::string& output) +{ + size_t i = 0; + size_t l = input.size(); + + output = ""; + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + char b1 = (char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + output += b1; + if (input[i + 2] != '=') + { + char b2 = (char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + output += b2; + } + if (input[i + 3] != '=') + { + char b3 = (char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + output += b3; + } + i += 4; + } + } +} + + +void Base64::decode(const std::string& input, unsigned char *output, size_t& sz) +{ + size_t i = 0; + size_t l = input.size(); + size_t j = 0; + + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + unsigned char b1 = (unsigned char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + if (output) + { + output[j] = b1; + } + j++; + if (input[i + 2] != '=') + { + unsigned char b2 = (unsigned char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + if (output) + { + output[j] = b2; + } + j++; + } + if (input[i + 3] != '=') + { + unsigned char b3 = (unsigned char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + if (output) + { + output[j] = b3; + } + j++; + } + i += 4; + } + } + sz = j; +} + + +size_t Base64::decode_length(const std::string& str64) +{ + if (!str64.size() || str64.size() % 4) + return 0; + size_t l = 3 * (str64.size() / 4 - 1) + 1; + if (str64[str64.size() - 2] != '=') + l++; + if (str64[str64.size() - 1] != '=') + l++; + return l; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/Base64.h b/Sockets/Base64.h new file mode 100644 index 0000000..a632bbe --- /dev/null +++ b/Sockets/Base64.h @@ -0,0 +1,77 @@ +/** \file Base64.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Base64_H +#define _SOCKETS_Base64_H + +#include "sockets-config.h" +#ifdef _MSC_VER +#pragma warning(disable:4514) +#endif + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup util Utilities */ + +/** Base64 encode/decode. + \ingroup util */ +class Base64 +{ +public: + Base64(); + + void encode(FILE *, std::string& , bool add_crlf = true); + void encode(const std::string&, std::string& , bool add_crlf = true); + void encode(const char *, size_t, std::string& , bool add_crlf = true); + void encode(const unsigned char *, size_t, std::string& , bool add_crlf = true); + + void decode(const std::string&, std::string& ); + void decode(const std::string&, unsigned char *, size_t&); + + size_t decode_length(const std::string& ); + +private: + Base64(const Base64& ) {} + Base64& operator=(const Base64& ) { return *this; } +static const char *bstr; +static const char rstr[128]; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Base64_H + diff --git a/Sockets/Debug.cpp b/Sockets/Debug.cpp new file mode 100644 index 0000000..ce4bad6 --- /dev/null +++ b/Sockets/Debug.cpp @@ -0,0 +1,79 @@ +#include "Debug.h" +#include + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +std::map Debug::m_level; +const char *Debug::colors[] = { + "\x1B[0;0m", // &n */ + "\x1B[0;0m\x1B[31m", // &r */ + "\x1B[0;0m\x1B[32m", // &g */ + "\x1B[0;0m\x1B[33m", // &y */ + "\x1B[0;0m\x1B[34m", // &b */ + "\x1B[0;0m\x1B[35m", // &m */ + "\x1B[0;0m\x1B[36m", // &c */ + "\x1B[0;0m\x1B[37m", // &w */ + "\x1B[0;0m\x1B[30m", // &l */ + "\x1B[1;31m", // &R */ + "\x1B[1;32m", // &G */ + "\x1B[1;33m", // &Y */ + "\x1B[1;34m", // &B */ + "\x1B[1;35m", // &M */ + "\x1B[1;36m", // &C */ + "\x1B[1;37m", // &W */ + "\x1B[1;30m" }; // &L */ + + + +void Debug::Print(const char *format, ...) +{ + char slask[5000]; // temporary for vsprintf / vsnprintf + va_list ap; + + va_start(ap, format); +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + + fprintf(stderr, "%s", colors[Utility::ThreadID() % 16 + 1]); + for (int i = 0; i < m_level[Utility::ThreadID()]; i++) + fprintf(stderr, " "); + if (slask[strlen(slask) - 1] == '\n') + slask[strlen(slask) - 1] = 0; + fprintf(stderr, "%s%s\n", slask, colors[0]); +} + + +Debug& Debug::operator<<(const std::string& str) +{ + m_line += str; + return *this; +} + + +Debug& Debug::operator<<(long l) +{ + m_line += Utility::l2string(l); + return *this; +} + + +Debug& Debug::operator<<(endl) +{ + Print("%s", m_line.c_str()); + m_line = ""; + return *this; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/Debug.h b/Sockets/Debug.h new file mode 100644 index 0000000..9bc06b9 --- /dev/null +++ b/Sockets/Debug.h @@ -0,0 +1,75 @@ +#ifndef _SOCKETS_Debug_H +#define _SOCKETS_Debug_H + +#include "sockets-config.h" +#include +#include "Utility.h" +#include + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class Debug +{ +static const char *colors[]; +public: + class endl { + public: + endl() {} + virtual ~endl() {} + }; + +public: + Debug() {} + Debug(const std::string& x) : m_id(0), m_text(x) { + fprintf(stderr, "%s", colors[Utility::ThreadID() % 16 + 1]); + for (int i = 0; i < m_level[Utility::ThreadID()]; i++) + fprintf(stderr, " "); + fprintf(stderr, "%s%s\n", x.c_str(), colors[0]); + m_level[Utility::ThreadID()]++; + } + Debug(int id, const std::string& x) : m_id(id), m_text(x) { + fprintf(stderr, "%s", colors[Utility::ThreadID() % 16 + 1]); + for (int i = 0; i < m_level[Utility::ThreadID()]; i++) + fprintf(stderr, " "); + fprintf(stderr, "%d> %s%s\n", m_id, x.c_str(), colors[0]); + m_level[Utility::ThreadID()]++; + } + ~Debug() { + if (!m_text.empty()) + { + if (m_level[Utility::ThreadID()]) + m_level[Utility::ThreadID()]--; + fprintf(stderr, "%s", colors[Utility::ThreadID() % 16 + 1]); + for (int i = 0; i < m_level[Utility::ThreadID()]; i++) + fprintf(stderr, " "); + if (m_id) + fprintf(stderr, "%d> /%s%s\n", m_id, m_text.c_str(), colors[0]); + else + fprintf(stderr, "/%s%s\n", m_text.c_str(), colors[0]); + fflush(stderr); + } + } +static void Print(const char *format, ...); + + Debug& operator<<(const std::string& ); + Debug& operator<<(long); + Debug& operator<<(endl); + +private: + int m_id; + std::string m_text; +static std::map m_level; + std::string m_line; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_Debug_H + diff --git a/Sockets/DevCpp/Sockets.dev b/Sockets/DevCpp/Sockets.dev new file mode 100644 index 0000000..fe38522 --- /dev/null +++ b/Sockets/DevCpp/Sockets.dev @@ -0,0 +1,1129 @@ +[Project] +FileName=Sockets.dev +Name=Sockets +UnitCount=96 +Type=2 +Ver=1 +ObjFiles= +Includes=C:\OpenSSL\include +Libs=C:\OpenSSL\lib\MinGW +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler=-D_VERSION='"2.1.9"'_@@_-D__CYGWIN___@@__@@_ +Linker=../../../../OpenSSL/lib/MinGW/ssleay32.a_@@_../../../../OpenSSL/lib/MinGW/libeay32.a_@@_ +IsCpp=1 +Icon= +ExeOutput=..\..\lib +ObjectOutput= +OverrideOutput=0 +OverrideOutputName=Sockets.a +HostApplication= +Folders="Asynchronous DNS","Basic Sockets","File handling","HTTP Sockets",Internal,"Log help classes",SMTP,Threading,"Timer Events",Utilities,"Webserver framework" +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000000000000000000 + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1050 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + +[Unit1] +FileName=..\socket_include.cpp +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=..\socket_include.h +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit3] +FileName=..\sockets-config.h +CompileCpp=1 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=..\ResolvServer.cpp +CompileCpp=1 +Folder=Asynchronous DNS +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=..\ResolvServer.h +CompileCpp=1 +Folder=Asynchronous DNS +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=..\ResolvSocket.cpp +CompileCpp=1 +Folder=Asynchronous DNS +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=..\ResolvSocket.h +CompileCpp=1 +Folder=Asynchronous DNS +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=..\ListenSocket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit9] +FileName=..\Socket.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=..\Socket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=..\SocketHandler.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit12] +FileName=..\SocketHandler.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit13] +FileName=..\TcpSocket.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit14] +FileName=..\TcpSocket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit15] +FileName=..\UdpSocket.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit16] +FileName=..\UdpSocket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit17] +FileName=..\SctpSocket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit18] +FileName=..\SctpSocket.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit19] +FileName=..\ISocketHandler.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit20] +FileName=..\StreamSocket.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit21] +FileName=..\Ipv4Address.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit22] +FileName=..\Ipv6Address.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit23] +FileName=..\Ipv4Address.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit25] +FileName=..\Ipv6Address.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit26] +FileName=..\SocketAddress.h +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit27] +FileName=..\MemFile.h +CompileCpp=1 +Folder=File handling +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit28] +FileName=..\File.h +CompileCpp=1 +Folder=File handling +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit29] +FileName=..\IFile.h +CompileCpp=1 +Folder=File handling +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit30] +FileName=..\MemFile.cpp +CompileCpp=1 +Folder=File handling +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit31] +FileName=..\File.cpp +CompileCpp=1 +Folder=File handling +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit32] +FileName=..\HttpDebugSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit33] +FileName=..\HttpDebugSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit34] +FileName=..\HttpGetSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit35] +FileName=..\HttpGetSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit36] +FileName=..\HttpPostSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit37] +FileName=..\HttpPostSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit38] +FileName=..\HttpPutSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit39] +FileName=..\HttpPutSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit40] +FileName=..\HTTPSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit41] +FileName=..\HTTPSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit42] +FileName=..\HttpClientSocket.h +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] +FileName=..\HttpClientSocket.cpp +CompileCpp=1 +Folder=HTTP Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit45] +FileName=..\SSLInitializer.h +CompileCpp=1 +Folder=Internal +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit46] +FileName=..\StdLog.h +CompileCpp=1 +Folder=Log help classes +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit47] +FileName=..\StdoutLog.cpp +CompileCpp=1 +Folder=Log help classes +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit48] +FileName=..\StdoutLog.h +CompileCpp=1 +Folder=Log help classes +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit49] +FileName=..\SmtpdSocket.cpp +CompileCpp=1 +Folder=SMTP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit50] +FileName=..\SmtpdSocket.h +CompileCpp=1 +Folder=SMTP +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit51] +FileName=..\sockets-config.h +CompileCpp=1 +Folder=Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit54] +FileName=..\Thread.cpp +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] +FileName=..\Thread.h +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit56] +FileName=..\Lock.h +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit57] +FileName=..\Lock.cpp +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit58] +FileName=..\Mutex.cpp +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit59] +FileName=..\Mutex.h +CompileCpp=1 +Folder=Threading +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit60] +FileName=..\EventTime.h +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit61] +FileName=..\Event.h +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit62] +FileName=..\EventHandler.cpp +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit63] +FileName=..\IEventOwner.cpp +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit64] +FileName=..\IEventOwner.h +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit65] +FileName=..\EventTime.cpp +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit66] +FileName=..\EventHandler.h +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit67] +FileName=..\IEventHandler.h +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit68] +FileName=..\Event.cpp +CompileCpp=1 +Folder=Timer Events +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit69] +FileName=..\Base64.cpp +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit70] +FileName=..\Base64.h +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit71] +FileName=..\Parse.cpp +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit72] +FileName=..\Parse.h +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit73] +FileName=..\Utility.cpp +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit74] +FileName=..\Utility.h +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit75] +FileName=..\RandomNumber.h +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit76] +FileName=..\RandomNumber.cpp +CompileCpp=1 +Folder=Utilities +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit77] +FileName=..\HttpdSocket.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit78] +FileName=..\HttpdCookies.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit79] +FileName=..\HttpdForm.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit80] +FileName=..\HttpdForm.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit81] +FileName=..\HttpdSocket.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit82] +FileName=..\HttpdCookies.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit83] +FileName=..\Ajp13Socket.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit84] +FileName=..\Ajp13Socket.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit85] +FileName=..\AjpBaseSocket.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit86] +FileName=..\AjpBaseSocket.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit87] +FileName=..\HttpBaseSocket.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit88] +FileName=..\HttpBaseSocket.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit89] +FileName=..\HttpTransaction.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit90] +FileName=..\HttpTransaction.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit91] +FileName=..\HttpRequest.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit92] +FileName=..\HttpRequest.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit93] +FileName=..\HttpResponse.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit94] +FileName=..\HttpResponse.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit95] +FileName=..\ajp13.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit96] +FileName=..\IHttpServer.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit97] +FileName=..\HttpTransaction.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit98] +FileName=..\HttpTransaction.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit99] +FileName=..\HttpRequest.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit100] +FileName=..\HttpRequest.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit101] +FileName=..\HttpResponse.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit102] +FileName=..\HttpResponse.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit103] +FileName=..\ajp13.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit104] +FileName=..\IHttpServer.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit105] +FileName=..\HttpResponse.cpp +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit106] +FileName=..\HttpResponse.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit107] +FileName=..\ajp13.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit108] +FileName=..\IHttpServer.h +CompileCpp=1 +Folder=Webserver framework +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit24] +FileName=..\StreamSocket.cpp +CompileCpp=1 +Folder=Basic Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit44] +FileName=..\SSLInitializer.cpp +CompileCpp=1 +Folder=Internal +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] +FileName=..\Exception.h +CompileCpp=1 +Folder=Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit53] +FileName=..\Exception.cpp +CompileCpp=1 +Folder=Sockets +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/Sockets/DevCpp/Test.dev b/Sockets/DevCpp/Test.dev new file mode 100644 index 0000000..913d938 --- /dev/null +++ b/Sockets/DevCpp/Test.dev @@ -0,0 +1,59 @@ +[Project] +FileName=Test.dev +Name=Test +UnitCount=1 +Type=1 +Ver=1 +ObjFiles= +Includes=..;C:\OpenSSL\include +Libs=C:\OpenSSL\lib\MinGW +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler=-DHAVE_OPENSSL_@@_-D__CYGWIN___@@_ +Linker=../../lib/Sockets.a_@@_../../../../OpenSSL/lib/MinGW/ssleay32.a_@@_../../../../OpenSSL/lib/MinGW/libeay32.a_@@_../../../../Dev-Cpp/lib/libwsock32.a_@@_../../../../Dev-Cpp/lib/libws2_32.a_@@_ +IsCpp=1 +Icon= +ExeOutput=..\..\bin +ObjectOutput=bin +OverrideOutput=0 +OverrideOutputName=Test.exe +HostApplication= +Folders= +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000000000000000000 + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + +[Unit1] +FileName=..\tests\sockets_test.cpp +CompileCpp=1 +Folder=Test +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/Sockets/Event.cpp b/Sockets/Event.cpp new file mode 100644 index 0000000..d8d1bba --- /dev/null +++ b/Sockets/Event.cpp @@ -0,0 +1,85 @@ +/** \file Event.cpp + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Event.h" +#ifdef _WIN32 +#else +#include +#endif + +//#include "IEventOwner.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// statics +long Event::m_unique_id = 0; + + +Event::Event(IEventOwner *from,long sec,long usec) : m_from(from), m_time(sec, usec), m_id(++m_unique_id) +{ +} + + +Event::~Event() +{ +} + + +bool Event::operator<(Event& e) +{ + return m_time < e.m_time; +} + + +long Event::GetID() +{ + return m_id; +} + + +const EventTime& Event::GetTime() const +{ + return m_time; +} + + +IEventOwner *Event::GetFrom() +{ + return m_from; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/Event.h b/Sockets/Event.h new file mode 100644 index 0000000..cf6521b --- /dev/null +++ b/Sockets/Event.h @@ -0,0 +1,76 @@ +/** \file Event.h + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Event_H +#define _SOCKETS_Event_H + +#include "sockets-config.h" +#ifdef _WIN32 +#else +#include +#endif +#include "EventTime.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IEventOwner; + +/** Store information about a timer event. + \ingroup timer */ +class Event +{ +public: + Event(IEventOwner *,long sec,long usec); + ~Event(); + + bool operator<(Event&); + long GetID(); + const EventTime& GetTime() const; + IEventOwner *GetFrom(); + +private: + Event(const Event& ) {} // copy constructor + Event& operator=(const Event& ) { return *this; } // assignment operator + IEventOwner *m_from; + EventTime m_time; + static long m_unique_id; + long m_id; +}; + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Event_H + diff --git a/Sockets/EventHandler.cpp b/Sockets/EventHandler.cpp new file mode 100644 index 0000000..4d90342 --- /dev/null +++ b/Sockets/EventHandler.cpp @@ -0,0 +1,223 @@ +/** \file EventHandler.cpp + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "EventHandler.h" +#include "IEventOwner.h" +#include "Event.h" +#include "Socket.h" +#include "TcpSocket.h" +#include "ListenSocket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +EventHandler::EventHandler(StdLog *p) : SocketHandler(p), m_quit(false), m_socket(NULL) +{ +} + + +EventHandler::EventHandler(Mutex& m,StdLog *p) : SocketHandler(m, p), m_quit(false), m_socket(NULL) +{ +} + + +EventHandler::~EventHandler() +{ + while (m_events.size()) + { + std::list::iterator it = m_events.begin(); + Event *e = *it; + e -> GetFrom() -> SetHandlerInvalid(); + delete e; + m_events.erase(it); + } +} + + +bool EventHandler::GetTimeUntilNextEvent(struct timeval *tv) +{ + if (!m_events.size()) + return false; + std::list::iterator it = m_events.begin(); + if (it != m_events.end()) + { + EventTime now; + mytime_t diff = (*it) -> GetTime() - now; + if (diff < 1) + { + diff = 1; + } + tv -> tv_sec = static_cast(diff / 1000000); + tv -> tv_usec = static_cast(diff % 1000000); + return true; + } + return false; +} + + +void EventHandler::CheckEvents() +{ + EventTime now; + std::list::iterator it = m_events.begin(); + while (it != m_events.end() && (*it) -> GetTime() < now) + { + Event *e = *it; + Socket *s = dynamic_cast(e -> GetFrom()); + /* + s == NULL This is another object implementing 'IEventOwner' and not a socket. + s != NULL This is a Socket implementing IEventOwner, and we can check that the + object instance still is valid using SocketHandler::Valid. + */ + if (!s || (s && Valid(s))) + { + e -> GetFrom() -> OnEvent(e -> GetID()); + } + for (it = m_events.begin(); it != m_events.end(); ++it) + if (*it == e) + break; + delete e; + if (it != m_events.end()) + m_events.erase(it); + it = m_events.begin(); + } +} + + +long EventHandler::AddEvent(IEventOwner *from,long sec,long usec) +{ + Event *e = new Event(from, sec, usec); + std::list::iterator it = m_events.begin(); + while (it != m_events.end() && *(*it) < *e) + { + it++; + } + m_events.insert(it, e); + if (m_socket) + { + m_socket -> Send("\n"); + } + return e -> GetID(); +} + + +void EventHandler::ClearEvents(IEventOwner *from) +{ + bool repeat; + do + { + repeat = false; + for (std::list::iterator it = m_events.begin(); it != m_events.end(); it++) + { + Event *e = *it; + if (e -> GetFrom() == from) + { + delete e; + m_events.erase(it); + repeat = true; + break; + } + } + } while (repeat); +} + + +void EventHandler::EventLoop() +{ + while (!m_quit) + { + struct timeval tv; + if (GetTimeUntilNextEvent(&tv)) + { + Select(&tv); + CheckEvents(); + } + else + { + Select(); + } + } +} + + +void EventHandler::SetQuit(bool x) +{ + m_quit = x; +} + + +void EventHandler::RemoveEvent(IEventOwner *from, long eid) +{ + for (std::list::iterator it = m_events.begin(); it != m_events.end(); it++) + { + Event *e = *it; + if (from == e -> GetFrom() && eid == e -> GetID()) + { + delete e; + m_events.erase(it); + break; + } + } +} + + +void EventHandler::Add(Socket *p) +{ + if (!m_socket) + { + ListenSocket *l = new ListenSocket(*this); + l -> SetDeleteByHandler(); + l -> Bind("127.0.0.1", 0); + m_port = l -> GetPort(); + SocketHandler::Add(l); + m_socket = new TcpSocket( *this ); + m_socket -> SetDeleteByHandler(); + m_socket -> SetConnectTimeout(5); + m_socket -> SetConnectionRetry(-1); +#ifdef ENABLE_RECONNECT + m_socket -> SetReconnect(true); +#endif + m_socket -> Open("127.0.0.1", m_port); + SocketHandler::Add(m_socket); + } + SocketHandler::Add( p ); +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + + diff --git a/Sockets/EventHandler.h b/Sockets/EventHandler.h new file mode 100644 index 0000000..9ca7cde --- /dev/null +++ b/Sockets/EventHandler.h @@ -0,0 +1,85 @@ +/** \file EventHandler.h + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_EventHandler_H +#define _SOCKETS_EventHandler_H + +#include "sockets-config.h" +#include "SocketHandler.h" +#include "IEventHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class StdLog; +class IEventOwner; +class Event; +class TcpSocket; + +/** SocketHandler implementing the IEventHandler interface. + \ingroup timer */ +class EventHandler : public SocketHandler,public IEventHandler +{ +public: + EventHandler(StdLog * = NULL); + EventHandler(Mutex&,StdLog * = NULL); + ~EventHandler(); + + bool GetTimeUntilNextEvent(struct timeval *tv); + void CheckEvents(); + long AddEvent(IEventOwner *from,long sec,long usec); + void ClearEvents(IEventOwner *from); + void RemoveEvent(IEventOwner *from,long eid); + + /** SocketHandler while() loop implemented with event functionality. */ + void EventLoop(); + /** Stop event loop. */ + void SetQuit(bool = true); + + void Add(Socket *); + +private: + EventHandler(const EventHandler& ) {} // copy constructor + EventHandler& operator=(const EventHandler& ) { return *this; } // assignment operator + std::list m_events; + bool m_quit; + TcpSocket *m_socket; + port_t m_port; +}; + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_EventHandler_H + diff --git a/Sockets/EventTime.cpp b/Sockets/EventTime.cpp new file mode 100644 index 0000000..a58cbb3 --- /dev/null +++ b/Sockets/EventTime.cpp @@ -0,0 +1,105 @@ +/** \file EventTime.cpp + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef MACOSX +#include +#include +#include +#endif +#include "EventTime.h" +#ifdef _WIN32 +#include +#else +#include +#include +#endif + + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +EventTime::EventTime() : m_time(Tick()) +{ +} + + +EventTime::EventTime(mytime_t sec,long usec) : m_time(Tick()) +{ + m_time += sec * 1000000 + usec; +} + + +EventTime::~EventTime() +{ +} + + +mytime_t EventTime::Tick() +{ + mytime_t t; +#ifdef _WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + t = ft.dwHighDateTime; + t = t << 32; + t += ft.dwLowDateTime; + t /= 10; // us +#else + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + t = tv.tv_sec; + t *= 1000000; + t += tv.tv_usec; +#endif + return t; +} + + +EventTime EventTime::operator - (const EventTime& x) const +{ + EventTime t; + t.m_time = m_time - x.m_time; + return t; +} + + +bool EventTime::operator < (const EventTime& x) const +{ + return m_time < x.m_time; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/EventTime.h b/Sockets/EventTime.h new file mode 100644 index 0000000..5c77695 --- /dev/null +++ b/Sockets/EventTime.h @@ -0,0 +1,77 @@ +/** \file EventTime.h + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_EventTime_H +#define _SOCKETS_EventTime_H + +#include "sockets-config.h" +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +#if defined( _WIN32) && !defined(__CYGWIN__) +typedef __int64 mytime_t; +#else +#include // int64_t +typedef int64_t mytime_t; +#endif + + +/** \defgroup timer EventTimer event handling */ + +/** EventTime primitive, returns current time as a 64-bit number. + \ingroup timer */ +class EventTime +{ +public: + EventTime(); + EventTime(mytime_t sec,long usec); + ~EventTime(); + + static mytime_t Tick(); + + operator mytime_t () { return m_time; } + EventTime operator - (const EventTime& x) const; + bool operator < (const EventTime& x) const; + +private: + EventTime(const EventTime& ) {} // copy constructor + EventTime& operator=(const EventTime& ) { return *this; } // assignment operator + mytime_t m_time; +}; + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_EventTime_H + diff --git a/Sockets/Exception.cpp b/Sockets/Exception.cpp new file mode 100644 index 0000000..a005669 --- /dev/null +++ b/Sockets/Exception.cpp @@ -0,0 +1,48 @@ +/** + ** \file Exception.cpp + ** \date 2007-09-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "Exception.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + +Exception::Exception(const std::string& description) : m_description(description) +{ +} + + +const std::string Exception::ToString() const +{ + return m_description; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/Exception.h b/Sockets/Exception.h new file mode 100644 index 0000000..81ba737 --- /dev/null +++ b/Sockets/Exception.h @@ -0,0 +1,58 @@ +/** + ** \file Exception.h + ** \date 2007-09-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _Sockets_Exception_H +#define _Sockets_Exception_H + +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + +class Exception +{ +public: + Exception(const std::string& description); + virtual ~Exception() {} + + virtual const std::string ToString() const; + + Exception(const Exception& ) {} // copy constructor + + Exception& operator=(const Exception& ) { return *this; } // assignment operator + +private: + std::string m_description; + +}; + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _Sockets_Exception_H + diff --git a/Sockets/File.cpp b/Sockets/File.cpp new file mode 100644 index 0000000..4c1e5bf --- /dev/null +++ b/Sockets/File.cpp @@ -0,0 +1,169 @@ +/** \file File.cpp + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "File.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +File::File() +:m_fil(NULL) +,m_rptr(0) +,m_wptr(0) +{ +} + + +File::~File() +{ + fclose(); +} + + +bool File::fopen(const std::string& path, const std::string& mode) +{ + m_path = path; + m_mode = mode; + m_fil = ::fopen(path.c_str(), mode.c_str()); + return m_fil ? true : false; +} + + +void File::fclose() +{ + if (m_fil) + { + ::fclose(m_fil); + m_fil = NULL; + } +} + + + +size_t File::fread(char *ptr, size_t size, size_t nmemb) const +{ + size_t r = 0; + if (m_fil) + { + fseek(m_fil, m_rptr, SEEK_SET); + r = ::fread(ptr, size, nmemb, m_fil); + m_rptr = ftell(m_fil); + } + return r; +} + + +size_t File::fwrite(const char *ptr, size_t size, size_t nmemb) +{ + size_t r = 0; + if (m_fil) + { + fseek(m_fil, m_wptr, SEEK_SET); + r = ::fwrite(ptr, size, nmemb, m_fil); + m_wptr = ftell(m_fil); + } + return r; +} + + + +char *File::fgets(char *s, int size) const +{ + char *r = NULL; + if (m_fil) + { + fseek(m_fil, m_rptr, SEEK_SET); + r = ::fgets(s, size, m_fil); + m_rptr = ftell(m_fil); + } + return r; +} + + +void File::fprintf(const char *format, ...) +{ + if (!m_fil) + return; + va_list ap; + va_start(ap, format); + fseek(m_fil, m_rptr, SEEK_SET); + vfprintf(m_fil, format, ap); + m_rptr = ftell(m_fil); + va_end(ap); +} + + +off_t File::size() const +{ + struct stat st; + if (stat(m_path.c_str(), &st) == -1) + { + return 0; + } + return st.st_size; +} + + +bool File::eof() const +{ + if (m_fil) + { + if (feof(m_fil)) + return true; + } + return false; +} + + +void File::reset_read() const +{ + m_rptr = 0; +} + + +void File::reset_write() +{ + m_wptr = 0; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/File.h b/Sockets/File.h new file mode 100644 index 0000000..58a1b71 --- /dev/null +++ b/Sockets/File.h @@ -0,0 +1,84 @@ +/** \file File.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_File_H +#define _SOCKETS_File_H + +#include "sockets-config.h" +#include "IFile.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** IFile implementation of a disk file. + \ingroup file */ +class File : public IFile +{ +public: + File(); + ~File(); + + bool fopen(const std::string&, const std::string&); + void fclose(); + + size_t fread(char *, size_t, size_t) const; + size_t fwrite(const char *, size_t, size_t); + + char *fgets(char *, int) const; + void fprintf(const char *format, ...); + + off_t size() const; + bool eof() const; + + void reset_read() const; + void reset_write(); + +private: + File(const File& ) {} // copy constructor + File& operator=(const File& ) { return *this; } // assignment operator + + std::string m_path; + std::string m_mode; + FILE *m_fil; + mutable long m_rptr; + long m_wptr; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_File_H + diff --git a/Sockets/HTTPSocket.cpp b/Sockets/HTTPSocket.cpp new file mode 100644 index 0000000..ca49822 --- /dev/null +++ b/Sockets/HTTPSocket.cpp @@ -0,0 +1,413 @@ +/** \file HTTPSocket.cpp + ** \date 2004-04-06 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HTTPSocket.h" +#include "Parse.h" +#include "ISocketHandler.h" +#include "Utility.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HTTPSocket::HTTPSocket(ISocketHandler& h) +:TcpSocket(h) +,m_first(true) +,m_header(true) +,m_http_version("HTTP/1.0") +,m_request(false) +,m_response(false) +,m_body_size_left(0) +,m_b_http_1_1(false) +,m_b_keepalive(false) +{ + SetLineProtocol(); + DisableInputBuffer(); +} + + +HTTPSocket::~HTTPSocket() +{ +} + + +void HTTPSocket::OnRawData(const char *buf,size_t len) +{ + if (!m_header) + { + if (!m_b_http_1_1 || !m_b_keepalive) + { + OnData(buf, len); + } + else + { + size_t sz = m_body_size_left < len ? m_body_size_left : len; + OnData(buf, sz); + m_body_size_left -= sz; + if (!m_body_size_left) + { + SetLineProtocol( true ); + m_first = true; + m_header = true; + m_body_size_left = 0; + if (len - sz > 0) + { + char tmp[TCP_BUFSIZE_READ]; + memcpy(tmp, buf + sz, len - sz); + tmp[len - sz] = 0; + OnRead( tmp, len - sz ); + } + } + } + } +} + + +void HTTPSocket::OnLine(const std::string& line) +{ + if (m_first) + { + Parse pa(line); + std::string str = pa.getword(); + if (str.size() > 4 && Utility::ToLower(str.substr(0,5)) == "http/") // response + { + m_http_version = str; + m_status = pa.getword(); + m_status_text = pa.getrest(); + m_response = true; + } + else // request + { + m_method = str; + m_url = pa.getword(); + size_t spl = m_url.find("?"); + if (spl != std::string::npos) + { + m_uri = m_url.substr(0,spl); + m_query_string = m_url.substr(spl + 1); + } + else + { + m_uri = m_url; + m_query_string = ""; + } + m_http_version = pa.getword(); + m_b_http_1_1 = m_http_version.size() > 4 && m_http_version.substr(4) == "/1.1"; + m_b_keepalive = m_b_http_1_1; + m_request = true; + } + m_first = false; + OnFirst(); + return; + } + if (!line.size()) + { + if (m_body_size_left || !m_b_http_1_1 || !m_b_keepalive) + { + SetLineProtocol(false); + m_header = false; + } + OnHeaderComplete(); + return; + } + Parse pa(line,":"); + std::string key = pa.getword(); + std::string value = pa.getrest(); + OnHeader(key,value); + if (Utility::ToLower(key) == "content-length") + { + m_body_size_left = atol(value.c_str()); + } + if (m_b_http_1_1 && Utility::ToLower(key) == "connection") + { + m_b_keepalive = Utility::ToLower(value) != "close"; + } + /* If remote end tells us to keep connection alive, and we're operating + in http/1.1 mode (not http/1.0 mode), then we mark the socket to be + retained. */ +#ifdef ENABLE_POOL + if (m_b_http_1_1 && m_b_keepalive) + { + SetRetain(); + } +#endif +} + + +void HTTPSocket::SendResponse() +{ + std::string msg; + msg = m_http_version + " " + m_status + " " + m_status_text + "\r\n"; + for (string_m::iterator it = m_response_header.begin(); it != m_response_header.end(); it++) + { + std::string key = (*it).first; + std::string val = (*it).second; + msg += key + ": " + val + "\r\n"; + } + for (std::list >::iterator it2 = m_response_header_append.begin(); it2 != m_response_header_append.end(); ++it2) + { + msg += it2 -> first + ": " + it2 -> second + "\r\n"; + } + msg += "\r\n"; + Send( msg ); +} + + +void HTTPSocket::AddResponseHeader(const std::string& header, const char *format, ...) +{ + char slask[5000]; // temporary for vsprintf / vsnprintf + va_list ap; + + va_start(ap, format); +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + + m_response_header[header] = slask; +} + + +void HTTPSocket::SendRequest() +{ + std::string msg; + msg = m_method + " " + m_url + " " + m_http_version + "\r\n"; + for (string_m::iterator it = m_response_header.begin(); it != m_response_header.end(); it++) + { + std::string key = (*it).first; + std::string val = (*it).second; + msg += key + ": " + val + "\r\n"; + } + msg += "\r\n"; + Send( msg ); +} + + +std::string HTTPSocket::MyUseragent() +{ + std::string version = "C++Sockets/"; +#ifdef _VERSION + version += _VERSION; +#endif + return version; +} + + +void HTTPSocket::Reset() +{ + m_first = true; + m_header = true; + m_request = false; + m_response = false; + SetLineProtocol(true); + while (m_response_header.size()) + { + string_m::iterator it = m_response_header.begin(); + m_response_header.erase(it); + } + while (m_response_header_append.size()) + { + std::list >::iterator it = m_response_header_append.begin(); + m_response_header_append.erase(it); + } + +} + + +const std::string& HTTPSocket::GetMethod() +{ + return m_method; +} + + +void HTTPSocket::SetMethod(const std::string& x) +{ + m_method = x; +} + + +const std::string& HTTPSocket::GetUrl() +{ + return m_url; +} + + +void HTTPSocket::SetUrl(const std::string& x) +{ + m_url = x; +} + + +const std::string& HTTPSocket::GetUri() +{ + return m_uri; +} + + +const std::string& HTTPSocket::GetQueryString() +{ + return m_query_string; +} + + +const std::string& HTTPSocket::GetHttpVersion() +{ + return m_http_version; +} + + +const std::string& HTTPSocket::GetStatus() +{ + return m_status; +} + + +const std::string& HTTPSocket::GetStatusText() +{ + return m_status_text; +} + + +bool HTTPSocket::IsRequest() +{ + return m_request; +} + + +bool HTTPSocket::IsResponse() +{ + return m_response; +} + + +void HTTPSocket::SetHttpVersion(const std::string& x) +{ + m_http_version = x; +} + + +void HTTPSocket::SetStatus(const std::string& x) +{ + m_status = x; +} + + +void HTTPSocket::SetStatusText(const std::string& x) +{ + m_status_text = x; +} + + +void HTTPSocket::AddResponseHeader(const std::string& x,const std::string& y) +{ + m_response_header[Utility::ToLower(x)] = y; +} + + +void HTTPSocket::AppendResponseHeader(const std::string& x,const std::string& y) +{ + m_response_header_append.push_back(std::pair(Utility::ToLower(x),y)); +} + + +void HTTPSocket::SetUri(const std::string& x) +{ + m_uri = x; +} + + +void HTTPSocket::url_this(const std::string& url_in,std::string& protocol,std::string& host,port_t& port,std::string& url,std::string& file) +{ + Parse pa(url_in,"/"); + protocol = pa.getword(); // http + if (!strcasecmp(protocol.c_str(), "https:")) + { +#ifdef HAVE_OPENSSL + EnableSSL(); +#else + Handler().LogError(this, "url_this", -1, "SSL not available", LOG_LEVEL_WARNING); +#endif + port = 443; + } + else + { + port = 80; + } + host = pa.getword(); + if (strstr(host.c_str(),":")) + { + Parse pa(host,":"); + pa.getword(host); + port = static_cast(pa.getvalue()); + } + url = "/" + pa.getrest(); + { + Parse pa(url,"/"); + std::string tmp = pa.getword(); + while (tmp.size()) + { + file = tmp; + tmp = pa.getword(); + } + } +} // url_this + + +bool HTTPSocket::ResponseHeaderIsSet(const std::string& name) +{ + string_m::iterator it = m_response_header.find( Utility::ToLower(name) ); + if (it != m_response_header.end()) + { + return true; + } + std::list >::iterator it2; + for (it2 = m_response_header_append.begin(); it2 != m_response_header_append.end(); it2++) + { + std::pair& ref = *it2; + if (ref.first == Utility::ToLower(name) ) + { + return true; + } + } + return false; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HTTPSocket.h b/Sockets/HTTPSocket.h new file mode 100644 index 0000000..00bb305 --- /dev/null +++ b/Sockets/HTTPSocket.h @@ -0,0 +1,152 @@ +/** \file HTTPSocket.h Class HTTPSocket definition. + ** \date 2004-04-06 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HTTPSocket_H +#define _SOCKETS_HTTPSocket_H + +#include "sockets-config.h" +#include +#include "TcpSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup http HTTP Sockets */ +/** HTTP request/response base class. + \ingroup http */ +class HTTPSocket : public TcpSocket +{ + /** map to hold http header values. */ + typedef std::map string_m; +public: + HTTPSocket(ISocketHandler& ); + ~HTTPSocket(); + + void OnRawData(const char *buf,size_t len); + void OnLine(const std::string& line); + + /** Callback executes when first line has been received. + GetMethod, GetUrl/GetUri, and GetHttpVersion are valid when this callback is executed. */ + virtual void OnFirst() = 0; + /** For each header line this callback is executed. + \param key Http header name + \param value Http header value */ + virtual void OnHeader(const std::string& key,const std::string& value) = 0; + /** Callback fires when all http headers have been received. */ + virtual void OnHeaderComplete() = 0; + /** Chunk of http body data recevied. */ + virtual void OnData(const char *,size_t) = 0; + + /** Get http method from incoming request, ie GET/POST/PUT etc */ + const std::string& GetMethod(); + /** Set http method to be used in request. */ + void SetMethod(const std::string& x); + /** Get url from request. */ + const std::string& GetUrl(); + /** Set url to be used in outgoing request. */ + void SetUrl(const std::string& x); + /** Get part of url before '?' character. */ + const std::string& GetUri(); + /** Now why would I need this when there is a SetUrl method? */ + void SetUri(const std::string& x); + /** Get part of url after '?' character. */ + const std::string& GetQueryString(); + /** Get http version from incoming request/response. */ + const std::string& GetHttpVersion(); + /** Get http status from incoming response. */ + const std::string& GetStatus(); + /** Get http statustext from incoming response. */ + const std::string& GetStatusText(); + /** Incoming header has been identified as a request (method url http_version\r\n). */ + bool IsRequest(); + /** Incoming header has been identified as a response (http_version status status_text\r\n). */ + bool IsResponse(); + /** Set http version to be used in outgoing request/response. */ + void SetHttpVersion(const std::string& x); + /** Set http status for outgoing response. */ + void SetStatus(const std::string& x); + /** Set http statustext for outgoing response. */ + void SetStatusText(const std::string& x); + /** Add (and replace if exists) http header. */ + void AddResponseHeader(const std::string& x,const std::string& y); + /** Add (and replace if exists) http header. */ + void AddResponseHeader(const std::string& x,const char *format, ...); + /** Add http header. */ + void AppendResponseHeader(const std::string& x,const std::string& y); + /** See if http header 'name' has been set. */ + bool ResponseHeaderIsSet(const std::string& name); + /** Send response prepared with calls to methods SetHttpVersion, SetStatus, SetStatusText, + and AddResponseHeader. */ + void SendResponse(); + /** Send request prepared with calls to methods SetMethod, SetUrl, SetHttpVersion, + and AddResponseHeader. */ + void SendRequest(); + + /** Implement this to return your own User-agent string. */ + virtual std::string MyUseragent(); + + /** Parse url. If protocol is https, EnableSSL() will be called. */ + void url_this(const std::string& url_in,std::string& protocol,std::string& host,port_t& port,std::string& url,std::string& file); + +protected: + HTTPSocket(const HTTPSocket& s) : TcpSocket(s) {} + /** Reset state of socket to sucessfully implement keep-alive. */ + virtual void Reset(); + +private: + HTTPSocket& operator=(const HTTPSocket& ) { return *this; } + bool m_first; + bool m_header; + std::string m_line; + std::string m_method; + std::string m_url; + std::string m_uri; + std::string m_query_string; + std::string m_http_version; + std::string m_status; + std::string m_status_text; + bool m_request; + bool m_response; + string_m m_response_header; + size_t m_body_size_left; + bool m_b_http_1_1; + bool m_b_keepalive; + std::list > m_response_header_append; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HTTPSocket_H + diff --git a/Sockets/HttpBaseSocket.cpp b/Sockets/HttpBaseSocket.cpp new file mode 100644 index 0000000..6f05b2e --- /dev/null +++ b/Sockets/HttpBaseSocket.cpp @@ -0,0 +1,204 @@ +/** + ** \file AjpBaseSocket.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HttpBaseSocket.h" +#include "IFile.h" +#include "Utility.h" +#include "HttpResponse.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +HttpBaseSocket::HttpBaseSocket(ISocketHandler& h) +:HTTPSocket(h) +,m_res_file(NULL) +,m_b_keepalive(false) +{ +} + + +HttpBaseSocket::~HttpBaseSocket() +{ +} + + +void HttpBaseSocket::OnFirst() +{ +DEB(fprintf(stderr, " %s %s %s\n", GetMethod().c_str(), GetUri().c_str(), GetHttpVersion().c_str());) + m_req.SetHttpMethod( GetMethod() ); + m_req.SetUri( GetUri() ); + m_req.SetHttpVersion( GetHttpVersion() ); + + m_req.SetAttribute("query_string", GetQueryString() ); + + m_req.SetRemoteAddr( GetRemoteAddress() ); + m_req.SetRemoteHost( "" ); // %! + m_req.SetServerName( GetSockAddress() ); + m_req.SetServerPort( GetSockPort() ); +} + + +void HttpBaseSocket::OnHeader(const std::string& key,const std::string& value) +{ +DEB(fprintf(stderr, " (request)OnHeader %s: %s\n", key.c_str(), value.c_str());) + if (Utility::ToLower(key) == "cookie") + m_req.AddCookie(value); + else + m_req.SetHeader(key, value); +} + + +void HttpBaseSocket::OnHeaderComplete() +{ + m_body_size_left = atol( m_req.Header("content-length").c_str() ); + if (m_body_size_left > 0) + { + m_req.InitBody( m_body_size_left ); + } + else + { + // execute + Execute(); + } +} + + +void HttpBaseSocket::OnData(const char *buf,size_t sz) +{ + m_req.Write( buf, sz ); + m_body_size_left -= sz; + if (!m_body_size_left) + { + m_req.CloseBody(); + + // execute + Execute(); + } +} + + +// -------------------------------------------------------------------------------------- +void HttpBaseSocket::Execute() +{ + // parse form data / query_string and cookie header if available + m_req.ParseBody(); + + // prepare page + OnExec( m_req ); + +DEB(printf(" *** http version: %s\n", m_req.HttpVersion().c_str()); +printf(" *** connection: %s\n", m_req.Header("connection").c_str());) + if ( !(m_req.HttpVersion().size() > 4 && m_req.HttpVersion().substr(m_req.HttpVersion().size() - 4) == "/1.1") || + m_req.Header("connection") == "close") + { + m_b_keepalive = false; +DEB(printf(" *** keepalive: false\n");) + } + else + { + m_b_keepalive = true; +DEB(printf(" *** keepalive: true\n");) + } + m_req.Reset(); + Reset(); +} + + +// -------------------------------------------------------------------------------------- +void HttpBaseSocket::Respond(const HttpResponse& res) +{ +// res.SetHeader("connection", "close"); + + SetHttpVersion( res.HttpVersion() ); + SetStatus( Utility::l2string(res.HttpStatusCode()) ); + SetStatusText( res.HttpStatusMsg() ); + + if (!ResponseHeaderIsSet("content-length")) + { + AddResponseHeader( "content-length", Utility::l2string( res.GetFile().size() ) ); + } + for (std::map::const_iterator it = res.Headers().begin(); it != res.Headers().end(); ++it) + { + AddResponseHeader( it -> first, it -> second ); + } + std::list vec = res.CookieNames(); + for (std::list::iterator it2 = vec.begin(); it2 != vec.end(); it2++) + { + AppendResponseHeader( "set-cookie", res.Cookie(*it2) ); + } + SendResponse(); + + m_res_file = &res.GetFile(); + + OnTransferLimit(); +} + + +// -------------------------------------------------------------------------------------- +void HttpBaseSocket::OnTransferLimit() +{ + char msg[32768]; + size_t n = m_res_file -> fread(msg, 1, 32768); + while (n > 0) + { + SendBuf( msg, n ); + if (GetOutputLength() > 1) + { + SetTransferLimit( 1 ); + break; + } + n = m_res_file -> fread(msg, 1, 32768); + } + if (!GetOutputLength()) + { + if (!m_b_keepalive) + { + SetCloseAndDelete(); + } + } +} + + +// -------------------------------------------------------------------------------------- +void HttpBaseSocket::Reset() +{ + HTTPSocket::Reset(); + m_body_size_left = 0; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/Sockets/HttpBaseSocket.h b/Sockets/HttpBaseSocket.h new file mode 100644 index 0000000..8ce4ad8 --- /dev/null +++ b/Sockets/HttpBaseSocket.h @@ -0,0 +1,76 @@ +/** + ** \file HttpBaseSocket.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _HttpBaseSocket_H +#define _HttpBaseSocket_H + +#include "HTTPSocket.h" +#include "HttpRequest.h" +#include "IHttpServer.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpResponse; + +class HttpBaseSocket : public HTTPSocket, public IHttpServer +{ +public: + HttpBaseSocket(ISocketHandler& h); + ~HttpBaseSocket(); + + void OnFirst(); + void OnHeader(const std::string& key,const std::string& value); + void OnHeaderComplete(); + void OnData(const char *,size_t); + + // implements IHttpServer::Respond + void Respond(const HttpResponse& res); + + void OnTransferLimit(); + +protected: + HttpBaseSocket(const HttpBaseSocket& s) : HTTPSocket(s) {} // copy constructor + // + HttpRequest m_req; + void Reset(); + +private: + HttpBaseSocket& operator=(const HttpBaseSocket& ) { return *this; } // assignment operator + void Execute(); + // + size_t m_body_size_left; + const IFile *m_res_file; + bool m_b_keepalive; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _HttpBaseSocket_H + diff --git a/Sockets/HttpClientSocket.cpp b/Sockets/HttpClientSocket.cpp new file mode 100644 index 0000000..c6f43e4 --- /dev/null +++ b/Sockets/HttpClientSocket.cpp @@ -0,0 +1,277 @@ +/** + ** \file HttpClientSocket.cpp + ** \date 2006-04-20 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HttpClientSocket.h" +#include "StdLog.h" +#include "ISocketHandler.h" +#include "Utility.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpClientSocket::HttpClientSocket(ISocketHandler& h) +:HTTPSocket(h) +,m_data_ptr(NULL) +,m_data_size(0) +,m_content_length(0) +,m_data_ptr_set(false) +,m_fil(NULL) +,m_content_ptr(0) +,m_b_complete(false) +,m_b_close_when_complete(false) +{ +} + + +HttpClientSocket::HttpClientSocket(ISocketHandler& h,const std::string& url_in) +:HTTPSocket(h) +,m_data_ptr(NULL) +,m_data_size(0) +,m_content_length(0) +,m_data_ptr_set(false) +,m_fil(NULL) +,m_content_ptr(0) +,m_b_complete(false) +,m_b_close_when_complete(false) +{ + std::string url; + url_this(url_in, m_protocol, m_host, m_port, url, m_url_filename); + SetUrl( url ); +} + + +HttpClientSocket::~HttpClientSocket() +{ + if (m_data_ptr && !m_data_ptr_set) + { + delete[] m_data_ptr; + } + if (m_fil) + { + fclose(m_fil); + } +} + + +void HttpClientSocket::OnFirst() +{ + if (!IsResponse()) + { + Handler().LogError(this, "OnFirst", 0, "Response expected but not received - aborting", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + m_content = GetHttpVersion() + " " + GetStatus() + " " + GetStatusText() + "\r\n"; +} + + +void HttpClientSocket::OnHeader(const std::string& key,const std::string& value) +{ + m_content += key + ": " + value + "\r\n"; + if (!strcasecmp(key.c_str(), "content-length")) + { + m_content_length = atoi(value.c_str()); + } + else + if (!strcasecmp(key.c_str(), "content-type")) + { + m_content_type = value; + } +} + + +void HttpClientSocket::OnHeaderComplete() +{ + if (m_filename.size()) + { + m_fil = fopen(m_filename.c_str(), "wb"); + } + else + if (!m_data_ptr && m_content_length) + { + m_data_ptr = new unsigned char[m_content_length]; + m_data_size = m_content_length; + } +} + + +void HttpClientSocket::OnData(const char *buf,size_t len) +{ + if (m_fil) + { + fwrite(buf, 1, len, m_fil); + } + else + if (m_data_ptr) + { + if (m_content_ptr + len > m_data_size) + { + Handler().LogError(this, "OnData", -1, "content buffer overflow", LOG_LEVEL_ERROR); + } + else + { + memcpy(m_data_ptr + m_content_ptr, buf, len); + } + } + m_content_ptr += len; + if (m_content_ptr == m_content_length && m_content_length) + { + if (m_fil) + { + fclose(m_fil); + m_fil = NULL; + } + m_b_complete = true; + OnContent(); + if (m_b_close_when_complete) + { + SetCloseAndDelete(); + } + } +} + + +void HttpClientSocket::OnDelete() +{ + if (!m_b_complete) + { + if (m_fil) + { + fclose(m_fil); + m_fil = NULL; + } + m_b_complete = true; + OnContent(); + } +} + + +void HttpClientSocket::SetFilename(const std::string& x) +{ + m_filename = x; +} + + +void HttpClientSocket::SetDataPtr(unsigned char *buf,size_t len) +{ + m_data_ptr = buf; + m_data_size = len; + m_data_ptr_set = true; +} + + +const std::string& HttpClientSocket::GetContent() +{ + return m_content; +} + + +size_t HttpClientSocket::GetContentLength() +{ + return m_content_length; +} + + +size_t HttpClientSocket::GetContentPtr() +{ + return m_content_ptr; +} + + +size_t HttpClientSocket::GetPos() +{ + return m_content_ptr; +} + + +bool HttpClientSocket::Complete() +{ + return m_b_complete; +} + + +const unsigned char *HttpClientSocket::GetDataPtr() const +{ + return m_data_ptr; +} + + +void HttpClientSocket::OnContent() +{ +} + + +void HttpClientSocket::SetCloseOnComplete(bool x) +{ + m_b_close_when_complete = x; +} + + +const std::string& HttpClientSocket::GetUrlProtocol() +{ + return m_protocol; +} + + +const std::string& HttpClientSocket::GetUrlHost() +{ + return m_host; +} + + +port_t HttpClientSocket::GetUrlPort() +{ + return m_port; +} + + +const std::string& HttpClientSocket::GetUrlFilename() +{ + return m_url_filename; +} + + +const std::string& HttpClientSocket::GetContentType() +{ + return m_content_type; +} + + +void HttpClientSocket::Url(const std::string& url_in,std::string& host,port_t& port) +{ + std::string url; + url_this(url_in, m_protocol, m_host, m_port, url, m_url_filename); + SetUrl(url); + host = GetUrlHost(); + port = GetUrlPort(); +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/HttpClientSocket.h b/Sockets/HttpClientSocket.h new file mode 100644 index 0000000..dc307a9 --- /dev/null +++ b/Sockets/HttpClientSocket.h @@ -0,0 +1,121 @@ +/** + ** \file HttpClientSocket.h + ** \date 2006-04-20 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpClientSocket_H +#define _SOCKETS_HttpClientSocket_H + +#include "sockets-config.h" +#include "HTTPSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Get http response to file or memory. + \ingroup http */ +class HttpClientSocket : public HTTPSocket +{ +public: + HttpClientSocket(ISocketHandler&); + HttpClientSocket(ISocketHandler&,const std::string& url_in); + ~HttpClientSocket(); + + /** Parse url to protocol,host,port,url and file. */ + void Url(const std::string& url_in,std::string& host,port_t& port); + + void OnFirst(); + void OnHeader(const std::string&,const std::string&); + void OnHeaderComplete(); + void OnData(const char *,size_t); + void OnDelete(); + + /** New callback method fires when all data is received. */ + virtual void OnContent(); + + /** Write response to this file */ + void SetFilename(const std::string& ); + const std::string& Filename() const { return m_filename; } + + /** Store response in this buffer. */ + void SetDataPtr(unsigned char *,size_t); + + /** Get response headers. */ + const std::string& GetContent(); + + /** Get size of response body. */ + size_t GetContentLength(); + + /** Get content type from response header. */ + const std::string& GetContentType(); + + /** Get size of received response body. */ + size_t GetContentPtr(); + /** Get size of received response body. */ + size_t GetPos(); + + /** Complete response has been received. */ + bool Complete(); + + /** Get ptr to response data buffer. */ + const unsigned char *GetDataPtr() const; + + /** Close socket when response received. */ + void SetCloseOnComplete(bool = true); + + /** Get protocol used from url. */ + const std::string& GetUrlProtocol(); + /** Get hostname from url. */ + const std::string& GetUrlHost(); + /** Get port from url. */ + port_t GetUrlPort(); + /** Get filename part of url. */ + const std::string& GetUrlFilename(); + +protected: + HttpClientSocket(const HttpClientSocket& s) : HTTPSocket(s) {} // copy constructor + HttpClientSocket& operator=(const HttpClientSocket& ) { return *this; } // assignment operator +private: + std::string m_filename; ///< Filename to write response to + unsigned char *m_data_ptr; ///< Ptr to buffer where to store response + size_t m_data_size; ///< Max size of data buffer + size_t m_content_length; ///< Content-length header received from remote + std::string m_content; ///< Received http headers + bool m_data_ptr_set; ///< Buffer set from outside, do not delete + FILE *m_fil; ///< Output file + size_t m_content_ptr; ///< Number of bytes received from body + bool m_b_complete; ///< The entire content-length number of bytes has been received + bool m_b_close_when_complete; ///< Close when the full response has been received + std::string m_protocol; ///< Protocol part of url_in + std::string m_host; ///< Hostname from url_in + port_t m_port; ///< Port from url_in + std::string m_url_filename; ///< Filename from url_in + std::string m_content_type; ///< Content-type: header from response +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_HttpClientSocket_H + diff --git a/Sockets/HttpDebugSocket.cpp b/Sockets/HttpDebugSocket.cpp new file mode 100644 index 0000000..70bf75b --- /dev/null +++ b/Sockets/HttpDebugSocket.cpp @@ -0,0 +1,125 @@ +/** \file HttpDebugSocket.cpp + ** \date 2004-10-08 +**/ +/* +Copyright (C) 2004-2007 Anders Hedström (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HttpDebugSocket.h" +#include "Utility.h" +#include "ISocketHandler.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpDebugSocket::HttpDebugSocket(ISocketHandler& h) : HTTPSocket(h) +,m_content_length(0) +,m_read_ptr(0) +{ +} + + +HttpDebugSocket::~HttpDebugSocket() +{ +} + + +void HttpDebugSocket::Init() +{ + if (GetParent() -> GetPort() == 443) + { +#ifdef HAVE_OPENSSL + EnableSSL(); +#else + Handler().LogError(this, "url_this", -1, "SSL not available", LOG_LEVEL_WARNING); +#endif + } +} + + +void HttpDebugSocket::OnFirst() +{ + Send( + "HTTP/1.1 200 OK\n" + "Content-type: text/html\n" + "Connection: close\n" + "Server: HttpDebugSocket/1.0\n" + "\n"); + Send( + "Echo Request" + "

Request Header

"); + Send( "
" + "
" + "
"); + Send( "
");
+	Send(GetMethod() + " " + GetUrl() + " " + GetHttpVersion() + "\n");
+}
+
+
+void HttpDebugSocket::OnHeader(const std::string& key,const std::string& value)
+{
+	if (!strcasecmp(key.c_str(),"content-length"))
+		m_content_length = atoi(value.c_str());
+
+	Send(key + ": " + value + "\n");
+}
+
+
+void HttpDebugSocket::OnHeaderComplete()
+{
+	if (m_content_length)
+	{
+		Send("

Request Body

");
+	}
+	else
+	{
+		Send("

"); + SetCloseAndDelete(); + } +} + + +void HttpDebugSocket::OnData(const char *p,size_t l) +{ + SendBuf(p,l); + m_read_ptr += (int)l; + if (m_read_ptr >= m_content_length && m_content_length) + { + Send("
"); + SetCloseAndDelete(); + } +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpDebugSocket.h b/Sockets/HttpDebugSocket.h new file mode 100644 index 0000000..f905904 --- /dev/null +++ b/Sockets/HttpDebugSocket.h @@ -0,0 +1,70 @@ +/** \file HttpDebugSocket.h + ** \date 2004-09-27 +**/ +/* +Copyright (C) 2004-2007 Anders Hedström (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpDebugSocket_H +#define _SOCKETS_HttpDebugSocket_H + +#include "sockets-config.h" +#include "HTTPSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class ISocketHandler; + +/** HTTP request "echo" class. This class echoes a http request/body +with a html formatted page. + \ingroup http */ +class HttpDebugSocket : public HTTPSocket +{ +public: + HttpDebugSocket(ISocketHandler&); + ~HttpDebugSocket(); + + void Init(); + + void OnFirst(); + void OnHeader(const std::string& key,const std::string& value); + void OnHeaderComplete(); + void OnData(const char *,size_t); + +private: + HttpDebugSocket(const HttpDebugSocket& s) : HTTPSocket(s) {} // copy constructor + HttpDebugSocket& operator=(const HttpDebugSocket& ) { return *this; } // assignment operator + int m_content_length; + int m_read_ptr; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpDebugSocket_H + diff --git a/Sockets/HttpGetSocket.cpp b/Sockets/HttpGetSocket.cpp new file mode 100644 index 0000000..fe5e3d5 --- /dev/null +++ b/Sockets/HttpGetSocket.cpp @@ -0,0 +1,114 @@ +/** \file HttpGetSocket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#else +#include +#endif +#include "Utility.h" +#include "Parse.h" +#include "ISocketHandler.h" +#include "HttpGetSocket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpGetSocket::HttpGetSocket(ISocketHandler& h) : HttpClientSocket(h) +{ +} + + +HttpGetSocket::HttpGetSocket(ISocketHandler& h,const std::string& url_in,const std::string& to_file) : HttpClientSocket(h, url_in) +{ + if (to_file.size()) + { + SetFilename(to_file); + } + if (!Open(GetUrlHost(),GetUrlPort())) + { + if (!Connecting()) + { + Handler().LogError(this, "HttpGetSocket", -1, "connect() failed miserably", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + } +} + + +HttpGetSocket::HttpGetSocket(ISocketHandler& h,const std::string& host,port_t port,const std::string& url,const std::string& to_file) : HttpClientSocket(h) +{ + SetUrl(url); + if (to_file.size()) + { + SetFilename(to_file); + } + if (!Open(host, port)) + { + if (!Connecting()) + { + Handler().LogError(this, "HttpGetSocket", -1, "connect() failed miserably", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + } +} + + +HttpGetSocket::~HttpGetSocket() +{ +} + + +void HttpGetSocket::OnConnect() +{ + SetMethod( "GET" ); + AddResponseHeader( "Accept", "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1"); + AddResponseHeader( "Accept-Language", "en-us,en;q=0.5"); + AddResponseHeader( "Accept-Encoding", "gzip,deflate"); + AddResponseHeader( "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); + AddResponseHeader( "User-agent", MyUseragent() ); + + if (GetUrlPort() != 80 && GetUrlPort() != 443) + AddResponseHeader( "Host", GetUrlHost() + ":" + Utility::l2string(GetUrlPort()) ); + else + AddResponseHeader( "Host", GetUrlHost() ); + SendRequest(); +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpGetSocket.h b/Sockets/HttpGetSocket.h new file mode 100644 index 0000000..beb1097 --- /dev/null +++ b/Sockets/HttpGetSocket.h @@ -0,0 +1,66 @@ +/** \file HttpGetSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpGetSocket_H +#define _SOCKETS_HttpGetSocket_H + +#include "sockets-config.h" +#include "HttpClientSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Get http page. + \ingroup http */ +class HttpGetSocket : public HttpClientSocket +{ +public: + HttpGetSocket(ISocketHandler&); + HttpGetSocket(ISocketHandler&,const std::string& url,const std::string& to_file = ""); + HttpGetSocket(ISocketHandler&,const std::string& host,port_t port,const std::string& url,const std::string& to_file = ""); + ~HttpGetSocket(); + + void OnConnect(); + +protected: + HttpGetSocket& operator=(const HttpGetSocket& ) { return *this; } + HttpGetSocket(const HttpGetSocket& s) : HttpClientSocket(s) {} +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpGetSocket_H + diff --git a/Sockets/HttpPostSocket.cpp b/Sockets/HttpPostSocket.cpp new file mode 100644 index 0000000..a72b8cf --- /dev/null +++ b/Sockets/HttpPostSocket.cpp @@ -0,0 +1,294 @@ +/** \file HttpPostSocket.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif +#else +#include +#include +#endif +#include "ISocketHandler.h" +#include +#include +#include "Parse.h" +#include "Utility.h" +#include "Lock.h" + +#include "HttpPostSocket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +int HttpPostSocket::m_boundary_count = 0; +Mutex HttpPostSocket::m_boundary_mutex; + + +HttpPostSocket::HttpPostSocket(ISocketHandler& h) : HttpClientSocket(h) +,m_bMultipart(false) +{ +} + + +HttpPostSocket::HttpPostSocket(ISocketHandler& h,const std::string& url_in) : HttpClientSocket(h, url_in) +,m_bMultipart(false) +{ + Lock lock(m_boundary_mutex); + + m_boundary = "----"; + for (int i = 0; i < 12; i++) + { + char c = m_boundary_count++ % 128; + while (!isalnum(c)) + c = m_boundary_count++ % 128; + m_boundary += c; + } + m_boundary += "__" + Utility::l2string(m_boundary_count++); +} + + +HttpPostSocket::~HttpPostSocket() +{ +} + + +void HttpPostSocket::AddField(const std::string& name,const std::string& value) +{ + std::list vec; + vec.push_back(value); + AddMultilineField(name, vec); +} + + +void HttpPostSocket::AddMultilineField(const std::string& name,std::list& values) +{ + m_fields[name] = values; +} + + +void HttpPostSocket::AddFile(const std::string& name,const std::string& filename,const std::string& type) +{ + struct stat st; + if (!stat(filename.c_str(), &st)) + { + m_files[name] = filename; + m_content_length[filename] = st.st_size; + m_content_type[filename] = type; + m_bMultipart = true; + } + else + { + Handler().LogError(this, "AddFile", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} + + +void HttpPostSocket::Open() +{ + // why do I have to specify TcpSocket:: to get to the Open() method?? + TcpSocket::Open(GetUrlHost(), GetUrlPort()); +} + + +void HttpPostSocket::OnConnect() +{ + if (m_bMultipart) + { + DoMultipartPost(); + } + else + { + std::string body; + + // only fields, no files, add urlencoding + for (std::map >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list& ref = (*it).second; + if (body.size()) + { + body += '&'; + } + body += name + "="; + bool first = true; + for (std::list::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + if (!first) + { + body += "%0d%0a"; // CRLF + } + body += Utility::rfc1738_encode(value); + first = false; + } + } + + // build header, send body + SetMethod("POST"); + SetHttpVersion( "HTTP/1.1" ); + AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding.. + AddResponseHeader( "User-agent", MyUseragent()); + AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" ); + AddResponseHeader( "Connection", "close" ); + AddResponseHeader( "Content-type", "application/x-www-form-urlencoded" ); + AddResponseHeader( "Content-length", Utility::l2string((long)body.size()) ); + SendRequest(); + + // send body + Send( body ); + } +} + + +void HttpPostSocket::DoMultipartPost() +{ + long length = 0; // calculate content_length of our post body + std::string tmp; + + // fields + { + for (std::map >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list& ref = (*it).second; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"\r\n" + "\r\n"; + for (std::list::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + tmp += value + "\r\n"; + } + length += (long)tmp.size(); + } + } + + // files + { + for (std::map::iterator it = m_files.begin(); it != m_files.end(); it++) + { + std::string name = (*it).first; + std::string filename = (*it).second; + long content_length = m_content_length[filename]; + std::string content_type = m_content_type[filename]; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n" + "content-type: " + content_type + "\r\n" + "\r\n"; + length += (long)tmp.size(); + length += content_length; + length += 2; // crlf after file + } + } + + // end + tmp = "--" + m_boundary + "--\r\n"; + length += (long)tmp.size(); + + // build header, send body + SetMethod("POST"); + SetHttpVersion( "HTTP/1.1" ); + AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding.. + AddResponseHeader( "User-agent", MyUseragent()); + AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" ); + AddResponseHeader( "Connection", "close" ); + AddResponseHeader( "Content-type", "multipart/form-data; boundary=" + m_boundary ); + AddResponseHeader( "Content-length", Utility::l2string(length) ); + + SendRequest(); + + // send fields + { + for (std::map >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list& ref = (*it).second; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"\r\n" + "\r\n"; + for (std::list::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + tmp += value + "\r\n"; + } + Send( tmp ); + } + } + + // send files + { + for (std::map::iterator it = m_files.begin(); it != m_files.end(); it++) + { + std::string name = (*it).first; + std::string filename = (*it).second; + std::string content_type = m_content_type[filename]; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n" + "content-type: " + content_type + "\r\n" + "\r\n"; + Send( tmp ); + { + FILE *fil = fopen(filename.c_str(),"rb"); + if (fil) + { + char slask[2000]; // for fread + size_t n; + while ((n = fread(slask, 1, 2000, fil)) > 0) + { + SendBuf(slask, n); + } + fclose(fil); + } + } + Send("\r\n"); + } + } + + // end of send + Send("--" + m_boundary + "--\r\n"); +} + + +void HttpPostSocket::SetMultipart() +{ + m_bMultipart = true; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpPostSocket.h b/Sockets/HttpPostSocket.h new file mode 100644 index 0000000..7815da3 --- /dev/null +++ b/Sockets/HttpPostSocket.h @@ -0,0 +1,94 @@ +/** \file HttpPostSocket.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpPostSocket_H +#define _SOCKETS_HttpPostSocket_H + +#include "sockets-config.h" +#include "HttpClientSocket.h" +#include "Mutex.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class ISocketHandler; + +/** Generate a http post request, get response. + \ingroup http */ +class HttpPostSocket : public HttpClientSocket +{ +public: + HttpPostSocket(ISocketHandler&); + /* client constructor, + \param url_in = 'http://host:port/resource' */ + HttpPostSocket(ISocketHandler&,const std::string& url_in); + ~HttpPostSocket(); + + // these must be specified before connecting / adding to handler + /** Add field to post. */ + void AddField(const std::string& name,const std::string& value); + /** Add multiline field to post. */ + void AddMultilineField(const std::string& name,std::list& values); + /** Add file to post. */ + void AddFile(const std::string& name,const std::string& filename,const std::string& type); + + /** use this to post with content-type multipart/form-data. + // when adding a file to the post, this is the default and only content-type */ + void SetMultipart(); + + /** connect to host:port derived from url in constructor */ + void Open(); + + /** http put client implemented in OnConnect */ + void OnConnect(); + +private: + HttpPostSocket(const HttpPostSocket& s) : HttpClientSocket(s) {} // copy constructor + HttpPostSocket& operator=(const HttpPostSocket& ) { return *this; } // assignment operator + void DoMultipartPost(); + // + std::map > m_fields; + std::map m_files; + std::string m_boundary; + std::map m_content_length; + std::map m_content_type; + bool m_bMultipart; +static int m_boundary_count; +static Mutex m_boundary_mutex; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpPostSocket_H + diff --git a/Sockets/HttpPutSocket.cpp b/Sockets/HttpPutSocket.cpp new file mode 100644 index 0000000..90ac7e4 --- /dev/null +++ b/Sockets/HttpPutSocket.cpp @@ -0,0 +1,125 @@ +/** \file HttpPutSocket.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#else +#include +#endif +#include "ISocketHandler.h" +#include +#include + +#include "Utility.h" +#include "Parse.h" + +#include "HttpPutSocket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpPutSocket::HttpPutSocket(ISocketHandler& h) : HttpClientSocket(h) +{ +} + + +HttpPutSocket::HttpPutSocket(ISocketHandler& h,const std::string& url_in) : HttpClientSocket(h, url_in) +{ +} + + +HttpPutSocket::~HttpPutSocket() +{ +} + + +void HttpPutSocket::SetFile(const std::string& file) +{ + struct stat st; + if (!stat(file.c_str(), &st)) + { + m_filename = file; + m_content_length = st.st_size; + } + else + { + Handler().LogError(this, "SetFile", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} + + +void HttpPutSocket::SetContentType(const std::string& type) +{ + m_content_type = type; +} + + + +void HttpPutSocket::Open() +{ + // why do I have to specify TcpSocket:: to get to the Open() method?? + TcpSocket::Open(GetUrlHost(), GetUrlPort()); +} + + +void HttpPutSocket::OnConnect() +{ + SetMethod( "PUT" ); + SetHttpVersion( "HTTP/1.1" ); + AddResponseHeader( "Host", GetUrlHost() ); + AddResponseHeader( "Content-type", m_content_type ); + AddResponseHeader( "Content-length", Utility::l2string(m_content_length) ); + AddResponseHeader( "User-agent", MyUseragent() ); + SendRequest(); + + FILE *fil = fopen(m_filename.c_str(), "rb"); + if (fil) + { + size_t n; + char buf[32768]; + while ((n = fread(buf, 1, 32768, fil)) > 0) + { + SendBuf(buf, n); + } + fclose(fil); + } +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpPutSocket.h b/Sockets/HttpPutSocket.h new file mode 100644 index 0000000..cafc7b9 --- /dev/null +++ b/Sockets/HttpPutSocket.h @@ -0,0 +1,83 @@ +/** \file HttpPutSocket.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpPutSocket_H +#define _SOCKETS_HttpPutSocket_H + +#include "sockets-config.h" +#include "HttpClientSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class ISocketHandler; + +/** Put http page. + \ingroup http */ +class HttpPutSocket : public HttpClientSocket +{ +public: + HttpPutSocket(ISocketHandler&); + /** client constructor, + \param url_in = 'http://host:port/resource' */ + HttpPutSocket(ISocketHandler&,const std::string& url_in); + ~HttpPutSocket(); + + // these must be specified before connecting / adding to handler + /** Set filename to send. */ + void SetFile(const std::string& ); + /** Set mimetype of file to send. */ + void SetContentType(const std::string& ); + + /** connect to host:port derived from url in constructor */ + void Open(); + + /** http put client implemented in OnConnect */ + void OnConnect(); + +private: + HttpPutSocket(const HttpPutSocket& s) : HttpClientSocket(s) {} // copy constructor + HttpPutSocket& operator=(const HttpPutSocket& ) { return *this; } // assignment operator + // + std::string m_filename; + std::string m_content_type; + long m_content_length; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpPutSocket_H + diff --git a/Sockets/HttpRequest.cpp b/Sockets/HttpRequest.cpp new file mode 100644 index 0000000..3909595 --- /dev/null +++ b/Sockets/HttpRequest.cpp @@ -0,0 +1,358 @@ +/** + ** \file HttpRequest.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HttpRequest.h" +#include "Utility.h" +#include "MemFile.h" +#include "HttpdForm.h" +#include "HttpdCookies.h" +#include "Parse.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x; fflush(stderr); +#else +#define DEB(x) +#endif + + +// -------------------------------------------------------------------------------------- +HttpRequest::HttpRequest() : HttpTransaction() +, m_server_port(0) +, m_is_ssl(false) +, m_body_file(NULL) +, m_form(NULL) +{ +} + + +// -------------------------------------------------------------------------------------- +HttpRequest::HttpRequest(const HttpRequest& src) : HttpTransaction(src) +, m_method(src.m_method) +, m_protocol(src.m_protocol) +, m_req_uri(src.m_req_uri) +, m_remote_addr(src.m_remote_addr) +, m_remote_host(src.m_remote_host) +, m_server_name(src.m_server_name) +, m_server_port(src.m_server_port) +, m_is_ssl(src.m_is_ssl) +, m_attribute(src.m_attribute) +, m_null(src.m_null) +, m_body_file(src.m_body_file) +, m_form(src.m_form) +, m_cookies(src.m_cookies) +, m_cookie(src.m_cookie) +{ +} + + +// -------------------------------------------------------------------------------------- +HttpRequest::~HttpRequest() +{ + m_body_file = std::auto_ptr(NULL); + m_form = std::auto_ptr(NULL); +/* + if (m_body_file) + { + delete m_body_file; + } + if (m_form) + { + delete m_form; + } +*/ +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetHttpMethod(const std::string& value) +{ + m_method = value; +} + + +const std::string& HttpRequest::HttpMethod() const +{ + return m_method; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetHttpVersion(const std::string& value) +{ + m_protocol = value; +} + + +const std::string& HttpRequest::HttpVersion() const +{ + return m_protocol; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetUri(const std::string& value) +{ + m_req_uri = Utility::rfc1738_decode(value); +} + + +const std::string& HttpRequest::Uri() const +{ + return m_req_uri; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetRemoteAddr(const std::string& value) +{ + m_remote_addr = value; +} + + +const std::string& HttpRequest::RemoteAddr() const +{ + return m_remote_addr; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetRemoteHost(const std::string& value) +{ + m_remote_host = value; +} + + +const std::string& HttpRequest::RemoteHost() const +{ + return m_remote_host; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetServerName(const std::string& value) +{ + m_server_name = value; +} + + +const std::string& HttpRequest::ServerName() const +{ + return m_server_name; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetServerPort(int value) +{ + m_server_port = value; +} + + +int HttpRequest::ServerPort() const +{ + return m_server_port; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetIsSsl(bool value) +{ + m_is_ssl = value; +} + + +bool HttpRequest::IsSsl() const +{ + return m_is_ssl; +} + + + +// -------------------------------------------------------------------------------------- +void HttpRequest::SetAttribute(const std::string& key, const std::string& value) +{ + m_attribute[Utility::ToLower(key)] = value; +} + + +void HttpRequest::SetAttribute(const std::string& key, long value) +{ + m_attribute[Utility::ToLower(key)] = Utility::l2string(value); +} + + +const std::string& HttpRequest::Attribute(const std::string& key) const +{ + std::map::const_iterator it; + if ( (it = m_attribute.find(Utility::ToLower(key))) != m_attribute.end()) + return it -> second; + return m_null; +} + + +// -------------------------------------------------------------------------------------- +const std::map& HttpRequest::Attributes() const +{ + return m_attribute; +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::AddCookie(const std::string& str) +{ + m_cookies.add( str ); + Parse pa(str, ";"); + std::string lstr = pa.getword(); + while (!lstr.empty()) + { + Parse pa2(lstr, "="); + std::string name = pa2.getword(); + m_cookie[name] = lstr; +DEB(fprintf(stderr, " *** AddCookie '%s' = '%s'\n", name.c_str(), lstr.c_str());) + lstr = pa.getword(); + } +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::InitBody( size_t sz ) +{ + if (!m_body_file.get()) + m_body_file = std::auto_ptr(new MemFile); +DEB( else + fprintf(stderr, "Body data file already opened\n");) +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::Write( const char *buf, size_t sz ) +{ + if (m_body_file.get()) + m_body_file -> fwrite(buf, 1, sz); +DEB( else + fprintf(stderr, "Write: Body data file not open\n");) +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::CloseBody() +{ + if (m_body_file.get()) + m_body_file -> fclose(); +DEB( else + fprintf(stderr, "CloseBody: File not open\n");) +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::ParseBody() +{ + std::map::const_iterator it; + if ( (it = m_attribute.find("query_string")) != m_attribute.end()) + { + std::string qs = it -> second; + m_form = std::auto_ptr(new HttpdForm( qs, qs.size() )); + } + else + if (m_body_file.get()) + { + m_form = std::auto_ptr(new HttpdForm( m_body_file.get(), ContentType(), ContentLength() )); + } + else + { + // dummy + m_form = std::auto_ptr(new HttpdForm( "", 0 )); + } +} + + +// -------------------------------------------------------------------------------------- +const HttpdForm& HttpRequest::Form() const +{ + return *m_form; +} + + +// -------------------------------------------------------------------------------------- +const HttpdCookies& HttpRequest::Cookies() const +{ + return m_cookies; +} + + +// -------------------------------------------------------------------------------------- +void HttpRequest::Reset() +{ + HttpTransaction::Reset(); + m_method = ""; + m_protocol = ""; + m_req_uri = ""; + m_remote_addr = ""; + m_remote_host = ""; + m_server_name = ""; + m_server_port = 0; + m_is_ssl = false; + while (!m_attribute.empty()) + { + m_attribute.erase(m_attribute.begin()); + } + m_body_file = std::auto_ptr(NULL); + m_form = std::auto_ptr(NULL); +/* + if (m_body_file) + { + delete m_body_file; + m_body_file = NULL; + } + if (m_form) + { + delete m_form; + m_form = NULL; + } +*/ + m_cookies.Reset(); + while (!m_cookie.empty()) + { + m_cookie.erase(m_cookie.begin()); + } +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/Sockets/HttpRequest.h b/Sockets/HttpRequest.h new file mode 100644 index 0000000..38290e4 --- /dev/null +++ b/Sockets/HttpRequest.h @@ -0,0 +1,125 @@ +/** + ** \file HttpRequest.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpRequest_H +#define _SOCKETS_HttpRequest_H + +#include "HttpTransaction.h" +#include "HttpdCookies.h" +#include +#include "IFile.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpdForm; +class IFile; + +class HttpRequest : public HttpTransaction +{ +public: + HttpRequest(); + HttpRequest(const HttpRequest& src); + ~HttpRequest(); + + /** Get, Post */ + void SetHttpMethod(const std::string& value); + const std::string& HttpMethod() const; + + /** HTTP/1.x */ + void SetHttpVersion(const std::string& value); + const std::string& HttpVersion() const; + + void SetUri(const std::string& value); + const std::string& Uri() const; + + void SetRemoteAddr(const std::string& value); + const std::string& RemoteAddr() const; + + void SetRemoteHost(const std::string& value); + const std::string& RemoteHost() const; + + void SetServerName(const std::string& value); + const std::string& ServerName() const; + + void SetServerPort(int value); + int ServerPort() const; + + void SetIsSsl(bool value); + bool IsSsl() const; + + /** Set / Read attribute value */ + void SetAttribute(const std::string& key, const std::string& value); + void SetAttribute(const std::string& key, long value); + const std::string& Attribute(const std::string& key) const; + + const std::map& Attributes() const; + + /** Cookies */ + void AddCookie(const std::string& ); + const std::map& CookieMap() const { return m_cookie; } + + /** Open file for body data */ + void InitBody( size_t sz ); + + /** Write body data */ + void Write( const char *buf, size_t sz ); + + /** No more writing */ + void CloseBody(); + + void ParseBody(); + + const HttpdForm& Form() const; + const HttpdCookies& Cookies() const; + + const IFile *BodyFile() const { return m_body_file.get(); } + + void Reset(); + +private: + std::string m_method; + std::string m_protocol; + std::string m_req_uri; + std::string m_remote_addr; + std::string m_remote_host; + std::string m_server_name; + int m_server_port; + bool m_is_ssl; + std::map m_attribute; + std::string m_null; + mutable std::auto_ptr m_body_file; + mutable std::auto_ptr m_form; + HttpdCookies m_cookies; + std::map m_cookie; + +}; // end of class + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_HttpRequest_H + diff --git a/Sockets/HttpResponse.cpp b/Sockets/HttpResponse.cpp new file mode 100644 index 0000000..c312ee2 --- /dev/null +++ b/Sockets/HttpResponse.cpp @@ -0,0 +1,201 @@ +/** + ** \file HttpResponse.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#include + +#include "HttpResponse.h" +#include "HttpRequest.h" +#include "MemFile.h" +#include "File.h" +#include "Parse.h" +#include "Utility.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x; fflush(stderr); +#else +#define DEB(x) +#endif + + +// -------------------------------------------------------------------------------------- +HttpResponse::HttpResponse(const std::string& version) : HttpTransaction() +, m_http_version( version ) +, m_http_status_code(0) +, m_file( new MemFile ) +{ +} + + +// -------------------------------------------------------------------------------------- +HttpResponse::~HttpResponse() +{ + delete m_file; +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::SetHttpVersion(const std::string& value) +{ + m_http_version = value; +} + + +// -------------------------------------------------------------------------------------- +const std::string& HttpResponse::HttpVersion() const +{ + return m_http_version; +} + + + +// -------------------------------------------------------------------------------------- +void HttpResponse::SetHttpStatusCode(int value) +{ + m_http_status_code = value; +} + + +int HttpResponse::HttpStatusCode() const +{ + return m_http_status_code; +} + + + +// -------------------------------------------------------------------------------------- +void HttpResponse::SetHttpStatusMsg(const std::string& value) +{ + m_http_status_msg = value; +} + + +const std::string& HttpResponse::HttpStatusMsg() const +{ + return m_http_status_msg; +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::SetCookie(const std::string& value) +{ + Parse pa(value, "="); + std::string name = pa.getword(); + m_cookie[Utility::ToLower(name)] = value; +DEB(fprintf(stderr, "HttpResponse::Set-Cookie<%s>: %s\n", name.c_str(), value.c_str());) +} + + +const std::string HttpResponse::Cookie(const std::string& name) const +{ + std::map::const_iterator it = m_cookie.find(name); +DEB(fprintf(stderr, "HttpResponse; get value of Cookie<%s>: ", name.c_str());) + if (it != m_cookie.end()) + { +DEB(fprintf(stderr, "%s\n", it -> second.c_str());) + return it -> second; + } +DEB(fprintf(stderr, "\n");) + return ""; +} + + +std::list HttpResponse::CookieNames() const +{ + std::list vec; + DEB(fprintf(stderr, "HttpResponse::CookieNames; ");) + for (std::map::const_iterator it = m_cookie.begin(); it != m_cookie.end(); it++) + { + DEB(fprintf(stderr, " %s", it -> first.c_str());) + vec.push_back(it -> first); + } + DEB(fprintf(stderr, "\n");) + return vec; +} + + + +// -------------------------------------------------------------------------------------- +void HttpResponse::Write( const std::string& str ) +{ + Write( str.c_str(), str.size() ); +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::Write( const char *buf, size_t sz ) +{ + m_file -> fwrite( buf, 1, sz ); +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::Writef( const char *format, ... ) +{ + va_list ap; + va_start(ap, format); + char tmp[10000]; + vsprintf(tmp, format, ap); + va_end(ap); + m_file -> fwrite( tmp, 1, strlen(tmp) ); +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::SetFile( const std::string& path ) +{ + delete m_file; + m_file = new File(); + m_file -> fopen( path, "rb" ); +} + + +// -------------------------------------------------------------------------------------- +void HttpResponse::Reset() +{ + HttpTransaction::Reset(); + m_http_version = ""; + m_http_status_code = 0; + m_http_status_msg = ""; + while (!m_cookie.empty()) + { + m_cookie.erase(m_cookie.begin()); + } + if (m_file) + { + delete m_file; + m_file = new MemFile; + } +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/HttpResponse.h b/Sockets/HttpResponse.h new file mode 100644 index 0000000..089ff1d --- /dev/null +++ b/Sockets/HttpResponse.h @@ -0,0 +1,82 @@ +/** + ** \file HttpResponse.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpResponse_H +#define _SOCKETS_HttpResponse_H + +#include "HttpTransaction.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IFile; + +class HttpResponse : public HttpTransaction +{ +public: + HttpResponse(const std::string& version = "HTTP/1.0"); + ~HttpResponse(); + + /** HTTP/1.x */ + void SetHttpVersion(const std::string& value); + const std::string& HttpVersion() const; + + void SetHttpStatusCode(int value); + int HttpStatusCode() const; + + void SetHttpStatusMsg(const std::string& value); + const std::string& HttpStatusMsg() const; + + void SetCookie(const std::string& value); + const std::string Cookie(const std::string& name) const; + std::list CookieNames() const; + + void Write( const std::string& str ); + void Write( const char *buf, size_t sz ); + void Writef( const char *format, ... ); + + const IFile& GetFile() const { return *m_file; } + + /** Replace memfile with file on disk, opened for read. */ + void SetFile( const std::string& path ); + + void Reset(); + +private: + std::string m_http_version; + int m_http_status_code; + std::string m_http_status_msg; + std::map m_cookie; + IFile *m_file; + +}; // end of class + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_HttpResponse_H + diff --git a/Sockets/HttpTransaction.cpp b/Sockets/HttpTransaction.cpp new file mode 100644 index 0000000..8221d1f --- /dev/null +++ b/Sockets/HttpTransaction.cpp @@ -0,0 +1,244 @@ +/** + ** \file HttpTransaction.cpp + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "HttpTransaction.h" +#include "Utility.h" +#include "Parse.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// -------------------------------------------------------------------------------------- +HttpTransaction::HttpTransaction() +{ +} + + +// -------------------------------------------------------------------------------------- +HttpTransaction::~HttpTransaction() +{ +} + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetHeader(const std::string& key, const std::string& value) +{ + m_header[Utility::ToLower(key)] = value; +} + + +void HttpTransaction::SetHeader(const std::string& key, long value) +{ + m_header[Utility::ToLower(key)] = Utility::l2string(value); +} + + +const std::string& HttpTransaction::Header(const std::string& key) const +{ + std::map::const_iterator it; + if ((it = m_header.find(Utility::ToLower(key))) != m_header.end()) + return it -> second; + return m_null; +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetAccept(const std::string& value) +{ + SetHeader("accept", value); +} + + +const std::string& HttpTransaction::Accept() const +{ + return Header("accept"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetAcceptCharset(const std::string& value) +{ + SetHeader("accept-charset", value); +} + + +const std::string& HttpTransaction::AcceptCharset() const +{ + return Header("accept-charset"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetAcceptEncoding(const std::string& value) +{ + SetHeader("accept-encoding", value); +} + + +const std::string& HttpTransaction::AcceptEncoding() const +{ + return Header("accept-encoding"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetAcceptLanguage(const std::string& value) +{ + SetHeader("accept-language", value); +} + + +const std::string& HttpTransaction::AcceptLanguage() const +{ + return Header("accept-language"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetConnection(const std::string& value) +{ + SetHeader("connection", value); +} + + +const std::string& HttpTransaction::Connection() const +{ + return Header("connection"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetContentType(const std::string& value) +{ + SetHeader("content-type", value); +} + + +const std::string& HttpTransaction::ContentType() const +{ + return Header("content-type"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetContentLength(long value) +{ + SetHeader("content-length", value ); +} + + +long HttpTransaction::ContentLength() const +{ + return atol(Header("content-length").c_str()); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetHost(const std::string& value) +{ + SetHeader("host", value); +} + + +const std::string& HttpTransaction::Host() const +{ + return Header("host"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetPragma(const std::string& value) +{ + SetHeader("pragma", value); +} + + +const std::string& HttpTransaction::Pragma() const +{ + return Header("pragma"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetReferer(const std::string& value) +{ + SetHeader("referer", value); +} + + +const std::string& HttpTransaction::Referer() const +{ + return Header("referer"); +} + + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::SetUserAgent(const std::string& value) +{ + SetHeader("user-agent", value); +} + + +const std::string& HttpTransaction::UserAgent() const +{ + return Header("user-agent"); +} + + +// -------------------------------------------------------------------------------------- +const std::map& HttpTransaction::Headers() const +{ + return m_header; +} + + +// -------------------------------------------------------------------------------------- +void HttpTransaction::Reset() +{ + while (!m_header.empty()) + { + m_header.erase(m_header.begin()); + } +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + diff --git a/Sockets/HttpTransaction.h b/Sockets/HttpTransaction.h new file mode 100644 index 0000000..a92fdda --- /dev/null +++ b/Sockets/HttpTransaction.h @@ -0,0 +1,94 @@ +/** + ** \file HttpTransaction.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpTransaction_H +#define _SOCKETS_HttpTransaction_H + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpTransaction +{ +public: + HttpTransaction(); + virtual ~HttpTransaction(); + + /** Set / Read http header value */ + void SetHeader(const std::string& key, const std::string& value); + void SetHeader(const std::string& key, long value); + const std::string& Header(const std::string& key) const; + + void SetAccept(const std::string& value); + const std::string& Accept() const; + + void SetAcceptCharset(const std::string& value); + const std::string& AcceptCharset() const; + + void SetAcceptEncoding(const std::string& value); + const std::string& AcceptEncoding() const; + + void SetAcceptLanguage(const std::string& value); + const std::string& AcceptLanguage() const; + + void SetConnection(const std::string& value); + const std::string& Connection() const; + + void SetContentType(const std::string& value); + const std::string& ContentType() const; + + void SetContentLength(long value); + long ContentLength() const; + + void SetHost(const std::string& value); + const std::string& Host() const; + + void SetPragma(const std::string& value); + const std::string& Pragma() const; + + void SetReferer(const std::string& value); + const std::string& Referer() const; + + void SetUserAgent(const std::string& value); + const std::string& UserAgent() const; + + const std::map& Headers() const; + + virtual void Reset(); + +private: + std::map m_header; + std::string m_null; + +}; // end of class + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_HttpTransaction_H + diff --git a/Sockets/HttpdCookies.cpp b/Sockets/HttpdCookies.cpp new file mode 100644 index 0000000..9662f0d --- /dev/null +++ b/Sockets/HttpdCookies.cpp @@ -0,0 +1,270 @@ +/** \file HttpdCookies.cpp +*/ +/* +Copyright (C) 2003-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "Parse.h" +#include "Utility.h" +#include "HTTPSocket.h" +#include "HttpdCookies.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x; fflush(stderr); +#else +#define DEB(x) +#endif + + +HttpdCookies::HttpdCookies() +{ +} + +HttpdCookies::HttpdCookies(const std::string& s) +{ + Parse *pa = new Parse(s,";"); + + std::string slask = pa -> getword(); + while (slask.size()) + { + Parse *pa2 = new Parse(slask,"="); + std::string name = pa2 -> getword(); + std::string value = pa2 -> getword(); + delete pa2; + m_cookies.push_back(std::pair(name, value)); + // + slask = pa -> getword(); + } + delete pa; +} + +void HttpdCookies::add(const std::string& s) +{ + Parse *pa = new Parse(s,";"); +DEB(fprintf(stderr, "Parse cookie: %s\n", s.c_str());) + std::string slask = pa -> getword(); + while (slask.size()) + { + Parse *pa2 = new Parse(slask,"="); + std::string name = pa2 -> getword(); + std::string value = pa2 -> getword(); + delete pa2; + m_cookies.push_back(std::pair(name, value)); + // + slask = pa -> getword(); + } + delete pa; +} + +HttpdCookies::~HttpdCookies() +{ +} + +bool HttpdCookies::getvalue(const std::string& name,std::string& buffer) const +{ + for (cookie_v::const_iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + const std::pair& ref = *it; + if (!strcasecmp(ref.first.c_str(),name.c_str())) + { + buffer = ref.second; + return true; + } + } + buffer = ""; + return false; +} + +void HttpdCookies::replacevalue(const std::string& name,const std::string& value) +{ + for (cookie_v::iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + std::pair& ref = *it; + if (!strcasecmp(ref.first.c_str(),name.c_str())) + { + ref.second = value; + return; + } + } + m_cookies.push_back(std::pair(name, value)); + +} + +void HttpdCookies::replacevalue(const std::string& name,long l) +{ + replacevalue(name, Utility::l2string(l)); +} + +void HttpdCookies::replacevalue(const std::string& name,int i) +{ + replacevalue(name, Utility::l2string(i)); +} + +size_t HttpdCookies::getlength(const std::string& name) const +{ + for (cookie_v::const_iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + const std::pair& ref = *it; + if (!strcasecmp(ref.first.c_str(),name.c_str())) + { + return ref.second.size(); + } + } + return 0; +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, const std::string& value) +{ + char *str = new char[name.size() + value.size() + domain.size() + path.size() + 100]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%s; domain=%s; path=%s; expires=%s", + name.c_str(), value.c_str(), + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%s; path=%s; expires=%s", + name.c_str(), value.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + replacevalue(name, value); +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, long value) +{ + char *str = new char[name.size() + domain.size() + path.size() + 100]; + char dt[80]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%ld; domain=%s; path=%s; expires=%s", + name.c_str(), value, + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%ld; path=%s; expires=%s", + name.c_str(), value, + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + sprintf(dt, "%ld", value); + replacevalue(name, dt); +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, int value) +{ + char *str = new char[name.size() + domain.size() + path.size() + 100]; + char dt[80]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%d; domain=%s; path=%s; expires=%s", + name.c_str(), value, + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%d; path=%s; expires=%s", + name.c_str(), value, + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + sprintf(dt, "%d", value); + replacevalue(name, dt); +} + + +const std::string& HttpdCookies::expiredatetime() const +{ + time_t t = time(NULL); + struct tm tp; +#ifdef _WIN32 + memcpy(&tp, gmtime(&t), sizeof(tp)); +#else + gmtime_r(&t, &tp); +#endif + const char *days[7] = {"Sunday", "Monday", + "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char dt[100]; + + sprintf(dt, "%s, %02d-%s-%04d %02d:%02d:%02d GMT", + days[tp.tm_wday], + tp.tm_mday, + months[tp.tm_mon], + tp.tm_year + 1910, + tp.tm_hour, + tp.tm_min, + tp.tm_sec); + m_date = dt; + return m_date; +} + + +void HttpdCookies::Reset() +{ + while (!m_cookies.empty()) + { + m_cookies.erase(m_cookies.begin()); + } + m_date = ""; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpdCookies.h b/Sockets/HttpdCookies.h new file mode 100644 index 0000000..79af17c --- /dev/null +++ b/Sockets/HttpdCookies.h @@ -0,0 +1,89 @@ +/** \file HttpdCookies.h +*/ +/* +Copyright (C) 2003-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _SOCKETS_HttpdCookies_H +#define _SOCKETS_HttpdCookies_H + +#include "sockets-config.h" +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +//! Store the cookies name/value pairs. + + + +//! Retrieve and manage cookies during a cgi call. +class HTTPSocket; + +/** HTTP Cookie parse/container class. +\sa HttpdSocket +\sa HttpdForm +\ingroup webserver */ +class HttpdCookies +{ + /** list of key/value structs. */ + typedef std::list > cookie_v; + +public: + HttpdCookies(); + HttpdCookies(const std::string& query_string); + ~HttpdCookies(); + + void add(const std::string& s); + + bool getvalue(const std::string&,std::string&) const; + void replacevalue(const std::string& ,const std::string& ); + void replacevalue(const std::string& ,long); + void replacevalue(const std::string& ,int); + size_t getlength(const std::string& ) const; + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,const std::string& v); + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,long v); + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,int v); + const std::string& expiredatetime() const; + + cookie_v& GetHttpdCookies() { return m_cookies; } + + void Reset(); + +private: + cookie_v m_cookies; + mutable std::string m_date; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpdCookies_H + diff --git a/Sockets/HttpdForm.cpp b/Sockets/HttpdForm.cpp new file mode 100644 index 0000000..1b1f689 --- /dev/null +++ b/Sockets/HttpdForm.cpp @@ -0,0 +1,616 @@ +/** \file HttpdForm.cpp - read stdin, parse cgi input + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "socket_include.h" +#include "Parse.h" +#include "IFile.h" +#include "HttpdForm.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define TMPSIZE 10000 + + +HttpdForm::HttpdForm(IFile *infil, const std::string& content_type, size_t content_length) : raw(false) +{ + CGI *cgi = NULL; + size_t extra = 2; + char name[TMPSIZE]; + + m_current = m_cgi.end(); + *name = 0; + + if (content_type.size() >= 19 && content_type.substr(0, 19) == "multipart/form-data") + { + Parse pa(content_type,";="); + char *tempcmp = NULL; + size_t tc = 0; + size_t l = 0; + std::string str = pa.getword(); + m_strBoundary = ""; + while (str.size()) + { + if (!strcmp(str.c_str(),"boundary")) + { + m_strBoundary = pa.getword(); + l = m_strBoundary.size(); + tempcmp = new char[l + extra]; + } + // + str = pa.getword(); + } + if (m_strBoundary.size()) + { + std::string content_type; + std::string current_name; + std::string current_filename; + char *slask = new char[TMPSIZE]; + infil -> fgets(slask, TMPSIZE); + while (!infil -> eof()) + { + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + content_type = ""; + current_name = ""; + current_filename = ""; + if ((strstr(slask,m_strBoundary.c_str()) || strstr(m_strBoundary.c_str(),slask)) && strcmp(slask, m_strBoundary.c_str())) + { + m_strBoundary = slask; + l = m_strBoundary.size(); + delete[] tempcmp; + tempcmp = new char[l + extra]; + } + if (!strcmp(slask, m_strBoundary.c_str())) + { + // Get headers until empty line + infil -> fgets(slask, TMPSIZE); + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + while (!infil -> eof() && *slask) + { + Parse pa(slask,";"); + std::string h = pa.getword(); + if (!strcasecmp(h.c_str(),"Content-type:")) + { + content_type = pa.getword(); + } + else + if (!strcasecmp(h.c_str(),"Content-Disposition:")) + { + h = pa.getword(); + if (!strcmp(h.c_str(),"form-data")) + { + pa.EnableQuote(true); + h = pa.getword(); + while (h.size()) + { + Parse pa2(slask,"="); + std::string name = pa2.getword(); + std::string h = pa2.getrest(); + if (!strcmp(name.c_str(),"name")) + { + if (h.size() && h[0] == '"') + { + current_name = h.substr(1, h.size() - 2); + } + else + { + current_name = h; + } + } + else + if (!strcmp(name.c_str(),"filename")) + { + if (h.size() && h[0] == '"') + { + current_filename = h.substr(1, h.size() - 2); + } + else + { + current_filename = h; + } + size_t x = 0; + for (size_t i = 0; i < current_filename.size(); i++) + { + if (current_filename[i] == '/' || current_filename[i] == '\\') + x = i + 1; + } + if (x) + { + current_filename = current_filename.substr(x); + } + } + h = pa.getword(); + } + } + } + // get next header value + infil -> fgets(slask, TMPSIZE); + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + } + // Read content, save...? + if (!current_filename.size()) // not a file + { + std::string val; + infil -> fgets(slask, TMPSIZE); + while (!infil -> eof() && strncmp(slask,m_strBoundary.c_str(),m_strBoundary.size() )) + { + val += slask; + infil -> fgets(slask, TMPSIZE); + } + // remove trailing cr/linefeed + while (val.size() && (val[val.size() - 1] == 13 || val[val.size() - 1] == 10)) + { + val = val.substr(0,val.size() - 1); + } + cgi = new CGI(current_name, val); + m_cgi.push_back(cgi); + } + else // current_filename.size() > 0 + { + // read until m_strBoundary... + FILE *fil; + int out = 0; + char c; + char fn[2000]; // where post'd file will be saved +#ifdef _WIN32 + { + char tmp_path[2000]; + ::GetTempPathA(2000, tmp_path); + if (tmp_path[strlen(tmp_path) - 1] != '\\') + { + strcat(tmp_path, "\\"); + } + sprintf(fn,"%s%s",tmp_path,current_filename.c_str()); + } +#else + sprintf(fn,"/tmp/%s",current_filename.c_str()); +#endif + if ((fil = fopen(fn, "wb")) != NULL) + { + infil -> fread(&c,1,1); + while (!infil -> eof()) + { + if (out) + { + fwrite(&tempcmp[tc],1,1,fil); + } + tempcmp[tc] = c; + tc++; + if (tc >= l + extra) + { + tc = 0; + out = 1; + } + if (tc) + { + if (!strncmp(tempcmp + tc + extra, m_strBoundary.c_str(), l - tc) && + !strncmp(tempcmp, m_strBoundary.c_str() + l - tc, tc)) + { + break; + } + } + else + { + if (!strncmp(tempcmp + extra, m_strBoundary.c_str(), l)) + { + break; + } + } + infil -> fread(&c,1,1); + } + fclose(fil); + + cgi = new CGI(current_name,fn,fn); + m_cgi.push_back(cgi); + + strcpy(slask, m_strBoundary.c_str()); + infil -> fgets(slask + strlen(slask), TMPSIZE); // next line + } + else + { + // couldn't open file + break; + } + } + } + else + { + // Probably '--' + break; + } + } // while (!infil -> eof()) + delete[] slask; + } // if (m_strBoundary) + if (tempcmp) + { + delete[] tempcmp; + } + } + else + if (strstr(content_type.c_str(), "x-www-form-urlencoded")) + { + bool got_name = false; // tnx to FatherNitwit + int i = 0; + int cl = (int)content_length; + char c,chigh,clow; + char *slask = new char[TMPSIZE]; + m_current = m_cgi.end(); + + *name = 0; + + infil -> fread(&c,1,1); + cl--; + while (cl >= 0 && !infil -> eof()) + { + switch (c) + { + case '=': /* end of name */ + slask[i] = 0; + i = 0; + strcpy(name,slask); + got_name = true; + break; + case '&': /* end of value */ + slask[i] = 0; + i = 0; + if (got_name) + { + cgi = new CGI(name,slask); + got_name = false; + } + else + { + cgi = new CGI(slask, ""); + } + m_cgi.push_back(cgi); + break; + case '+': /* space */ + slask[i++] = ' '; + break; + case '%': /* hex value */ + infil -> fread(&chigh,1,1); + cl--; + chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0); + infil -> fread(&clow,1,1); + cl--; + clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0); + slask[i++] = (char)(chigh * 16 + clow); + break; + default: /* just another char */ + slask[i++] = c; + break; + } + // + if (cl > 0) + { + infil -> fread(&c,1,1); + } + cl--; + } + slask[i] = 0; + i = 0; + if (got_name) + { + cgi = new CGI(name,slask); + } + else + { + cgi = new CGI(slask, ""); + } + m_cgi.push_back(cgi); + delete[] slask; + } +} + + +// HttpdForm(buffer,l) -- request_method GET + +HttpdForm::HttpdForm(const std::string& buffer,size_t l) : raw(false) +{ + CGI *cgi = NULL; + char *slask = new char[TMPSIZE]; + char *name = new char[TMPSIZE]; + int i = 0; + char c,chigh,clow; + size_t ptr = 0; + bool got_name = false; + + m_current = m_cgi.end(); + + *name = 0; + + ptr = 0; + while (ptr < l) + { + c = buffer[ptr++]; + switch (c) + { + case '=': /* end of name */ + slask[i] = 0; + i = 0; + strcpy(name,slask); + got_name = true; + break; + case '&': /* end of value */ + slask[i] = 0; + i = 0; + if (got_name) + { + cgi = new CGI(name,slask); + got_name = false; + } + else + { + cgi = new CGI(slask, ""); + } + m_cgi.push_back(cgi); + break; + case '+': /* space */ + slask[i++] = ' '; + break; + case '%': /* hex value */ + chigh = buffer[ptr++]; + chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0); + clow = buffer[ptr++]; + clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0); + slask[i++] = (char)(chigh * 16 + clow); + break; + default: /* just another char */ + slask[i++] = c; + break; + } + } + slask[i] = 0; + i = 0; + if (got_name) + { + cgi = new CGI(name,slask); + } + else + { + cgi = new CGI(slask, ""); + } + m_cgi.push_back(cgi); + delete[] slask; + delete[] name; +} + + +HttpdForm::~HttpdForm() +{ + CGI *cgi = NULL; //,*tmp; + + for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + delete cgi; + } +} + + +void HttpdForm::EnableRaw(bool b) +{ + raw = b; +} + + +void HttpdForm::strcpyval(std::string& v,const char *value) const +{ + v = ""; + for (size_t i = 0; i < strlen(value); i++) + { + if (value[i] == '<') + { + v += "<"; + } + else + if (value[i] == '>') + { + v += ">"; + } + else + if (value[i] == '&') + { + v += "&"; + } + else + { + v += value[i]; + } + } +} + + +bool HttpdForm::getfirst(std::string& n) const +{ + m_current = m_cgi.begin(); + return getnext(n); +} + + +bool HttpdForm::getnext(std::string& n) const +{ + if (m_current != m_cgi.end() ) + { + CGI *current = *m_current; + n = current -> name; + m_current++; + return true; + } + else + { + n = ""; + } + return false; +} + + +bool HttpdForm::getfirst(std::string& n,std::string& v) const +{ + m_current = m_cgi.begin(); + return getnext(n,v); +} + + +bool HttpdForm::getnext(std::string& n,std::string& v) const +{ + if (m_current != m_cgi.end() ) + { + CGI *current = *m_current; + n = current -> name; + if (raw) + { + v = current -> value; + } + else + { + strcpyval(v,current -> value.c_str()); + } + m_current++; + return true; + } + else + { + n = ""; + } + return false; +} + + +int HttpdForm::getvalue(const std::string& n,std::string& v) const +{ + CGI *cgi = NULL; + int r = 0; + + for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + if (cgi -> name == n) + break; + cgi = NULL; + } + if (cgi) + { + if (raw) + { + v = cgi -> value; + } + else + { + strcpyval(v,cgi -> value.c_str()); + } + r++; + } + else + { + v = ""; + } + + return r; +} + + +std::string HttpdForm::getvalue(const std::string& n) const +{ + for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + CGI *cgi = *it; + if (cgi -> name == n) + { + return cgi -> value; + } + } + return ""; +} + + +size_t HttpdForm::getlength(const std::string& n) const +{ + CGI *cgi = NULL; + size_t l; + + for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + if (cgi -> name == n) + break; + cgi = NULL; + } + l = cgi ? cgi -> value.size() : 0; + if (cgi && !raw) + { + for (size_t i = 0; i < cgi -> value.size(); i++) + { + switch (cgi -> value[i]) + { + case '<': // < + case '>': // > + l += 4; + break; + case '&': // & + l += 5; + break; + } + } + } + return l; +} + + +HttpdForm::cgi_v& HttpdForm::getbase() +{ + return m_cgi; +} + + +const std::string& HttpdForm::GetBoundary() const +{ + return m_strBoundary; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpdForm.h b/Sockets/HttpdForm.h new file mode 100644 index 0000000..962a753 --- /dev/null +++ b/Sockets/HttpdForm.h @@ -0,0 +1,118 @@ +/** \file HttpdForm.h - read stdin, parse cgi input + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _SOCKETS_HttpdForm_H +#define _SOCKETS_HttpdForm_H + +#include "sockets-config.h" +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IFile; + +/** Parse/store a http query_string/form-data body. + \ingroup webserver */ +class HttpdForm +{ + /** + * Store the name/value pairs from a GET/POST operation. + * "name" does not have to be unique. + \ingroup webserver + */ + struct CGI + { + CGI(const std::string& n,const std::string& v) : name(n),value(v) {} + CGI(const std::string& n,const std::string& v,const std::string& p) : name(n),value(v),path(p) {} + std::string name; + std::string value; + std::string path; + }; + /** list of key/value pairs. */ + typedef std::list cgi_v; + +public: + /** + * Default constructor (used in POST operations). + * Input is read from stdin. Number of characters to read + * can be found in the environment variable CONTENT_LENGTH. + */ + HttpdForm(IFile *, const std::string& content_type, size_t content_length); + /** + * Another constructor (used in GET operations). + * Input is read from the environment variable QUERY_STRING. + * @param query_string The httpd server provided QUERY_STRING + * @param length Query string length. + */ + HttpdForm(const std::string& query_string,size_t length); + ~HttpdForm(); + + void EnableRaw(bool); + + /** Encode characters '<' '>' '&' as < > & */ + void strcpyval(std::string&,const char *) const; + + /* get names */ + bool getfirst(std::string& n) const; + bool getnext(std::string& n) const; + + /* get names and values */ + bool getfirst(std::string& n,std::string& v) const; + bool getnext(std::string& n,std::string& v) const; + + /* get value */ + int getvalue(const std::string& ,std::string& ) const; + std::string getvalue(const std::string& ) const; + size_t getlength(const std::string& ) const; + cgi_v& getbase(); + + const std::string& GetBoundary() const; + +private: + HttpdForm(const HttpdForm& ) {} + HttpdForm& operator=(const HttpdForm& ) { return *this; } + cgi_v m_cgi; + mutable cgi_v::const_iterator m_current; + std::string m_strBoundary; + bool raw; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpdForm_H + diff --git a/Sockets/HttpdSocket.cpp b/Sockets/HttpdSocket.cpp new file mode 100644 index 0000000..777dd07 --- /dev/null +++ b/Sockets/HttpdSocket.cpp @@ -0,0 +1,528 @@ +/** \file HttpdSocket.cpp +*/ +/* +Copyright (C) 2001-2007 Anders Hedstrom (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "Utility.h" +#include "HttpdCookies.h" +#include "HttpdForm.h" +#include "MemFile.h" +#include "HttpdSocket.h" +#include "Base64.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define TMPSIZE 10000 + + +// statics +int HttpdSocket::m_request_count = 0; +std::string HttpdSocket::m_start = ""; + + +HttpdSocket::HttpdSocket(ISocketHandler& h) +:HTTPSocket(h) +,m_content_length(0) +,m_file(NULL) +,m_received(0) +,m_request_id(++m_request_count) +,m_cookies(NULL) +,m_form(NULL) +{ + m_http_date = datetime2httpdate(GetDate()); + if (!m_start.size()) + m_start = m_http_date; +} + + +HttpdSocket::~HttpdSocket() +{ + if (m_file) + { + delete m_file; + } + if (m_cookies) + delete m_cookies; + if (m_form) + delete m_form; +} + + +void HttpdSocket::OnFirst() +{ +} + + +void HttpdSocket::OnHeader(const std::string& key,const std::string& value) +{ + if (!strcasecmp(key.c_str(),"content-length")) + { + m_content_length = atoi(value.c_str()); + m_content_length_str = value; + } + else + if (!strcasecmp(key.c_str(),"cookie")) + { + m_http_cookie = value; + } + else + if (!strcasecmp(key.c_str(),"content-type")) + { + m_content_type = value; + } + else + if (!strcasecmp(key.c_str(),"if-modified-since")) + { + m_if_modified_since = value; + } +} + + +void HttpdSocket::OnHeaderComplete() +{ + m_cookies = new HttpdCookies(m_http_cookie); + + if (GetMethod() == "GET") + { + Utility::SetEnv("QUERY_STRING", GetQueryString()); + } + Utility::SetEnv("REQUEST_METHOD", GetMethod()); + Utility::SetEnv("HTTP_COOKIE", m_http_cookie); + Utility::SetEnv("CONTENT_TYPE", m_content_type); + Utility::SetEnv("CONTENT_LENGTH", m_content_length_str); +/* +#if (defined(SOLARIS8) || defined(SOLARIS)) + { + char slask[TMPSIZE]; + if (GetMethod() == "GET") + { + sprintf(slask,"QUERY_STRING=%s", GetQueryString().c_str()); + putenv(slask); + } + sprintf(slask,"REQUEST_METHOD=%s", GetMethod().c_str()); + putenv(slask); + sprintf(slask,"HTTP_COOKIE=%s", m_http_cookie.c_str()); + putenv(slask); + sprintf(slask,"CONTENT_TYPE=%s", m_content_type.c_str()); + putenv(slask); + sprintf(slask,"CONTENT_LENGTH=%s", m_content_length_str.c_str()); + putenv(slask); + } +#elif defined _WIN32 + { + char slask[TMPSIZE]; + if (GetMethod() == "GET") + { + sprintf(slask,"QUERY_STRING=%s", GetQueryString().c_str()); + _putenv(slask); + } + sprintf(slask,"REQUEST_METHOD=%s", GetMethod().c_str()); + _putenv(slask); + sprintf(slask,"HTTP_COOKIE=%s", m_http_cookie.c_str()); + _putenv(slask); + sprintf(slask,"CONTENT_TYPE=%s", m_content_type.c_str()); + _putenv(slask); + sprintf(slask,"CONTENT_LENGTH=%s", m_content_length_str.c_str()); + _putenv(slask); + } +#else + if (GetMethod() == "GET") + { + setenv("QUERY_STRING", GetQueryString().c_str(), 1); + } + setenv("REQUEST_METHOD", GetMethod().c_str(), 1); + setenv("HTTP_COOKIE", m_http_cookie.c_str(), 1); + setenv("CONTENT_TYPE", m_content_type.c_str(), 1); + setenv("CONTENT_LENGTH", m_content_length_str.c_str(), 1); +#endif +*/ + if (GetMethod() == "POST") + { + m_file = new MemFile; + } + else + if (GetMethod() == "GET") + { + m_form = new HttpdForm(GetQueryString(), GetQueryString().size() ); + AddResponseHeader("Date", datetime2httpdate(GetDate()) ); + if (GetUri() == "/image") + { + std::string str64 = +"iVBORw0KGgoAAAANSUhEUgAAAGAAAABeCAIAAABTioayAAAACXBIWXMAAABkAAAAZAAPlsXdAAAAUHRF" +"WHRSYXcgcHJvZmlsZSB0eXBlIEFQUDEyAApnZW5lcmljIHByb2ZpbGUKICAgICAgMTUKNDQ3NTYzNmI3" +"OTAwMDEwMDA0MDAwMDAwM2MwMDAwCg2F1B0AABb4SURBVHja7Xx7rGVXed/3WGs/zvPeuTPXMzbjcUpq" +"HAwlpQlYoUlDaJsAiVuFFKSKGlUlKgXqIKJAKxfRR4IUZEpSt1FoQEK4oYQEErlSFAoKIEoo2BQasBmc" +"2NjyYzxz5957Hnufvfd6fF//2Oecez2hvbXvmZkg3U9b556Zc85ea//291rf+n0bVRWO5P8udLUn8Jdd" +"jgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QMxVHHtfoUWx" +"/dP+C5fvABGv4gwBAK9qPWg5NLbvrjIY302ujgapAmKrQQiqC3xAQQF0H1AIcyXSq4XeZdegfedfWs78" +"jYKAAgDi3sVri4mqzH+D9DSTu+I2dyVNTEFRl34GUTUikiqotrgoAqqIAhBxqzj7ZocAighXWJUuI0Cq" +"injJ+SMAIZKolkXlGrOzM61mjfckCoN+X9UjorU26wgb7HTSPAfDRlVVFQAR21NeOT26XAC1p10CpKoI" +"iKTO+ckkFpO4vVX1Bh3kkKRsDCIBE4sIMRNx8FTPYgwQfJ1k4ZpT3W4nAQBVQaSF/1pcw+VE63JrEKiC" +"KhChC83Fi6EYEyp2uzxYN4kFY6H1O63TIUQAiCIIEgK5Rke7zWhUE2a9AR+/JunmpIr7of/eA2hhWREA" +"QUlBkGg8af7s7E4n75+6Lu/2iBkUAgJLhP2+RkSJsNU5ACUCQCAyxTQ88eS0rt1gSGdOD621Ik8zs8uH" +"0eUCSDSqCDHGUD/2mCvLXpbWp051bEqqGoIAINHeVRHRJTMREQBEACQEUDa0vT07/2QZvbvxphO9vhGh" +"1m0j0uVzSysGaOF6QASJQvD4yKN1UeiZ053eULzX4FsspB221RdVZWYARaTFaeYXjIgiqqpEag16jw9+" +"e3daTH/gBevHjw1EoIXle0ODdJH/KQiCxiiPPOLrWX7taeh1o3ek8wQH26jUao1qa1ZPy6VbgNovtIAi" +"gCoZK17l3GPNdFLf8H2djY1cRAlJUfHyhP+VAbQM6gsPAt/404uzypw509vYoMYhKCHOQxsAiAohaqtL" +"OhdEUlAm8iEQEgAQIRHHGJGQkBSAjILyhXNaN9VzTuOgn6jOl9yXQ49WvJpXEFUhoAcf2t7apn7f5p04" +"K9XVXkVcE4IXiSoRUEkFVYiAEZnZMltmoxFUKAYFJRWQiK4JwUfvoncxBnUNKMTBMUVKzj2OdSNzXC6P" +"ka1mLbZMdkCQSbZ36q3z2Oma4yesAipwEI8iLkTD4GNEBGZGAEJSBUBQ0RAcqLBJBdRYC0gxKKqKqgrE" +"4FUFgNAY7xFAOx0a79KTT4bvu4FVlYAUFRVgpXp0WID2W6gqIMXGyeOPN73ORm8QrcUQRFUUTNNEAIyt" +"CxJ1PmQ2H093vvr1L0rUm5//N+5/4N7g4o/92Cs7/R6AiAIxi4AKsOGvf/3Ljz/x0F974S2nr7/RNd5a" +"MqSj8e60JMPl9ddvSBRiVlBYaURbgQYtXI+qAhKce7JxLuMkGquq3K63GA0YEBFEFAEFNWx8rP7nV75w" +"/sJjhs31z7n54vaWd3Vd10nWFQ1EBACERlERaHc0vrD1eNNEIkMkIURQNMY0Lj7+xGzz5CCxCMqwak/0" +"7H3QUnf2LZR0VoWHHh7PytnO9nnnQ1XVohoVIqioMBMRGEuGbZbar977xe88+tDx9ecURVNUoZgUs6Is" +"at80TfAxBAEFkdi40ASoqqqYTJomqoIoEhkyNs0StlRV9qlzJSGLCuy391XICjRoEbmEGR9/fFo10utC" +"jFrOast2Vk2NtYRoGANCkiRRJDHm22fPfvozf3jL33w1BXvx4v+alOV4NPLONT5KlCyzPsQIEHxwXtTR" +"aLQ7Gu0W5cw5EAVERKC800nyrndxNmucF8OrzxhXFcUQMLjGl1N7zanrTGKStEtkvJcYNAbxPjLbpglV" +"1YjAuacufPx37+72jv3A837iqYtPlbNpPXNFWU3GRftNERGVEGII4l10LpSzejwqyqKqZm46KVUpeA1e" +"isKRZe+SyaSZJ2ErlRVFMVBGGs2aAMYQ9fs9BXFumqdJmiaACCCzqlHwosY7+MQnP/LYYw/f9o/vEORi" +"Ot0d7c6qZjQeVVWTcjYYWCIBTBHBeW1qrWqqinp3tF3WTROVrXGunhTNeBTKWTOMvU7emUz1+IYCoEgk" +"4vnEDq1QzxKgpwcvbZfsFy5MQ+xaE0WAmLrdfpIgoRKRQlTVqm66nfSP/uj3P/PH/+0fvvbtJzb/Su0q" +"1/jJZFqU1WRazGY7H/3ob/R6HcTYRmtCCh6cl0e+862imDnvkUwIDag4J87HPLfBu3FwwetzTiVsCXSV" +"FaNnr0HL4AUAIhEVgzOjUeHzWgHyvEekWZIzY5TIDACwPjz2rW9/84MffN8PvvjlP/wjP1nVZac7dL6a" +"TkvX+GJaFdPxZz/7iRi0hR0ASZkIFCXv9WMjdV0llpUzUGFkaxVJJECSmrKYNnXsJAKSrDBpfMYALW/O" +"Eh1VJcCyno2mNRECYlPXzAwgT20V/W5qEzZs2NhmWrz/vf8u65x4zWtuZ+LhIMsTU1bFZFq4BqbT0ax0" +"r339e05ec7KtRFtjIWhVO+T0Dz555wP/+3M+oAIoAmOa5mLTTGIkgqIsbd6/OCnO9NciRAa+agDtR2oJ" +"kAiEIGTs5uZ6NZv1el0RiTEAQu0ccQYosQl33fUr933ts6dP3/C+975FoicGBNy+cFEizqpqVpZFUW2e" +"uOn0mTMqjoiYGaJOZ42xPR9pMhkVxSwEQJQonsgAKJMR0PX19cY51amE1sL2V74PZW6HctLt8CISI6hQ" +"nnYIaTjsqcJ4PBkOB8SAqDHGTqf7B7939+/8zm+dueH5SL3zF84bY0UFVMqicIJFUZSzWTkbAVVpKgRC" +"BIjqnEuzCNhUs3I2mznvptNadcyUEHOWdkSE2cQYm8apoggorcA3rwagef4sKCJRBNgIBFVjDB8/fgxQ" +"CAEMJIbP3n//r773jtPX3/zP3/Yh5swY7ORZFAEwH/iPb/va1/64CaFpmqZyvdycOG4tISEBooidVaFu" +"uqpSOwhBogICIGrTVE3tQpQQfZqmo9F0Y70nogrKtLJgf9gwL6KiAKBN02xv" +"T4hNJ82SxBpj806GKDFI1ci7/tVbtnYvvPvffHzj2BnndrMsURCM5D35RqtZ5byv66quK9FgjHZSawzPLYUQALwPABCjDIdZU9fW2DRLvZO6dgpS13WeZ7z4yQqT" +"oVVokGqMAgAhhG6WFeV4wIOqruumMgZ63WN3vu9f3PeVz7/lbXfe/MJbGr+1udlhQiT1XqsZGwoiERRANEQBVQARjcQUQqjrWhQBE5B51EeEPM+tsSKSJmysb1wd" +"Y0ySTGIZY0REYFxuq1w1gFpPqICiEqIqmMEgiYLdXj8EEY1BJOHhf/34f/7QB371lpfd+qpXvzHEyclret2cLRMAlI3vpmnVjGblyLtZiD4ExxiJmE1AVIkBAEV0" +"NqtCmAFAjI0hCKJISMgxQpJam1gFcFUTfIyArFHEECEAtstjOETGeDgTUxBRERWJqpLnSZKse+/KcrZ+bC3GEIKcP7f9s6+7/Wd++o2G0RqXGCOxVrLGMIJToB//" +"2z93zbUveN6NL/GvelOMs43jJ6whZgYAUcny1MUmRveyv/X31teve+73v3Bra5JkTlW982nWAQUEBERmTVKMIsv8bCVu+hmXXJel1dYxey/eh6ZxVdNs7QZrNpLE" +"MhMxAmhTBwhU" +"eDMa7wxz7PWstY21lCZp0zREtDtGHzuzWspxk3XTTm7zxB8/Dnlm5gOBzup44byrfVo3ShAsl3mPCCnG2FaL8jzzPjDJcDAb9oeJpcRaZiZCxMPuxj4zDbp0N1nb" +"/wQRReRQz8qwneVZr9dLOQHQKN4FCUFOHM97nTSxIU1SQxo8MkdgyDLV2Yyk6fWU2Tvv+r0OmVxViAgBQwQmO1hT2fWMIc0MU56kxrkmhIgISnFnvBMa6XZ0Y70b" +"JBplFQReDaPmUKt5XToiRRFNU5zNJiJxe3tre3u7KIrZrDQGT2z0+13D7NfWkk996g/f855f3tiwSWKtMYNB3uvRqVPZ5qY9ccKcvKY/HOS+abwPTdOUsxJQAcQa" +"3Niwv/4f7rh48dGN4700tb1+59ix9SxNsyzPsk6ScJoRES+LVIvjsLKiehBgjJLmJkmwqmaIEKOv6zgcDpxzdTUeDrKsY/MMyun2nz14v4+QWOyvZ9NCT5ywk0kw" +"hGxoPK0Iw/qgU85ciLC+1i3ruH7c7OzG8U519ptfrsvbmAFJCEkAu71OFLVJXnG0SSNRwPJyi2QlnKJnpkFPN+aWyoLzaSARcmIBxK2tHcvzPM1s42rF2Ol0vnX/" +"N19766t+6idv/dR//7RNekUDX/of99766p/+qZff8uZ/+iZj+MMf/M3f/ujdx453Bv38Xe+648v33hsovPGNb3jlT7z0H/z9n/3zBx6KkhCa7dHk4siNp6WPMai4" +"GIJGwKDqDBgFBMW2ugmEK/HShzAxvfStqnY6eaebpanpdPJer9fr9YbD9aIs3vGOn7/x+T/0mte99fOf+0JZlI88svvuf/3OW156653v/y+TIv7Ld75z89RzP/yh" +"3woOzn774XvuuefMDX/17W+9vdM99f67fv+vv/jl7/iltzS1AyARcc4XRbmzuzuZTMbjcTWrfFNp8IbpcvAwnrGTXr7uV19cfGoMV43b2dkarh/LbYaIaUbfeejP" +"LfX/2Vt/hTJ8/W1vfujBb9x371cfefTs2Qfvu/9bX5lOii//yeff8E/eXjf1ffc++KUvfvpFP/gSH8yXvvCZH/nRV9z1n37ZED7y8NlvfPMBm6RENktTiUmU2B5N" +"MfFVsTHs4JxedVUBgqfxTlodVkREQiREJNVojbm4s9sd9keTOs+zaRUn4wJIHYhR7vWG1iaN4zw//tznvnQ8Lq6/nl70opcxnXzRi3/8Yx/78MMPPvCKV77uyScK" +"Qnvt6RcgZszZbT//4sHGZhN8mvY6HZOmawIymkzzJCsnjliSNBVtAzoSLXahV+GEDluTnk8KkJCIkEANwfra4PxT57Z3dra2tqbj0ckzN06K3Y/d/WtPPPrgPb97" +"dzGbfP+NNyMZSjqv+pk31BL/9IGvKcdXvOLnPvHxDzx57tGbnv+jtjvcOHndaLv4O3/39Zsbx//kc/cQpDF4xBgjxKigOuh1DSFIHAyGCsDMuIBowf68Ghr0NHSW" +"irTIxwgBVQyhihiDKupjmfU2b//FX/vNf3/Hpz/1yTPPu3FtfXNtbfNNb373Rz5y529/+D3Dwdrtb/+3onLTTT/0kh9+5clrT2+evLZ201/4xbt+49d/6Rfe/HsR" +"4bZ/9M5ud2392HUmSceTqTWapMY19Xh31xhuw8RyDojz97CKKPbMMuk9hgIAqMYoPkgIsWl81YSmcXXtnYshiCiUVd3t9fP+cGen9JK6Wpxz3W7X+4CIAJQwjXYv" +"bhw/Nhx0ETUEr5CIRiZFgBDZC168uGWTZH1tfXd0AQkG/Rx02s3T3e1t72O/lyeGU8M2NVlms4ST1GRpkjCzQaI5P+SqFcwAgWhu98zExMzCDDEqqnbSrKkbonrQ" +"607KWQOBGVRdmggzxhgQ0sFaDzE0bjwcDijgaLzT7XXSxDCR81HqsLm5joiI/sRG3/k6S7GqtWncrCz7vV7CbFqaDBG1eLTri7lSH1p/njVAC2p3a1ZKRERMFJmZ" +"SIkQBFTVIJbTadaV4SDLsgBIWZ4TikosyzKx1DQ+SQkBkDyA63Sp3zej8e7aYNhN" +"E05w0aQACJTHFBGYOuPR7nA4yJIENBIxMzK1xwKhuXmtAKFD7WoAzkMYEREJMzMLMRCRkIAAgaYWm2rqfTUc9m3CXqssyQhtt7NujPU+IAKSVFWVZMYIGAOdLDWG" +"i2ICACFERMzzXCCyyqwoZ0XFDIYYRdkYYiIGNkgEhpEJiYB4CdBhI9khlxoICETErMzIBjkSMxsDqggiURRUEyYf/YXz59IsTfJMYrDWpmnqQ8VMbAwADQd9UW2a" +"RlUUYlFOo8S6rtsklFgIzWh7R6MYTgAEoR2XmdtXNmwMG95nYiuRw0axNlwwozEkEcPcD0GMGgnaOiGAGkKTdmdlHZq65BJIh8Oh840PfmNjw9qWA62EqBrTNEGi" +"siyzPGFIxuPx1JfqAwJYNqCKCIw0d3zMhtttJSbGuYXNHZAeXomeGUBLYmXLgSYijUogimCIhMgyBKMqGiOqIiIHRVBREUTpdVIVqYMGH3cu7HgNaZ5d3NoZrq05" +"77zz62trTV1bm0gUDca5xte7KgKItuV3LsYlanUHmckaNoaMIUJc1IBWoz7PUoP2ZQZKBAJEIkjEzMaiVVElEdMypZhBZP4rEQGANElSRBWJIhrBVc1Ove28Q8RY" +"haIsOnmH0bZfZgbLRkXaaN2GLGY2xliLxpJN2FhmA8xIzEiEuMo1x7MBaLHawAVNGZCQW1IUoFVVAYmgiqpxUVTbIxPh3O7AkgGANLeimpqEEKPIsDdUEcaIBubE" +"TpnT69oYBQDMxIaY0RhjDVvDlts8A1ur3zfJw3qjFVDw2m4mImKGKGqYhUEtAMRFJQQAQaIsd0HaQonu8Z3mbHsCBInYEvD2XRsSIszTHWPYWDaGrGVrrbXGEhkm" +"g0SkiwxoZezdZ5sHPb0w1IZ7JmajAmqVFFnm21OMqAARkVQgxgiw7Kab3+OFz8BFjaldNMByqbe0LGZmA3PjssYYYoPGEDMiQbtiRpx35V3ltdg+QwNEIAAlMECw" +"10CICIEQiABAJUIUbQmtqm2nwZ6zWNxwwkV/Ai5Aovk6GA2zMcSGbELWmsQYYykxzKZNf9r4vr9n76omirAvnC3nxAjAtOCLIyoQARIgYowaYqQAIiBRFz0sqqD7" +"am+I+5WH2hxiniAbg8aSNdZYtoYSIsNsGKmNa4gAArrSGHZ4H7QPo7YnDIgVgBRVERNgDG3sjRikLYSKgJCIggrqJYRLFNhvVy0TcZEQGottOmgNW0OGqTUuwqU5" +"4cpb7lZCwZt7IiKAFhhSQEbECNrmJoTIJMIUAkdZCqqAzjf3W19Ne9pD86yPGYnJEBlLxpBhYwwZg0xA7TGvbyxnskom8Ep40stZKQEqoKIAKBChUSRAAgJgJolq" +"WH2MIlFUVEi17VFddnjMzYsIFot0ZAZitMxsyDAaZDZIDLioJQAoos490F9OEmcr863QFhqNhIotNoREFEVjUCIlQVGKIhpx2ciyNLNFgbJ1K+16AucHATOaeWFj" +"rzxGcwtfrfNZNUB7ffCgiECttpMitMUPYQJBiCRRMLaaIyAK836fvS2kdqt9bmI8RwqJgQiZieep6Tx3aksul6+xdJUatLyBrZlw6ywVkQCh7elSZozCoqqCCu0e" +"P+3xefZyn7k/MqhtcjNXJ8KFNQG2vAVcphp7r6u8qMuB/b7esbYFF1RVZb5vFURk7nsIVEV0uaEluFeraAFibJkuS+Rg8bnuux8rYwNdIYBgX/chwDy2qMCCM4xz" +"y4I2c95rFtzPN1jokS42BeZnJmrLKC0ul8XvXAmAYMlZaiGY3+TWHWP7CIa9msBfmALCHJe9dcjyKRZIl7SqfI+1hV+CUXu5oHvE8z2a9SJz0Zb7vO/JHrjvKRSL" +"h5vsQ2+Oy+Wb+JUC6LuAden47Wf/HxO9Igb1Xca9ks8PWjjvxXV+F7X4f/12/sUrC9KVfsDSpcPtIbX/sq+aQf1FubpPoPoekKOHvB0gRwAdIEcAHSBHAB0gRwAd" +"IEcAHSBHAB0gRwAdIEcAHSBHAB0gRwAdIP8HqHccFP4Rq4QAAAAASUVORK5CYII="; + Send64(str64, "image/png"); + } + else + { + Exec(); + } + Reset(); // prepare for next request + } + else + { + AddResponseHeader("Date", GetHttpDate()); + AddResponseHeader("Connection", "close"); + SetStatus("405"); + SetStatusText("Method not allowed"); + SendResponse(); + } +} + + +void HttpdSocket::OnData(const char *p,size_t l) +{ + if (m_file) + { + m_file -> fwrite(p,1,l); + } + m_received += l; + if (m_received >= m_content_length && m_content_length) + { + // all done + if (m_file && !m_form) + { + m_form = new HttpdForm(m_file, m_content_type, m_content_length); + AddResponseHeader("Date", datetime2httpdate(GetDate()) ); + if (GetUri() == "/image") + { + std::string str64 = +"iVBORw0KGgoAAAANSUhEUgAAAGAAAABeCAIAAABTioayAAAACXBIWXMAAABkAAAAZAAPlsXdAAAAUHRF" +"WHRSYXcgcHJvZmlsZSB0eXBlIEFQUDEyAApnZW5lcmljIHByb2ZpbGUKICAgICAgMTUKNDQ3NTYzNmI3" +"OTAwMDEwMDA0MDAwMDAwM2MwMDAwCg2F1B0AABb4SURBVHja7Xx7rGVXed/3WGs/zvPeuTPXMzbjcUpq" +"HAwlpQlYoUlDaJsAiVuFFKSKGlUlKgXqIKJAKxfRR4IUZEpSt1FoQEK4oYQEErlSFAoKIEoo2BQasBmc" +"2NjyYzxz5957Hnufvfd6fF//2Oecez2hvbXvmZkg3U9b556Zc85ea//291rf+n0bVRWO5P8udLUn8Jdd" +"jgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QI4AOkCOADpAjgA6QMxVHHtfoUWx" +"/dP+C5fvABGv4gwBAK9qPWg5NLbvrjIY302ujgapAmKrQQiqC3xAQQF0H1AIcyXSq4XeZdegfedfWs78" +"jYKAAgDi3sVri4mqzH+D9DSTu+I2dyVNTEFRl34GUTUikiqotrgoAqqIAhBxqzj7ZocAighXWJUuI0Cq" +"injJ+SMAIZKolkXlGrOzM61mjfckCoN+X9UjorU26wgb7HTSPAfDRlVVFQAR21NeOT26XAC1p10CpKoI" +"iKTO+ckkFpO4vVX1Bh3kkKRsDCIBE4sIMRNx8FTPYgwQfJ1k4ZpT3W4nAQBVQaSF/1pcw+VE63JrEKiC" +"KhChC83Fi6EYEyp2uzxYN4kFY6H1O63TIUQAiCIIEgK5Rke7zWhUE2a9AR+/JunmpIr7of/eA2hhWREA" +"QUlBkGg8af7s7E4n75+6Lu/2iBkUAgJLhP2+RkSJsNU5ACUCQCAyxTQ88eS0rt1gSGdOD621Ik8zs8uH" +"0eUCSDSqCDHGUD/2mCvLXpbWp051bEqqGoIAINHeVRHRJTMREQBEACQEUDa0vT07/2QZvbvxphO9vhGh" +"1m0j0uVzSysGaOF6QASJQvD4yKN1UeiZ053eULzX4FsspB221RdVZWYARaTFaeYXjIgiqqpEag16jw9+" +"e3daTH/gBevHjw1EoIXle0ODdJH/KQiCxiiPPOLrWX7taeh1o3ek8wQH26jUao1qa1ZPy6VbgNovtIAi" +"gCoZK17l3GPNdFLf8H2djY1cRAlJUfHyhP+VAbQM6gsPAt/404uzypw509vYoMYhKCHOQxsAiAohaqtL" +"OhdEUlAm8iEQEgAQIRHHGJGQkBSAjILyhXNaN9VzTuOgn6jOl9yXQ49WvJpXEFUhoAcf2t7apn7f5p04" +"K9XVXkVcE4IXiSoRUEkFVYiAEZnZMltmoxFUKAYFJRWQiK4JwUfvoncxBnUNKMTBMUVKzj2OdSNzXC6P" +"ka1mLbZMdkCQSbZ36q3z2Oma4yesAipwEI8iLkTD4GNEBGZGAEJSBUBQ0RAcqLBJBdRYC0gxKKqKqgrE" +"4FUFgNAY7xFAOx0a79KTT4bvu4FVlYAUFRVgpXp0WID2W6gqIMXGyeOPN73ORm8QrcUQRFUUTNNEAIyt" +"CxJ1PmQ2H093vvr1L0rUm5//N+5/4N7g4o/92Cs7/R6AiAIxi4AKsOGvf/3Ljz/x0F974S2nr7/RNd5a" +"MqSj8e60JMPl9ddvSBRiVlBYaURbgQYtXI+qAhKce7JxLuMkGquq3K63GA0YEBFEFAEFNWx8rP7nV75w" +"/sJjhs31z7n54vaWd3Vd10nWFQ1EBACERlERaHc0vrD1eNNEIkMkIURQNMY0Lj7+xGzz5CCxCMqwak/0" +"7H3QUnf2LZR0VoWHHh7PytnO9nnnQ1XVohoVIqioMBMRGEuGbZbar977xe88+tDx9ecURVNUoZgUs6Is" +"at80TfAxBAEFkdi40ASoqqqYTJomqoIoEhkyNs0StlRV9qlzJSGLCuy391XICjRoEbmEGR9/fFo10utC" +"jFrOast2Vk2NtYRoGANCkiRRJDHm22fPfvozf3jL33w1BXvx4v+alOV4NPLONT5KlCyzPsQIEHxwXtTR" +"aLQ7Gu0W5cw5EAVERKC800nyrndxNmucF8OrzxhXFcUQMLjGl1N7zanrTGKStEtkvJcYNAbxPjLbpglV" +"1YjAuacufPx37+72jv3A837iqYtPlbNpPXNFWU3GRftNERGVEGII4l10LpSzejwqyqKqZm46KVUpeA1e" +"isKRZe+SyaSZJ2ErlRVFMVBGGs2aAMYQ9fs9BXFumqdJmiaACCCzqlHwosY7+MQnP/LYYw/f9o/vEORi" +"Ot0d7c6qZjQeVVWTcjYYWCIBTBHBeW1qrWqqinp3tF3WTROVrXGunhTNeBTKWTOMvU7emUz1+IYCoEgk" +"4vnEDq1QzxKgpwcvbZfsFy5MQ+xaE0WAmLrdfpIgoRKRQlTVqm66nfSP/uj3P/PH/+0fvvbtJzb/Su0q" +"1/jJZFqU1WRazGY7H/3ob/R6HcTYRmtCCh6cl0e+862imDnvkUwIDag4J87HPLfBu3FwwetzTiVsCXSV" +"FaNnr0HL4AUAIhEVgzOjUeHzWgHyvEekWZIzY5TIDACwPjz2rW9/84MffN8PvvjlP/wjP1nVZac7dL6a" +"TkvX+GJaFdPxZz/7iRi0hR0ASZkIFCXv9WMjdV0llpUzUGFkaxVJJECSmrKYNnXsJAKSrDBpfMYALW/O" +"Eh1VJcCyno2mNRECYlPXzAwgT20V/W5qEzZs2NhmWrz/vf8u65x4zWtuZ+LhIMsTU1bFZFq4BqbT0ax0" +"r339e05ec7KtRFtjIWhVO+T0Dz555wP/+3M+oAIoAmOa5mLTTGIkgqIsbd6/OCnO9NciRAa+agDtR2oJ" +"kAiEIGTs5uZ6NZv1el0RiTEAQu0ccQYosQl33fUr933ts6dP3/C+975FoicGBNy+cFEizqpqVpZFUW2e" +"uOn0mTMqjoiYGaJOZ42xPR9pMhkVxSwEQJQonsgAKJMR0PX19cY51amE1sL2V74PZW6HctLt8CISI6hQ" +"nnYIaTjsqcJ4PBkOB8SAqDHGTqf7B7939+/8zm+dueH5SL3zF84bY0UFVMqicIJFUZSzWTkbAVVpKgRC" +"BIjqnEuzCNhUs3I2mznvptNadcyUEHOWdkSE2cQYm8apoggorcA3rwagef4sKCJRBNgIBFVjDB8/fgxQ" +"CAEMJIbP3n//r773jtPX3/zP3/Yh5swY7ORZFAEwH/iPb/va1/64CaFpmqZyvdycOG4tISEBooidVaFu" +"uqpSOwhBogICIGrTVE3tQpQQfZqmo9F0Y70nogrKtLJgf9gwL6KiAKBN02xv" +"T4hNJ82SxBpj806GKDFI1ci7/tVbtnYvvPvffHzj2BnndrMsURCM5D35RqtZ5byv66quK9FgjHZSawzPLYUQALwPABCjDIdZU9fW2DRLvZO6dgpS13WeZ7z4yQqT" +"oVVokGqMAgAhhG6WFeV4wIOqruumMgZ63WN3vu9f3PeVz7/lbXfe/MJbGr+1udlhQiT1XqsZGwoiERRANEQBVQARjcQUQqjrWhQBE5B51EeEPM+tsSKSJmysb1wd" +"Y0ySTGIZY0REYFxuq1w1gFpPqICiEqIqmMEgiYLdXj8EEY1BJOHhf/34f/7QB371lpfd+qpXvzHEyclret2cLRMAlI3vpmnVjGblyLtZiD4ExxiJmE1AVIkBAEV0" +"NqtCmAFAjI0hCKJISMgxQpJam1gFcFUTfIyArFHEECEAtstjOETGeDgTUxBRERWJqpLnSZKse+/KcrZ+bC3GEIKcP7f9s6+7/Wd++o2G0RqXGCOxVrLGMIJToB//" +"2z93zbUveN6NL/GvelOMs43jJ6whZgYAUcny1MUmRveyv/X31teve+73v3Bra5JkTlW982nWAQUEBERmTVKMIsv8bCVu+hmXXJel1dYxey/eh6ZxVdNs7QZrNpLE" +"MhMxAmhTBwhU" +"eDMa7wxz7PWstY21lCZp0zREtDtGHzuzWspxk3XTTm7zxB8/Dnlm5gOBzup44byrfVo3ShAsl3mPCCnG2FaL8jzzPjDJcDAb9oeJpcRaZiZCxMPuxj4zDbp0N1nb" +"/wQRReRQz8qwneVZr9dLOQHQKN4FCUFOHM97nTSxIU1SQxo8MkdgyDLV2Yyk6fWU2Tvv+r0OmVxViAgBQwQmO1hT2fWMIc0MU56kxrkmhIgISnFnvBMa6XZ0Y70b" +"JBplFQReDaPmUKt5XToiRRFNU5zNJiJxe3tre3u7KIrZrDQGT2z0+13D7NfWkk996g/f855f3tiwSWKtMYNB3uvRqVPZ5qY9ccKcvKY/HOS+abwPTdOUsxJQAcQa" +"3Niwv/4f7rh48dGN4700tb1+59ix9SxNsyzPsk6ScJoRES+LVIvjsLKiehBgjJLmJkmwqmaIEKOv6zgcDpxzdTUeDrKsY/MMyun2nz14v4+QWOyvZ9NCT5ywk0kw" +"hGxoPK0Iw/qgU85ciLC+1i3ruH7c7OzG8U519ptfrsvbmAFJCEkAu71OFLVJXnG0SSNRwPJyi2QlnKJnpkFPN+aWyoLzaSARcmIBxK2tHcvzPM1s42rF2Ol0vnX/" +"N19766t+6idv/dR//7RNekUDX/of99766p/+qZff8uZ/+iZj+MMf/M3f/ujdx453Bv38Xe+648v33hsovPGNb3jlT7z0H/z9n/3zBx6KkhCa7dHk4siNp6WPMai4" +"GIJGwKDqDBgFBMW2ugmEK/HShzAxvfStqnY6eaebpanpdPJer9fr9YbD9aIs3vGOn7/x+T/0mte99fOf+0JZlI88svvuf/3OW156653v/y+TIv7Ld75z89RzP/yh" +"3woOzn774XvuuefMDX/17W+9vdM99f67fv+vv/jl7/iltzS1AyARcc4XRbmzuzuZTMbjcTWrfFNp8IbpcvAwnrGTXr7uV19cfGoMV43b2dkarh/LbYaIaUbfeejP" +"LfX/2Vt/hTJ8/W1vfujBb9x371cfefTs2Qfvu/9bX5lOii//yeff8E/eXjf1ffc++KUvfvpFP/gSH8yXvvCZH/nRV9z1n37ZED7y8NlvfPMBm6RENktTiUmU2B5N" +"MfFVsTHs4JxedVUBgqfxTlodVkREQiREJNVojbm4s9sd9keTOs+zaRUn4wJIHYhR7vWG1iaN4zw//tznvnQ8Lq6/nl70opcxnXzRi3/8Yx/78MMPPvCKV77uyScK" +"Qnvt6RcgZszZbT//4sHGZhN8mvY6HZOmawIymkzzJCsnjliSNBVtAzoSLXahV+GEDluTnk8KkJCIkEANwfra4PxT57Z3dra2tqbj0ckzN06K3Y/d/WtPPPrgPb97" +"dzGbfP+NNyMZSjqv+pk31BL/9IGvKcdXvOLnPvHxDzx57tGbnv+jtjvcOHndaLv4O3/39Zsbx//kc/cQpDF4xBgjxKigOuh1DSFIHAyGCsDMuIBowf68Ghr0NHSW" +"irTIxwgBVQyhihiDKupjmfU2b//FX/vNf3/Hpz/1yTPPu3FtfXNtbfNNb373Rz5y529/+D3Dwdrtb/+3onLTTT/0kh9+5clrT2+evLZ201/4xbt+49d/6Rfe/HsR" +"4bZ/9M5ud2392HUmSceTqTWapMY19Xh31xhuw8RyDojz97CKKPbMMuk9hgIAqMYoPkgIsWl81YSmcXXtnYshiCiUVd3t9fP+cGen9JK6Wpxz3W7X+4CIAJQwjXYv" +"bhw/Nhx0ETUEr5CIRiZFgBDZC168uGWTZH1tfXd0AQkG/Rx02s3T3e1t72O/lyeGU8M2NVlms4ST1GRpkjCzQaI5P+SqFcwAgWhu98zExMzCDDEqqnbSrKkbonrQ" +"607KWQOBGVRdmggzxhgQ0sFaDzE0bjwcDijgaLzT7XXSxDCR81HqsLm5joiI/sRG3/k6S7GqtWncrCz7vV7CbFqaDBG1eLTri7lSH1p/njVAC2p3a1ZKRERMFJmZ" +"SIkQBFTVIJbTadaV4SDLsgBIWZ4TikosyzKx1DQ+SQkBkDyA63Sp3zej8e7aYNhN" +"E05w0aQACJTHFBGYOuPR7nA4yJIENBIxMzK1xwKhuXmtAKFD7WoAzkMYEREJMzMLMRCRkIAAgaYWm2rqfTUc9m3CXqssyQhtt7NujPU+IAKSVFWVZMYIGAOdLDWG" +"i2ICACFERMzzXCCyyqwoZ0XFDIYYRdkYYiIGNkgEhpEJiYB4CdBhI9khlxoICETErMzIBjkSMxsDqggiURRUEyYf/YXz59IsTfJMYrDWpmnqQ8VMbAwADQd9UW2a" +"RlUUYlFOo8S6rtsklFgIzWh7R6MYTgAEoR2XmdtXNmwMG95nYiuRw0axNlwwozEkEcPcD0GMGgnaOiGAGkKTdmdlHZq65BJIh8Oh840PfmNjw9qWA62EqBrTNEGi" +"siyzPGFIxuPx1JfqAwJYNqCKCIw0d3zMhtttJSbGuYXNHZAeXomeGUBLYmXLgSYijUogimCIhMgyBKMqGiOqIiIHRVBREUTpdVIVqYMGH3cu7HgNaZ5d3NoZrq05" +"77zz62trTV1bm0gUDca5xte7KgKItuV3LsYlanUHmckaNoaMIUJc1IBWoz7PUoP2ZQZKBAJEIkjEzMaiVVElEdMypZhBZP4rEQGANElSRBWJIhrBVc1Ove28Q8RY" +"haIsOnmH0bZfZgbLRkXaaN2GLGY2xliLxpJN2FhmA8xIzEiEuMo1x7MBaLHawAVNGZCQW1IUoFVVAYmgiqpxUVTbIxPh3O7AkgGANLeimpqEEKPIsDdUEcaIBubE" +"TpnT69oYBQDMxIaY0RhjDVvDlts8A1ur3zfJw3qjFVDw2m4mImKGKGqYhUEtAMRFJQQAQaIsd0HaQonu8Z3mbHsCBInYEvD2XRsSIszTHWPYWDaGrGVrrbXGEhkm" +"g0SkiwxoZezdZ5sHPb0w1IZ7JmajAmqVFFnm21OMqAARkVQgxgiw7Kab3+OFz8BFjaldNMByqbe0LGZmA3PjssYYYoPGEDMiQbtiRpx35V3ltdg+QwNEIAAlMECw" +"10CICIEQiABAJUIUbQmtqm2nwZ6zWNxwwkV/Ai5Aovk6GA2zMcSGbELWmsQYYykxzKZNf9r4vr9n76omirAvnC3nxAjAtOCLIyoQARIgYowaYqQAIiBRFz0sqqD7" +"am+I+5WH2hxiniAbg8aSNdZYtoYSIsNsGKmNa4gAArrSGHZ4H7QPo7YnDIgVgBRVERNgDG3sjRikLYSKgJCIggrqJYRLFNhvVy0TcZEQGottOmgNW0OGqTUuwqU5" +"4cpb7lZCwZt7IiKAFhhSQEbECNrmJoTIJMIUAkdZCqqAzjf3W19Ne9pD86yPGYnJEBlLxpBhYwwZg0xA7TGvbyxnskom8Ep40stZKQEqoKIAKBChUSRAAgJgJolq" +"WH2MIlFUVEi17VFddnjMzYsIFot0ZAZitMxsyDAaZDZIDLioJQAoos490F9OEmcr863QFhqNhIotNoREFEVjUCIlQVGKIhpx2ciyNLNFgbJ1K+16AucHATOaeWFj" +"rzxGcwtfrfNZNUB7ffCgiECttpMitMUPYQJBiCRRMLaaIyAK836fvS2kdqt9bmI8RwqJgQiZieep6Tx3aksul6+xdJUatLyBrZlw6ywVkQCh7elSZozCoqqCCu0e" +"P+3xefZyn7k/MqhtcjNXJ8KFNQG2vAVcphp7r6u8qMuB/b7esbYFF1RVZb5vFURk7nsIVEV0uaEluFeraAFibJkuS+Rg8bnuux8rYwNdIYBgX/chwDy2qMCCM4xz" +"y4I2c95rFtzPN1jokS42BeZnJmrLKC0ul8XvXAmAYMlZaiGY3+TWHWP7CIa9msBfmALCHJe9dcjyKRZIl7SqfI+1hV+CUXu5oHvE8z2a9SJz0Zb7vO/JHrjvKRSL" +"h5vsQ2+Oy+Wb+JUC6LuAden47Wf/HxO9Igb1Xca9ks8PWjjvxXV+F7X4f/12/sUrC9KVfsDSpcPtIbX/sq+aQf1FubpPoPoekKOHvB0gRwAdIEcAHSBHAB0gRwAd" +"IEcAHSBHAB0gRwAdIEcAHSBHAB0gRwAdIP8HqHccFP4Rq4QAAAAASUVORK5CYII="; + Send64(str64, "image/png"); + } + else + { + Exec(); + } + Reset(); // prepare for next request + } + } +} + + +void HttpdSocket::Send64(const std::string& str64, const std::string& type) +{ + Base64 bb; + + if (!strcasecmp(m_start.c_str(), m_if_modified_since.c_str())) + { + SetStatus("304"); + SetStatusText("Not Modified"); + SendResponse(); + } + else + { + size_t len = bb.decode_length(str64); + unsigned char *buf = new unsigned char[len]; + + SetStatus("200"); + SetStatusText("OK"); + + AddResponseHeader("Content-length", Utility::l2string( (long)len) ); + AddResponseHeader("Content-type", type ); + AddResponseHeader("Last-modified", m_start); + SendResponse(); + + bb.decode(str64, buf, len); + SendBuf( (char *)buf, len); + delete[] buf; + } +} + + +std::string HttpdSocket::datetime2httpdate(const std::string& dt) +{ + struct tm tp; + time_t t; + const char *days[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; + const char *months[] = { "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" }; + int i; + char s[40]; + +/* 1997-12-16 09:50:40 */ + + if (dt.size() == 19) + { + tp.tm_year = atoi(dt.substr(0,4).c_str()) - 1900; + i = atoi(dt.substr(5,2).c_str()) - 1; + tp.tm_mon = i >= 0 ? i : 0; + tp.tm_mday = atoi(dt.substr(8,2).c_str()); + tp.tm_hour = atoi(dt.substr(11,2).c_str()); + tp.tm_min = atoi(dt.substr(14,2).c_str()); + tp.tm_sec = atoi(dt.substr(17,2).c_str()); + tp.tm_wday = 0; + tp.tm_yday = 0; + tp.tm_isdst = 0; + t = mktime(&tp); + if (t == -1) + { + Handler().LogError(this, "datetime2httpdate", 0, "mktime() failed"); + } + + sprintf(s,"%s, %02d %s %d %02d:%02d:%02d GMT", + days[tp.tm_wday], + tp.tm_mday, + months[tp.tm_mon], + tp.tm_year + 1900, + tp.tm_hour,tp.tm_min,tp.tm_sec); + } + else + { + *s = 0; + } + return s; +} + + +std::string HttpdSocket::GetDate() +{ + time_t t = time(NULL); + struct tm tp; +#ifdef _WIN32 + memcpy(&tp, localtime(&t), sizeof(tp)); +#else + localtime_r(&t, &tp); +#endif + char slask[40]; // yyyy-mm-dd hh:mm:ss + sprintf(slask,"%d-%02d-%02d %02d:%02d:%02d", + tp.tm_year + 1900, + tp.tm_mon + 1, + tp.tm_mday, + tp.tm_hour,tp.tm_min,tp.tm_sec); + return slask; +} + + +void HttpdSocket::Reset() +{ + HTTPSocket::Reset(); + m_content_length = 0; + if (m_file) + { + delete m_file; + m_file = NULL; + } + m_received = 0; + m_request_id = ++m_request_count; + if (m_cookies) + delete m_cookies; + m_cookies = NULL; + if (m_form) + delete m_form; + m_form = NULL; +} + + +const std::string& HttpdSocket::GetHttpDate() const +{ + return m_http_date; +} + + +HttpdCookies *HttpdSocket::GetCookies() +{ + return m_cookies; +} + + +const HttpdForm *HttpdSocket::GetForm() const +{ + return m_form; +} + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/HttpdSocket.h b/Sockets/HttpdSocket.h new file mode 100644 index 0000000..c4d10f5 --- /dev/null +++ b/Sockets/HttpdSocket.h @@ -0,0 +1,106 @@ +/** \file HttpdSocket.h +*/ +/* +Copyright (C) 2001-2007 Anders Hedstrom (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_HttpdSocket_H +#define _SOCKETS_HttpdSocket_H + +#include "sockets-config.h" +#include "HTTPSocket.h" +#include "ISocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpdCookies; +class HttpdForm; +class IFile; + +/** \defgroup webserver Webserver framework */ +/** Web server socket framework. + \ingroup webserver */ +class HttpdSocket : public HTTPSocket +{ +public: + HttpdSocket(ISocketHandler& ); + ~HttpdSocket(); + + void OnFirst(); + void OnHeader(const std::string& key,const std::string& value); + void OnHeaderComplete(); + void OnData(const char *,size_t); + + /** This method needs to be implemented with logic to produce + a response to an incoming request. */ + virtual void Exec() = 0; + /** Get current date in http rfc format. */ + const std::string& GetHttpDate() const; + /** Get pointer to cookie class. */ + HttpdCookies *GetCookies(); + /** Get pointer to query string/form data class. */ + const HttpdForm *GetForm() const; + + size_t ContentLength() const { return m_content_length; } + const IFile *Body() const { return m_file; } + int RequestId() const { return m_request_id; } + +protected: + HttpdSocket(const HttpdSocket& s) : HTTPSocket(s) {} + /** Decode and send a base64-encoded string. + \param str64 Base64-encoded string + \param type Mime type of content (content-type header) */ + void Send64(const std::string& str64, const std::string& type); + std::string datetime2httpdate(const std::string& dt); + std::string GetDate(); + void Reset(); + // headers + std::string m_http_cookie; + std::string m_content_type; + std::string m_content_length_str; + std::string m_if_modified_since; + +private: + HttpdSocket& operator=(const HttpdSocket& s) { return *this; } +static int m_request_count; +static std::string m_start; + size_t m_content_length; + IFile *m_file; + size_t m_received; + int m_request_id; + std::string m_http_date; + HttpdCookies *m_cookies; + HttpdForm *m_form; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_HttpdSocket_H + diff --git a/Sockets/IBase.h b/Sockets/IBase.h new file mode 100644 index 0000000..93bfdf9 --- /dev/null +++ b/Sockets/IBase.h @@ -0,0 +1,44 @@ +/** + ** \file + ** \date 2007-06-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IBase_H +#define _SOCKETS_IBase_H + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IBase +{ +public: + virtual ~IBase() {} + +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_IBase_H + diff --git a/Sockets/IEventHandler.h b/Sockets/IEventHandler.h new file mode 100644 index 0000000..fcbc432 --- /dev/null +++ b/Sockets/IEventHandler.h @@ -0,0 +1,75 @@ +/** \file IEventHandler.h + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IEventHandler_H +#define _SOCKETS_IEventHandler_H + +#include "sockets-config.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IEventOwner; + +/** Timer event handler pure virtual base class. + \ingroup timer */ +class IEventHandler +{ +public: + virtual ~IEventHandler() {} + + /** Return time left until next event. + \param tv Time struct that will be filled by method + \return true if time is set for next event */ + virtual bool GetTimeUntilNextEvent(struct timeval *) = 0; + /** Will fire events whose time has come. */ + virtual void CheckEvents() = 0; + /** Schedule event for an owner. + \param from Event owner + \param sec Seconds until event + \param usec Microseconds until event */ + virtual long AddEvent(IEventOwner *,long sec,long usec) = 0; + /** Clear all events for a specific owner. */ + virtual void ClearEvents(IEventOwner *) = 0; + /** Remove one specific event for an event owner. */ + virtual void RemoveEvent(IEventOwner *,long) = 0; + +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_IEventHandler_H + diff --git a/Sockets/IEventOwner.cpp b/Sockets/IEventOwner.cpp new file mode 100644 index 0000000..6c73551 --- /dev/null +++ b/Sockets/IEventOwner.cpp @@ -0,0 +1,79 @@ +/** \file IEventOwner.cpp + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "IEventOwner.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +IEventOwner::IEventOwner(IEventHandler& h) : m_event_handler(h), m_handler_invalid(false) +{ +} + + +IEventOwner::~IEventOwner() +{ + if (!m_handler_invalid) + { + m_event_handler.ClearEvents(this); + } +} + + +IEventHandler& IEventOwner::EventHandler() +{ + return m_event_handler; +} + + +long IEventOwner::AddEvent(long sec,long usec) +{ + return m_event_handler.AddEvent(this, sec, usec); +} + + +void IEventOwner::ClearEvents() +{ + m_event_handler.ClearEvents(this); +} + + +void IEventOwner::RemoveEvent(long eid) +{ + m_event_handler.RemoveEvent(this, eid); +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/IEventOwner.h b/Sockets/IEventOwner.h new file mode 100644 index 0000000..f3fc1cd --- /dev/null +++ b/Sockets/IEventOwner.h @@ -0,0 +1,77 @@ +/** \file IEventOwner.h + ** \date 2005-12-07 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IEventOwner_H +#define _SOCKETS_IEventOwner_H + +#include "sockets-config.h" +#include "IEventHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Any class that wants to use timer events inherits this. + \ingroup timer */ +class IEventOwner +{ +public: + IEventOwner(IEventHandler& h); + virtual ~IEventOwner(); + + /** Schedule event. + \param sec Seconds until event + \param usec Microseconds until event + \return Event ID */ + long AddEvent(long sec,long usec); + /** Clear all events scheduled by this owner. */ + void ClearEvents(); + /** Remove one event scheduled by this owner. + \param eid Event ID to remove */ + void RemoveEvent(long eid); + /** Event callback will fire when time is up. */ + virtual void OnEvent(int) = 0; + + IEventHandler& EventHandler(); + void SetHandlerInvalid(bool x = true) { m_handler_invalid = x; } + +private: + IEventHandler& m_event_handler; + bool m_handler_invalid; +}; + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_IEventOwner_H + diff --git a/Sockets/IFile.h b/Sockets/IFile.h new file mode 100644 index 0000000..aecc50f --- /dev/null +++ b/Sockets/IFile.h @@ -0,0 +1,71 @@ +/** \file IFile.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IFile_H +#define _SOCKETS_IFile_H + +#include "sockets-config.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup file File handling */ +/** Pure virtual file I/O interface. + \ingroup file */ +class IFile +{ +public: + virtual ~IFile() {} + + virtual bool fopen(const std::string&, const std::string&) = 0; + virtual void fclose() = 0; + + virtual size_t fread(char *, size_t, size_t) const = 0; + virtual size_t fwrite(const char *, size_t, size_t) = 0; + + virtual char *fgets(char *, int) const = 0; + virtual void fprintf(const char *format, ...) = 0; + + virtual off_t size() const = 0; + virtual bool eof() const = 0; + + virtual void reset_read() const = 0; + virtual void reset_write() = 0; + +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_IFile_H + diff --git a/Sockets/IHttpServer.h b/Sockets/IHttpServer.h new file mode 100644 index 0000000..34e34c3 --- /dev/null +++ b/Sockets/IHttpServer.h @@ -0,0 +1,56 @@ +/** + ** \file IHttpServer.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_IHttpServer_H +#define _SOCKETS_IHttpServer_H + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpRequest; +class HttpResponse; + +class IHttpServer +{ +public: + virtual ~IHttpServer() {} + + /** Complete request has been received and parsed. Send response + using the Respond() method. */ + virtual void OnExec(const HttpRequest& req) = 0; + + /** Send response. */ + virtual void Respond(const HttpResponse& res) = 0; + +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_IHttpServer_H + diff --git a/Sockets/ISocketHandler.h b/Sockets/ISocketHandler.h new file mode 100644 index 0000000..e32463e --- /dev/null +++ b/Sockets/ISocketHandler.h @@ -0,0 +1,233 @@ +/** \file ISocketHandler.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ISocketHandler_H +#define _SOCKETS_ISocketHandler_H +#include "sockets-config.h" + +#include +#include + +#include "socket_include.h" +#include "Socket.h" +#include "StdLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef enum { + LIST_CALLONCONNECT = 0, +#ifdef ENABLE_DETACH + LIST_DETACH, +#endif + LIST_TIMEOUT, + LIST_RETRY, + LIST_CLOSE +} list_t; + +class SocketAddress; +class Mutex; + + +/** Socket container class, event generator. + \ingroup basic */ +class ISocketHandler +{ + friend class Socket; + +public: + /** Connection pool class for internal use by the ISocketHandler. + \ingroup internal */ +#ifdef ENABLE_POOL + class PoolSocket : public Socket + { + public: + PoolSocket(ISocketHandler& h,Socket *src) : Socket(h) { + CopyConnection( src ); + SetIsClient(); + } + + void OnRead() { + Handler().LogError(this, "OnRead", 0, "data on hibernating socket", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + void OnOptions(int,int,int,SOCKET) {} + + }; +#endif + +public: + virtual ~ISocketHandler() {} + + /** Get mutex reference for threadsafe operations. */ + virtual Mutex& GetMutex() const = 0; + + /** Register StdLog object for error callback. + \param log Pointer to log class */ + virtual void RegStdLog(StdLog *log) = 0; + + /** Log error to log class for print out / storage. */ + virtual void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING) = 0; + + // ------------------------------------------------------------------------- + // Socket stuff + // ------------------------------------------------------------------------- + /** Add socket instance to socket map. Removal is always automatic. */ + virtual void Add(Socket *) = 0; +private: + /** Remove socket from socket map, used by Socket class. */ + virtual void Remove(Socket *) = 0; +public: + /** Get status of read/write/exception file descriptor set for a socket. */ + virtual void Get(SOCKET s,bool& r,bool& w,bool& e) = 0; + /** Set read/write/exception file descriptor sets (fd_set). */ + virtual void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true) = 0; + + /** Wait for events, generate callbacks. */ + virtual int Select(long sec,long usec) = 0; + /** This method will not return until an event has been detected. */ + virtual int Select() = 0; + /** Wait for events, generate callbacks. */ + virtual int Select(struct timeval *tsel) = 0; + + /** Check that a socket really is handled by this socket handler. */ + virtual bool Valid(Socket *) = 0; + /** Return number of sockets handled by this handler. */ + virtual size_t GetCount() = 0; + + /** Override and return false to deny all incoming connections. + \param p ListenSocket class pointer (use GetPort to identify which one) */ + virtual bool OkToAccept(Socket *p) = 0; + + /** Called by Socket when a socket changes state. */ + virtual void AddList(SOCKET s,list_t which_one,bool add) = 0; + + // ------------------------------------------------------------------------- + // Connection pool + // ------------------------------------------------------------------------- +#ifdef ENABLE_POOL + /** Find available open connection (used by connection pool). */ + virtual ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&) = 0; + /** Enable connection pool (by default disabled). */ + virtual void EnablePool(bool = true) = 0; + /** Check pool status. + \return true if connection pool is enabled */ + virtual bool PoolEnabled() = 0; +#endif // ENABLE_POOL + + // ------------------------------------------------------------------------- + // Socks4 + // ------------------------------------------------------------------------- +#ifdef ENABLE_SOCKS4 + /** Set socks4 server ip that all new tcp sockets should use. */ + virtual void SetSocks4Host(ipaddr_t) = 0; + /** Set socks4 server hostname that all new tcp sockets should use. */ + virtual void SetSocks4Host(const std::string& ) = 0; + /** Set socks4 server port number that all new tcp sockets should use. */ + virtual void SetSocks4Port(port_t) = 0; + /** Set optional socks4 userid. */ + virtual void SetSocks4Userid(const std::string& ) = 0; + /** If connection to socks4 server fails, immediately try direct connection to final host. */ + virtual void SetSocks4TryDirect(bool = true) = 0; + /** Get socks4 server ip. + \return socks4 server ip */ + virtual ipaddr_t GetSocks4Host() = 0; + /** Get socks4 port number. + \return socks4 port number */ + virtual port_t GetSocks4Port() = 0; + /** Get socks4 userid (optional). + \return socks4 userid */ + virtual const std::string& GetSocks4Userid() = 0; + /** Check status of socks4 try direct flag. + \return true if direct connection should be tried if connection to socks4 server fails */ + virtual bool Socks4TryDirect() = 0; +#endif // ENABLE_SOCKS4 + + // ------------------------------------------------------------------------- + // DNS resolve server + // ------------------------------------------------------------------------- +#ifdef ENABLE_RESOLVER + /** Enable asynchronous DNS. + \param port Listen port of asynchronous dns server */ + virtual void EnableResolver(port_t = 16667) = 0; + /** Check resolver status. + \return true if resolver is enabled */ + virtual bool ResolverEnabled() = 0; + /** Queue a dns request. + \param host Hostname to be resolved + \param port Port number will be echoed in Socket::OnResolved callback */ + virtual int Resolve(Socket *,const std::string& host,port_t port) = 0; +#ifdef ENABLE_IPV6 + virtual int Resolve6(Socket *,const std::string& host,port_t port) = 0; +#endif + /** Do a reverse dns lookup. */ + virtual int Resolve(Socket *,ipaddr_t a) = 0; +#ifdef ENABLE_IPV6 + virtual int Resolve(Socket *,in6_addr& a) = 0; +#endif + /** Get listen port of asynchronous dns server. */ + virtual port_t GetResolverPort() = 0; + /** Resolver thread ready for queries. */ + virtual bool ResolverReady() = 0; + /** Returns true if socket waiting for a resolve event. */ + virtual bool Resolving(Socket *) = 0; +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_TRIGGERS + /** Fetch unique trigger id. */ + virtual int TriggerID(Socket *src) = 0; + /** Subscribe socket to trigger id. */ + virtual bool Subscribe(int id, Socket *dst) = 0; + /** Unsubscribe socket from trigger id. */ + virtual bool Unsubscribe(int id, Socket *dst) = 0; + /** Execute OnTrigger for subscribed sockets. + \param id Trigger ID + \param data Data passed from source to destination + \param erase Empty trigger id source and destination maps if 'true', + Leave them in place if 'false' - if a trigger should be called many times */ + virtual void Trigger(int id, Socket::TriggerData& data, bool erase = true) = 0; +#endif // ENABLE_TRIGGERS + +#ifdef ENABLE_DETACH + /** Indicates that the handler runs under SocketThread. */ + virtual void SetSlave(bool x = true) = 0; + /** Indicates that the handler runs under SocketThread. */ + virtual bool IsSlave() = 0; +#endif // ENABLE_DETACH + +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_ISocketHandler_H + diff --git a/Sockets/Ipv4Address.cpp b/Sockets/Ipv4Address.cpp new file mode 100644 index 0000000..58f25e5 --- /dev/null +++ b/Sockets/Ipv4Address.cpp @@ -0,0 +1,214 @@ +/** + ** \file Ipv4Address.cpp + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Ipv4Address.h" +#include "Utility.h" +#include "Parse.h" +#ifndef _WIN32 +#include +#endif + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + +Ipv4Address::Ipv4Address(port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); +} + + +Ipv4Address::Ipv4Address(ipaddr_t a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr)); +} + + +Ipv4Address::Ipv4Address(struct in_addr& a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + m_addr.sin_addr = a; +} + + +Ipv4Address::Ipv4Address(const std::string& host,port_t port) : m_valid(false) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons( port ); + { + ipaddr_t a; + if (Utility::u2ip(host, a)) + { + memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr)); + m_valid = true; + } + } +} + + +Ipv4Address::Ipv4Address(struct sockaddr_in& sa) +{ + m_addr = sa; + m_valid = sa.sin_family == AF_INET; +} + + +Ipv4Address::~Ipv4Address() +{ +} + + +Ipv4Address::operator struct sockaddr *() +{ + return (struct sockaddr *)&m_addr; +} + + +Ipv4Address::operator socklen_t() +{ + return sizeof(struct sockaddr_in); +} + + +void Ipv4Address::SetPort(port_t port) +{ + m_addr.sin_port = htons( port ); +} + + +port_t Ipv4Address::GetPort() +{ + return ntohs( m_addr.sin_port ); +} + + +bool Ipv4Address::Resolve(const std::string& hostname,struct in_addr& a) +{ + struct sockaddr_in sa; + memset(&a, 0, sizeof(a)); + if (Utility::isipv4(hostname)) + { + if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST)) + return false; + a = sa.sin_addr; + return true; + } + if (!Utility::u2ip(hostname, sa)) + return false; + a = sa.sin_addr; + return true; +} + + +bool Ipv4Address::Reverse(struct in_addr& a,std::string& name) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = a; + return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name); +} + + +std::string Ipv4Address::Convert(bool include_port) +{ + if (include_port) + return Convert(m_addr.sin_addr) + ":" + Utility::l2string(GetPort()); + return Convert(m_addr.sin_addr); +} + + +std::string Ipv4Address::Convert(struct in_addr& a) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = a; + std::string name; + Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST); + return name; +} + + +void Ipv4Address::SetAddress(struct sockaddr *sa) +{ + memcpy(&m_addr, sa, sizeof(struct sockaddr_in)); +} + + +int Ipv4Address::GetFamily() +{ + return m_addr.sin_family; +} + + +bool Ipv4Address::IsValid() +{ + return m_valid; +} + + +bool Ipv4Address::operator==(SocketAddress& a) +{ + if (a.GetFamily() != GetFamily()) + return false; + if ((socklen_t)a != sizeof(m_addr)) + return false; + struct sockaddr *sa = a; + struct sockaddr_in *p = (struct sockaddr_in *)sa; + if (p -> sin_port != m_addr.sin_port) + return false; + if (memcmp(&p -> sin_addr, &m_addr.sin_addr, 4)) + return false; + return true; +} + + +std::auto_ptr Ipv4Address::GetCopy() +{ + return std::auto_ptr(new Ipv4Address(m_addr)); +} + + +std::string Ipv4Address::Reverse() +{ + std::string tmp; + Reverse(m_addr.sin_addr, tmp); + return tmp; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/Ipv4Address.h b/Sockets/Ipv4Address.h new file mode 100644 index 0000000..b58c2ce --- /dev/null +++ b/Sockets/Ipv4Address.h @@ -0,0 +1,98 @@ +/** + ** \file Ipv4Address.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ipv4Address_H +#define _SOCKETS_Ipv4Address_H + +#include "sockets-config.h" +#include "SocketAddress.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/* Ipv4 address implementation. + \ingroup basic */ +class Ipv4Address : public SocketAddress +{ +public: + /** Create empty Ipv4 address structure. + \param port Port number */ + Ipv4Address(port_t port = 0); + /** Create Ipv4 address structure. + \param a Socket address in network byte order (as returned by Utility::u2ip) + \param port Port number in host byte order */ + Ipv4Address(ipaddr_t a,port_t port); + /** Create Ipv4 address structure. + \param a Socket address in network byte order + \param port Port number in host byte order */ + Ipv4Address(struct in_addr& a,port_t port); + /** Create Ipv4 address structure. + \param host Hostname to be resolved + \param port Port number in host byte order */ + Ipv4Address(const std::string& host,port_t port); + Ipv4Address(struct sockaddr_in&); + ~Ipv4Address(); + + // SocketAddress implementation + + operator struct sockaddr *(); + operator socklen_t(); + bool operator==(SocketAddress&); + + void SetPort(port_t port); + port_t GetPort(); + + void SetAddress(struct sockaddr *sa); + int GetFamily(); + + bool IsValid(); + std::auto_ptr GetCopy(); + + /** Convert address struct to text. */ + std::string Convert(bool include_port = false); + std::string Reverse(); + + /** Resolve hostname. */ +static bool Resolve(const std::string& hostname,struct in_addr& a); + /** Reverse resolve (IP to hostname). */ +static bool Reverse(struct in_addr& a,std::string& name); + /** Convert address struct to text. */ +static std::string Convert(struct in_addr& a); + +private: + Ipv4Address(const Ipv4Address& ) {} // copy constructor + Ipv4Address& operator=(const Ipv4Address& ) { return *this; } // assignment operator + struct sockaddr_in m_addr; + bool m_valid; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_Ipv4Address_H + diff --git a/Sockets/Ipv6Address.cpp b/Sockets/Ipv6Address.cpp new file mode 100644 index 0000000..2e0f1e9 --- /dev/null +++ b/Sockets/Ipv6Address.cpp @@ -0,0 +1,270 @@ +/** + ** \file Ipv6Address.cpp + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Ipv6Address.h" +#ifdef ENABLE_IPV6 + +#include "Utility.h" +#include "Parse.h" +#ifndef _WIN32 +#include +#endif +#ifdef IPPROTO_IPV6 + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +Ipv6Address::Ipv6Address(port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); +} + + +Ipv6Address::Ipv6Address(struct in6_addr& a,port_t port) : m_valid(true) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); + m_addr.sin6_addr = a; +} + + +Ipv6Address::Ipv6Address(const std::string& host,port_t port) : m_valid(false) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin6_family = AF_INET6; + m_addr.sin6_port = htons( port ); + { + struct in6_addr a; + if (Utility::u2ip(host, a)) + { + m_addr.sin6_addr = a; + m_valid = true; + } + } +} + + +Ipv6Address::Ipv6Address(struct sockaddr_in6& sa) +{ + m_addr = sa; + m_valid = sa.sin6_family == AF_INET6; +} + + +Ipv6Address::~Ipv6Address() +{ +} + + +Ipv6Address::operator struct sockaddr *() +{ + return (struct sockaddr *)&m_addr; +} + + +Ipv6Address::operator socklen_t() +{ + return sizeof(struct sockaddr_in6); +} + + +void Ipv6Address::SetPort(port_t port) +{ + m_addr.sin6_port = htons( port ); +} + + +port_t Ipv6Address::GetPort() +{ + return ntohs( m_addr.sin6_port ); +} + + +bool Ipv6Address::Resolve(const std::string& hostname,struct in6_addr& a) +{ + struct sockaddr_in6 sa; + memset(&a, 0, sizeof(a)); + if (Utility::isipv6(hostname)) + { + if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST)) + return false; + a = sa.sin6_addr; + return true; + } + if (!Utility::u2ip(hostname, sa)) + return false; + a = sa.sin6_addr; + return true; +} + + +bool Ipv6Address::Reverse(struct in6_addr& a,std::string& name) +{ + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = a; + return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name); +} + + +std::string Ipv6Address::Convert(bool include_port) +{ + if (include_port) + return Convert(m_addr.sin6_addr) + ":" + Utility::l2string(GetPort()); + return Convert(m_addr.sin6_addr); +} + + +std::string Ipv6Address::Convert(struct in6_addr& a,bool mixed) +{ + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + if (mixed) + { + unsigned short x; + unsigned short addr16[8]; + memcpy(addr16, &a, sizeof(addr16)); + for (size_t i = 0; i < 6; i++) + { + x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + x = ntohs(addr16[6]); + sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255); + x = ntohs(addr16[7]); + sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255); + } + else + { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = a; + std::string name; + Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST); + return name; + } + return slask; +} + + +void Ipv6Address::SetAddress(struct sockaddr *sa) +{ + memcpy(&m_addr, sa, sizeof(struct sockaddr_in6)); +} + + +int Ipv6Address::GetFamily() +{ + return m_addr.sin6_family; +} + + +void Ipv6Address::SetFlowinfo(uint32_t x) +{ + m_addr.sin6_flowinfo = x; +} + + +uint32_t Ipv6Address::GetFlowinfo() +{ + return m_addr.sin6_flowinfo; +} + + +#ifndef _WIN32 +void Ipv6Address::SetScopeId(uint32_t x) +{ + m_addr.sin6_scope_id = x; +} + + +uint32_t Ipv6Address::GetScopeId() +{ + return m_addr.sin6_scope_id; +} +#endif + + +bool Ipv6Address::IsValid() +{ + return m_valid; +} + + +bool Ipv6Address::operator==(SocketAddress& a) +{ + if (a.GetFamily() != GetFamily()) + return false; + if ((socklen_t)a != sizeof(m_addr)) + return false; + struct sockaddr *sa = a; + struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa; + if (p -> sin6_port != m_addr.sin6_port) + return false; + if (memcmp(&p -> sin6_addr, &m_addr.sin6_addr, sizeof(struct in6_addr))) + return false; + return true; +} + + +std::auto_ptr Ipv6Address::GetCopy() +{ + return std::auto_ptr(new Ipv6Address(m_addr)); +} + + +std::string Ipv6Address::Reverse() +{ + std::string tmp; + Reverse(m_addr.sin6_addr, tmp); + return tmp; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 + diff --git a/Sockets/Ipv6Address.h b/Sockets/Ipv6Address.h new file mode 100644 index 0000000..a071141 --- /dev/null +++ b/Sockets/Ipv6Address.h @@ -0,0 +1,107 @@ +/** + ** \file Ipv6Address.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ipv6Address_H +#define _SOCKETS_Ipv6Address_H +#include "sockets-config.h" +#ifdef ENABLE_IPV6 + +#include "SocketAddress.h" +#ifdef IPPROTO_IPV6 +#if defined( _WIN32) && !defined(__CYGWIN__) +typedef unsigned __int32 uint32_t; +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Ipv6 address implementation. + \ingroup basic */ +class Ipv6Address : public SocketAddress +{ +public: + /** Create empty Ipv6 address structure. + \param port Port number */ + Ipv6Address(port_t port = 0); + /** Create Ipv6 address structure. + \param a Socket address in network byte order + \param port Port number in host byte order */ + Ipv6Address(struct in6_addr& a,port_t port); + /** Create Ipv6 address structure. + \param host Hostname to be resolved + \param port Port number in host byte order */ + Ipv6Address(const std::string& host,port_t port); + Ipv6Address(struct sockaddr_in6&); + ~Ipv6Address(); + + // SocketAddress implementation + + operator struct sockaddr *(); + operator socklen_t(); + bool operator==(SocketAddress&); + + void SetPort(port_t port); + port_t GetPort(); + + void SetAddress(struct sockaddr *sa); + int GetFamily(); + + bool IsValid(); + std::auto_ptr GetCopy(); + + /** Convert address struct to text. */ + std::string Convert(bool include_port = false); + std::string Reverse(); + + /** Resolve hostname. */ +static bool Resolve(const std::string& hostname,struct in6_addr& a); + /** Reverse resolve (IP to hostname). */ +static bool Reverse(struct in6_addr& a,std::string& name); + /** Convert address struct to text. */ +static std::string Convert(struct in6_addr& a,bool mixed = false); + + void SetFlowinfo(uint32_t); + uint32_t GetFlowinfo(); +#ifndef _WIN32 + void SetScopeId(uint32_t); + uint32_t GetScopeId(); +#endif + +private: + Ipv6Address(const Ipv6Address& ) {} // copy constructor + Ipv6Address& operator=(const Ipv6Address& ) { return *this; } // assignment operator + struct sockaddr_in6 m_addr; + bool m_valid; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 +#endif // _SOCKETS_Ipv6Address_H + diff --git a/Sockets/ListenSocket.h b/Sockets/ListenSocket.h new file mode 100644 index 0000000..f4edc37 --- /dev/null +++ b/Sockets/ListenSocket.h @@ -0,0 +1,420 @@ +/** \file ListenSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ListenSocket_H +#define _SOCKETS_ListenSocket_H +#include "sockets-config.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "ISocketHandler.h" +#include "Socket.h" +#include "Utility.h" +#include "SctpSocket.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Binds incoming port number to new Socket class X. + \ingroup basic */ +template +class ListenSocket : public Socket +{ +public: + /** Constructor. + \param h ISocketHandler reference + \param use_creator Optional use of creator (default true) */ + ListenSocket(ISocketHandler& h,bool use_creator = true) : Socket(h), m_depth(0), m_creator(NULL) + ,m_bHasCreate(false) + { + if (use_creator) + { + m_creator = new X(h); + Socket *tmp = m_creator -> Create(); + if (tmp && dynamic_cast(tmp)) + { + m_bHasCreate = true; + } + if (tmp) + { + delete tmp; + } + } + } + ~ListenSocket() { + if (m_creator) + { + delete m_creator; + } + } + + /** Close file descriptor. */ + int Close() { + if (GetSocket() != INVALID_SOCKET) + { + closesocket(GetSocket()); + } + return 0; + } + + /** Bind and listen to any interface. + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(port_t port,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, depth); + } + else +#endif +#endif + { + Ipv4Address ad(port); + return Bind(ad, depth); + } + } + + int Bind(SocketAddress& ad,int depth) { +#ifdef USE_SCTP + if (dynamic_cast(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + + /** Bind and listen to any interface, with optional protocol. + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(port_t port,const std::string& protocol,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, protocol, depth); + } + else +#endif +#endif + { + Ipv4Address ad(port); + return Bind(ad, protocol, depth); + } + } + + /** Bind and listen to specific interface. + \param intf Interface hostname + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(const std::string& intf,port_t port,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + else +#endif +#endif + { + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + } + + /** Bind and listen to specific interface. + \param intf Interface hostname + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(const std::string& intf,port_t port,const std::string& protocol,int depth = 20) { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, protocol, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + else +#endif +#endif + { + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, protocol, depth); + } + Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL); + return -1; + } + } + + /** Bind and listen to ipv4 interface. + \param a Ipv4 interface address + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(ipaddr_t a,port_t port,int depth = 20) { + Ipv4Address ad(a, port); +#ifdef USE_SCTP + if (dynamic_cast(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + /** Bind and listen to ipv4 interface. + \param a Ipv4 interface address + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(ipaddr_t a,port_t port,const std::string& protocol,int depth) { + Ipv4Address ad(a, port); + return Bind(ad, protocol, depth); + } + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Bind and listen to ipv6 interface. + \param a Ipv6 interface address + \param port Port (0 is random) + \param depth Listen queue depth */ + int Bind(in6_addr a,port_t port,int depth = 20) { + Ipv6Address ad(a, port); +#ifdef USE_SCTP + if (dynamic_cast(m_creator)) + { + return Bind(ad, "sctp", depth); + } +#endif + return Bind(ad, "tcp", depth); + } + /** Bind and listen to ipv6 interface. + \param a Ipv6 interface address + \param port Port (0 is random) + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(in6_addr a,port_t port,const std::string& protocol,int depth) { + Ipv6Address ad(a, port); + return Bind(ad, protocol, depth); + } +#endif +#endif + + /** Bind and listen to network interface. + \param ad Interface address + \param protocol Network protocol + \param depth Listen queue depth */ + int Bind(SocketAddress& ad,const std::string& protocol,int depth) { + SOCKET s; + if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET) + { + return -1; + } + if (bind(s, ad, ad) == -1) + { + Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL); + closesocket(s); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno)); +#endif + return -1; + } + if (listen(s, depth) == -1) + { + Handler().LogError(this, "listen", Errno, StrError(Errno), LOG_LEVEL_FATAL); + closesocket(s); +#ifdef ENABLE_EXCEPTIONS + throw Exception("listen() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno)); +#endif + return -1; + } + m_depth = depth; + Attach(s); + return 0; + } + + /** Return assigned port number. */ + port_t GetPort() + { + return GetSockPort(); + } + + /** Return listen queue depth. */ + int GetDepth() + { + return m_depth; + } + + /** OnRead on a ListenSocket receives an incoming connection. */ + void OnRead() + { + struct sockaddr sa; + socklen_t sa_len = sizeof(struct sockaddr); + SOCKET a_s = accept(GetSocket(), &sa, &sa_len); + + if (a_s == INVALID_SOCKET) + { + Handler().LogError(this, "accept", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return; + } + if (!Handler().OkToAccept(this)) + { + Handler().LogError(this, "accept", -1, "Not OK to accept", LOG_LEVEL_WARNING); + closesocket(a_s); + return; + } + if (Handler().GetCount() >= FD_SETSIZE) + { + Handler().LogError(this, "accept", (int)Handler().GetCount(), "ISocketHandler fd_set limit reached", LOG_LEVEL_FATAL); + closesocket(a_s); + return; + } + Socket *tmp = m_bHasCreate ? m_creator -> Create() : new X(Handler()); +#ifdef ENABLE_IPV6 + tmp -> SetIpv6( IsIpv6() ); +#endif + tmp -> SetParent(this); + tmp -> Attach(a_s); + tmp -> SetNonblocking(true); + { +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (sa_len == sizeof(struct sockaddr_in6)) + { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)&sa; + if (p -> sin6_family == AF_INET6) + { + Ipv6Address ad(p -> sin6_addr,ntohs(p -> sin6_port)); + ad.SetFlowinfo(p -> sin6_flowinfo); +#ifndef _WIN32 + ad.SetScopeId(p -> sin6_scope_id); +#endif + tmp -> SetRemoteAddress(ad); + } + } +#endif +#endif + if (sa_len == sizeof(struct sockaddr_in)) + { + struct sockaddr_in *p = (struct sockaddr_in *)&sa; + if (p -> sin_family == AF_INET) + { + Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port)); + tmp -> SetRemoteAddress(ad); + } + } + } + tmp -> SetConnected(true); + tmp -> Init(); + tmp -> SetDeleteByHandler(true); + Handler().Add(tmp); +#ifdef HAVE_OPENSSL + if (tmp -> IsSSL()) // SSL Enabled socket + { + // %! OnSSLAccept calls SSLNegotiate that can finish in this one call. + // %! If that happens and negotiation fails, the 'tmp' instance is + // %! still added to the list of active sockets in the sockethandler. + // %! See bugfix for this in SocketHandler::Select - don't Set rwx + // %! flags if CloseAndDelete() flag is true. + // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids + // %! the Add problem altogether, so ignore the above. + // %! (OnSSLAccept does no longer call SSLNegotiate().) + tmp -> OnSSLAccept(); + } + else +#endif + { + tmp -> OnAccept(); + } + } + + /** Please don't use this method. + "accept()" is handled automatically in the OnRead() method. */ + virtual SOCKET Accept(SOCKET socket, struct sockaddr *saptr, socklen_t *lenptr) + { + return accept(socket, saptr, lenptr); + } + + bool HasCreator() { return m_bHasCreate; } + + void OnOptions(int,int,int,SOCKET) { + SetSoReuseaddr(true); + } + +protected: + ListenSocket(const ListenSocket& s) : Socket(s) {} +private: + ListenSocket& operator=(const ListenSocket& ) { return *this; } + int m_depth; + X *m_creator; + bool m_bHasCreate; +}; + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_ListenSocket_H + diff --git a/Sockets/Lock.cpp b/Sockets/Lock.cpp new file mode 100644 index 0000000..3f7902e --- /dev/null +++ b/Sockets/Lock.cpp @@ -0,0 +1,55 @@ +/** \file Lock.cpp + ** \date 2005-08-22 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Mutex.h" +#include "Lock.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +Lock::Lock(Mutex& m) : m_mutex(m) +{ + m_mutex.Lock(); +} + + +Lock::~Lock() +{ + m_mutex.Unlock(); +} + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/Lock.h b/Sockets/Lock.h new file mode 100644 index 0000000..71fdd5e --- /dev/null +++ b/Sockets/Lock.h @@ -0,0 +1,59 @@ +/** \file Lock.h + ** \date 2005-08-22 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2005,2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Lock_H +#define _SOCKETS_Lock_H + +#include "sockets-config.h" +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Mutex; + +/** Mutex encapsulation class. + \ingroup threading */ +class Lock +{ +public: + Lock(Mutex&); + ~Lock(); + +private: + Mutex& m_mutex; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif +#endif // _SOCKETS_Lock_H + diff --git a/Sockets/Makefile b/Sockets/Makefile new file mode 100644 index 0000000..631501d --- /dev/null +++ b/Sockets/Makefile @@ -0,0 +1,154 @@ +# platforms: +# linux-x86-32 +# win32-cygwin +# solaris9-sparc-64 +# macosx +# solaris8 +PLATFORM = linux-x86-32 +IPPROTO_IPV6 = yes + +# .h files installed to $(PREFIX)/include/Sockets +# static lib .a files installed to $(PREFIX)/lib +PREFIX = /usr/local + +# include paths +INCLUDE = + +include Makefile.version + +# CXX, CFLAGS, LIBS +include Makefile.Defines.$(PLATFORM) + +# uncomment these lines if the library should be in its own namespace +CFLAGS += -DSOCKETS_NAMESPACE=sockets +CFLAGS += -DSOCKETS_NAMESPACE_STR='"sockets"' + +# Enable insane amounts of debug output to stdout/stderr: +#CFLAGS += -D_DEBUG +#CFLAGS += -DIPPROTO_IPV6 + + +CPPFLAGS = $(CFLAGS) + +PROGS = Sockets-config + +all: libSockets.a $(PROGS) + +libSockets.a: $(OBJS) + ar cru $@ $(OBJS) + ranlib $@ + +Sockets-config: Sockets-config.o + $(CXX) -o $@ $^ + +clean: + rm -f *.o *~ slask *.d $(PROGS) *.a */*~ + +-include *.d + + +# everything which follows is www.alhem.net/Sockets website maintenance stuff, please ignore +HTDOCS = /usr/local/apache/www.alhem.net/htdocs + +diff: + diff -b -B -C 3 /usr/src/Sockets-$(DIFF_VERSION) . | \ + /usr/local/bin/d2html > \ + $(HTDOCS)/Sockets/latest_diff.html + +install: all + @mkdir -p $(PREFIX)/lib + cp libSockets.a $(PREFIX)/lib + @mkdir -p $(PREFIX)/include/Sockets + cp -a *.h $(PREFIX)/include/Sockets + @rm -f $(PREFIX)/include/Sockets/SSLSocket.* + @rm -f $(PREFIX)/include/Sockets/HttpsGetSocket.* + @rm -f $(PREFIX)/include/Sockets/HttpsSocket.* + @rm -f $(PREFIX)/include/Sockets/EventSocket.* + @rm -f $(PREFIX)/include/Sockets/PoolSocket.* + @rm -f $(PREFIX)/include/Sockets/SocketThread.* + @rm -f $(PREFIX)/include/Sockets/CircularBuffer.* + @rm -f $(PREFIX)/include/Sockets/*Crypt.h + @rm -f $(PREFIX)/include/Sockets/CTcpSocket.h + @rm -f $(PREFIX)/include/Sockets/Min*Socket.h + @rm -f $(PREFIX)/include/Sockets/Min*Handler.h + @rm -f $(PREFIX)/include/Sockets/Uid.h + @mkdir -p $(PREFIX)/bin + install Sockets-config $(PREFIX)/bin + +# no binary files, zip will translate lf to cr lf +FILES = *.h *.cpp Makefile Makefile.* Project/*.ds* \ + README.macosx gpl.txt mkdot.sh \ + Project.net/Sockets/*.vcproj Project.net/Sockets/*.sln \ + Project.net/Test/*.vcproj \ + DevCpp/*.dev tests/Makefile tests/*.cpp \ + OSX.zip + +tar: clean + rm -f MinderSocket_T.h + rm -f uuid.h + rm -f Stdin.* + rm -f sockets_test.cpp + rm -f ListenSocketBase.* + rm -f CircularBuffer.* + rm -f ICrypt.* NullCrypt.* CTcpSocket.* + rm -f Min*Socket.* Min*Handler.* + rm -f Uid.* + tar czf Sockets-$(VERSION).tar.gz $(FILES) + /usr/local/bin/tarfix.sh Sockets-$(VERSION) + cp Sockets-$(VERSION).tar.gz $(HTDOCS)/Sockets + cp Sockets-$(VERSION).zip $(HTDOCS)/Sockets + cp tests/sockets_test.cpp /usr/local/apache/www.alhem.net/htdocs/Sockets/ +# $(MAKE) -C ../SocketsLite tar + +tmptar: clean + rm -f MinderSocket_T.h + rm -f uuid.h + rm -f Stdin.* + rm -f sockets_test.cpp + rm -f ListenSocketBase.* + rm -f CircularBuffer.* + rm -f ICrypt.* NullCrypt.* CTcpSocket.* + rm -f Min*Socket.* Min*Handler.* + rm -f Uid.* + tar czf Sockets-$(VERSION).tar.gz $(FILES) + /usr/local/bin/tarfix.sh Sockets-$(VERSION) + cp Sockets-$(VERSION).tar.gz $(HTDOCS)/Sockets-tmp + cp Sockets-$(VERSION).zip $(HTDOCS)/Sockets-tmp + cp tests/sockets_test.cpp /usr/local/apache/www.alhem.net/htdocs/Sockets-tmp + $(MAKE) -C ../SocketsLite tmptar + +docs: clean + doxygen dox2.cfg + rm -f MinderSocket_T.h + rm -f uuid.h + rm -f Stdin.* + rm -f sockets_test.cpp + rm -f ListenSocketBase.* + rm -f CircularBuffer.* + rm -f ICrypt.* NullCrypt.* CTcpSocket.* + rm -f Min*Socket.* Min*Handler.* + rm -f Uid.* + rm -f tests/*.cpp + ./mkdot.sh + rm -rf /usr/local/apache/www.alhem.net/htdocs/Sockets/html + doxygen doxygen.cfg + ./packdocs.sh Sockets-$(VERSION)-doxygendocs + cvs up +# $(MAKE) -C ../SocketsLite docs + +tmpdocs: clean + rm -f MinderSocket_T.h + rm -f uuid.h + rm -f Stdin.* + rm -f sockets_test.cpp + rm -f ListenSocketBase.* + rm -f CircularBuffer.* + rm -f ICrypt.* NullCrypt.* CTcpSocket.* + rm -f Min*Socket.* Min*Handler.* + rm -f Uid.* + rm -f tests/*.cpp + ./mkdot-tmp.sh + rm -rf /usr/local/apache/www.alhem.net/htdocs/Sockets-tmp/html + doxygen doxygen-tmp.cfg + cvs up + $(MAKE) -C ../SocketsLite tmpdocs diff --git a/Sockets/Makefile.Defines.linux-x86-32 b/Sockets/Makefile.Defines.linux-x86-32 new file mode 100644 index 0000000..df06d6b --- /dev/null +++ b/Sockets/Makefile.Defines.linux-x86-32 @@ -0,0 +1,28 @@ +CXX = g++ + +CFLAGS += -Wall -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 + + +# uncomment depending on your operating system +CFLAGS += -DLINUX +#CFLAGS += -DMACOSX +# solaris > 8 +#CFLAGS += -DSOLARIS +# solaris 8 +#CFLAGS += -DSOLARIS8 +# windows and/or cygwin +#CFLAGS += -D_WIN32 +# cygwin +#CFLAGS += -D__CYGWIN__ + + +LIBS += \ + -lssl -lcrypto \ + -lpthread + +OBJS += Semaphore.o + diff --git a/Sockets/Makefile.Defines.macosx b/Sockets/Makefile.Defines.macosx new file mode 100644 index 0000000..9b54559 --- /dev/null +++ b/Sockets/Makefile.Defines.macosx @@ -0,0 +1,28 @@ +CXX = g++ + +CFLAGS += -Wall -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 + + +# uncomment depending on your operating system +#CFLAGS += -DLINUX +CFLAGS += -DMACOSX +# solaris > 8 +#CFLAGS += -DSOLARIS +# solaris 8 +#CFLAGS += -DSOLARIS8 +# windows and/or cygwin +#CFLAGS += -D_WIN32 +# cygwin +#CFLAGS += -D__CYGWIN__ + + +LIBS += \ + -lssl -lcrypto \ + -lpthread + +OBJS += Semaphore.o + diff --git a/Sockets/Makefile.Defines.solaris8 b/Sockets/Makefile.Defines.solaris8 new file mode 100644 index 0000000..ec31056 --- /dev/null +++ b/Sockets/Makefile.Defines.solaris8 @@ -0,0 +1,29 @@ +CXX = /opt/SUNWspro/bin/CC + +CFLAGS += -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' +CFLAGS += -features=rtti -mt + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 + + +# uncomment depending on your operating system +#CFLAGS += -DLINUX +#CFLAGS += -DMACOSX +# solaris > 8 +#CFLAGS += -DSOLARIS +# solaris 8 +CFLAGS += -DSOLARIS8 +# windows and/or cygwin +#CFLAGS += -D_WIN32 +# cygwin +#CFLAGS += -D__CYGWIN__ + + +LIBS += \ + -lssl -lcrypto \ + -lpthread + +OBJS += Semaphore.o + diff --git a/Sockets/Makefile.Defines.solaris9-sparc-64 b/Sockets/Makefile.Defines.solaris9-sparc-64 new file mode 100644 index 0000000..42fa8c4 --- /dev/null +++ b/Sockets/Makefile.Defines.solaris9-sparc-64 @@ -0,0 +1,29 @@ +CXX = /opt/SUNWspro/bin/CC + +CFLAGS += -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' +CFLAGS += -features=rtti -mt + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 + + +# uncomment depending on your operating system +#CFLAGS += -DLINUX +#CFLAGS += -DMACOSX +# solaris > 8 +CFLAGS += -DSOLARIS +# solaris 8 +#CFLAGS += -DSOLARIS8 +# windows and/or cygwin +#CFLAGS += -D_WIN32 +# cygwin +#CFLAGS += -D__CYGWIN__ + + +LIBS += \ + -lssl -lcrypto \ + -lpthread + +OBJS += Semaphore.o + diff --git a/Sockets/Makefile.Defines.win32-cygwin b/Sockets/Makefile.Defines.win32-cygwin new file mode 100644 index 0000000..66ca672 --- /dev/null +++ b/Sockets/Makefile.Defines.win32-cygwin @@ -0,0 +1,26 @@ +CXX = g++ + +CFLAGS += -Wall -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 -mno-cygwin + + +# uncomment depending on your operating system +#CFLAGS += -DLINUX +#CFLAGS += -DMACOSX +# solaris > 8 +#CFLAGS += -DSOLARIS +# solaris 8 +#CFLAGS += -DSOLARIS8 +# windows and/or cygwin +CFLAGS += -D_WIN32 +# cygwin +CFLAGS += -D__CYGWIN__ + + +LIBS += c:/openssl/lib/mingw/ssleay32.a \ + c:/openssl/lib/mingw/libeay32.a \ + -lws2_32 + diff --git a/Sockets/Makefile.solaris9-sparc-64 b/Sockets/Makefile.solaris9-sparc-64 new file mode 100644 index 0000000..82f5ddf --- /dev/null +++ b/Sockets/Makefile.solaris9-sparc-64 @@ -0,0 +1,70 @@ +include Makefile.version + +INCLUDE = + +# .h files installed to $(PREFIX)/include/Sockets +# static lib .a files installed to $(PREFIX)/lib +PREFIX = /usr/local + +CXX = /opt/SUNWspro/bin/CC + +CFLAGS = -g $(INCLUDE) -MD -D_VERSION='"$(VERSION)"' +CFLAGS += -features=rtti -mt + +# turn off optimization if compiling with gcc 2.9.x: +#CFLAGS += -O0 +CFLAGS += -O2 + + +# uncomment depending on your operating system +#CFLAGS += -DLINUX +#CFLAGS += -DMACOSX +# solaris > 8 +CFLAGS += -DSOLARIS +# solaris 8 +#CFLAGS += -DSOLARIS8 +# windows and/or cygwin +#CFLAGS += -D_WIN32 +# cygwin +#CFLAGS += -D__CYGWIN__ + + +# uncomment these three lines if the library should be in its own namespace +#CFLAGS += -DSOCKETS_NAMESPACE=sockets +#CFLAGS += -DSOCKETS_NAMESPACE_STR='"sockets"' + + +# Enable insane amounts of debug output to stdout/stderr: +#CFLAGS += -D_DEBUG + + +CPPFLAGS = $(CFLAGS) + +LIBS = -lSockets -lpthread -lssl -lcrypto + +PROGS = Sockets-config + +all: libSockets.a $(PROGS) + +Sockets-config: Sockets-config.o + $(CXX) -o $@ $^ + +libSockets.a: $(OBJS) + ar cru $@ $(OBJS) + ranlib $@ + +stressclient: stressclient.o libSockets.a + g++ -o $@ $^ $(LIBS) + +echoserver: echoserver.o libSockets.a + g++ -o $@ $^ $(LIBS) + +clean: + rm -f *.o *~ slask *.d $(PROGS) *.a */*~ + +.cpp.o: + @echo Compiling $< + @$(CXX) $(CFLAGS) -c -o $@ $< + +-include *.d + diff --git a/Sockets/Makefile.version b/Sockets/Makefile.version new file mode 100644 index 0000000..cde12a1 --- /dev/null +++ b/Sockets/Makefile.version @@ -0,0 +1,49 @@ +VERSION = 2.2.6 +DIFF_VERSION = 2.2.5 + +# Sockets +OBJS = \ + Ajp13Socket.o \ + AjpBaseSocket.o \ + Base64.o \ + Debug.o \ + Event.o \ + EventHandler.o \ + EventTime.o \ + Exception.o \ + File.o \ + HTTPSocket.o \ + HttpBaseSocket.o \ + HttpClientSocket.o \ + HttpDebugSocket.o \ + HttpGetSocket.o \ + HttpPostSocket.o \ + HttpPutSocket.o \ + HttpRequest.o \ + HttpResponse.o \ + HttpTransaction.o \ + HttpdCookies.o \ + HttpdForm.o \ + HttpdSocket.o \ + IEventOwner.o \ + Ipv4Address.o \ + Ipv6Address.o \ + Lock.o \ + MemFile.o \ + Mutex.o \ + Parse.o \ + RandomNumber.o \ + ResolvServer.o \ + ResolvSocket.o \ + SSLInitializer.o \ + SctpSocket.o \ + SmtpdSocket.o \ + Socket.o \ + SocketHandler.o \ + StdoutLog.o \ + StreamSocket.o \ + TcpSocket.o \ + Thread.o \ + UdpSocket.o \ + Utility.o \ + socket_include.o diff --git a/Sockets/MemFile.cpp b/Sockets/MemFile.cpp new file mode 100644 index 0000000..edbe619 --- /dev/null +++ b/Sockets/MemFile.cpp @@ -0,0 +1,288 @@ +/** \file MemFile.cpp + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#include + +#include "MemFile.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +std::map MemFile::m_files; + + +MemFile::MemFile() +:m_temporary(true) +,m_base(new block_t) +,m_current_read(m_base) +,m_current_write(m_base) +,m_current_write_nr(0) +,m_read_ptr(0) +,m_write_ptr(0) +,m_b_read_caused_eof(false) +{ +} + + +MemFile::MemFile(const std::string& path) +:m_path(path) +,m_temporary(false) +,m_base(m_files[path]) +,m_current_read(NULL) +,m_current_write(NULL) +,m_current_write_nr(0) +,m_read_ptr(0) +,m_write_ptr(0) +,m_b_read_caused_eof(false) +{ + if (!m_base) + { + m_base = new block_t; + m_files[path] = m_base; + } + m_current_read = m_base; + m_current_write = m_base; +} + + +MemFile::~MemFile() +{ + while (m_base && m_temporary) + { + block_t *p = m_base; + m_base = p -> next; + delete p; + } +} + + +bool MemFile::fopen(const std::string& path, const std::string& mode) +{ + return true; +} + + +void MemFile::fclose() +{ +} + + +size_t MemFile::fread(char *ptr, size_t size, size_t nmemb) const +{ + size_t p = m_read_ptr % BLOCKSIZE; + size_t sz = size * nmemb; + size_t available = m_write_ptr - m_read_ptr; + if (sz > available) // read beyond eof + { + sz = available; + m_b_read_caused_eof = true; + } + if (!sz) + { + return 0; + } + if (p + sz < BLOCKSIZE) + { + memcpy(ptr, m_current_read -> data + p, sz); + m_read_ptr += sz; + } + else + { + size_t sz1 = BLOCKSIZE - p; + size_t sz2 = sz - sz1; + memcpy(ptr, m_current_read -> data + p, sz1); + m_read_ptr += sz1; + while (sz2 > BLOCKSIZE) + { + if (m_current_read -> next) + { + m_current_read = m_current_read -> next; + memcpy(ptr + sz1, m_current_read -> data, BLOCKSIZE); + m_read_ptr += BLOCKSIZE; + sz1 += BLOCKSIZE; + sz2 -= BLOCKSIZE; + } + else + { + return sz1; + } + } + if (m_current_read -> next) + { + m_current_read = m_current_read -> next; + memcpy(ptr + sz1, m_current_read -> data, sz2); + m_read_ptr += sz2; + } + else + { + return sz1; + } + } + return sz; +} + + +size_t MemFile::fwrite(const char *ptr, size_t size, size_t nmemb) +{ + size_t p = m_write_ptr % BLOCKSIZE; + int nr = (int)m_write_ptr / BLOCKSIZE; + size_t sz = size * nmemb; + if (m_current_write_nr < nr) + { + block_t *next = new block_t; + m_current_write -> next = next; + m_current_write = next; + m_current_write_nr++; + } + if (p + sz <= BLOCKSIZE) + { + memcpy(m_current_write -> data + p, ptr, sz); + m_write_ptr += sz; + } + else + { + size_t sz1 = BLOCKSIZE - p; // size left + size_t sz2 = sz - sz1; + memcpy(m_current_write -> data + p, ptr, sz1); + m_write_ptr += sz1; + while (sz2 > BLOCKSIZE) + { + if (m_current_write -> next) + { + m_current_write = m_current_write -> next; + m_current_write_nr++; + } + else + { + block_t *next = new block_t; + m_current_write -> next = next; + m_current_write = next; + m_current_write_nr++; + } + memcpy(m_current_write -> data, ptr + sz1, BLOCKSIZE); + m_write_ptr += BLOCKSIZE; + sz1 += BLOCKSIZE; + sz2 -= BLOCKSIZE; + } + if (m_current_write -> next) + { + m_current_write = m_current_write -> next; + m_current_write_nr++; + } + else + { + block_t *next = new block_t; + m_current_write -> next = next; + m_current_write = next; + m_current_write_nr++; + } + memcpy(m_current_write -> data, ptr + sz1, sz2); + m_write_ptr += sz2; + } + return sz; +} + + + +char *MemFile::fgets(char *s, int size) const +{ + int n = 0; + while (n < size - 1 && !eof()) + { + char c; + size_t sz = fread(&c, 1, 1); + if (sz) + { + if (c == 10) + { + s[n] = 0; + return s; + } + s[n++] = c; + } + } + s[n] = 0; + return s; +} + + +void MemFile::fprintf(const char *format, ...) +{ + va_list ap; + char tmp[BLOCKSIZE]; + va_start(ap, format); +#ifdef _WIN32 + vsprintf(tmp, format, ap); +#else + vsnprintf(tmp, BLOCKSIZE - 1, format, ap); +#endif + va_end(ap); + fwrite(tmp, 1, strlen(tmp)); +} + + +off_t MemFile::size() const +{ + return (off_t)m_write_ptr; +} + + +bool MemFile::eof() const +{ + return m_b_read_caused_eof; //(m_read_ptr < m_write_ptr) ? false : true; +} + + +void MemFile::reset_read() const +{ + m_read_ptr = 0; + m_current_read = m_base; +} + + +void MemFile::reset_write() +{ + m_write_ptr = 0; + m_current_write = m_base; + m_current_write_nr = 0; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/MemFile.h b/Sockets/MemFile.h new file mode 100644 index 0000000..c97b6bc --- /dev/null +++ b/Sockets/MemFile.h @@ -0,0 +1,100 @@ +/** \file MemFile.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_MemFile_H +#define _SOCKETS_MemFile_H + +#include "sockets-config.h" +#include +#include "IFile.h" + +#define BLOCKSIZE 32768 + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Implements a memory file. + \ingroup file */ +class MemFile : public IFile +{ +public: + /** File block structure. + \ingroup file */ + struct block_t { + block_t() : next(NULL) {} + struct block_t *next; + char data[BLOCKSIZE]; + }; +public: + MemFile(); + MemFile(const std::string& path); + ~MemFile(); + + bool fopen(const std::string& path, const std::string& mode); + void fclose(); + + size_t fread(char *ptr, size_t size, size_t nmemb) const; + size_t fwrite(const char *ptr, size_t size, size_t nmemb); + + char *fgets(char *s, int size) const; + void fprintf(const char *format, ...); + + off_t size() const; + bool eof() const; + + void reset_read() const; + void reset_write(); + +private: + MemFile(const MemFile& ) {} // copy constructor + MemFile& operator=(const MemFile& ) { return *this; } // assignment operator + +static std::map m_files; + std::string m_path; + bool m_temporary; + block_t *m_base; + mutable block_t *m_current_read; + block_t *m_current_write; + int m_current_write_nr; + mutable size_t m_read_ptr; + size_t m_write_ptr; + mutable bool m_b_read_caused_eof; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_MemFile_H + diff --git a/Sockets/Mutex.cpp b/Sockets/Mutex.cpp new file mode 100644 index 0000000..4b0e681 --- /dev/null +++ b/Sockets/Mutex.cpp @@ -0,0 +1,81 @@ +/** \file Mutex.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Mutex.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +Mutex::Mutex() +{ +#ifdef _WIN32 + m_mutex = ::CreateMutex(NULL, FALSE, NULL); +#else + pthread_mutex_init(&m_mutex, NULL); +#endif +} + + +Mutex::~Mutex() +{ +#ifdef _WIN32 + ::CloseHandle(m_mutex); +#else + pthread_mutex_destroy(&m_mutex); +#endif +} + + +void Mutex::Lock() +{ +#ifdef _WIN32 + DWORD d = WaitForSingleObject(m_mutex, INFINITE); + /// \todo check 'd' for result +#else + pthread_mutex_lock(&m_mutex); +#endif +} + + +void Mutex::Unlock() +{ +#ifdef _WIN32 + ::ReleaseMutex(m_mutex); +#else + pthread_mutex_unlock(&m_mutex); +#endif +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/Mutex.h b/Sockets/Mutex.h new file mode 100644 index 0000000..1de2776 --- /dev/null +++ b/Sockets/Mutex.h @@ -0,0 +1,68 @@ +/** \file Mutex.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Mutex_H +#define _SOCKETS_Mutex_H + +#include "sockets-config.h" +#ifndef _WIN32 +#include +#else +#include +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Mutex container class, used by Lock. + \ingroup threading */ +class Mutex +{ + friend class Lock; +public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); +private: +#ifdef _WIN32 + HANDLE m_mutex; +#else + pthread_mutex_t m_mutex; +#endif +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif +#endif // _SOCKETS_Mutex_H + diff --git a/Sockets/OSX.zip b/Sockets/OSX.zip new file mode 100755 index 0000000..e4046a4 Binary files /dev/null and b/Sockets/OSX.zip differ diff --git a/Sockets/Parse.cpp b/Sockets/Parse.cpp new file mode 100644 index 0000000..6de56f0 --- /dev/null +++ b/Sockets/Parse.cpp @@ -0,0 +1,321 @@ +/** \file Parse.cpp - parse a string + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include +#include + +#include "Parse.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/* implementation of class Parse */ + +Parse::Parse() +:pa_the_str("") +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s) +:pa_the_str(s) +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp,short nospace) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(1) +,pa_quote(false) +{ +} + + +Parse::~Parse() +{ +} + +#define C ((pa_the_ptr + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/***************************************************/ +/* interface of class Parse */ + +/** Splits a string whatever way you want. + \ingroup util */ +class Parse +{ +public: + Parse(); + Parse(const std::string&); + Parse(const std::string&,const std::string&); + Parse(const std::string&,const std::string&,short); + ~Parse(); + short issplit(const char); + void getsplit(); + void getsplit(std::string&); + std::string getword(); + void getword(std::string&); + void getword(std::string&,std::string&,int); + std::string getrest(); + void getrest(std::string&); + long getvalue(); + void setbreak(const char); + int getwordlen(); + int getrestlen(); + void enablebreak(const char c) { + pa_enable = c; + } + void disablebreak(const char c) { + pa_disable = c; + } + void getline(); + void getline(std::string&); + size_t getptr() { return pa_the_ptr; } + void EnableQuote(bool b) { pa_quote = b; } + +private: + std::string pa_the_str; + std::string pa_splits; + std::string pa_ord; + size_t pa_the_ptr; + char pa_breakchar; + char pa_enable; + char pa_disable; + short pa_nospace; + bool pa_quote; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Parse_H + diff --git a/Sockets/Project.net/Sockets/Sockets.sln b/Sockets/Project.net/Sockets/Sockets.sln new file mode 100644 index 0000000..9204f43 --- /dev/null +++ b/Sockets/Project.net/Sockets/Sockets.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sockets", "Sockets.vcproj", "{1EC00E34-E016-4EB4-BF7D-B5B7E101AE57}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "..\Test\Test.vcproj", "{D73A0C4B-7554-4A86-A452-5A12675925A0}" + ProjectSection(ProjectDependencies) = postProject + {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57} = {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57}.Debug.ActiveCfg = Debug|Win32 + {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57}.Debug.Build.0 = Debug|Win32 + {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57}.Release.ActiveCfg = Release|Win32 + {1EC00E34-E016-4EB4-BF7D-B5B7E101AE57}.Release.Build.0 = Release|Win32 + {D73A0C4B-7554-4A86-A452-5A12675925A0}.Debug.ActiveCfg = Debug|Win32 + {D73A0C4B-7554-4A86-A452-5A12675925A0}.Debug.Build.0 = Debug|Win32 + {D73A0C4B-7554-4A86-A452-5A12675925A0}.Release.ActiveCfg = Release|Win32 + {D73A0C4B-7554-4A86-A452-5A12675925A0}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Sockets/Project.net/Sockets/Sockets.vcproj b/Sockets/Project.net/Sockets/Sockets.vcproj new file mode 100644 index 0000000..4e008d6 --- /dev/null +++ b/Sockets/Project.net/Sockets/Sockets.vcproj @@ -0,0 +1,452 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sockets/Project.net/Test/Test.vcproj b/Sockets/Project.net/Test/Test.vcproj new file mode 100644 index 0000000..fa24b0e --- /dev/null +++ b/Sockets/Project.net/Test/Test.vcproj @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sockets/Project/Sockets.dsp b/Sockets/Project/Sockets.dsp new file mode 100644 index 0000000..b9bda66 --- /dev/null +++ b/Sockets/Project/Sockets.dsp @@ -0,0 +1,526 @@ +# Microsoft Developer Studio Project File - Name="Sockets" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=Sockets - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Sockets.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Sockets.mak" CFG="Sockets - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Sockets - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "Sockets - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Sockets - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\lib\R" +# PROP Intermediate_Dir "R" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I "c:\openssl\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy ..\*.h Include /I /Y +# End Special Build Tool + +!ELSEIF "$(CFG)" == "Sockets - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\lib\D" +# PROP Intermediate_Dir "D" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I "c:\openssl\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy ..\*.h Include /I /Y +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "Sockets - Win32 Release" +# Name "Sockets - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\Exception.cpp +# End Source File +# Begin Source File + +SOURCE=..\socket_include.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\Exception.h +# End Source File +# Begin Source File + +SOURCE=..\socket_include.h +# End Source File +# Begin Source File + +SOURCE="..\sockets-config.h" +# End Source File +# End Group +# Begin Group "Utilities" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Base64.cpp +# End Source File +# Begin Source File + +SOURCE=..\Base64.h +# End Source File +# Begin Source File + +SOURCE=..\Parse.cpp +# End Source File +# Begin Source File + +SOURCE=..\Parse.h +# End Source File +# Begin Source File + +SOURCE=..\RandomNumber.cpp +# End Source File +# Begin Source File + +SOURCE=..\RandomNumber.h +# End Source File +# Begin Source File + +SOURCE=..\Utility.cpp +# End Source File +# Begin Source File + +SOURCE=..\Utility.h +# End Source File +# End Group +# Begin Group "Internal" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\SSLInitializer.cpp +# End Source File +# Begin Source File + +SOURCE=..\SSLInitializer.h +# End Source File +# End Group +# Begin Group "Webserver framework" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\HttpdCookies.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpdCookies.h +# End Source File +# Begin Source File + +SOURCE=..\HttpdForm.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpdForm.h +# End Source File +# Begin Source File + +SOURCE=..\HttpdSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpdSocket.h +# End Source File +# Begin Source File + +SOURCE=..\Ajp13Socket.cpp +# End Source File +# Begin Source File + +SOURCE=..\Ajp13Socket.h +# End Source File +# Begin Source File + +SOURCE=..\AjpBaseSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\AjpBaseSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpBaseSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpBaseSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpTransaction.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpTransaction.h +# End Source File +# Begin Source File + +SOURCE=..\HttpRequest.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpRequest.h +# End Source File +# Begin Source File + +SOURCE=..\HttpResponse.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpResponse.h +# End Source File +# Begin Source File + +SOURCE=..\ajp13.h +# End Source File +# Begin Source File + +SOURCE=..\IHttpServer.h +# End Source File +# End Group +# Begin Group "HTTP Sockets" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\HttpClientSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpClientSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpDebugSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpDebugSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpGetSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpGetSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpPostSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpPostSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HttpPutSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HttpPutSocket.h +# End Source File +# Begin Source File + +SOURCE=..\HTTPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\HTTPSocket.h +# End Source File +# End Group +# Begin Group "File handling" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\File.cpp +# End Source File +# Begin Source File + +SOURCE=..\File.h +# End Source File +# Begin Source File + +SOURCE=..\IFile.h +# End Source File +# Begin Source File + +SOURCE=..\MemFile.cpp +# End Source File +# Begin Source File + +SOURCE=..\MemFile.h +# End Source File +# End Group +# Begin Group "Asynchronous DNS" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\ResolvServer.cpp +# End Source File +# Begin Source File + +SOURCE=..\ResolvServer.h +# End Source File +# Begin Source File + +SOURCE=..\ResolvSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\ResolvSocket.h +# End Source File +# End Group +# Begin Group "Basic Sockets" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Ipv4Address.cpp +# End Source File +# Begin Source File + +SOURCE=..\Ipv4Address.h +# End Source File +# Begin Source File + +SOURCE=..\Ipv6Address.cpp +# End Source File +# Begin Source File + +SOURCE=..\Ipv6Address.h +# End Source File +# Begin Source File + +SOURCE=..\ISocketHandler.h +# End Source File +# Begin Source File + +SOURCE=..\ListenSocket.h +# End Source File +# Begin Source File + +SOURCE=..\SctpSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\SctpSocket.h +# End Source File +# Begin Source File + +SOURCE=..\Socket.cpp +# End Source File +# Begin Source File + +SOURCE=..\Socket.h +# End Source File +# Begin Source File + +SOURCE=..\SocketAddress.h +# End Source File +# Begin Source File + +SOURCE=..\SocketHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\SocketHandler.h +# End Source File +# Begin Source File + +SOURCE=..\StreamSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\StreamSocket.h +# End Source File +# Begin Source File + +SOURCE=..\TcpSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\TcpSocket.h +# End Source File +# Begin Source File + +SOURCE=..\UdpSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\UdpSocket.h +# End Source File +# End Group +# Begin Group "Log help classes" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\StdLog.h +# End Source File +# Begin Source File + +SOURCE=..\StdoutLog.cpp +# End Source File +# Begin Source File + +SOURCE=..\StdoutLog.h +# End Source File +# End Group +# Begin Group "Threading" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Lock.cpp +# End Source File +# Begin Source File + +SOURCE=..\Lock.h +# End Source File +# Begin Source File + +SOURCE=..\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\Mutex.h +# End Source File +# Begin Source File + +SOURCE=..\Thread.cpp +# End Source File +# Begin Source File + +SOURCE=..\Thread.h +# End Source File +# End Group +# Begin Group "Timer Events" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Event.cpp +# End Source File +# Begin Source File + +SOURCE=..\Event.h +# End Source File +# Begin Source File + +SOURCE=..\EventHandler.cpp +# End Source File +# Begin Source File + +SOURCE=..\EventHandler.h +# End Source File +# Begin Source File + +SOURCE=..\EventTime.cpp +# End Source File +# Begin Source File + +SOURCE=..\EventTime.h +# End Source File +# Begin Source File + +SOURCE=..\IEventHandler.h +# End Source File +# Begin Source File + +SOURCE=..\IEventOwner.cpp +# End Source File +# Begin Source File + +SOURCE=..\IEventOwner.h +# End Source File +# End Group +# Begin Group "SMTP" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\SmtpdSocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\SmtpdSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/Sockets/Project/Sockets.dsw b/Sockets/Project/Sockets.dsw new file mode 100644 index 0000000..3a7694c --- /dev/null +++ b/Sockets/Project/Sockets.dsw @@ -0,0 +1,68 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Sockets"=.\Sockets.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "Test"=.\Test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Sockets + End Project Dependency +}}} + +############################################################################### + +Project: "echoserver"=.\echoserver.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "stressclient"=.\stressclient.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Sockets/Project/Test.dsp b/Sockets/Project/Test.dsp new file mode 100644 index 0000000..74efc10 --- /dev/null +++ b/Sockets/Project/Test.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "bin\R" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I "Include" /I "c:\openssl\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib libeay32md.lib ssleay32md.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\lib\R" /libpath:"c:\openssl\lib\vc" + +!ELSEIF "$(CFG)" == "Test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "bin\D" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I "Include" /I "c:\openssl\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib libeay32md.lib ssleay32md.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\lib\D" /libpath:"c:\openssl\lib\vc" + +!ENDIF + +# Begin Target + +# Name "Test - Win32 Release" +# Name "Test - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\tests\sockets_test.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Sockets/Project/echoserver.dsp b/Sockets/Project/echoserver.dsp new file mode 100644 index 0000000..d848565 --- /dev/null +++ b/Sockets/Project/echoserver.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="echoserver" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=echoserver - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "echoserver.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "echoserver.mak" CFG="echoserver - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "echoserver - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "echoserver - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "echoserver - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "echoserver___Win32_Release" +# PROP BASE Intermediate_Dir "echoserver___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "R" +# PROP Intermediate_Dir "echoserver___Win32_Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I "..\..\Sockets" /I "c:\openssl\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /c +# ADD BASE RSC /l 0x41d /d "NDEBUG" +# ADD RSC /l 0x41d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib ssleay32md.lib libeay32md.lib /nologo /subsystem:console /machine:I386 /libpath:"\openssl\lib\vc" /libpath:"..\..\lib\r" + +!ELSEIF "$(CFG)" == "echoserver - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "echoserver___Win32_Debug" +# PROP BASE Intermediate_Dir "echoserver___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "D" +# PROP Intermediate_Dir "echoserver___Win32_Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I "..\..\Sockets" /I "c:\openssl\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /GZ /c +# ADD BASE RSC /l 0x41d /d "_DEBUG" +# ADD RSC /l 0x41d /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib ssleay32md.lib libeay32md.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"\openssl\lib\vc" /libpath:"..\..\lib\d" + +!ENDIF + +# Begin Target + +# Name "echoserver - Win32 Release" +# Name "echoserver - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\tests\echoserver.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Sockets/Project/stressclient.dsp b/Sockets/Project/stressclient.dsp new file mode 100644 index 0000000..34eb22d --- /dev/null +++ b/Sockets/Project/stressclient.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="stressclient" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stressclient - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stressclient.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stressclient.mak" CFG="stressclient - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stressclient - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stressclient - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stressclient - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "R" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I "..\..\Sockets" /I "c:\openssl\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /c +# ADD BASE RSC /l 0x41d /d "NDEBUG" +# ADD RSC /l 0x41d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib ssleay32md.lib libeay32md.lib /nologo /subsystem:console /machine:I386 /libpath:"\openssl\lib\vc" /libpath:"..\..\lib\r" + +!ELSEIF "$(CFG)" == "stressclient - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "D" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I "..\..\Sockets" /I "c:\openssl\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "HAVE_OPENSSL" /YX /FD /GZ /c +# ADD BASE RSC /l 0x41d /d "_DEBUG" +# ADD RSC /l 0x41d /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Sockets.lib ssleay32md.lib libeay32md.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"\openssl\lib\vc" /libpath:"..\..\lib\d" + +!ENDIF + +# Begin Target + +# Name "stressclient - Win32 Release" +# Name "stressclient - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\tests\stressclient.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Sockets/README.macosx b/Sockets/README.macosx new file mode 100644 index 0000000..b886673 --- /dev/null +++ b/Sockets/README.macosx @@ -0,0 +1,3 @@ +Find uuid.h here + http://www.die.net/doc/linux/include/uuid/uuid.h + diff --git a/Sockets/RandomNumber.cpp b/Sockets/RandomNumber.cpp new file mode 100644 index 0000000..77ba30d --- /dev/null +++ b/Sockets/RandomNumber.cpp @@ -0,0 +1,116 @@ +/** + * @author Adam McLaurin + * @date September 2006 + */ +/* +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GN6U General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "RandomNumber.h" +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +const unsigned long int RandomNumber::X_SEED_DEFAULT = 123456789UL; +const unsigned long int RandomNumber::Y_SEED_DEFAULT = 362436069UL; +const unsigned long int RandomNumber::Z_SEED_DEFAULT = 521288629UL; +const unsigned long int RandomNumber::W_SEED_DEFAULT = 88675123UL; + + +RandomNumber::RandomNumber(bool time_shuffle) +:mXSeed(time_shuffle ? (unsigned long)time(NULL) ^ X_SEED_DEFAULT : X_SEED_DEFAULT) +,mYSeed(time_shuffle ? (unsigned long)time(NULL) ^ Y_SEED_DEFAULT : Y_SEED_DEFAULT) +,mZSeed(time_shuffle ? (unsigned long)time(NULL) ^ Z_SEED_DEFAULT : Z_SEED_DEFAULT) +,mWSeed(time_shuffle ? (unsigned long)time(NULL) ^ W_SEED_DEFAULT : W_SEED_DEFAULT) +{ + reset(); +} + +RandomNumber::RandomNumber( + unsigned long int x_seed, + unsigned long int y_seed, + unsigned long int z_seed, + unsigned long int w_seed) +:mXSeed(x_seed) +,mYSeed(y_seed) +,mZSeed(z_seed) +,mWSeed(w_seed) +{ + reset(); +} + +RandomNumber::~RandomNumber() +{ +} + +void RandomNumber::reset() +{ + mX = mXSeed; + mY = mYSeed; + mZ = mZSeed; + mW = mWSeed; +} + +RandomNumber::operator unsigned long int() const +{ + return(mW); +} + +unsigned long int RandomNumber::next() +{ + register unsigned long int t = (mX ^ (mX<<11)); + + mX = mY; + + mY = mZ; + + mZ = mW; + + return(mW = (mW ^ (mW>>19)) ^ (t ^ (t>>8))); +} + +unsigned long int RandomNumber::skip(unsigned long int s) +{ + for(register unsigned long int i = 0 ; i < s ; ++i) + { + (void)next(); + } + + return(mW); +} + +void RandomNumber::getSeed( + unsigned long int& x_seed, + unsigned long int& y_seed, + unsigned long int& z_seed, + unsigned long int& w_seed) +{ + x_seed = mXSeed; + y_seed = mYSeed; + z_seed = mZSeed; + w_seed = mWSeed; +} + +unsigned long int RandomNumber::max_random() +{ + return(std::numeric_limits::max()); +} + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/RandomNumber.h b/Sockets/RandomNumber.h new file mode 100644 index 0000000..376db43 --- /dev/null +++ b/Sockets/RandomNumber.h @@ -0,0 +1,202 @@ +/** + * @author Adam McLaurin + * @date September 2006 + */ +/* +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _SOCKET_RandomNumber_H +#define _SOCKET_RandomNumber_H + +#include "sockets-config.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** + * The following class uses an xorshift algorithm proposed in the following + * paper: + * - http://www.jstatsoft.org/v08/i14/xorshift.pdf + * + * The algorithm provides a PRNG with a period of (2^128)-1 + * + * This PRNG is *not* intended for cryptographic purposes + */ +class RandomNumber +{ + +public: + /** + * Default constructor + * + * NOTE: Internal seeds are set to defaults proposed by the paper + */ + RandomNumber(bool time_shuffle = false); + + /** + * Custom constructor + * + * @param x_seed X seed + * + * @param y_seed Y seed + * + * @param z_seed Z seed + * + * @param w_seed W seed + */ + RandomNumber( + unsigned long int x_seed, + unsigned long int y_seed, + unsigned long int z_seed, + unsigned long int w_seed); + + /** + * Destructor + */ + ~RandomNumber(); + + // public methods + + /** + * Reset internal state to initial seed values + */ + void reset(); + + /** + * Cast operator to obtain current random value in the PRNG + * + * @return Current random value in the PRNG + */ + operator unsigned long int() const; + + /** + * Go to the next number in the PRNG sequence + * + * NOTE: This method is a slightly modified implementation of the xor128() + * function proposed in the paper + * + * @return Next value produced by the PRNG (after updating) + */ + unsigned long int next(); + + /** + * Skip ahead in the PRNG sequence by a given number of iterations + * + * @param s Number of iterations to skip ahead + * + * @return Value produced by the PRNG after skipping ahead in the sequence + */ + unsigned long int skip(unsigned long int s); + + /** + * Obtain all the initial seeds for this PRNG + * + * @param x_seed X seed (output) + * + * @param y_seed Y seed (output) + * + * @param z_seed Z seed (output) + * + * @param w_seed W seed (output) + */ + void getSeed( + unsigned long int& x_seed, + unsigned long int& y_seed, + unsigned long int& z_seed, + unsigned long int& w_seed); + + /** + * Get the maximum possible random number from this PRNG + * + * @return Maximum possible random number from this PRNG + */ + static unsigned long int max_random(); + + // public constants + + /** + * Default x-seed as proposed by the paper + */ + static const unsigned long int X_SEED_DEFAULT; + + /** + * Default y-seed as proposed by the paper + */ + static const unsigned long int Y_SEED_DEFAULT; + + /** + * Default z-seed as proposed by the paper + */ + static const unsigned long int Z_SEED_DEFAULT; + + /** + * Default w-seed as proposed by the paper + */ + static const unsigned long int W_SEED_DEFAULT; + +private: + /** + * X seed + */ + unsigned long int mXSeed; + + /** + * Y seed + */ + unsigned long int mYSeed; + + /** + * Z seed + */ + unsigned long int mZSeed; + + /** + * W seed + */ + unsigned long int mWSeed; + + /** + * X value + */ + unsigned long int mX; + + /** + * Y value + */ + unsigned long int mY; + + /** + * Z value + */ + unsigned long int mZ; + + /** + * W value + * + * NOTE: This is the externally-visible next value produced by the PRNG + */ + unsigned long int mW; + +}; + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_RandomNumber_H + diff --git a/Sockets/ResolvServer.cpp b/Sockets/ResolvServer.cpp new file mode 100644 index 0000000..83f842b --- /dev/null +++ b/Sockets/ResolvServer.cpp @@ -0,0 +1,97 @@ +/** \file ResolvServer.cpp + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "ResolvServer.h" +#ifdef ENABLE_RESOLVER +#include "StdoutLog.h" +#include "ListenSocket.h" +#include "ResolvSocket.h" +#include "SocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +ResolvServer::ResolvServer(port_t port) +:Thread() +,m_quit(false) +,m_port(port) +,m_ready(false) +{ +} + + +ResolvServer::~ResolvServer() +{ +} + + +void ResolvServer::Run() +{ +// StdoutLog log; + SocketHandler h; + ListenSocket l(h); + + if (l.Bind("127.0.0.1", m_port)) + { + return; + } + h.Add(&l); + + m_ready = true; + while (!m_quit && IsRunning() ) + { + h.Select(0, 500000); + } + SetRunning(false); +} + + +void ResolvServer::Quit() +{ + m_quit = true; +} + + +bool ResolvServer::Ready() +{ + return m_ready; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER + diff --git a/Sockets/ResolvServer.h b/Sockets/ResolvServer.h new file mode 100644 index 0000000..f8f8f5a --- /dev/null +++ b/Sockets/ResolvServer.h @@ -0,0 +1,73 @@ +/** \file ResolvServer.h + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ResolvServer_H +#define _SOCKETS_ResolvServer_H +#include "sockets-config.h" +#ifdef ENABLE_RESOLVER +#include "socket_include.h" +#include "Thread.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup async Asynchronous DNS */ +/** Async DNS resolver thread. + \ingroup async */ +class ResolvServer : public Thread +{ +public: + ResolvServer(port_t); + ~ResolvServer(); + + void Run(); + void Quit(); + + bool Ready(); + +private: + ResolvServer(const ResolvServer& ) {} // copy constructor + ResolvServer& operator=(const ResolvServer& ) { return *this; } // assignment operator + + bool m_quit; + port_t m_port; + bool m_ready; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER +#endif // _SOCKETS_ResolvServer_H + diff --git a/Sockets/ResolvSocket.cpp b/Sockets/ResolvSocket.cpp new file mode 100644 index 0000000..4a3644f --- /dev/null +++ b/Sockets/ResolvSocket.cpp @@ -0,0 +1,436 @@ +/** \file ResolvSocket.cpp + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif +#else +#include +#endif +#include "ResolvSocket.h" +#ifdef ENABLE_RESOLVER +#include "Utility.h" +#include "Parse.h" +#include "ISocketHandler.h" +#include "Lock.h" +#include "Mutex.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +// static +ResolvSocket::cache_t ResolvSocket::m_cache; +ResolvSocket::timeout_t ResolvSocket::m_cache_to; +Mutex ResolvSocket::m_cache_mutex; + + +ResolvSocket::ResolvSocket(ISocketHandler& h) +:TcpSocket(h) +,m_bServer(false) +,m_parent(NULL) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(false) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + + +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, const std::string& host, port_t port, bool ipv6) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_host(host) +,m_resolv_port(port) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(ipv6) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + + +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, ipaddr_t a) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_port(0) +,m_resolv_address(a) +#ifdef ENABLE_IPV6 +,m_resolve_ipv6(false) +#endif +,m_cached(false) +{ + SetLineProtocol(); +} + + +#ifdef ENABLE_IPV6 +ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, in6_addr& a) +:TcpSocket(h) +,m_bServer(false) +,m_parent(parent) +,m_resolv_port(0) +,m_resolve_ipv6(true) +,m_resolv_address6(a) +,m_cached(false) +{ + SetLineProtocol(); +} +#endif + + +ResolvSocket::~ResolvSocket() +{ +} + + +void ResolvSocket::OnLine(const std::string& line) +{ + Parse pa(line, ":"); + if (m_bServer) + { + m_query = pa.getword(); + m_data = pa.getrest(); +DEB( fprintf(stderr, " *** ResolvSocket server; query=%s, data=%s\n", m_query.c_str(), m_data.c_str());) + // %! check cache + { + Lock lock(m_cache_mutex); + if (m_cache[m_query].find(m_data) != m_cache[m_query].end()) + { + if (time(NULL) - m_cache_to[m_query][m_data] < 3600) // ttl + { + std::string result = m_cache[m_query][m_data]; +DEB(fprintf(stderr, " *** Returning cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), result.c_str());) + Send("Cached\n"); + if (!result.size()) /* failed */ + { + Send("Failed\n\n"); + SetCloseAndDelete(); + return; + } + else + if (m_query == "gethostbyname") + { + Send("A: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (m_query == "gethostbyname2") + { + Send("AAAA: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + else +#endif +#endif + if (m_query == "gethostbyaddr") + { + Send("Name: " + result + "\n\n"); + SetCloseAndDelete(); + return; + } + } + } + } + if (!Detach()) // detach failed? + { + SetCloseAndDelete(); + } + return; + } + std::string key = pa.getword(); + std::string value = pa.getrest(); +DEB( fprintf(stderr, " *** ResolvSocket response; %s: %s\n", key.c_str(), value.c_str());) + + if (key == "Cached") + { + m_cached = true; + } + else + if (key == "Failed" && m_parent) + { +DEB( fprintf(stderr, " ************ Resolve failed\n");) + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnResolveFailed(m_resolv_id); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } + else + if (key == "Name" && !m_resolv_host.size() && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnReverseResolved(m_resolv_id, value); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } + else + if (key == "A" && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + ipaddr_t l; + Utility::u2ip(value, l); // ip2ipaddr_t + m_parent -> OnResolved(m_resolv_id, l, m_resolv_port); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; // always use first ip in case there are several + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + else + if (key == "AAAA" && m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + in6_addr a; + Utility::u2ip(value, a); + m_parent -> OnResolved(m_resolv_id, a, m_resolv_port); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } +#endif +#endif +} + + +void ResolvSocket::OnDetached() +{ +DEB( fprintf(stderr, " *** ResolvSocket::OnDetached(); query=%s, data=%s\n", m_query.c_str(), m_data.c_str());) + if (m_query == "gethostbyname") + { + struct sockaddr_in sa; + if (Utility::u2ip(m_data, sa)) + { + std::string ip; + Utility::l2ip(sa.sin_addr, ip); + Send("A: " + ip + "\n"); + } + else + { + Send("Failed\n"); + } + Send("\n"); + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (m_query == "gethostbyname2") + { + struct sockaddr_in6 sa; + if (Utility::u2ip(m_data, sa)) + { + std::string ip; + Utility::l2ip(sa.sin6_addr, ip); + Send("AAAA: " + ip + "\n"); + } + else + { + Send("Failed\n"); + } + Send("\n"); + } + else +#endif +#endif + if (m_query == "gethostbyaddr") + { + if (Utility::isipv4( m_data )) + { + struct sockaddr_in sa; + if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST)) + { + Send("Failed: convert to sockaddr_in failed\n"); + } + else + { + std::string name; + if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name)) + { + Send("Failed: ipv4 reverse lookup of " + m_data + "\n"); + } + else + { + Send("Name: " + name + "\n"); + } + } + } + else +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (Utility::isipv6( m_data )) + { + struct sockaddr_in6 sa; + if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST)) + { + Send("Failed: convert to sockaddr_in6 failed\n"); + } + else + { + std::string name; + if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name)) + { + Send("Failed: ipv6 reverse lookup of " + m_data + "\n"); + } + else + { + Send("Name: " + name + "\n"); + } + } + } + else +#endif +#endif + { + Send("Failed: malformed address\n"); + } + Send("\n"); + } + else + { + std::string msg = "Unknown query type: " + m_query; + Handler().LogError(this, "OnDetached", 0, msg); + Send("Unknown\n\n"); + } + SetCloseAndDelete(); +} + + +void ResolvSocket::OnConnect() +{ + if (m_resolv_host.size()) + { +#ifdef ENABLE_IPV6 + std::string msg = (m_resolve_ipv6 ? "gethostbyname2 " : "gethostbyname ") + m_resolv_host + "\n"; + m_query = m_resolve_ipv6 ? "gethostbyname2" : "gethostbyname"; +#else + std::string msg = "gethostbyname " + m_resolv_host + "\n"; + m_query = "gethostbyname"; +#endif + m_data = m_resolv_host; + Send( msg ); + return; + } +#ifdef ENABLE_IPV6 + if (m_resolve_ipv6) + { + std::string tmp; + Utility::l2ip(m_resolv_address6, tmp); + m_query = "gethostbyaddr"; + m_data = tmp; + std::string msg = "gethostbyaddr " + tmp + "\n"; + Send( msg ); + } +#endif + std::string tmp; + Utility::l2ip(m_resolv_address, tmp); + m_query = "gethostbyaddr"; + m_data = tmp; + std::string msg = "gethostbyaddr " + tmp + "\n"; + Send( msg ); +} + + +void ResolvSocket::OnDelete() +{ + if (m_parent) + { + if (Handler().Resolving(m_parent) || Handler().Valid(m_parent)) + { + m_parent -> OnResolveFailed(m_resolv_id); + } + // update cache + if (!m_cached) + { + Lock lock(m_cache_mutex); + std::string value; +DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());) + m_cache[m_query][m_data] = value; + m_cache_to[m_query][m_data] = time(NULL); + } + m_parent = NULL; + } +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER + diff --git a/Sockets/ResolvSocket.h b/Sockets/ResolvSocket.h new file mode 100644 index 0000000..ff4b1a5 --- /dev/null +++ b/Sockets/ResolvSocket.h @@ -0,0 +1,106 @@ +/** \file ResolvSocket.h + ** \date 2005-03-24 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_ResolvSocket_H +#define _SOCKETS_ResolvSocket_H +#include "sockets-config.h" +#ifdef ENABLE_RESOLVER +#include "TcpSocket.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Mutex; + +/** Async DNS resolver socket. + \ingroup async */ +class ResolvSocket : public TcpSocket +{ + typedef std::map > cache_t; /* host, result */ + typedef std::map > timeout_t; /* host, time */ + +public: + ResolvSocket(ISocketHandler&); + ResolvSocket(ISocketHandler&, Socket *parent, const std::string& host, port_t port, bool ipv6 = false); + ResolvSocket(ISocketHandler&, Socket *parent, ipaddr_t); +#ifdef ENABLE_IPV6 + ResolvSocket(ISocketHandler&, Socket *parent, in6_addr&); +#endif + ~ResolvSocket(); + + void OnAccept() { m_bServer = true; } + void OnLine(const std::string& line); + void OnDetached(); + void OnDelete(); + + void SetId(int x) { m_resolv_id = x; } + int GetId() { return m_resolv_id; } + + void OnConnect(); + +#ifdef ENABLE_IPV6 + void SetResolveIpv6(bool x = true) { m_resolve_ipv6 = x; } +#endif + +private: + ResolvSocket(const ResolvSocket& s) : TcpSocket(s) {} // copy constructor + ResolvSocket& operator=(const ResolvSocket& ) { return *this; } // assignment operator + + std::string m_query; + std::string m_data; + bool m_bServer; + Socket *m_parent; + int m_resolv_id; + std::string m_resolv_host; + port_t m_resolv_port; + ipaddr_t m_resolv_address; +#ifdef ENABLE_IPV6 + bool m_resolve_ipv6; + in6_addr m_resolv_address6; +#endif + static cache_t m_cache; + static timeout_t m_cache_to; + static Mutex m_cache_mutex; + bool m_cached; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // ENABLE_RESOLVER +#endif // _SOCKETS_ResolvSocket_H + diff --git a/Sockets/SSLInitializer.cpp b/Sockets/SSLInitializer.cpp new file mode 100644 index 0000000..65218b1 --- /dev/null +++ b/Sockets/SSLInitializer.cpp @@ -0,0 +1,144 @@ +/** + ** \file SSLInitializer.cpp + ** \date 2007-04-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#endif +#include "SSLInitializer.h" +#ifdef HAVE_OPENSSL +#include +#include "Utility.h" +#include +#include "Mutex.h" + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + +SSLInitializer::SSLInitializer() +{ +DEB( fprintf(stderr, "SSLInitializer()\n");) + + bio_err = NULL; + m_rand_size = 1024; + + /* An error write context */ + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); + + /* Global system initialization*/ + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + CRYPTO_set_locking_callback( SSL_locking_function ); + CRYPTO_set_id_callback( SSL_id_function ); + + char *randfile = getenv("RANDFILE"); + char *home = getenv("HOME"); + if (!randfile && !home) + { + char *homepath = getenv("HOMEPATH"); + if (homepath) + { + Utility::SetEnv("HOME", homepath); + } + } + char path[512]; + *path = 0; + RAND_file_name(path, 512); + if (*path) + { + m_rand_file = path; + m_rand_size = 1024; + RAND_write_file(path); + } + else + { +DEB( fprintf(stderr, "SSLInitializer: no random file generated\n");) + } + + /* Load randomness */ + if (!m_rand_file.size() || !RAND_load_file(m_rand_file.c_str(), m_rand_size)) + { +DEB( fprintf(stderr, "SSLInitializer: PRNG not initialized\n");) + } + +} + + +SSLInitializer::~SSLInitializer() +{ +DEB( fprintf(stderr, "~SSLInitializer()\n");) + DeleteRandFile(); + // %! delete mutexes +} + + +void SSLInitializer::DeleteRandFile() +{ + if (m_rand_file.size()) + { + unlink(m_rand_file.c_str()); + } +} + + +void SSLInitializer::SSL_locking_function(int mode, int n, const char *file, int line) +{ +static std::map mmap; + if (mmap.find(n) == mmap.end()) + { + mmap[n] = new Mutex; + } + if (mode & CRYPTO_LOCK) + { + mmap[n] -> Lock(); + } + else + { + mmap[n] -> Unlock(); + } +} + + +unsigned long SSLInitializer::SSL_id_function() +{ + return Utility::ThreadID(); +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // HAVE_OPENSSL + diff --git a/Sockets/SSLInitializer.h b/Sockets/SSLInitializer.h new file mode 100644 index 0000000..9f81d1c --- /dev/null +++ b/Sockets/SSLInitializer.h @@ -0,0 +1,76 @@ +/** + ** \file SSLInitializer.h + ** \date 2007-04-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef __SOCKETS_SSLInitializer_H +#define __SOCKETS_SSLInitializer_H +#include "sockets-config.h" +#ifdef HAVE_OPENSSL + +#include +#include + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class SSLInitializer +{ +public: + /** + init openssl + bio_err + create random file + */ + SSLInitializer(); + + /** + remove random file + */ + ~SSLInitializer(); + + void DeleteRandFile(); + + /** SSL; mutex locking function callback. */ +static void SSL_locking_function(int mode, int n, const char *file, int line); + + /** Return thread id. */ +static unsigned long SSL_id_function(); + + BIO *bio_err; + +private: + std::string m_rand_file; + long m_rand_size; + +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // HAVE_OPENSSL +#endif // __SOCKETS_SSLInitializer_H + diff --git a/Sockets/SctpSocket.cpp b/Sockets/SctpSocket.cpp new file mode 100644 index 0000000..2267086 --- /dev/null +++ b/Sockets/SctpSocket.cpp @@ -0,0 +1,496 @@ +/** + ** \file SctpSocket.cpp + ** \date 2006-09-04 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SctpSocket.h" +#ifdef USE_SCTP +#include "Utility.h" +#include "ISocketHandler.h" +#include +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE +{ +#endif + + +SctpSocket::SctpSocket(ISocketHandler& h,int type) : StreamSocket(h) +,m_type(type) +,m_buf(new char[SCTP_BUFSIZE_READ]) +{ + if (type != SOCK_STREAM && type != SOCK_SEQPACKET) + { + } +} + + +SctpSocket::~SctpSocket() +{ + delete[] m_buf; +} + + +int SctpSocket::Bind(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return Bind(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return Bind(ad); +} + + +int SctpSocket::Bind(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), m_type, "sctp")); + } + if (GetSocket() != INVALID_SOCKET) + { + int n = bind(GetSocket(), ad, ad); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "bind() failed", LOG_LEVEL_ERROR); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for SctpSocket, port: " + Utility::l2string(ad.GetPort())); +#endif + } + return n; + } + return -1; +} + + +int SctpSocket::AddAddress(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return AddAddress(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return AddAddress(ad); +} + + +int SctpSocket::AddAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "AddAddress called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_ADD_ADDR); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR); + } + return n; +} + + +int SctpSocket::RemoveAddress(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return RemoveAddress(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return RemoveAddress(ad); +} + + +int SctpSocket::RemoveAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "RemoveAddress called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_REM_ADDR); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR); + } + return n; +} + + +int SctpSocket::Open(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return Open(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return Open(ad); +} + + +int SctpSocket::Open(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), m_type, "sctp")); + } + if (GetSocket() != INVALID_SOCKET) + { + if (!SetNonblocking(true)) + { + return -1; + } + int n = connect(GetSocket(), ad, ad); + if (n == -1) + { + // check error code that means a connect is in progress +#ifdef _WIN32 + if (Errno == WSAEWOULDBLOCK) +#else + if (Errno == EINPROGRESS) +#endif + { + Handler().LogError(this, "connect: connection pending", Errno, StrError(Errno), LOG_LEVEL_INFO); + SetConnecting( true ); // this flag will control fd_set's + } + else + { + Handler().LogError(this, "SctpSocket", -1, "connect() failed", LOG_LEVEL_ERROR); + } + } + return n; + } + return -1; +} + + +#ifndef SOLARIS +int SctpSocket::AddConnection(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return AddConnection(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return AddConnection(ad); +} + + +int SctpSocket::AddConnection(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "AddConnection called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_connectx(GetSocket(), ad, ad); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_connectx() failed", LOG_LEVEL_ERROR); + } + else + { + SetConnecting(); + } + return n; +} +#endif + + +int SctpSocket::getpaddrs(sctp_assoc_t id,std::list& vec) +{ + struct sockaddr *p = NULL; + int n = sctp_getpaddrs(GetSocket(), id, &p); + if (!n || n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_getpaddrs failed", LOG_LEVEL_WARNING); + return n; + } + for (int i = 0; i < n; i++) + { + vec.push_back(Utility::Sa2String(&p[i])); + } + sctp_freepaddrs(p); + return n; +} + + +int SctpSocket::getladdrs(sctp_assoc_t id,std::list& vec) +{ + struct sockaddr *p = NULL; + int n = sctp_getladdrs(GetSocket(), id, &p); + if (!n || n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_getladdrs failed", LOG_LEVEL_WARNING); + return n; + } + for (int i = 0; i < n; i++) + { + vec.push_back(Utility::Sa2String(&p[i])); + } + sctp_freeladdrs(p); + return n; +} + + +int SctpSocket::PeelOff(sctp_assoc_t id) +{ + int n = sctp_peeloff(GetSocket(), id); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "PeelOff failed", LOG_LEVEL_WARNING); + return -1; + } + Socket *p = Create(); + p -> Attach(n); + p -> SetDeleteByHandler(); + Handler().Add(p); + return n; +} + + +void SctpSocket::OnRead() +{ +/* + int sctp_recvmsg(int sd, void * msg, size_t * len, + struct sockaddr * from, socklen_t * fromlen, + struct sctp_sndrcvinfo * sinfo, int * msg_flags); + + DESCRIPTION + sctp_recvmsg is a wrapper library function that can be used to receive a message from a socket while using the advanced + features of SCTP. sd is the socket descriptor on which the message pointed to by msg of length len is received. + + If from is not NULL, the source address of the message is filled in. The argument fromlen is a value-result parameter. + initialized to the size of the buffer associated with from , and modified on return to indicate the actual size of the + address stored. + + sinfo is a pointer to a sctp_sndrcvinfo structure to be filled upon receipt of the message. msg_flags is a pointer to a + integer that is filled with any message flags like MSG_NOTIFICATION or MSG_EOR. + +*/ + struct sockaddr sa; + socklen_t sa_len = 0; + struct sctp_sndrcvinfo sinfo; + int flags = 0; + int n = sctp_recvmsg(GetSocket(), m_buf, SCTP_BUFSIZE_READ, &sa, &sa_len, &sinfo, &flags); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + else + { + OnReceiveMessage(m_buf, n, &sa, sa_len, &sinfo, flags); + } +} + + +void SctpSocket::OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags) +{ +} + + +void SctpSocket::OnWrite() +{ + if (Connecting()) + { + int err = SoError(); + + // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on + /// \todo add to read fd_set here + if (!err) // ok + { + Set(!IsDisableRead(), false); + SetConnecting(false); + SetCallOnConnect(); + return; + } + Handler().LogError(this, "sctp: connect failed", err, StrError(err), LOG_LEVEL_FATAL); + Set(false, false); // no more monitoring because connection failed + + // failed +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + return; + } +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + // even though the connection failed at once, only retry after + // the connection timeout. + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + return; + } + SetConnecting(false); + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + return; + } +} + + +void SctpSocket::OnConnectTimeout() +{ + Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL); +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + // retry direct connection + } + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + IncreaseConnectionRetries(); + // ask socket via OnConnectRetry callback if we should continue trying + if (OnConnectRetry()) + { + SetRetryClientConnect(); + } + else + { + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + } + } + else + { + SetCloseAndDelete(true); + /// \todo state reason why connect failed + OnConnectFailed(); + } + // + SetConnecting(false); +} + + +#ifdef _WIN32 +void SctpSocket::OnException() +{ + if (Connecting()) + { +#ifdef ENABLE_SOCKS4 + if (Socks4()) + OnSocks4ConnectFailed(); + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && + GetConnectionRetries() < GetConnectionRetry() )) + { + // even though the connection failed at once, only retry after + // the connection timeout + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + } + else + { + SetConnecting(false); // tnx snibbe + SetCloseAndDelete(); + OnConnectFailed(); + } + return; + } + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} +#endif // _WIN32 + + +int SctpSocket::Protocol() +{ + return IPPROTO_SCTP; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE +#endif + + +#endif // USE_SCTP + diff --git a/Sockets/SctpSocket.h b/Sockets/SctpSocket.h new file mode 100644 index 0000000..12e72a0 --- /dev/null +++ b/Sockets/SctpSocket.h @@ -0,0 +1,110 @@ +/** + ** \file SctpSocket.h + ** \date 2006-09-04 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SctpSocket_H +#define _SOCKETS_SctpSocket_H +#include "sockets-config.h" + +#include +#include "StreamSocket.h" +#ifdef USE_SCTP +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define SCTP_BUFSIZE_READ 16400 + +class SocketAddress; + + +class SctpSocket : public StreamSocket +{ +public: + /** SctpSocket constructor. + \param h Owner + \param type SCTP_STREAM or SCTP_SEQPACKET */ + SctpSocket(ISocketHandler& h,int type); + ~SctpSocket(); + + /** bind() */ + int Bind(const std::string&,port_t); + int Bind(SocketAddress&); + /** sctp_bindx() */ + int AddAddress(const std::string&,port_t); + int AddAddress(SocketAddress&); + /** sctp_bindx() */ + int RemoveAddress(const std::string&,port_t); + int RemoveAddress(SocketAddress&); + + /** connect() */ + int Open(const std::string&,port_t); + int Open(SocketAddress&); + + /** Connect timeout callback. */ + void OnConnectTimeout(); +#ifdef _WIN32 + /** Connection failed reported as exception on win32 */ + void OnException(); +#endif + +#ifndef SOLARIS + /** sctp_connectx() */ + int AddConnection(const std::string&,port_t); + int AddConnection(SocketAddress&); +#endif + + /** Get peer addresses of an association. */ + int getpaddrs(sctp_assoc_t id,std::list&); + /** Get all bound addresses of an association. */ + int getladdrs(sctp_assoc_t id,std::list&); + + /** sctp_peeloff */ + int PeelOff(sctp_assoc_t id); + + /** recvmsg callback */ + virtual void OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags) = 0; + + void OnOptions(int,int,int,SOCKET) {} + + virtual int Protocol(); + +protected: + SctpSocket(const SctpSocket& s) : StreamSocket(s) {} + void OnRead(); + void OnWrite(); + +private: + SctpSocket& operator=(const SctpSocket& s) { return *this; } + int m_type; ///< SCTP_STREAM or SCTP_SEQPACKET + char *m_buf; ///< Temporary receive buffer +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE +#endif + +#endif // USE_SCTP +#endif // _SOCKETS_SctpSocket_H + diff --git a/Sockets/Semaphore.cpp b/Sockets/Semaphore.cpp new file mode 100644 index 0000000..93cefc7 --- /dev/null +++ b/Sockets/Semaphore.cpp @@ -0,0 +1,76 @@ +/** + ** \file Semaphore.cpp + ** \date 2007-04-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Semaphore.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +Semaphore::Semaphore() +{ + sem_init(&m_sem, 0, 0); +} + + +Semaphore::Semaphore(unsigned int start_val) +{ + sem_init(&m_sem, 0, start_val); +} + + +Semaphore::~Semaphore() +{ + sem_destroy(&m_sem); +} + + +int Semaphore::Post() +{ + return sem_post(&m_sem); +} + + +int Semaphore::Wait() +{ + return sem_wait(&m_sem); +} + + +int Semaphore::TryWait() +{ + return sem_trywait(&m_sem); +} + + +int Semaphore::GetValue(int& i) +{ + return sem_getvalue(&m_sem, &i); +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/Semaphore.h b/Sockets/Semaphore.h new file mode 100644 index 0000000..acafea4 --- /dev/null +++ b/Sockets/Semaphore.h @@ -0,0 +1,67 @@ +/** + ** \file Semaphore.h + ** \date 2007-04-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Semaphore_H +#define _SOCKETS_Semaphore_H + +#include "sockets-config.h" +#include +#ifdef MACOSX +#include +#else +#include +#endif + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** pthread semaphore wrapper. + \ingroup threading */ +class Semaphore +{ +public: + Semaphore(); + Semaphore(unsigned int start_val); + ~Semaphore(); + + int Post(); + int Wait(); + int TryWait(); + int GetValue(int&); + +private: + Semaphore(const Semaphore& ) {} // copy constructor + Semaphore& operator=(const Semaphore& ) { return *this; } // assignment operator + sem_t m_sem; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_Semaphore_H + diff --git a/Sockets/SmtpdSocket.cpp b/Sockets/SmtpdSocket.cpp new file mode 100644 index 0000000..0d62925 --- /dev/null +++ b/Sockets/SmtpdSocket.cpp @@ -0,0 +1,234 @@ +/** + ** \file SmtpdSocket.cpp + ** \date 2007-05-10 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SmtpdSocket.h" +#include "Parse.h" +#include "Utility.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +SmtpdSocket::SmtpdSocket(ISocketHandler& h) +:TcpSocket(h) +,m_hello(false) +,m_data(false) +,m_header(false) +{ + SetLineProtocol(); +} + + +void SmtpdSocket::OnAccept() +{ + Send("220 ESMTP; \r\n"); +} + + +void SmtpdSocket::OnLine(const std::string& line) +{ + if (m_data) + { + if (m_header) + { + if (!line.size()) + { + if (m_header_line.size()) + { + Parse pa(m_header_line, ":"); + std::string key = pa.getword(); + OnHeader(key, pa.getrest()); + } + m_header = false; + OnHeaderComplete(); + } + else + if (line[0] == ' ' || line[0] == '\t') + { + m_header_line += line; + } + else + { + if (m_header_line.size()) + { + Parse pa(m_header_line, ":"); + std::string key = pa.getword(); + OnHeader(key, pa.getrest()); + } + m_header_line = line; + } + } + else + if (line == ".") + { + m_data = false; + if (OnDataComplete()) + Send("250 OK\r\n"); + else + Send("550 Failed\r\n"); + } + else + if (line.size() && line[0] == '.') + { + OnData(line.substr(1)); + } + else + { + OnData(line); + } + return; + } + Parse pa(line); + std::string cmd = Utility::ToUpper(pa.getword()); + + if (cmd == "EHLO") + { + if (!OnHello(pa.getrest())) + { + Send("550 Failed\r\n"); + } + else + { + m_hello = true; + Send("250 mail.alhem.net\r\n"); + } + } + else + if (cmd == "HELO") + { + if (!OnHello(pa.getrest())) + { + Send("550 Failed\r\n"); + } + else + { + m_hello = true; + Send("250 mail.alhem.net\r\n"); + } + } + else + if (!m_hello) + { + OnAbort(SMTP_NO_HELLO); + SetCloseAndDelete(); + } + else + if (cmd == "MAIL") // mail from: + { + Parse pa(line, ":"); + pa.getword(); // 'mail' + pa.getword(); // 'from' + std::string email = Utility::ToLower(pa.getrest()); + + EmailAddress addr( email ); + if (addr.GetName().size() > 64) + { + OnAbort(SMTP_NAME_TOO_LONG); + Send("500 Name too long.\r\n"); + return; + } + if (addr.GetDomain().size() > 64) + { + OnAbort(SMTP_DOMAIN_TOO_LONG); + Send("500 Domain too long.\r\n"); + return; + } + + if (!OnMailFrom( addr )) + { + Send("550 Failed\r\n"); + } + else + { + Send("250 OK\r\n"); + } + } + else + if (cmd == "RCPT") // rcpt to: + { + Parse pa(line, ":"); + pa.getword(); // 'rcpt' + pa.getword(); // 'to' + std::string email = Utility::ToLower(pa.getrest()); + // %! reject based on user / domain? + EmailAddress addr( email ); + + if (addr.GetName().size() > 64) + { + OnAbort(SMTP_NAME_TOO_LONG); + Send("500 Name too long.\r\n"); + return; + } + if (addr.GetDomain().size() > 64) + { + OnAbort(SMTP_DOMAIN_TOO_LONG); + Send("500 Domain too long.\r\n"); + return; + } + + if (!OnRcptTo( addr )) + { + Send("553 Failed\r\n"); + } + else + { + Send("250 OK\r\n"); + } + } + else + if (cmd == "DATA") + { + Send("354 Enter mail, end with \".\" on a line by itself\r\n"); + m_data = true; + m_header = false; + } + else + if (cmd == "RSET") + { + m_data = false; + m_header = false; + OnRset(); + Send("250 OK\r\n"); // %! ??? + } + else + if (cmd == "QUIT") + { + OnAbort(SMTP_QUIT); + Send("221 Bye Bye\r\n"); + SetCloseAndDelete(); + } + else + if (cmd == "NOOP") + { + Send("250 OK\r\n"); + } + else + { + OnNotSupported(cmd, pa.getrest()); + } +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/SmtpdSocket.h b/Sockets/SmtpdSocket.h new file mode 100644 index 0000000..d884d05 --- /dev/null +++ b/Sockets/SmtpdSocket.h @@ -0,0 +1,143 @@ +/** + ** \file SmtpdSocket.h + ** \date 2007-05-10 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SmtpdSocket_H +#define _SOCKETS_SmtpdSocket_H + +#include "sockets-config.h" +#include +#include "TcpSocket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Smtp server base class. */ +class SmtpdSocket : public TcpSocket +{ +protected: + typedef enum { + SMTP_NO_HELLO, + SMTP_NAME_TOO_LONG, + SMTP_DOMAIN_TOO_LONG, + SMTP_QUIT + } reason_t; + +public: + class EmailAddress { + public: + EmailAddress(const std::string& str_in) + { + std::string str = str_in; + size_t i = str.find("<"); + if (i != std::string::npos) + str = str.substr(i + 1); + i = str.find("@"); + if (i != std::string::npos) + { + m_name = str.substr(0, i); + str = str.substr(i + 1); + i = str.find(">"); + if (i != std::string::npos) + str = str.substr(0, i); + m_domain = str; + } + while (m_name.size() && m_name[m_name.size() - 1] == ' ') + m_name.resize(m_name.size() - 1); + while (m_domain.size() && m_domain[m_domain.size() - 1] == ' ') + m_domain.resize(m_domain.size() - 1); + while (m_name.size() && m_name[0] == ' ') + m_name = m_name.substr(1); + while (m_domain.size() && m_domain[0] == ' ') + m_domain = m_domain.substr(1); + m_top = m_domain; + { + for (size_t i = 0; i < m_domain.size(); i++) + { + if (m_domain[i] == '.') + { + m_sub = m_top; + m_top = m_domain.substr(i + 1); + } + } + } + } + + const std::string& GetName() const { return m_name; } + const std::string& GetDomain() const { return m_domain; } + const std::string& GetTopDomain() const { return m_top; } + const std::string& GetSubDomain() const { return m_sub; } + + std::string ToString() const { return m_name + "@" + m_domain; } + + private: + std::string m_name; + std::string m_domain; + std::string m_top; + std::string m_sub; + }; + +public: + SmtpdSocket(ISocketHandler&); + + void OnAccept(); + void OnLine(const std::string&); + + /** \return 'false' to abort */ + virtual bool OnHello(const std::string& domain) = 0; + + /** \return 'false' to abort */ + virtual bool OnMailFrom(const EmailAddress& addr) = 0; + + /** \return 'false' to abort */ + virtual bool OnRcptTo(const EmailAddress& addr) = 0; + + virtual void OnHeader(const std::string& key, const std::string& value) = 0; + + virtual void OnHeaderComplete() = 0; + + virtual void OnData(const std::string& line) = 0; + + /** \return 'false' if message write failed (message will probably be resent) */ + virtual bool OnDataComplete() = 0; + + virtual void OnRset() = 0; + + virtual void OnAbort(reason_t) = 0; + + virtual void OnNotSupported(const std::string& cmd, const std::string& arg) = 0; + +private: + bool m_hello; // we need HELO or EHLO first of all + bool m_data; + bool m_header; + std::string m_header_line; +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_SmtpdSocket_H + diff --git a/Sockets/Socket.cpp b/Sockets/Socket.cpp new file mode 100644 index 0000000..42d2c23 --- /dev/null +++ b/Sockets/Socket.cpp @@ -0,0 +1,1896 @@ +/** \file Socket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Socket.h" +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#else +#include +#include +#endif +#include +#include + +#include "ISocketHandler.h" +#include "Utility.h" + +#include "SocketAddress.h" +#include "SocketHandler.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif +#include "Ipv4Address.h" + +#ifdef _DEBUG +#define DEB(x) x; fflush(stderr); +#else +#define DEB(x) +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// statics +#ifdef _WIN32 +WSAInitializer Socket::m_winsock_init; +#endif + + +Socket::Socket(ISocketHandler& h) +//:m_flags(0) +:m_handler(h) +,m_socket( INVALID_SOCKET ) +,m_bDel(false) +,m_bClose(false) +,m_tCreate(time(NULL)) +,m_parent(NULL) +,m_b_disable_read(false) +,m_connected(false) +,m_b_erased_by_handler(false) +,m_tClose(0) +,m_client_remote_address(NULL) +,m_remote_address(NULL) +,m_traffic_monitor(NULL) +#ifdef HAVE_OPENSSL +,m_b_enable_ssl(false) +,m_b_ssl(false) +,m_b_ssl_server(false) +#endif +#ifdef ENABLE_IPV6 +,m_ipv6(false) +#endif +#ifdef ENABLE_POOL +,m_socket_type(0) +,m_bClient(false) +,m_bRetain(false) +,m_bLost(false) +#endif +#ifdef ENABLE_SOCKS4 +,m_bSocks4(false) +,m_socks4_host(h.GetSocks4Host()) +,m_socks4_port(h.GetSocks4Port()) +,m_socks4_userid(h.GetSocks4Userid()) +#endif +#ifdef ENABLE_DETACH +,m_detach(false) +,m_detached(false) +,m_pThread(NULL) +,m_slave_handler(NULL) +#endif +{ +} + + +Socket::~Socket() +{ + Handler().Remove(this); + if (m_socket != INVALID_SOCKET +#ifdef ENABLE_POOL + && !m_bRetain +#endif + ) + { + Close(); + } +} + + +void Socket::Init() +{ +} + + +void Socket::OnRead() +{ +} + + +void Socket::OnWrite() +{ +} + + +void Socket::OnException() +{ + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} + + +void Socket::OnDelete() +{ +} + + +void Socket::OnConnect() +{ +} + + +void Socket::OnAccept() +{ +} + + +int Socket::Close() +{ + if (m_socket == INVALID_SOCKET) // this could happen + { + Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING); + return 0; + } + int n; + if ((n = closesocket(m_socket)) == -1) + { + // failed... + Handler().LogError(this, "close", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + Handler().Set(m_socket, false, false, false); // remove from fd_set's + Handler().AddList(m_socket, LIST_CALLONCONNECT, false); +#ifdef ENABLE_DETACH + Handler().AddList(m_socket, LIST_DETACH, false); +#endif + Handler().AddList(m_socket, LIST_TIMEOUT, false); + Handler().AddList(m_socket, LIST_RETRY, false); + Handler().AddList(m_socket, LIST_CLOSE, false); + m_socket = INVALID_SOCKET; + return n; +} + + +SOCKET Socket::CreateSocket(int af,int type, const std::string& protocol) +{ + struct protoent *p = NULL; + SOCKET s; + +#ifdef ENABLE_POOL + m_socket_type = type; + m_socket_protocol = protocol; +#endif + if (protocol.size()) + { + p = getprotobyname( protocol.c_str() ); + if (!p) + { + Handler().LogError(this, "getprotobyname", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception(std::string("getprotobyname() failed: ") + StrError(Errno)); +#endif + return INVALID_SOCKET; + } + } + int protno = p ? p -> p_proto : 0; + + s = socket(af, type, protno); + if (s == INVALID_SOCKET) + { + Handler().LogError(this, "socket", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception(std::string("socket() failed: ") + StrError(Errno)); +#endif + return INVALID_SOCKET; + } + Attach(s); + OnOptions(af, type, protno, s); + Attach(INVALID_SOCKET); + return s; +} + + +void Socket::Attach(SOCKET s) +{ + m_socket = s; +} + + +SOCKET Socket::GetSocket() +{ + return m_socket; +} + + +void Socket::SetDeleteByHandler(bool x) +{ + m_bDel = x; +} + + +bool Socket::DeleteByHandler() +{ + return m_bDel; +} + + +void Socket::SetCloseAndDelete(bool x) +{ + if (x != m_bClose) + { + Handler().AddList(m_socket, LIST_CLOSE, x); + m_bClose = x; + if (x) + { + m_tClose = time(NULL); + } + } +} + + +bool Socket::CloseAndDelete() +{ + return m_bClose; +} + + +void Socket::SetRemoteAddress(SocketAddress& ad) //struct sockaddr* sa, socklen_t l) +{ + m_remote_address = ad.GetCopy(); +} + + +std::auto_ptr Socket::GetRemoteSocketAddress() +{ + return std::auto_ptr(m_remote_address -> GetCopy()); +} + + +ISocketHandler& Socket::Handler() const +{ +#ifdef ENABLE_DETACH + if (IsDetached()) + return *m_slave_handler; +#endif + return m_handler; +} + + +ISocketHandler& Socket::MasterHandler() const +{ + return m_handler; +} + + +ipaddr_t Socket::GetRemoteIP4() +{ + ipaddr_t l = 0; +#ifdef ENABLE_IPV6 + if (m_ipv6) + { + Handler().LogError(this, "GetRemoteIP4", 0, "get ipv4 address for ipv6 socket", LOG_LEVEL_WARNING); + } +#endif + if (m_remote_address.get() != NULL) + { + struct sockaddr *p = *m_remote_address; + struct sockaddr_in *sa = (struct sockaddr_in *)p; + memcpy(&l, &sa -> sin_addr, sizeof(struct in_addr)); + } + return l; +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +struct in6_addr Socket::GetRemoteIP6() +{ + if (!m_ipv6) + { + Handler().LogError(this, "GetRemoteIP6", 0, "get ipv6 address for ipv4 socket", LOG_LEVEL_WARNING); + } + struct sockaddr_in6 fail; + if (m_remote_address.get() != NULL) + { + struct sockaddr *p = *m_remote_address; + memcpy(&fail, p, sizeof(struct sockaddr_in6)); + } + else + { + memset(&fail, 0, sizeof(struct sockaddr_in6)); + } + return fail.sin6_addr; +} +#endif +#endif + + +port_t Socket::GetRemotePort() +{ + if (!m_remote_address.get()) + { + return 0; + } + return m_remote_address -> GetPort(); +} + + +std::string Socket::GetRemoteAddress() +{ + if (!m_remote_address.get()) + { + return ""; + } + return m_remote_address -> Convert(false); +} + + +std::string Socket::GetRemoteHostname() +{ + if (!m_remote_address.get()) + { + return ""; + } + return m_remote_address -> Reverse(); +} + + +bool Socket::SetNonblocking(bool bNb) +{ +#ifdef _WIN32 + unsigned long l = bNb ? 1 : 0; + int n = ioctlsocket(m_socket, FIONBIO, &l); + if (n != 0) + { + Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, ""); + return false; + } + return true; +#else + if (bNb) + { + if (fcntl(m_socket, F_SETFL, O_NONBLOCK) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + else + { + if (fcntl(m_socket, F_SETFL, 0) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + return true; +#endif +} + + +bool Socket::SetNonblocking(bool bNb, SOCKET s) +{ +#ifdef _WIN32 + unsigned long l = bNb ? 1 : 0; + int n = ioctlsocket(s, FIONBIO, &l); + if (n != 0) + { + Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, ""); + return false; + } + return true; +#else + if (bNb) + { + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + else + { + if (fcntl(s, F_SETFL, 0) == -1) + { + Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR); + return false; + } + } + return true; +#endif +} + + +void Socket::Set(bool bRead, bool bWrite, bool bException) +{ + Handler().Set(m_socket, bRead, bWrite, bException); +} + + +bool Socket::Ready() +{ + if (m_socket != INVALID_SOCKET && !CloseAndDelete()) + return true; + return false; +} + + +void Socket::OnLine(const std::string& ) +{ +} + + +void Socket::OnConnectFailed() +{ +} + + +Socket *Socket::GetParent() +{ + return m_parent; +} + + +void Socket::SetParent(Socket *x) +{ + m_parent = x; +} + + +port_t Socket::GetPort() +{ + Handler().LogError(this, "GetPort", 0, "GetPort only implemented for ListenSocket", LOG_LEVEL_WARNING); + return 0; +} + + +bool Socket::OnConnectRetry() +{ + return true; +} + + +#ifdef ENABLE_RECONNECT +void Socket::OnReconnect() +{ +} +#endif + + +time_t Socket::Uptime() +{ + return time(NULL) - m_tCreate; +} + + +#ifdef ENABLE_IPV6 +void Socket::SetIpv6(bool x) +{ + m_ipv6 = x; +} + + +bool Socket::IsIpv6() +{ + return m_ipv6; +} +#endif + + +void Socket::DisableRead(bool x) +{ + m_b_disable_read = x; +} + + +bool Socket::IsDisableRead() +{ + return m_b_disable_read; +} + + +void Socket::SendBuf(const char *,size_t,int) +{ +} + + +void Socket::Send(const std::string&,int) +{ +} + + +void Socket::SetConnected(bool x) +{ + m_connected = x; +} + + +bool Socket::IsConnected() +{ + return m_connected; +} + + +void Socket::OnDisconnect() +{ +} + + +void Socket::SetErasedByHandler(bool x) +{ + m_b_erased_by_handler = x; +} + + +bool Socket::ErasedByHandler() +{ + return m_b_erased_by_handler; +} + + +time_t Socket::TimeSinceClose() +{ + return time(NULL) - m_tClose; +} + + +void Socket::SetClientRemoteAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SetClientRemoteAddress", 0, "remote address not valid", LOG_LEVEL_ERROR); + } + m_client_remote_address = ad.GetCopy(); +} + + +std::auto_ptr Socket::GetClientRemoteAddress() +{ + if (!m_client_remote_address.get()) + { + Handler().LogError(this, "GetClientRemoteAddress", 0, "remote address not yet set", LOG_LEVEL_ERROR); + } + return std::auto_ptr(m_client_remote_address -> GetCopy()); +} + + +uint64_t Socket::GetBytesSent(bool) +{ + return 0; +} + + +uint64_t Socket::GetBytesReceived(bool) +{ + return 0; +} + + +#ifdef HAVE_OPENSSL +void Socket::OnSSLConnect() +{ +} + + +void Socket::OnSSLAccept() +{ +} + + +bool Socket::SSLNegotiate() +{ + return false; +} + + +bool Socket::IsSSL() +{ + return m_b_enable_ssl; +} + + +void Socket::EnableSSL(bool x) +{ + m_b_enable_ssl = x; +} + + +bool Socket::IsSSLNegotiate() +{ + return m_b_ssl; +} + + +void Socket::SetSSLNegotiate(bool x) +{ + m_b_ssl = x; +} + + +bool Socket::IsSSLServer() +{ + return m_b_ssl_server; +} + + +void Socket::SetSSLServer(bool x) +{ + m_b_ssl_server = x; +} + + +void Socket::OnSSLConnectFailed() +{ +} + + +void Socket::OnSSLAcceptFailed() +{ +} +#endif // HAVE_OPENSSL + + +#ifdef ENABLE_POOL +void Socket::CopyConnection(Socket *sock) +{ + Attach( sock -> GetSocket() ); +#ifdef ENABLE_IPV6 + SetIpv6( sock -> IsIpv6() ); +#endif + SetSocketType( sock -> GetSocketType() ); + SetSocketProtocol( sock -> GetSocketProtocol() ); + + SetClientRemoteAddress( *sock -> GetClientRemoteAddress() ); + SetRemoteAddress( *sock -> GetRemoteSocketAddress() ); +} + + +void Socket::SetIsClient() +{ + m_bClient = true; +} + + +void Socket::SetSocketType(int x) +{ + m_socket_type = x; +} + + +int Socket::GetSocketType() +{ + return m_socket_type; +} + + +void Socket::SetSocketProtocol(const std::string& x) +{ + m_socket_protocol = x; +} + + +const std::string& Socket::GetSocketProtocol() +{ + return m_socket_protocol; +} + + +void Socket::SetRetain() +{ + if (m_bClient) m_bRetain = true; +} + + +bool Socket::Retain() +{ + return m_bRetain; +} + + +void Socket::SetLost() +{ + m_bLost = true; +} + + +bool Socket::Lost() +{ + return m_bLost; +} +#endif // ENABLE_POOL + + +#ifdef ENABLE_SOCKS4 +void Socket::OnSocks4Connect() +{ + Handler().LogError(this, "OnSocks4Connect", 0, "Use with TcpSocket only"); +} + + +void Socket::OnSocks4ConnectFailed() +{ + Handler().LogError(this, "OnSocks4ConnectFailed", 0, "Use with TcpSocket only"); +} + + +bool Socket::OnSocks4Read() +{ + Handler().LogError(this, "OnSocks4Read", 0, "Use with TcpSocket only"); + return true; +} + + +void Socket::SetSocks4Host(const std::string& host) +{ + Utility::u2ip(host, m_socks4_host); +} + + +bool Socket::Socks4() +{ + return m_bSocks4; +} + + +void Socket::SetSocks4(bool x) +{ + m_bSocks4 = x; +} + + +void Socket::SetSocks4Host(ipaddr_t a) +{ + m_socks4_host = a; +} + + +void Socket::SetSocks4Port(port_t p) +{ + m_socks4_port = p; +} + + +void Socket::SetSocks4Userid(const std::string& x) +{ + m_socks4_userid = x; +} + + +ipaddr_t Socket::GetSocks4Host() +{ + return m_socks4_host; +} + + +port_t Socket::GetSocks4Port() +{ + return m_socks4_port; +} + + +const std::string& Socket::GetSocks4Userid() +{ + return m_socks4_userid; +} +#endif // ENABLE_SOCKS4 + + +#ifdef ENABLE_DETACH +bool Socket::Detach() +{ + if (!DeleteByHandler()) + return false; + if (m_pThread) + return false; + if (m_detached) + return false; + SetDetach(); + return true; +} + + +void Socket::DetachSocket() +{ + SetDetached(); + m_pThread = new SocketThread(this); + m_pThread -> SetRelease(true); +} + + +void Socket::OnDetached() +{ +} + + +void Socket::SetDetach(bool x) +{ + Handler().AddList(m_socket, LIST_DETACH, x); + m_detach = x; +} + + +bool Socket::IsDetach() +{ + return m_detach; +} + + +void Socket::SetDetached(bool x) +{ + m_detached = x; +} + + +const bool Socket::IsDetached() const +{ + return m_detached; +} + + +void Socket::SetSlaveHandler(ISocketHandler *p) +{ + m_slave_handler = p; +} + + +Socket::SocketThread::SocketThread(Socket *p) +:Thread(false) +,m_socket(p) +{ + // Creator will release +} + + +Socket::SocketThread::~SocketThread() +{ + if (IsRunning()) + { + SetRelease(true); + SetRunning(false); +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } +} + + +void Socket::SocketThread::Run() +{ + SocketHandler h; + h.SetSlave(); + h.Add(m_socket); + m_socket -> SetSlaveHandler(&h); + m_socket -> OnDetached(); + while (h.GetCount() && IsRunning()) + { + h.Select(0, 500000); + } + // m_socket now deleted oops + // yeah oops m_socket delete its socket thread, that means this + // so Socket will no longer delete its socket thread, instead we do this: + SetDeleteOnExit(); +} +#endif // ENABLE_DETACH + + +#ifdef ENABLE_RESOLVER +int Socket::Resolve(const std::string& host,port_t port) +{ + return Handler().Resolve(this, host, port); +} + + +#ifdef ENABLE_IPV6 +int Socket::Resolve6(const std::string& host,port_t port) +{ + return Handler().Resolve6(this, host, port); +} +#endif + + +int Socket::Resolve(ipaddr_t a) +{ + return Handler().Resolve(this, a); +} + + +#ifdef ENABLE_IPV6 +int Socket::Resolve(in6_addr& a) +{ + return Handler().Resolve(this, a); +} +#endif + + +void Socket::OnResolved(int,ipaddr_t,port_t) +{ +} + + +#ifdef ENABLE_IPV6 +void Socket::OnResolved(int,in6_addr&,port_t) +{ +} +#endif + + +void Socket::OnReverseResolved(int,const std::string&) +{ +} + + +void Socket::OnResolveFailed(int) +{ +} +#endif // ENABLE_RESOLVER + + +/* IP options */ + + +bool Socket::SetIpOptions(const void *p, socklen_t len) +{ +#ifdef IP_OPTIONS + if (setsockopt(GetSocket(), IPPROTO_IP, IP_OPTIONS, (char *)p, len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_OPTIONS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_OPTIONS", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef IP_PKTINFO +bool Socket::SetIpPktinfo(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_PKTINFO, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_PKTINFO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_RECVTOS +bool Socket::SetIpRecvTOS(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTOS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_RECVTTL +bool Socket::SetIpRecvTTL(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTTL, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_RECVOPTS +bool Socket::SetIpRecvopts(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVOPTS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_RETOPTS +bool Socket::SetIpRetopts(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RETOPTS, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RETOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SetIpTOS(unsigned char tos) +{ +#ifdef IP_TOS + if (setsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO); + return false; +#endif +} + + +unsigned char Socket::IpTOS() +{ + unsigned char tos = 0; +#ifdef IP_TOS + socklen_t len = sizeof(tos); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, &len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO); +#endif + return tos; +} + + +bool Socket::SetIpTTL(int ttl) +{ +#ifdef IP_TTL + if (setsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO); + return false; +#endif +} + + +int Socket::IpTTL() +{ + int ttl = 0; +#ifdef IP_TTL + socklen_t len = sizeof(ttl); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, &len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO); +#endif + return ttl; +} + + +bool Socket::SetIpHdrincl(bool x) +{ +#ifdef IP_HDRINCL + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_HDRINCL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_HDRINCL", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef IP_RECVERR +bool Socket::SetIpRecverr(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVERR, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVERR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_MTU_DISCOVER +bool Socket::SetIpMtudiscover(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MTU_DISCOVER, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MTU_DISCOVER)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef IP_MTU +int Socket::IpMtu() +{ + int mtu = 0; + socklen_t len = sizeof(mtu); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_MTU, (char *)&mtu, &len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MTU)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } + return mtu; +} +#endif + + +#ifdef IP_ROUTER_ALERT +bool Socket::SetIpRouterAlert(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ROUTER_ALERT, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ROUTER_ALERT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SetIpMulticastTTL(int ttl) +{ +#ifdef IP_MULTICAST_TTL + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO); + return false; +#endif +} + + +int Socket::IpMulticastTTL() +{ + int ttl = 0; +#ifdef IP_MULTICAST_TTL + socklen_t len = sizeof(ttl); + if (getsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, &len) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO); +#endif + return ttl; +} + + +bool Socket::SetMulticastLoop(bool x) +{ +#ifdef IP_MULTICAST_LOOP + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_LOOP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_LOOP", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef LINUX +bool Socket::IpAddMembership(struct ip_mreqn& ref) +{ +#ifdef IP_ADD_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} +#endif + + +bool Socket::IpAddMembership(struct ip_mreq& ref) +{ +#ifdef IP_ADD_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef LINUX +bool Socket::IpDropMembership(struct ip_mreqn& ref) +{ +#ifdef IP_DROP_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} +#endif + + +bool Socket::IpDropMembership(struct ip_mreq& ref) +{ +#ifdef IP_DROP_MEMBERSHIP + if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO); + return false; +#endif +} + + +/* SOCKET options */ + + +bool Socket::SetSoReuseaddr(bool x) +{ +#ifdef SO_REUSEADDR + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_REUSEADDR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_REUSEADDR", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoKeepalive(bool x) +{ +#ifdef SO_KEEPALIVE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_KEEPALIVE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_KEEPALIVE", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef SO_NOSIGPIPE +bool Socket::SetSoNosigpipe(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SoAcceptconn() +{ + int value = 0; +#ifdef SO_ACCEPTCONN + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_ACCEPTCONN, (char *)&value, &len) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_ACCEPTCONN)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_ACCEPTCONN", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + + +#ifdef SO_BSDCOMPAT +bool Socket::SetSoBsdcompat(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BSDCOMPAT, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BSDCOMPAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef SO_BINDTODEVICE +bool Socket::SetSoBindtodevice(const std::string& intf) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BINDTODEVICE, (char *)intf.c_str(), intf.size()) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BINDTODEVICE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SetSoBroadcast(bool x) +{ +#ifdef SO_BROADCAST + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BROADCAST)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_BROADCAST", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoDebug(bool x) +{ +#ifdef SO_DEBUG + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_DEBUG, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DEBUG)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_DEBUG", LOG_LEVEL_INFO); + return false; +#endif +} + + +int Socket::SoError() +{ + int value = 0; +#ifdef SO_ERROR + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_ERROR, (char *)&value, &len) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_ERROR)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_ERROR", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + + +bool Socket::SetSoDontroute(bool x) +{ +#ifdef SO_DONTROUTE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_DONTROUTE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DONTROUTE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_DONTROUTE", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoLinger(int onoff, int linger) +{ +#ifdef SO_LINGER + struct linger stl; + stl.l_onoff = onoff; + stl.l_linger = linger; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_LINGER, (char *)&stl, sizeof(stl)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_LINGER)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_LINGER", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoOobinline(bool x) +{ +#ifdef SO_OOBINLINE + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_OOBINLINE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_OOBINLINE", LOG_LEVEL_INFO); + return false; +#endif +} + + +#ifdef SO_PASSCRED +bool Socket::SetSoPasscred(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PASSCRED, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PASSCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef SO_PEERCRED +bool Socket::SoPeercred(struct ucred& ucr) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PEERCRED, (char *)&ucr, sizeof(ucr)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PEERCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef SO_PRIORITY +bool Socket::SetSoPriority(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_PRIORITY, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PRIORITY)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SetSoRcvlowat(int x) +{ +#ifdef SO_RCVLOWAT + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVLOWAT, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVLOWAT", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoSndlowat(int x) +{ +#ifdef SO_SNDLOWAT + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDLOWAT, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDLOWAT", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoRcvtimeo(struct timeval& tv) +{ +#ifdef SO_RCVTIMEO + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVTIMEO", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoSndtimeo(struct timeval& tv) +{ +#ifdef SO_SNDTIMEO + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDTIMEO", LOG_LEVEL_INFO); + return false; +#endif +} + + +bool Socket::SetSoRcvbuf(int x) +{ +#ifdef SO_RCVBUF + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO); + return false; +#endif +} + + +int Socket::SoRcvbuf() +{ + int value = 0; +#ifdef SO_RCVBUF + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&value, &len) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + + +#ifdef SO_RCVBUFFORCE +bool Socket::SetSoRcvbufforce(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUFFORCE, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +bool Socket::SetSoSndbuf(int x) +{ +#ifdef SO_SNDBUF + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO); + return false; +#endif +} + + +int Socket::SoSndbuf() +{ + int value = 0; +#ifdef SO_SNDBUF + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&value, &len) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + + +#ifdef SO_SNDBUFFORCE +bool Socket::SetSoSndbufforce(int x) +{ + if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUFFORCE, (char *)&x, sizeof(x)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +#ifdef SO_TIMESTAMP +bool Socket::SetSoTimestamp(bool x) +{ + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_SOCKET, SO_TIMESTAMP, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_TIMESTAMP)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +} +#endif + + +int Socket::SoType() +{ + int value = 0; +#ifdef SO_TYPE + socklen_t len = sizeof(value); + if (getsockopt(GetSocket(), SOL_SOCKET, SO_TYPE, (char *)&value, &len) == -1) + { + Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_TYPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + } +#else + Handler().LogError(this, "socket option not available", 0, "SO_TYPE", LOG_LEVEL_INFO); +#endif + return value ? true : false; +} + + +#ifdef ENABLE_TRIGGERS +void Socket::Subscribe(int id) +{ + Handler().Subscribe(id, this); +} + + +void Socket::Unsubscribe(int id) +{ + Handler().Unsubscribe(id, this); +} + + +void Socket::OnTrigger(int, const TriggerData&) +{ +} + + +void Socket::OnCancelled(int) +{ +} +#endif + + +void Socket::SetTimeout(time_t secs) +{ + if (!secs) + { + Handler().AddList(m_socket, LIST_TIMEOUT, false); + return; + } + Handler().AddList(m_socket, LIST_TIMEOUT, true); + m_timeout_start = time(NULL); + m_timeout_limit = secs; +} + + +void Socket::OnTimeout() +{ +} + + +void Socket::OnConnectTimeout() +{ +} + + +bool Socket::Timeout(time_t tnow) +{ + if (tnow - m_timeout_start > m_timeout_limit) + return true; + return false; +} + + +/** Returns local port number for bound socket file descriptor. */ +port_t Socket::GetSockPort() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return ntohs(sa.sin6_port); + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return ntohs(sa.sin_port); +} + + +/** Returns local ipv4 address for bound socket file descriptor. */ +ipaddr_t Socket::GetSockIP4() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + return 0; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + ipaddr_t a; + memcpy(&a, &sa.sin_addr, 4); + return a; +} + + +/** Returns local ipv4 address as text for bound socket file descriptor. */ +std::string Socket::GetSockAddress() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + return ""; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + Ipv4Address addr( sa ); + return addr.Convert(); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +/** Returns local ipv6 address for bound socket file descriptor. */ +struct in6_addr Socket::GetSockIP6() +{ + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + return sa.sin6_addr; + } + struct in6_addr a; + memset(&a, 0, sizeof(a)); + return a; +} + + +/** Returns local ipv6 address as text for bound socket file descriptor. */ +std::string Socket::GetSockAddress6() +{ + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sockaddr_length = sizeof(struct sockaddr_in6); + if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1) + memset(&sa, 0, sizeof(sa)); + Ipv6Address addr( sa ); + return addr.Convert(); + } + return ""; +} +#endif +#endif + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/Socket.h b/Sockets/Socket.h new file mode 100644 index 0000000..764b69b --- /dev/null +++ b/Sockets/Socket.h @@ -0,0 +1,738 @@ +/** \file Socket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This software is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Socket_H +#define _SOCKETS_Socket_H +#include "sockets-config.h" + +#include +#include +#include +#ifdef HAVE_OPENSSL +#include +#endif + +#include "socket_include.h" +#include +#include "SocketAddress.h" +#include "Thread.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class ISocketHandler; +class SocketAddress; +class IFile; + + +/** \defgroup basic Basic sockets */ +/** Socket base class. + \ingroup basic */ +class Socket +{ + friend class ISocketHandler; +#ifdef ENABLE_DETACH + /** Detached socket run thread. + \ingroup internal */ + class SocketThread : public Thread + { + public: + SocketThread(Socket *p); + ~SocketThread(); + + void Run(); + + private: + Socket *GetSocket() const { return m_socket; } + SocketThread(const SocketThread& s) : m_socket(s.GetSocket()) {} + SocketThread& operator=(const SocketThread& ) { return *this; } + Socket *m_socket; + }; +#endif // ENABLE_DETACH + +#ifdef ENABLE_TRIGGERS +public: + /** Data pass class from source to destination. */ + class TriggerData + { + public: + TriggerData() : m_src(NULL) {} + virtual ~TriggerData() {} + + Socket *GetSource() const { return m_src; } + void SetSource(Socket *x) { m_src = x; } + + private: + Socket *m_src; + }; +#endif // ENABLE_TRIGGERS + + /** Socket mode flags. */ +/* + enum { + // Socket + SOCK_DEL = 0x01, ///< Delete by handler flag + SOCK_CLOSE = 0x02, ///< Close and delete flag + SOCK_DISABLE_READ = 0x04, ///< Disable checking for read events + SOCK_CONNECTED = 0x08, ///< Socket is connected (tcp/udp) + + SOCK_ERASED_BY_HANDLER = 0x10, ///< Set by handler before delete + // HAVE_OPENSSL + SOCK_ENABLE_SSL = 0x20, ///< Enable SSL for this TcpSocket + SOCK_SSL = 0x40, ///< ssl negotiation mode (TcpSocket) + SOCK_SSL_SERVER = 0x80, ///< True if this is an incoming ssl TcpSocket connection + + // ENABLE_IPV6 + SOCK_IPV6 = 0x0100, ///< This is an ipv6 socket if this one is true + // ENABLE_POOL + SOCK_CLIENT = 0x0200, ///< only client connections are pooled + SOCK_RETAIN = 0x0400, ///< keep connection on close + SOCK_LOST = 0x0800, ///< connection lost + + // ENABLE_SOCKS4 + SOCK_SOCKS4 = 0x1000, ///< socks4 negotiation mode (TcpSocket) + // ENABLE_DETACH + SOCK_DETACH = 0x2000, ///< Socket ordered to detach flag + SOCK_DETACHED = 0x4000, ///< Socket has been detached + // StreamSocket + STREAMSOCK_CONNECTING = 0x8000, ///< Flag indicating connection in progress + + STREAMSOCK_FLUSH_BEFORE_CLOSE = 0x010000L, ///< Send all data before closing (default true) + STREAMSOCK_CALL_ON_CONNECT = 0x020000L, ///< OnConnect will be called next ISocketHandler cycle if true + STREAMSOCK_RETRY_CONNECT = 0x040000L, ///< Try another connection attempt next ISocketHandler cycle + STREAMSOCK_LINE_PROTOCOL = 0x080000L, ///< Line protocol mode flag + + }; +*/ + +public: + /** "Default" constructor */ + Socket(ISocketHandler&); + + virtual ~Socket(); + + /** Socket class instantiation method. Used when a "non-standard" constructor + * needs to be used for the socket class. Note: the socket class still needs + * the "default" constructor with one ISocketHandler& as input parameter. + */ + virtual Socket *Create() { return NULL; } + + /** Returns reference to sockethandler that owns the socket. + If the socket is detached, this is a reference to the slave sockethandler. + */ + ISocketHandler& Handler() const; + + /** Returns reference to sockethandler that owns the socket. + This one always returns the reference to the original sockethandler, + even if the socket is detached. + */ + ISocketHandler& MasterHandler() const; + + /** Called by ListenSocket after accept but before socket is added to handler. + * CTcpSocket uses this to create its ICrypt member variable. + * The ICrypt member variable is created by a virtual method, therefore + * it can't be called directly from the CTcpSocket constructor. + * Also used to determine if incoming HTTP connection is normal (port 80) + * or ssl (port 443). + */ + virtual void Init(); + + /** Create a socket file descriptor. + \param af Address family AF_INET / AF_INET6 / ... + \param type SOCK_STREAM / SOCK_DGRAM / ... + \param protocol "tcp" / "udp" / ... */ + SOCKET CreateSocket(int af,int type,const std::string& protocol = ""); + + /** Assign this socket a file descriptor created + by a call to socket() or otherwise. */ + void Attach(SOCKET s); + + /** Return file descriptor assigned to this socket. */ + SOCKET GetSocket(); + + /** Close connection immediately - internal use. + \sa SetCloseAndDelete */ + virtual int Close(); + + /** Add file descriptor to sockethandler fd_set's. */ + void Set(bool bRead,bool bWrite,bool bException = true); + + /** Returns true when socket file descriptor is valid + and socket is not about to be closed. */ + virtual bool Ready(); + + /** Returns pointer to ListenSocket that created this instance + * on an incoming connection. */ + Socket *GetParent(); + + /** Used by ListenSocket to set parent pointer of newly created + * socket instance. */ + void SetParent(Socket *); + + /** Get listening port from ListenSocket<>. */ + virtual port_t GetPort(); + + /** Set socket non-block operation. */ + bool SetNonblocking(bool); + + /** Set socket non-block operation. */ + bool SetNonblocking(bool, SOCKET); + + /** Total lifetime of instance. */ + time_t Uptime(); + + /** Set address/port of last connect() call. */ + void SetClientRemoteAddress(SocketAddress&); + + /** Get address/port of last connect() call. */ + std::auto_ptr GetClientRemoteAddress(); + + /** Common interface for SendBuf used by Tcp and Udp sockets. */ + virtual void SendBuf(const char *,size_t,int = 0); + + /** Common interface for Send used by Tcp and Udp sockets. */ + virtual void Send(const std::string&,int = 0); + + /** Outgoing traffic counter. */ + virtual uint64_t GetBytesSent(bool clear = false); + + /** Incoming traffic counter. */ + virtual uint64_t GetBytesReceived(bool clear = false); + + // LIST_TIMEOUT + + /** Enable timeout control. 0=disable timeout check. */ + void SetTimeout(time_t secs); + + /** Check timeout. \return true if time limit reached */ + bool Timeout(time_t tnow); + + /** Used by ListenSocket. ipv4 and ipv6 */ + void SetRemoteAddress(SocketAddress&); + + /** \name Event callbacks */ + //@{ + + /** Called when there is something to be read from the file descriptor. */ + virtual void OnRead(); + /** Called when there is room for another write on the file descriptor. */ + virtual void OnWrite(); + /** Called on socket exception. */ + virtual void OnException(); + /** Called before a socket class is deleted by the ISocketHandler. */ + virtual void OnDelete(); + /** Called when a connection has completed. */ + virtual void OnConnect(); + /** Called when an incoming connection has been completed. */ + virtual void OnAccept(); + /** Called when a complete line has been read and the socket is in + * line protocol mode. */ + virtual void OnLine(const std::string& ); + /** Called on connect timeout (5s). */ + virtual void OnConnectFailed(); + /** Called when a client socket is created, to set socket options. + \param family AF_INET, AF_INET6, etc + \param type SOCK_STREAM, SOCK_DGRAM, etc + \param protocol Protocol number (tcp, udp, sctp, etc) + \param s Socket file descriptor + */ + virtual void OnOptions(int family,int type,int protocol,SOCKET s) = 0; + /** Connection retry callback - return false to abort connection attempts */ + virtual bool OnConnectRetry(); +#ifdef ENABLE_RECONNECT + /** a reconnect has been made */ + virtual void OnReconnect(); +#endif + /** TcpSocket: When a disconnect has been detected (recv/SSL_read returns 0 bytes). */ + virtual void OnDisconnect(); + /** Timeout callback. */ + virtual void OnTimeout(); + /** Connection timeout. */ + virtual void OnConnectTimeout(); + //@} + + /** \name Socket mode flags, set/reset */ + //@{ + /** Set delete by handler true when you want the sockethandler to + delete the socket instance after use. */ + void SetDeleteByHandler(bool = true); + /** Check delete by handler flag. + \return true if this instance should be deleted by the sockethandler */ + bool DeleteByHandler(); + + // LIST_CLOSE - conditional event queue + + /** Set close and delete to terminate the connection. */ + void SetCloseAndDelete(bool = true); + /** Check close and delete flag. + \return true if this socket should be closed and the instance removed */ + bool CloseAndDelete(); + + /** Return number of seconds since socket was ordered to close. \sa SetCloseAndDelete */ + time_t TimeSinceClose(); + + /** Ignore read events for an output only socket. */ + void DisableRead(bool x = true); + /** Check ignore read events flag. + \return true if read events should be ignored */ + bool IsDisableRead(); + + /** Set connected status. */ + void SetConnected(bool = true); + /** Check connected status. + \return true if connected */ + bool IsConnected(); + + /** Set flag indicating the socket is being actively deleted by the sockethandler. */ + void SetErasedByHandler(bool x = true); + /** Get value of flag indicating socket is deleted by sockethandler. */ + bool ErasedByHandler(); + + //@} + + /** \name Information about remote connection */ + //@{ + /** Returns address of remote end. */ + std::auto_ptr GetRemoteSocketAddress(); + /** Returns address of remote end: ipv4. */ + ipaddr_t GetRemoteIP4(); +#ifdef ENABLE_IPV6 + /** Returns address of remote end: ipv6. */ +#ifdef IPPROTO_IPV6 + struct in6_addr GetRemoteIP6(); +#endif +#endif + /** Returns remote port number: ipv4 and ipv6. */ + port_t GetRemotePort(); + /** Returns remote ip as string? ipv4 and ipv6. */ + std::string GetRemoteAddress(); + /** ipv4 and ipv6(not implemented) */ + std::string GetRemoteHostname(); + //@} + + /** Returns local port number for bound socket file descriptor. */ + port_t GetSockPort(); + /** Returns local ipv4 address for bound socket file descriptor. */ + ipaddr_t GetSockIP4(); + /** Returns local ipv4 address as text for bound socket file descriptor. */ + std::string GetSockAddress(); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Returns local ipv6 address for bound socket file descriptor. */ + struct in6_addr GetSockIP6(); + /** Returns local ipv6 address as text for bound socket file descriptor. */ + std::string GetSockAddress6(); +#endif +#endif + // -------------------------------------------------------------------------- + /** @name IP options + When an ip or socket option is available on all of the operating systems + I'm testing on (linux 2.4.x, _win32, macosx, solaris9 intel) they are not + checked with an #ifdef below. + This might cause a compile error on other operating systems. */ + // -------------------------------------------------------------------------- + + // IP options + //@{ + + bool SetIpOptions(const void *p, socklen_t len); + bool SetIpTOS(unsigned char tos); + unsigned char IpTOS(); + bool SetIpTTL(int ttl); + int IpTTL(); + bool SetIpHdrincl(bool x = true); + bool SetIpMulticastTTL(int); + int IpMulticastTTL(); + bool SetMulticastLoop(bool x = true); + bool IpAddMembership(struct ip_mreq&); + bool IpDropMembership(struct ip_mreq&); + +#ifdef IP_PKTINFO + bool SetIpPktinfo(bool x = true); +#endif +#ifdef IP_RECVTOS + bool SetIpRecvTOS(bool x = true); +#endif +#ifdef IP_RECVTTL + bool SetIpRecvTTL(bool x = true); +#endif +#ifdef IP_RECVOPTS + bool SetIpRecvopts(bool x = true); +#endif +#ifdef IP_RETOPTS + bool SetIpRetopts(bool x = true); +#endif +#ifdef IP_RECVERR + bool SetIpRecverr(bool x = true); +#endif +#ifdef IP_MTU_DISCOVER + bool SetIpMtudiscover(bool x = true); +#endif +#ifdef IP_MTU + int IpMtu(); +#endif +#ifdef IP_ROUTER_ALERT + bool SetIpRouterAlert(bool x = true); +#endif +#ifdef LINUX + bool IpAddMembership(struct ip_mreqn&); +#endif +#ifdef LINUX + bool IpDropMembership(struct ip_mreqn&); +#endif + //@} + + // SOCKET options + /** @name Socket Options */ + //@{ + + bool SoAcceptconn(); + bool SetSoBroadcast(bool x = true); + bool SetSoDebug(bool x = true); + int SoError(); + bool SetSoDontroute(bool x = true); + bool SetSoLinger(int onoff, int linger); + bool SetSoOobinline(bool x = true); + bool SetSoRcvlowat(int); + bool SetSoSndlowat(int); + bool SetSoRcvtimeo(struct timeval&); + bool SetSoSndtimeo(struct timeval&); + bool SetSoRcvbuf(int); + int SoRcvbuf(); + bool SetSoSndbuf(int); + int SoSndbuf(); + int SoType(); + bool SetSoReuseaddr(bool x = true); + bool SetSoKeepalive(bool x = true); + +#ifdef SO_BSDCOMPAT + bool SetSoBsdcompat(bool x = true); +#endif +#ifdef SO_BINDTODEVICE + bool SetSoBindtodevice(const std::string& intf); +#endif +#ifdef SO_PASSCRED + bool SetSoPasscred(bool x = true); +#endif +#ifdef SO_PEERCRED + bool SoPeercred(struct ucred& ); +#endif +#ifdef SO_PRIORITY + bool SetSoPriority(int); +#endif +#ifdef SO_RCVBUFFORCE + bool SetSoRcvbufforce(int); +#endif +#ifdef SO_SNDBUFFORCE + bool SetSoSndbufforce(int); +#endif +#ifdef SO_TIMESTAMP + bool SetSoTimestamp(bool x = true); +#endif +#ifdef SO_NOSIGPIPE + bool SetSoNosigpipe(bool x = true); +#endif + //@} + + // TCP options in TcpSocket.h/TcpSocket.cpp + + +#ifdef HAVE_OPENSSL + /** @name SSL Support */ + //@{ + /** SSL client/server support - internal use. \sa TcpSocket */ + virtual void OnSSLConnect(); + /** SSL client/server support - internal use. \sa TcpSocket */ + virtual void OnSSLAccept(); + /** SSL negotiation failed for client connect. */ + virtual void OnSSLConnectFailed(); + /** SSL negotiation failed for server accept. */ + virtual void OnSSLAcceptFailed(); + /** new SSL support */ + virtual bool SSLNegotiate(); + /** Check if SSL is Enabled for this TcpSocket. + \return true if this is a TcpSocket with SSL enabled */ + bool IsSSL(); + /** Enable SSL operation for a TcpSocket. */ + void EnableSSL(bool x = true); + /** Still negotiating ssl connection. + \return true if ssl negotiating is still in progress */ + bool IsSSLNegotiate(); + /** Set flag indicating ssl handshaking still in progress. */ + void SetSSLNegotiate(bool x = true); + /** OnAccept called with SSL Enabled. + \return true if this is a TcpSocket with an incoming SSL connection */ + bool IsSSLServer(); + /** Set flag indicating that this is a TcpSocket with incoming SSL connection. */ + void SetSSLServer(bool x = true); + /** SSL; Get pointer to ssl context structure. */ + virtual SSL_CTX *GetSslContext() { return NULL; } + /** SSL; Get pointer to ssl structure. */ + virtual SSL *GetSsl() { return NULL; } + //@} +#endif // HAVE_OPENSSL + +#ifdef ENABLE_IPV6 + /** Enable ipv6 for this socket. */ + void SetIpv6(bool x = true); + /** Check ipv6 socket. + \return true if this is an ipv6 socket */ + bool IsIpv6(); +#endif + +#ifdef ENABLE_POOL + /** @name Connection Pool */ + //@{ + /** Client = connecting TcpSocket. */ + void SetIsClient(); + /** Socket type from socket() call. */ + void SetSocketType(int x); + /** Socket type from socket() call. */ + int GetSocketType(); + /** Protocol type from socket() call. */ + void SetSocketProtocol(const std::string& x); + /** Protocol type from socket() call. */ + const std::string& GetSocketProtocol(); + /** Instruct a client socket to stay open in the connection pool after use. + If you have connected to a server using tcp, you can call SetRetain + to leave the connection open after your socket instance has been deleted. + The next connection you make to the same server will reuse the already + opened connection, if it is still available. + */ + void SetRetain(); + /** Check retain flag. + \return true if the socket should be moved to connection pool after use */ + bool Retain(); + /** Connection lost - error while reading/writing from a socket - TcpSocket only. */ + void SetLost(); + /** Check connection lost status flag, used by TcpSocket only. + \return true if there was an error while r/w causing the socket to close */ + bool Lost(); + /** Copy connection parameters from sock. */ + void CopyConnection(Socket *sock); + //@} +#endif // ENABLE_POOL + +#ifdef ENABLE_SOCKS4 + /** \name Socks4 support */ + //@{ + /** Socks4 client support internal use. \sa TcpSocket */ + virtual void OnSocks4Connect(); + /** Socks4 client support internal use. \sa TcpSocket */ + virtual void OnSocks4ConnectFailed(); + /** Socks4 client support internal use. \sa TcpSocket */ + virtual bool OnSocks4Read(); + /** Called when the last write caused the tcp output buffer to + * become empty. */ + /** socket still in socks4 negotiation mode */ + bool Socks4(); + /** Set flag indicating Socks4 handshaking in progress */ + void SetSocks4(bool x = true); + + /** Set socks4 server host address to use */ + void SetSocks4Host(ipaddr_t a); + /** Set socks4 server hostname to use. */ + void SetSocks4Host(const std::string& ); + /** Socks4 server port to use. */ + void SetSocks4Port(port_t p); + /** Provide a socks4 userid if required by the socks4 server. */ + void SetSocks4Userid(const std::string& x); + /** Get the ip address of socks4 server to use. + \return socks4 server host address */ + ipaddr_t GetSocks4Host(); + /** Get the socks4 server port to use. + \return socks4 server port */ + port_t GetSocks4Port(); + /** Get socks4 userid. + \return Socks4 userid */ + const std::string& GetSocks4Userid(); + //@} +#endif // ENABLE_SOCKS4 + +#ifdef ENABLE_RESOLVER + /** \name Asynchronous Resolver */ + //@{ + /** Request an asynchronous dns resolution. + \param host hostname to be resolved + \param port port number passed along for the ride + \return Resolve ID */ + int Resolve(const std::string& host,port_t port = 0); +#ifdef ENABLE_IPV6 + int Resolve6(const std::string& host, port_t port = 0); +#endif + /** Callback returning a resolved address. + \param id Resolve ID from Resolve call + \param a resolved ip address + \param port port number passed to Resolve */ + virtual void OnResolved(int id,ipaddr_t a,port_t port); +#ifdef ENABLE_IPV6 + virtual void OnResolved(int id,in6_addr& a,port_t port); +#endif + /** Request asynchronous reverse dns lookup. + \param a in_addr to be translated */ + int Resolve(ipaddr_t a); +#ifdef ENABLE_IPV6 + int Resolve(in6_addr& a); +#endif + /** Callback returning reverse resolve results. + \param id Resolve ID + \param name Resolved hostname */ + virtual void OnReverseResolved(int id,const std::string& name); + /** Callback indicating failed dns lookup. + \param id Resolve ID */ + virtual void OnResolveFailed(int id); + //@} +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_DETACH + /** \name Thread Support */ + //@{ + /** Callback fires when a new socket thread has started and this + socket is ready for operation again. + \sa ResolvSocket */ + virtual void OnDetached(); + + // LIST_DETACH + + /** Internal use. */ + void SetDetach(bool x = true); + /** Check detach flag. + \return true if the socket should detach to its own thread */ + bool IsDetach(); + + /** Internal use. */ + void SetDetached(bool x = true); + /** Check detached flag. + \return true if the socket runs in its own thread. */ + const bool IsDetached() const; + /** Order this socket to start its own thread and call OnDetached + when ready for operation. */ + bool Detach(); + /** Store the slave sockethandler pointer. */ + void SetSlaveHandler(ISocketHandler *); + /** Create new thread for this socket to run detached in. */ + void DetachSocket(); + //@} +#endif // ENABLE_DETACH + + /** Write traffic to an IFile. Socket will not delete this object. */ + void SetTrafficMonitor(IFile *p) { m_traffic_monitor = p; } + +#ifdef ENABLE_TRIGGERS + /** \name Triggers */ + //@{ + /** Subscribe to trigger id. */ + void Subscribe(int id); + /** Unsubscribe from trigger id. */ + void Unsubscribe(int id); + /** Trigger callback, with data passed from source to destination. */ + virtual void OnTrigger(int id, const TriggerData& data); + /** Trigger cancelled because source has been deleted (as in delete). */ + virtual void OnCancelled(int id); + //@} +#endif + +protected: + /** default constructor not available */ + Socket() : m_handler(m_handler) {} + /** copy constructor not available */ + Socket(const Socket& s) : m_handler(s.m_handler) {} + + /** assignment operator not available. */ + Socket& operator=(const Socket& ) { return *this; } + + /** All traffic will be written to this IFile, if set. */ + IFile *GetTrafficMonitor() { return m_traffic_monitor; } + +// unsigned long m_flags; ///< boolean flags, replacing old 'bool' members + +private: + ISocketHandler& m_handler; ///< Reference of ISocketHandler in control of this socket + SOCKET m_socket; ///< File descriptor + bool m_bDel; ///< Delete by handler flag + bool m_bClose; ///< Close and delete flag + time_t m_tCreate; ///< Time in seconds when this socket was created + Socket *m_parent; ///< Pointer to ListenSocket class, valid for incoming sockets + bool m_b_disable_read; ///< Disable checking for read events + bool m_connected; ///< Socket is connected (tcp/udp) + bool m_b_erased_by_handler; ///< Set by handler before delete + time_t m_tClose; ///< Time in seconds when ordered to close + std::auto_ptr m_client_remote_address; ///< Address of last connect() + std::auto_ptr m_remote_address; ///< Remote end address + IFile *m_traffic_monitor; + time_t m_timeout_start; ///< Set by SetTimeout + time_t m_timeout_limit; ///< Defined by SetTimeout + +#ifdef _WIN32 +static WSAInitializer m_winsock_init; ///< Winsock initialization singleton class +#endif + +#ifdef HAVE_OPENSSL + bool m_b_enable_ssl; ///< Enable SSL for this TcpSocket + bool m_b_ssl; ///< ssl negotiation mode (TcpSocket) + bool m_b_ssl_server; ///< True if this is an incoming ssl TcpSocket connection +#endif + +#ifdef ENABLE_IPV6 + bool m_ipv6; ///< This is an ipv6 socket if this one is true +#endif + +#ifdef ENABLE_POOL + int m_socket_type; ///< Type of socket, from socket() call + std::string m_socket_protocol; ///< Protocol, from socket() call + bool m_bClient; ///< only client connections are pooled + bool m_bRetain; ///< keep connection on close + bool m_bLost; ///< connection lost +#endif + +#ifdef ENABLE_SOCKS4 + bool m_bSocks4; ///< socks4 negotiation mode (TcpSocket) + ipaddr_t m_socks4_host; ///< socks4 server address + port_t m_socks4_port; ///< socks4 server port number + std::string m_socks4_userid; ///< socks4 server usedid +#endif + +#ifdef ENABLE_DETACH + bool m_detach; ///< Socket ordered to detach flag + bool m_detached; ///< Socket has been detached + SocketThread *m_pThread; ///< Detach socket thread class pointer + ISocketHandler *m_slave_handler; ///< Actual sockethandler while detached +#endif +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + + +#endif // _SOCKETS_Socket_H + diff --git a/Sockets/SocketAddress.h b/Sockets/SocketAddress.h new file mode 100644 index 0000000..d5a5643 --- /dev/null +++ b/Sockets/SocketAddress.h @@ -0,0 +1,95 @@ +/** + ** \file SocketAddress.h + ** \date 2006-09-21 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SocketAddress_H +#define _SOCKETS_SocketAddress_H + +#include "sockets-config.h" +#include +#include +#include "socket_include.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** + This class and its subclasses is intended to be used as replacement + for the internal data type 'ipaddr_t' and various implementations of + IPv6 addressing found throughout the library. + 'ipaddr_t' is an IPv4 address in network byte order. + 'port_t' is the portnumber in host byte order. + 'struct in6_addr' is an IPv6 address. + 'struct in_addr' is an IPv4 address. + \ingroup basic +*/ +class SocketAddress +{ +public: + virtual ~SocketAddress() {} + + /** Get a pointer to the address struct. */ + virtual operator struct sockaddr *() = 0; + + /** Get length of address struct. */ + virtual operator socklen_t() = 0; + + /** Compare two addresses. */ + virtual bool operator==(SocketAddress&) = 0; + + /** Set port number. + \param port Port number in host byte order */ + virtual void SetPort(port_t port) = 0; + + /** Get port number. + \return Port number in host byte order. */ + virtual port_t GetPort() = 0; + + /** Set socket address. + \param sa Pointer to either 'struct sockaddr_in' or 'struct sockaddr_in6'. */ + virtual void SetAddress(struct sockaddr *sa) = 0; + + /** Convert address to text. */ + virtual std::string Convert(bool include_port) = 0; + + /** Reverse lookup of address. */ + virtual std::string Reverse() = 0; + + /** Get address family. */ + virtual int GetFamily() = 0; + + /** Address structure is valid. */ + virtual bool IsValid() = 0; + + /** Get a copy of this SocketAddress object. */ + virtual std::auto_ptr GetCopy() = 0; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif +#endif // _SOCKETS_SocketAddress_H + diff --git a/Sockets/SocketHandler.cpp b/Sockets/SocketHandler.cpp new file mode 100644 index 0000000..c563aac --- /dev/null +++ b/Sockets/SocketHandler.cpp @@ -0,0 +1,1422 @@ +/** \file SocketHandler.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#endif +#include +#include + +#include "SocketHandler.h" +#include "UdpSocket.h" +#include "ResolvSocket.h" +#include "ResolvServer.h" +#include "TcpSocket.h" +#include "Mutex.h" +#include "Utility.h" +#include "SocketAddress.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +#ifdef _DEBUG +#define DEB(x) x; fflush(stderr); +#else +#define DEB(x) +#endif + + +SocketHandler::SocketHandler(StdLog *p) +:m_stdlog(p) +,m_mutex(m_mutex) +,m_b_use_mutex(false) +,m_maxsock(0) +,m_preverror(-1) +,m_errcnt(0) +,m_tlast(0) +#ifdef ENABLE_SOCKS4 +,m_socks4_host(0) +,m_socks4_port(0) +,m_bTryDirect(false) +#endif +#ifdef ENABLE_RESOLVER +,m_resolv_id(0) +,m_resolver(NULL) +#endif +#ifdef ENABLE_POOL +,m_b_enable_pool(false) +#endif +#ifdef ENABLE_TRIGGERS +,m_next_trigger_id(0) +#endif +#ifdef ENABLE_DETACH +,m_slave(false) +#endif +{ + FD_ZERO(&m_rfds); + FD_ZERO(&m_wfds); + FD_ZERO(&m_efds); +} + + +SocketHandler::SocketHandler(Mutex& mutex,StdLog *p) +:m_stdlog(p) +,m_mutex(mutex) +,m_b_use_mutex(true) +,m_maxsock(0) +,m_preverror(-1) +,m_errcnt(0) +,m_tlast(0) +#ifdef ENABLE_SOCKS4 +,m_socks4_host(0) +,m_socks4_port(0) +,m_bTryDirect(false) +#endif +#ifdef ENABLE_RESOLVER +,m_resolv_id(0) +,m_resolver(NULL) +#endif +#ifdef ENABLE_POOL +,m_b_enable_pool(false) +#endif +#ifdef ENABLE_TRIGGERS +,m_next_trigger_id(0) +#endif +#ifdef ENABLE_DETACH +,m_slave(false) +#endif +{ + m_mutex.Lock(); + FD_ZERO(&m_rfds); + FD_ZERO(&m_wfds); + FD_ZERO(&m_efds); +} + + +SocketHandler::~SocketHandler() +{ +#ifdef ENABLE_RESOLVER + if (m_resolver) + { + m_resolver -> Quit(); + } +#endif + { + while (m_sockets.size()) + { +DEB( fprintf(stderr, "Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());) + socket_m::iterator it = m_sockets.begin(); + Socket *p = it -> second; + if (p) + { +DEB( fprintf(stderr, " fd %d\n", p -> GetSocket());) + p -> Close(); +DEB( fprintf(stderr, " fd closed %d\n", p -> GetSocket());) +// p -> OnDelete(); // hey, I turn this back on. what's the worst that could happen??!! + // MinionSocket breaks, calling MinderHandler methods in OnDelete - + // MinderHandler is already gone when that happens... + + // only delete socket when controlled + // ie master sockethandler can delete non-detached sockets + // and a slave sockethandler can only delete a detach socket + if (p -> DeleteByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { + p -> SetErasedByHandler(); + delete p; + } + m_sockets.erase(it); + } + else + { + m_sockets.erase(it); + } +DEB( fprintf(stderr, "next\n");) + } +DEB( fprintf(stderr, "/Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());) + } +#ifdef ENABLE_RESOLVER + if (m_resolver) + { + delete m_resolver; + } +#endif + if (m_b_use_mutex) + { + m_mutex.Unlock(); + } +} + + +Mutex& SocketHandler::GetMutex() const +{ + return m_mutex; +} + + +#ifdef ENABLE_DETACH +void SocketHandler::SetSlave(bool x) +{ + m_slave = x; +} + + +bool SocketHandler::IsSlave() +{ + return m_slave; +} +#endif + + +void SocketHandler::RegStdLog(StdLog *log) +{ + m_stdlog = log; +} + + +void SocketHandler::LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t) +{ + if (m_stdlog) + { + m_stdlog -> error(this, p, user_text, err, sys_err, t); + } +} + + +void SocketHandler::Add(Socket *p) +{ + if (p -> GetSocket() == INVALID_SOCKET) + { + LogError(p, "Add", -1, "Invalid socket", LOG_LEVEL_WARNING); + if (p -> CloseAndDelete()) + { + m_delete.push_back(p); + } + return; + } + if (m_add.find(p -> GetSocket()) != m_add.end()) + { + LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in add queue", LOG_LEVEL_FATAL); + m_delete.push_back(p); + return; + } + m_add[p -> GetSocket()] = p; +} + + +void SocketHandler::Get(SOCKET s,bool& r,bool& w,bool& e) +{ + if (s >= 0) + { + r = FD_ISSET(s, &m_rfds) ? true : false; + w = FD_ISSET(s, &m_wfds) ? true : false; + e = FD_ISSET(s, &m_efds) ? true : false; + } +} + + +void SocketHandler::Set(SOCKET s,bool bRead,bool bWrite,bool bException) +{ +DEB( fprintf(stderr, "Set(%d, %s, %s, %s)\n", s, bRead ? "true" : "false", bWrite ? "true" : "false", bException ? "true" : "false");) + if (s >= 0) + { + if (bRead) + { + if (!FD_ISSET(s, &m_rfds)) + { + FD_SET(s, &m_rfds); + } + } + else + { + FD_CLR(s, &m_rfds); + } + if (bWrite) + { + if (!FD_ISSET(s, &m_wfds)) + { + FD_SET(s, &m_wfds); + } + } + else + { + FD_CLR(s, &m_wfds); + } + if (bException) + { + if (!FD_ISSET(s, &m_efds)) + { + FD_SET(s, &m_efds); + } + } + else + { + FD_CLR(s, &m_efds); + } + } +} + + +int SocketHandler::Select(long sec,long usec) +{ + struct timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + return Select(&tv); +} + + +int SocketHandler::Select() +{ + if (m_fds_callonconnect.size() || +#ifdef ENABLE_DETACH + (!m_slave && m_fds_detach.size()) || +#endif + m_fds_timeout.size() || + m_fds_retry.size() || + m_fds_close.size() || + m_fds_erase.size()) + { + return Select(0, 200000); + } + return Select(NULL); +} + + +int SocketHandler::Select(struct timeval *tsel) +{ + size_t ignore = 0; + while (m_add.size() > ignore) + { + if (m_sockets.size() >= FD_SETSIZE) + { + LogError(NULL, "Select", (int)m_sockets.size(), "FD_SETSIZE reached", LOG_LEVEL_WARNING); + break; + } + socket_m::iterator it = m_add.begin(); + SOCKET s = it -> first; + Socket *p = it -> second; +DEB( fprintf(stderr, "Trying to add fd %d, m_add.size() %d, ignore %d\n", (int)s, (int)m_add.size(), (int)ignore);) + // + if (m_sockets.find(p -> GetSocket()) != m_sockets.end()) + { + LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in controlled queue", LOG_LEVEL_FATAL); + // %! it's a dup, don't add to delete queue, just ignore it + m_delete.push_back(p); + m_add.erase(it); +// ignore++; + continue; + } + if (!p -> CloseAndDelete()) + { + StreamSocket *scp = dynamic_cast(p); + if (scp && scp -> Connecting()) // 'Open' called before adding socket + { + Set(s,false,true); + } + else + { + TcpSocket *tcp = dynamic_cast(p); + bool bWrite = tcp ? tcp -> GetOutputLength() != 0 : false; + if (p -> IsDisableRead()) + { + Set(s, false, bWrite); + } + else + { + Set(s, true, bWrite); + } + } + m_maxsock = (s > m_maxsock) ? s : m_maxsock; + } + else + { + LogError(p, "Add", (int)p -> GetSocket(), "Trying to add socket with SetCloseAndDelete() true", LOG_LEVEL_WARNING); + } + // only add to m_fds (process fd_set events) if + // slave handler and detached/detaching socket + // master handler and non-detached socket +#ifdef ENABLE_DETACH + if (!(m_slave ^ p -> IsDetach())) +#endif + { + m_fds.push_back(s); + } + m_sockets[s] = p; + // + m_add.erase(it); + } +#ifdef MACOSX + fd_set rfds; + fd_set wfds; + fd_set efds; + FD_COPY(&m_rfds, &rfds); + FD_COPY(&m_wfds, &wfds); + FD_COPY(&m_efds, &efds); +#else + fd_set rfds = m_rfds; + fd_set wfds = m_wfds; + fd_set efds = m_efds; +#endif + int n; + if (m_b_use_mutex) + { + m_mutex.Unlock(); + n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel); + m_mutex.Lock(); + } + else + { + n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel); + } + if (n == -1) + { + /* + EBADF An invalid file descriptor was given in one of the sets. + EINTR A non blocked signal was caught. + EINVAL n is negative. Or struct timeval contains bad time values (<0). + ENOMEM select was unable to allocate memory for internal tables. + */ + if (Errno != m_preverror || m_errcnt++ % 10000 == 0) + { + LogError(NULL, "select", Errno, StrError(Errno)); +DEB( fprintf(stderr, "m_maxsock: %d\n", m_maxsock); + fprintf(stderr, "%s\n", Errno == EINVAL ? "EINVAL" : + Errno == EINTR ? "EINTR" : + Errno == EBADF ? "EBADF" : + Errno == ENOMEM ? "ENOMEM" : ""); + // test bad fd + for (SOCKET i = 0; i <= m_maxsock; i++) + { + bool t = false; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + if (FD_ISSET(i, &m_rfds)) + { + FD_SET(i, &rfds); + t = true; + } + if (FD_ISSET(i, &m_wfds)) + { + FD_SET(i, &wfds); + t = true; + } + if (FD_ISSET(i, &m_efds)) + { + FD_SET(i, &efds); + t = true; + } + if (t && m_sockets.find(i) == m_sockets.end()) + { + fprintf(stderr, "Bad fd in fd_set: %d\n", i); + } + } +) // DEB + m_preverror = Errno; + } + /// \todo rebuild fd_set's from active sockets list (m_sockets) here + } + else + if (!n) + { + m_preverror = -1; + } + else + if (n > 0) + { + for (socket_v::iterator it2 = m_fds.begin(); it2 != m_fds.end() && n; it2++) + { + SOCKET i = *it2; + if (FD_ISSET(i, &rfds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + // new SSL negotiate method +#ifdef HAVE_OPENSSL + if (p -> IsSSLNegotiate()) + { + p -> SSLNegotiate(); + } + else +#endif + { + p -> OnRead(); + } + } + else + { + LogError(NULL, "GetSocket/handler/1", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + if (FD_ISSET(i, &wfds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + // new SSL negotiate method +#ifdef HAVE_OPENSSL + if (p -> IsSSLNegotiate()) + { + p -> SSLNegotiate(); + } + else +#endif + { + p -> OnWrite(); + } + } + else + { + LogError(NULL, "GetSocket/handler/2", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + if (FD_ISSET(i, &efds)) + { + socket_m::iterator itmp = m_sockets.find(i); + if (itmp != m_sockets.end()) // found + { + Socket *p = itmp -> second; + p -> OnException(); + } + else + { + LogError(NULL, "GetSocket/handler/3", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + n--; + } + } // m_fds loop + m_preverror = -1; + } // if (n > 0) + + // check CallOnConnect - EVENT + if (m_fds_callonconnect.size()) + { + socket_v tmp = m_fds_callonconnect; + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/4", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> CallOnConnect() && p -> Ready() ) + { + p -> SetConnected(); // moved here from inside if (tcp) check below +#ifdef HAVE_OPENSSL + if (p -> IsSSL()) // SSL Enabled socket + p -> OnSSLConnect(); + else +#endif +#ifdef ENABLE_SOCKS4 + if (p -> Socks4()) + p -> OnSocks4Connect(); + else +#endif + { + TcpSocket *tcp = dynamic_cast(p); + if (tcp) + { + if (tcp -> GetOutputLength()) + { + p -> OnWrite(); + } + } +#ifdef ENABLE_RECONNECT + if (tcp && tcp -> IsReconnect()) + p -> OnReconnect(); + else +#endif + { +// LogError(p, "Calling OnConnect", 0, "Because CallOnConnect", LOG_LEVEL_INFO); + p -> OnConnect(); + } + } +// p -> SetCallOnConnect( false ); + AddList(p -> GetSocket(), LIST_CALLONCONNECT, false); + } + } + } + } +#ifdef ENABLE_DETACH + // check detach of socket if master handler - EVENT + if (!m_slave && m_fds_detach.size()) + { + // %! why not using tmp list here??!? + for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/5", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> IsDetach()) + { + Set(p -> GetSocket(), false, false, false); + // After DetachSocket(), all calls to Handler() will return a reference + // to the new slave SocketHandler running in the new thread. + p -> DetachSocket(); + // Adding the file descriptor to m_fds_erase will now also remove the + // socket from the detach queue - tnx knightmad + m_fds_erase.push_back(p -> GetSocket()); + } + } + } + } +#endif + // check Connecting - connection timeout - conditional event + if (m_fds_timeout.size()) + { + time_t tnow = time(NULL); + if (tnow != m_tlast) + { + socket_v tmp = m_fds_timeout; +DEB( fprintf(stderr, "Checking %d socket(s) for timeout\n", tmp.size());) + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + itmp = m_add.find(*it); + if (itmp != m_add.end()) + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/6", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + } + if (p) + { + if (p -> Timeout(tnow)) + { + StreamSocket *scp = dynamic_cast(p); + if (scp && scp -> Connecting()) + p -> OnConnectTimeout(); + else + p -> OnTimeout(); + p -> SetTimeout(0); + } + } + } + m_tlast = tnow; + } // tnow != tlast + } + // check retry client connect - EVENT + if (m_fds_retry.size()) + { + socket_v tmp = m_fds_retry; + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/7", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + if (p) + { +// if (p -> RetryClientConnect()) + { + TcpSocket *tcp = dynamic_cast(p); + SOCKET nn = *it; //(*it3).first; + tcp -> SetRetryClientConnect(false); +DEB( fprintf(stderr, "Close() before retry client connect\n");) + p -> Close(); // removes from m_fds_retry + std::auto_ptr ad = p -> GetClientRemoteAddress(); + if (ad.get()) + { + tcp -> Open(*ad); + } + else + { + LogError(p, "RetryClientConnect", 0, "no address", LOG_LEVEL_ERROR); + } + Add(p); + m_fds_erase.push_back(nn); + } + } + } + } + // check close and delete - conditional event + if (m_fds_close.size()) + { + socket_v tmp = m_fds_close; +DEB( fprintf(stderr, "m_fds_close.size() == %d\n", (int)m_fds_close.size());) + for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++) + { + Socket *p = NULL; + { + socket_m::iterator itmp = m_sockets.find(*it); + if (itmp != m_sockets.end()) // found + { + p = itmp -> second; + } + else + { + itmp = m_add.find(*it); + if (itmp != m_add.end()) + { + p = itmp -> second; + } + else + { + LogError(NULL, "GetSocket/handler/8", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING); + } + } + } + if (p) + { +// if (p -> CloseAndDelete() ) + { + TcpSocket *tcp = dynamic_cast(p); + // new graceful tcp - flush and close timeout 5s + if (tcp && p -> IsConnected() && tcp -> GetFlushBeforeClose() && +#ifdef HAVE_OPENSSL + !tcp -> IsSSL() && +#endif + p -> TimeSinceClose() < 5) + { +DEB( fprintf(stderr, " close(1)\n");) + if (tcp -> GetOutputLength()) + { + LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Sending all data before closing", LOG_LEVEL_INFO); + } + else // shutdown write when output buffer is empty + if (!(tcp -> GetShutdown() & SHUT_WR)) + { + SOCKET nn = *it; + if (nn != INVALID_SOCKET && shutdown(nn, SHUT_WR) == -1) + { + LogError(p, "graceful shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + tcp -> SetShutdown(SHUT_WR); + } + } + else +#ifdef ENABLE_RECONNECT + if (tcp && p -> IsConnected() && tcp -> Reconnect()) + { + SOCKET nn = *it; //(*it3).first; +DEB( fprintf(stderr, " close(2) fd %d\n", nn);) + p -> SetCloseAndDelete(false); + tcp -> SetIsReconnect(); + p -> SetConnected(false); +DEB( fprintf(stderr, "Close() before reconnect\n");) + p -> Close(); // dispose of old file descriptor (Open creates a new) + p -> OnDisconnect(); + std::auto_ptr ad = p -> GetClientRemoteAddress(); + if (ad.get()) + { + tcp -> Open(*ad); + } + else + { + LogError(p, "Reconnect", 0, "no address", LOG_LEVEL_ERROR); + } + tcp -> ResetConnectionRetries(); + Add(p); + m_fds_erase.push_back(nn); + } + else +#endif + { + SOCKET nn = *it; //(*it3).first; +DEB( fprintf(stderr, " close(3) fd %d GetSocket() %d\n", nn, p -> GetSocket());) + if (tcp && p -> IsConnected() && tcp -> GetOutputLength()) + { + LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Closing socket while data still left to send", LOG_LEVEL_WARNING); + } +#ifdef ENABLE_POOL + if (p -> Retain() && !p -> Lost()) + { + PoolSocket *p2 = new PoolSocket(*this, p); + p2 -> SetDeleteByHandler(); + Add(p2); + // + p -> SetCloseAndDelete(false); // added - remove from m_fds_close + } + else +#endif // ENABLE_POOL + { + Set(p -> GetSocket(),false,false,false); +DEB( fprintf(stderr, "Close() before OnDelete\n");) + p -> Close(); + } + p -> OnDelete(); + if (p -> DeleteByHandler()) + { + p -> SetErasedByHandler(); + } + m_fds_erase.push_back(nn); + } + } + } + } + } + + // check erased sockets + bool check_max_fd = false; + while (m_fds_erase.size()) + { + socket_v::iterator it = m_fds_erase.begin(); + SOCKET nn = *it; +#ifdef ENABLE_DETACH + { + for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++) + { + if (*it == nn) + { + m_fds_detach.erase(it); + break; + } + } + } +#endif + { + for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++) + { + if (*it == nn) + { + m_fds.erase(it); + break; + } + } + } + { + socket_m::iterator it = m_sockets.find(nn); + if (it != m_sockets.end()) + { + Socket *p = it -> second; + /* Sometimes a SocketThread class can finish its run before the master + sockethandler gets here. In that case, the SocketThread has set the + 'ErasedByHandler' flag on the socket which will make us end up with a + double delete on the socket instance. + The fix is to make sure that the master sockethandler only can delete + non-detached sockets, and a slave sockethandler only can delete + detach sockets. */ + if (p -> ErasedByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { +#ifdef ENABLE_TRIGGERS + bool again = false; + do + { + again = false; + for (std::map::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++) + { + int id = it -> first; + Socket *src = it -> second; + if (src == p) + { + for (std::map::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnCancelled(id); + } + } + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + again = true; + break; + } + } + } while (again); +#endif + delete p; + } + m_sockets.erase(it); + } + } + m_fds_erase.erase(it); + check_max_fd = true; + } + // calculate max file descriptor for select() call + if (check_max_fd) + { + m_maxsock = 0; + for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++) + { + SOCKET s = *it; + m_maxsock = s > m_maxsock ? s : m_maxsock; + } + } + // remove Add's that fizzed + while (m_delete.size()) + { + std::list::iterator it = m_delete.begin(); + Socket *p = *it; + p -> OnDelete(); + m_delete.erase(it); + if (p -> DeleteByHandler() +#ifdef ENABLE_DETACH + && !(m_slave ^ p -> IsDetached()) +#endif + ) + { + p -> SetErasedByHandler(); +#ifdef ENABLE_TRIGGERS + bool again = false; + do + { + again = false; + for (std::map::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++) + { + int id = it -> first; + Socket *src = it -> second; + if (src == p) + { + for (std::map::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnCancelled(id); + } + } + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + again = true; + break; + } + } + } while (again); +#endif + delete p; + } + } + return n; +} + + +#ifdef ENABLE_RESOLVER +bool SocketHandler::Resolving(Socket *p0) +{ + std::map::iterator it = m_resolve_q.find(p0); + return it != m_resolve_q.end(); +} +#endif + + +bool SocketHandler::Valid(Socket *p0) +{ + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + Socket *p = it -> second; + if (p0 == p) + return true; + } + return false; +} + + +bool SocketHandler::OkToAccept(Socket *) +{ + return true; +} + + +size_t SocketHandler::GetCount() +{ +/* +printf(" m_sockets : %d\n", m_sockets.size()); +printf(" m_add : %d\n", m_add.size()); +printf(" m_delete : %d\n", m_delete.size()); +*/ + return m_sockets.size() + m_add.size() + m_delete.size(); +} + + +#ifdef ENABLE_SOCKS4 +void SocketHandler::SetSocks4Host(ipaddr_t a) +{ + m_socks4_host = a; +} + + +void SocketHandler::SetSocks4Host(const std::string& host) +{ + Utility::u2ip(host, m_socks4_host); +} + + +void SocketHandler::SetSocks4Port(port_t port) +{ + m_socks4_port = port; +} + + +void SocketHandler::SetSocks4Userid(const std::string& id) +{ + m_socks4_userid = id; +} +#endif + + +#ifdef ENABLE_RESOLVER +int SocketHandler::Resolve(Socket *p,const std::string& host,port_t port) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, host, port); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; +DEB( fprintf(stderr, " *** Resolve '%s:%d' id#%d m_resolve_q size: %d p: %p\n", host.c_str(), port, resolv -> GetId(), m_resolve_q.size(), p);) + return resolv -> GetId(); +} + + +#ifdef ENABLE_IPV6 +int SocketHandler::Resolve6(Socket *p,const std::string& host,port_t port) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, host, port, true); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} +#endif + + +int SocketHandler::Resolve(Socket *p,ipaddr_t a) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, a); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} + + +#ifdef ENABLE_IPV6 +int SocketHandler::Resolve(Socket *p,in6_addr& a) +{ + // check cache + ResolvSocket *resolv = new ResolvSocket(*this, p, a); + resolv -> SetId(++m_resolv_id); + resolv -> SetDeleteByHandler(); + ipaddr_t local; + Utility::u2ip("127.0.0.1", local); + if (!resolv -> Open(local, m_resolver_port)) + { + LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL); + } + Add(resolv); + m_resolve_q[p] = true; + return resolv -> GetId(); +} +#endif + + +void SocketHandler::EnableResolver(port_t port) +{ + if (!m_resolver) + { + m_resolver_port = port; + m_resolver = new ResolvServer(port); + } +} + + +bool SocketHandler::ResolverReady() +{ + return m_resolver ? m_resolver -> Ready() : false; +} +#endif // ENABLE_RESOLVER + + +#ifdef ENABLE_SOCKS4 +void SocketHandler::SetSocks4TryDirect(bool x) +{ + m_bTryDirect = x; +} + + +ipaddr_t SocketHandler::GetSocks4Host() +{ + return m_socks4_host; +} + + +port_t SocketHandler::GetSocks4Port() +{ + return m_socks4_port; +} + + +const std::string& SocketHandler::GetSocks4Userid() +{ + return m_socks4_userid; +} + + +bool SocketHandler::Socks4TryDirect() +{ + return m_bTryDirect; +} +#endif + + +#ifdef ENABLE_RESOLVER +bool SocketHandler::ResolverEnabled() +{ + return m_resolver ? true : false; +} + + +port_t SocketHandler::GetResolverPort() +{ + return m_resolver_port; +} +#endif // ENABLE_RESOLVER + + +#ifdef ENABLE_POOL +ISocketHandler::PoolSocket *SocketHandler::FindConnection(int type,const std::string& protocol,SocketAddress& ad) +{ + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end() && m_sockets.size(); it++) + { + PoolSocket *pools = dynamic_cast(it -> second); + if (pools) + { + if (pools -> GetSocketType() == type && + pools -> GetSocketProtocol() == protocol && +// %! pools -> GetClientRemoteAddress() && + *pools -> GetClientRemoteAddress() == ad) + { + m_sockets.erase(it); + pools -> SetRetain(); // avoid Close in Socket destructor + return pools; // Caller is responsible that this socket is deleted + } + } + } + return NULL; +} + + +void SocketHandler::EnablePool(bool x) +{ + m_b_enable_pool = x; +} + + +bool SocketHandler::PoolEnabled() +{ + return m_b_enable_pool; +} +#endif + + +void SocketHandler::Remove(Socket *p) +{ +#ifdef ENABLE_RESOLVER + std::map::iterator it4 = m_resolve_q.find(p); + if (it4 != m_resolve_q.end()) + m_resolve_q.erase(it4); +#endif + if (p -> ErasedByHandler()) + { + return; + } + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + if (it -> second == p) + { + LogError(p, "Remove", -1, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_sockets.erase(it); + return; + } + } + for (socket_m::iterator it2 = m_add.begin(); it2 != m_add.end(); it2++) + { + if ((*it2).second == p) + { + LogError(p, "Remove", -2, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_add.erase(it2); + return; + } + } + for (std::list::iterator it3 = m_delete.begin(); it3 != m_delete.end(); it3++) + { + if (*it3 == p) + { + LogError(p, "Remove", -3, "Socket destructor called while still in use", LOG_LEVEL_WARNING); + m_delete.erase(it3); + return; + } + } +} + + +void SocketHandler::CheckSanity() +{ + CheckList(m_fds, "active sockets"); // active sockets + CheckList(m_fds_erase, "sockets to be erased"); // should always be empty anyway + CheckList(m_fds_callonconnect, "checklist CallOnConnect"); +#ifdef ENABLE_DETACH + CheckList(m_fds_detach, "checklist Detach"); +#endif + CheckList(m_fds_timeout, "checklist Timeout"); + CheckList(m_fds_retry, "checklist retry client connect"); + CheckList(m_fds_close, "checklist close and delete"); +} + + +void SocketHandler::CheckList(socket_v& ref,const std::string& listname) +{ + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + SOCKET s = *it; + if (m_sockets.find(s) != m_sockets.end()) + continue; + if (m_add.find(s) != m_add.end()) + continue; + bool found = false; + for (std::list::iterator it = m_delete.begin(); it != m_delete.end(); it++) + { + Socket *p = *it; + if (p -> GetSocket() == s) + { + found = true; + break; + } + } + if (!found) + { + fprintf(stderr, "CheckList failed for \"%s\": fd %d\n", listname.c_str(), s); + } + } +} + + +void SocketHandler::AddList(SOCKET s,list_t which_one,bool add) +{ + if (s == INVALID_SOCKET) + { +DEB( fprintf(stderr, "AddList: invalid_socket\n");) + return; + } + socket_v& ref = + (which_one == LIST_CALLONCONNECT) ? m_fds_callonconnect : +#ifdef ENABLE_DETACH + (which_one == LIST_DETACH) ? m_fds_detach : +#endif + (which_one == LIST_TIMEOUT) ? m_fds_timeout : + (which_one == LIST_RETRY) ? m_fds_retry : + (which_one == LIST_CLOSE) ? m_fds_close : m_fds_close; + if (add) + { +#ifdef ENABLE_DETACH +DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" : + (which_one == LIST_DETACH) ? "Detach" : + (which_one == LIST_TIMEOUT) ? "Timeout" : + (which_one == LIST_RETRY) ? "Retry" : + (which_one == LIST_CLOSE) ? "Close" : "", + add ? "Add" : "Remove");) +#else +DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" : + (which_one == LIST_TIMEOUT) ? "Timeout" : + (which_one == LIST_RETRY) ? "Retry" : + (which_one == LIST_CLOSE) ? "Close" : "", + add ? "Add" : "Remove");) +#endif + } + if (add) + { + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + if (*it == s) // already there + { + return; + } + } + ref.push_back(s); + return; + } + // remove + for (socket_v::iterator it = ref.begin(); it != ref.end(); it++) + { + if (*it == s) + { + ref.erase(it); + break; + } + } +//DEB( fprintf(stderr, "/AddList\n");) +} + + +#ifdef ENABLE_TRIGGERS +int SocketHandler::TriggerID(Socket *src) +{ + int id = m_next_trigger_id++; + m_trigger_src[id] = src; + return id; +} + + +bool SocketHandler::Subscribe(int id, Socket *dst) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + std::map::iterator it = m_trigger_dst[id].find(dst); + if (it != m_trigger_dst[id].end()) + { + m_trigger_dst[id][dst] = true; + return true; + } + LogError(dst, "Subscribe", id, "Already subscribed", LOG_LEVEL_INFO); + return false; + } + LogError(dst, "Subscribe", id, "Trigger id not found", LOG_LEVEL_INFO); + return false; +} + + +bool SocketHandler::Unsubscribe(int id, Socket *dst) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + std::map::iterator it = m_trigger_dst[id].find(dst); + if (it != m_trigger_dst[id].end()) + { + m_trigger_dst[id].erase(it); + return true; + } + LogError(dst, "Unsubscribe", id, "Not subscribed", LOG_LEVEL_INFO); + return false; + } + LogError(dst, "Unsubscribe", id, "Trigger id not found", LOG_LEVEL_INFO); + return false; +} + + +void SocketHandler::Trigger(int id, Socket::TriggerData& data, bool erase) +{ + if (m_trigger_src.find(id) != m_trigger_src.end()) + { + data.SetSource( m_trigger_src[id] ); + for (std::map::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++) + { + Socket *dst = it -> first; + if (Valid(dst)) + { + dst -> OnTrigger(id, data); + } + } + if (erase) + { + m_trigger_src.erase(m_trigger_src.find(id)); + m_trigger_dst.erase(m_trigger_dst.find(id)); + } + } + else + { + LogError(NULL, "Trigger", id, "Trigger id not found", LOG_LEVEL_INFO); + } +} +#endif // ENABLE_TRIGGERS + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/SocketHandler.h b/Sockets/SocketHandler.h new file mode 100644 index 0000000..c7e20df --- /dev/null +++ b/Sockets/SocketHandler.h @@ -0,0 +1,266 @@ +/** \file SocketHandler.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_SocketHandler_H +#define _SOCKETS_SocketHandler_H + +#include "sockets-config.h" +#include +#include + +#include "socket_include.h" +#include "ISocketHandler.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class Socket; +#ifdef ENABLE_RESOLVER +class ResolvServer; +#endif +class Mutex; + +/** Socket container class, event generator. + \ingroup basic */ +class SocketHandler : public ISocketHandler +{ +protected: + /** Map type for holding file descriptors/socket object pointers. */ + typedef std::map socket_m; + +public: + /** SocketHandler constructor. + \param log Optional log class pointer */ + SocketHandler(StdLog *log = NULL); + + /** SocketHandler threadsafe constructor. + \param mutex Externally declared mutex variable + \param log Optional log class pointer */ + SocketHandler(Mutex& mutex,StdLog *log = NULL); + + ~SocketHandler(); + + /** Get mutex reference for threadsafe operations. */ + Mutex& GetMutex() const; + + /** Register StdLog object for error callback. + \param log Pointer to log class */ + void RegStdLog(StdLog *log); + + /** Log error to log class for print out / storage. */ + void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING); + + /** Add socket instance to socket map. Removal is always automatic. */ + void Add(Socket *); + + /** Get status of read/write/exception file descriptor set for a socket. */ + void Get(SOCKET s,bool& r,bool& w,bool& e); + + /** Set read/write/exception file descriptor sets (fd_set). */ + void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true); + + /** Wait for events, generate callbacks. */ + int Select(long sec,long usec); + + /** This method will not return until an event has been detected. */ + int Select(); + + /** Wait for events, generate callbacks. */ + int Select(struct timeval *tsel); + + /** Check that a socket really is handled by this socket handler. */ + bool Valid(Socket *); + + /** Return number of sockets handled by this handler. */ + size_t GetCount(); + + /** Override and return false to deny all incoming connections. + \param p ListenSocket class pointer (use GetPort to identify which one) */ + bool OkToAccept(Socket *p); + + /** Called by Socket when a socket changes state. */ + void AddList(SOCKET s,list_t which_one,bool add); + + // Connection pool +#ifdef ENABLE_POOL + /** Find available open connection (used by connection pool). */ + ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&); + /** Enable connection pool (by default disabled). */ + void EnablePool(bool x = true); + /** Check pool status. + \return true if connection pool is enabled */ + bool PoolEnabled(); +#endif // ENABLE_POOL + + // Socks4 +#ifdef ENABLE_SOCKS4 + /** Set socks4 server ip that all new tcp sockets should use. */ + void SetSocks4Host(ipaddr_t); + /** Set socks4 server hostname that all new tcp sockets should use. */ + void SetSocks4Host(const std::string& ); + /** Set socks4 server port number that all new tcp sockets should use. */ + void SetSocks4Port(port_t); + /** Set optional socks4 userid. */ + void SetSocks4Userid(const std::string& ); + /** If connection to socks4 server fails, immediately try direct connection to final host. */ + void SetSocks4TryDirect(bool x = true); + /** Get socks4 server ip. + \return socks4 server ip */ + ipaddr_t GetSocks4Host(); + /** Get socks4 port number. + \return socks4 port number */ + port_t GetSocks4Port(); + /** Get socks4 userid (optional). + \return socks4 userid */ + const std::string& GetSocks4Userid(); + /** Check status of socks4 try direct flag. + \return true if direct connection should be tried if connection to socks4 server fails */ + bool Socks4TryDirect(); +#endif // ENABLE_SOCKS4 + + // DNS resolve server +#ifdef ENABLE_RESOLVER + /** Enable asynchronous DNS. + \param port Listen port of asynchronous dns server */ + void EnableResolver(port_t port = 16667); + /** Check resolver status. + \return true if resolver is enabled */ + bool ResolverEnabled(); + /** Queue a dns request. + \param host Hostname to be resolved + \param port Port number will be echoed in Socket::OnResolved callback */ + int Resolve(Socket *,const std::string& host,port_t port); +#ifdef ENABLE_IPV6 + int Resolve6(Socket *,const std::string& host,port_t port); +#endif + /** Do a reverse dns lookup. */ + int Resolve(Socket *,ipaddr_t a); +#ifdef ENABLE_IPV6 + int Resolve(Socket *,in6_addr& a); +#endif + /** Get listen port of asynchronous dns server. */ + port_t GetResolverPort(); + /** Resolver thread ready for queries. */ + bool ResolverReady(); + /** Returns true if the socket is waiting for a resolve event. */ + bool Resolving(Socket *); +#endif // ENABLE_RESOLVER + +#ifdef ENABLE_TRIGGERS + /** Fetch unique trigger id. */ + int TriggerID(Socket *src); + /** Subscribe socket to trigger id. */ + bool Subscribe(int id, Socket *dst); + /** Unsubscribe socket from trigger id. */ + bool Unsubscribe(int id, Socket *dst); + /** Execute OnTrigger for subscribed sockets. + \param id Trigger ID + \param data Data passed from source to destination + \param erase Empty trigger id source and destination maps if 'true', + Leave them in place if 'false' - if a trigger should be called many times */ + void Trigger(int id, Socket::TriggerData& data, bool erase = true); +#endif // ENABLE_TRIGGERS + +#ifdef ENABLE_DETACH + /** Indicates that the handler runs under SocketThread. */ + void SetSlave(bool x = true); + /** Indicates that the handler runs under SocketThread. */ + bool IsSlave(); +#endif + + /** Sanity check of those accursed lists. */ + void CheckSanity(); + +protected: + socket_m m_sockets; ///< Active sockets map + socket_m m_add; ///< Sockets to be added to sockets map + std::list m_delete; ///< Sockets to be deleted (failed when Add) + +protected: + StdLog *m_stdlog; ///< Registered log class, or NULL + Mutex& m_mutex; ///< Thread safety mutex + bool m_b_use_mutex; ///< Mutex correctly initialized + +private: + void CheckList(socket_v&,const std::string&); ///< Used by CheckSanity + /** Remove socket from socket map, used by Socket class. */ + void Remove(Socket *); + SOCKET m_maxsock; ///< Highest file descriptor + 1 in active sockets list + fd_set m_rfds; ///< file descriptor set monitored for read events + fd_set m_wfds; ///< file descriptor set monitored for write events + fd_set m_efds; ///< file descriptor set monitored for exceptions + int m_preverror; ///< debug select() error + int m_errcnt; ///< debug select() error + time_t m_tlast; ///< timeout control + + // state lists + socket_v m_fds; ///< Active file descriptor list + socket_v m_fds_erase; ///< File descriptors that are to be erased from m_sockets + socket_v m_fds_callonconnect; ///< checklist CallOnConnect +#ifdef ENABLE_DETACH + socket_v m_fds_detach; ///< checklist Detach +#endif + socket_v m_fds_timeout; ///< checklist timeout + socket_v m_fds_retry; ///< checklist retry client connect + socket_v m_fds_close; ///< checklist close and delete + +#ifdef ENABLE_SOCKS4 + ipaddr_t m_socks4_host; ///< Socks4 server host ip + port_t m_socks4_port; ///< Socks4 server port number + std::string m_socks4_userid; ///< Socks4 userid + bool m_bTryDirect; ///< Try direct connection if socks4 server fails +#endif +#ifdef ENABLE_RESOLVER + int m_resolv_id; ///< Resolver id counter + ResolvServer *m_resolver; ///< Resolver thread pointer + port_t m_resolver_port; ///< Resolver listen port + std::map m_resolve_q; ///< resolve queue +#endif +#ifdef ENABLE_POOL + bool m_b_enable_pool; ///< Connection pool enabled if true +#endif +#ifdef ENABLE_TRIGGERS + int m_next_trigger_id; ///< Unique trigger id counter + std::map m_trigger_src; ///< mapping trigger id to source socket + std::map > m_trigger_dst; ///< mapping trigger id to destination sockets +#endif +#ifdef ENABLE_DETACH + bool m_slave; ///< Indicates that this is a ISocketHandler run in SocketThread +#endif +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_SocketHandler_H + diff --git a/Sockets/Sockets-config.cpp b/Sockets/Sockets-config.cpp new file mode 100644 index 0000000..7311147 --- /dev/null +++ b/Sockets/Sockets-config.cpp @@ -0,0 +1,121 @@ +/** + ** \file Sockets-config.cpp + ** \date 2006-09-04 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include +#include +#include "sockets-config.h" +#include "socket_include.h" + +int main(int argc,char *argv[]) +{ + if (argc > 1 && !strcmp(argv[1], "-info")) + { +#ifdef HAVE_OPENSSL + printf("SSL support\n"); +#endif +#ifdef IPPROTO_IPV6 + printf("IPv6 support\n"); +#endif +#ifdef USE_SCTP +#ifdef IPPROTO_SCTP + printf("SCTP support\n"); +# ifdef HAVE_SCTP + printf(" HAVE_SCTP: yes\n"); +# else + printf(" HAVE_SCTP: no\n"); +# endif +# ifdef HAVE_KERNEL_SCTP + printf(" HAVE_KERNEL_SCTP: yes\n"); +# else + printf(" HAVE_KERNEL_SCTP: no\n"); +# endif +# ifdef HAVE_SCTP_PRSCTP + printf(" HAVE_SCTP_PRSCTP: yes\n"); +# else + printf(" HAVE_SCTP_PRSCTP: no\n"); +# endif +# ifdef HAVE_SCTP_ADDIP + printf(" HAVE_SCTP_ADDIP: yes\n"); +# else + printf(" HAVE_SCTP_ADDIP: no\n"); +# endif +# ifdef HAVE_SCTP_CANSET_PRIMARY + printf(" HAVE_SCTP_CANSETPRIMARY: yes\n"); +# else + printf(" HAVE_SCTP_CANSETPRIMARY: no\n"); +# endif +# ifdef HAVE_SCTP_SAT_NETWORK_CAPABILITY + printf(" HAVE_SCTP_SAT_NETWORK_CAPABILITY: yes\n"); +# else + printf(" HAVE_SCTP_SAT_NETWORK_CAPABILITY: no\n"); +# endif +# ifdef HAVE_SCTP_MULTIBUF + printf(" HAVE_SCTP_MULTIBUF: yes\n"); +# else + printf(" HAVE_SCTP_MULTIBUF: no\n"); +# endif +# ifdef HAVE_SCTP_NOCONNECT + printf(" HAVE_SCTP_NOCONNECT: yes\n"); +# else + printf(" HAVE_SCTP_NOCONNECT: no\n"); +# endif +# ifdef HAVE_SCTP_EXT_RCVINFO + printf(" HAVE_SCTP_EXT_RCVINFO: yes\n"); +# else + printf(" HAVE_SCTP_EXT_RCVINFO: no\n"); +# endif +#else + printf("No SCTP support\n"); +#endif +#endif + return 0; + } + printf(" -D_VERSION='\"%s\"'", _VERSION); + +#ifdef LINUX + printf(" -DLINUX"); +#endif +#ifdef MACOSX + printf(" -DMACOSX"); +#endif +#ifdef SOLARIS + printf(" -DSOLARIS"); +#endif +#ifdef SOLARIS8 + printf(" -DSOLARIS8"); +#endif +#ifdef _WIN32 + printf(" -D_WIN32"); +#endif +#ifdef __CYGWIN__ + printf(" -D__CYGWIN__"); +#endif +#ifdef SOCKETS_NAMESPACE + printf(" -DSOCKETS_NAMESPACE=%s", SOCKETS_NAMESPACE_STR); +#endif +#ifdef _DEBUG + printf(" -D_DEBUG"); +#endif + +} + + diff --git a/Sockets/StdLog.h b/Sockets/StdLog.h new file mode 100644 index 0000000..c07037b --- /dev/null +++ b/Sockets/StdLog.h @@ -0,0 +1,74 @@ +/** \file StdLog.h + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_StdLog_H +#define _SOCKETS_StdLog_H + +#include "sockets-config.h" +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** error level enum. */ +typedef enum +{ + LOG_LEVEL_WARNING = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_FATAL, + LOG_LEVEL_INFO +} loglevel_t; + + +class ISocketHandler; +class Socket; + +/** \defgroup logging Log help classes */ +/** Log class interface. + \ingroup logging */ +class StdLog +{ +public: + virtual ~StdLog() {} + + virtual void error(ISocketHandler *,Socket *, + const std::string& user_text, + int err, + const std::string& sys_err, + loglevel_t = LOG_LEVEL_WARNING) = 0; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_StdLog_H + diff --git a/Sockets/StdoutLog.cpp b/Sockets/StdoutLog.cpp new file mode 100644 index 0000000..c01d8b8 --- /dev/null +++ b/Sockets/StdoutLog.cpp @@ -0,0 +1,96 @@ +/** \file StdoutLog.cpp + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include "ISocketHandler.h" +#include "Socket.h" +#include "StdoutLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + + +void StdoutLog::error(ISocketHandler *,Socket *sock,const std::string& call,int err,const std::string& sys_err,loglevel_t lvl) +{ + time_t t = time(NULL); + struct tm tp; +#ifdef _WIN32 + memcpy(&tp, localtime(&t), sizeof(tp)); +#else + localtime_r(&t, &tp); +#endif + std::string level; + + switch (lvl) + { + case LOG_LEVEL_WARNING: + level = "Warning"; + break; + case LOG_LEVEL_ERROR: + level = "Error"; + break; + case LOG_LEVEL_FATAL: + level = "Fatal"; + break; + case LOG_LEVEL_INFO: + level = "Info"; + break; + } + if (sock) + { + printf("%d-%02d-%02d %02d:%02d:%02d :: fd %d :: %s: %d %s (%s)\n", + tp.tm_year + 1900, + tp.tm_mon + 1, + tp.tm_mday, + tp.tm_hour,tp.tm_min,tp.tm_sec, + sock -> GetSocket(), + call.c_str(),err,sys_err.c_str(),level.c_str()); + } + else + { + printf("%d-%02d-%02d %02d:%02d:%02d :: %s: %d %s (%s)\n", + tp.tm_year + 1900, + tp.tm_mon + 1, + tp.tm_mday, + tp.tm_hour,tp.tm_min,tp.tm_sec, + call.c_str(),err,sys_err.c_str(),level.c_str()); + } +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/StdoutLog.h b/Sockets/StdoutLog.h new file mode 100644 index 0000000..5f96b34 --- /dev/null +++ b/Sockets/StdoutLog.h @@ -0,0 +1,57 @@ +/** \file StdoutLog.h + ** \date 2004-06-01 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_StdoutLog_H +#define _SOCKETS_StdoutLog_H + +#include "sockets-config.h" +#include "StdLog.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** StdLog implementation, logs to stdout. + \ingroup logging */ +class StdoutLog : public StdLog +{ +public: + void error(ISocketHandler *,Socket *,const std::string& call,int err,const std::string& sys_err,loglevel_t); +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_StdoutLog_H + diff --git a/Sockets/StreamSocket.cpp b/Sockets/StreamSocket.cpp new file mode 100644 index 0000000..5c5780e --- /dev/null +++ b/Sockets/StreamSocket.cpp @@ -0,0 +1,169 @@ +#include "StreamSocket.h" +#include "ISocketHandler.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +StreamSocket::StreamSocket(ISocketHandler& h) : Socket(h) +,m_bConnecting(false) +,m_connect_timeout(5) +,m_flush_before_close(true) +,m_connection_retry(0) +,m_retries(0) +,m_call_on_connect(false) +,m_b_retry_connect(false) +,m_line_protocol(false) +,m_shutdown(0) +{ +} + + +StreamSocket::~StreamSocket() +{ +} + + +void StreamSocket::SetConnecting(bool x) +{ + if (x != m_bConnecting) + { + m_bConnecting = x; + if (x) + { + SetTimeout( GetConnectTimeout() ); + } + else + { + SetTimeout( 0 ); + } + } +} + + +bool StreamSocket::Connecting() +{ + return m_bConnecting; +} + + +bool StreamSocket::Ready() +{ + if (GetSocket() != INVALID_SOCKET && !Connecting() && !CloseAndDelete()) + return true; + return false; +} + + +void StreamSocket::SetConnectTimeout(int x) +{ + m_connect_timeout = x; +} + + +int StreamSocket::GetConnectTimeout() +{ + return m_connect_timeout; +} + + +void StreamSocket::SetFlushBeforeClose(bool x) +{ + m_flush_before_close = x; +} + + +bool StreamSocket::GetFlushBeforeClose() +{ + return m_flush_before_close; +} + + +int StreamSocket::GetConnectionRetry() +{ + return m_connection_retry; +} + + +void StreamSocket::SetConnectionRetry(int x) +{ + m_connection_retry = x; +} + + +int StreamSocket::GetConnectionRetries() +{ + return m_retries; +} + + +void StreamSocket::IncreaseConnectionRetries() +{ + m_retries++; +} + + +void StreamSocket::ResetConnectionRetries() +{ + m_retries = 0; +} + + +void StreamSocket::SetCallOnConnect(bool x) +{ + Handler().AddList(GetSocket(), LIST_CALLONCONNECT, x); + m_call_on_connect = x; +} + + +bool StreamSocket::CallOnConnect() +{ + return m_call_on_connect; +} + + +void StreamSocket::SetRetryClientConnect(bool x) +{ + Handler().AddList(GetSocket(), LIST_RETRY, x); + m_b_retry_connect = x; +} + + +bool StreamSocket::RetryClientConnect() +{ + return m_b_retry_connect; +} + + +void StreamSocket::SetLineProtocol(bool x) +{ + m_line_protocol = x; +} + + +bool StreamSocket::LineProtocol() +{ + return m_line_protocol; +} + + +void StreamSocket::SetShutdown(int x) +{ + m_shutdown = x; +} + + +int StreamSocket::GetShutdown() +{ + return m_shutdown; +} + + + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + diff --git a/Sockets/StreamSocket.h b/Sockets/StreamSocket.h new file mode 100644 index 0000000..3c24811 --- /dev/null +++ b/Sockets/StreamSocket.h @@ -0,0 +1,127 @@ +#ifndef _StreamSocket_H +#define _StreamSocket_H + +#include "Socket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** SOCK_STREAM Socket base class. + \ingroup basic */ +class StreamSocket : public Socket +{ +public: + StreamSocket(ISocketHandler& ); + ~StreamSocket(); + + /** Socket should Check Connect on next write event from select(). */ + void SetConnecting(bool = true); + + /** Check connecting flag. + \return true if the socket is still trying to connect */ + bool Connecting(); + + /** Returns true when socket file descriptor is valid, + socket connection is established, and socket is not about to + be closed. */ + bool Ready(); + + /** Set timeout to use for connection attempt. + \param x Timeout in seconds */ + void SetConnectTimeout(int x); + + /** Return number of seconds to wait for a connection. + \return Connection timeout (seconds) */ + int GetConnectTimeout(); + + /** Set flush before close to make a tcp socket completely empty its + output buffer before closing the connection. */ + void SetFlushBeforeClose(bool = true); + + /** Check flush before status. + \return true if the socket should send all data before closing */ + bool GetFlushBeforeClose(); + + /** Define number of connection retries (tcp only). + n = 0 - no retry + n > 0 - number of retries + n = -1 - unlimited retries */ + void SetConnectionRetry(int n); + + /** Get number of maximum connection retries (tcp only). */ + int GetConnectionRetry(); + + /** Increase number of actual connection retries (tcp only). */ + void IncreaseConnectionRetries(); + + /** Get number of actual connection retries (tcp only). */ + int GetConnectionRetries(); + + /** Reset actual connection retries (tcp only). */ + void ResetConnectionRetries(); + + // LIST_CALLONCONNECT + + /** Instruct socket to call OnConnect callback next sockethandler cycle. */ + void SetCallOnConnect(bool x = true); + + /** Check call on connect flag. + \return true if OnConnect() should be called a.s.a.p */ + bool CallOnConnect(); + + // LIST_RETRY + + /** Set flag to initiate a connection attempt after a connection timeout. */ + void SetRetryClientConnect(bool x = true); + + /** Check if a connection attempt should be made. + \return true when another attempt should be made */ + bool RetryClientConnect(); + + /** Called after OnRead if socket is in line protocol mode. + \sa SetLineProtocol */ + /** Enable the OnLine callback. Do not create your own OnRead + * callback when using this. */ + virtual void SetLineProtocol(bool = true); + + /** Check line protocol mode. + \return true if socket is in line protocol mode */ + bool LineProtocol(); + + /** Set shutdown status. */ + void SetShutdown(int); + + /** Get shutdown status. */ + int GetShutdown(); + + /** Returns IPPROTO_TCP or IPPROTO_SCTP */ + virtual int Protocol() = 0; + +protected: + StreamSocket(const StreamSocket& ) {} // copy constructor + +private: + StreamSocket& operator=(const StreamSocket& ) { return *this; } // assignment operator + + bool m_bConnecting; ///< Flag indicating connection in progress + int m_connect_timeout; ///< Connection timeout (seconds) + bool m_flush_before_close; ///< Send all data before closing (default true) + int m_connection_retry; ///< Maximum connection retries (tcp) + int m_retries; ///< Actual number of connection retries (tcp) + bool m_call_on_connect; ///< OnConnect will be called next ISocketHandler cycle if true + bool m_b_retry_connect; ///< Try another connection attempt next ISocketHandler cycle + bool m_line_protocol; ///< Line protocol mode flag + int m_shutdown; ///< Shutdown status +}; + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + + +#endif // _StreamSocket_H + diff --git a/Sockets/TcpSocket.cpp b/Sockets/TcpSocket.cpp new file mode 100644 index 0000000..960ac35 --- /dev/null +++ b/Sockets/TcpSocket.cpp @@ -0,0 +1,1729 @@ +/** \file TcpSocket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#else +#include +#endif +#include "ISocketHandler.h" +#include +#include +#include +#ifdef HAVE_OPENSSL +#include +#include +#endif + +#include "TcpSocket.h" +#include "Utility.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#include "Mutex.h" +#include "IFile.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + + +// statics +#ifdef HAVE_OPENSSL +SSLInitializer TcpSocket::m_ssl_init; +#endif + + +// thanks, q +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(ISocketHandler& h) : StreamSocket(h) +,ibuf(TCP_BUFSIZE_READ) +,m_b_input_buffer_disabled(false) +,m_bytes_sent(0) +,m_bytes_received(0) +,m_skip_c(false) +#ifdef SOCKETS_DYNAMIC_TEMP +,m_buf(new char[TCP_BUFSIZE_READ + 1]) +#endif +,m_obuf_top(NULL) +,m_transfer_limit(0) +,m_output_length(0) +#ifdef HAVE_OPENSSL +,m_ssl_ctx(NULL) +,m_ssl(NULL) +,m_sbio(NULL) +#endif +#ifdef ENABLE_SOCKS4 +,m_socks4_state(0) +#endif +#ifdef ENABLE_RESOLVER +,m_resolver_id(0) +#endif +#ifdef ENABLE_RECONNECT +,m_b_reconnect(false) +,m_b_is_reconnect(false) +#endif +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(ISocketHandler& h,size_t isize,size_t osize) : StreamSocket(h) +,ibuf(isize) +,m_b_input_buffer_disabled(false) +,m_bytes_sent(0) +,m_bytes_received(0) +,m_skip_c(false) +#ifdef SOCKETS_DYNAMIC_TEMP +,m_buf(new char[TCP_BUFSIZE_READ + 1]) +#endif +,m_obuf_top(NULL) +,m_transfer_limit(0) +,m_output_length(0) +#ifdef HAVE_OPENSSL +,m_ssl_ctx(NULL) +,m_ssl(NULL) +,m_sbio(NULL) +#endif +#ifdef ENABLE_SOCKS4 +,m_socks4_state(0) +#endif +#ifdef ENABLE_RESOLVER +,m_resolver_id(0) +#endif +#ifdef ENABLE_RECONNECT +,m_b_reconnect(false) +,m_b_is_reconnect(false) +#endif +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + + +TcpSocket::~TcpSocket() +{ +#ifdef SOCKETS_DYNAMIC_TEMP + delete[] m_buf; +#endif + // %! empty m_obuf +#ifdef HAVE_OPENSSL + if (m_ssl) + { + SSL_free(m_ssl); + } +#endif +} + + +bool TcpSocket::Open(ipaddr_t ip,port_t port,bool skip_socks) +{ + Ipv4Address ad(ip, port); + Ipv4Address local; + return Open(ad, local, skip_socks); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool TcpSocket::Open(in6_addr ip,port_t port,bool skip_socks) +{ + Ipv6Address ad(ip, port); + return Open(ad, skip_socks); +} +#endif +#endif + + +bool TcpSocket::Open(SocketAddress& ad,bool skip_socks) +{ + Ipv4Address bind_ad("0.0.0.0", 0); + return Open(ad, bind_ad, skip_socks); +} + + +bool TcpSocket::Open(SocketAddress& ad,SocketAddress& bind_ad,bool skip_socks) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "Open", 0, "Invalid SocketAddress", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + if (Handler().GetCount() >= FD_SETSIZE) + { + Handler().LogError(this, "Open", 0, "no space left in fd_set", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + SetConnecting(false); +#ifdef ENABLE_SOCKS4 + SetSocks4(false); +#endif + // check for pooling +#ifdef ENABLE_POOL + if (Handler().PoolEnabled()) + { + ISocketHandler::PoolSocket *pools = Handler().FindConnection(SOCK_STREAM, "tcp", ad); + if (pools) + { + CopyConnection( pools ); + delete pools; + + SetIsClient(); + SetCallOnConnect(); // ISocketHandler must call OnConnect + Handler().LogError(this, "SetCallOnConnect", 0, "Found pooled connection", LOG_LEVEL_INFO); + return true; + } + } +#endif + // if not, create new connection + SOCKET s = CreateSocket(ad.GetFamily(), SOCK_STREAM, "tcp"); + if (s == INVALID_SOCKET) + { + return false; + } + // socket must be nonblocking for async connect + if (!SetNonblocking(true, s)) + { + SetCloseAndDelete(); + closesocket(s); + return false; + } +#ifdef ENABLE_POOL + SetIsClient(); // client because we connect +#endif + SetClientRemoteAddress(ad); + int n = 0; + if (bind_ad.GetPort() != 0) + { + bind(s, bind_ad, bind_ad); + } +#ifdef ENABLE_SOCKS4 + if (!skip_socks && GetSocks4Host() && GetSocks4Port()) + { + Ipv4Address sa(GetSocks4Host(), GetSocks4Port()); + { + std::string sockshost; + Utility::l2ip(GetSocks4Host(), sockshost); + Handler().LogError(this, "Open", 0, "Connecting to socks4 server @ " + sockshost + ":" + + Utility::l2string(GetSocks4Port()), LOG_LEVEL_INFO); + } + SetSocks4(); + n = connect(s, sa, sa); + SetRemoteAddress(sa); + } + else +#endif + { + n = connect(s, ad, ad); + SetRemoteAddress(ad); + } + if (n == -1) + { + // check error code that means a connect is in progress +#ifdef _WIN32 + if (Errno == WSAEWOULDBLOCK) +#else + if (Errno == EINPROGRESS) +#endif + { + Attach(s); + SetConnecting( true ); // this flag will control fd_set's + } + else +#ifdef ENABLE_SOCKS4 + if (Socks4() && Handler().Socks4TryDirect() ) // retry + { + closesocket(s); + return Open(ad, true); + } + else +#endif +#ifdef ENABLE_RECONNECT + if (Reconnect()) + { + Handler().LogError(this, "connect: failed, reconnect pending", Errno, StrError(Errno), LOG_LEVEL_INFO); + Attach(s); + SetConnecting( true ); // this flag will control fd_set's + } + else +#endif + { + Handler().LogError(this, "connect: failed", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + closesocket(s); + return false; + } + } + else + { + Attach(s); + SetCallOnConnect(); // ISocketHandler must call OnConnect + } + + // 'true' means connected or connecting(not yet connected) + // 'false' means something failed + return true; //!Connecting(); +} + + +bool TcpSocket::Open(const std::string &host,port_t port) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { +#ifdef ENABLE_RESOLVER + if (!Handler().ResolverEnabled() || Utility::isipv6(host) ) + { +#endif + in6_addr a; + if (!Utility::u2ip(host, a)) + { + SetCloseAndDelete(); + return false; + } + Ipv6Address ad(a, port); + Ipv6Address local; + return Open(ad, local); +#ifdef ENABLE_RESOLVER + } + m_resolver_id = Resolve6(host, port); + return true; +#endif + } +#endif +#endif +#ifdef ENABLE_RESOLVER + if (!Handler().ResolverEnabled() || Utility::isipv4(host) ) + { +#endif + ipaddr_t l; + if (!Utility::u2ip(host,l)) + { + SetCloseAndDelete(); + return false; + } + Ipv4Address ad(l, port); + Ipv4Address local; + return Open(ad, local); +#ifdef ENABLE_RESOLVER + } + // resolve using async resolver thread + m_resolver_id = Resolve(host, port); + return true; +#endif +} + + +#ifdef ENABLE_RESOLVER +void TcpSocket::OnResolved(int id,ipaddr_t a,port_t port) +{ +DEB( fprintf(stderr, "TcpSocket::OnResolved id %d addr %x port %d\n", id, a, port);) + if (id == m_resolver_id) + { + if (a && port) + { + Ipv4Address ad(a, port); + Ipv4Address local; + if (Open(ad, local)) + { + if (!Handler().Valid(this)) + { + Handler().Add(this); + } + } + } + else + { + Handler().LogError(this, "OnResolved", 0, "Resolver failed", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + } + else + { + Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} + + +#ifdef ENABLE_IPV6 +void TcpSocket::OnResolved(int id,in6_addr& a,port_t port) +{ + if (id == m_resolver_id) + { + Ipv6Address ad(a, port); + if (ad.IsValid()) + { + Ipv6Address local; + if (Open(ad, local)) + { + if (!Handler().Valid(this)) + { + Handler().Add(this); + } + } + } + } + else + { + Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} +#endif +#endif + + +void TcpSocket::OnRead() +{ + int n = 0; +#ifdef SOCKETS_DYNAMIC_TEMP + char *buf = m_buf; +#else + char buf[TCP_BUFSIZE_READ]; +#endif +#ifdef HAVE_OPENSSL + if (IsSSL()) + { + if (!Ready()) + return; + n = SSL_read(m_ssl, buf, TCP_BUFSIZE_READ); + if (n == -1) + { + n = SSL_get_error(m_ssl, n); + switch (n) + { + case SSL_ERROR_NONE: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + case SSL_ERROR_ZERO_RETURN: +DEB( fprintf(stderr, "SSL_read() returns zero - closing socket\n");) + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + break; + default: +DEB( fprintf(stderr, "SSL read problem, errcode = %d\n",n);) + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + } + return; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + SetShutdown(SHUT_WR); + return; + } + else + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + m_bytes_received += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n)) + { + Handler().LogError(this, "OnRead(ssl)", 0, "ibuf overflow", LOG_LEVEL_WARNING); + } + } + else + { + Handler().LogError(this, "OnRead(ssl)", n, "abnormal value from SSL_read", LOG_LEVEL_ERROR); + } + } + else +#endif // HAVE_OPENSSL + { + n = recv(GetSocket(), buf, TCP_BUFSIZE_READ, MSG_NOSIGNAL); + if (n == -1) + { + Handler().LogError(this, "read", Errno, StrError(Errno), LOG_LEVEL_FATAL); + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + return; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + SetShutdown(SHUT_WR); + return; + } + else + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + m_bytes_received += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n)) + { + Handler().LogError(this, "OnRead", 0, "ibuf overflow", LOG_LEVEL_WARNING); + } + } + else + { + Handler().LogError(this, "OnRead", n, "abnormal value from recv", LOG_LEVEL_ERROR); + } + } + // + OnRead( buf, n ); +} + + +void TcpSocket::OnRead( char *buf, size_t n ) +{ + // unbuffered + if (n > 0 && n <= TCP_BUFSIZE_READ) + { + if (LineProtocol()) + { + buf[n] = 0; + size_t i = 0; + if (m_skip_c && (buf[i] == 13 || buf[i] == 10) && buf[i] != m_c) + { + m_skip_c = false; + i++; + } + size_t x = i; + for (; i < n && LineProtocol(); i++) + { + while ((buf[i] == 13 || buf[i] == 10) && LineProtocol()) + { + char c = buf[i]; + buf[i] = 0; + if (buf[x]) + { + m_line += (buf + x); + } + OnLine( m_line ); + i++; + m_skip_c = true; + m_c = c; + if (i < n && (buf[i] == 13 || buf[i] == 10) && buf[i] != c) + { + m_skip_c = false; + i++; + } + x = i; + m_line = ""; + } + if (!LineProtocol()) + { + break; + } + } + if (!LineProtocol()) + { + if (i < n) + { + OnRawData(buf + i, n - i); + } + } + else + if (buf[x]) + { + m_line += (buf + x); + } + } + else + { + OnRawData(buf, n); + } + } + if (m_b_input_buffer_disabled) + { + return; + } + // further processing: socks4 +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + bool need_more = false; + while (GetInputLength() && !need_more && !CloseAndDelete()) + { + need_more = OnSocks4Read(); + } + } +#endif +} + + +void TcpSocket::OnWriteComplete() +{ +} + + +void TcpSocket::OnWrite() +{ + if (Connecting()) + { + int err = SoError(); + + // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on + if (!err) // ok + { + Set(!IsDisableRead(), false); + SetConnecting(false); + SetCallOnConnect(); + return; + } + Handler().LogError(this, "tcp: connect failed", err, StrError(err), LOG_LEVEL_FATAL); + Set(false, false); // no more monitoring because connection failed + + // failed +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + // %! leave 'Connecting' flag set? + OnSocks4ConnectFailed(); + return; + } +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + // even though the connection failed at once, only retry after + // the connection timeout. + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + return; + } + SetConnecting(false); + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + return; + } + // try send next block in buffer + // if full block is sent, repeat + // if all blocks are sent, reset m_wfds + + bool repeat = false; + size_t sz = m_transfer_limit ? GetOutputLength() : 0; + do + { + output_l::iterator it = m_obuf.begin(); + OUTPUT *p = *it; + repeat = false; + int n = TryWrite(p -> Buf(), p -> Len()); + if (n > 0) + { + size_t left = p -> Remove(n); + m_output_length -= n; + if (!left) + { + delete p; + m_obuf.erase(it); + if (!m_obuf.size()) + { + m_obuf_top = NULL; + OnWriteComplete(); + } + else + { + repeat = true; + } + } + } + } while (repeat); + + if (m_transfer_limit && sz > m_transfer_limit && GetOutputLength() < m_transfer_limit) + { + OnTransferLimit(); + } + + // check output buffer set, set/reset m_wfds accordingly + { + bool br; + bool bw; + bool bx; + Handler().Get(GetSocket(), br, bw, bx); + if (m_obuf.size()) + Set(br, true); + else + Set(br, false); + } +} + + +int TcpSocket::TryWrite(const char *buf, size_t len) +{ + int n = 0; +#ifdef HAVE_OPENSSL + if (IsSSL()) + { + n = SSL_write(m_ssl, buf, (int)len); + if (n == -1) + { + int errnr = SSL_get_error(m_ssl, n); + if ( errnr != SSL_ERROR_WANT_READ && errnr != SSL_ERROR_WANT_WRITE ) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + const char *errbuf = ERR_error_string(errnr, NULL); + Handler().LogError(this, "OnWrite/SSL_write", errnr, errbuf, LOG_LEVEL_FATAL); + } + return 0; + } + else + if (!n) + { + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif +DEB( int errnr = SSL_get_error(m_ssl, n); + const char *errbuf = ERR_error_string(errnr, NULL); + fprintf(stderr, "SSL_write() returns 0: %d : %s\n",errnr, errbuf);) + } + } + else +#endif // HAVE_OPENSSL + { + n = send(GetSocket(), buf, (int)len, MSG_NOSIGNAL); + if (n == -1) + { + // normal error codes: + // WSAEWOULDBLOCK + // EAGAIN or EWOULDBLOCK +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + { + Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_FATAL); + OnDisconnect(); + SetCloseAndDelete(true); + SetFlushBeforeClose(false); +#ifdef ENABLE_POOL + SetLost(); +#endif + } + return 0; + } + } + if (n > 0) + { + m_bytes_sent += n; + if (GetTrafficMonitor()) + { + GetTrafficMonitor() -> fwrite(buf, 1, n); + } + } + return n; +} + + +void TcpSocket::Buffer(const char *buf, size_t len) +{ + size_t ptr = 0; + m_output_length += len; + while (ptr < len) + { + // buf/len => pbuf/sz + size_t space = 0; + if (m_obuf_top && (space = m_obuf_top -> Space()) > 0) + { + const char *pbuf = buf + ptr; + size_t sz = len - ptr; + if (space >= sz) + { + m_obuf_top -> Add(pbuf, sz); + ptr += sz; + } + else + { + m_obuf_top -> Add(pbuf, space); + ptr += space; + } + } + else + { + m_obuf_top = new OUTPUT; + m_obuf.push_back( m_obuf_top ); + } + } +} + + +void TcpSocket::Send(const std::string &str,int i) +{ + SendBuf(str.c_str(),str.size(),i); +} + + +void TcpSocket::SendBuf(const char *buf,size_t len,int) +{ + if (!Ready() && !Connecting()) + { + Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-ready socket" ); // warning + if (GetSocket() == INVALID_SOCKET) + Handler().LogError(this, "SendBuf", 0, " * GetSocket() == INVALID_SOCKET", LOG_LEVEL_INFO); + if (Connecting()) + Handler().LogError(this, "SendBuf", 0, " * Connecting()", LOG_LEVEL_INFO); + if (CloseAndDelete()) + Handler().LogError(this, "SendBuf", 0, " * CloseAndDelete()", LOG_LEVEL_INFO); + return; + } + if (!IsConnected()) + { + Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-connected socket, will be sent on connect" ); // warning + Buffer(buf, len); + return; + } + if (m_obuf_top) + { + Buffer(buf, len); + return; + } + int n = TryWrite(buf, len); + if (n >= 0 && n < (int)len) + { + Buffer(buf + n, len - n); + } + // if ( data in buffer || !IsConnected ) + // { + // add to buffer + // } + // else + // try_send + // if any data is unsent, buffer it and set m_wfds + + // check output buffer set, set/reset m_wfds accordingly + { + bool br; + bool bw; + bool bx; + Handler().Get(GetSocket(), br, bw, bx); + if (m_obuf.size()) + Set(br, true); + else + Set(br, false); + } +} + + +void TcpSocket::OnLine(const std::string& ) +{ +} + + +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif +TcpSocket::TcpSocket(const TcpSocket& s) +:StreamSocket(s) +,ibuf(0) +{ +} +#ifdef _MSC_VER +#pragma warning(default:4355) +#endif + + +#ifdef ENABLE_SOCKS4 +void TcpSocket::OnSocks4Connect() +{ + char request[1000]; + memset(request, 0, sizeof(request)); + request[0] = 4; // socks v4 + request[1] = 1; // command code: CONNECT + { + std::auto_ptr ad = GetClientRemoteAddress(); + if (ad.get()) + { + struct sockaddr *p0 = (struct sockaddr *)*ad; + struct sockaddr_in *p = (struct sockaddr_in *)p0; + if (p -> sin_family == AF_INET) + { + memcpy(request + 2, &p -> sin_port, 2); // nwbo is ok here + memcpy(request + 4, &p -> sin_addr, sizeof(struct in_addr)); + } + else + { + /// \todo warn + } + } + else + { + /// \todo warn + } + } + strcpy(request + 8, GetSocks4Userid().c_str()); + size_t length = GetSocks4Userid().size() + 8 + 1; + SendBuf(request, length); + m_socks4_state = 0; +} + + +void TcpSocket::OnSocks4ConnectFailed() +{ + Handler().LogError(this,"OnSocks4ConnectFailed",0,"connection to socks4 server failed, trying direct connection",LOG_LEVEL_WARNING); + if (!Handler().Socks4TryDirect()) + { + SetConnecting(false); + SetCloseAndDelete(); + OnConnectFailed(); // just in case + } + else + { + SetRetryClientConnect(); + } +} + + +bool TcpSocket::OnSocks4Read() +{ + switch (m_socks4_state) + { + case 0: + ibuf.Read(&m_socks4_vn, 1); + m_socks4_state = 1; + break; + case 1: + ibuf.Read(&m_socks4_cd, 1); + m_socks4_state = 2; + break; + case 2: + if (GetInputLength() > 1) + { + ibuf.Read( (char *)&m_socks4_dstport, 2); + m_socks4_state = 3; + } + else + { + return true; + } + break; + case 3: + if (GetInputLength() > 3) + { + ibuf.Read( (char *)&m_socks4_dstip, 4); + SetSocks4(false); + + switch (m_socks4_cd) + { + case 90: + OnConnect(); + Handler().LogError(this, "OnSocks4Read", 0, "Connection established", LOG_LEVEL_INFO); + break; + case 91: + case 92: + case 93: + Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server reports connect failed",LOG_LEVEL_FATAL); + SetConnecting(false); + SetCloseAndDelete(); + OnConnectFailed(); + break; + default: + Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server unrecognized response",LOG_LEVEL_FATAL); + SetCloseAndDelete(); + break; + } + } + else + { + return true; + } + break; + } + return false; +} +#endif + + +void TcpSocket::Sendf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char slask[5000]; // vsprintf / vsnprintf temporary +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + Send( slask ); +} + + +#ifdef HAVE_OPENSSL +void TcpSocket::OnSSLConnect() +{ + SetNonblocking(true); + { + if (m_ssl_ctx) + { +DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");) + SetCloseAndDelete(true); + return; + } + InitSSLClient(); + } + if (m_ssl_ctx) + { + /* Connect the SSL socket */ + m_ssl = SSL_new(m_ssl_ctx); + if (!m_ssl) + { +DEB( fprintf(stderr, " m_ssl is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY); + m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE); + if (!m_sbio) + { +DEB( fprintf(stderr, " m_sbio is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_bio(m_ssl, m_sbio, m_sbio); + if (!SSLNegotiate()) + { + SetSSLNegotiate(); + } + } + else + { + SetCloseAndDelete(); + } +} + + +void TcpSocket::OnSSLAccept() +{ + SetNonblocking(true); + { + if (m_ssl_ctx) + { +DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");) + SetCloseAndDelete(true); + return; + } + InitSSLServer(); + SetSSLServer(); + } + if (m_ssl_ctx) + { + m_ssl = SSL_new(m_ssl_ctx); + if (!m_ssl) + { +DEB( fprintf(stderr, " m_ssl is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY); + m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE); + if (!m_sbio) + { +DEB( fprintf(stderr, " m_sbio is NULL\n");) + SetCloseAndDelete(true); + return; + } + SSL_set_bio(m_ssl, m_sbio, m_sbio); +// if (!SSLNegotiate()) + { + SetSSLNegotiate(); + } + } +} + + +bool TcpSocket::SSLNegotiate() +{ + if (!IsSSLServer()) // client + { + int r = SSL_connect(m_ssl); + if (r > 0) + { + SetSSLNegotiate(false); + /// \todo: resurrect certificate check... client +// CheckCertificateChain( "");//ServerHOST); + SetNonblocking(false); + // + { + SetConnected(); + if (GetOutputLength()) + { + OnWrite(); + } + } +#ifdef ENABLE_RECONNECT + if (IsReconnect()) + OnReconnect(); + else +#endif + { + OnConnect(); + } + Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection established", LOG_LEVEL_INFO); + return true; + } + else + if (!r) + { + Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection failed", LOG_LEVEL_INFO); + SetSSLNegotiate(false); + SetCloseAndDelete(); + OnSSLConnectFailed(); + } + else + { + r = SSL_get_error(m_ssl, r); + if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) + { + Handler().LogError(this, "SSLNegotiate/SSL_connect", -1, "Connection failed", LOG_LEVEL_INFO); +DEB( fprintf(stderr, "SSL_connect() failed - closing socket, return code: %d\n",r);) + SetSSLNegotiate(false); + SetCloseAndDelete(true); + OnSSLConnectFailed(); + } + } + } + else // server + { + int r = SSL_accept(m_ssl); + if (r > 0) + { + SetSSLNegotiate(false); + /// \todo: resurrect certificate check... server +// CheckCertificateChain( "");//ClientHOST); + SetNonblocking(false); + // + { + SetConnected(); + if (GetOutputLength()) + { + OnWrite(); + } + } + OnAccept(); + Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection established", LOG_LEVEL_INFO); + return true; + } + else + if (!r) + { + Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection failed", LOG_LEVEL_INFO); + SetSSLNegotiate(false); + SetCloseAndDelete(); + OnSSLAcceptFailed(); + } + else + { + r = SSL_get_error(m_ssl, r); + if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) + { + Handler().LogError(this, "SSLNegotiate/SSL_accept", -1, "Connection failed", LOG_LEVEL_INFO); +DEB( fprintf(stderr, "SSL_accept() failed - closing socket, return code: %d\n",r);) + SetSSLNegotiate(false); + SetCloseAndDelete(true); + OnSSLAcceptFailed(); + } + } + } + return false; +} + + +void TcpSocket::InitSSLClient() +{ + InitializeContext("", SSLv23_method()); +} + + +void TcpSocket::InitSSLServer() +{ + Handler().LogError(this, "InitSSLServer", 0, "You MUST implement your own InitSSLServer method", LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} + + +void TcpSocket::InitializeContext(const std::string& context, SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map client_contexts; + if (client_contexts.find(context) == client_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = client_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + } + else + { + m_ssl_ctx = client_contexts[context]; + } +} + + +void TcpSocket::InitializeContext(const std::string& context,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map server_contexts; + if (server_contexts.find(context) == server_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + // session id + if (context.size()) + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size()); + else + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9); + } + else + { + m_ssl_ctx = server_contexts[context]; + } + + /* Load our keys and certificates*/ + if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL); + } + + m_password = password; + SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb); + SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this); + if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL); + } +} + + +void TcpSocket::InitializeContext(const std::string& context,const std::string& certfile,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in) +{ + /* Create our context*/ + static std::map server_contexts; + if (server_contexts.find(context) == server_contexts.end()) + { + SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method(); + m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth); + SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY); + // session id + if (context.size()) + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size()); + else + SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9); + } + else + { + m_ssl_ctx = server_contexts[context]; + } + + /* Load our keys and certificates*/ + if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, certfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL); + } + + m_password = password; + SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb); + SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this); + if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL); + } +} + + +int TcpSocket::SSL_password_cb(char *buf,int num,int rwflag,void *userdata) +{ + Socket *p0 = static_cast(userdata); + TcpSocket *p = dynamic_cast(p0); + std::string pw = p ? p -> GetPassword() : ""; + if ( (size_t)num < pw.size() + 1) + { + return 0; + } + strcpy(buf,pw.c_str()); + return (int)pw.size(); +} +#endif // HAVE_OPENSSL + + +int TcpSocket::Close() +{ + if (GetSocket() == INVALID_SOCKET) // this could happen + { + Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING); + return 0; + } + int n; + SetNonblocking(true); + if (IsConnected() && !(GetShutdown() & SHUT_WR)) + { + if (shutdown(GetSocket(), SHUT_WR) == -1) + { + // failed... + Handler().LogError(this, "shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + } + // + char tmp[1000]; + if ((n = recv(GetSocket(),tmp,1000,0)) >= 0) + { + if (n) + { + Handler().LogError(this, "read() after shutdown", n, "bytes read", LOG_LEVEL_WARNING); + } + } +#ifdef HAVE_OPENSSL + if (IsSSL() && m_ssl) + SSL_shutdown(m_ssl); + if (m_ssl) + { + SSL_free(m_ssl); + m_ssl = NULL; + } +#endif + return Socket::Close(); +} + + +#ifdef HAVE_OPENSSL +SSL_CTX *TcpSocket::GetSslContext() +{ + if (!m_ssl_ctx) + Handler().LogError(this, "GetSslContext", 0, "SSL Context is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING); + return m_ssl_ctx; +} + +SSL *TcpSocket::GetSsl() +{ + if (!m_ssl) + Handler().LogError(this, "GetSsl", 0, "SSL is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING); + return m_ssl; +} +#endif + + +#ifdef ENABLE_RECONNECT +void TcpSocket::SetReconnect(bool x) +{ + m_b_reconnect = x; +} +#endif + + +void TcpSocket::OnRawData(const char *buf_in,size_t len) +{ +} + + +size_t TcpSocket::GetInputLength() +{ + return ibuf.GetLength(); +} + + +size_t TcpSocket::GetOutputLength() +{ + return m_output_length; +} + + +uint64_t TcpSocket::GetBytesReceived(bool clear) +{ + uint64_t z = m_bytes_received; + if (clear) + m_bytes_received = 0; + return z; +} + + +uint64_t TcpSocket::GetBytesSent(bool clear) +{ + uint64_t z = m_bytes_sent; + if (clear) + m_bytes_sent = 0; + return z; +} + + +#ifdef ENABLE_RECONNECT +bool TcpSocket::Reconnect() +{ + return m_b_reconnect; +} + + +void TcpSocket::SetIsReconnect(bool x) +{ + m_b_is_reconnect = x; +} + + +bool TcpSocket::IsReconnect() +{ + return m_b_is_reconnect; +} +#endif + + +#ifdef HAVE_OPENSSL +const std::string& TcpSocket::GetPassword() +{ + return m_password; +} +#endif + + +void TcpSocket::DisableInputBuffer(bool x) +{ + m_b_input_buffer_disabled = x; +} + + +void TcpSocket::OnOptions(int family,int type,int protocol,SOCKET s) +{ +DEB( fprintf(stderr, "Socket::OnOptions()\n");) +#ifdef SO_NOSIGPIPE + SetSoNosigpipe(true); +#endif + SetSoReuseaddr(true); + SetSoKeepalive(true); +} + + +void TcpSocket::SetLineProtocol(bool x) +{ + StreamSocket::SetLineProtocol(x); + DisableInputBuffer(x); +} + + +bool TcpSocket::SetTcpNodelay(bool x) +{ +#ifdef TCP_NODELAY + int optval = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_TCP, TCP_NODELAY)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + return false; + } + return true; +#else + Handler().LogError(this, "socket option not available", 0, "TCP_NODELAY", LOG_LEVEL_INFO); + return false; +#endif +} + + +TcpSocket::CircularBuffer::CircularBuffer(size_t size) +:buf(new char[2 * size]) +,m_max(size) +,m_q(0) +,m_b(0) +,m_t(0) +,m_count(0) +{ +} + + +TcpSocket::CircularBuffer::~CircularBuffer() +{ + delete[] buf; +} + + +bool TcpSocket::CircularBuffer::Write(const char *s,size_t l) +{ + if (m_q + l > m_max) + { + return false; // overflow + } + m_count += (unsigned long)l; + if (m_t + l > m_max) // block crosses circular border + { + size_t l1 = m_max - m_t; // size left until circular border crossing + // always copy full block to buffer(buf) + top pointer(m_t) + // because we have doubled the buffer size for performance reasons + memcpy(buf + m_t, s, l); + memcpy(buf, s + l1, l - l1); + m_t = l - l1; + m_q += l; + } + else + { + memcpy(buf + m_t, s, l); + memcpy(buf + m_max + m_t, s, l); + m_t += l; + if (m_t >= m_max) + m_t -= m_max; + m_q += l; + } + return true; +} + + +bool TcpSocket::CircularBuffer::Read(char *s,size_t l) +{ + if (l > m_q) + { + return false; // not enough chars + } + if (m_b + l > m_max) // block crosses circular border + { + size_t l1 = m_max - m_b; + if (s) + { + memcpy(s, buf + m_b, l1); + memcpy(s + l1, buf, l - l1); + } + m_b = l - l1; + m_q -= l; + } + else + { + if (s) + { + memcpy(s, buf + m_b, l); + } + m_b += l; + if (m_b >= m_max) + m_b -= m_max; + m_q -= l; + } + if (!m_q) + { + m_b = m_t = 0; + } + return true; +} + + +bool TcpSocket::CircularBuffer::Remove(size_t l) +{ + return Read(NULL, l); +} + + +size_t TcpSocket::CircularBuffer::GetLength() +{ + return m_q; +} + + +const char *TcpSocket::CircularBuffer::GetStart() +{ + return buf + m_b; +} + + +size_t TcpSocket::CircularBuffer::GetL() +{ + return (m_b + m_q > m_max) ? m_max - m_b : m_q; +} + + +size_t TcpSocket::CircularBuffer::Space() +{ + return m_max - m_q; +} + + +unsigned long TcpSocket::CircularBuffer::ByteCounter(bool clear) +{ + if (clear) + { + unsigned long x = m_count; + m_count = 0; + return x; + } + return m_count; +} + + +std::string TcpSocket::CircularBuffer::ReadString(size_t l) +{ + char *sz = new char[l + 1]; + if (!Read(sz, l)) // failed, debug printout in Read() method + { + delete[] sz; + return ""; + } + sz[l] = 0; + std::string tmp = sz; + delete[] sz; + return tmp; +} + + +void TcpSocket::OnConnectTimeout() +{ + Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL); +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + // retry direct connection + } + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + IncreaseConnectionRetries(); + // ask socket via OnConnectRetry callback if we should continue trying + if (OnConnectRetry()) + { + SetRetryClientConnect(); + } + else + { + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + } + } + else + { + SetCloseAndDelete(true); + /// \todo state reason why connect failed + OnConnectFailed(); + } + // + SetConnecting(false); +} + + +#ifdef _WIN32 +void TcpSocket::OnException() +{ + if (Connecting()) + { +#ifdef ENABLE_SOCKS4 + if (Socks4()) + OnSocks4ConnectFailed(); + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && + GetConnectionRetries() < GetConnectionRetry() )) + { + // even though the connection failed at once, only retry after + // the connection timeout + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + } + else + { + SetConnecting(false); // tnx snibbe + SetCloseAndDelete(); + OnConnectFailed(); + } + return; + } + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} +#endif // _WIN32 + + +int TcpSocket::Protocol() +{ + return IPPROTO_TCP; +} + + +void TcpSocket::SetTransferLimit(size_t sz) +{ + m_transfer_limit = sz; +} + + +void TcpSocket::OnTransferLimit() +{ +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/TcpSocket.h b/Sockets/TcpSocket.h new file mode 100644 index 0000000..47f7d15 --- /dev/null +++ b/Sockets/TcpSocket.h @@ -0,0 +1,355 @@ +/** \file TcpSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_TcpSocket_H +#define _SOCKETS_TcpSocket_H +#include "sockets-config.h" +#include "StreamSocket.h" +#ifdef HAVE_OPENSSL +#include +#include "SSLInitializer.h" +#endif + + +#define TCP_BUFSIZE_READ 16400 +#define TCP_OUTPUT_CAPACITY 1024000 + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class SocketAddress; + + +/** Socket implementation for TCP. + \ingroup basic */ +class TcpSocket : public StreamSocket +{ + /** \defgroup internal Internal utility */ +protected: + /** Buffer class containing one read/write circular buffer. + \ingroup internal */ + class CircularBuffer + { + public: + CircularBuffer(size_t size); + ~CircularBuffer(); + + /** append l bytes from p to buffer */ + bool Write(const char *p,size_t l); + /** copy l bytes from buffer to dest */ + bool Read(char *dest,size_t l); + /** skip l bytes from buffer */ + bool Remove(size_t l); + /** read l bytes from buffer, returns as string. */ + std::string ReadString(size_t l); + + /** total buffer length */ + size_t GetLength(); + /** pointer to circular buffer beginning */ + const char *GetStart(); + /** return number of bytes from circular buffer beginning to buffer physical end */ + size_t GetL(); + /** return free space in buffer, number of bytes until buffer overrun */ + size_t Space(); + + /** return total number of bytes written to this buffer, ever */ + unsigned long ByteCounter(bool clear = false); + + private: + CircularBuffer(const CircularBuffer& s) {} + CircularBuffer& operator=(const CircularBuffer& ) { return *this; } + char *buf; + size_t m_max; + size_t m_q; + size_t m_b; + size_t m_t; + unsigned long m_count; + }; + /** Output buffer struct. + \ingroup internal */ + struct OUTPUT { + OUTPUT() : _b(0), _t(0), _q(0) {} + OUTPUT(const char *buf, size_t len) : _b(0), _t(len), _q(len) { + memcpy(_buf, buf, len); + } + size_t Space() { + return TCP_OUTPUT_CAPACITY - _t; + } + void Add(const char *buf, size_t len) { + memcpy(_buf + _t, buf, len); + _t += len; + _q += len; + } + size_t Remove(size_t len) { + _b += len; + _q -= len; + return _q; + } + const char *Buf() { + return _buf + _b; + } + size_t Len() { + return _q; + } + size_t _b; + size_t _t; + size_t _q; + char _buf[TCP_OUTPUT_CAPACITY]; + }; + typedef std::list output_l; + +public: + /** Constructor with standard values on input/output buffers. */ + TcpSocket(ISocketHandler& ); + /** Constructor with custom values for i/o buffer. + \param h ISocketHandler reference + \param isize Input buffer size + \param osize Output buffer size */ + TcpSocket(ISocketHandler& h,size_t isize,size_t osize); + ~TcpSocket(); + + /** Open a connection to a remote server. + If you want your socket to connect to a server, + always call Open before Add'ing a socket to the sockethandler. + If not, the connection attempt will not be monitored by the + socket handler... + \param ip IP address + \param port Port number + \param skip_socks Do not use socks4 even if configured */ + bool Open(ipaddr_t ip,port_t port,bool skip_socks = false); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Open connection. + \param ip Ipv6 address + \param port Port number + \param skip_socks Do not use socks4 even if configured */ + bool Open(in6_addr ip,port_t port,bool skip_socks = false); +#endif +#endif + bool Open(SocketAddress&,bool skip_socks = false); + bool Open(SocketAddress&,SocketAddress& bind_address,bool skip_socks = false); + /** Open connection. + \param host Hostname + \param port Port number */ + bool Open(const std::string &host,port_t port); + + /** Connect timeout callback. */ + void OnConnectTimeout(); +#ifdef _WIN32 + /** Connection failed reported as exception on win32 */ + void OnException(); +#endif + + /** Close file descriptor - internal use only. + \sa SetCloseAndDelete */ + int Close(); + + /** Send a string. + \param s String to send + \param f Dummy flags -- not used */ + void Send(const std::string &s,int f = 0); + /** Send string using printf formatting. */ + void Sendf(const char *format, ...); + /** Send buffer of bytes. + \param buf Buffer pointer + \param len Length of data + \param f Dummy flags -- not used */ + void SendBuf(const char *buf,size_t len,int f = 0); + /** This callback is executed after a successful read from the socket. + \param buf Pointer to the data + \param len Length of the data */ + virtual void OnRawData(const char *buf,size_t len); + + /** Called when output buffer has been sent. + Note: Will only be called IF the output buffer has been used. + Send's that was successful without needing the output buffer + will not generate a call to this method. */ + virtual void OnWriteComplete(); + /** Number of bytes in input buffer. */ + size_t GetInputLength(); + /** Number of bytes in output buffer. */ + size_t GetOutputLength(); + + /** Callback fires when a socket in line protocol has read one full line. + \param line Line read */ + void OnLine(const std::string& line); + /** Get counter of number of bytes received. */ + uint64_t GetBytesReceived(bool clear = false); + /** Get counter of number of bytes sent. */ + uint64_t GetBytesSent(bool clear = false); + + /** Socks4 specific callback. */ + void OnSocks4Connect(); + /** Socks4 specific callback. */ + void OnSocks4ConnectFailed(); + /** Socks4 specific callback. + \return 'need_more' */ + bool OnSocks4Read(); + +#ifdef ENABLE_RESOLVER + /** Callback executed when resolver thread has finished a resolve request. */ + void OnResolved(int id,ipaddr_t a,port_t port); +#ifdef ENABLE_IPV6 + void OnResolved(int id,in6_addr& a,port_t port); +#endif +#endif +#ifdef HAVE_OPENSSL + /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */ + void OnSSLConnect(); + /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */ + void OnSSLAccept(); + /** This method must be implemented to initialize + the ssl context for an outgoing connection. */ + virtual void InitSSLClient(); + /** This method must be implemented to initialize + the ssl context for an incoming connection. */ + virtual void InitSSLServer(); +#endif + +#ifdef ENABLE_RECONNECT + /** Flag that says a broken connection will try to reconnect. */ + void SetReconnect(bool = true); + /** Check reconnect on lost connection flag status. */ + bool Reconnect(); + /** Flag to determine if a reconnect is in progress. */ + void SetIsReconnect(bool x = true); + /** Socket is reconnecting. */ + bool IsReconnect(); +#endif + + void DisableInputBuffer(bool = true); + + void OnOptions(int,int,int,SOCKET); + + void SetLineProtocol(bool = true); + + // TCP options + bool SetTcpNodelay(bool = true); + + virtual int Protocol(); + + /** Trigger limit for callback OnTransferLimit. */ + void SetTransferLimit(size_t sz); + /** This callback fires when the output buffer drops below the value + set by SetTransferLimit. Default: 0 (disabled). */ + virtual void OnTransferLimit(); + +protected: + TcpSocket(const TcpSocket& ); + void OnRead(); + void OnRead( char *buf, size_t n ); + void OnWrite(); +#ifdef HAVE_OPENSSL + /** SSL; Initialize ssl context for a client socket. + \param meth_in SSL method */ + void InitializeContext(const std::string& context, SSL_METHOD *meth_in = NULL); + /** SSL; Initialize ssl context for a server socket. + \param keyfile Combined private key/certificate file + \param password Password for private key + \param meth_in SSL method */ + void InitializeContext(const std::string& context, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL); + /** SSL; Initialize ssl context for a server socket. + \param certfile Separate certificate file + \param keyfile Combined private key/certificate file + \param password Password for private key + \param meth_in SSL method */ + void InitializeContext(const std::string& context, const std::string& certfile, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL); + /** SSL; Password callback method. */ +static int SSL_password_cb(char *buf,int num,int rwflag,void *userdata); + /** SSL; Get pointer to ssl context structure. */ + virtual SSL_CTX *GetSslContext(); + /** SSL; Get pointer to ssl structure. */ + virtual SSL *GetSsl(); + /** ssl; still negotiating connection. */ + bool SSLNegotiate(); + /** SSL; Get ssl password. */ + const std::string& GetPassword(); +#endif + + CircularBuffer ibuf; ///< Circular input buffer + +private: + TcpSocket& operator=(const TcpSocket& ) { return *this; } + + /** the actual send() */ + int TryWrite(const char *buf, size_t len); + /** add data to output buffer top */ + void Buffer(const char *buf, size_t len); + + // + bool m_b_input_buffer_disabled; + uint64_t m_bytes_sent; + uint64_t m_bytes_received; + bool m_skip_c; ///< Skip second char of CRLF or LFCR sequence in OnRead + char m_c; ///< First char in CRLF or LFCR sequence + std::string m_line; ///< Current line in line protocol mode +#ifdef SOCKETS_DYNAMIC_TEMP + char *m_buf; ///< temporary read buffer +#endif + output_l m_obuf; ///< output buffer + OUTPUT *m_obuf_top; ///< output buffer on top + size_t m_transfer_limit; + size_t m_output_length; + +#ifdef HAVE_OPENSSL +static SSLInitializer m_ssl_init; + SSL_CTX *m_ssl_ctx; ///< ssl context + SSL *m_ssl; ///< ssl 'socket' + BIO *m_sbio; ///< ssl bio + std::string m_password; ///< ssl password +#endif + +#ifdef ENABLE_SOCKS4 + int m_socks4_state; ///< socks4 support + char m_socks4_vn; ///< socks4 support, temporary variable + char m_socks4_cd; ///< socks4 support, temporary variable + unsigned short m_socks4_dstport; ///< socks4 support + unsigned long m_socks4_dstip; ///< socks4 support +#endif + +#ifdef ENABLE_RESOLVER + int m_resolver_id; ///< Resolver id (if any) for current Open call +#endif + +#ifdef ENABLE_RECONNECT + bool m_b_reconnect; ///< Reconnect on lost connection flag + bool m_b_is_reconnect; ///< Trying to reconnect +#endif + +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_TcpSocket_H + diff --git a/Sockets/Thread.cpp b/Sockets/Thread.cpp new file mode 100644 index 0000000..2717e32 --- /dev/null +++ b/Sockets/Thread.cpp @@ -0,0 +1,166 @@ +/** \file Thread.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include +#ifdef _WIN32 +#include +#include "socket_include.h" +#else +#include +#endif + +#include "Thread.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +Thread::Thread(bool release) +:m_thread(0) +,m_running(true) +,m_release(false) +,m_b_delete_on_exit(false) +,m_b_destructor(false) +{ +#ifdef _WIN32 +// m_thread = ::CreateThread(NULL, 0, StartThread, this, 0, &m_dwThreadId); + m_thread = (HANDLE)_beginthreadex(NULL, 0, &StartThread, this, 0, &m_dwThreadId); +#else + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + if (pthread_create(&m_thread,&attr, StartThread,this) == -1) + { + perror("Thread: create failed"); + SetRunning(false); + } +// pthread_attr_destroy(&attr); +#endif + m_release = release; +} + + +Thread::~Thread() +{ + m_b_destructor = true; + if (m_running) + { + SetRelease(true); + SetRunning(false); +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } +#ifdef _WIN32 + if (m_thread) + ::CloseHandle(m_thread); +#endif +} + + +threadfunc_t STDPREFIX Thread::StartThread(threadparam_t zz) +{ + Thread *p = (Thread *)zz; + + while (p -> m_running && !p -> m_release) + { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + if (p -> m_running) + { + p -> Run(); + } + p -> SetRunning(false); // if return + if (p -> DeleteOnExit() && !p -> IsDestructor()) + { + delete p; + } +#ifdef _WIN32 + _endthreadex(0); +#endif + return (threadfunc_t)NULL; +} + + +bool Thread::IsRunning() +{ + return m_running; +} + + +void Thread::SetRunning(bool x) +{ + m_running = x; +} + + +bool Thread::IsReleased() +{ + return m_release; +} + + +void Thread::SetRelease(bool x) +{ + m_release = x; +} + + +bool Thread::DeleteOnExit() +{ + return m_b_delete_on_exit; +} + + +void Thread::SetDeleteOnExit(bool x) +{ + m_b_delete_on_exit = x; +} + + +bool Thread::IsDestructor() +{ + return m_b_destructor; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/Thread.h b/Sockets/Thread.h new file mode 100644 index 0000000..d784a15 --- /dev/null +++ b/Sockets/Thread.h @@ -0,0 +1,100 @@ +/** \file Thread.h + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Thread_H +#define _SOCKETS_Thread_H + +#include "sockets-config.h" +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#ifdef _WIN32 +// to be +//typedef DWORD threadfunc_t; +//typedef LPVOID threadparam_t; +//#define STDPREFIX WINAPI +typedef unsigned threadfunc_t; +typedef void * threadparam_t; +#define STDPREFIX __stdcall +#else +#include + +typedef void * threadfunc_t; +typedef void * threadparam_t; +#define STDPREFIX +#endif + +/** \defgroup threading Threading */ +/** Thread base class. +The Thread class is used by the resolver (ResolvServer) and running a detached socket (SocketThread). +When you know some processing will take a long time and will freeze up a socket, there is always the +possibility to call Detach() on that socket before starting the processing. +When the OnDetached() callback is later called the processing can continue, now in its own thread. + \ingroup threading */ +class Thread +{ +public: + Thread(bool release = true); + virtual ~Thread(); + + static threadfunc_t STDPREFIX StartThread(threadparam_t); + + virtual void Run() = 0; + + bool IsRunning(); + void SetRunning(bool x); + bool IsReleased(); + void SetRelease(bool x); + bool DeleteOnExit(); + void SetDeleteOnExit(bool x = true); + bool IsDestructor(); + +private: + Thread(const Thread& ) {} + Thread& operator=(const Thread& ) { return *this; } +#ifdef _WIN32 + HANDLE m_thread; + unsigned m_dwThreadId; +#else + pthread_t m_thread; +#endif + bool m_running; + bool m_release; + bool m_b_delete_on_exit; + bool m_b_destructor; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Thread_H + diff --git a/Sockets/UdpSocket.cpp b/Sockets/UdpSocket.cpp new file mode 100644 index 0000000..75bebf2 --- /dev/null +++ b/Sockets/UdpSocket.cpp @@ -0,0 +1,843 @@ +/** \file UdpSocket.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif +#include +#else +#include +#endif + +#include "ISocketHandler.h" +#include "UdpSocket.h" +#include "Utility.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif +// include this to see strange sights +//#include + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +UdpSocket::UdpSocket(ISocketHandler& h, int ibufsz, bool ipv6, int retries) : Socket(h) +, m_ibuf(new char[ibufsz]) +, m_ibufsz(ibufsz) +, m_bind_ok(false) +, m_port(0) +, m_last_size_written(-1) +, m_retries(retries) +, m_b_read_ts(false) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + SetIpv6(ipv6); +#endif +#endif +} + + +UdpSocket::~UdpSocket() +{ + Close(); + delete[] m_ibuf; +} + + +int UdpSocket::Bind(port_t &port, int range) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(port); + return Bind(ad, range); + } +#endif +#endif + Ipv4Address ad(port); + return Bind(ad, range); +} + + +int UdpSocket::Bind(const std::string& intf, port_t &port, int range) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, range); + } + SetCloseAndDelete(); + return -1; + } +#endif +#endif + Ipv4Address ad(intf, port); + if (ad.IsValid()) + { + return Bind(ad, range); + } + SetCloseAndDelete(); + return -1; +} + + +int UdpSocket::Bind(ipaddr_t a, port_t &port, int range) +{ + Ipv4Address ad(a, port); + return Bind(ad, range); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +int UdpSocket::Bind(in6_addr a, port_t &port, int range) +{ + Ipv6Address ad(a, port); + return Bind(ad, range); +} +#endif +#endif + + +int UdpSocket::Bind(SocketAddress& ad, int range) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + int n = bind(GetSocket(), ad, ad); + int tries = range; + while (n == -1 && tries--) + { + ad.SetPort(ad.GetPort() + 1); + n = bind(GetSocket(), ad, ad); + } + if (n == -1) + { + Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for UdpSocket, port:range: " + Utility::l2string(ad.GetPort()) + ":" + Utility::l2string(range)); +#endif + return -1; + } + m_bind_ok = true; + m_port = ad.GetPort(); + return 0; + } + return -1; +} + + +/** if you wish to use Send, first Open a connection */ +bool UdpSocket::Open(ipaddr_t l, port_t port) +{ + Ipv4Address ad(l, port); + return Open(ad); +} + + +bool UdpSocket::Open(const std::string& host, port_t port) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(host, port); + if (ad.IsValid()) + { + return Open(ad); + } + return false; + } +#endif +#endif + Ipv4Address ad(host, port); + if (ad.IsValid()) + { + return Open(ad); + } + return false; +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool UdpSocket::Open(struct in6_addr& a, port_t port) +{ + Ipv6Address ad(a, port); + return Open(ad); +} +#endif +#endif + + +bool UdpSocket::Open(SocketAddress& ad) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + if (connect(GetSocket(), ad, ad) == -1) + { + Handler().LogError(this, "connect", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + return false; + } + SetConnected(); + return true; + } + return false; +} + + +void UdpSocket::CreateConnection() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + if (GetSocket() == INVALID_SOCKET) + { + SOCKET s = CreateSocket(AF_INET6, SOCK_DGRAM, "udp"); + if (s == INVALID_SOCKET) + { + return; + } + SetNonblocking(true, s); + Attach(s); + } + return; + } +#endif +#endif + if (GetSocket() == INVALID_SOCKET) + { + SOCKET s = CreateSocket(AF_INET, SOCK_DGRAM, "udp"); + if (s == INVALID_SOCKET) + { + return; + } + SetNonblocking(true, s); + Attach(s); + } +} + + +/** send to specified address */ +void UdpSocket::SendToBuf(const std::string& h, port_t p, const char *data, int len, int flags) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(h, p); + if (ad.IsValid()) + { + SendToBuf(ad, data, len, flags); + } + return; + } +#endif +#endif + Ipv4Address ad(h, p); + if (ad.IsValid()) + { + SendToBuf(ad, data, len, flags); + } +} + + +/** send to specified address */ +void UdpSocket::SendToBuf(ipaddr_t a, port_t p, const char *data, int len, int flags) +{ + Ipv4Address ad(a, p); + SendToBuf(ad, data, len, flags); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SendToBuf(in6_addr a, port_t p, const char *data, int len, int flags) +{ + Ipv6Address ad(a, p); + SendToBuf(ad, data, len, flags); +} +#endif +#endif + + +void UdpSocket::SendToBuf(SocketAddress& ad, const char *data, int len, int flags) +{ + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp")); + } + if (GetSocket() != INVALID_SOCKET) + { + SetNonblocking(true); + if ((m_last_size_written = sendto(GetSocket(), data, len, flags, ad, ad)) == -1) + { + Handler().LogError(this, "sendto", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + } +} + + +void UdpSocket::SendTo(const std::string& a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} + + +void UdpSocket::SendTo(ipaddr_t a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SendTo(in6_addr a, port_t p, const std::string& str, int flags) +{ + SendToBuf(a, p, str.c_str(), (int)str.size(), flags); +} +#endif +#endif + + +void UdpSocket::SendTo(SocketAddress& ad, const std::string& str, int flags) +{ + SendToBuf(ad, str.c_str(), (int)str.size(), flags); +} + + +/** send to connected address */ +void UdpSocket::SendBuf(const char *data, size_t len, int flags) +{ + if (!IsConnected()) + { + Handler().LogError(this, "SendBuf", 0, "not connected", LOG_LEVEL_ERROR); + return; + } + if ((m_last_size_written = send(GetSocket(), data, (int)len, flags)) == -1) + { + Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } +} + + +void UdpSocket::Send(const std::string& str, int flags) +{ + SendBuf(str.c_str(), (int)str.size(), flags); +} + + +#ifndef _WIN32 +int UdpSocket::ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts) +{ + struct msghdr msg; + struct iovec vec[1]; + union { + struct cmsghdr cm; + char data[CMSG_SPACE(sizeof(struct timeval))]; + } cmsg_un; + struct cmsghdr *cmsg; + struct timeval *tv; + + vec[0].iov_base = ioBuf; + vec[0].iov_len = inBufSize; + + memset(&msg, 0, sizeof(msg)); + memset(from, 0, fromlen); + memset(ioBuf, 0, inBufSize); + memset(&cmsg_un, 0, sizeof(cmsg_un)); + + msg.msg_name = (caddr_t)from; + msg.msg_namelen = fromlen; + msg.msg_iov = vec; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_un.data; + msg.msg_controllen = sizeof(cmsg_un.data); + msg.msg_flags = 0; + + // Original version - for reference only + //int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + + int n = recvmsg(GetSocket(), &msg, MSG_DONTWAIT); + + // now ioBuf will contain the data, as if we used recvfrom + + // Now get the time + if(n != -1 && msg.msg_controllen >= sizeof(struct cmsghdr) && !(msg.msg_flags & MSG_CTRUNC)) + { + tv = 0; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) + { + tv = (struct timeval *)CMSG_DATA(cmsg); + } + } + if (tv) + { + memcpy(ts, tv, sizeof(struct timeval)); + } + } + // The address is in network order, but that's OK right now + return n; +} +#endif + + +void UdpSocket::OnRead() +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct sockaddr_in6 sa; + socklen_t sa_len = sizeof(sa); + if (m_b_read_ts) + { + struct timeval ts; + Utility::GetTime(&ts); +#ifdef _WIN32 + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); +#else + int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts); +#endif + if (n > 0) + { + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts); + } + else + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + int q = m_retries; // receive max 10 at one cycle + while (n > 0) + { + if (sa_len != sizeof(sa)) + { + Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING); + } + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len); + if (!q--) + break; + // + n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + } + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } +#endif +#endif + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + if (m_b_read_ts) + { + struct timeval ts; + Utility::GetTime(&ts); +#ifdef _WIN32 + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); +#else + int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts); +#endif + if (n > 0) + { + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts); + } + else + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } + return; + } + int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + int q = m_retries; + while (n > 0) + { + if (sa_len != sizeof(sa)) + { + Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING); + } + this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len); + if (!q--) + break; + // + n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len); + } + if (n == -1) + { +#ifdef _WIN32 + if (Errno != WSAEWOULDBLOCK) +#else + if (Errno != EWOULDBLOCK) +#endif + Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR); + } +} + + +void UdpSocket::SetBroadcast(bool b) +{ + int one = 1; + int zero = 0; + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (b) + { + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof(one)) == -1) + { + Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + else + { + if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &zero, sizeof(zero)) == -1) + { + Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + + +bool UdpSocket::IsBroadcast() +{ + int is_broadcast = 0; + socklen_t size; + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (getsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&is_broadcast, &size) == -1) + { + Handler().LogError(this, "IsBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_broadcast != 0; +} + + +void UdpSocket::SetMulticastTTL(int ttl) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + + +int UdpSocket::GetMulticastTTL() +{ + int ttl = 0; + socklen_t size = sizeof(int); + + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, &size) == -1) + { + Handler().LogError(this, "GetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return ttl; +} + + +void UdpSocket::SetMulticastLoop(bool x) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + int val = x ? 1 : 0; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return; + } +#endif +#endif + int val = x ? 1 : 0; + if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + + +bool UdpSocket::IsMulticastLoop() +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + int is_loop = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&is_loop, &size) == -1) + { + Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_loop ? true : false; + } +#endif +#endif + int is_loop = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&is_loop, &size) == -1) + { + Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return is_loop ? true : false; +} + + +void UdpSocket::AddMulticastMembership(const std::string& group, const std::string& local_if, int if_index) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct ipv6_mreq x; + struct in6_addr addr; + if (Utility::u2ip( group, addr )) + { + x.ipv6mr_multiaddr = addr; + x.ipv6mr_interface = if_index; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1) + { + Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + return; + } +#endif +#endif + struct ip_mreq x; // ip_mreqn + ipaddr_t addr; + if (Utility::u2ip( group, addr )) + { + memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr)); + Utility::u2ip( local_if, addr); + memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr)); +// x.imr_ifindex = if_index; + if (setsockopt(GetSocket(), SOL_IP, IP_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + + +void UdpSocket::DropMulticastMembership(const std::string& group, const std::string& local_if, int if_index) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + struct ipv6_mreq x; + struct in6_addr addr; + if (Utility::u2ip( group, addr )) + { + x.ipv6mr_multiaddr = addr; + x.ipv6mr_interface = if_index; + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1) + { + Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } + return; + } +#endif +#endif + struct ip_mreq x; // ip_mreqn + ipaddr_t addr; + if (Utility::u2ip( group, addr )) + { + memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr)); + Utility::u2ip( local_if, addr); + memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr)); +// x.imr_ifindex = if_index; + if (setsockopt(GetSocket(), SOL_IP, IP_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1) + { + Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + } +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void UdpSocket::SetMulticastHops(int hops) +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (!IsIpv6()) + { + Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR); + return; + } + if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, sizeof(int)) == -1) + { + Handler().LogError(this, "SetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } +} + + +int UdpSocket::GetMulticastHops() +{ + if (GetSocket() == INVALID_SOCKET) + { + CreateConnection(); + } + if (!IsIpv6()) + { + Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR); + return -1; + } + int hops = 0; + socklen_t size = sizeof(int); + if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, &size) == -1) + { + Handler().LogError(this, "GetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING); + } + return hops; +} +#endif // IPPROTO_IPV6 +#endif + + +bool UdpSocket::IsBound() +{ + return m_bind_ok; +} + + +void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len) +{ +} + + +void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len, struct timeval *ts) +{ +} + + +port_t UdpSocket::GetPort() +{ + return m_port; +} + + +int UdpSocket::GetLastSizeWritten() +{ + return m_last_size_written; +} + + +void UdpSocket::SetTimestamp(bool x) +{ + m_b_read_ts = x; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + diff --git a/Sockets/UdpSocket.h b/Sockets/UdpSocket.h new file mode 100644 index 0000000..a456172 --- /dev/null +++ b/Sockets/UdpSocket.h @@ -0,0 +1,215 @@ +/** \file UdpSocket.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_UdpSocket_H +#define _SOCKETS_UdpSocket_H + +#include "sockets-config.h" +#include "Socket.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Socket implementation for UDP. + \ingroup basic */ +class UdpSocket : public Socket +{ +public: + /** Constructor. + \param h ISocketHandler reference + \param ibufsz Maximum size of receive message (extra bytes will be truncated) + \param ipv6 'true' if this is an ipv6 socket */ + UdpSocket(ISocketHandler& h,int ibufsz = 16384,bool ipv6 = false, int retries = 0); + ~UdpSocket(); + + /** Called when incoming data has been received. + \param buf Pointer to data + \param len Length of data + \param sa Pointer to sockaddr struct of sender + \param sa_len Length of sockaddr struct */ + virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len); + + /** Called when incoming data has been received and read timestamp is enabled. + \param buf Pointer to data + \param len Length of data + \param sa Pointer to sockaddr struct of sender + \param sa_len Length of sockaddr struct + \param ts Timestamp from message */ + virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len,struct timeval *ts); + + /** To receive incoming data, call Bind to setup an incoming port. + \param port Incoming port number + \param range Port range to try if ports already in use + \return 0 if bind succeeded */ + int Bind(port_t& port,int range = 1); + /** To receive data on a specific interface:port, use this. + \param intf Interface ip/hostname + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(const std::string& intf,port_t& port,int range = 1); + /** To receive data on a specific interface:port, use this. + \param a Ip address + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(ipaddr_t a,port_t& port,int range = 1); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** To receive data on a specific interface:port, use this. + \param a Ipv6 address + \param port Port number + \param range Port range + \return 0 if bind succeeded */ + int Bind(in6_addr a,port_t& port,int range = 1); +#endif +#endif + /** To receive data on a specific interface:port, use this. + \param ad Socket address + \param range Port range + \return 0 if bind succeeded */ + int Bind(SocketAddress& ad,int range = 1); + + /** Define remote host. + \param l Address of remote host + \param port Port of remote host + \return true if successful */ + bool Open(ipaddr_t l,port_t port); + /** Define remote host. + \param host Hostname + \param port Port number + \return true if successful */ + bool Open(const std::string& host,port_t port); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Define remote host. + \param a Address of remote host, ipv6 + \param port Port of remote host + \return true if successful */ + bool Open(struct in6_addr& a,port_t port); +#endif +#endif + /** Define remote host. + \param ad Socket address + \return true if successful */ + bool Open(SocketAddress& ad); + + /** Send to specified host */ + void SendToBuf(const std::string& ,port_t,const char *data,int len,int flags = 0); + /** Send to specified address */ + void SendToBuf(ipaddr_t,port_t,const char *data,int len,int flags = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Send to specified ipv6 address */ + void SendToBuf(in6_addr,port_t,const char *data,int len,int flags = 0); +#endif +#endif + /** Send to specified socket address */ + void SendToBuf(SocketAddress& ad,const char *data,int len,int flags = 0); + + /** Send string to specified host */ + void SendTo(const std::string&,port_t,const std::string&,int flags = 0); + /** Send string to specified address */ + void SendTo(ipaddr_t,port_t,const std::string&,int flags = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Send string to specified ipv6 address */ + void SendTo(in6_addr,port_t,const std::string&,int flags = 0); +#endif +#endif + /** Send string to specified socket address */ + void SendTo(SocketAddress& ad,const std::string&,int flags = 0); + + /** Send to connected address */ + void SendBuf(const char *data,size_t,int flags = 0); + /** Send string to connected address. */ + void Send(const std::string& ,int flags = 0); + + /** Set broadcast */ + void SetBroadcast(bool b = true); + /** Check broadcast flag. + \return true broadcast is enabled. */ + bool IsBroadcast(); + + /** multicast */ + void SetMulticastTTL(int ttl = 1); + int GetMulticastTTL(); + void SetMulticastLoop(bool = true); + bool IsMulticastLoop(); + void AddMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0); + void DropMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** multicast, ipv6 only */ + void SetMulticastHops(int = -1); + /** multicast, ipv6 only */ + int GetMulticastHops(); +#endif +#endif + /** Returns true if Bind succeeded. */ + bool IsBound(); + /** Return Bind port number */ + port_t GetPort(); + + void OnOptions(int,int,int,SOCKET) {} + + int GetLastSizeWritten(); + + /** Also read timestamp information from incoming message */ + void SetTimestamp(bool = true); + +protected: + UdpSocket(const UdpSocket& s) : Socket(s) {} + void OnRead(); +#ifndef _WIN32 + /** This method emulates socket recvfrom, but uses messages so we can get the timestamp */ + int ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts); +#endif + +private: + UdpSocket& operator=(const UdpSocket& ) { return *this; } + /** create before using sendto methods */ + void CreateConnection(); + char *m_ibuf; ///< Input buffer + int m_ibufsz; ///< Size of input buffer + bool m_bind_ok; ///< Bind completed successfully + port_t m_port; ///< Bind port number + int m_last_size_written; + int m_retries; + bool m_b_read_ts; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_UdpSocket_H + diff --git a/Sockets/Utility.cpp b/Sockets/Utility.cpp new file mode 100644 index 0000000..a80e2a8 --- /dev/null +++ b/Sockets/Utility.cpp @@ -0,0 +1,953 @@ +/** \file Utility.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "Utility.h" +#include "Parse.h" +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#include "RandomNumber.h" +#include "Base64.h" +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// statics +std::string Utility::m_host; +bool Utility::m_local_resolved = false; +ipaddr_t Utility::m_ip = 0; +std::string Utility::m_addr; +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +struct in6_addr Utility::m_local_ip6; +std::string Utility::m_local_addr6; +#endif +#endif + + +std::string Utility::base64(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.encode(str_in, str, false); // , false == do not add cr/lf + return str; +} + + +std::string Utility::base64d(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.decode(str_in, str); + return str; +} + + +std::string Utility::l2string(long l) +{ + std::string str; + char tmp[100]; + sprintf(tmp,"%ld",l); + str = tmp; + return str; +} + + +std::string Utility::bigint2string(uint64_t l) +{ + std::string str; + uint64_t tmp = l; + while (tmp) + { + uint64_t a = tmp % 10; + str = (char)(a + 48) + str; + tmp /= 10; + } + if (!str.size()) + { + str = "0"; + } + return str; +} + + +uint64_t Utility::atoi64(const std::string& str) +{ + uint64_t l = 0; + for (size_t i = 0; i < str.size(); i++) + { + l = l * 10 + str[i] - 48; + } + return l; +} + + +unsigned int Utility::hex2unsigned(const std::string& str) +{ + unsigned int r = 0; + for (size_t i = 0; i < str.size(); i++) + { + r = r * 16 + str[i] - 48 - ((str[i] >= 'A') ? 7 : 0) - ((str[i] >= 'a') ? 32 : 0); + } + return r; +} + + +/* +* Encode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_encode(const std::string& src) +{ +static char hex[] = "0123456789ABCDEF"; + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (isalnum(src[i])) + { + dst += src[i]; + } + else + if (src[i] == ' ') + { + dst += '+'; + } + else + { + unsigned char c = static_cast(src[i]); + dst += '%'; + dst += hex[c / 16]; + dst += hex[c % 16]; + } + } + return dst; +} // rfc1738_encode + + +/* +* Decode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_decode(const std::string& src) +{ + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (src[i] == '%' && isxdigit(src[i + 1]) && isxdigit(src[i + 2])) + { + char c1 = src[++i]; + char c2 = src[++i]; + c1 = c1 - 48 - ((c1 >= 'A') ? 7 : 0) - ((c1 >= 'a') ? 32 : 0); + c2 = c2 - 48 - ((c2 >= 'A') ? 7 : 0) - ((c2 >= 'a') ? 32 : 0); + dst += (char)(c1 * 16 + c2); + } + else + if (src[i] == '+') + { + dst += ' '; + } + else + { + dst += src[i]; + } + } + return dst; +} // rfc1738_decode + + +bool Utility::isipv4(const std::string& str) +{ + int dots = 0; + // %! ignore :port? + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '.') + dots++; + else + if (!isdigit(str[i])) + return false; + } + if (dots != 3) + return false; + return true; +} + + +bool Utility::isipv6(const std::string& str) +{ + size_t qc = 0; + size_t qd = 0; + for (size_t i = 0; i < str.size(); i++) + { + qc += (str[i] == ':') ? 1 : 0; + qd += (str[i] == '.') ? 1 : 0; + } + if (qc > 7) + { + return false; + } + if (qd && qd != 3) + { + return false; + } + Parse pa(str,":."); + std::string tmp = pa.getword(); + while (tmp.size()) + { + if (tmp.size() > 4) + { + return false; + } + for (size_t i = 0; i < tmp.size(); i++) + { + if (tmp[i] < '0' || (tmp[i] > '9' && tmp[i] < 'A') || + (tmp[i] > 'F' && tmp[i] < 'a') || tmp[i] > 'f') + { + return false; + } + } + // + tmp = pa.getword(); + } + return true; +} + + +bool Utility::u2ip(const std::string& str, ipaddr_t& l) +{ + struct sockaddr_in sa; + bool r = Utility::u2ip(str, sa); + memcpy(&l, &sa.sin_addr, sizeof(l)); + return r; +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool Utility::u2ip(const std::string& str, struct in6_addr& l) +{ + struct sockaddr_in6 sa; + bool r = Utility::u2ip(str, sa); + l = sa.sin6_addr; + return r; +} +#endif +#endif + + +void Utility::l2ip(const ipaddr_t ip, std::string& str) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, &ip, sizeof(sa.sin_addr)); + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); +} + + +void Utility::l2ip(const in_addr& ip, std::string& str) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = ip; + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +void Utility::l2ip(const struct in6_addr& ip, std::string& str,bool mixed) +{ + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + if (mixed) + { + unsigned short x; + unsigned short addr16[8]; + memcpy(addr16, &ip, sizeof(addr16)); + for (size_t i = 0; i < 6; i++) + { + x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + x = ntohs(addr16[6]); + sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255); + x = ntohs(addr16[7]); + sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255); + } + else + { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_addr = ip; + Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST); + return; + } + str = slask; +} + + +int Utility::in6_addr_compare(in6_addr a,in6_addr b) +{ + for (size_t i = 0; i < 16; i++) + { + if (a.s6_addr[i] < b.s6_addr[i]) + return -1; + if (a.s6_addr[i] > b.s6_addr[i]) + return 1; + } + return 0; +} +#endif +#endif + + +void Utility::ResolveLocal() +{ + char h[256]; + + // get local hostname and translate into ip-address + *h = 0; + gethostname(h,255); + { + if (Utility::u2ip(h, m_ip)) + { + Utility::l2ip(m_ip, m_addr); + } + } +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + memset(&m_local_ip6, 0, sizeof(m_local_ip6)); + { + if (Utility::u2ip(h, m_local_ip6)) + { + Utility::l2ip(m_local_ip6, m_local_addr6); + } + } +#endif +#endif + m_host = h; + m_local_resolved = true; +} + + +const std::string& Utility::GetLocalHostname() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_host; +} + + +ipaddr_t Utility::GetLocalIP() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_ip; +} + + +const std::string& Utility::GetLocalAddress() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_addr; +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +const struct in6_addr& Utility::GetLocalIP6() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_local_ip6; +} + + +const std::string& Utility::GetLocalAddress6() +{ + if (!m_local_resolved) + { + ResolveLocal(); + } + return m_local_addr6; +} +#endif +#endif + + +void Utility::SetEnv(const std::string& var,const std::string& value) +{ +#if (defined(SOLARIS8) || defined(SOLARIS)) + { + static std::map vmap; + if (vmap.find(var) != vmap.end()) + { + delete[] vmap[var]; + } + vmap[var] = new char[var.size() + 1 + value.size() + 1]; + sprintf(vmap[var], "%s=%s", var.c_str(), value.c_str()); + putenv( vmap[var] ); + } +#elif defined _WIN32 + { + std::string slask = var + "=" + value; + _putenv( (char *)slask.c_str()); + } +#else + setenv(var.c_str(), value.c_str(), 1); +#endif +} + + +std::string Utility::Sa2String(struct sockaddr *sa) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (sa -> sa_family == AF_INET6) + { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + std::string tmp; + Utility::l2ip(sa6 -> sin6_addr, tmp); + return tmp + ":" + Utility::l2string(ntohs(sa6 -> sin6_port)); + } +#endif +#endif + if (sa -> sa_family == AF_INET) + { + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + ipaddr_t a; + memcpy(&a, &sa4 -> sin_addr, 4); + std::string tmp; + Utility::l2ip(a, tmp); + return tmp + ":" + Utility::l2string(ntohs(sa4 -> sin_port)); + } + return ""; +} + + +void Utility::GetTime(struct timeval *p) +{ +#ifdef _WIN32 + FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). + GetSystemTimeAsFileTime(&ft); + uint64_t tt; + memcpy(&tt, &ft, sizeof(tt)); + tt /= 10; // make it usecs + p->tv_sec = (long)tt / 1000000; + p->tv_usec = (long)tt % 1000000; +#else + gettimeofday(p, NULL); +#endif +} + + +std::auto_ptr Utility::CreateAddress(struct sockaddr *sa,socklen_t sa_len) +{ + switch (sa -> sa_family) + { + case AF_INET: + if (sa_len == sizeof(struct sockaddr_in)) + { + struct sockaddr_in *p = (struct sockaddr_in *)sa; + return std::auto_ptr(new Ipv4Address(*p)); + } + break; +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + case AF_INET6: + if (sa_len == sizeof(struct sockaddr_in6)) + { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa; + return std::auto_ptr(new Ipv6Address(*p)); + } + break; +#endif +#endif + } + return std::auto_ptr(NULL); +} + + +bool Utility::u2ip(const std::string& host, struct sockaddr_in& sa, int ai_flags) +{ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; +#ifdef NO_GETADDRINFO + if ((ai_flags & AI_NUMERICHOST) != 0 || isipv4(host)) + { + Parse pa((char *)host.c_str(), "."); + union { + struct { + unsigned char b1; + unsigned char b2; + unsigned char b3; + unsigned char b4; + } a; + ipaddr_t l; + } u; + u.a.b1 = static_cast(pa.getvalue()); + u.a.b2 = static_cast(pa.getvalue()); + u.a.b3 = static_cast(pa.getvalue()); + u.a.b4 = static_cast(pa.getvalue()); + memcpy(&sa.sin_addr, &u.l, sizeof(sa.sin_addr)); + return true; + } +#ifndef LINUX + struct hostent *he = gethostbyname( host.c_str() ); + if (!he) + { + return false; + } + memcpy(&sa.sin_addr, he -> h_addr, sizeof(sa.sin_addr)); +#else + struct hostent he; + struct hostent *result = NULL; + int myerrno = 0; + char buf[2000]; + int n = gethostbyname_r(host.c_str(), &he, buf, sizeof(buf), &result, &myerrno); + if (n || !result) + { + return false; + } + if (he.h_addr_list && he.h_addr_list[0]) + memcpy(&sa.sin_addr, he.h_addr, 4); + else + return false; +#endif + return true; +#else + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + // AI_NUMERICHOST + // AI_CANONNAME + // AI_PASSIVE - server + // AI_ADDRCONFIG + // AI_V4MAPPED + // AI_ALL + // AI_NUMERICSERV + hints.ai_flags = ai_flags; + hints.ai_family = AF_INET; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + if (Utility::isipv4(host)) + hints.ai_flags |= AI_NUMERICHOST; + int n = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (!n) + { + static RandomNumber prng( true ); + std::vector vec; + struct addrinfo *ai = res; + while (ai) + { + if (ai -> ai_addrlen == sizeof(sa)) + vec.push_back( ai ); + prng.next(); + // + ai = ai -> ai_next; + } + if (!vec.size()) + return false; + ai = vec[prng.next() % vec.size()]; + { + memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); + } + freeaddrinfo(res); + return true; + } + std::string error = "Error: "; +#ifndef __CYGWIN__ + error += gai_strerror(n); +#endif + return false; +#endif // NO_GETADDRINFO +} + + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 +bool Utility::u2ip(const std::string& host, struct sockaddr_in6& sa, int ai_flags) +{ + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; +#ifdef NO_GETADDRINFO + if ((ai_flags & AI_NUMERICHOST) != 0 || isipv6(host)) + { + std::list vec; + size_t x = 0; + for (size_t i = 0; i <= host.size(); i++) + { + if (i == host.size() || host[i] == ':') + { + std::string s = host.substr(x, i - x); + // + if (strstr(s.c_str(),".")) // x.x.x.x + { + Parse pa(s,"."); + char slask[100]; // u2ip temporary hex2string conversion + unsigned long b0 = static_cast(pa.getvalue()); + unsigned long b1 = static_cast(pa.getvalue()); + unsigned long b2 = static_cast(pa.getvalue()); + unsigned long b3 = static_cast(pa.getvalue()); + sprintf(slask,"%lx",b0 * 256 + b1); + vec.push_back(slask); + sprintf(slask,"%lx",b2 * 256 + b3); + vec.push_back(slask); + } + else + { + vec.push_back(s); + } + // + x = i + 1; + } + } + size_t sz = vec.size(); // number of byte pairs + size_t i = 0; // index in in6_addr.in6_u.u6_addr16[] ( 0 .. 7 ) + unsigned short addr16[8]; + for (std::list::iterator it = vec.begin(); it != vec.end(); it++) + { + std::string bytepair = *it; + if (bytepair.size()) + { + addr16[i++] = htons(Utility::hex2unsigned(bytepair)); + } + else + { + addr16[i++] = 0; + while (sz++ < 8) + { + addr16[i++] = 0; + } + } + } + memcpy(&sa.sin6_addr, addr16, sizeof(addr16)); + return true; + } +#ifdef SOLARIS + int errnum = 0; + struct hostent *he = getipnodebyname( host.c_str(), AF_INET6, 0, &errnum ); +#else + struct hostent *he = gethostbyname2( host.c_str(), AF_INET6 ); +#endif + if (!he) + { + return false; + } + memcpy(&sa.sin6_addr,he -> h_addr_list[0],he -> h_length); +#ifdef SOLARIS + free(he); +#endif + return true; +#else + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = ai_flags; + hints.ai_family = AF_INET6; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + if (Utility::isipv6(host)) + hints.ai_flags |= AI_NUMERICHOST; + int n = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (!n) + { + static RandomNumber prng( true ); + std::vector vec; + struct addrinfo *ai = res; + while (ai) + { + if (ai -> ai_addrlen == sizeof(sa)) + vec.push_back( ai ); + prng.next(); + // + ai = ai -> ai_next; + } + if (!vec.size()) + return false; + ai = vec[prng.next() % vec.size()]; + { + memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen); + } + freeaddrinfo(res); + return true; + } + std::string error = "Error: "; +#ifndef __CYGWIN__ + error += gai_strerror(n); +#endif + return false; +#endif // NO_GETADDRINFO +} +#endif // IPPROTO_IPV6 +#endif // ENABLE_IPV6 + + +bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, int flags) +{ + std::string service; + return Utility::reverse(sa, sa_len, hostname, service, flags); +} + + +bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags) +{ + hostname = ""; + service = ""; +#ifdef NO_GETADDRINFO + switch (sa -> sa_family) + { + case AF_INET: + if (flags & NI_NUMERICHOST) + { + union { + struct { + unsigned char b1; + unsigned char b2; + unsigned char b3; + unsigned char b4; + } a; + ipaddr_t l; + } u; + struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; + memcpy(&u.l, &sa_in -> sin_addr, sizeof(u.l)); + char tmp[100]; + sprintf(tmp, "%u.%u.%u.%u", u.a.b1, u.a.b2, u.a.b3, u.a.b4); + hostname = tmp; + return true; + } + else + { + struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; + struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin_addr, sizeof(sa_in -> sin_addr), AF_INET); + if (h) + { + hostname = h -> h_name; + return true; + } + } + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + if (flags & NI_NUMERICHOST) + { + char slask[100]; // l2ip temporary + *slask = 0; + unsigned int prev = 0; + bool skipped = false; + bool ok_to_skip = true; + { + unsigned short addr16[8]; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; + memcpy(addr16, &sa_in6 -> sin6_addr, sizeof(addr16)); + for (size_t i = 0; i < 8; i++) + { + unsigned short x = ntohs(addr16[i]); + if (*slask && (x || !ok_to_skip || prev)) + strcat(slask,":"); + if (x || !ok_to_skip) + { + sprintf(slask + strlen(slask),"%x", x); + if (x && skipped) + ok_to_skip = false; + } + else + { + skipped = true; + } + prev = x; + } + } + if (!*slask) + strcpy(slask, "::"); + hostname = slask; + return true; + } + else + { + // %! TODO: ipv6 reverse lookup + struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)sa; + struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin6_addr, sizeof(sa_in -> sin6_addr), AF_INET6); + if (h) + { + hostname = h -> h_name; + return true; + } + } + break; +#endif + } + return false; +#else + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + // NI_NOFQDN + // NI_NUMERICHOST + // NI_NAMEREQD + // NI_NUMERICSERV + // NI_DGRAM + int n = getnameinfo(sa, sa_len, host, sizeof(host), serv, sizeof(serv), flags); + if (n) + { + // EAI_AGAIN + // EAI_BADFLAGS + // EAI_FAIL + // EAI_FAMILY + // EAI_MEMORY + // EAI_NONAME + // EAI_OVERFLOW + // EAI_SYSTEM + return false; + } + hostname = host; + service = serv; + return true; +#endif // NO_GETADDRINFO +} + + +bool Utility::u2service(const std::string& name, int& service, int ai_flags) +{ +#ifdef NO_GETADDRINFO + // %! + return false; +#else + struct addrinfo hints; + service = 0; + memset(&hints, 0, sizeof(hints)); + // AI_NUMERICHOST + // AI_CANONNAME + // AI_PASSIVE - server + // AI_ADDRCONFIG + // AI_V4MAPPED + // AI_ALL + // AI_NUMERICSERV + hints.ai_flags = ai_flags; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + struct addrinfo *res; + int n = getaddrinfo(NULL, name.c_str(), &hints, &res); + if (!n) + { + service = res -> ai_protocol; + freeaddrinfo(res); + return true; + } + return false; +#endif // NO_GETADDRINFO +} + + +unsigned long Utility::ThreadID() +{ +#ifdef _WIN32 + return GetCurrentThreadId(); +#else + return (unsigned long)pthread_self(); +#endif +} + + +std::string Utility::ToLower(const std::string& str) +{ + std::string r; + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] >= 'A' && str[i] <= 'Z') + r += str[i] | 32; + else + r += str[i]; + } + return r; +} + + +std::string Utility::ToUpper(const std::string& str) +{ + std::string r; + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] >= 'a' && str[i] <= 'z') + r += (char)(str[i] - 32); + else + r += str[i]; + } + return r; +} + + +std::string Utility::ToString(double d) +{ + char tmp[100]; + sprintf(tmp, "%f", d); + return tmp; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/Sockets/Utility.h b/Sockets/Utility.h new file mode 100644 index 0000000..133fbd1 --- /dev/null +++ b/Sockets/Utility.h @@ -0,0 +1,153 @@ +/** \file Utility.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Utility_H +#define _SOCKETS_Utility_H + +#include "sockets-config.h" +#include +#include +#include "socket_include.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class SocketAddress; + +/** Conversion utilities. + \ingroup util */ +class Utility +{ +public: + static std::string base64(const std::string& str_in); + static std::string base64d(const std::string& str_in); + static std::string l2string(long l); + static std::string bigint2string(uint64_t l); + static uint64_t atoi64(const std::string& str); + static unsigned int hex2unsigned(const std::string& str); + static std::string rfc1738_encode(const std::string& src); + static std::string rfc1738_decode(const std::string& src); + + /** Checks whether a string is a valid ipv4/ipv6 ip number. */ + static bool isipv4(const std::string&); + /** Checks whether a string is a valid ipv4/ipv6 ip number. */ + static bool isipv6(const std::string&); + + /** Hostname to ip resolution ipv4, not asynchronous. */ + static bool u2ip(const std::string&, ipaddr_t&); + static bool u2ip(const std::string&, struct sockaddr_in& sa, int ai_flags = 0); + +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Hostname to ip resolution ipv6, not asynchronous. */ + static bool u2ip(const std::string&, struct in6_addr&); + static bool u2ip(const std::string&, struct sockaddr_in6& sa, int ai_flags = 0); +#endif +#endif + + /** Reverse lookup of address to hostname */ + static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string&, int flags = 0); + static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags = 0); + + static bool u2service(const std::string& name, int& service, int ai_flags = 0); + + /** Convert binary ip address to string: ipv4. */ + static void l2ip(const ipaddr_t,std::string& ); + static void l2ip(const in_addr&,std::string& ); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Convert binary ip address to string: ipv6. */ + static void l2ip(const struct in6_addr&,std::string& ,bool mixed = false); + + /** ipv6 address compare. */ + static int in6_addr_compare(in6_addr,in6_addr); +#endif +#endif + /** ResolveLocal (hostname) - call once before calling any GetLocal method. */ + static void ResolveLocal(); + /** Returns local hostname, ResolveLocal must be called once before using. + \sa ResolveLocal */ + static const std::string& GetLocalHostname(); + /** Returns local ip, ResolveLocal must be called once before using. + \sa ResolveLocal */ + static ipaddr_t GetLocalIP(); + /** Returns local ip number as string. + \sa ResolveLocal */ + static const std::string& GetLocalAddress(); +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + /** Returns local ipv6 ip. + \sa ResolveLocal */ + static const struct in6_addr& GetLocalIP6(); + /** Returns local ipv6 address. + \sa ResolveLocal */ + static const std::string& GetLocalAddress6(); +#endif +#endif + /** Set environment variable. + \param var Name of variable to set + \param value Value */ + static void SetEnv(const std::string& var,const std::string& value); + /** Convert sockaddr struct to human readable string. + \param sa Ptr to sockaddr struct */ + static std::string Sa2String(struct sockaddr *sa); + + /** Get current time in sec/microseconds. */ + static void GetTime(struct timeval *); + + static std::auto_ptr CreateAddress(struct sockaddr *,socklen_t); + + static unsigned long ThreadID(); + + static std::string ToLower(const std::string& str); + static std::string ToUpper(const std::string& str); + + static std::string ToString(double d); + +private: + static std::string m_host; ///< local hostname + static ipaddr_t m_ip; ///< local ip address + static std::string m_addr; ///< local ip address in string format +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + static struct in6_addr m_local_ip6; ///< local ipv6 address +#endif + static std::string m_local_addr6; ///< local ipv6 address in string format +#endif + static bool m_local_resolved; ///< ResolveLocal has been called if true +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _SOCKETS_Utility_H + diff --git a/Sockets/ajp13.h b/Sockets/ajp13.h new file mode 100644 index 0000000..b82f45d --- /dev/null +++ b/Sockets/ajp13.h @@ -0,0 +1,91 @@ +/** + ** \file ajp13.h + ** \date 2007-10-05 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_Ajp13_H +#define _SOCKETS_Ajp13_H + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define HTTP_REQUEST_ACCEPT 0xa001 +#define HTTP_REQUEST_ACCEPT_CHARSET 0xa002 +#define HTTP_REQUEST_ACCEPT_ENCODING 0xa003 +#define HTTP_REQUEST_ACCEPT_LANGUAGE 0xa004 +#define HTTP_REQUEST_AUTHORIZATION 0xa005 +#define HTTP_REQUEST_CONNECTION 0xa006 +#define HTTP_REQUEST_CONTENT_TYPE 0xa007 +#define HTTP_REQUEST_CONTENT_LENGTH 0xa008 +#define HTTP_REQUEST_COOKIE 0xa009 +#define HTTP_REQUEST_COOKIE2 0xa00a +#define HTTP_REQUEST_HOST 0xa00b +#define HTTP_REQUEST_PRAGMA 0xa00c +#define HTTP_REQUEST_REFERER 0xa00d +#define HTTP_REQUEST_USER_AGENT 0xa00e + +#define HTTP_METHOD_OPTIONS 1 +#define HTTP_METHOD_GET 2 +#define HTTP_METHOD_HEAD 3 +#define HTTP_METHOD_POST 4 +#define HTTP_METHOD_PUT 5 +#define HTTP_METHOD_DELETE 6 +#define HTTP_METHOD_TRACE 7 +#define HTTP_METHOD_PROPFIND 8 +#define HTTP_METHOD_PROPPATCH 9 +#define HTTP_METHOD_MKCOL 10 +#define HTTP_METHOD_COPY 11 +#define HTTP_METHOD_MOVE 12 +#define HTTP_METHOD_LOCK 13 +#define HTTP_METHOD_UNLOCK 14 +#define HTTP_METHOD_ACL 15 +#define HTTP_METHOD_REPORT 16 +#define HTTP_METHOD_VERSION_CONTROL 17 // with a dash "VERSION-CONTROL" +#define HTTP_METHOD_CHECKIN 18 +#define HTTP_METHOD_CHECKOUT 19 +#define HTTP_METHOD_UNCHECKOUT 20 +#define HTTP_METHOD_SEARCH 21 +#define HTTP_METHOD_MKWORKSPACE 22 +#define HTTP_METHOD_UPDATE 23 +#define HTTP_METHOD_LABEL 24 +#define HTTP_METHOD_MERGE 25 +#define HTTP_METHOD_BASELINE_CONTROL 26 +#define HTTP_METHOD_MKACTIVITY 27 + +#define ATTR_CONTEXT 1 +#define ATTR_SERVLET_PATH 2 +#define ATTR_REMOTE_USER 3 +#define ATTR_AUTH_TYPE 4 +#define ATTR_QUERY_STRING 5 +#define ATTR_ROUTE 6 +#define ATTR_SSL_CERT 7 +#define ATTR_SSL_CIPHER 8 +#define ATTR_SSL_SESSION 9 +#define ATTR_SSL_KEY_SIZE 11 +#define ATTR_SECRET 12 +#define ATTR_STORED_METHOD 13 + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE { +#endif + +#endif // _SOCKETS_Ajp13_H + diff --git a/Sockets/gpl.txt b/Sockets/gpl.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/Sockets/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Sockets/mkdot.sh b/Sockets/mkdot.sh new file mode 100755 index 0000000..598de96 --- /dev/null +++ b/Sockets/mkdot.sh @@ -0,0 +1,13 @@ +#!/bin/sh +PROJECT_DIR=/usr/local/apache/www.alhem.net/htdocs/Sockets +PROJECT_NAME=sockets +/usr/local/bin/dp \ + -D_RUN_DP \ + -DUSE_SCTP -D_THREADSAFE_SOCKETS \ + -DHAVE_OPENSSL -DENABLE_POOL -DENABLE_RECONNECT -DENABLE_SOCKS4 \ + -DENABLE_IPV6 -DENABLE_DETACH -DENABLE_RESOLVER -DENABLE_TRIGGERS \ + -fontsize 12 \ + -lr -dot -cluster | grep -v std > x.dot +dot -Tpng -o $PROJECT_DIR/sockets.png x.dot +dot -Tcmapx -o $PROJECT_DIR/sockets.cmap x.dot +/usr/local/bin/mkjs $PROJECT_DIR/$PROJECT_NAME.cmap > $PROJECT_DIR/$PROJECT_NAME.js diff --git a/Sockets/socket_include.cpp b/Sockets/socket_include.cpp new file mode 100644 index 0000000..07b986b --- /dev/null +++ b/Sockets/socket_include.cpp @@ -0,0 +1,90 @@ +/** \file socket_include.cpp + ** \date 2004-11-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include + +// only to be included in win32 projects +const char *StrError(int x) +{ +static char tmp[100]; + switch (x) + { + case 10004: return "Interrupted function call."; + case 10013: return "Permission denied."; + case 10014: return "Bad address."; + case 10022: return "Invalid argument."; + case 10024: return "Too many open files."; + case 10035: return "Resource temporarily unavailable."; + case 10036: return "Operation now in progress."; + case 10037: return "Operation already in progress."; + case 10038: return "Socket operation on nonsocket."; + case 10039: return "Destination address required."; + case 10040: return "Message too long."; + case 10041: return "Protocol wrong type for socket."; + case 10042: return "Bad protocol option."; + case 10043: return "Protocol not supported."; + case 10044: return "Socket type not supported."; + case 10045: return "Operation not supported."; + case 10046: return "Protocol family not supported."; + case 10047: return "Address family not supported by protocol family."; + case 10048: return "Address already in use."; + case 10049: return "Cannot assign requested address."; + case 10050: return "Network is down."; + case 10051: return "Network is unreachable."; + case 10052: return "Network dropped connection on reset."; + case 10053: return "Software caused connection abort."; + case 10054: return "Connection reset by peer."; + case 10055: return "No buffer space available."; + case 10056: return "Socket is already connected."; + case 10057: return "Socket is not connected."; + case 10058: return "Cannot send after socket shutdown."; + case 10060: return "Connection timed out."; + case 10061: return "Connection refused."; + case 10064: return "Host is down."; + case 10065: return "No route to host."; + case 10067: return "Too many processes."; + case 10091: return "Network subsystem is unavailable."; + case 10092: return "Winsock.dll version out of range."; + case 10093: return "Successful WSAStartup not yet performed."; + case 10101: return "Graceful shutdown in progress."; + case 10109: return "Class type not found."; + case 11001: return "Host not found."; + case 11002: return "Nonauthoritative host not found."; + case 11003: return "This is a nonrecoverable error."; + case 11004: return "Valid name, no data record of requested type."; + + default: + break; + } + sprintf(tmp, "Winsock error code: %d", x); + return tmp; +} + + + diff --git a/Sockets/socket_include.h b/Sockets/socket_include.h new file mode 100644 index 0000000..213c8b3 --- /dev/null +++ b/Sockets/socket_include.h @@ -0,0 +1,288 @@ +/** \file socket_include.h + ** \date 2005-04-12 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_socket_include_H +#define _SOCKETS_socket_include_H +#include "sockets-config.h" + +#ifdef _MSC_VER +#pragma warning(disable:4514) +#endif + +// common defines affecting library and applications using library + +/* Define SOCKETS_DYNAMIC_TEMP to use dynamically allocated buffers + in read operations - helps on ECOS */ +#define SOCKETS_DYNAMIC_TEMP + + +// platform specific stuff +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif +#include + +// int64 +#ifdef _WIN32 +typedef unsigned __int64 uint64_t; +#else +#include +#ifdef SOLARIS +# include +#else +# include +#endif +#endif + +#ifndef _WIN32 +// ---------------------------------------- +// common unix includes / defines +#include +#include +#include +#include +#include +#include +//#include + +// all typedefs in this file will be declared outside the sockets namespace, +// because some os's will already have one or more of the type defined. +typedef int SOCKET; +#define Errno errno +#define StrError strerror + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// WIN32 adapt +#define closesocket close +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif // INADDR_NONE + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // !_WIN32 + + +// ---------------------------------------- +// Generic +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#endif + + +// ---------------------------------------- +// OS specific adaptions + +#ifdef SOLARIS +// ---------------------------------------- +// Solaris +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 _S6_un._S6_u8 +#define MSG_NOSIGNAL 0 + +#elif defined __FreeBSD__ +// ---------------------------------------- +// FreeBSD +# if __FreeBSD_version >= 400014 +# define s6_addr16 __u6_addr.__u6_addr16 +# if !defined(MSG_NOSIGNAL) +# define MSG_NOSIGNAL 0 +# endif +# include +typedef in_addr_t ipaddr_t; +typedef in_port_t port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +# else +# error FreeBSD versions prior to 400014 does not support ipv6 +# endif + +#elif defined MACOSX +// ---------------------------------------- +// Mac OS X +#include +#include +typedef unsigned long ipaddr_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 __u6_addr.__u6_addr16 +#define MSG_NOSIGNAL 0 // oops - thanks Derek +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP + +#elif defined _WIN32 +// ---------------------------------------- +// Win32 +#ifdef _MSC_VER +#pragma comment(lib, "wsock32.lib") +#endif +#define strcasecmp _stricmp + +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +typedef int socklen_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + +// 1.8.6: define FD_SETSIZE to something bigger than 64 if there are a lot of +// simultaneous connections (must be done before including winsock.h) +//#define FD_SETSIZE 1024 + +// windows 2000 with ipv6 preview installed: +// http://msdn.microsoft.com/downloads/sdks/platform/tpipv6.asp +// see the FAQ on how to install +#define WIN32_LEAN_AND_MEAN +#include +#include +#if _MSC_VER < 1200 +#ifndef __CYGWIN__ +#ifdef ENABLE_IPV6 +#include // For IPv6 Tech Preview. +#endif +#endif +#endif // _MSC_VER < 1200 + + +#define MSG_NOSIGNAL 0 +//#define SHUT_RDWR 2 +#define SHUT_WR 1 + +#define Errno WSAGetLastError() +const char *StrError(int x); + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +// class WSAInitializer is a part of the Socket class (on win32) +// as a static instance - so whenever an application uses a Socket, +// winsock is initialized +class WSAInitializer // Winsock Initializer +{ +public: + WSAInitializer() { + if (WSAStartup(0x101,&m_wsadata)) + { + exit(-1); + } + } + ~WSAInitializer() { + WSACleanup(); + } +private: + WSADATA m_wsadata; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#else +// ---------------------------------------- +// LINUX +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif +// no defs + +#ifdef SOCKETS_NAMESPACE +} +#endif + + +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + /** List type containing file descriptors. */ + typedef std::list socket_v; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + +// getaddrinfo / getnameinfo replacements +#ifdef NO_GETADDRINFO +#ifndef AI_NUMERICHOST +#define AI_NUMERICHOST 1 +#endif +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 1 +#endif +#endif + + +#endif // _SOCKETS_socket_include_H + diff --git a/Sockets/sockets-config.h b/Sockets/sockets-config.h new file mode 100644 index 0000000..dd04647 --- /dev/null +++ b/Sockets/sockets-config.h @@ -0,0 +1,97 @@ +/** + ** \file sockets-config.h + ** \date 2007-04-14 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef _SOCKETS_CONFIG_H +#define _SOCKETS_CONFIG_H + +#ifndef _RUN_DP +/* First undefine symbols if already defined. */ +#undef HAVE_OPENSSL +#undef ENABLE_IPV6 +#undef USE_SCTP +#undef NO_GETADDRINFO +#undef ENABLE_POOL +#undef ENABLE_SOCKS4 +#undef ENABLE_RESOLVER +#undef ENABLE_RECONNECT +#undef ENABLE_DETACH +#undef ENABLE_TRIGGERS +#undef ENABLE_EXCEPTIONS +#endif // _RUN_DP + + +/* OpenSSL support. */ +#define HAVE_OPENSSL + + +/* Ipv6 support. */ +//#define ENABLE_IPV6 + + +/* SCTP support. */ +//#define USE_SCTP + + +/* Define NO_GETADDRINFO if your operating system does not support + the "getaddrinfo" and "getnameinfo" function calls. */ +#define NO_GETADDRINFO + + +/* Connection pool support. */ +//#define ENABLE_POOL + + +/* Socks4 client support. */ +//#define ENABLE_SOCKS4 + + +/* Asynchronous resolver. */ +#define ENABLE_RESOLVER + + +/* Enable TCP reconnect on lost connection. + Socket::OnReconnect + Socket::OnDisconnect +*/ +#define ENABLE_RECONNECT + + +/* Enable socket thread detach functionality. */ +#define ENABLE_DETACH + + +/* Enable socket to socket triggers. Not yet in use. */ +//#define ENABLE_TRIGGERS + + +/* Enabled exceptions. */ +#define ENABLE_EXCEPTIONS + + +/* Resolver uses the detach function so either enable both or disable both. */ +#ifndef ENABLE_DETACH +#undef ENABLE_RESOLVER +#endif + + +#endif // _SOCKETS_CONFIG_H + diff --git a/Sockets/tests/Makefile b/Sockets/tests/Makefile new file mode 100644 index 0000000..0bbabf8 --- /dev/null +++ b/Sockets/tests/Makefile @@ -0,0 +1,73 @@ +SOCKETS = .. +PLATFORM = linux-x86-32 +LIBS = -L$(SOCKETS) -lSockets + +INCLUDE = -I$(SOCKETS) +include $(SOCKETS)/Makefile.version +include $(SOCKETS)/Makefile.Defines.$(PLATFORM) + +CPPFLAGS = $(CFLAGS) + +#LIBS += -lsctp + +PROGS = events resolve sockets_test base64 semtest \ + echoserver stressclient http httpd retry resume \ + http_post x copy crlf https sloppy_http + +all: $(PROGS) + +events: events.o + $(CXX) -o $@ $^ $(LIBS) + +resolve: resolve.o + $(CXX) -o $@ $^ $(LIBS) + +sockets_test: sockets_test.o + $(CXX) -o $@ $^ $(LIBS) + +base64: base64.o + $(CXX) -o $@ $^ $(LIBS) + +semtest: semtest.o + $(CXX) -o $@ $^ $(LIBS) + +echoserver: echoserver.o + $(CXX) -o $@ $^ $(LIBS) + +stressclient: stressclient.o + $(CXX) -o $@ $^ $(LIBS) + +http: http.o + $(CXX) -o $@ $^ $(LIBS) + +httpd: httpd.o + $(CXX) -o $@ $^ $(LIBS) + +retry: retry.o + $(CXX) -o $@ $^ $(LIBS) + +resume: resume.o + $(CXX) -o $@ $^ $(LIBS) + +http_post: http_post.o + $(CXX) -o $@ $^ $(LIBS) + +x: x.o + $(CXX) -o $@ $^ $(LIBS) + +copy: copy.o + $(CXX) -o $@ $^ $(LIBS) + +crlf: crlf.o + $(CXX) -o $@ $^ $(LIBS) + +https: https.o + $(CXX) -o $@ $^ $(LIBS) + +sloppy_http: sloppy_http.o + $(CXX) -o $@ $^ $(LIBS) + +clean: + rm -f *.o *~ slask *.d $(PROGS) + +-include *.d diff --git a/Sockets/tests/base64.cpp b/Sockets/tests/base64.cpp new file mode 100644 index 0000000..722439d --- /dev/null +++ b/Sockets/tests/base64.cpp @@ -0,0 +1,34 @@ +#include + +#include "Base64.h" + + +int main(int argc,char *argv[]) +{ + if (argc < 2) + return -1; + std::string str; + std::string str2; + Base64 b; + if (!strcmp(argv[1],"-file")) + { + if (argc < 3) + return -2; + FILE *fil; + if ((fil = fopen(argv[2],"rt")) != NULL) + { + b.encode(fil, str); + fclose(fil); + } + printf("File:\n%s\n--End of File\n",str.c_str()); + b.decode(str, str2); + printf("Content:\n%s\n--End of Content\n",str2.c_str()); + } + else + { + b.encode(argv[1], strlen(argv[1]), str); + printf("'%s' ==> '%s'",argv[1], str.c_str()); + b.decode(str, str2); + printf(" ==> '%s'\n",str2.c_str()); + } +} diff --git a/Sockets/tests/copy.cpp b/Sockets/tests/copy.cpp new file mode 100644 index 0000000..2134ac2 --- /dev/null +++ b/Sockets/tests/copy.cpp @@ -0,0 +1,264 @@ +#include +#include +#include +#include + +#define BUFSZ 7024 + +#ifdef _WIN32 +#define MIN(a,b) (a 0 && GetOutputLength() < lim) + { + SendBuf(buf, n); + m_sz -= n; + n = MIN(m_sz, BUFSZ); + } + if (!n) + { + SetCloseAndDelete(); + } + else + { + SendBuf(buf, n); + m_sz -= n; + } + return; + } + size_t x = 0; + for (size_t i = 0; i < m_filename.size(); i++) + if (m_filename[i] == '/') + x = i + 1; + Send(m_filename.substr(x) + "\n"); + m_fil = fopen(m_filename.c_str(), "rb"); + if (m_fil) + { + char buf[BUFSZ]; + int n = fread(buf, 1, BUFSZ, m_fil); + while (n > 0 && GetOutputLength() < lim) + { + SendBuf(buf, n); + n = fread(buf, 1, BUFSZ, m_fil); + } + if (!n) + { + SetCloseAndDelete(); + fclose(m_fil); + m_fil = NULL; + } + else + { + SendBuf(buf, n); + } + } + } + + void OnWriteComplete() { + } + + void OnTransferLimit() { + if (!m_filename.size()) + { + char buf[BUFSZ]; + int n = MIN(m_sz, BUFSZ); + while (n > 0 && GetOutputLength() < lim) + { + SendBuf(buf, n); + m_sz -= n; + n = MIN(m_sz, BUFSZ); + } + if (!n) + { + SetCloseAndDelete(); + } + else + { + SendBuf(buf, n); + m_sz -= n; + } + return; + } + if (m_fil) + { + char buf[BUFSZ]; + int n = fread(buf, 1, BUFSZ, m_fil); + while (n > 0 && GetOutputLength() < lim) + { + SendBuf(buf, n); + n = fread(buf, 1, BUFSZ, m_fil); + } + if (!n) + { + SetCloseAndDelete(); + fclose(m_fil); + m_fil = NULL; + } + else + { + SendBuf(buf, n); + } + } + } + + void OnAccept() { + Utility::GetTime(&m_start); + m_b_server = true; + SetLineProtocol(); + DisableInputBuffer(); + } + + void OnLine(const std::string& line) { + if (line.size()) + m_out = fopen(line.c_str(), "wb"); + SetLineProtocol(false); + DisableInputBuffer(); + } + + void OnDelete() { + struct timeval stop; + Utility::GetTime(&stop); + stop.tv_sec -= m_start.tv_sec; + stop.tv_usec -= m_start.tv_usec; + if (stop.tv_usec < 0) + { + stop.tv_usec += 1000000; + stop.tv_sec -= 1; + } + double t = stop.tv_usec; + t /= 1000000; + t += stop.tv_sec; + printf("OnDelete: %s\n", m_b_server ? "SERVER" : "CLIENT"); + printf(" Time: %ld.%06ld (%f)\n", stop.tv_sec, stop.tv_usec, t); + double r = GetBytesReceived(); + printf(" bytes in: %lld (%f Mbytes/sec)\n", GetBytesReceived(), r / t / 1000000); + double s = GetBytesSent(); + printf(" bytes out: %lld (%f Mbytes/sec)\n", GetBytesSent(), s / t / 1000000); + printf("\n"); + if (m_out) + fclose(m_out); + } + + void OnRawData(const char *buf, size_t len) { + if (m_out) + fwrite(buf, 1, len, m_out); + } + +private: + std::string m_filename; + FILE *m_fil; + FILE *m_out; + bool m_b_server; + struct timeval m_start; + size_t m_sz; +}; + + +int main(int argc, char *argv[]) +{ + std::string host = "127.0.0.1"; + int port = 12344; + std::list filenames; + + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-host") && i < argc - 1) + host = argv[++i]; + else + if (!strcmp(argv[i], "-port") && i < argc - 1) + port = atoi(argv[++i]); + else + if (!strcmp(argv[i], "-h")) + { + fprintf(stderr, "Usage: %s [-host ] [-port ] []\n", *argv); + fprintf(stderr, " Will run as host only if isn't specified.\n"); + fprintf(stderr, " host default: 127.0.0.1\n"); + fprintf(stderr, " port default: 12344\n"); + return 0; + } + else + { + filenames.push_back( argv[i] ); + } + } + + try + { + SocketHandler h; + ListenSocket l(h); + if (filenames.empty()) + { + if (l.Bind( port ) != 0) + { + fprintf(stderr, "Bind() port %d failed - exiting\n", port); + return -1; + } + h.Add(&l); + } + for (std::list::iterator it = filenames.begin(); it != filenames.end(); it++) + { + std::string filename = *it; + size_t sz = atol(filename.c_str()); + if (sz) + { + CopySocket *sock = new CopySocket(h, sz); + sock -> SetDeleteByHandler(); + sock -> Open(host, port); + h.Add(sock); + } + else + { + CopySocket *sock = new CopySocket(h, filename); + sock -> SetDeleteByHandler(); + sock -> Open(host, port); + h.Add(sock); + } + } + if (!filenames.size()) + { + fprintf(stderr, "Starting as server only, listening on port %d\n", port); + quit++; + } + while (quit > 0) + { + h.Select(5, 0); + } + return 0; + } + catch (const Exception& e) + { + printf("%s\n", e.ToString().c_str()); + } +} + + diff --git a/Sockets/tests/crlf.cpp b/Sockets/tests/crlf.cpp new file mode 100644 index 0000000..34500c4 --- /dev/null +++ b/Sockets/tests/crlf.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + + +static bool quit = false; + + +class IOSocket : public TcpSocket +{ +public: + IOSocket(ISocketHandler& h) : TcpSocket(h) { + SetLineProtocol(); + } + ~IOSocket() {} + + void OnConnect() { + Utility::GetTime(&m_start); + while (GetOutputLength() < 500000) + { + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + } +// Send("\r\n"); + } + + void OnAccept() { + SetLineProtocol(); + } + + void OnDelete() { + printf("OnDelete\n"); + } + + void OnWriteComplete() { +printf("OnWriteComplete\n"); + struct timeval tv; + Utility::GetTime(&tv); + + tv.tv_sec -= m_start.tv_sec; + tv.tv_usec -= m_start.tv_usec; + Utility::GetTime(&m_start); + if (tv.tv_usec < 0) + { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + double t = tv.tv_usec; + t /= 1000000; + t += tv.tv_sec; + printf(" Time: %ld.%06ld (%f)\n", tv.tv_sec, tv.tv_usec, t); + double r = GetBytesReceived(); + printf(" bytes in: %lld (%f Mbytes/sec)\n", GetBytesReceived(true), r / t / 1000000); + double s = GetBytesSent(); + printf(" bytes out: %lld (%f Mbytes/sec)\n", GetBytesSent(true), s / t / 1000000); + printf("\n"); + + while (GetOutputLength() < 500000) + { + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk54\r\n"); + Send("zzzzzzzzzmmmmmmmmm1234lkkk543\r\n"); + } +// Send("\r\n"); + } + + void OnLine(const std::string& line) { + if (line == "") + { + struct timeval tv; + Utility::GetTime(&tv); + + tv.tv_sec -= m_start.tv_sec; + tv.tv_usec -= m_start.tv_usec; + Utility::GetTime(&m_start); + if (tv.tv_usec < 0) + { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + double t = tv.tv_usec; + t /= 1000000; + t += tv.tv_sec; + printf(" Time: %ld.%06ld (%f)\n", tv.tv_sec, tv.tv_usec, t); + double r = GetBytesReceived(); + printf(" bytes in: %lld (%f Mbytes/sec)\n", GetBytesReceived(true), r / t / 1000000); + double s = GetBytesSent(); + printf(" bytes out: %lld (%f Mbytes/sec)\n", GetBytesSent(true), s / t / 1000000); + printf("\n"); + + } + else + if (line != "zzzzzzzzzmmmmmmmmm1234lkkk54" && + line != "zzzzzzzzzmmmmmmmmm1234lkkk543") + { +printf("\n-------------------------------------------------------\n"); + for (size_t i = 0; i < line.size(); i++) + if (!isprint(line[i])) + printf("<%d>", line[i]); + else + printf("%c", line[i]); + printf("\n"); +fflush(stdout); +exit(-1); + quit = true; + } + } + + struct timeval m_start; +}; + + +int main(int argc, char *argv[]) +{ + SocketHandler h; + ListenSocket l(h); + l.Bind(12344); + h.Add(&l); + IOSocket sock(h); + sock.Open("192.168.7.4", 12344); + h.Add(&sock); + while (!quit) + { + h.Select(1, 0); + } +} + + diff --git a/Sockets/tests/echoserver.cpp b/Sockets/tests/echoserver.cpp new file mode 100644 index 0000000..45cb6c2 --- /dev/null +++ b/Sockets/tests/echoserver.cpp @@ -0,0 +1,237 @@ +/** + ** \file echoserver.cpp + ** \date 2006-10-02 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2006 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include + + +#ifdef ENABLE_DETACH +static bool g_b_detach = false; +#endif +#ifdef HAVE_OPENSSL +static bool g_b_ssl = false; +#endif +static std::string gFilename = "server.pem"; +static std::string gPw; +static bool quit = false; +static bool g_b_http = false; +static bool g_b_nobuf = false; +#ifdef ENABLE_DETACH +static bool g_b_detach2 = false; +#endif + +#define DEB(x) + + +class MySocket : public TcpSocket +{ +public: + MySocket(ISocketHandler& h) : TcpSocket(h) { + SetLineProtocol(); +#ifdef HAVE_OPENSSL + if (g_b_ssl) + EnableSSL(); +#endif + if (g_b_nobuf) + DisableInputBuffer(); + } + ~MySocket() { + } + + void OnAccept() { +#ifdef ENABLE_DETACH + if (g_b_detach) + { + if (!Detach()) + fprintf(stderr, "\nDetach failed\n"); + } +#endif + } + + void OnRawData(const char *buf,size_t len) { + if (g_b_nobuf) + { + SendBuf(buf, len); + } + } + + void OnLine(const std::string& line) { +#ifdef ENABLE_DETACH + if (g_b_detach2 && !IsDetach()) + { + m_line = line; + if (!Detach()) + { + fprintf(stderr, "\nDetach failed\n"); + } + return; + } +DEB(printf("fd %d OnLine; %s\n", GetSocket(), Handler().IsSlave() ? "slave" : "master");) +#endif + Send(line + "\n"); + } + + void OnDetached() { +DEB(printf("fd %d OnDetached; %s\n", GetSocket(), Handler().IsSlave() ? "slave" : "master");) +// fprintf(stderr, "-"); +// fflush(stderr); +#ifdef ENABLE_DETACH + if (g_b_detach2) + { + Send(m_line + "\n"); + } +#endif + } + +#ifdef HAVE_OPENSSL + void InitSSLServer() { + InitializeContext("echoserver", gFilename, gPw, SSLv23_method()); + } +#endif + +private: + std::string m_line; +}; + + +#ifndef _WIN32 +void sigint(int) +{ + quit = true; +} +#endif + +int main(int argc,char *argv[]) +{ +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); + signal(SIGINT, sigint); +#endif + port_t port = 2222; + bool enableLog = false; + int queue = 20; + for (int i = 1; i < argc; i++) + { +#ifdef ENABLE_DETACH + if (!strcmp(argv[i], "-detach")) + g_b_detach = true; + if (!strcmp(argv[i], "-detach2")) + g_b_detach2 = true; +#endif +#ifdef HAVE_OPENSSL + if (!strcmp(argv[i], "-ssl")) + g_b_ssl = true; + if (!strcmp(argv[i], "-file") && i < argc - 1) + gFilename = argv[++i]; +#endif + if (!strcmp(argv[i], "-port") && i < argc - 1) + port = atoi(argv[++i]); + if (!strcmp(argv[i], "-pw") && i < argc - 1) + gPw = argv[++i]; + if (!strcmp(argv[i], "-log")) + enableLog = true; + if (!strcmp(argv[i], "-queue") && i < argc - 1) + queue = atoi(argv[++i]); + if (!strcmp(argv[i], "-http")) + g_b_http = true; + if (!strcmp(argv[i], "-nobuf")) + g_b_nobuf = true; + if (!strcmp(argv[i], "-h")) + { + printf("Usage: %s [ options ] [-ssl]\n", *argv); + printf(" -port nn listen on port nn\n"); +#ifdef ENABLE_DETACH + printf(" -detach detach each socket on accept\n"); + printf(" -detach2 detach when line received\n"); +#endif +#ifdef HAVE_OPENSSL + printf(" -ssl run as ssl server, .pem file needed\n"); + printf(" -file xx .pem filename, default is \"server.pem\"\n"); +#endif + printf(" -pw xx private key password\n"); + printf(" -log enable sockethandler debug log\n"); + printf(" -queue nn listen queue size (default 20)\n"); + printf(" -http enable http server with dummy response\n"); + printf(" -nobuf echo raw data\n"); + exit(-1); + } + } + if (g_b_http && g_b_nobuf) + { + printf("Nobuf and Http does not work together\n"); + exit(-1); + } + StdoutLog *log = enableLog ? new StdoutLog() : NULL; + SocketHandler h(log); + ListenSocket l(h); + ListenSocket l2(h); + if (!g_b_http) + { + if (l.Bind(port, queue)) + { + fprintf(stderr, "Bind to port %d failed\n", port); + return -1; + } + fprintf(stderr, "Listening on port %d\n", port); + h.Add(&l); + } + else + { + printf("Enable HttpDebugSocket\n"); + if (l2.Bind(port, queue)) + { + fprintf(stderr, "Bind to port %d failed\n", port); + return -1; + } + fprintf(stderr, "Listening on port %d\n", port); + h.Add(&l2); + } +#ifdef ENABLE_DETACH + if (g_b_detach) + fprintf(stderr, "Will detach each incoming socket\n"); +#endif +#ifdef HAVE_OPENSSL + if (g_b_ssl) + fprintf(stderr, "Using SSL\n"); +#endif + while (!quit) + { + h.Select(1, 0); + } + fprintf(stderr, "\nExiting...\n"); + if (log) + { +// delete log; + } + return 0; +} + + diff --git a/Sockets/tests/events.cpp b/Sockets/tests/events.cpp new file mode 100644 index 0000000..5d08973 --- /dev/null +++ b/Sockets/tests/events.cpp @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include + + +double start; + +double gettime() +{ + struct timeval tv; + Utility::GetTime(&tv); + return tv.tv_sec + (double)tv.tv_usec / 1000000; +} + + +class MyEvHandler : public EventHandler +{ +public: + MyEvHandler() : EventHandler() {} + MyEvHandler(StdLog *p) : EventHandler(p) {} + +}; + + +class EvThread : public Thread +{ +public: + EvThread(Socket *p) : m_socket(p) {} + + void Run(); + +private: + Socket *m_socket; +}; + + +class EvSocket : public TcpSocket, public IEventOwner +{ +public: + EvSocket(ISocketHandler& h) : TcpSocket(h) + , IEventOwner( static_cast(h) ) { + } + + void OnEvent(int id) { + std::cout << "OnEvent" << std::endl; + std::cout << gettime() - start << std::endl; + SetCloseAndDelete(); + } + + void OnAccept() { + std::cout << "Incoming" << std::endl; + } + + void OnConnect() { + std::cout << "Connected" << std::endl; + EvThread *thr = new EvThread( this ); + thr -> SetDeleteOnExit(); + } + + void OnDelete() { + std::cout << "EvSocket::OnDelete()" << std::endl; + } +}; + + +void EvThread::Run() +{ + Socket *p0 = (Socket *)m_socket; + EvSocket *p = dynamic_cast(p0); + if (p) + { +#ifdef _WIN32 + Sleep( 5000 ); +#else + sleep(10); +#endif + std::cout << "Add event" << std::endl; + start = gettime(); + p -> AddEvent(1, 50000); + } + else + { + std::cout << "Thread: not an EvSocket" << std::endl; + } +} + + +//------------------------------------------------------------ +// myTimer class +//------------------------------------------------------------ +class myTimer : public IEventOwner +{ +public: + // Prototype of user call back function + typedef void (*myTimer_cb)(myTimer* user_func, void* user_id); + + myTimer(IEventHandler& h, long ssec, long susec, + long psec, long pusec, myTimer_cb func, void* id); + ~myTimer(); + +private: + void OnEvent(int id); + long ssec_; + long susec_; + long psec_; + long pusec_; + myTimer_cb user_func_; + void* user_id_; + bool periodic_; +}; + + +//------------------------------------------------------------ +myTimer::myTimer(IEventHandler& h, long ssec, long susec, + long psec, long pusec, myTimer_cb user_func, + void* user_id) : + IEventOwner(h), + ssec_(ssec), + susec_(susec), + psec_(psec), + pusec_(pusec), + user_func_(user_func), + user_id_(user_id), + periodic_(psec != 0 || pusec != 0) +{ + AddEvent(ssec_, susec_); +} + + +//------------------------------------------------------------ +myTimer::~myTimer() +{ + ClearEvents(); +} + + +//------------------------------------------------------------ +void myTimer::OnEvent(int id) +{ + if (periodic_) + { + int n = AddEvent(psec_, pusec_); + } + + user_func_(this, user_id_); + + if (!periodic_) + { + delete this; + } +} + + +//------------------------------------------------------------ +static void myfunc(myTimer* t, void* user_id) +{ + printf("Event %s\n", (char*)user_id); +} + + +/* +int main(int argc, char *argv[]) +{ + EventHandler h; + + // Example 1: Create a 2 sec one-shot timer. + // No need to save the pointer as it will delete + // itself upon expiry. + new myTimer(h, 2, 0, 0, 0, myfunc, (void*)"My Timer 0"); + + // Example 2: Create a 1 sec periodic timer. + // Save the pointer because we can stop the timer + // later with a "delete mytimer". + myTimer* mytimer = new myTimer(h, 0, 0, 1, 0, myfunc, + (void*)"My Timer 1"); + + // Drop into the event handler .. + h.EventLoop(); +} +*/ + + +int main(int argc, char *argv[]) +{ + StdoutLog log; + MyEvHandler h(&log); + ListenSocket l(h); + l.Bind("localhost", 12344); + h.Add(&l); + + EvSocket sock(h); + sock.Open("localhost", 12344); + h.Add(&sock); + + // Example 1: Create a 2 sec one-shot timer. + // No need to save the pointer as it will delete + // itself upon expiry. + new myTimer(h, 2, 0, 0, 0, myfunc, (void*)"My Timer 0"); + + // Example 2: Create a 1 sec periodic timer. + // Save the pointer because we can stop the timer + // later with a "delete mytimer". + myTimer* mytimer = new myTimer(h, 0, 0, 1, 0, myfunc, + (void*)"My Timer 1"); + + h.EventLoop(); +} + + diff --git a/Sockets/tests/http.cpp b/Sockets/tests/http.cpp new file mode 100644 index 0000000..6800699 --- /dev/null +++ b/Sockets/tests/http.cpp @@ -0,0 +1,65 @@ +#include +#include + + +class tSocket : public HTTPSocket +{ +public: + tSocket(ISocketHandler& h, const std::string& host) : HTTPSocket(h), m_host(host) + , m_first(false) + , m_sz(0) { + Open(host, 80); + } + + void OnConnect() { + Send("GET /index.html HTTP/1.0\r\n"); + Send("Host: " + m_host + "\r\n"); + Send("\r\n"); + } + + void OnFirst() { + } + + void OnHeader(const std::string& key, const std::string& value) { + fprintf(stderr, "%s: %s\n", key.c_str(), value.c_str()); + } + + void OnHeaderComplete() { + fprintf(stderr, "\n"); + } + + void OnData(const char *buf, size_t sz) { + if (1||!m_first) + { + std::string str = buf; + str.resize( sz ); + printf("%s", str.c_str()); + m_first = true; + } + m_sz += sz; + } + + void OnDelete() { + fprintf(stderr, "Content length: %d\n", m_sz); + } + +private: + std::string m_host; + bool m_first; + size_t m_sz; +}; + + +int main(int argc, char *argv[]) +{ + std::string host = argc > 1 ? argv[1] : "www.alhem.net"; + SocketHandler h; + tSocket sock(h, host); + h.Add(&sock); + while (h.GetCount()) + { + h.Select(1, 0); + } +} + + diff --git a/Sockets/tests/http_post.cpp b/Sockets/tests/http_post.cpp new file mode 100644 index 0000000..3b0b830 --- /dev/null +++ b/Sockets/tests/http_post.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include + + +class sSocket : public HttpdSocket +{ +public: + sSocket(ISocketHandler& h) : HttpdSocket(h) { + } + + void Init() + { + if (GetParent() -> GetPort() == 443 || GetParent() -> GetPort() == 8443) + { +#ifdef HAVE_OPENSSL + EnableSSL(); +#else + fprintf(stderr, "SSL not available\n"); +#endif + } + } + + void Exec() + { + std::string name; + std::string value; + GetForm() -> getfirst(name, value); + while (name.size()) + { +fprintf(stderr, "%s: '%s'\n", name.c_str(), value.c_str()); + GetForm() -> getnext(name, value); + } + CreateHeader(); + GenerateDocument(); + } + + void CreateHeader() + { + SetStatus("200"); + SetStatusText("OK"); +fprintf(stderr, "Uri: '%s'\n", GetUri().c_str()); + { + size_t x = 0; + for (size_t i = 0; i < GetUri().size(); i++) + if (GetUri()[i] == '.') + x = i; + std::string ext = GetUri().substr(x + 1); + if (ext == "gif" || ext == "jpg" || ext == "png") + AddResponseHeader("Content-type", "image/" + ext); + else + AddResponseHeader("Content-type", "text/" + ext); + } + AddResponseHeader("Connection", "close"); + SendResponse(); + } + + void GenerateDocument() + { + Send(""); + SetCloseAndDelete(); + } + +#ifdef HAVE_OPENSSL + void InitSSLServer() + { + InitializeContext("httpd", "comb.pem", "", SSLv23_method()); + } +#endif + +}; + + +int main(int argc, char *argv[]) +{ + std::string host = argc > 1 ? argv[1] : "www.alhem.net"; + StdoutLog log; + SocketHandler h(&log); + ListenSocket l(h); + if (l.Bind(1028)) + { + printf("Bind port 1028 failed\n"); + return -1; + } + h.Add(&l); + ListenSocket l2(h); + if (l2.Bind(8443)) + { + printf("Bind port 8443 failed\n"); + return -1; + } + h.Add(&l2); + HttpPostSocket sock(h, "http://localhost:1028/postdata"); + sock.AddField("name", "value"); + sock.Open(); + h.Add(&sock); + while (h.GetCount()) + { + h.Select(1, 0); + } +} + + diff --git a/Sockets/tests/httpd.cpp b/Sockets/tests/httpd.cpp new file mode 100644 index 0000000..e1106a0 --- /dev/null +++ b/Sockets/tests/httpd.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + + +class sSocket : public HttpdSocket +{ +public: + sSocket(ISocketHandler& h) : HttpdSocket(h) { + } + + void Init() + { + if (GetParent() -> GetPort() == 443 || GetParent() -> GetPort() == 8443) + { +#ifdef HAVE_OPENSSL + EnableSSL(); +#else + fprintf(stderr, "SSL not available\n"); +#endif + } + } + + void Exec() + { + CreateHeader(); + GenerateDocument(); + } + + void CreateHeader() + { + SetStatus("200"); + SetStatusText("OK"); +fprintf(stderr, "Uri: '%s'\n", GetUri().c_str()); + { + size_t x = 0; + for (size_t i = 0; i < GetUri().size(); i++) + if (GetUri()[i] == '.') + x = i; + std::string ext = GetUri().substr(x + 1); + if (ext == "gif" || ext == "jpg" || ext == "png") + AddResponseHeader("Content-type", "image/" + ext); + else + AddResponseHeader("Content-type", "text/" + ext); + } + AddResponseHeader("Connection", "close"); + SendResponse(); + } + + void GenerateDocument() + { + std::string fn = GetUri().substr(1); + FILE *fil = fopen(fn.c_str(), "rb"); + if (fil) + { + char slask[1000]; + int n = fread(slask,1,1000,fil); + while (n > 0) + { + SendBuf(slask, n); + n = fread(slask,1,1000,fil); + } + fclose(fil); + } + else + { + SetStatus("404"); + SetStatusText("Not Found"); + } + SetCloseAndDelete(); + } + +#ifdef HAVE_OPENSSL + void InitSSLServer() + { + InitializeContext("httpd", "comb.pem", "", SSLv23_method()); + } +#endif + +}; + + +int main(int argc, char *argv[]) +{ + std::string host = argc > 1 ? argv[1] : "www.alhem.net"; + StdoutLog log; + SocketHandler h(&log); + ListenSocket l(h); + if (l.Bind(1028)) + { + printf("Bind port 1028 failed\n"); + return -1; + } + h.Add(&l); + ListenSocket l2(h); + if (l2.Bind(8443)) + { + printf("Bind port 8443 failed\n"); + return -1; + } + h.Add(&l2); + while (h.GetCount()) + { + h.Select(1, 0); + } +} + + diff --git a/Sockets/tests/https.cpp b/Sockets/tests/https.cpp new file mode 100644 index 0000000..45b36ff --- /dev/null +++ b/Sockets/tests/https.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include + + +class tSocket : public HTTPSocket +{ +public: + tSocket(ISocketHandler& h) : HTTPSocket(h) + , m_first(false) + , m_sz(0) { + EnableSSL(); + } + + tSocket(ISocketHandler& h, const std::string& host) : HTTPSocket(h), m_host(host) + , m_first(false) + , m_sz(0) { + EnableSSL(); + Open(host, 4443); + } + + void InitSSLServer() { + InitializeContext("", "comb.pem", "", SSLv23_method()); + } + + void OnSSLAccept() { + printf("OnSSLAccept()\n"); + HTTPSocket::OnSSLAccept(); + } + + void OnSSLConnect() { + printf("OnSSLConnect()\n"); + HTTPSocket::OnSSLConnect(); + } + + void OnAccept() { +printf("OnAccept\n"); + } + + void OnConnect() { +printf("OnConnect\n"); + Send("GET /index.html HTTP/1.0\r\n"); + Send("Host: " + m_host + "\r\n"); + Send("\r\n"); + } + + void OnFirst() { + } + + void OnHeader(const std::string& key, const std::string& value) { + fprintf(stderr, "%s: %s\n", key.c_str(), value.c_str()); + } + + void OnHeaderComplete() { + fprintf(stderr, "\n"); + } + + void OnData(const char *buf, size_t sz) { + if (1||!m_first) + { + std::string str = buf; + str.resize( sz ); + printf("%s", str.c_str()); + m_first = true; + } + m_sz += sz; + } + + void OnDelete() { + fprintf(stderr, "Content length: %d\n", m_sz); + } + +private: + std::string m_host; + bool m_first; + size_t m_sz; +}; + + +int main(int argc, char *argv[]) +{ + try + { + SocketHandler h; + if (argc > 1 && !strcmp(argv[1], "-server")) + { + ListenSocket l(h); + l.Bind(4443); + h.Add(&l); + while (h.GetCount()) + { + h.Select(1, 0); + } + } + else + { + std::string host = argc > 1 ? argv[1] : "www.alhem.net"; + tSocket sock(h, host); + h.Add(&sock); + while (h.GetCount()) + { + h.Select(1, 0); + } + } + } + catch (const Exception& e) + { + printf("%s\n", e.ToString().c_str()); + } +} + + diff --git a/Sockets/tests/resolve.cpp b/Sockets/tests/resolve.cpp new file mode 100644 index 0000000..802b910 --- /dev/null +++ b/Sockets/tests/resolve.cpp @@ -0,0 +1,156 @@ +#include +#ifndef _WIN32 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +int main(int argc, char *argv[]) +{ + StdoutLog log; + SocketHandler h(&log); +/* + h.EnableResolver(9999); + +// printf("Waiting for resolver ..."); + while (!h.ResolverReady()) + ; +// printf(" resolver ready!\n"); +*/ + std::string hostname = argc < 2 ? "www.ipv6.org" : argv[1]; + + { + printf("Using hostname : %s\n", hostname.c_str()); + printf("------------------------------------------- normal (old) Utility::u2ip\n"); + ipaddr_t oa; + if (!Utility::u2ip(hostname, oa)) + printf("Ipv4 lookup failed\n"); +#ifdef ENABLE_IPV6 + in6_addr oa6; + if (!Utility::u2ip(hostname, oa6)) + printf("Ipv6 lookup failed\n"); +#endif + std::string oname; + Utility::l2ip(oa, oname); + printf("Ipv4 : %s (old)\n", oname.c_str()); + +#ifdef ENABLE_IPV6 + std::string oname6; + Utility::l2ip(oa6, oname6); + printf("Ipv6 : %s (old)\n", oname6.c_str()); +#endif + + printf("------------------------------------------- new Utility::u2ip, Utility::reverse\n"); + struct sockaddr_in sa; + if (!Utility::u2ip(hostname, sa)) + printf("Ipv4 lookup failed\n"); + + ipaddr_t a; + memcpy(&a, &sa.sin_addr, sizeof(a)); + std::string l2ipname; + Utility::l2ip(a, l2ipname); + printf("Ipv4 : %s\n", l2ipname.c_str()); + + std::string numeric; + Utility::reverse((struct sockaddr *)&sa, sizeof(sa), numeric, NI_NUMERICHOST); + printf("Ipv4 numeric : %s\n", numeric.c_str()); + + std::string rname; + if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), rname)) + printf("Reverse Ipv4 failed\n"); + else + printf("Ipv4 Utility::reverse : %s\n", rname.c_str()); + +#ifdef ENABLE_IPV6 + printf("------------------------------------------- new Utility::u2ip, Utility::reverse (Ipv6)\n"); + struct sockaddr_in6 sa6; + if (!Utility::u2ip(hostname, sa6)) + printf("Ipv6 lookup failed\n"); + + std::string l2ipname6; + Utility::l2ip(sa6.sin6_addr, l2ipname6); + printf("Ipv6 : %s\n", l2ipname6.c_str()); + + std::string numeric6; + Utility::reverse((struct sockaddr *)&sa6, sizeof(sa6), numeric6, NI_NUMERICHOST); + printf("Ipv6 numeric : %s\n", numeric6.c_str()); + + std::string rname6; + if (!Utility::reverse( (struct sockaddr *)&sa6, sizeof(sa6), rname6)) + printf("Reverse Ipv6 failed\n"); + else + printf("Ipv6 Utility::reverse : %s\n", rname6.c_str()); +#endif + + printf("-------------------------------------------\n"); + in_addr ia; + /** Resolve hostname. */ +//static bool Resolve(const std::string& hostname,struct in_addr& a); + /** Reverse resolve (IP to hostname). */ +//static bool Reverse(struct in_addr& a,std::string& name); + /** Convert address struct to text. */ +//static std::string Convert(struct in_addr& a); + std::string name; + if (!Ipv4Address::Resolve(hostname, ia)) + printf("Ipv4 lookup failed (Ipv4Address)\n"); + memcpy(&a, &ia, sizeof(a)); + Utility::l2ip(a, name); + printf("Ipv4 : %s (Ipv4Address)\n", name.c_str()); + assert(name == l2ipname); + if (!Ipv4Address::Reverse(ia, name)) + printf("Reverse Ipv4 lookup failed (Ipv4Address)\n"); + else + printf("Reverse Ipv4 : %s\n", name.c_str()); + assert(name == rname); + assert(Ipv4Address::Convert(ia) == l2ipname); + +#ifdef ENABLE_IPV6 + printf("-------------------------------------------\n"); + /** Resolve hostname. */ +//static bool Resolve(const std::string& hostname,struct in6_addr& a); + /** Reverse resolve (IP to hostname). */ +//static bool Reverse(struct in6_addr& a,std::string& name); + /** Convert address struct to text. */ +//static std::string Convert(struct in6_addr& a,bool mixed = false); + if (!Ipv6Address::Resolve(hostname, oa6)) + printf("Ipv6 lookup failed (Ipv4Address)\n"); + Utility::l2ip(oa6, name); + assert(name == l2ipname6); + printf("Ipv6 : %s (Ipv6Address)\n", name.c_str()); + if (!Ipv6Address::Reverse(oa6, name)) + printf("Reverse Ipv6 lookup failed (Ipv4Address)\n"); + else + printf("Reverse Ipv6 : %s\n", name.c_str()); + assert(name == rname6); + std::string mixed_false = Ipv6Address::Convert(oa6, false); + std::string mixed_true = Ipv6Address::Convert(oa6, true); + printf("Ipv6Address::Convert(false) : %s\n", mixed_false.c_str()); + printf("Ipv6Address::Convert(true ) : %s\n", mixed_true.c_str()); + assert(mixed_false == l2ipname6); +#endif +/* + printf("-------------------------------------------\n"); + int protocol; + Utility::u2service("tcp", protocol); + printf("tcp: %d\n", protocol); + Utility::u2service("udp", protocol); + printf("udp: %d\n", protocol); + Utility::u2service("echo", protocol); + printf("echo: %d\n", protocol); +*/ + } + printf("\n"); + printf("OK\n"); + +// sleep(100); +} + + diff --git a/Sockets/tests/resume.cpp b/Sockets/tests/resume.cpp new file mode 100644 index 0000000..e518109 --- /dev/null +++ b/Sockets/tests/resume.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include + + +class eHandler : public EventHandler +{ +public: + eHandler() : EventHandler() {} +}; + + +class eSocket : public TcpSocket, public IEventOwner +{ +public: + eSocket(ISocketHandler& h) : TcpSocket(h), IEventOwner(static_cast(h)), m_listen_socket(NULL), m_server(false) { + SetLineProtocol(); + } + + bool OnConnectRetry() { + printf("Retrying connect\n"); + if (GetConnectionRetries() == 5) + { + printf("Creating ListenSocket\n"); + m_listen_socket = new ListenSocket(Handler()); + if (m_listen_socket -> Bind(12345)) + { + printf("Bind port 12345 failed\n"); + } + m_listen_socket -> SetDeleteByHandler(); + Handler().Add( m_listen_socket ); + } + return true; + } + + void OnAccept() { + m_id_stop_socket = AddEvent(5, 0); +printf("Stop socket id: %d\n", m_id_stop_socket); + m_server = true; + } + + void OnConnect() { + m_id_stop_listen = AddEvent(10, 0); +printf("Stop listen id: %d\n", m_id_stop_listen); + } + + void OnEvent(int id) { +printf("Event id: %d\n", id); + if (id == m_id_stop_socket && m_server) + SetCloseAndDelete(); + if (id == m_id_stop_listen && !m_server) + m_listen_socket -> SetCloseAndDelete(); + } + + void OnLine(const std::string& line) { + printf("Incoming data: %s\n", line.c_str()); + } + + void OnDelete() { + printf("eSocket::OnDelete(), server: %s\n", m_server ? "true" : "false"); + } + + void OnDisconnect() { + printf("Disconnect, server: %s\n", m_server ? "true" : "false"); + } + +private: + ListenSocket *m_listen_socket; + int m_id_stop_socket; + int m_id_stop_listen; + bool m_server; +}; + + +class Sender : public IEventOwner +{ +public: + Sender(IEventHandler& h, TcpSocket& ref) : IEventOwner(h), m_socket(ref), m_count(1) { + AddEvent(1, 0); + } + + void OnEvent(int id) { + if (static_cast(EventHandler()).Valid(&m_socket)) + m_socket.Send("Event#" + Utility::l2string(m_count++) + "\n"); + EventHandler().AddEvent(this, 1, 0); + } + +private: + TcpSocket& m_socket; + int m_count; +}; + + +int main(int argc, char *argv[]) +{ + eHandler h; + eSocket sock(h); + sock.SetConnectTimeout(3); + sock.SetConnectionRetry(-1); +#ifdef ENABLE_RECONNECT + sock.SetReconnect(); +#endif + sock.Open("localhost", 12345); + h.Add( &sock ); + Sender send(h, sock); + h.AddEvent( &send, 1, 0 ); + h.EventLoop(); +} + diff --git a/Sockets/tests/retry.cpp b/Sockets/tests/retry.cpp new file mode 100644 index 0000000..36954a6 --- /dev/null +++ b/Sockets/tests/retry.cpp @@ -0,0 +1,67 @@ +#include +#include +#include + + + bool quit = false; + +/* + virtual bool OnConnectRetry(); +- void SetRetryClientConnect(bool x = true); +- bool RetryClientConnect(); + void SetConnectionRetry(int n); + int GetConnectionRetry(); + void IncreaseConnectionRetries(); + int GetConnectionRetries(); + void ResetConnectionRetries(); +*/ +class RetrySocket : public TcpSocket +{ +public: + RetrySocket(ISocketHandler& h) : TcpSocket(h) { + SetConnectTimeout(2); + SetConnectionRetry(-1); + } + + bool OnConnectRetry() { + printf("Connection attempt#%d\n", GetConnectionRetries()); + if (GetConnectionRetries() == 3) + { + ListenSocket *l = new ListenSocket(Handler()); + if (l -> Bind(12345)) + { + printf("Bind port 12345 failed\n"); + } + l -> SetDeleteByHandler(); + Handler().Add(l); + } + return true; + } + + void OnConnect() { + printf("Connected\n"); + printf("GetRemoteAddress(): %s\n", GetRemoteAddress().c_str()); + printf("Remote address: %s\n", GetRemoteSocketAddress() -> Convert(false).c_str()); + printf("Remote address: %s\n", GetRemoteSocketAddress() -> Convert(true).c_str()); + SetCloseAndDelete(); + } + + void OnDelete() { + quit = true; + } +}; + + +int main(int argc, char *argv[]) +{ + SocketHandler h; + RetrySocket sock(h); + sock.Open("localhost", 12345); + h.Add(&sock); + while (!quit) + { + h.Select(0, 200000); + } +} + + diff --git a/Sockets/tests/semtest.cpp b/Sockets/tests/semtest.cpp new file mode 100644 index 0000000..442d5d6 --- /dev/null +++ b/Sockets/tests/semtest.cpp @@ -0,0 +1,96 @@ +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include + + +class MyThread : public Thread +{ +public: + void Run() { + printf("Thread\n"); + } +}; + + +class SemLock +{ +public: + SemLock(Semaphore& sem) : m_sem(sem) { + m_sem.Wait(); + } + ~SemLock() { + m_sem.Post(); + } + +private: + Semaphore& m_sem; +}; + + +/** + * Return time difference between two struct timeval's, in seconds + * \param t0 start time + * \param t end time + */ +double Diff(struct timeval t0,struct timeval t) +{ + t.tv_sec -= t0.tv_sec; + t.tv_usec -= t0.tv_usec; + if (t.tv_usec < 0) + { + t.tv_usec += 1000000; + t.tv_sec -= 1; + } + return t.tv_sec + (double)t.tv_usec / 1000000; +} + + +static int val = 0; + +void lock(Mutex& m, int i) +{ + Lock l(m); + val += i; +} + + +void lock(Semaphore& s, int i) +{ + SemLock l(s); + val += i; +} +#endif // WIN32 + + +int main() +{ +#ifndef _WIN32 + Mutex mutex; + Semaphore sema(1); + struct timeval start; + struct timeval tt; + double d; + + Utility::GetTime(&start); + for (int i = 0; i < 100000; i++) + lock(mutex, i); + Utility::GetTime(&tt); + d = Diff(start, tt); + printf("%.4f sec\n", d); + + Utility::GetTime(&start); + for (int i = 0; i < 100000; i++) + lock(sema, i); + Utility::GetTime(&tt); + d = Diff(start, tt); + printf("%.4f sec\n", d); +#endif +} + + diff --git a/Sockets/tests/sloppy_http.cpp b/Sockets/tests/sloppy_http.cpp new file mode 100644 index 0000000..9ea79c1 --- /dev/null +++ b/Sockets/tests/sloppy_http.cpp @@ -0,0 +1,55 @@ +#include +#include + + +class GetHttp : public TcpSocket +{ +public: + GetHttp(ISocketHandler& h, const char *request) : TcpSocket(h) + , m_request(request) {} + + void OnConnect() { + Send( m_request ); + } + + void OnRawData( const char *buf, size_t len ) { + if (len > 0) { + std::string tmp; + tmp.resize( len ); + memcpy( &tmp[0], buf, len ); + m_response += tmp; + } + } + + const std::string& Response() { + return m_response; + } + +private: + std::string m_request; + std::string m_response; +}; + + +std::string get_http(const char *host, int port, const char *request) +{ + SocketHandler h; + GetHttp sock(h, request); + sock.Open( host, port ); + h.Add(&sock); + while (h.GetCount()) { + h.Select(1, 0); + } + return sock.Response(); +} + + +int main(int argc, char *argv[]) +{ + std::string zz = get_http("www.alhem.net", 80, "GET /index.html HTTP/1.0\r\n" + "Host: www.alhem.net\r\n" + "\r\n"); + printf("%s\n%d\n", zz.c_str(), zz.size()); +} + + diff --git a/Sockets/tests/sockets_test.cpp b/Sockets/tests/sockets_test.cpp new file mode 100644 index 0000000..78fd2bc --- /dev/null +++ b/Sockets/tests/sockets_test.cpp @@ -0,0 +1,347 @@ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SOCKETS_NAMESPACE +using namespace SOCKETS_NAMESPACE; +#endif + + +class MyHandler : public SocketHandler +{ +public: + MyHandler(StdLog *p) : SocketHandler(p),m_done(false),m_quit(false) {} + ~MyHandler() {} + + void List(TcpSocket *p) { + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + Socket *p0 = (*it).second; +#ifdef ENABLE_POOL + if (dynamic_cast(p0)) + { + p -> Send("PoolSocket\n"); + } + else +#endif + if (dynamic_cast(p0)) + { + p -> Send("HttpGetSocket\n"); + } + else + if (dynamic_cast(p0)) + { + p -> Send("TcpSocket\n"); + } + else + { + p -> Send("Some kind of Socket\n"); + } + bool r; + bool w; + bool e; + Get(p -> GetSocket(), r, w, e); + char slask[1000]; + sprintf(slask," Read: %s Write: %s Exception: %s\n", + r ? "SET" : "not set", + w ? "SET" : "not set", + e ? "SET" : "not set"); + p -> Send( slask ); + } + } + void SetQuit() { m_quit = true; } + bool Quit() { return m_quit; } + void CheckHtml() { + if (m_done) + { + if (m_ok) + printf("Html OK:\n%s\n", m_html.c_str()); + else + printf("Html Failed\n"); + m_done = false; + } + } + + std::string m_html; + bool m_ok; + bool m_done; + +private: + bool m_quit; +}; + + +class MySocket : public TcpSocket +{ +public: + MySocket(ISocketHandler& h) : TcpSocket(h) { + } + void OnAccept() { + int port = GetParent() -> GetPort(); + Send("I'm the server at port " + + Utility::l2string(port) + "\n"); + SetCloseAndDelete(); + } +}; + + +class hSocket : public HttpGetSocket +{ +public: + hSocket(ISocketHandler& h,const std::string& x,const std::string& y) : HttpGetSocket(h,x,y) {} + + void OnConnect() { + printf("hSocket::OnConnect\n"); + HttpGetSocket::OnConnect(); + } +}; + + +class OrderSocket : public TcpSocket +{ +public: + OrderSocket(ISocketHandler& h) : TcpSocket(h) { + SetLineProtocol(); + } + Socket *Create() { + Handler().LogError(this, "Create", 0, "OrderSocket", LOG_LEVEL_INFO); + return new OrderSocket(Handler()); + } + void OnAccept() { + Send("Cmd (get,quit,list,stop,detach,count,resolve )>"); + } + void OnLine(const std::string& line) { + Parse pa(line); + std::string cmd = pa.getword(); + std::string arg = pa.getrest(); + if (cmd == "get") + { + HttpGetSocket *p = new hSocket(Handler(), arg, "tmpfile.html"); + p -> SetHttpVersion("HTTP/1.1"); + p -> AddResponseHeader("Connection", "keep-alive"); + p -> SetDeleteByHandler(); + Handler().Add( p ); + Send("Reading url '" + arg + "'\n"); + } + else + if (cmd == "quit") + { + Send("Goodbye!\n"); + SetCloseAndDelete(); + } + else + if (cmd == "list") + { + static_cast(Handler()).List( this ); + } + else + if (cmd == "stop") + { + static_cast(Handler()).SetQuit(); + } + else + if (cmd == "resolve") + { + //Resolve( arg ); + ipaddr_t a; + if (Utility::u2ip(arg, a)) + { + std::string tmp; + Utility::l2ip(a, tmp); + Send("Resolved: " + tmp + "\n"); + } + else + { + Send("Resolve failed: " + arg + "\n"); + } + } + else +/* + if (cmd == "reverse") + { + ipaddr_t a; + Utility::u2ip(arg, a); // ip -> ipaddr_t + int id = Socket::Resolve(a, 0); + Send("Resolve id = " + Utility::l2string(id) + "\n"); + } + else +*/ +#ifdef ENABLE_DETACH + if (cmd == "detach") + { + if (!Detach()) + { + Send("Detach() call failed\n"); + } + else + { + Send("Ok.\n"); + } + } + else +#endif + if (cmd == "count") + { + Send("Socket count: " + Utility::l2string( (long)Handler().GetCount()) + "\n"); + } + else + { + Send("Huh?\n"); + } + Send("Cmd>"); + } + void OnDelete() { + printf("OrderSocket::OnDelete()\n"); + } +#ifdef ENABLE_RESOLVER + void OnResolved(int id,ipaddr_t a,port_t port) + { + } + void OnResolved(int id,const std::string& name,port_t port) + { + Send("Resolve id " + Utility::l2string(id) + " = " + name + "\n"); + } +/* + void OnResolved(const char *p,size_t l) { + printf("OnResolved, %d bytes:\n", l); + for (size_t i = 0; i < l; i++) + { + unsigned char c = p[i]; + if (isprint(c)) + printf("%c",c); + else + printf("<%02X>",c); + } + printf("\n"); + } +*/ +#endif +#ifdef ENABLE_DETACH + void OnDetached() { + Send("\nDetached.\nCmd>"); + } +#endif +}; + + +class TestSocket : public TcpSocket +{ +public: + TestSocket(ISocketHandler& h) : TcpSocket(h) { + SetLineProtocol(); + } + void OnConnect() { + printf("TestSocket connected, sending QUIT\n"); + Send( "quit\n" ); + } + void OnConnectFailed() { + printf("TestSocket::OnConnectFailed\n"); + SetCloseAndDelete(); + } + void OnLine(const std::string& line) { + printf("TestSocket: %s\n", line.c_str()); + } + void OnDelete() { + printf("TestSocket::OnDelete()\n"); + } +#ifdef ENABLE_RESOLVER + void OnResolved(int id,ipaddr_t a,port_t port) { + printf("TestSocket::OnResolved(): %d, %08x:%d\n", id, a, port); + TcpSocket::OnResolved(id,a,port); + } +#endif +}; + + +int main() +{ + StdoutLog log; + MyHandler h(&log); + +#ifdef ENABLE_RESOLVER + h.EnableResolver(9999); +#endif +// Utility::ResolveLocal(); + printf(" *** My hostname: %s\n", Utility::GetLocalHostname().c_str()); + printf(" *** My local IP: %s\n", Utility::GetLocalAddress().c_str()); + + // socks4 options +/* + h.SetSocks4Host("127.0.0.1"); + h.SetSocks4Port(1080); + h.SetSocks4Userid("www.alhem.net"); + h.SetSocks4TryDirect( true ); + printf("Socks4Host: %x\n", h.GetSocks4Host()); +*/ + + // first server + ListenSocket l1(h); + if (l1.Bind(1024)) + { + printf("Bind 1024 failed\n"); + exit(-1); + } + h.Add(&l1); + + // second server + ListenSocket l2(h); + if (l2.Bind(1025)) + { + printf("Bind 1025 failed\n"); + exit(-1); + } + h.Add(&l2); + + // line server + ListenSocket l3(h); + if (l3.Bind(1027)) + { + printf("Bind 1027 failed\n"); + exit(-1); + } + h.Add(&l3); + + // http debug + ListenSocket l4(h); + if (l4.Bind(8080)) + { + printf("Bind 8080 failed\n"); + exit(-1); + } + h.Add(&l4); + + // wait for resolver to really start +#ifdef ENABLE_RESOLVER + printf("Waiting for resolver ..."); + while (!h.ResolverReady()) + ; + printf(" resolver ready!\n"); +#endif + + TestSocket ts(h); +printf(">>> TestSocket.Open\n"); + ts.Open("localhost", 1027); +printf(">>> Adding TestSocket\n"); + h.Add(&ts); + +printf(">>> mainloop\n"); + h.Select(0,0); + while (!h.Quit()) + { + h.Select(1,0); + h.CheckSanity(); + } + + return 0; +} + + diff --git a/Sockets/tests/stressclient.cpp b/Sockets/tests/stressclient.cpp new file mode 100644 index 0000000..4187868 --- /dev/null +++ b/Sockets/tests/stressclient.cpp @@ -0,0 +1,516 @@ +/** + ** \file stressclient.cpp + ** \date 2006-10-02 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2006 Anders Hedstrom + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#else +typedef __int64 int64_t; +#endif +#include + + +static double g_min_time = 10000; +static double g_max_time = 0; +static double g_tot_time = 0; +static int g_ant = 0; +static double g_min_time2 = 10000; +static double g_max_time2 = 0; +static double g_tot_time2 = 0; +static int g_ant2 = 0; +static bool gQuit = false; +static size_t g_max_connections = 0; +static std::string gHost = "localhost"; +static port_t gPort = 2222; +static bool g_b_flood = false; +static bool g_b_off = false; +static bool g_b_limit = false; +static int64_t gBytesIn = 0; +static int64_t gBytesOut = 0; +static bool g_b_repeat = false; +static std::string g_data; +static size_t g_data_size = 1024; +static bool g_b_stop = false; +#ifdef HAVE_OPENSSL +static bool g_b_ssl = false; +#endif +static bool g_b_instant = false; +static struct timeval g_t_start; + + +/** + * Return time difference between two struct timeval's, in seconds + * \param t0 start time + * \param t end time + */ +double Diff(struct timeval t0,struct timeval t) +{ + t.tv_sec -= t0.tv_sec; + t.tv_usec -= t0.tv_usec; + if (t.tv_usec < 0) + { + t.tv_usec += 1000000; + t.tv_sec -= 1; + } + return t.tv_sec + (double)t.tv_usec / 1000000; +} + + +void gettime(struct timeval *p, struct timezone *) +{ +#ifdef _WIN32 + FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). + GetSystemTimeAsFileTime(&ft); + uint64_t tt; + memcpy(&tt, &ft, sizeof(tt)); + tt /= 10; + p->tv_sec = tt / 1000000; + p->tv_usec = tt % 1000000; +#else + gettimeofday(p, NULL); +#endif +} + + +void printreport() +{ + struct timeval tv; + gettime(&tv, NULL); + double rt = Diff(g_t_start, tv); + g_t_start = tv; + // + printf("\n"); + if (g_ant) + printf("connect; ant: %5d min: %.4f max: %.4f med: %.4f\n", g_ant, g_min_time, g_max_time, g_tot_time / g_ant); + if (g_ant2) + printf("reply; ant: %5d min: %.4f max: %.4f med: %.4f\n", g_ant2, g_min_time2, g_max_time2, g_tot_time2 / g_ant2); + double mbi = (double)gBytesIn / 1024; + mbi /= 1024; + mbi /= rt; + double mbo = (double)gBytesOut / 1024; + mbo /= 1024; + mbo /= rt; + printf("bytes in: %lld", gBytesIn); + printf(" (%.2f MB/sec)", mbi); + printf(" bytes out: %lld", gBytesOut); + printf(" (%.2f MB/sec)", mbo); + printf(" time: %.2f sec\n", rt); +} + + +void printreport_reset() +{ + printreport(); + // + g_min_time = 10000; + g_max_time = 0; + g_tot_time = 0; + g_ant = 0; + g_min_time2 = 10000; + g_max_time2 = 0; + g_tot_time2 = 0; + g_ant2 = 0; + gBytesIn = gBytesOut = 0; +} + + +class MySocket : public TcpSocket +{ +public: + MySocket(ISocketHandler& h,bool one) : TcpSocket(h), m_b_client(false), m_b_one(one), m_b_created(false), m_b_active(false) { + gettime(&m_create, NULL); + SetLineProtocol(); +#ifdef HAVE_OPENSSL + if (g_b_ssl) + EnableSSL(); +#endif + if (g_max_connections && !m_b_one && Handler().GetCount() >= g_max_connections) + { + fprintf(stderr, "\nConnection limit reached: %d, continuing in single connection stress mode\n", (int)g_max_connections); + if (g_b_off) + printreport_reset(); + g_b_limit = true; + m_b_one = true; + // + g_b_flood = g_b_repeat; + } + if (!m_b_one && Handler().GetCount() >= FD_SETSIZE - 17) + { + fprintf(stderr, "\nFD_SETSIZE connection limit reached: %d, continuing in single connection stress mode\n", (int)Handler().GetCount()); + if (g_b_off) + printreport_reset(); + g_b_limit = true; + m_b_one = true; + // + g_b_flood = g_b_repeat; + } + } + ~MySocket() { + } + + void OnConnect() { + gettime(&m_connect, NULL); + m_b_active = true; + { + double tconnect = Diff(m_create, m_connect); + // + g_min_time = tconnect < g_min_time ? tconnect : g_min_time; + g_max_time = tconnect > g_max_time ? tconnect : g_max_time; + g_tot_time += tconnect; + g_ant += 1; + } + SendBlock(); + m_b_client = true; + } + + void SendBlock() { + gettime(&m_send, NULL); + Send(g_data + "\n"); + } + + void OnLine(const std::string& line) { + gettime(&m_reply, NULL); + m_b_active = true; + { + double treply = Diff(m_send, m_reply); + // + g_min_time2 = treply < g_min_time2 ? treply : g_min_time2; + g_max_time2 = treply > g_max_time2 ? treply : g_max_time2; + g_tot_time2 += treply; + g_ant2 += 1; + } + // + if (line != g_data) + { + fprintf(stderr, "\n%s\n%s\n", line.c_str(), g_data.c_str()); + fprintf(stderr, "(reply did not match data - exiting)\n"); + exit(-1); + } + // + gBytesIn += GetBytesReceived(true); + gBytesOut += GetBytesSent(true); + if (m_b_one) + { + SetCloseAndDelete(); + } + else + if (g_b_repeat && g_b_limit) + { + SendBlock(); + } + // add another + if (!m_b_created && (!g_b_limit || !g_b_off) && !g_b_instant) + { + MySocket *p = new MySocket(Handler(), m_b_one); + p -> SetDeleteByHandler(); + p -> Open(gHost, gPort); + Handler().Add(p); + m_b_created = true; + } + } + + bool IsActive() { + bool b = m_b_active; + m_b_active = false; + return b; + } + +private: + bool m_b_client; + bool m_b_one; + bool m_b_created; + bool m_b_active; + struct timeval m_create; + struct timeval m_connect; + struct timeval m_send; + struct timeval m_reply; +}; + + +class MyHttpSocket : public HttpGetSocket +{ +public: + MyHttpSocket(ISocketHandler& h,const std::string& url) : HttpGetSocket(h,url), m_url(url) { + gettime(&m_create, NULL); + AddResponseHeader("content-length", Utility::l2string(g_data_size)); + } + ~MyHttpSocket() {} + + void OnConnect() { + gettime(&m_connect, NULL); + { + double tconnect = Diff(m_create, m_connect); + // + g_min_time = tconnect < g_min_time ? tconnect : g_min_time; + g_max_time = tconnect > g_max_time ? tconnect : g_max_time; + g_tot_time += tconnect; + g_ant += 1; + } + gettime(&m_send, NULL); + + // send request header + HttpGetSocket::OnConnect(); + + // send body + Send(g_data); + } + + void OnContent() { + gettime(&m_reply, NULL); + { + double treply = Diff(m_send, m_reply); + // + g_min_time2 = treply < g_min_time2 ? treply : g_min_time2; + g_max_time2 = treply > g_max_time2 ? treply : g_max_time2; + g_tot_time2 += treply; + g_ant2 += 1; + } + CreateNew(); + } + void CreateNew() { + if (g_b_off) + return; + MyHttpSocket *p = new MyHttpSocket(Handler(), m_url); + p -> SetDeleteByHandler(); + Handler().Add(p); + SetCloseAndDelete(); + } + +private: + std::string m_url; + struct timeval m_create; + struct timeval m_connect; + struct timeval m_send; + struct timeval m_reply; +}; + + +#ifndef _WIN32 +void sigint(int) +{ + printreport(); + gQuit = true; +} + + +void sigusr1(int) +{ + g_b_flood = true; +} + + +void sigusr2(int) +{ + printreport_reset(); +} +#endif + + +class MyHandler : public SocketHandler +{ +public: + MyHandler() : SocketHandler() { + } + MyHandler(StdoutLog *p) : SocketHandler(p) { + } + ~MyHandler() { + } + void Flood() { + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + Socket *p0 = it -> second; + MySocket *p = dynamic_cast(p0); + if (p) + { + p -> SendBlock(); + } + } + } + void Report() { + int ant = 0; + int act = 0; + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + MySocket *p = dynamic_cast(it -> second); + if (p) + { + ant++; + if (p -> IsActive()) + { + act++; + } + } + } + printf("Number of //stress// sockets: %d Active: %d\n", ant, act); + } +}; + + +int main(int argc,char *argv[]) +{ + bool many = false; + bool one = false; + bool enableLog = false; + bool http = false; + std::string url; + time_t report_period = 10; + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-many")) + many = true; + if (!strcmp(argv[i], "-one")) + one = true; + if (*argv[i] == '-' && strlen(argv[i]) > 1 && isdigit(argv[i][1]) ) + g_max_connections = atoi(argv[i] + 1); + if (!strcmp(argv[i], "-host") && i < argc - 1) + gHost = argv[++i]; + if (!strcmp(argv[i], "-port") && i < argc - 1) + gPort = atoi(argv[++i]); + if (!strcmp(argv[i], "-off")) + g_b_off = true; + if (!strcmp(argv[i], "-repeat")) + g_b_repeat = true; + if (!strcmp(argv[i], "-size") && i < argc - 1) + g_data_size = atoi(argv[++i]); + if (!strcmp(argv[i], "-log")) + enableLog = true; + if (!strcmp(argv[i], "-time") && i < argc - 1) + report_period = atoi(argv[++i]); + if (!strcmp(argv[i], "-stop")) + g_b_stop = true; +#ifdef HAVE_OPENSSL + if (!strcmp(argv[i], "-ssl")) + g_b_ssl = true; +#endif + if (!strcmp(argv[i], "-instant")) + g_b_instant = true; + if (!strcmp(argv[i], "-http")) + http = true; + if (!strcmp(argv[i], "-url") && i < argc - 1) + url = argv[++i]; + } + if (argc < 2 || (!many && !one && !g_max_connections && !http) ) + { + printf("Usage: %s [mode] [options]\n", *argv); + printf(" Modes (only use one of these):\n"); + printf(" -many start max number of connections\n"); + printf(" -one open - close - repeat\n"); + printf(" -nn open nn connections, then start -one mode\n"); + printf(" -http send/receive http request/response\n"); + printf(" Options:\n"); + printf(" -host xx host to connect to\n"); + printf(" -port nn port number to connection to\n"); + printf(" -off turn off new connections when connection limit reached\n"); + printf(" -repeat send new block when reply is received\n"); + printf(" -size nn size of block to send, default is 1024 bytes\n"); + printf(" -log enable debug log\n"); + printf(" -time nn time between reports, default 10s\n"); + printf(" -stop stop after time elapsed\n"); + printf(" -instant create all sockets at once\n"); +#ifdef HAVE_OPENSSL + printf(" -ssl use ssl\n"); +#endif + printf(" -url xx url to use in http mode (default http://:/)\n"); + exit(-1); + } + fprintf(stderr, "Using data size: %d bytes\n", (int)g_data_size); + std::string chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + while (g_data.size() < g_data_size) + { + g_data += chars[rand() % chars.size()]; + } +#ifndef _WIN32 + signal(SIGINT, sigint); + signal(SIGUSR1, sigusr1); + signal(SIGUSR2, sigusr2); + signal(SIGPIPE, SIG_IGN); +#endif + StdoutLog *log = enableLog ? new StdoutLog() : NULL; + MyHandler h(log); + if (http) + { + if (!url.size()) + { + url = "http://" + gHost + ":" + Utility::l2string(gPort) + "/"; + } + MyHttpSocket *s = new MyHttpSocket(h, url); + s -> SetDeleteByHandler(); + h.Add(s); + } + else + if (g_b_instant) + { + for (size_t i = 0; i < g_max_connections; i++) + { + MySocket *s = new MySocket(h, one); + s -> SetDeleteByHandler(); + s -> Open(gHost, gPort); + h.Add(s); + } + g_b_limit = true; + } + else + { + MySocket *s = new MySocket(h, one); + s -> SetDeleteByHandler(); + s -> Open(gHost, gPort); + h.Add(s); + } + time_t t = time(NULL); + gettime(&g_t_start, NULL); + while (!gQuit) + { + h.Select(1, 0); + if (g_b_flood) + { + fprintf(stderr, "\nFlooding\n"); + h.Flood(); + g_b_flood = false; + } + if (time(NULL) - t >= report_period) // report + { + t = time(NULL); + printreport_reset(); + h.Report(); + if (g_b_stop) + { + gQuit = true; + } + } + } + fprintf(stderr, "\nExiting...\n"); + if (log) + { +// delete log; + } + return 0; +} + + diff --git a/Sockets/tests/x.cpp b/Sockets/tests/x.cpp new file mode 100644 index 0000000..2ca9b26 --- /dev/null +++ b/Sockets/tests/x.cpp @@ -0,0 +1,298 @@ +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#define HOST "localhost" +#define PORT 2222 + +#define TIME_US 250000 + +#define DEB(x) x + + +static bool quit = false; +static EventHandler *ph = NULL; + +static int lc = 0; +static int l2c = 0; +static int l3c = 0; + + +#ifndef _WIN32 +void sighandler(int s) +{ + quit = true; + ph -> SetQuit(); +} + + +void sigpipe(int s) +{ +} +#endif + + +class evHandler : public EventHandler +{ +public: + evHandler() : EventHandler() {} + evHandler(StdLog *p) : EventHandler(p) {} + + void CloseAll() { + for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) + { + Socket *p = it -> second; + p -> SetCloseAndDelete(); + } + } + +}; + + +class lSocket : public TcpSocket, public IEventOwner +{ +public: + lSocket(ISocketHandler& h, bool first = false) : TcpSocket(h), IEventOwner(static_cast(h)), m_id(0), m_first(first) { + SetLineProtocol(); + lc++; + } + ~lSocket() { + } + + void OnConnect() { + if (!m_id) + { + m_id = AddEvent(0, TIME_US); + } + printf("."); + fflush(stdout); + } + void OnConnectFailed() { + printf("lSocket::OnConnectFailed\n"); + } + + void OnDelete() { + if (m_first) + printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@ OnDelete() first @@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); + } + + void OnEvent(int id) { + if (id != m_id) + { + printf("ID MISMATCH!\n"); + exit(-1); + } + if (!quit) + { + lSocket *p = new lSocket(Handler()); + p -> SetDeleteByHandler(); + p -> Open(HOST, PORT); + Handler().Add(p); + } + static_cast(Handler()).SetQuit( quit ); + SetCloseAndDelete(); + } + +private: + int m_id; + bool m_first; +}; + + +class l2Socket : public TcpSocket, public IEventOwner +{ +public: + l2Socket(ISocketHandler& h, bool first = false) : TcpSocket(h), IEventOwner(static_cast(h)), m_id(0), m_first(first) { + SetLineProtocol(); + l2c++; + } + ~l2Socket() { + } + + void OnConnect() { + printf("."); + fflush(stdout); + } + void OnConnectFailed() { + printf("l2Socket::OnConnectFailed\n"); + } + + void OnLine(const std::string& line) { + if (!m_id) + { + m_id = AddEvent(0, TIME_US); + } + } + + void OnDelete() { + if (m_first) + printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@ OnDelete() first @@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); + } + + void OnEvent(int id) { + if (id != m_id) + { + printf("ID MISMATCH!\n"); + exit(-1); + } + if (!quit) + { + l2Socket *p = new l2Socket(Handler()); + p -> SetDeleteByHandler(); + p -> Open(HOST, PORT); + Handler().Add(p); + } + static_cast(Handler()).SetQuit( quit ); + SetCloseAndDelete(); + } + +private: + int m_id; + bool m_first; +}; + + +class l3Socket : public TcpSocket, public IEventOwner +{ +public: + l3Socket(ISocketHandler& h, bool first = false) : TcpSocket(h), IEventOwner(static_cast(h)), m_ehlo(false), m_id(0), m_first(first) { + SetLineProtocol(); + l3c++; + } + ~l3Socket() { + } + + void OnConnect() { + printf("."); + fflush(stdout); + } + void OnConnectFailed() { + printf("l3Socket::OnConnectFailed\n"); + } + + void OnLine(const std::string& line) { + if (!m_ehlo) + { + Send("EHLO alhem.net\r\n"); + m_ehlo = true; + } + else + if (!m_id) + { + m_id = AddEvent(0, TIME_US); + } + } + + void OnDelete() { + if (m_first) + printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@ OnDelete() first @@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); + } + + void OnEvent(int id) { + if (id != m_id) + { + printf("ID MISMATCH!\n"); + exit(-1); + } + if (!quit) + { + l3Socket *p = new l3Socket(Handler()); + p -> SetDeleteByHandler(); + p -> Open(HOST, PORT); + Handler().Add(p); + } + static_cast(Handler()).SetQuit( quit ); + Send("QUIT\r\n"); + } + +private: + bool m_ehlo; + int m_id; + bool m_first; +}; + + +int main(int argc, char *argv[]) +{ +#ifndef _WIN32 + signal(SIGHUP, sighandler); + signal(SIGINT, sighandler); + signal(SIGPIPE, sigpipe); +#endif + + time_t tstart = time(NULL); + { + StdoutLog log; + { + evHandler h(&log); + ph = &h; + { + lSocket sock(h, true); + sock.Open(HOST, PORT); + h.Add(&sock); + { + l2Socket sock2(h, true); + sock2.Open(HOST, PORT); + h.Add(&sock2); + { + l3Socket sock3(h, true); + sock3.Open(HOST, PORT); + h.Add(&sock3); + + h.EventLoop(); + h.CloseAll(); + + // %! one socket refuses to go away + while (h.GetCount() > 1) + { + h.Select(1, 0); + } + printf("Exiting 1...\n"); + fflush(stdout); + } + printf("Exiting 2...\n"); + fflush(stdout); + } + printf("Exiting 3...\n"); + fflush(stdout); + } + printf("Exiting 4...\n"); + fflush(stdout); + } + printf("Exiting 5...\n"); + fflush(stdout); + } + printf("Exiting 6...\n"); + fflush(stdout); + + time_t tstop = time(NULL); + + time_t total = tstop - tstart + 1; + + printf("lc: %d\n", lc); + printf("l2c: %d\n", l2c); + printf("l3c: %d\n", l3c); + + printf("lc: %f\n", (double)lc / (double)total); + printf("l2c: %f\n", (double)l2c / (double)total); + printf("l3c: %f\n", (double)l3c / (double)total); +} + + -- cgit v1.2.3